...

Source file src/github.com/bazelbuild/buildtools/warn/warn_operation.go

Documentation: github.com/bazelbuild/buildtools/warn

     1  /*
     2  Copyright 2020 Google LLC
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      https://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Warnings about deprecated operations in Starlark
    18  
    19  package warn
    20  
    21  import (
    22  	"github.com/bazelbuild/buildtools/build"
    23  )
    24  
    25  func dictionaryConcatenationWarning(f *build.File) []*LinterFinding {
    26  	var findings []*LinterFinding
    27  
    28  	var addWarning = func(expr build.Expr) {
    29  		findings = append(findings,
    30  			makeLinterFinding(expr, "Dictionary concatenation is deprecated."))
    31  	}
    32  
    33  	types := DetectTypes(f)
    34  	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
    35  		switch expr := expr.(type) {
    36  		case *build.BinaryExpr:
    37  			if expr.Op != "+" {
    38  				return
    39  			}
    40  			if types[expr.X] == Dict || types[expr.Y] == Dict {
    41  				addWarning(expr)
    42  			}
    43  		case *build.AssignExpr:
    44  			if expr.Op != "+=" {
    45  				return
    46  			}
    47  			if types[expr.LHS] == Dict || types[expr.RHS] == Dict {
    48  				addWarning(expr)
    49  			}
    50  		}
    51  	})
    52  	return findings
    53  }
    54  
    55  // Detect the patterns:
    56  //
    57  //	dict.pop(..., default = ...)
    58  //	dict.pop(..., default = ...)
    59  //	dict.setdefault(..., default = ...)
    60  //
    61  // And recommend removing the `default =` part. These functions take positional argument instead.
    62  func dictMethodNamedArgWarning(f *build.File) []*LinterFinding {
    63  	var findings []*LinterFinding
    64  
    65  	types := DetectTypes(f)
    66  	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
    67  
    68  		var callexpr *build.CallExpr
    69  		var ok bool
    70  		if callexpr, ok = expr.(*build.CallExpr); !ok {
    71  			return
    72  		}
    73  		var dotexpr *build.DotExpr
    74  		if dotexpr, ok = callexpr.X.(*build.DotExpr); !ok || types[dotexpr.X] != Dict {
    75  			return
    76  		}
    77  
    78  		if dotexpr.Name != "pop" && dotexpr.Name != "get" && dotexpr.Name != "setdefault" {
    79  			return
    80  		}
    81  
    82  		for _, expr := range callexpr.List {
    83  			assignExpr, ok := expr.(*build.AssignExpr)
    84  			if !ok {
    85  				continue
    86  			}
    87  			if left, ok := assignExpr.LHS.(*build.Ident); !ok || left.Name != "default" {
    88  				continue
    89  			}
    90  			findings = append(findings,
    91  				makeLinterFinding(expr, "Named argument \"default\" not allowed, use a positional (unnamed) argument"))
    92  		}
    93  	})
    94  	return findings
    95  }
    96  
    97  func stringIterationWarning(f *build.File) []*LinterFinding {
    98  	var findings []*LinterFinding
    99  
   100  	addWarning := func(expr build.Expr) {
   101  		findings = append(findings,
   102  			makeLinterFinding(expr, "String iteration is deprecated."))
   103  	}
   104  
   105  	types := DetectTypes(f)
   106  	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
   107  		switch expr := expr.(type) {
   108  		case *build.ForStmt:
   109  			if types[expr.X] == String {
   110  				addWarning(expr.X)
   111  			}
   112  		case *build.ForClause:
   113  			if types[expr.X] == String {
   114  				addWarning(expr.X)
   115  			}
   116  		case *build.CallExpr:
   117  			ident, ok := expr.X.(*build.Ident)
   118  			if !ok {
   119  				return
   120  			}
   121  			switch ident.Name {
   122  			case "all", "any", "reversed", "max", "min":
   123  				if len(expr.List) != 1 {
   124  					return
   125  				}
   126  				if types[expr.List[0]] == String {
   127  					addWarning(expr.List[0])
   128  				}
   129  			case "zip":
   130  				for _, arg := range expr.List {
   131  					if types[arg] == String {
   132  						addWarning(arg)
   133  					}
   134  				}
   135  			}
   136  		}
   137  	})
   138  	return findings
   139  }
   140  
   141  func integerDivisionWarning(f *build.File) []*LinterFinding {
   142  	var findings []*LinterFinding
   143  
   144  	types := DetectTypes(f)
   145  	build.WalkPointers(f, func(e *build.Expr, stack []build.Expr) {
   146  		switch expr := (*e).(type) {
   147  		case *build.BinaryExpr:
   148  			if expr.Op != "/" {
   149  				return
   150  			}
   151  			if types[expr.X] != Int || types[expr.Y] != Int {
   152  				return
   153  			}
   154  			newBinary := *expr
   155  			newBinary.Op = "//"
   156  			findings = append(findings,
   157  				makeLinterFinding(expr, `The "/" operator for integer division is deprecated in favor of "//".`,
   158  					LinterReplacement{e, &newBinary}))
   159  
   160  		case *build.AssignExpr:
   161  			if expr.Op != "/=" {
   162  				return
   163  			}
   164  			if types[expr.LHS] != Int || types[expr.RHS] != Int {
   165  				return
   166  			}
   167  			newAssign := *expr
   168  			newAssign.Op = "//="
   169  			findings = append(findings,
   170  				makeLinterFinding(expr, `The "/=" operator for integer division is deprecated in favor of "//=".`,
   171  					LinterReplacement{e, &newAssign}))
   172  		}
   173  	})
   174  	return findings
   175  }
   176  
   177  func listAppendWarning(f *build.File) []*LinterFinding {
   178  	var findings []*LinterFinding
   179  
   180  	build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) {
   181  		as, ok := (*expr).(*build.AssignExpr)
   182  		if !ok || as.Op != "+=" {
   183  			return
   184  		}
   185  
   186  		list, ok := as.RHS.(*build.ListExpr)
   187  		if !ok || len(list.List) != 1 {
   188  			return
   189  		}
   190  
   191  		_, end := as.Span()
   192  		findings = append(findings, makeLinterFinding(as, `Prefer using ".append()" to adding a single element list.`,
   193  			LinterReplacement{expr, &build.CallExpr{
   194  				Comments: as.Comments,
   195  				X: &build.DotExpr{
   196  					X:    as.LHS,
   197  					Name: "append",
   198  				},
   199  				List: list.List,
   200  				End:  build.End{Pos: end},
   201  			}}))
   202  
   203  	})
   204  	return findings
   205  }
   206  

View as plain text