add ruleguard support, fix all the reported issues (#10335)
parent
bc2ebe0021
commit
caad314faa
@ -0,0 +1,80 @@ |
||||
// +build linux,arm linux,386
|
||||
|
||||
/* |
||||
* MinIO Cloud Storage, (C) 2020 MinIO, Inc. |
||||
* |
||||
* 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 |
||||
* |
||||
* http://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. |
||||
*/ |
||||
|
||||
package disk |
||||
|
||||
import ( |
||||
"fmt" |
||||
"strconv" |
||||
"syscall" |
||||
) |
||||
|
||||
// fsType2StringMap - list of filesystems supported on linux
|
||||
var fsType2StringMap = map[string]string{ |
||||
"1021994": "TMPFS", |
||||
"137d": "EXT", |
||||
"4244": "HFS", |
||||
"4d44": "MSDOS", |
||||
"52654973": "REISERFS", |
||||
"5346544e": "NTFS", |
||||
"58465342": "XFS", |
||||
"61756673": "AUFS", |
||||
"6969": "NFS", |
||||
"ef51": "EXT2OLD", |
||||
"ef53": "EXT4", |
||||
"f15f": "ecryptfs", |
||||
"794c7630": "overlayfs", |
||||
"2fc12fc1": "zfs", |
||||
"ff534d42": "cifs", |
||||
"53464846": "wslfs", |
||||
} |
||||
|
||||
// getFSType returns the filesystem type of the underlying mounted filesystem
|
||||
func getFSType(ftype int32) string { |
||||
fsTypeHex := strconv.FormatInt(int64(ftype), 16) |
||||
fsTypeString, ok := fsType2StringMap[fsTypeHex] |
||||
if !ok { |
||||
return "UNKNOWN" |
||||
} |
||||
return fsTypeString |
||||
} |
||||
|
||||
// GetInfo returns total and free bytes available in a directory, e.g. `/`.
|
||||
func GetInfo(path string) (info Info, err error) { |
||||
s := syscall.Statfs_t{} |
||||
err = syscall.Statfs(path, &s) |
||||
if err != nil { |
||||
return Info{}, err |
||||
} |
||||
reservedBlocks := s.Bfree - s.Bavail |
||||
info = Info{ |
||||
Total: uint64(s.Frsize) * (s.Blocks - reservedBlocks), |
||||
Free: uint64(s.Frsize) * s.Bavail, |
||||
Files: s.Files, |
||||
Ffree: s.Ffree, |
||||
FSType: getFSType(s.Type), |
||||
} |
||||
// Check for overflows.
|
||||
// https://github.com/minio/minio/issues/8035
|
||||
// XFS can show wrong values at times error out
|
||||
// in such scenarios.
|
||||
if info.Free > info.Total { |
||||
return info, fmt.Errorf("detected free space (%d) > total disk space (%d), fs corruption at (%s). please run 'fsck'", info.Free, info.Total, path) |
||||
} |
||||
return info, nil |
||||
} |
@ -0,0 +1,80 @@ |
||||
// +build linux,s390x
|
||||
|
||||
/* |
||||
* MinIO Cloud Storage, (C) 2020 MinIO, Inc. |
||||
* |
||||
* 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 |
||||
* |
||||
* http://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. |
||||
*/ |
||||
|
||||
package disk |
||||
|
||||
import ( |
||||
"fmt" |
||||
"strconv" |
||||
"syscall" |
||||
) |
||||
|
||||
// fsType2StringMap - list of filesystems supported on linux
|
||||
var fsType2StringMap = map[string]string{ |
||||
"1021994": "TMPFS", |
||||
"137d": "EXT", |
||||
"4244": "HFS", |
||||
"4d44": "MSDOS", |
||||
"52654973": "REISERFS", |
||||
"5346544e": "NTFS", |
||||
"58465342": "XFS", |
||||
"61756673": "AUFS", |
||||
"6969": "NFS", |
||||
"ef51": "EXT2OLD", |
||||
"ef53": "EXT4", |
||||
"f15f": "ecryptfs", |
||||
"794c7630": "overlayfs", |
||||
"2fc12fc1": "zfs", |
||||
"ff534d42": "cifs", |
||||
"53464846": "wslfs", |
||||
} |
||||
|
||||
// getFSType returns the filesystem type of the underlying mounted filesystem
|
||||
func getFSType(ftype uint32) string { |
||||
fsTypeHex := strconv.FormatUint(uint64(ftype), 16) |
||||
fsTypeString, ok := fsType2StringMap[fsTypeHex] |
||||
if !ok { |
||||
return "UNKNOWN" |
||||
} |
||||
return fsTypeString |
||||
} |
||||
|
||||
// GetInfo returns total and free bytes available in a directory, e.g. `/`.
|
||||
func GetInfo(path string) (info Info, err error) { |
||||
s := syscall.Statfs_t{} |
||||
err = syscall.Statfs(path, &s) |
||||
if err != nil { |
||||
return Info{}, err |
||||
} |
||||
reservedBlocks := s.Bfree - s.Bavail |
||||
info = Info{ |
||||
Total: uint64(s.Frsize) * (s.Blocks - reservedBlocks), |
||||
Free: uint64(s.Frsize) * s.Bavail, |
||||
Files: s.Files, |
||||
Ffree: s.Ffree, |
||||
FSType: getFSType(s.Type), |
||||
} |
||||
// Check for overflows.
|
||||
// https://github.com/minio/minio/issues/8035
|
||||
// XFS can show wrong values at times error out
|
||||
// in such scenarios.
|
||||
if info.Free > info.Total { |
||||
return info, fmt.Errorf("detected free space (%d) > total disk space (%d), fs corruption at (%s). please run 'fsck'", info.Free, info.Total, path) |
||||
} |
||||
return info, nil |
||||
} |
@ -0,0 +1,86 @@ |
||||
// +build linux,arm linux,386
|
||||
|
||||
/* |
||||
* MinIO Cloud Storage, (C) 2020 MinIO, Inc. |
||||
* |
||||
* 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 |
||||
* |
||||
* http://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. |
||||
*/ |
||||
|
||||
package sys |
||||
|
||||
import ( |
||||
"os" |
||||
"syscall" |
||||
|
||||
"github.com/minio/minio/pkg/cgroup" |
||||
) |
||||
|
||||
// Get the final system memory limit chosen by the user.
|
||||
// by default without any configuration on a vanilla Linux
|
||||
// system you would see physical RAM limit. If cgroup
|
||||
// is configured at some point in time this function
|
||||
// would return the memory limit chosen for the given pid.
|
||||
func getMemoryLimit() (sysLimit uint64, err error) { |
||||
if sysLimit, err = getSysinfoMemoryLimit(); err != nil { |
||||
// Physical memory info is not accessible, just exit here.
|
||||
return 0, err |
||||
} |
||||
|
||||
// Following code is deliberately ignoring the error.
|
||||
cGroupLimit, gerr := cgroup.GetMemoryLimit(os.Getpid()) |
||||
if gerr != nil { |
||||
// Upon error just return system limit.
|
||||
return sysLimit, nil |
||||
} |
||||
|
||||
// cgroup limit is lesser than system limit means
|
||||
// user wants to limit the memory usage further
|
||||
// treat cgroup limit as the system limit.
|
||||
if cGroupLimit <= sysLimit { |
||||
sysLimit = cGroupLimit |
||||
} |
||||
|
||||
// Final system limit.
|
||||
return sysLimit, nil |
||||
|
||||
} |
||||
|
||||
// Get physical RAM size of the node.
|
||||
func getSysinfoMemoryLimit() (limit uint64, err error) { |
||||
var si syscall.Sysinfo_t |
||||
if err = syscall.Sysinfo(&si); err != nil { |
||||
return 0, err |
||||
} |
||||
|
||||
// Some fields in syscall.Sysinfo_t have different integer sizes
|
||||
// in different platform architectures. Cast all fields to uint64.
|
||||
unit := si.Unit |
||||
totalRAM := si.Totalram |
||||
|
||||
// Total RAM is always the multiplicative value
|
||||
// of unit size and total ram.
|
||||
return uint64(unit) * uint64(totalRAM), nil |
||||
} |
||||
|
||||
// GetStats - return system statistics, currently only
|
||||
// supported value is TotalRAM.
|
||||
func GetStats() (stats Stats, err error) { |
||||
var limit uint64 |
||||
limit, err = getMemoryLimit() |
||||
if err != nil { |
||||
return Stats{}, err |
||||
} |
||||
|
||||
stats.TotalRAM = limit |
||||
return stats, nil |
||||
} |
@ -0,0 +1,420 @@ |
||||
// +build ignore
|
||||
|
||||
package gorules |
||||
|
||||
import "github.com/quasilyte/go-ruleguard/dsl/fluent" |
||||
|
||||
// This is a collection of rules for ruleguard: https://github.com/quasilyte/go-ruleguard
|
||||
|
||||
// Remove extra conversions: mdempsky/unconvert
|
||||
func unconvert(m fluent.Matcher) { |
||||
m.Match("int($x)").Where(m["x"].Type.Is("int") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") |
||||
|
||||
m.Match("float32($x)").Where(m["x"].Type.Is("float32") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") |
||||
m.Match("float64($x)").Where(m["x"].Type.Is("float64") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") |
||||
|
||||
// m.Match("byte($x)").Where(m["x"].Type.Is("byte")).Report("unnecessary conversion").Suggest("$x")
|
||||
// m.Match("rune($x)").Where(m["x"].Type.Is("rune")).Report("unnecessary conversion").Suggest("$x")
|
||||
m.Match("bool($x)").Where(m["x"].Type.Is("bool") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") |
||||
|
||||
m.Match("int8($x)").Where(m["x"].Type.Is("int8") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") |
||||
m.Match("int16($x)").Where(m["x"].Type.Is("int16") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") |
||||
m.Match("int32($x)").Where(m["x"].Type.Is("int32") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") |
||||
m.Match("int64($x)").Where(m["x"].Type.Is("int64") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") |
||||
|
||||
m.Match("uint8($x)").Where(m["x"].Type.Is("uint8") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") |
||||
m.Match("uint16($x)").Where(m["x"].Type.Is("uint16") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") |
||||
m.Match("uint32($x)").Where(m["x"].Type.Is("uint32") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") |
||||
m.Match("uint64($x)").Where(m["x"].Type.Is("uint64") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x") |
||||
|
||||
m.Match("time.Duration($x)").Where(m["x"].Type.Is("time.Duration") && !m["x"].Text.Matches("^[0-9]*$")).Report("unnecessary conversion").Suggest("$x") |
||||
} |
||||
|
||||
// Don't use == or != with time.Time
|
||||
// https://github.com/dominikh/go-tools/issues/47 : Wontfix
|
||||
func timeeq(m fluent.Matcher) { |
||||
m.Match("$t0 == $t1").Where(m["t0"].Type.Is("time.Time")).Report("using == with time.Time") |
||||
m.Match("$t0 != $t1").Where(m["t0"].Type.Is("time.Time")).Report("using != with time.Time") |
||||
} |
||||
|
||||
// Wrong err in error check
|
||||
func wrongerr(m fluent.Matcher) { |
||||
m.Match("if $*_, $err0 := $*_; $err1 != nil { $*_ }"). |
||||
Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("if $*_, $err0 := $*_; $err1 != nil { $*_ }"). |
||||
Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("if $*_, $err0 = $*_; $err1 != nil { $*_ }"). |
||||
Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("if $*_, $err0 = $*_; $err1 != nil { $*_ }"). |
||||
Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("if $*_, $err0 := $*_; $err1 == nil { $*_ }"). |
||||
Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("if $*_, $err0 := $*_; $err1 == nil { $*_ }"). |
||||
Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("if $*_, $err0 = $*_; $err1 == nil { $*_ }"). |
||||
Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("if $*_, $err0 = $*_; $err1 == nil { $*_ }"). |
||||
Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("$*_, $err0 := $*_; if $err1 != nil { $*_ }"). |
||||
Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("$*_, $err0 := $*_; if $err1 != nil { $*_ }"). |
||||
Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("$*_, $err0 := $*_; if $err1 == nil { $*_ }"). |
||||
Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("$*_, $err0 := $*_; if $err1 == nil { $*_ }"). |
||||
Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("$*_, $err0 = $*_; if $err1 != nil { $*_ }"). |
||||
Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("$*_, $err0 = $*_; if $err1 != nil { $*_ }"). |
||||
Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("$*_, $err0 = $*_; if $err1 == nil { $*_ }"). |
||||
Where(m["err0"].Text == "err" && m["err0"].Type.Is("error") && m["err1"].Text != "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
|
||||
m.Match("$*_, $err0 = $*_; if $err1 == nil { $*_ }"). |
||||
Where(m["err0"].Text != "err" && m["err0"].Type.Is("error") && m["err1"].Text == "err" && m["err1"].Type.Is("error")). |
||||
Report("maybe wrong err in error check") |
||||
} |
||||
|
||||
// err but no an error
|
||||
func errnoterror(m fluent.Matcher) { |
||||
|
||||
// Would be easier to check for all err identifiers instead, but then how do we get the type from m[] ?
|
||||
|
||||
m.Match( |
||||
"if $*_, err := $x; $err != nil { $*_ } else if $_ { $*_ }", |
||||
"if $*_, err := $x; $err != nil { $*_ } else { $*_ }", |
||||
"if $*_, err := $x; $err != nil { $*_ }", |
||||
|
||||
"if $*_, err = $x; $err != nil { $*_ } else if $_ { $*_ }", |
||||
"if $*_, err = $x; $err != nil { $*_ } else { $*_ }", |
||||
"if $*_, err = $x; $err != nil { $*_ }", |
||||
|
||||
"$*_, err := $x; if $err != nil { $*_ } else if $_ { $*_ }", |
||||
"$*_, err := $x; if $err != nil { $*_ } else { $*_ }", |
||||
"$*_, err := $x; if $err != nil { $*_ }", |
||||
|
||||
"$*_, err = $x; if $err != nil { $*_ } else if $_ { $*_ }", |
||||
"$*_, err = $x; if $err != nil { $*_ } else { $*_ }", |
||||
"$*_, err = $x; if $err != nil { $*_ }", |
||||
). |
||||
Where(m["err"].Text == "err" && !m["err"].Type.Is("error") && m["x"].Text != "recover()"). |
||||
Report("err variable not error type") |
||||
} |
||||
|
||||
// Identical if and else bodies
|
||||
func ifbodythenbody(m fluent.Matcher) { |
||||
m.Match("if $*_ { $body } else { $body }"). |
||||
Report("identical if and else bodies") |
||||
|
||||
// Lots of false positives.
|
||||
// m.Match("if $*_ { $body } else if $*_ { $body }").
|
||||
// Report("identical if and else bodies")
|
||||
} |
||||
|
||||
// Odd inequality: A - B < 0 instead of !=
|
||||
// Too many false positives.
|
||||
/* |
||||
func subtractnoteq(m fluent.Matcher) { |
||||
m.Match("$a - $b < 0").Report("consider $a != $b") |
||||
m.Match("$a - $b > 0").Report("consider $a != $b") |
||||
m.Match("0 < $a - $b").Report("consider $a != $b") |
||||
m.Match("0 > $a - $b").Report("consider $a != $b") |
||||
} |
||||
*/ |
||||
|
||||
// Self-assignment
|
||||
func selfassign(m fluent.Matcher) { |
||||
m.Match("$x = $x").Report("useless self-assignment") |
||||
} |
||||
|
||||
// Odd nested ifs
|
||||
func oddnestedif(m fluent.Matcher) { |
||||
m.Match("if $x { if $x { $*_ }; $*_ }", |
||||
"if $x == $y { if $x != $y {$*_ }; $*_ }", |
||||
"if $x != $y { if $x == $y {$*_ }; $*_ }", |
||||
"if $x { if !$x { $*_ }; $*_ }", |
||||
"if !$x { if $x { $*_ }; $*_ }"). |
||||
Report("odd nested ifs") |
||||
|
||||
m.Match("for $x { if $x { $*_ }; $*_ }", |
||||
"for $x == $y { if $x != $y {$*_ }; $*_ }", |
||||
"for $x != $y { if $x == $y {$*_ }; $*_ }", |
||||
"for $x { if !$x { $*_ }; $*_ }", |
||||
"for !$x { if $x { $*_ }; $*_ }"). |
||||
Report("odd nested for/ifs") |
||||
} |
||||
|
||||
// odd bitwise expressions
|
||||
func oddbitwise(m fluent.Matcher) { |
||||
m.Match("$x | $x", |
||||
"$x | ^$x", |
||||
"^$x | $x"). |
||||
Report("odd bitwise OR") |
||||
|
||||
m.Match("$x & $x", |
||||
"$x & ^$x", |
||||
"^$x & $x"). |
||||
Report("odd bitwise AND") |
||||
|
||||
m.Match("$x &^ $x"). |
||||
Report("odd bitwise AND-NOT") |
||||
} |
||||
|
||||
// odd sequence of if tests with return
|
||||
func ifreturn(m fluent.Matcher) { |
||||
m.Match("if $x { return $*_ }; if $x {$*_ }").Report("odd sequence of if test") |
||||
m.Match("if $x { return $*_ }; if !$x {$*_ }").Report("odd sequence of if test") |
||||
m.Match("if !$x { return $*_ }; if $x {$*_ }").Report("odd sequence of if test") |
||||
m.Match("if $x == $y { return $*_ }; if $x != $y {$*_ }").Report("odd sequence of if test") |
||||
m.Match("if $x != $y { return $*_ }; if $x == $y {$*_ }").Report("odd sequence of if test") |
||||
|
||||
} |
||||
|
||||
func oddifsequence(m fluent.Matcher) { |
||||
/* |
||||
m.Match("if $x { $*_ }; if $x {$*_ }").Report("odd sequence of if test") |
||||
|
||||
m.Match("if $x == $y { $*_ }; if $y == $x {$*_ }").Report("odd sequence of if tests") |
||||
m.Match("if $x != $y { $*_ }; if $y != $x {$*_ }").Report("odd sequence of if tests") |
||||
|
||||
m.Match("if $x < $y { $*_ }; if $y > $x {$*_ }").Report("odd sequence of if tests") |
||||
m.Match("if $x <= $y { $*_ }; if $y >= $x {$*_ }").Report("odd sequence of if tests") |
||||
|
||||
m.Match("if $x > $y { $*_ }; if $y < $x {$*_ }").Report("odd sequence of if tests") |
||||
m.Match("if $x >= $y { $*_ }; if $y <= $x {$*_ }").Report("odd sequence of if tests") |
||||
*/ |
||||
} |
||||
|
||||
// odd sequence of nested if tests
|
||||
func nestedifsequence(m fluent.Matcher) { |
||||
/* |
||||
m.Match("if $x < $y { if $x >= $y {$*_ }; $*_ }").Report("odd sequence of nested if tests") |
||||
m.Match("if $x <= $y { if $x > $y {$*_ }; $*_ }").Report("odd sequence of nested if tests") |
||||
m.Match("if $x > $y { if $x <= $y {$*_ }; $*_ }").Report("odd sequence of nested if tests") |
||||
m.Match("if $x >= $y { if $x < $y {$*_ }; $*_ }").Report("odd sequence of nested if tests") |
||||
*/ |
||||
} |
||||
|
||||
// odd sequence of assignments
|
||||
func identicalassignments(m fluent.Matcher) { |
||||
m.Match("$x = $y; $y = $x").Report("odd sequence of assignments") |
||||
} |
||||
|
||||
func oddcompoundop(m fluent.Matcher) { |
||||
m.Match("$x += $x + $_", |
||||
"$x += $x - $_"). |
||||
Report("odd += expression") |
||||
|
||||
m.Match("$x -= $x + $_", |
||||
"$x -= $x - $_"). |
||||
Report("odd -= expression") |
||||
} |
||||
|
||||
func constswitch(m fluent.Matcher) { |
||||
m.Match("switch $x { $*_ }", "switch $*_; $x { $*_ }"). |
||||
Where(m["x"].Const && !m["x"].Text.Matches(`^runtime\.`)). |
||||
Report("constant switch") |
||||
} |
||||
|
||||
func oddcomparisons(m fluent.Matcher) { |
||||
m.Match( |
||||
"$x - $y == 0", |
||||
"$x - $y != 0", |
||||
"$x - $y < 0", |
||||
"$x - $y <= 0", |
||||
"$x - $y > 0", |
||||
"$x - $y >= 0", |
||||
"$x ^ $y == 0", |
||||
"$x ^ $y != 0", |
||||
).Report("odd comparison") |
||||
} |
||||
|
||||
func oddmathbits(m fluent.Matcher) { |
||||
m.Match( |
||||
"64 - bits.LeadingZeros64($x)", |
||||
"32 - bits.LeadingZeros32($x)", |
||||
"16 - bits.LeadingZeros16($x)", |
||||
"8 - bits.LeadingZeros8($x)", |
||||
).Report("odd math/bits expression: use bits.Len*() instead?") |
||||
} |
||||
|
||||
func floateq(m fluent.Matcher) { |
||||
m.Match( |
||||
"$x == $y", |
||||
"$x != $y", |
||||
). |
||||
Where(m["x"].Type.Is("float32") && !m["x"].Const && !m["y"].Text.Matches("0(.0+)?")). |
||||
Report("floating point tested for equality") |
||||
|
||||
m.Match( |
||||
"$x == $y", |
||||
"$x != $y", |
||||
). |
||||
Where(m["x"].Type.Is("float64") && !m["x"].Const && !m["y"].Text.Matches("0(.0+)?")). |
||||
Report("floating point tested for equality") |
||||
|
||||
m.Match("switch $x { $*_ }", "switch $*_; $x { $*_ }"). |
||||
Where(m["x"].Type.Is("float32")). |
||||
Report("floating point as switch expression") |
||||
|
||||
m.Match("switch $x { $*_ }", "switch $*_; $x { $*_ }"). |
||||
Where(m["x"].Type.Is("float64")). |
||||
Report("floating point as switch expression") |
||||
|
||||
} |
||||
|
||||
func badexponent(m fluent.Matcher) { |
||||
m.Match( |
||||
"2 ^ $x", |
||||
"10 ^ $x", |
||||
). |
||||
Report("caret (^) is not exponentiation") |
||||
} |
||||
|
||||
func floatloop(m fluent.Matcher) { |
||||
m.Match( |
||||
"for $i := $x; $i < $y; $i += $z { $*_ }", |
||||
"for $i = $x; $i < $y; $i += $z { $*_ }", |
||||
). |
||||
Where(m["i"].Type.Is("float64")). |
||||
Report("floating point for loop counter") |
||||
|
||||
m.Match( |
||||
"for $i := $x; $i < $y; $i += $z { $*_ }", |
||||
"for $i = $x; $i < $y; $i += $z { $*_ }", |
||||
). |
||||
Where(m["i"].Type.Is("float32")). |
||||
Report("floating point for loop counter") |
||||
} |
||||
|
||||
func urlredacted(m fluent.Matcher) { |
||||
|
||||
m.Match( |
||||
"log.Println($x, $*_)", |
||||
"log.Println($*_, $x, $*_)", |
||||
"log.Println($*_, $x)", |
||||
"log.Printf($*_, $x, $*_)", |
||||
"log.Printf($*_, $x)", |
||||
|
||||
"log.Println($x, $*_)", |
||||
"log.Println($*_, $x, $*_)", |
||||
"log.Println($*_, $x)", |
||||
"log.Printf($*_, $x, $*_)", |
||||
"log.Printf($*_, $x)", |
||||
). |
||||
Where(m["x"].Type.Is("*url.URL")). |
||||
Report("consider $x.Redacted() when outputting URLs") |
||||
} |
||||
|
||||
func sprinterr(m fluent.Matcher) { |
||||
m.Match(`fmt.Sprint($err)`, |
||||
`fmt.Sprintf("%s", $err)`, |
||||
`fmt.Sprintf("%v", $err)`, |
||||
). |
||||
Where(m["err"].Type.Is("error")). |
||||
Report("maybe call $err.Error() instead of fmt.Sprint()?") |
||||
|
||||
} |
||||
|
||||
func largeloopcopy(m fluent.Matcher) { |
||||
m.Match( |
||||
`for $_, $v := range $_ { $*_ }`, |
||||
). |
||||
Where(m["v"].Type.Size > 512). |
||||
Report(`loop copies large value each iteration`) |
||||
} |
||||
|
||||
func joinpath(m fluent.Matcher) { |
||||
m.Match( |
||||
`strings.Join($_, "/")`, |
||||
`strings.Join($_, "\\")`, |
||||
"strings.Join($_, `\\`)", |
||||
). |
||||
Report(`did you mean path.Join() or filepath.Join() ?`) |
||||
} |
||||
|
||||
func readfull(m fluent.Matcher) { |
||||
m.Match(`$n, $err := io.ReadFull($_, $slice) |
||||
if $err != nil || $n != len($slice) { |
||||
$*_ |
||||
}`, |
||||
`$n, $err := io.ReadFull($_, $slice) |
||||
if $n != len($slice) || $err != nil { |
||||
$*_ |
||||
}`, |
||||
`$n, $err = io.ReadFull($_, $slice) |
||||
if $err != nil || $n != len($slice) { |
||||
$*_ |
||||
}`, |
||||
`$n, $err = io.ReadFull($_, $slice) |
||||
if $n != len($slice) || $err != nil { |
||||
$*_ |
||||
}`, |
||||
`if $n, $err := io.ReadFull($_, $slice); $n != len($slice) || $err != nil { |
||||
$*_ |
||||
}`, |
||||
`if $n, $err := io.ReadFull($_, $slice); $err != nil || $n != len($slice) { |
||||
$*_ |
||||
}`, |
||||
`if $n, $err = io.ReadFull($_, $slice); $n != len($slice) || $err != nil { |
||||
$*_ |
||||
}`, |
||||
`if $n, $err = io.ReadFull($_, $slice); $err != nil || $n != len($slice) { |
||||
$*_ |
||||
}`, |
||||
).Report("io.ReadFull() returns err == nil iff n == len(slice)") |
||||
} |
||||
|
||||
func nilerr(m fluent.Matcher) { |
||||
m.Match( |
||||
`if err == nil { return err }`, |
||||
`if err == nil { return $*_, err }`, |
||||
). |
||||
Report(`return nil error instead of nil value`) |
||||
|
||||
} |
||||
|
||||
func mailaddress(m fluent.Matcher) { |
||||
m.Match( |
||||
"fmt.Sprintf(`\"%s\" <%s>`, $NAME, $EMAIL)", |
||||
"fmt.Sprintf(`\"%s\"<%s>`, $NAME, $EMAIL)", |
||||
"fmt.Sprintf(`%s <%s>`, $NAME, $EMAIL)", |
||||
"fmt.Sprintf(`%s<%s>`, $NAME, $EMAIL)", |
||||
`fmt.Sprintf("\"%s\"<%s>", $NAME, $EMAIL)`, |
||||
`fmt.Sprintf("\"%s\" <%s>", $NAME, $EMAIL)`, |
||||
`fmt.Sprintf("%s<%s>", $NAME, $EMAIL)`, |
||||
`fmt.Sprintf("%s <%s>", $NAME, $EMAIL)`, |
||||
). |
||||
Report("use net/mail Address.String() instead of fmt.Sprintf()"). |
||||
Suggest("(&mail.Address{Name:$NAME, Address:$EMAIL}).String()") |
||||
|
||||
} |
Loading…
Reference in new issue