| /* |
| 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. |
| */ |
| |
| // Warnings about deprecated Bazel-related operations |
| |
| package warn |
| |
| import ( |
| "fmt" |
| |
| "github.com/bazelbuild/buildtools/build" |
| ) |
| |
| func depsetUnionWarning(f *build.File) []*LinterFinding { |
| var findings []*LinterFinding |
| addWarning := func(expr build.Expr) { |
| findings = append(findings, |
| makeLinterFinding(expr, `Depsets should be joined using the "depset()" constructor.`)) |
| } |
| |
| types := DetectTypes(f) |
| build.Walk(f, func(expr build.Expr, stack []build.Expr) { |
| switch expr := expr.(type) { |
| case *build.BinaryExpr: |
| // `depset1 + depset2` or `depset1 | depset2` |
| if types[expr.X] != Depset && types[expr.Y] != Depset { |
| return |
| } |
| switch expr.Op { |
| case "+", "|": |
| addWarning(expr) |
| } |
| case *build.AssignExpr: |
| // `depset1 += depset2` or `depset1 |= depset2` |
| if types[expr.LHS] != Depset && types[expr.RHS] != Depset { |
| return |
| } |
| switch expr.Op { |
| case "+=", "|=": |
| addWarning(expr) |
| } |
| case *build.CallExpr: |
| // `depset1.union(depset2)` |
| if len(expr.List) == 0 { |
| return |
| } |
| dot, ok := expr.X.(*build.DotExpr) |
| if !ok { |
| return |
| } |
| if dot.Name != "union" { |
| return |
| } |
| if types[dot.X] != Depset && types[expr.List[0]] != Depset { |
| return |
| } |
| addWarning(expr) |
| } |
| }) |
| return findings |
| } |
| |
| func depsetIterationWarning(f *build.File) []*LinterFinding { |
| var findings []*LinterFinding |
| |
| addFinding := func(expr *build.Expr) { |
| _, end := (*expr).Span() |
| newNode := &build.CallExpr{ |
| X: &build.DotExpr{ |
| X: *expr, |
| Name: "to_list", |
| }, |
| End: build.End{Pos: end}, |
| } |
| findings = append(findings, |
| makeLinterFinding(*expr, `Depset iteration is deprecated, use the "to_list()" method instead.`, LinterReplacement{expr, newNode})) |
| } |
| |
| types := DetectTypes(f) |
| build.WalkPointers(f, func(e *build.Expr, stack []build.Expr) { |
| switch expr := (*e).(type) { |
| case *build.ForStmt: |
| if types[expr.X] != Depset { |
| return |
| } |
| addFinding(&expr.X) |
| case *build.ForClause: |
| if types[expr.X] != Depset { |
| return |
| } |
| addFinding(&expr.X) |
| case *build.BinaryExpr: |
| if expr.Op != "in" && expr.Op != "not in" { |
| return |
| } |
| if types[expr.Y] != Depset { |
| return |
| } |
| addFinding(&expr.Y) |
| case *build.CallExpr: |
| ident, ok := expr.X.(*build.Ident) |
| if !ok { |
| return |
| } |
| switch ident.Name { |
| case "all", "any", "depset", "len", "sorted", "max", "min", "list", "tuple": |
| if len(expr.List) != 1 { |
| return |
| } |
| if types[expr.List[0]] != Depset { |
| return |
| } |
| addFinding(&expr.List[0]) |
| if ident.Name == "list" { |
| // `list(d.to_list())` can be simplified to just `d.to_list()` |
| findings[len(findings)-1].Replacement[0].Old = e |
| } |
| case "zip": |
| for i, arg := range expr.List { |
| if types[arg] != Depset { |
| continue |
| } |
| addFinding(&expr.List[i]) |
| } |
| } |
| } |
| return |
| }) |
| return findings |
| } |
| |
| func overlyNestedDepsetWarning(f *build.File) []*LinterFinding { |
| var findings []*LinterFinding |
| build.WalkStatements(f, func(expr build.Expr, stack []build.Expr) (err error) { |
| // Are we inside a for-loop? |
| isForLoop := false |
| for _, e := range stack { |
| if _, ok := e.(*build.ForStmt); ok { |
| isForLoop = true |
| break |
| } |
| } |
| if !isForLoop { |
| return |
| } |
| |
| // Search for assignment statements |
| assign, ok := expr.(*build.AssignExpr) |
| if !ok { |
| return |
| } |
| // Is the LHS an ident? |
| lhs, ok := assign.LHS.(*build.Ident) |
| if !ok { |
| return |
| } |
| // Is the RHS a depset constructor? |
| call, ok := assign.RHS.(*build.CallExpr) |
| if !ok { |
| return |
| } |
| if ident, ok := call.X.(*build.Ident); !ok || ident.Name != "depset" { |
| return |
| } |
| _, _, param := getParam(call.List, "transitive") |
| if param == nil { |
| return |
| } |
| transitives, ok := param.RHS.(*build.ListExpr) |
| if !ok { |
| return |
| } |
| for _, transitive := range transitives.List { |
| if ident, ok := transitive.(*build.Ident); ok && ident.Name == lhs.Name { |
| findings = append(findings, makeLinterFinding(assign, fmt.Sprintf("Depset %q is potentially overly nested.", lhs.Name))) |
| return |
| } |
| } |
| return |
| }) |
| return findings |
| } |