storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/ruleguard.rules.go (about) 1 //go:build ignore 2 // +build ignore 3 4 package gorules 5 6 import "github.com/quasilyte/go-ruleguard/dsl/fluent" 7 8 // This is a collection of rules for ruleguard: https://github.com/quasilyte/go-ruleguard 9 10 // Remove extra conversions: mdempsky/unconvert 11 func unconvert(m fluent.Matcher) { 12 m.Match("int($x)").Where(m["x"].Type.Is("int") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") 13 14 m.Match("float32($x)").Where(m["x"].Type.Is("float32") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") 15 m.Match("float64($x)").Where(m["x"].Type.Is("float64") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") 16 17 // m.Match("byte($x)").Where(m["x"].Type.Is("byte")).Report("unnecessary conversion").Suggest("$x") 18 // m.Match("rune($x)").Where(m["x"].Type.Is("rune")).Report("unnecessary conversion").Suggest("$x") 19 m.Match("bool($x)").Where(m["x"].Type.Is("bool") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") 20 21 m.Match("int8($x)").Where(m["x"].Type.Is("int8") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") 22 m.Match("int16($x)").Where(m["x"].Type.Is("int16") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") 23 m.Match("int32($x)").Where(m["x"].Type.Is("int32") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") 24 m.Match("int64($x)").Where(m["x"].Type.Is("int64") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") 25 26 m.Match("uint8($x)").Where(m["x"].Type.Is("uint8") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") 27 m.Match("uint16($x)").Where(m["x"].Type.Is("uint16") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") 28 m.Match("uint32($x)").Where(m["x"].Type.Is("uint32") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") 29 m.Match("uint64($x)").Where(m["x"].Type.Is("uint64") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") 30 31 m.Match("time.Duration($x)").Where(m["x"].Type.Is("time.Duration") && !m["x"].Text.Matches("^[0-9]*$")).Report("unnecessary conversion").Suggest("$x") 32 } 33 34 // Don't use == or != with time.Time 35 // https://github.com/dominikh/go-tools/issues/47 : Wontfix 36 func timeeq(m fluent.Matcher) { 37 m.Match("$t0 == $t1").Where(m["t0"].Type.Is("time.Time")).Report("using == with time.Time") 38 m.Match("$t0 != $t1").Where(m["t0"].Type.Is("time.Time")).Report("using != with time.Time") 39 m.Match(`map[$k]$v`).Where(m["k"].Type.Is("time.Time")).Report("map with time.Time keys are easy to misuse") 40 } 41 42 // Wrong err in error check 43 func wrongerr(m fluent.Matcher) { 44 m.Match("if $*_, $err0 := $*_; $err1 != nil { $*_ }"). 45 Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). 46 Report("maybe wrong err in error check") 47 48 m.Match("if $*_, $err0 := $*_; $err1 != nil { $*_ }"). 49 Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). 50 Report("maybe wrong err in error check") 51 52 m.Match("if $*_, $err0 = $*_; $err1 != nil { $*_ }"). 53 Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). 54 Report("maybe wrong err in error check") 55 56 m.Match("if $*_, $err0 = $*_; $err1 != nil { $*_ }"). 57 Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). 58 Report("maybe wrong err in error check") 59 60 m.Match("if $*_, $err0 := $*_; $err1 == nil { $*_ }"). 61 Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). 62 Report("maybe wrong err in error check") 63 64 m.Match("if $*_, $err0 := $*_; $err1 == nil { $*_ }"). 65 Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). 66 Report("maybe wrong err in error check") 67 68 m.Match("if $*_, $err0 = $*_; $err1 == nil { $*_ }"). 69 Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). 70 Report("maybe wrong err in error check") 71 72 m.Match("if $*_, $err0 = $*_; $err1 == nil { $*_ }"). 73 Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). 74 Report("maybe wrong err in error check") 75 76 m.Match("$*_, $err0 := $*_; if $err1 != nil { $*_ }"). 77 Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). 78 Report("maybe wrong err in error check") 79 80 m.Match("$*_, $err0 := $*_; if $err1 != nil { $*_ }"). 81 Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). 82 Report("maybe wrong err in error check") 83 84 m.Match("$*_, $err0 := $*_; if $err1 == nil { $*_ }"). 85 Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). 86 Report("maybe wrong err in error check") 87 88 m.Match("$*_, $err0 := $*_; if $err1 == nil { $*_ }"). 89 Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). 90 Report("maybe wrong err in error check") 91 92 m.Match("$*_, $err0 = $*_; if $err1 != nil { $*_ }"). 93 Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). 94 Report("maybe wrong err in error check") 95 96 m.Match("$*_, $err0 = $*_; if $err1 != nil { $*_ }"). 97 Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). 98 Report("maybe wrong err in error check") 99 100 m.Match("$*_, $err0 = $*_; if $err1 == nil { $*_ }"). 101 Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). 102 Report("maybe wrong err in error check") 103 104 m.Match("$*_, $err0 = $*_; if $err1 == nil { $*_ }"). 105 Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). 106 Report("maybe wrong err in error check") 107 } 108 109 // err but no an error 110 func errnoterror(m fluent.Matcher) { 111 112 // Would be easier to check for all err identifiers instead, but then how do we get the type from m[] ? 113 114 m.Match( 115 "if $*_, err := $x; $err != nil { $*_ } else if $_ { $*_ }", 116 "if $*_, err := $x; $err != nil { $*_ } else { $*_ }", 117 "if $*_, err := $x; $err != nil { $*_ }", 118 119 "if $*_, err = $x; $err != nil { $*_ } else if $_ { $*_ }", 120 "if $*_, err = $x; $err != nil { $*_ } else { $*_ }", 121 "if $*_, err = $x; $err != nil { $*_ }", 122 123 "$*_, err := $x; if $err != nil { $*_ } else if $_ { $*_ }", 124 "$*_, err := $x; if $err != nil { $*_ } else { $*_ }", 125 "$*_, err := $x; if $err != nil { $*_ }", 126 127 "$*_, err = $x; if $err != nil { $*_ } else if $_ { $*_ }", 128 "$*_, err = $x; if $err != nil { $*_ } else { $*_ }", 129 "$*_, err = $x; if $err != nil { $*_ }", 130 ). 131 Where(m["err"].Text == "err" && !m["err"].Type.Is("error") && m["x"].Text != "recover()"). 132 Report("err variable not error type") 133 } 134 135 // Identical if and else bodies 136 func ifbodythenbody(m fluent.Matcher) { 137 m.Match("if $*_ { $body } else { $body }"). 138 Report("identical if and else bodies") 139 140 // Lots of false positives. 141 // m.Match("if $*_ { $body } else if $*_ { $body }"). 142 // Report("identical if and else bodies") 143 } 144 145 // Odd inequality: A - B < 0 instead of != 146 // Too many false positives. 147 /* 148 func subtractnoteq(m fluent.Matcher) { 149 m.Match("$a - $b < 0").Report("consider $a != $b") 150 m.Match("$a - $b > 0").Report("consider $a != $b") 151 m.Match("0 < $a - $b").Report("consider $a != $b") 152 m.Match("0 > $a - $b").Report("consider $a != $b") 153 } 154 */ 155 156 // Self-assignment 157 func selfassign(m fluent.Matcher) { 158 m.Match("$x = $x").Report("useless self-assignment") 159 } 160 161 // Odd nested ifs 162 func oddnestedif(m fluent.Matcher) { 163 m.Match("if $x { if $x { $*_ }; $*_ }", 164 "if $x == $y { if $x != $y {$*_ }; $*_ }", 165 "if $x != $y { if $x == $y {$*_ }; $*_ }", 166 "if $x { if !$x { $*_ }; $*_ }", 167 "if !$x { if $x { $*_ }; $*_ }"). 168 Report("odd nested ifs") 169 170 m.Match("for $x { if $x { $*_ }; $*_ }", 171 "for $x == $y { if $x != $y {$*_ }; $*_ }", 172 "for $x != $y { if $x == $y {$*_ }; $*_ }", 173 "for $x { if !$x { $*_ }; $*_ }", 174 "for !$x { if $x { $*_ }; $*_ }"). 175 Report("odd nested for/ifs") 176 } 177 178 // odd bitwise expressions 179 func oddbitwise(m fluent.Matcher) { 180 m.Match("$x | $x", 181 "$x | ^$x", 182 "^$x | $x"). 183 Report("odd bitwise OR") 184 185 m.Match("$x & $x", 186 "$x & ^$x", 187 "^$x & $x"). 188 Report("odd bitwise AND") 189 190 m.Match("$x &^ $x"). 191 Report("odd bitwise AND-NOT") 192 } 193 194 // odd sequence of if tests with return 195 func ifreturn(m fluent.Matcher) { 196 m.Match("if $x { return $*_ }; if $x {$*_ }").Report("odd sequence of if test") 197 m.Match("if $x { return $*_ }; if !$x {$*_ }").Report("odd sequence of if test") 198 m.Match("if !$x { return $*_ }; if $x {$*_ }").Report("odd sequence of if test") 199 m.Match("if $x == $y { return $*_ }; if $x != $y {$*_ }").Report("odd sequence of if test") 200 m.Match("if $x != $y { return $*_ }; if $x == $y {$*_ }").Report("odd sequence of if test") 201 202 } 203 204 func oddifsequence(m fluent.Matcher) { 205 /* 206 m.Match("if $x { $*_ }; if $x {$*_ }").Report("odd sequence of if test") 207 208 m.Match("if $x == $y { $*_ }; if $y == $x {$*_ }").Report("odd sequence of if tests") 209 m.Match("if $x != $y { $*_ }; if $y != $x {$*_ }").Report("odd sequence of if tests") 210 211 m.Match("if $x < $y { $*_ }; if $y > $x {$*_ }").Report("odd sequence of if tests") 212 m.Match("if $x <= $y { $*_ }; if $y >= $x {$*_ }").Report("odd sequence of if tests") 213 214 m.Match("if $x > $y { $*_ }; if $y < $x {$*_ }").Report("odd sequence of if tests") 215 m.Match("if $x >= $y { $*_ }; if $y <= $x {$*_ }").Report("odd sequence of if tests") 216 */ 217 } 218 219 // odd sequence of nested if tests 220 func nestedifsequence(m fluent.Matcher) { 221 /* 222 m.Match("if $x < $y { if $x >= $y {$*_ }; $*_ }").Report("odd sequence of nested if tests") 223 m.Match("if $x <= $y { if $x > $y {$*_ }; $*_ }").Report("odd sequence of nested if tests") 224 m.Match("if $x > $y { if $x <= $y {$*_ }; $*_ }").Report("odd sequence of nested if tests") 225 m.Match("if $x >= $y { if $x < $y {$*_ }; $*_ }").Report("odd sequence of nested if tests") 226 */ 227 } 228 229 // odd sequence of assignments 230 func identicalassignments(m fluent.Matcher) { 231 m.Match("$x = $y; $y = $x").Report("odd sequence of assignments") 232 } 233 234 func oddcompoundop(m fluent.Matcher) { 235 m.Match("$x += $x + $_", 236 "$x += $x - $_"). 237 Report("odd += expression") 238 239 m.Match("$x -= $x + $_", 240 "$x -= $x - $_"). 241 Report("odd -= expression") 242 } 243 244 func constswitch(m fluent.Matcher) { 245 m.Match("switch $x { $*_ }", "switch $*_; $x { $*_ }"). 246 Where(m["x"].Const && !m["x"].Text.Matches(`^runtime\.`)). 247 Report("constant switch") 248 } 249 250 func oddcomparisons(m fluent.Matcher) { 251 m.Match( 252 "$x - $y == 0", 253 "$x - $y != 0", 254 "$x - $y < 0", 255 "$x - $y <= 0", 256 "$x - $y > 0", 257 "$x - $y >= 0", 258 "$x ^ $y == 0", 259 "$x ^ $y != 0", 260 ).Report("odd comparison") 261 } 262 263 func oddmathbits(m fluent.Matcher) { 264 m.Match( 265 "64 - bits.LeadingZeros64($x)", 266 "32 - bits.LeadingZeros32($x)", 267 "16 - bits.LeadingZeros16($x)", 268 "8 - bits.LeadingZeros8($x)", 269 ).Report("odd math/bits expression: use bits.Len*() instead?") 270 } 271 272 func floateq(m fluent.Matcher) { 273 m.Match( 274 "$x == $y", 275 "$x != $y", 276 ). 277 Where(m["x"].Type.Is("float32") && !m["x"].Const && !m["y"].Text.Matches("0(.0+)?")). 278 Report("floating point tested for equality") 279 280 m.Match( 281 "$x == $y", 282 "$x != $y", 283 ). 284 Where(m["x"].Type.Is("float64") && !m["x"].Const && !m["y"].Text.Matches("0(.0+)?")). 285 Report("floating point tested for equality") 286 287 m.Match("switch $x { $*_ }", "switch $*_; $x { $*_ }"). 288 Where(m["x"].Type.Is("float32")). 289 Report("floating point as switch expression") 290 291 m.Match("switch $x { $*_ }", "switch $*_; $x { $*_ }"). 292 Where(m["x"].Type.Is("float64")). 293 Report("floating point as switch expression") 294 295 } 296 297 func badexponent(m fluent.Matcher) { 298 m.Match( 299 "2 ^ $x", 300 "10 ^ $x", 301 ). 302 Report("caret (^) is not exponentiation") 303 } 304 305 func floatloop(m fluent.Matcher) { 306 m.Match( 307 "for $i := $x; $i < $y; $i += $z { $*_ }", 308 "for $i = $x; $i < $y; $i += $z { $*_ }", 309 ). 310 Where(m["i"].Type.Is("float64")). 311 Report("floating point for loop counter") 312 313 m.Match( 314 "for $i := $x; $i < $y; $i += $z { $*_ }", 315 "for $i = $x; $i < $y; $i += $z { $*_ }", 316 ). 317 Where(m["i"].Type.Is("float32")). 318 Report("floating point for loop counter") 319 } 320 321 func urlredacted(m fluent.Matcher) { 322 323 m.Match( 324 "log.Println($x, $*_)", 325 "log.Println($*_, $x, $*_)", 326 "log.Println($*_, $x)", 327 "log.Printf($*_, $x, $*_)", 328 "log.Printf($*_, $x)", 329 330 "log.Println($x, $*_)", 331 "log.Println($*_, $x, $*_)", 332 "log.Println($*_, $x)", 333 "log.Printf($*_, $x, $*_)", 334 "log.Printf($*_, $x)", 335 ). 336 Where(m["x"].Type.Is("*url.URL")). 337 Report("consider $x.Redacted() when outputting URLs") 338 } 339 340 func sprinterr(m fluent.Matcher) { 341 m.Match(`fmt.Sprint($err)`, 342 `fmt.Sprintf("%s", $err)`, 343 `fmt.Sprintf("%v", $err)`, 344 ). 345 Where(m["err"].Type.Is("error")). 346 Report("maybe call $err.Error() instead of fmt.Sprint()?") 347 348 } 349 350 func largeloopcopy(m fluent.Matcher) { 351 m.Match( 352 `for $_, $v := range $_ { $*_ }`, 353 ). 354 Where(m["v"].Type.Size > 1024). 355 Report(`loop copies large value each iteration`) 356 } 357 358 func joinpath(m fluent.Matcher) { 359 m.Match( 360 `strings.Join($_, "/")`, 361 `strings.Join($_, "\\")`, 362 "strings.Join($_, `\\`)", 363 ). 364 Report(`did you mean path.Join() or filepath.Join() ?`) 365 } 366 367 func readfull(m fluent.Matcher) { 368 m.Match(`$n, $err := io.ReadFull($_, $slice) 369 if $err != nil || $n != len($slice) { 370 $*_ 371 }`, 372 `$n, $err := io.ReadFull($_, $slice) 373 if $n != len($slice) || $err != nil { 374 $*_ 375 }`, 376 `$n, $err = io.ReadFull($_, $slice) 377 if $err != nil || $n != len($slice) { 378 $*_ 379 }`, 380 `$n, $err = io.ReadFull($_, $slice) 381 if $n != len($slice) || $err != nil { 382 $*_ 383 }`, 384 `if $n, $err := io.ReadFull($_, $slice); $n != len($slice) || $err != nil { 385 $*_ 386 }`, 387 `if $n, $err := io.ReadFull($_, $slice); $err != nil || $n != len($slice) { 388 $*_ 389 }`, 390 `if $n, $err = io.ReadFull($_, $slice); $n != len($slice) || $err != nil { 391 $*_ 392 }`, 393 `if $n, $err = io.ReadFull($_, $slice); $err != nil || $n != len($slice) { 394 $*_ 395 }`, 396 ).Report("io.ReadFull() returns err == nil iff n == len(slice)") 397 } 398 399 func nilerr(m fluent.Matcher) { 400 m.Match( 401 `if err == nil { return err }`, 402 `if err == nil { return $*_, err }`, 403 ). 404 Report(`return nil error instead of nil value`) 405 406 } 407 408 func mailaddress(m fluent.Matcher) { 409 m.Match( 410 "fmt.Sprintf(`\"%s\" <%s>`, $NAME, $EMAIL)", 411 "fmt.Sprintf(`\"%s\"<%s>`, $NAME, $EMAIL)", 412 "fmt.Sprintf(`%s <%s>`, $NAME, $EMAIL)", 413 "fmt.Sprintf(`%s<%s>`, $NAME, $EMAIL)", 414 `fmt.Sprintf("\"%s\"<%s>", $NAME, $EMAIL)`, 415 `fmt.Sprintf("\"%s\" <%s>", $NAME, $EMAIL)`, 416 `fmt.Sprintf("%s<%s>", $NAME, $EMAIL)`, 417 `fmt.Sprintf("%s <%s>", $NAME, $EMAIL)`, 418 ). 419 Report("use net/mail Address.String() instead of fmt.Sprintf()"). 420 Suggest("(&mail.Address{Name:$NAME, Address:$EMAIL}).String()") 421 422 } 423 424 func errnetclosed(m fluent.Matcher) { 425 m.Match( 426 `strings.Contains($err.Error(), $text)`, 427 ). 428 Where(m["text"].Text.Matches("\".*closed network connection.*\"")). 429 Report(`String matching against error texts is fragile; use net.ErrClosed instead`). 430 Suggest(`errors.Is($err, net.ErrClosed)`) 431 432 } 433 434 func httpheaderadd(m fluent.Matcher) { 435 m.Match( 436 `$H.Add($KEY, $VALUE)`, 437 ). 438 Where(m["H"].Type.Is("http.Header")). 439 Report("use http.Header.Set method instead of Add to overwrite all existing header values"). 440 Suggest(`$H.Set($KEY, $VALUE)`) 441 } 442 443 func hmacnew(m fluent.Matcher) { 444 m.Match("hmac.New(func() hash.Hash { return $x }, $_)", 445 `$f := func() hash.Hash { return $x } 446 $*_ 447 hmac.New($f, $_)`, 448 ).Where(m["x"].Pure). 449 Report("invalid hash passed to hmac.New()") 450 } 451 452 func writestring(m fluent.Matcher) { 453 m.Match(`io.WriteString($w, string($b))`). 454 Where(m["b"].Type.Is("[]byte")). 455 Suggest("$w.Write($b)") 456 } 457 458 func badlock(m fluent.Matcher) { 459 // Shouldn't give many false positives without type filter 460 // as Lock+Unlock pairs in combination with defer gives us pretty 461 // a good chance to guess correctly. If we constrain the type to sync.Mutex 462 // then it'll be harder to match embedded locks and custom methods 463 // that may forward the call to the sync.Mutex (or other synchronization primitive). 464 465 m.Match(`$mu.Lock(); defer $mu.RUnlock()`).Report(`maybe $mu.RLock() was intended?`) 466 m.Match(`$mu.RLock(); defer $mu.Unlock()`).Report(`maybe $mu.Lock() was intended?`) 467 }