blob: db92667795ab6a20ee69f6206afe7617a14a895d [file] [log] [blame] [edit]
/*
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. NOTE: This does not work if the target is a select, hence this warning can safely be ignored or suppressed.`,
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
}