/* 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 operations in Starlark package warn import ( "github.com/bazelbuild/buildtools/build" ) func dictionaryConcatenationWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding var addWarning = func(expr build.Expr) { findings = append(findings, makeLinterFinding(expr, "Dictionary concatenation is deprecated.")) } types := DetectTypes(f) build.Walk(f, func(expr build.Expr, stack []build.Expr) { switch expr := expr.(type) { case *build.BinaryExpr: if expr.Op != "+" { return } if types[expr.X] == Dict || types[expr.Y] == Dict { addWarning(expr) } case *build.AssignExpr: if expr.Op != "+=" { return } if types[expr.LHS] == Dict || types[expr.RHS] == Dict { addWarning(expr) } } }) return findings } // Detect the patterns: // // dict.pop(..., default = ...) // dict.pop(..., default = ...) // dict.setdefault(..., default = ...) // // And recommend removing the `default =` part. These functions take positional argument instead. func dictMethodNamedArgWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding types := DetectTypes(f) build.Walk(f, func(expr build.Expr, stack []build.Expr) { var callexpr *build.CallExpr var ok bool if callexpr, ok = expr.(*build.CallExpr); !ok { return } var dotexpr *build.DotExpr if dotexpr, ok = callexpr.X.(*build.DotExpr); !ok || types[dotexpr.X] != Dict { return } if dotexpr.Name != "pop" && dotexpr.Name != "get" && dotexpr.Name != "setdefault" { return } for _, expr := range callexpr.List { assignExpr, ok := expr.(*build.AssignExpr) if !ok { continue } if left, ok := assignExpr.LHS.(*build.Ident); !ok || left.Name != "default" { continue } findings = append(findings, makeLinterFinding(expr, "Named argument \"default\" not allowed, use a positional (unnamed) argument")) } }) return findings } func stringIterationWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding addWarning := func(expr build.Expr) { findings = append(findings, makeLinterFinding(expr, "String iteration is deprecated.")) } types := DetectTypes(f) build.Walk(f, func(expr build.Expr, stack []build.Expr) { switch expr := expr.(type) { case *build.ForStmt: if types[expr.X] == String { addWarning(expr.X) } case *build.ForClause: if types[expr.X] == String { addWarning(expr.X) } case *build.CallExpr: ident, ok := expr.X.(*build.Ident) if !ok { return } switch ident.Name { case "all", "any", "reversed", "max", "min": if len(expr.List) != 1 { return } if types[expr.List[0]] == String { addWarning(expr.List[0]) } case "zip": for _, arg := range expr.List { if types[arg] == String { addWarning(arg) } } } } }) return findings } func integerDivisionWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding types := DetectTypes(f) build.WalkPointers(f, func(e *build.Expr, stack []build.Expr) { switch expr := (*e).(type) { case *build.BinaryExpr: if expr.Op != "/" { return } if types[expr.X] != Int || types[expr.Y] != Int { return } newBinary := *expr newBinary.Op = "//" findings = append(findings, makeLinterFinding(expr, `The "/" operator for integer division is deprecated in favor of "//".`, LinterReplacement{e, &newBinary})) case *build.AssignExpr: if expr.Op != "/=" { return } if types[expr.LHS] != Int || types[expr.RHS] != Int { return } newAssign := *expr newAssign.Op = "//=" findings = append(findings, makeLinterFinding(expr, `The "/=" operator for integer division is deprecated in favor of "//=".`, LinterReplacement{e, &newAssign})) } }) return findings } func listAppendWarning(f *build.File) []*LinterFinding { var findings []*LinterFinding build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) { as, ok := (*expr).(*build.AssignExpr) if !ok || as.Op != "+=" { return } list, ok := as.RHS.(*build.ListExpr) if !ok || len(list.List) != 1 { return } _, end := as.Span() findings = append(findings, makeLinterFinding(as, `Prefer using ".append()" to adding a single element list.`, LinterReplacement{expr, &build.CallExpr{ Comments: as.Comments, X: &build.DotExpr{ X: as.LHS, Name: "append", }, List: list.List, End: build.End{Pos: end}, }})) }) return findings }