Skip to content

Commit

Permalink
Add Noctx (#1179)
Browse files Browse the repository at this point in the history
Support for linter that enforces using context on each `http.Request`
https://github.com/sonatard/noctx
  • Loading branch information
sonatard committed Jun 8, 2020
1 parent b784f44 commit f1a46f9
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 7 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Expand Up @@ -114,6 +114,7 @@ linters:
# - goerr113
# - maligned
# - nestif
# - noctx (TODO: enable after next release; current release at time of writing is v1.27)
# - prealloc
# - testpackage
# - wsl
Expand Down
1 change: 1 addition & 0 deletions go.mod
Expand Up @@ -40,6 +40,7 @@ require (
github.com/securego/gosec/v2 v2.3.0
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada // v2.19.8
github.com/sirupsen/logrus v1.6.0
github.com/sonatard/noctx v0.0.1
github.com/sourcegraph/go-diff v0.5.3
github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5
Expand Down
12 changes: 6 additions & 6 deletions go.sum
Expand Up @@ -159,6 +159,8 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
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/gostaticanalysis/analysisutil v0.0.3 h1:iwp+5/UAyzQSFgQ4uR2sni99sJ8Eo9DEacKWM5pekIg=
github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
Expand Down Expand Up @@ -329,8 +331,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sourcegraph/go-diff v0.5.2 h1:aREwkyV8nKvCkMW0129XBB4+ZmE/zyLkdZU569ylqmQ=
github.com/sourcegraph/go-diff v0.5.2/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak=
github.com/sonatard/noctx v0.0.1 h1:VC1Qhl6Oxx9vvWo3UDgrGXYCeKCe3Wbw7qAWL6FrmTY=
github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI=
github.com/sourcegraph/go-diff v0.5.3 h1:lhIKJ2nXLZZ+AfbHpYxTn0pXpNTTui0DX7DO3xeb1Zs=
github.com/sourcegraph/go-diff v0.5.3/go.mod h1:v9JDtjCE4HHHCZGId75rg8gkKKa98RVjBcBGsVmMmak=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
Expand Down Expand Up @@ -417,6 +419,7 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -507,16 +510,13 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200117220505-0cba7a3a9ee9/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200321224714-0d839f3cf2ed/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200331202046-9d5940d49312/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e h1:3Dzrrxi54Io7Aoyb0PYLsI47K2TxkRQg+cqUn+m04do=
golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 h1:M9Fif0OxNji8w+HvmhVQ8KJtiZOsjU9RgslJGhn95XE=
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 h1:M9Fif0OxNji8w+HvmhVQ8KJtiZOsjU9RgslJGhn95XE=
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770 h1:M9Fif0OxNji8w+HvmhVQ8KJtiZOsjU9RgslJGhn95XE=
golang.org/x/tools v0.0.0-20200502202811-ed308ab3e770/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a h1:gILuVKC+ZPD6g/tj6zBOdnOH1ZHI0zZ86+KLMogc6/s=
golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
Expand Down
21 changes: 21 additions & 0 deletions pkg/golinters/noctx.go
@@ -0,0 +1,21 @@
package golinters

import (
"github.com/sonatard/noctx"
"golang.org/x/tools/go/analysis"

"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
)

func NewNoctx() *goanalysis.Linter {
analyzers := []*analysis.Analyzer{
noctx.Analyzer,
}

return goanalysis.NewLinter(
"noctx",
"noctx finds sending http request without context.Context",
analyzers,
nil,
).WithLoadMode(goanalysis.LoadModeTypesInfo)
}
4 changes: 4 additions & 0 deletions pkg/lint/lintersdb/manager.go
Expand Up @@ -104,6 +104,10 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
WithLoadForGoAnalysis().
WithPresets(linter.PresetPerformance, linter.PresetBugs).
WithURL("https://github.com/timakin/bodyclose"),
linter.NewConfig(golinters.NewNoctx()).
WithLoadForGoAnalysis().
WithPresets(linter.PresetPerformance, linter.PresetBugs).
WithURL("https://github.com/sonatard/noctx"),
linter.NewConfig(golinters.NewErrcheck()).
WithLoadForGoAnalysis().
WithPresets(linter.PresetBugs).
Expand Down
2 changes: 1 addition & 1 deletion test/linters_test.go
Expand Up @@ -110,7 +110,7 @@ func testOneSource(t *testing.T, sourcePath string) {
"--print-issued-lines=false",
"--print-linter-name=false",
"--out-format=line-number",
"--max-same-issues=10",
"--max-same-issues=100",
}

rc := extractRunContextFromComments(t, sourcePath)
Expand Down
134 changes: 134 additions & 0 deletions test/testdata/noctx.go
@@ -0,0 +1,134 @@
//args: -Enoctx
package testdata

import (
"context"
"net/http"
)

var newRequestPkg = http.NewRequest

func Noctx() {
const url = "http://example.com"
cli := &http.Client{}

ctx := context.Background()
http.Get(url) // ERROR "net/http\.Get must not be called"
_ = http.Get // OK
f := http.Get // OK
f(url) // ERROR "net/http\.Get must not be called"

http.Head(url) // ERROR "net/http\.Head must not be called"
http.Post(url, "", nil) // ERROR "net/http\.Post must not be called"
http.PostForm(url, nil) // ERROR "net/http\.PostForm must not be called"

cli.Get(url) // ERROR "\(\*net/http\.Client\)\.Get must not be called"
_ = cli.Get // OK
m := cli.Get // OK
m(url) // ERROR "\(\*net/http\.Client\)\.Get must not be called"

cli.Head(url) // ERROR "\(\*net/http\.Client\)\.Head must not be called"
cli.Post(url, "", nil) // ERROR "\(\*net/http\.Client\)\.Post must not be called"
cli.PostForm(url, nil) // ERROR "\(\*net/http\.Client\)\.PostForm must not be called"

req, _ := http.NewRequest(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
cli.Do(req)

req2, _ := http.NewRequestWithContext(ctx, http.MethodPost, url, nil) // OK
cli.Do(req2)

req3, _ := http.NewRequest(http.MethodPost, url, nil) // OK
req3 = req3.WithContext(ctx)
cli.Do(req3)

f2 := func(req *http.Request, ctx context.Context) *http.Request {
return req
}
req4, _ := http.NewRequest(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
req4 = f2(req4, ctx)

req41, _ := http.NewRequest(http.MethodPost, url, nil) // OK
req41 = req41.WithContext(ctx)
req41 = f2(req41, ctx)

newRequest := http.NewRequest
req5, _ := newRequest(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
cli.Do(req5)

req51, _ := newRequest(http.MethodPost, url, nil) // OK
req51 = req51.WithContext(ctx)
cli.Do(req51)

req52, _ := newRequestPkg(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
cli.Do(req52)

type MyRequest = http.Request
f3 := func(req *MyRequest, ctx context.Context) *MyRequest {
return req
}
req6, _ := http.NewRequest(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
req6 = f3(req6, ctx)

req61, _ := http.NewRequest(http.MethodPost, url, nil) // OK
req61 = req61.WithContext(ctx)
req61 = f3(req61, ctx)

type MyRequest2 http.Request
f4 := func(req *MyRequest2, ctx context.Context) *MyRequest2 {
return req
}
req7, _ := http.NewRequest(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
req71 := MyRequest2(*req7)
f4(&req71, ctx)

req72, _ := http.NewRequest(http.MethodPost, url, nil) // OK
req72 = req72.WithContext(ctx)
req73 := MyRequest2(*req7)
f4(&req73, ctx)

req8, _ := func() (*http.Request, error) {
return http.NewRequest(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
}()
cli.Do(req8)

req82, _ := func() (*http.Request, error) {
req82, _ := http.NewRequest(http.MethodPost, url, nil) // OK
req82 = req82.WithContext(ctx)
return req82, nil
}()
cli.Do(req82)

f5 := func(req, req2 *http.Request, ctx context.Context) (*http.Request, *http.Request) {
return req, req2
}
req9, _ := http.NewRequest(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
req9, _ = f5(req9, req9, ctx)

req91, _ := http.NewRequest(http.MethodPost, url, nil) // OK
req91 = req91.WithContext(ctx)
req9, _ = f5(req91, req91, ctx)

req10, _ := http.NewRequest(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
req11, _ := http.NewRequest(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
req10, req11 = f5(req10, req11, ctx)

req101, _ := http.NewRequest(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
req111, _ := http.NewRequest(http.MethodPost, url, nil) // OK
req111 = req111.WithContext(ctx)
req101, req111 = f5(req101, req111, ctx)

func() (*http.Request, *http.Request) {
req12, _ := http.NewRequest(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
req13, _ := http.NewRequest(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
return req12, req13
}()

func() (*http.Request, *http.Request) {
req14, _ := http.NewRequest(http.MethodPost, url, nil) // ERROR "should rewrite http.NewRequestWithContext or add \(\*Request\).WithContext"
req15, _ := http.NewRequest(http.MethodPost, url, nil) // OK
req15 = req15.WithContext(ctx)

return req14, req15
}()
}

0 comments on commit f1a46f9

Please sign in to comment.