Merge branch 'master' into lukeshu/reproduce
This commit is contained in:
commit
66d4a16c6a
@ -61,6 +61,6 @@ issues:
|
||||
# golangci.com configuration
|
||||
# https://github.com/golangci/golangci/wiki/Configuration
|
||||
service:
|
||||
golangci-lint-version: 1.15.x # use the fixed version to not introduce new linters unexpectedly
|
||||
golangci-lint-version: 1.16.x # use the fixed version to not introduce new linters unexpectedly
|
||||
prepare:
|
||||
- echo "here I can run custom commands, but no preparation needed for this repo"
|
||||
|
11
README.md
11
README.md
@ -204,6 +204,7 @@ and the following linters are disabled by default:
|
||||
$ golangci-lint help linters
|
||||
...
|
||||
Disabled by default linters:
|
||||
bodyclose: checks whether HTTP response body is closed successfully [fast: false, auto-fix: false]
|
||||
depguard: Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false]
|
||||
dupl: Tool for code clone detection [fast: true, auto-fix: false]
|
||||
gochecknoglobals: Checks that no globals are present in Go code [fast: true, auto-fix: false]
|
||||
@ -344,7 +345,7 @@ Read [this section](#internals) for details.
|
||||
|
||||
### Memory Usage of Golangci-lint
|
||||
|
||||
A trade-off between memory usage and execution time can be controlled by [`GOCC`](https://golang.org/pkg/runtime/#hdr-Environment_Variables) environment variable.
|
||||
A trade-off between memory usage and execution time can be controlled by [`GOGC`](https://golang.org/pkg/runtime/#hdr-Environment_Variables) environment variable.
|
||||
Less `GOGC` values trigger garbage collection more frequently and golangci-lint consumes less memory and more CPU. Below is the trade-off table for running on this repo:
|
||||
|
||||
|`GOGC`|Peak Memory, GB|Executon Time, s|
|
||||
@ -416,6 +417,7 @@ golangci-lint help linters
|
||||
|
||||
### Disabled By Default Linters (`-E/--enable`)
|
||||
|
||||
- [bodyclose](https://github.com/timakin/bodyclose) - checks whether HTTP response body is closed successfully
|
||||
- [golint](https://github.com/golang/lint) - Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
|
||||
- [stylecheck](https://github.com/dominikh/go-tools/tree/master/stylecheck) - Stylecheck is a replacement for golint
|
||||
- [gosec](https://github.com/securego/gosec) - Inspects source code for security problems
|
||||
@ -871,7 +873,7 @@ issues:
|
||||
# golangci.com configuration
|
||||
# https://github.com/golangci/golangci/wiki/Configuration
|
||||
service:
|
||||
golangci-lint-version: 1.15.x # use the fixed version to not introduce new linters unexpectedly
|
||||
golangci-lint-version: 1.16.x # use the fixed version to not introduce new linters unexpectedly
|
||||
prepare:
|
||||
- echo "here I can run custom commands, but no preparation needed for this repo"
|
||||
```
|
||||
@ -926,8 +928,8 @@ Short answer: go 1.11 and newer are oficially supported.
|
||||
Long answer:
|
||||
1. go < 1.9 isn't supported
|
||||
2. go 1.9 is supported by golangci-lint <= v1.10.2
|
||||
3. go 1.10 is oficially supported by golangci-lint <= 1.15.0.
|
||||
4. go1.11 and go1.12 are oficially supported by the latest version of golangci-lint.
|
||||
3. go 1.10 is officially supported by golangci-lint <= 1.15.0.
|
||||
4. go1.11 and go1.12 are officially supported by the latest version of golangci-lint.
|
||||
|
||||
**`golangci-lint` doesn't work**
|
||||
|
||||
@ -946,6 +948,7 @@ Thanks to [alecthomas/gometalinter](https://github.com/alecthomas/gometalinter)
|
||||
Thanks to [bradleyfalzon/revgrep](https://github.com/bradleyfalzon/revgrep) for cool diff tool.
|
||||
|
||||
Thanks to developers and authors of used linters:
|
||||
- [timakin](https://github.com/timakin)
|
||||
- [kisielk](https://github.com/kisielk)
|
||||
- [golang](https://github.com/golang)
|
||||
- [dominikh](https://github.com/dominikh)
|
||||
|
@ -313,7 +313,7 @@ Read [this section](#internals) for details.
|
||||
|
||||
### Memory Usage of Golangci-lint
|
||||
|
||||
A trade-off between memory usage and execution time can be controlled by [`GOCC`](https://golang.org/pkg/runtime/#hdr-Environment_Variables) environment variable.
|
||||
A trade-off between memory usage and execution time can be controlled by [`GOGC`](https://golang.org/pkg/runtime/#hdr-Environment_Variables) environment variable.
|
||||
Less `GOGC` values trigger garbage collection more frequently and golangci-lint consumes less memory and more CPU. Below is the trade-off table for running on this repo:
|
||||
|
||||
|`GOGC`|Peak Memory, GB|Executon Time, s|
|
||||
@ -475,8 +475,8 @@ Short answer: go 1.11 and newer are oficially supported.
|
||||
Long answer:
|
||||
1. go < 1.9 isn't supported
|
||||
2. go 1.9 is supported by golangci-lint <= v1.10.2
|
||||
3. go 1.10 is oficially supported by golangci-lint <= 1.15.0.
|
||||
4. go1.11 and go1.12 are oficially supported by the latest version of golangci-lint.
|
||||
3. go 1.10 is officially supported by golangci-lint <= 1.15.0.
|
||||
4. go1.11 and go1.12 are officially supported by the latest version of golangci-lint.
|
||||
|
||||
**`golangci-lint` doesn't work**
|
||||
|
||||
|
1
go.mod
1
go.mod
@ -50,6 +50,7 @@ require (
|
||||
github.com/spf13/viper v1.0.2
|
||||
github.com/stretchr/testify v1.2.2
|
||||
github.com/valyala/quicktemplate v1.1.1
|
||||
github.com/timakin/bodyclose v0.0.0-20190407043127-4a873e97b2bb
|
||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977 // indirect
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313 // indirect
|
||||
|
6
go.sum
6
go.sum
@ -77,6 +77,8 @@ github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw=
|
||||
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
|
||||
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno=
|
||||
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
@ -160,6 +162,8 @@ github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk
|
||||
github.com/valyala/quicktemplate v1.1.1 h1:C58y/wN0FMTi2PR0n3onltemfFabany53j7M6SDDB8k=
|
||||
github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/timakin/bodyclose v0.0.0-20190407043127-4a873e97b2bb h1:lI9ufgFfvuqRctP9Ny8lDDLbSWCMxBPletcSqrnyFYM=
|
||||
github.com/timakin/bodyclose v0.0.0-20190407043127-4a873e97b2bb/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U=
|
||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@ -188,6 +192,8 @@ golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20181205014116-22934f0fdb62/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190420000508-685fecacd0a0 h1:pa1CyBALPFjblgkNQp7T7gEcFcG/GOG5Ck8IcnSVWGs=
|
||||
golang.org/x/tools v0.0.0-20190420000508-685fecacd0a0/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
|
||||
|
@ -172,7 +172,8 @@ log_crit() {
|
||||
uname_os() {
|
||||
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
case "$os" in
|
||||
msys_nt) os="windows" ;;
|
||||
msys_nt*) os="windows" ;;
|
||||
mingw*) os="windows" ;;
|
||||
esac
|
||||
echo "$os"
|
||||
}
|
||||
|
21
pkg/golinters/bodyclose.go
Normal file
21
pkg/golinters/bodyclose.go
Normal file
@ -0,0 +1,21 @@
|
||||
package golinters
|
||||
|
||||
import (
|
||||
"github.com/timakin/bodyclose/passes/bodyclose"
|
||||
"golang.org/x/tools/go/analysis"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
|
||||
)
|
||||
|
||||
func NewBodyclose() *goanalysis.Linter {
|
||||
analyzers := []*analysis.Analyzer{
|
||||
bodyclose.Analyzer,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
"bodyclose",
|
||||
"checks whether HTTP response body is closed successfully",
|
||||
analyzers,
|
||||
nil,
|
||||
)
|
||||
}
|
@ -89,6 +89,11 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
||||
WithSpeed(4).
|
||||
WithAlternativeNames("vet", "vetshadow").
|
||||
WithURL("https://golang.org/cmd/vet/"),
|
||||
linter.NewConfig(golinters.NewBodyclose()).
|
||||
WithSSA().
|
||||
WithPresets(linter.PresetPerformance, linter.PresetBugs).
|
||||
WithSpeed(4).
|
||||
WithURL("https://github.com/timakin/bodyclose"),
|
||||
linter.NewConfig(golinters.Errcheck{}).
|
||||
WithTypeInfo().
|
||||
WithPresets(linter.PresetBugs).
|
||||
|
12
test/testdata/bodyclose.go
vendored
Normal file
12
test/testdata/bodyclose.go
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
//args: -Ebodyclose
|
||||
package testdata
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func BodycloseNotClosed() {
|
||||
resp, _ := http.Get("https://google.com") // ERROR "bodyclose: response body must be closed"
|
||||
_, _ = ioutil.ReadAll(resp.Body)
|
||||
}
|
21
vendor/github.com/gostaticanalysis/analysisutil/LICENSE
generated
vendored
Normal file
21
vendor/github.com/gostaticanalysis/analysisutil/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 GoStaticAnalysis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
10
vendor/github.com/gostaticanalysis/analysisutil/README.md
generated
vendored
Normal file
10
vendor/github.com/gostaticanalysis/analysisutil/README.md
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
# analysisutil
|
||||
|
||||
[![godoc.org][godoc-badge]][godoc]
|
||||
|
||||
Utilities for x/tools/go/analysis package.
|
||||
|
||||
<!-- links -->
|
||||
[godoc]: https://godoc.org/github.com/gostaticanalysis/analysisutil
|
||||
[godoc-badge]: https://img.shields.io/badge/godoc-reference-4F73B3.svg?style=flat-square&label=%20godoc.org
|
||||
|
18
vendor/github.com/gostaticanalysis/analysisutil/file.go
generated
vendored
Normal file
18
vendor/github.com/gostaticanalysis/analysisutil/file.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package analysisutil
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
// File finds *ast.File in pass.Files by pos.
|
||||
func File(pass *analysis.Pass, pos token.Pos) *ast.File {
|
||||
for _, f := range pass.Files {
|
||||
if f.Pos() <= pos && pos <= f.End() {
|
||||
return f
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
5
vendor/github.com/gostaticanalysis/analysisutil/go.mod
generated
vendored
Normal file
5
vendor/github.com/gostaticanalysis/analysisutil/go.mod
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
module github.com/gostaticanalysis/analysisutil
|
||||
|
||||
go 1.12
|
||||
|
||||
require golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5
|
6
vendor/github.com/gostaticanalysis/analysisutil/go.sum
generated
vendored
Normal file
6
vendor/github.com/gostaticanalysis/analysisutil/go.sum
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5 h1:ZcPpqKMdoZeNQ/4GHlyY4COf8n8SmpPv6mcqF1+VPSM=
|
||||
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
26
vendor/github.com/gostaticanalysis/analysisutil/pkg.go
generated
vendored
Normal file
26
vendor/github.com/gostaticanalysis/analysisutil/pkg.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package analysisutil
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RemoVendor removes vendoring infomation from import path.
|
||||
func RemoveVendor(path string) string {
|
||||
i := strings.Index(path, "vendor")
|
||||
if i >= 0 {
|
||||
return path[i+len("vendor")+1:]
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// LookupFromImports finds an object from import paths.
|
||||
func LookupFromImports(imports []*types.Package, path, name string) types.Object {
|
||||
path = RemoveVendor(path)
|
||||
for i := range imports {
|
||||
if path == RemoveVendor(imports[i].Path()) {
|
||||
return imports[i].Scope().Lookup(name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
31
vendor/github.com/gostaticanalysis/analysisutil/ssa.go
generated
vendored
Normal file
31
vendor/github.com/gostaticanalysis/analysisutil/ssa.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
package analysisutil
|
||||
|
||||
import "golang.org/x/tools/go/ssa"
|
||||
|
||||
// IfInstr returns *ssa.If which is contained in the block b.
|
||||
// If the block b has not any if instruction, IfInstr returns nil.
|
||||
func IfInstr(b *ssa.BasicBlock) *ssa.If {
|
||||
if len(b.Instrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
ifinstr, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ifinstr
|
||||
}
|
||||
|
||||
// Phi returns phi values which are contained in the block b.
|
||||
func Phi(b *ssa.BasicBlock) (phis []*ssa.Phi) {
|
||||
for _, instr := range b.Instrs {
|
||||
if phi, ok := instr.(*ssa.Phi); ok {
|
||||
phis = append(phis, phi)
|
||||
} else {
|
||||
// no more phi
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
21
vendor/github.com/timakin/bodyclose/LICENSE
generated
vendored
Normal file
21
vendor/github.com/timakin/bodyclose/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Seiji Takahashi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
352
vendor/github.com/timakin/bodyclose/passes/bodyclose/bodyclose.go
generated
vendored
Normal file
352
vendor/github.com/timakin/bodyclose/passes/bodyclose/bodyclose.go
generated
vendored
Normal file
@ -0,0 +1,352 @@
|
||||
package bodyclose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gostaticanalysis/analysisutil"
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/buildssa"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "bodyclose",
|
||||
Doc: Doc,
|
||||
Run: new(runner).run,
|
||||
Requires: []*analysis.Analyzer{
|
||||
buildssa.Analyzer,
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
Doc = "bodyclose checks whether HTTP response body is closed successfully"
|
||||
|
||||
nethttpPath = "net/http"
|
||||
closeMethod = "Close"
|
||||
)
|
||||
|
||||
type runner struct {
|
||||
pass *analysis.Pass
|
||||
resObj types.Object
|
||||
resTyp *types.Pointer
|
||||
bodyObj types.Object
|
||||
closeMthd *types.Func
|
||||
skipFile map[*ast.File]bool
|
||||
}
|
||||
|
||||
func (r *runner) run(pass *analysis.Pass) (interface{}, error) {
|
||||
r.pass = pass
|
||||
funcs := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs
|
||||
|
||||
r.resObj = analysisutil.LookupFromImports(pass.Pkg.Imports(), nethttpPath, "Response")
|
||||
if r.resObj == nil {
|
||||
// skip checking
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
resNamed, ok := r.resObj.Type().(*types.Named)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot find http.Response")
|
||||
}
|
||||
r.resTyp = types.NewPointer(resNamed)
|
||||
|
||||
resStruct, ok := r.resObj.Type().Underlying().(*types.Struct)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("cannot find http.Response")
|
||||
}
|
||||
for i := 0; i < resStruct.NumFields(); i++ {
|
||||
field := resStruct.Field(i)
|
||||
if field.Id() == "Body" {
|
||||
r.bodyObj = field
|
||||
}
|
||||
}
|
||||
if r.bodyObj == nil {
|
||||
return nil, fmt.Errorf("cannot find the object http.Response.Body")
|
||||
}
|
||||
bodyNamed := r.bodyObj.Type().(*types.Named)
|
||||
bodyItrf := bodyNamed.Underlying().(*types.Interface)
|
||||
for i := 0; i < bodyItrf.NumMethods(); i++ {
|
||||
bmthd := bodyItrf.Method(i)
|
||||
if bmthd.Id() == closeMethod {
|
||||
r.closeMthd = bmthd
|
||||
}
|
||||
}
|
||||
|
||||
r.skipFile = map[*ast.File]bool{}
|
||||
for _, f := range funcs {
|
||||
if r.noImportedNetHTTP(f) {
|
||||
// skip this
|
||||
continue
|
||||
}
|
||||
|
||||
// skip if the function is just referenced
|
||||
var isreffunc bool
|
||||
for i := 0; i < f.Signature.Results().Len(); i++ {
|
||||
if f.Signature.Results().At(i).Type().String() == r.resTyp.String() {
|
||||
isreffunc = true
|
||||
}
|
||||
}
|
||||
if isreffunc {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, b := range f.Blocks {
|
||||
for i := range b.Instrs {
|
||||
pos := b.Instrs[i].Pos()
|
||||
if r.isopen(b, i) {
|
||||
pass.Reportf(pos, "response body must be closed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *runner) isopen(b *ssa.BasicBlock, i int) bool {
|
||||
call, ok := r.getReqCall(b.Instrs[i])
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(*call.Referrers()) == 0 {
|
||||
return true
|
||||
}
|
||||
cRefs := *call.Referrers()
|
||||
for _, cRef := range cRefs {
|
||||
val, ok := r.getResVal(cRef)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(*val.Referrers()) == 0 {
|
||||
return true
|
||||
}
|
||||
resRefs := *val.Referrers()
|
||||
for _, resRef := range resRefs {
|
||||
switch resRef := resRef.(type) {
|
||||
case *ssa.Store: // Call in Closure function
|
||||
if len(*resRef.Addr.Referrers()) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, aref := range *resRef.Addr.Referrers() {
|
||||
if c, ok := aref.(*ssa.MakeClosure); ok {
|
||||
f := c.Fn.(*ssa.Function)
|
||||
if r.noImportedNetHTTP(f) {
|
||||
// skip this
|
||||
return false
|
||||
}
|
||||
called := r.isClosureCalled(c)
|
||||
|
||||
return r.calledInFunc(f, called)
|
||||
}
|
||||
|
||||
}
|
||||
case *ssa.Call: // Indirect function call
|
||||
if f, ok := resRef.Call.Value.(*ssa.Function); ok {
|
||||
for _, b := range f.Blocks {
|
||||
for i := range b.Instrs {
|
||||
return r.isopen(b, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ssa.FieldAddr: // Normal reference to response entity
|
||||
if resRef.Referrers() == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
bRefs := *resRef.Referrers()
|
||||
|
||||
for _, bRef := range bRefs {
|
||||
bOp, ok := r.getBodyOp(bRef)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if len(*bOp.Referrers()) == 0 {
|
||||
return true
|
||||
}
|
||||
ccalls := *bOp.Referrers()
|
||||
for _, ccall := range ccalls {
|
||||
if r.isCloseCall(ccall) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *runner) getReqCall(instr ssa.Instruction) (*ssa.Call, bool) {
|
||||
call, ok := instr.(*ssa.Call)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
if !strings.Contains(call.Type().String(), r.resTyp.String()) {
|
||||
return nil, false
|
||||
}
|
||||
return call, true
|
||||
}
|
||||
|
||||
func (r *runner) getResVal(instr ssa.Instruction) (ssa.Value, bool) {
|
||||
switch instr := instr.(type) {
|
||||
case *ssa.FieldAddr:
|
||||
if instr.X.Type().String() == r.resTyp.String() {
|
||||
return instr.X.(ssa.Value), true
|
||||
}
|
||||
case ssa.Value:
|
||||
if instr.Type().String() == r.resTyp.String() {
|
||||
return instr, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (r *runner) getBodyOp(instr ssa.Instruction) (*ssa.UnOp, bool) {
|
||||
op, ok := instr.(*ssa.UnOp)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
if op.Type() != r.bodyObj.Type() {
|
||||
return nil, false
|
||||
}
|
||||
return op, true
|
||||
}
|
||||
|
||||
func (r *runner) isCloseCall(ccall ssa.Instruction) bool {
|
||||
switch ccall := ccall.(type) {
|
||||
case *ssa.Defer:
|
||||
if ccall.Call.Method.Name() == r.closeMthd.Name() {
|
||||
return true
|
||||
}
|
||||
case *ssa.Call:
|
||||
if ccall.Call.Method.Name() == r.closeMthd.Name() {
|
||||
return true
|
||||
}
|
||||
case *ssa.ChangeInterface:
|
||||
if ccall.Type().String() == "io.Closer" {
|
||||
closeMtd := ccall.Type().Underlying().(*types.Interface).Method(0)
|
||||
crs := *ccall.Referrers()
|
||||
for _, cs := range crs {
|
||||
if cs, ok := cs.(*ssa.Defer); ok {
|
||||
if val, ok := cs.Common().Value.(*ssa.Function); ok {
|
||||
for _, b := range val.Blocks {
|
||||
for _, instr := range b.Instrs {
|
||||
if c, ok := instr.(*ssa.Call); ok {
|
||||
if c.Call.Method == closeMtd {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *runner) isClosureCalled(c *ssa.MakeClosure) bool {
|
||||
refs := *c.Referrers()
|
||||
if len(refs) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, ref := range refs {
|
||||
switch ref.(type) {
|
||||
case *ssa.Call, *ssa.Defer:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *runner) noImportedNetHTTP(f *ssa.Function) (ret bool) {
|
||||
obj := f.Object()
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
file := analysisutil.File(r.pass, obj.Pos())
|
||||
if file == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if skip, has := r.skipFile[file]; has {
|
||||
return skip
|
||||
}
|
||||
defer func() {
|
||||
r.skipFile[file] = ret
|
||||
}()
|
||||
|
||||
for _, impt := range file.Imports {
|
||||
path, err := strconv.Unquote(impt.Path.Value)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
path = analysisutil.RemoveVendor(path)
|
||||
if path == nethttpPath {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *runner) calledInFunc(f *ssa.Function, called bool) bool {
|
||||
for _, b := range f.Blocks {
|
||||
for i, instr := range b.Instrs {
|
||||
switch instr := instr.(type) {
|
||||
case *ssa.UnOp:
|
||||
refs := *instr.Referrers()
|
||||
if len(refs) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, r := range refs {
|
||||
if v, ok := r.(ssa.Value); ok {
|
||||
if ptr, ok := v.Type().(*types.Pointer); !ok || !isNamedType(ptr.Elem(), "io", "ReadCloser") {
|
||||
return true
|
||||
}
|
||||
vrefs := *v.Referrers()
|
||||
for _, vref := range vrefs {
|
||||
if vref, ok := vref.(*ssa.UnOp); ok {
|
||||
vrefs := *vref.Referrers()
|
||||
if len(vrefs) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, vref := range vrefs {
|
||||
if c, ok := vref.(*ssa.Call); ok {
|
||||
if c.Call.Method.Name() == closeMethod {
|
||||
return !called
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
default:
|
||||
return r.isopen(b, i) || !called
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isNamedType reports whether t is the named type path.name.
|
||||
func isNamedType(t types.Type, path, name string) bool {
|
||||
n, ok := t.(*types.Named)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
obj := n.Obj()
|
||||
return obj.Name() == name && obj.Pkg() != nil && obj.Pkg().Path() == path
|
||||
}
|
4
vendor/modules.txt
vendored
4
vendor/modules.txt
vendored
@ -106,6 +106,8 @@ github.com/golangci/prealloc
|
||||
github.com/golangci/revgrep
|
||||
# github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4
|
||||
github.com/golangci/unconvert
|
||||
# github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3
|
||||
github.com/gostaticanalysis/analysisutil
|
||||
# github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce
|
||||
github.com/hashicorp/hcl
|
||||
github.com/hashicorp/hcl/hcl/printer
|
||||
@ -183,6 +185,8 @@ github.com/stretchr/testify/require
|
||||
github.com/valyala/bytebufferpool
|
||||
# github.com/valyala/quicktemplate v1.1.1
|
||||
github.com/valyala/quicktemplate
|
||||
# github.com/timakin/bodyclose v0.0.0-20190407043127-4a873e97b2bb
|
||||
github.com/timakin/bodyclose/passes/bodyclose
|
||||
# golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a
|
||||
golang.org/x/crypto/ssh/terminal
|
||||
# golang.org/x/sys v0.0.0-20190312061237-fead79001313
|
||||
|
Loading…
x
Reference in New Issue
Block a user