| /* |
| 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. |
| */ |
| |
| // General Bazel-related warnings |
| |
| package warn |
| |
| import ( |
| "fmt" |
| "strings" |
| |
| "github.com/bazelbuild/buildtools/build" |
| ) |
| |
| var functionsWithPositionalArguments = map[string]bool{ |
| "exports_files": true, |
| "licenses": true, |
| "print": true, |
| "register_toolchains": true, |
| "vardef": true, |
| } |
| |
| func constantGlobPatternWarning(patterns *build.ListExpr) []*LinterFinding { |
| findings := []*LinterFinding{} |
| for _, expr := range patterns.List { |
| str, ok := expr.(*build.StringExpr) |
| if !ok { |
| continue |
| } |
| if !strings.Contains(str.Value, "*") { |
| message := fmt.Sprintf( |
| `Glob pattern %q has no wildcard ('*'). Constant patterns can be error-prone, move the file outside the glob.`, str.Value) |
| findings = append(findings, makeLinterFinding(expr, message)) |
| return findings // at most one warning per glob |
| } |
| } |
| return findings |
| } |
| |
| func constantGlobWarning(f *build.File) []*LinterFinding { |
| switch f.Type { |
| case build.TypeBuild, build.TypeWorkspace, build.TypeBzl: |
| default: |
| // Not applicable |
| return nil |
| } |
| |
| findings := []*LinterFinding{} |
| build.Walk(f, func(expr build.Expr, stack []build.Expr) { |
| call, ok := expr.(*build.CallExpr) |
| if !ok || len(call.List) == 0 { |
| return |
| } |
| ident, ok := (call.X).(*build.Ident) |
| if !ok || ident.Name != "glob" { |
| return |
| } |
| patterns, ok := call.List[0].(*build.ListExpr) |
| if ok { |
| // first arg is unnamed and is a list |
| findings = append(findings, constantGlobPatternWarning(patterns)...) |
| return // at most one warning per glob |
| } |
| |
| // look for named args called include |
| for _, arg := range call.List { |
| assignExpr, ok := arg.(*build.AssignExpr) |
| if !ok { |
| continue |
| } |
| str, ok := assignExpr.LHS.(*build.Ident) |
| if !ok || str.Name != "include" { |
| continue |
| } |
| patterns, ok := assignExpr.RHS.(*build.ListExpr) |
| if ok { |
| findings = append(findings, constantGlobPatternWarning(patterns)...) |
| return // at most one warning per glob |
| } |
| } |
| }) |
| return findings |
| } |
| |
| func nativeInBuildFilesWarning(f *build.File) []*LinterFinding { |
| if f.Type != build.TypeBuild { |
| return nil |
| } |
| |
| findings := []*LinterFinding{} |
| build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { |
| // Search for `native.xxx` nodes |
| dot, ok := (*expr).(*build.DotExpr) |
| if !ok { |
| return |
| } |
| ident, ok := dot.X.(*build.Ident) |
| if !ok || ident.Name != "native" { |
| return |
| } |
| |
| findings = append(findings, |
| makeLinterFinding(ident, |
| `The "native" module shouldn't be used in BUILD files, its members are available as global symbols.`, |
| LinterReplacement{expr, &build.Ident{Name: dot.Name}})) |
| }) |
| return findings |
| } |
| |
| func nativePackageWarning(f *build.File) []*LinterFinding { |
| if f.Type != build.TypeBzl { |
| return nil |
| } |
| |
| findings := []*LinterFinding{} |
| build.Walk(f, func(expr build.Expr, stack []build.Expr) { |
| // Search for `native.package()` nodes |
| call, ok := expr.(*build.CallExpr) |
| if !ok { |
| return |
| } |
| dot, ok := call.X.(*build.DotExpr) |
| if !ok || dot.Name != "package" { |
| return |
| } |
| ident, ok := dot.X.(*build.Ident) |
| if !ok || ident.Name != "native" { |
| return |
| } |
| |
| findings = append(findings, |
| makeLinterFinding(call, `"native.package()" shouldn't be used in .bzl files.`)) |
| }) |
| return findings |
| } |
| |
| func duplicatedNameWarning(f *build.File) []*LinterFinding { |
| if f.Type != build.TypeBuild && f.Type != build.TypeWorkspace { |
| // Not applicable to .bzl files. |
| return nil |
| } |
| |
| findings := []*LinterFinding{} |
| names := make(map[string]int) // map from name to line number |
| msg := `A rule with name %q was already found on line %d. ` + |
| `Even if it's valid for Blaze, this may confuse other tools. ` + |
| `Please rename it and use different names.` |
| |
| for _, rule := range f.Rules("") { |
| name := rule.ExplicitName() |
| if name == "" { |
| continue |
| } |
| start, _ := rule.Call.Span() |
| if line, ok := names[name]; ok { |
| finding := makeLinterFinding(rule.Call, fmt.Sprintf(msg, name, line)) |
| if nameNode := rule.Attr("name"); nameNode != nil { |
| finding.Start, finding.End = nameNode.Span() |
| start = finding.Start |
| } |
| findings = append(findings, finding) |
| } else { |
| names[name] = start.Line |
| } |
| } |
| return findings |
| } |
| |
| func positionalArgumentsWarning(call *build.CallExpr, pkg string) *LinterFinding { |
| if id, ok := call.X.(*build.Ident); !ok || functionsWithPositionalArguments[id.Name] { |
| return nil |
| } |
| for _, arg := range call.List { |
| if _, ok := arg.(*build.AssignExpr); ok || arg == nil { |
| continue |
| } |
| return makeLinterFinding(arg, "All calls to rules or macros should pass arguments by keyword (arg_name=value) syntax.") |
| } |
| return nil |
| } |
| |
| func argsKwargsInBuildFilesWarning(f *build.File) []*LinterFinding { |
| if f.Type != build.TypeBuild { |
| return nil |
| } |
| |
| findings := []*LinterFinding{} |
| build.Walk(f, func(expr build.Expr, stack []build.Expr) { |
| // Search for function call nodes |
| call, ok := expr.(*build.CallExpr) |
| if !ok { |
| return |
| } |
| for _, param := range call.List { |
| unary, ok := param.(*build.UnaryExpr) |
| if !ok { |
| continue |
| } |
| switch unary.Op { |
| case "*": |
| findings = append(findings, |
| makeLinterFinding(param, `*args are not allowed in BUILD files.`)) |
| case "**": |
| findings = append(findings, |
| makeLinterFinding(param, `**kwargs are not allowed in BUILD files.`)) |
| } |
| } |
| }) |
| return findings |
| } |
| |
| func printWarning(f *build.File) []*LinterFinding { |
| if f.Type == build.TypeDefault { |
| // Only applicable to Bazel files |
| return nil |
| } |
| |
| findings := []*LinterFinding{} |
| build.Walk(f, func(expr build.Expr, stack []build.Expr) { |
| call, ok := expr.(*build.CallExpr) |
| if !ok { |
| return |
| } |
| ident, ok := (call.X).(*build.Ident) |
| if !ok || ident.Name != "print" { |
| return |
| } |
| findings = append(findings, |
| makeLinterFinding(expr, `"print()" is a debug function and shouldn't be submitted.`)) |
| }) |
| return findings |
| } |