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  }