| /* | |
| Copyright 2020 Google LLC | |
| 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 | |
| https://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 warn | |
| import ( | |
| "fmt" | |
| "strings" | |
| "github.com/bazelbuild/buildtools/build" | |
| "github.com/bazelbuild/buildtools/bzlenv" | |
| ) | |
| var ambiguousNames = map[string]bool{ | |
| "I": true, | |
| "l": true, | |
| "O": true, | |
| } | |
| // ambiguousNameCheck checks for the names of idents and functions | |
| func ambiguousNameCheck(expr build.Expr, name string, findings []*LinterFinding) []*LinterFinding { | |
| if ambiguousNames[name] { | |
| findings = append(findings, | |
| makeLinterFinding(expr, `Never use 'l', 'I', or 'O' as names (they're too easily confused with 'I', 'l', or '0').`)) | |
| } | |
| return findings | |
| } | |
| func confusingNameWarning(f *build.File) []*LinterFinding { | |
| var findings []*LinterFinding | |
| // check for global variable names | |
| for _, ident := range collectLocalVariables(f.Stmt) { | |
| findings = ambiguousNameCheck(ident, ident.Name, findings) | |
| } | |
| build.Walk(f, func(expr build.Expr, stack []build.Expr) { | |
| switch expr := expr.(type) { | |
| case *build.DefStmt: | |
| findings = ambiguousNameCheck(expr, expr.Name, findings) | |
| for _, param := range expr.Params { | |
| name, _ := build.GetParamName(param) | |
| findings = ambiguousNameCheck(param, name, findings) | |
| } | |
| for _, ident := range collectLocalVariables(expr.Body) { | |
| findings = ambiguousNameCheck(ident, ident.Name, findings) | |
| } | |
| case *build.Comprehension: | |
| for _, clause := range expr.Clauses { | |
| forClause, ok := clause.(*build.ForClause) | |
| if !ok { | |
| continue | |
| } | |
| for _, ident := range bzlenv.CollectLValues(forClause.Vars) { | |
| findings = ambiguousNameCheck(ident, ident.Name, findings) | |
| } | |
| } | |
| } | |
| }) | |
| return findings | |
| } | |
| func isUpperCamelCase(name string) bool { | |
| if strings.HasPrefix(name, "_") { | |
| // Private providers are allowed | |
| name = name[1:] | |
| } | |
| return !strings.ContainsRune(name, '_') && name == strings.Title(name) | |
| } | |
| func isLowerSnakeCase(name string) bool { | |
| return name == strings.ToLower(name) | |
| } | |
| func isUpperSnakeCase(name string) bool { | |
| return name == strings.ToUpper(name) | |
| } | |
| func nameConventionsWarning(f *build.File) []*LinterFinding { | |
| var findings []*LinterFinding | |
| build.WalkStatements(f, func(stmt build.Expr, stack []build.Expr) (err error) { | |
| // looking for provider declaration statements: `xxx = provider()` | |
| // note that the code won't trigger on complex assignments, such as `x, y = foo, provider()` | |
| binary, ok := stmt.(*build.AssignExpr) | |
| if !ok { | |
| return | |
| } | |
| for _, ident := range bzlenv.CollectLValues(binary.LHS) { | |
| if isLowerSnakeCase(ident.Name) || isUpperSnakeCase(ident.Name) { | |
| continue | |
| } | |
| if isUpperCamelCase(ident.Name) && strings.HasSuffix(ident.Name, "Info") { | |
| continue | |
| } | |
| findings = append(findings, | |
| makeLinterFinding(ident, | |
| fmt.Sprintf(`Variable name "%s" should be lower_snake_case (for variables), UPPER_SNAKE_CASE (for constants), or UpperCamelCase ending with 'Info' (for providers).`, ident.Name))) | |
| } | |
| return | |
| }) | |
| return findings | |
| } |