go/lint: only report kBlah error for constants and top level vars

The error for variables that start with k followed by a variable name,
such as "kBlah", has a lot of false positives. It's intended to avoid
cases where users try to follow the Google C++ style guide or hungarian
notation for constants. So it should only be reported for constants and
top-level variables because those are the cases where users make this
mistake.

Change-Id: I7e862dbc1013707b6b9ebaa72ee07fb623dd0ce8
Reviewed-on: https://go-review.googlesource.com/c/154339
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/lint.go b/lint.go
index b424245..6b9fd6e 100644
--- a/lint.go
+++ b/lint.go
@@ -540,6 +540,18 @@
 	"kWh":          true,
 }
 
+func isInTopLevel(f *ast.File, ident *ast.Ident) bool {
+	path, _ := astutil.PathEnclosingInterval(f, ident.Pos(), ident.End())
+	for _, f := range path {
+		switch f.(type) {
+		case *ast.File, *ast.GenDecl, *ast.ValueSpec, *ast.Ident:
+			continue
+		}
+		return false
+	}
+	return true
+}
+
 // lintNames examines all names in the file.
 // It complains if any use underscores or incorrect known initialisms.
 func (f *file) lintNames() {
@@ -572,9 +584,11 @@
 				return
 			}
 		}
-		if len(id.Name) > 2 && id.Name[0] == 'k' && id.Name[1] >= 'A' && id.Name[1] <= 'Z' {
-			should := string(id.Name[1]+'a'-'A') + id.Name[2:]
-			f.errorf(id, 0.8, link(styleGuideBase+"#mixed-caps"), category("naming"), "don't use leading k in Go names; %s %s should be %s", thing, id.Name, should)
+		if thing == "const" || (thing == "var" && isInTopLevel(f.f, id)) {
+			if len(id.Name) > 2 && id.Name[0] == 'k' && id.Name[1] >= 'A' && id.Name[1] <= 'Z' {
+				should := string(id.Name[1]+'a'-'A') + id.Name[2:]
+				f.errorf(id, 0.8, link(styleGuideBase+"#mixed-caps"), category("naming"), "don't use leading k in Go names; %s %s should be %s", thing, id.Name, should)
+			}
 		}
 
 		should := lintName(id.Name)
diff --git a/testdata/names.go b/testdata/names.go
index ed7dd41..328a8d7 100644
--- a/testdata/names.go
+++ b/testdata/names.go
@@ -64,6 +64,27 @@
 	V1_10_5 = 5 // okay; fewer than two uppercase letters
 )
 
+var kVarsAreSometimesUsedAsConstants = 0 // MATCH /k.*varsAreSometimesUsedAsConstants/
+var (
+	kVarsAreSometimesUsedAsConstants2 = 0 // MATCH /k.*varsAreSometimesUsedAsConstants2/
+)
+
+var kThisIsNotOkay = struct { // MATCH /k.*thisIsNotOkay/
+	kThisIsOkay bool
+}{}
+
+func kThisIsOkay() { // this is okay because this is a function name
+	var kThisIsAlsoOkay = 1 // this is okay because this is a non-top-level variable
+	_ = kThisIsAlsoOkay
+	const kThisIsNotOkay = 2 // MATCH /k.*thisIsNotOkay/
+}
+
+var anotherFunctionScope = func() {
+	var kThisIsOkay = 1 // this is okay because this is a non-top-level variable
+	_ = kThisIsOkay
+	const kThisIsNotOkay = 2 // MATCH /k.*thisIsNotOkay/}
+}
+
 func f(bad_name int)                    {}            // MATCH /underscore.*func parameter.*bad_name/
 func g() (no_way int)                   { return 0 }  // MATCH /underscore.*func result.*no_way/
 func (t *t_wow) f(more_under string)    {}            // MATCH /underscore.*method parameter.*more_under/