From def2362682ad3f9744509e8c01fc778e319a7474 Mon Sep 17 00:00:00 2001 From: renovate Date: Sat, 9 May 2020 13:44:17 +0000 Subject: [PATCH] Update module spf13/viper to v1.7.0 (#494) Update module spf13/viper to v1.7.0 Reviewed-on: https://kolaente.dev/vikunja/api/pulls/494 --- go.mod | 4 +- go.sum | 116 ++ vendor/github.com/spf13/viper/.editorconfig | 15 + vendor/github.com/spf13/viper/.gitignore | 17 +- vendor/github.com/spf13/viper/.golangci.yml | 5 +- vendor/github.com/spf13/viper/.travis.yml | 32 - vendor/github.com/spf13/viper/README.md | 31 +- vendor/github.com/spf13/viper/go.mod | 13 +- vendor/github.com/spf13/viper/go.sum | 222 ++- vendor/github.com/spf13/viper/viper.go | 58 +- .../x/tools/go/analysis/analysis.go | 237 +++ .../x/tools/go/analysis/diagnostic.go | 61 + vendor/golang.org/x/tools/go/analysis/doc.go | 301 ++++ .../go/analysis/passes/inspect/inspect.go | 49 + .../x/tools/go/analysis/validate.go | 97 ++ .../x/tools/go/types/objectpath/objectpath.go | 523 +++++++ .../internal/analysisinternal/analysis.go | 118 ++ vendor/honnef.co/go/tools/LICENSE-THIRD-PARTY | 226 +++ .../honnef.co/go/tools/callgraph/callgraph.go | 129 -- .../go/tools/callgraph/static/static.go | 35 - vendor/honnef.co/go/tools/callgraph/util.go | 181 --- .../go/tools/cmd/staticcheck/staticcheck.go | 29 +- vendor/honnef.co/go/tools/config/config.go | 66 +- vendor/honnef.co/go/tools/facts/deprecated.go | 144 ++ vendor/honnef.co/go/tools/facts/generated.go | 86 ++ vendor/honnef.co/go/tools/facts/purity.go | 175 +++ vendor/honnef.co/go/tools/facts/token.go | 24 + .../honnef.co/go/tools/functions/concrete.go | 56 - .../honnef.co/go/tools/functions/functions.go | 150 -- vendor/honnef.co/go/tools/functions/loops.go | 14 +- vendor/honnef.co/go/tools/functions/pure.go | 101 +- .../go/tools/functions/terminates.go | 4 +- .../go/tools/go/types/typeutil/identical.go | 74 +- .../go/tools/go/types/typeutil/map.go | 6 +- .../go/tools/internal/cache/cache.go | 474 ++++++ .../go/tools/internal/cache/default.go | 85 ++ .../honnef.co/go/tools/internal/cache/hash.go | 176 +++ .../internal/passes/buildssa/buildssa.go | 116 ++ .../go/tools/internal/renameio/renameio.go | 83 ++ .../go/tools/internal/sharedcheck/lint.go | 10 +- vendor/honnef.co/go/tools/lint/generated.go | 44 - vendor/honnef.co/go/tools/lint/lint.go | 712 ++++----- .../go/tools/lint/lintdsl/lintdsl.go | 121 +- .../go/tools/lint/lintutil/format/format.go | 29 +- .../honnef.co/go/tools/lint/lintutil/stats.go | 7 + .../go/tools/lint/lintutil/stats_bsd.go | 10 + .../go/tools/lint/lintutil/stats_posix.go | 10 + .../honnef.co/go/tools/lint/lintutil/util.go | 332 +++-- vendor/honnef.co/go/tools/lint/runner.go | 970 +++++++++++++ vendor/honnef.co/go/tools/lint/stats.go | 20 + vendor/honnef.co/go/tools/loader/loader.go | 197 +++ vendor/honnef.co/go/tools/simple/analysis.go | 223 +++ vendor/honnef.co/go/tools/simple/doc.go | 629 ++++---- vendor/honnef.co/go/tools/simple/lint.go | 525 ++++--- vendor/honnef.co/go/tools/ssa/builder.go | 18 +- vendor/honnef.co/go/tools/ssa/const.go | 44 +- vendor/honnef.co/go/tools/ssa/create.go | 17 +- vendor/honnef.co/go/tools/ssa/doc.go | 12 +- vendor/honnef.co/go/tools/ssa/dom.go | 6 +- vendor/honnef.co/go/tools/ssa/emit.go | 1 + vendor/honnef.co/go/tools/ssa/func.go | 64 + vendor/honnef.co/go/tools/ssa/lift.go | 30 +- vendor/honnef.co/go/tools/ssa/methods.go | 4 +- vendor/honnef.co/go/tools/ssa/sanity.go | 18 +- vendor/honnef.co/go/tools/ssa/source.go | 2 +- vendor/honnef.co/go/tools/ssa/ssa.go | 16 +- vendor/honnef.co/go/tools/ssa/ssautil/load.go | 143 -- .../honnef.co/go/tools/ssa/ssautil/switch.go | 234 --- .../honnef.co/go/tools/ssa/ssautil/visit.go | 79 - .../honnef.co/go/tools/ssa/staticcheck.conf | 3 + vendor/honnef.co/go/tools/ssa/testmain.go | 8 +- vendor/honnef.co/go/tools/ssa/wrappers.go | 6 +- .../go/tools/staticcheck/analysis.go | 525 +++++++ vendor/honnef.co/go/tools/staticcheck/doc.go | 795 +++++----- .../go/tools/staticcheck/knowledge.go | 25 + vendor/honnef.co/go/tools/staticcheck/lint.go | 1277 +++++++---------- .../honnef.co/go/tools/staticcheck/rules.go | 19 +- .../honnef.co/go/tools/staticcheck/vrp/vrp.go | 24 +- .../honnef.co/go/tools/stylecheck/analysis.go | 111 ++ vendor/honnef.co/go/tools/stylecheck/doc.go | 188 ++- vendor/honnef.co/go/tools/stylecheck/lint.go | 203 ++- vendor/honnef.co/go/tools/stylecheck/names.go | 25 +- vendor/honnef.co/go/tools/unused/edge.go | 54 + .../go/tools/unused/edgekind_string.go | 109 ++ vendor/honnef.co/go/tools/unused/unused.go | 1189 ++++++++------- .../honnef.co/go/tools/version/buildinfo.go | 46 + .../go/tools/version/buildinfo111.go | 6 + vendor/honnef.co/go/tools/version/version.go | 31 +- vendor/modules.txt | 16 +- 89 files changed, 9018 insertions(+), 4502 deletions(-) create mode 100644 vendor/github.com/spf13/viper/.editorconfig delete mode 100644 vendor/github.com/spf13/viper/.travis.yml create mode 100644 vendor/golang.org/x/tools/go/analysis/analysis.go create mode 100644 vendor/golang.org/x/tools/go/analysis/diagnostic.go create mode 100644 vendor/golang.org/x/tools/go/analysis/doc.go create mode 100644 vendor/golang.org/x/tools/go/analysis/passes/inspect/inspect.go create mode 100644 vendor/golang.org/x/tools/go/analysis/validate.go create mode 100644 vendor/golang.org/x/tools/go/types/objectpath/objectpath.go create mode 100644 vendor/golang.org/x/tools/internal/analysisinternal/analysis.go create mode 100644 vendor/honnef.co/go/tools/LICENSE-THIRD-PARTY delete mode 100644 vendor/honnef.co/go/tools/callgraph/callgraph.go delete mode 100644 vendor/honnef.co/go/tools/callgraph/static/static.go delete mode 100644 vendor/honnef.co/go/tools/callgraph/util.go create mode 100644 vendor/honnef.co/go/tools/facts/deprecated.go create mode 100644 vendor/honnef.co/go/tools/facts/generated.go create mode 100644 vendor/honnef.co/go/tools/facts/purity.go create mode 100644 vendor/honnef.co/go/tools/facts/token.go delete mode 100644 vendor/honnef.co/go/tools/functions/concrete.go delete mode 100644 vendor/honnef.co/go/tools/functions/functions.go create mode 100644 vendor/honnef.co/go/tools/internal/cache/cache.go create mode 100644 vendor/honnef.co/go/tools/internal/cache/default.go create mode 100644 vendor/honnef.co/go/tools/internal/cache/hash.go create mode 100644 vendor/honnef.co/go/tools/internal/passes/buildssa/buildssa.go create mode 100644 vendor/honnef.co/go/tools/internal/renameio/renameio.go delete mode 100644 vendor/honnef.co/go/tools/lint/generated.go create mode 100644 vendor/honnef.co/go/tools/lint/lintutil/stats.go create mode 100644 vendor/honnef.co/go/tools/lint/lintutil/stats_bsd.go create mode 100644 vendor/honnef.co/go/tools/lint/lintutil/stats_posix.go create mode 100644 vendor/honnef.co/go/tools/lint/runner.go create mode 100644 vendor/honnef.co/go/tools/lint/stats.go create mode 100644 vendor/honnef.co/go/tools/loader/loader.go create mode 100644 vendor/honnef.co/go/tools/simple/analysis.go delete mode 100644 vendor/honnef.co/go/tools/ssa/ssautil/load.go delete mode 100644 vendor/honnef.co/go/tools/ssa/ssautil/switch.go delete mode 100644 vendor/honnef.co/go/tools/ssa/ssautil/visit.go create mode 100644 vendor/honnef.co/go/tools/ssa/staticcheck.conf create mode 100644 vendor/honnef.co/go/tools/staticcheck/analysis.go create mode 100644 vendor/honnef.co/go/tools/staticcheck/knowledge.go create mode 100644 vendor/honnef.co/go/tools/stylecheck/analysis.go create mode 100644 vendor/honnef.co/go/tools/unused/edge.go create mode 100644 vendor/honnef.co/go/tools/unused/edgekind_string.go create mode 100644 vendor/honnef.co/go/tools/version/buildinfo.go create mode 100644 vendor/honnef.co/go/tools/version/buildinfo111.go diff --git a/go.mod b/go.mod index 918fa6b8d6..2b395e3163 100644 --- a/go.mod +++ b/go.mod @@ -62,7 +62,7 @@ require ( github.com/spf13/afero v1.2.2 github.com/spf13/cobra v0.0.7 github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/viper v1.6.3 + github.com/spf13/viper v1.7.0 github.com/stretchr/testify v1.5.1 github.com/swaggo/swag v1.6.3 github.com/ulule/limiter/v3 v3.5.0 @@ -75,7 +75,7 @@ require ( gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/d4l3k/messagediff.v1 v1.2.1 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df - honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a + honnef.co/go/tools v0.0.1-2019.2.3 src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273 src.techknowlogick.com/xormigrate v1.2.0 xorm.io/builder v0.3.7 diff --git a/go.sum b/go.sum index fdba737a3e..9ede9f88c3 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,19 @@ cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= code.vikunja.io/web v0.0.0-20200208214421-c90649369427 h1:6ps5r0OxZNRdmCavh1k/xMwftN27hHauo+EtdTGxLug= code.vikunja.io/web v0.0.0-20200208214421-c90649369427/go.mod h1:cuP1/ieGWAZzgQGw+QPt6Y5F0fVb/8Ol5NV4QSezGdo= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= gitea.com/xorm/tests v0.7.0 h1:pFcaxTGGAWw3rDuVfhBdyr+mX1uzdTtncyAKxkCQ/IE= @@ -19,6 +30,7 @@ gitea.com/xorm/xorm-redis-cache v0.2.0 h1:qglRHt6/7vJmDeld6j+n10M9PmruAh+Le2lgNr gitea.com/xorm/xorm-redis-cache v0.2.0/go.mod h1:juYdjkmIKvLbPkdfBVKGVJ2daFQIJAgKsn4mL4ZK8Zk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -36,7 +48,10 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= @@ -47,6 +62,8 @@ github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee h1:BnPxIde0gjtTnc9Er7cxvBk8DHLWhEux0SxayC8dP6I= @@ -62,6 +79,7 @@ github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMe github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -86,6 +104,7 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8 github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= @@ -103,6 +122,7 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -153,6 +173,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= @@ -182,7 +203,10 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf h1:vc7Dmrk4JwS0ZPS6WZvWlwDflgDTA26jItmbSj83nug= @@ -190,12 +214,31 @@ github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1: github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 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= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8= github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= @@ -271,10 +314,12 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= @@ -294,7 +339,14 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJK github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -325,6 +377,7 @@ github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= @@ -338,6 +391,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok= github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -378,8 +432,11 @@ github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shopspring/decimal v0.0.0-20191009025716-f1972eb1d1f5/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= @@ -414,6 +471,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs= github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw= +github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -463,12 +522,16 @@ github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wK go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A= @@ -492,12 +555,25 @@ golang.org/x/crypto v0.0.0-20200427165652-729f1e841bcc/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88= golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +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= @@ -506,13 +582,16 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58 h1:otZG8yDCO4LVps5+9bxOeNiCvgmOyt96J3roHTYs7oE= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= @@ -530,6 +609,7 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -537,22 +617,28 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190609082536-301114b31cce/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -584,14 +670,24 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628034336-212fb13d595e h1:ZlQjfVdpDxeqxRfmO30CdqWWzTvgRCj0MxaUVfxEG1k= golang.org/x/tools v0.0.0-20190628034336-212fb13d595e/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +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-20200130002326-2f3ba24bd6e7 h1:EBZoQjiKKPaLbPrbpssUfuHtwM6KV/vb4U85g/cigFY= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -602,18 +698,34 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw= google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -632,6 +744,7 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/d4l3k/messagediff.v1 v1.2.1 h1:70AthpjunwzUiarMHyED52mj9UwtAnE89l1Gmrt3EU0= gopkg.in/d4l3k/messagediff.v1 v1.2.1/go.mod h1:EUzikiKadqXWcD1AzJLagx0j/BeeWGtn++04Xniyg44= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= @@ -657,6 +770,9 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a h1:LJwr7TCTghdatWv40WobzlKXc9c4s8oGa7QKJUtHhWA= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273 h1:dE6ry9rVwDn3soD4wPCXqEG60AZTuhniZzHdnj3c+74= src.techknowlogick.com/xgo v0.0.0-20200408234745-bb0faa361273/go.mod h1:31CE1YKtDOrKTk9PSnjTpe6YbO6W/0LTYZ1VskL09oU= src.techknowlogick.com/xormigrate v1.2.0 h1:bq9JaI48bxB+OddMghicjmV7sGmBUogJq4HmTN0DOcw= diff --git a/vendor/github.com/spf13/viper/.editorconfig b/vendor/github.com/spf13/viper/.editorconfig new file mode 100644 index 0000000000..63afcbcdd4 --- /dev/null +++ b/vendor/github.com/spf13/viper/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.go] +indent_style = tab + +[{Makefile, *.mk}] +indent_style = tab diff --git a/vendor/github.com/spf13/viper/.gitignore b/vendor/github.com/spf13/viper/.gitignore index d6941f324b..8962508398 100644 --- a/vendor/github.com/spf13/viper/.gitignore +++ b/vendor/github.com/spf13/viper/.gitignore @@ -1,20 +1,5 @@ +/.idea/ /bin/ /build/ /var/ /vendor/ - -# IDE integration -/.vscode/* -!/.vscode/launch.json -!/.vscode/tasks.json -/.idea/* -!/.idea/codeStyles/ -!/.idea/copyright/ -!/.idea/dataSources.xml -!/.idea/*.iml -!/.idea/externalDependencies.xml -!/.idea/go.imports.xml -!/.idea/modules.xml -!/.idea/runConfigurations/ -!/.idea/scopes/ -!/.idea/sqldialects.xml diff --git a/vendor/github.com/spf13/viper/.golangci.yml b/vendor/github.com/spf13/viper/.golangci.yml index 0ea9249e79..a0755ce7e1 100644 --- a/vendor/github.com/spf13/viper/.golangci.yml +++ b/vendor/github.com/spf13/viper/.golangci.yml @@ -21,4 +21,7 @@ linters: - scopelint - gocyclo - gocognit - - gocritic \ No newline at end of file + - gocritic + +service: + golangci-lint-version: 1.21.x diff --git a/vendor/github.com/spf13/viper/.travis.yml b/vendor/github.com/spf13/viper/.travis.yml deleted file mode 100644 index ed677bbb81..0000000000 --- a/vendor/github.com/spf13/viper/.travis.yml +++ /dev/null @@ -1,32 +0,0 @@ -go_import_path: github.com/spf13/viper - -language: go - -env: - global: - - GO111MODULE="on" - - GOFLAGS="-mod=readonly" - -go: - - 1.11.x - - 1.12.x - - 1.13.x - - tip - -os: - - linux - - osx - -matrix: - allow_failures: - - go: tip - fast_finish: true - -script: - - go install ./... - - diff -u <(echo -n) <(gofmt -d .) - - go test -v ./... - -after_success: - - go get -u -d github.com/spf13/hugo - - cd $GOPATH/src/github.com/spf13/hugo && make && ./hugo -s docs && cd - diff --git a/vendor/github.com/spf13/viper/README.md b/vendor/github.com/spf13/viper/README.md index a574677f8d..dfd8034fd5 100644 --- a/vendor/github.com/spf13/viper/README.md +++ b/vendor/github.com/spf13/viper/README.md @@ -1,10 +1,13 @@ -![viper logo](https://cloud.githubusercontent.com/assets/173412/10886745/998df88a-8151-11e5-9448-4736db51020d.png) +![Viper](.github/logo.png?raw=true) -Go configuration with fangs! +[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration) -[![Actions](https://github.com/spf13/viper/workflows/CI/badge.svg)](https://github.com/spf13/viper) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/spf13/viper/CI?style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI) [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![GoDoc](https://godoc.org/github.com/spf13/viper?status.svg)](https://godoc.org/github.com/spf13/viper) +[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper) +[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/mod/github.com/spf13/viper) + +**Go configuration with fangs!** Many Go projects are built using Viper including: @@ -400,7 +403,7 @@ in a Key/Value store such as etcd or Consul. These values take precedence over default values, but are overridden by configuration values retrieved from disk, flags, or environment variables. -Viper uses [crypt](https://github.com/xordataexchange/crypt) to retrieve +Viper uses [crypt](https://github.com/bketelsen/crypt) to retrieve configuration from the K/V store, which means that you can store your configuration values encrypted and have them automatically decrypted if you have the correct gpg keyring. Encryption is optional. @@ -412,7 +415,7 @@ independently of it. K/V store. `crypt` defaults to etcd on http://127.0.0.1:4001. ```bash -$ go get github.com/xordataexchange/crypt/bin/crypt +$ go get github.com/bketelsen/crypt/bin/crypt $ crypt set -plaintext /config/hugo.json /Users/hugo/settings/config.json ``` @@ -435,7 +438,7 @@ err := viper.ReadRemoteConfig() ``` #### Consul -You need to set a key to Consul key/value storage with JSON value containing your desired config. +You need to set a key to Consul key/value storage with JSON value containing your desired config. For example, create a Consul key/value store key `MY_CONSUL_KEY` with value: ```json @@ -454,6 +457,16 @@ fmt.Println(viper.Get("port")) // 8080 fmt.Println(viper.Get("hostname")) // myhostname.com ``` +#### Firestore + +```go +viper.AddRemoteProvider("firestore", "google-cloud-project-id", "collection/document") +viper.SetConfigType("json") // Config's format: "json", "toml", "yaml", "yml" +err := viper.ReadRemoteConfig() +``` + +Of course, you're allowed to use `SecureRemoteProvider` also + ### Remote Key/Value Store Example - Encrypted ```go @@ -728,14 +741,14 @@ Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/maps ### Marshalling to string -You may need to marshal all the settings held in viper into a string rather than write them to a file. +You may need to marshal all the settings held in viper into a string rather than write them to a file. You can use your favorite format's marshaller with the config returned by `AllSettings()`. ```go import ( yaml "gopkg.in/yaml.v2" // ... -) +) func yamlStringSettings() string { c := viper.AllSettings() diff --git a/vendor/github.com/spf13/viper/go.mod b/vendor/github.com/spf13/viper/go.mod index 3d51b8c0d3..7d108dcc21 100644 --- a/vendor/github.com/spf13/viper/go.mod +++ b/vendor/github.com/spf13/viper/go.mod @@ -3,27 +3,22 @@ module github.com/spf13/viper go 1.12 require ( - github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 // indirect + github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c github.com/coreos/bbolt v1.3.2 // indirect - github.com/coreos/etcd v3.3.13+incompatible // indirect - github.com/coreos/go-semver v0.2.0 // indirect github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/fsnotify/fsnotify v1.4.7 github.com/gogo/protobuf v1.2.1 // indirect github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect - github.com/google/btree v1.0.0 // indirect - github.com/gorilla/websocket v1.4.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.9.0 // indirect github.com/hashicorp/hcl v1.0.0 github.com/jonboulle/clockwork v0.1.0 // indirect - github.com/json-iterator/go v1.1.9 // indirect github.com/magiconair/properties v1.8.1 github.com/mitchellh/mapstructure v1.1.2 - github.com/modern-go/reflect2 v1.0.1 // indirect github.com/pelletier/go-toml v1.2.0 github.com/prometheus/client_golang v0.9.3 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect @@ -36,14 +31,10 @@ require ( github.com/subosito/gotenv v1.2.0 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect - github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 go.etcd.io/bbolt v1.3.2 // indirect go.uber.org/atomic v1.4.0 // indirect go.uber.org/multierr v1.1.0 // indirect go.uber.org/zap v1.10.0 // indirect - golang.org/x/net v0.0.0-20190522155817-f3200d17e092 // indirect - golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect - google.golang.org/grpc v1.21.0 // indirect gopkg.in/ini.v1 v1.51.0 gopkg.in/yaml.v2 v2.2.4 ) diff --git a/vendor/github.com/spf13/viper/go.sum b/vendor/github.com/spf13/viper/go.sum index 098f5d3a59..463aa7dbf5 100644 --- a/vendor/github.com/spf13/viper/go.sum +++ b/vendor/github.com/spf13/viper/go.sum @@ -1,22 +1,46 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0 h1:9x7Bx0A9R5/M9jibeJeZWqjeVEIxYW9fZYqB9a70/bY= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= @@ -27,10 +51,12 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -43,29 +69,77 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= 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 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= 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 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -81,23 +155,39 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -112,6 +202,10 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzr github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= @@ -141,10 +235,11 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= @@ -152,46 +247,132 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +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/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= @@ -200,3 +381,8 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go index 265eae7f11..f61f4ed755 100644 --- a/vendor/github.com/spf13/viper/viper.go +++ b/vendor/github.com/spf13/viper/viper.go @@ -287,7 +287,7 @@ func NewWithOptions(opts ...Option) *Viper { func Reset() { v = New() SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"} - SupportedRemoteProviders = []string{"etcd", "consul"} + SupportedRemoteProviders = []string{"etcd", "consul", "firestore"} } type defaultRemoteProvider struct { @@ -328,7 +328,7 @@ type RemoteProvider interface { var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "dotenv", "env", "ini"} // SupportedRemoteProviders are universally supported remote providers. -var SupportedRemoteProviders = []string{"etcd", "consul"} +var SupportedRemoteProviders = []string{"etcd", "consul", "firestore"} func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) } func (v *Viper) OnConfigChange(run func(in fsnotify.Event)) { @@ -477,7 +477,7 @@ func (v *Viper) AddConfigPath(in string) { // AddRemoteProvider adds a remote configuration source. // Remote Providers are searched in the order they are added. -// provider is a string value, "etcd" or "consul" are currently supported. +// provider is a string value: "etcd", "consul" or "firestore" are currently supported. // endpoint is the url. etcd requires http://ip:port consul requires ip:port // path is the path in the k/v store to retrieve configuration // To retrieve a config file called myapp.json from /configs/myapp.json @@ -506,14 +506,14 @@ func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error { // AddSecureRemoteProvider adds a remote configuration source. // Secure Remote Providers are searched in the order they are added. -// provider is a string value, "etcd" or "consul" are currently supported. +// provider is a string value: "etcd", "consul" or "firestore" are currently supported. // endpoint is the url. etcd requires http://ip:port consul requires ip:port // secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg // path is the path in the k/v store to retrieve configuration // To retrieve a config file called myapp.json from /configs/myapp.json // you should set path to /configs and set config name (SetConfigName()) to // "myapp" -// Secure Remote Providers are implemented with github.com/xordataexchange/crypt +// Secure Remote Providers are implemented with github.com/bketelsen/crypt func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring) } @@ -996,11 +996,6 @@ func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) { } // BindFlagValue binds a specific key to a FlagValue. -// Example (where serverCmd is a Cobra instance): -// -// serverCmd.Flags().Int("port", 1138, "Port to run Application server on") -// Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port")) -// func BindFlagValue(key string, flag FlagValue) error { return v.BindFlagValue(key, flag) } func (v *Viper) BindFlagValue(key string, flag FlagValue) error { if flag == nil { @@ -1088,6 +1083,8 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} { s = strings.TrimSuffix(s, "]") res, _ := readAsCSV(s) return cast.ToIntSlice(res) + case "stringToString": + return stringToStringConv(flag.ValueString()) default: return flag.ValueString() } @@ -1163,6 +1160,8 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} { s = strings.TrimSuffix(s, "]") res, _ := readAsCSV(s) return cast.ToIntSlice(res) + case "stringToString": + return stringToStringConv(flag.ValueString()) default: return flag.ValueString() } @@ -1182,6 +1181,30 @@ func readAsCSV(val string) ([]string, error) { return csvReader.Read() } +// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/master/string_to_string.go#L79 +// alterations are: errors are swallowed, map[string]interface{} is returned in order to enable cast.ToStringMap +func stringToStringConv(val string) interface{} { + val = strings.Trim(val, "[]") + // An empty string would cause an empty map + if len(val) == 0 { + return map[string]interface{}{} + } + r := csv.NewReader(strings.NewReader(val)) + ss, err := r.Read() + if err != nil { + return nil + } + out := make(map[string]interface{}, len(ss)) + for _, pair := range ss { + kv := strings.SplitN(pair, "=", 2) + if len(kv) != 2 { + return nil + } + out[kv[0]] = kv[1] + } + return out +} + // IsSet checks to see if the key has been set in any of the data locations. // IsSet is case-insensitive for a key. func IsSet(key string) bool { return v.IsSet(key) } @@ -1418,11 +1441,18 @@ func (v *Viper) SafeWriteConfigAs(filename string) error { func (v *Viper) writeConfig(filename string, force bool) error { jww.INFO.Println("Attempting to write configuration to file.") + var configType string + ext := filepath.Ext(filename) - if len(ext) <= 1 { - return fmt.Errorf("filename: %s requires valid extension", filename) + if ext != "" { + configType = ext[1:] + } else { + configType = v.configType } - configType := ext[1:] + if configType == "" { + return fmt.Errorf("config type could not be determined for %s", filename) + } + if !stringInSlice(configType, SupportedExts) { return UnsupportedConfigError(configType) } @@ -1619,7 +1649,7 @@ func (v *Viper) marshalWriter(f afero.File, configType string) error { if sectionName == "default" { sectionName = "" } - cfg.Section(sectionName).Key(keyName).SetValue(Get(key).(string)) + cfg.Section(sectionName).Key(keyName).SetValue(v.Get(key).(string)) } cfg.WriteTo(f) } diff --git a/vendor/golang.org/x/tools/go/analysis/analysis.go b/vendor/golang.org/x/tools/go/analysis/analysis.go new file mode 100644 index 0000000000..8c9977355c --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/analysis.go @@ -0,0 +1,237 @@ +package analysis + +import ( + "flag" + "fmt" + "go/ast" + "go/token" + "go/types" + "reflect" + + "golang.org/x/tools/internal/analysisinternal" +) + +// An Analyzer describes an analysis function and its options. +type Analyzer struct { + // The Name of the analyzer must be a valid Go identifier + // as it may appear in command-line flags, URLs, and so on. + Name string + + // Doc is the documentation for the analyzer. + // The part before the first "\n\n" is the title + // (no capital or period, max ~60 letters). + Doc string + + // Flags defines any flags accepted by the analyzer. + // The manner in which these flags are exposed to the user + // depends on the driver which runs the analyzer. + Flags flag.FlagSet + + // Run applies the analyzer to a package. + // It returns an error if the analyzer failed. + // + // On success, the Run function may return a result + // computed by the Analyzer; its type must match ResultType. + // The driver makes this result available as an input to + // another Analyzer that depends directly on this one (see + // Requires) when it analyzes the same package. + // + // To pass analysis results between packages (and thus + // potentially between address spaces), use Facts, which are + // serializable. + Run func(*Pass) (interface{}, error) + + // RunDespiteErrors allows the driver to invoke + // the Run method of this analyzer even on a + // package that contains parse or type errors. + RunDespiteErrors bool + + // Requires is a set of analyzers that must run successfully + // before this one on a given package. This analyzer may inspect + // the outputs produced by each analyzer in Requires. + // The graph over analyzers implied by Requires edges must be acyclic. + // + // Requires establishes a "horizontal" dependency between + // analysis passes (different analyzers, same package). + Requires []*Analyzer + + // ResultType is the type of the optional result of the Run function. + ResultType reflect.Type + + // FactTypes indicates that this analyzer imports and exports + // Facts of the specified concrete types. + // An analyzer that uses facts may assume that its import + // dependencies have been similarly analyzed before it runs. + // Facts must be pointers. + // + // FactTypes establishes a "vertical" dependency between + // analysis passes (same analyzer, different packages). + FactTypes []Fact +} + +func (a *Analyzer) String() string { return a.Name } + +func init() { + // Set the analysisinternal functions to be able to pass type errors + // to the Pass type without modifying the go/analysis API. + analysisinternal.SetTypeErrors = func(p interface{}, errors []types.Error) { + p.(*Pass).typeErrors = errors + } + analysisinternal.GetTypeErrors = func(p interface{}) []types.Error { + return p.(*Pass).typeErrors + } +} + +// A Pass provides information to the Run function that +// applies a specific analyzer to a single Go package. +// +// It forms the interface between the analysis logic and the driver +// program, and has both input and an output components. +// +// As in a compiler, one pass may depend on the result computed by another. +// +// The Run function should not call any of the Pass functions concurrently. +type Pass struct { + Analyzer *Analyzer // the identity of the current analyzer + + // syntax and type information + Fset *token.FileSet // file position information + Files []*ast.File // the abstract syntax tree of each file + OtherFiles []string // names of non-Go files of this package + Pkg *types.Package // type information about the package + TypesInfo *types.Info // type information about the syntax trees + TypesSizes types.Sizes // function for computing sizes of types + + // Report reports a Diagnostic, a finding about a specific location + // in the analyzed source code such as a potential mistake. + // It may be called by the Run function. + Report func(Diagnostic) + + // ResultOf provides the inputs to this analysis pass, which are + // the corresponding results of its prerequisite analyzers. + // The map keys are the elements of Analysis.Required, + // and the type of each corresponding value is the required + // analysis's ResultType. + ResultOf map[*Analyzer]interface{} + + // -- facts -- + + // ImportObjectFact retrieves a fact associated with obj. + // Given a value ptr of type *T, where *T satisfies Fact, + // ImportObjectFact copies the value to *ptr. + // + // ImportObjectFact panics if called after the pass is complete. + // ImportObjectFact is not concurrency-safe. + ImportObjectFact func(obj types.Object, fact Fact) bool + + // ImportPackageFact retrieves a fact associated with package pkg, + // which must be this package or one of its dependencies. + // See comments for ImportObjectFact. + ImportPackageFact func(pkg *types.Package, fact Fact) bool + + // ExportObjectFact associates a fact of type *T with the obj, + // replacing any previous fact of that type. + // + // ExportObjectFact panics if it is called after the pass is + // complete, or if obj does not belong to the package being analyzed. + // ExportObjectFact is not concurrency-safe. + ExportObjectFact func(obj types.Object, fact Fact) + + // ExportPackageFact associates a fact with the current package. + // See comments for ExportObjectFact. + ExportPackageFact func(fact Fact) + + // AllPackageFacts returns a new slice containing all package facts of the analysis's FactTypes + // in unspecified order. + // WARNING: This is an experimental API and may change in the future. + AllPackageFacts func() []PackageFact + + // AllObjectFacts returns a new slice containing all object facts of the analysis's FactTypes + // in unspecified order. + // WARNING: This is an experimental API and may change in the future. + AllObjectFacts func() []ObjectFact + + // typeErrors contains types.Errors that are associated with the pkg. + typeErrors []types.Error + + /* Further fields may be added in future. */ + // For example, suggested or applied refactorings. +} + +// PackageFact is a package together with an associated fact. +// WARNING: This is an experimental API and may change in the future. +type PackageFact struct { + Package *types.Package + Fact Fact +} + +// ObjectFact is an object together with an associated fact. +// WARNING: This is an experimental API and may change in the future. +type ObjectFact struct { + Object types.Object + Fact Fact +} + +// Reportf is a helper function that reports a Diagnostic using the +// specified position and formatted error message. +func (pass *Pass) Reportf(pos token.Pos, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + pass.Report(Diagnostic{Pos: pos, Message: msg}) +} + +// The Range interface provides a range. It's equivalent to and satisfied by +// ast.Node. +type Range interface { + Pos() token.Pos // position of first character belonging to the node + End() token.Pos // position of first character immediately after the node +} + +// ReportRangef is a helper function that reports a Diagnostic using the +// range provided. ast.Node values can be passed in as the range because +// they satisfy the Range interface. +func (pass *Pass) ReportRangef(rng Range, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + pass.Report(Diagnostic{Pos: rng.Pos(), End: rng.End(), Message: msg}) +} + +func (pass *Pass) String() string { + return fmt.Sprintf("%s@%s", pass.Analyzer.Name, pass.Pkg.Path()) +} + +// A Fact is an intermediate fact produced during analysis. +// +// Each fact is associated with a named declaration (a types.Object) or +// with a package as a whole. A single object or package may have +// multiple associated facts, but only one of any particular fact type. +// +// A Fact represents a predicate such as "never returns", but does not +// represent the subject of the predicate such as "function F" or "package P". +// +// Facts may be produced in one analysis pass and consumed by another +// analysis pass even if these are in different address spaces. +// If package P imports Q, all facts about Q produced during +// analysis of that package will be available during later analysis of P. +// Facts are analogous to type export data in a build system: +// just as export data enables separate compilation of several passes, +// facts enable "separate analysis". +// +// Each pass (a, p) starts with the set of facts produced by the +// same analyzer a applied to the packages directly imported by p. +// The analysis may add facts to the set, and they may be exported in turn. +// An analysis's Run function may retrieve facts by calling +// Pass.Import{Object,Package}Fact and update them using +// Pass.Export{Object,Package}Fact. +// +// A fact is logically private to its Analysis. To pass values +// between different analyzers, use the results mechanism; +// see Analyzer.Requires, Analyzer.ResultType, and Pass.ResultOf. +// +// A Fact type must be a pointer. +// Facts are encoded and decoded using encoding/gob. +// A Fact may implement the GobEncoder/GobDecoder interfaces +// to customize its encoding. Fact encoding should not fail. +// +// A Fact should not be modified once exported. +type Fact interface { + AFact() // dummy method to avoid type errors +} diff --git a/vendor/golang.org/x/tools/go/analysis/diagnostic.go b/vendor/golang.org/x/tools/go/analysis/diagnostic.go new file mode 100644 index 0000000000..57eaf6faa2 --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/diagnostic.go @@ -0,0 +1,61 @@ +package analysis + +import "go/token" + +// A Diagnostic is a message associated with a source location or range. +// +// An Analyzer may return a variety of diagnostics; the optional Category, +// which should be a constant, may be used to classify them. +// It is primarily intended to make it easy to look up documentation. +// +// If End is provided, the diagnostic is specified to apply to the range between +// Pos and End. +type Diagnostic struct { + Pos token.Pos + End token.Pos // optional + Category string // optional + Message string + + // SuggestedFixes contains suggested fixes for a diagnostic which can be used to perform + // edits to a file that address the diagnostic. + // TODO(matloob): Should multiple SuggestedFixes be allowed for a diagnostic? + // Diagnostics should not contain SuggestedFixes that overlap. + // Experimental: This API is experimental and may change in the future. + SuggestedFixes []SuggestedFix // optional + + // Experimental: This API is experimental and may change in the future. + Related []RelatedInformation // optional +} + +// RelatedInformation contains information related to a diagnostic. +// For example, a diagnostic that flags duplicated declarations of a +// variable may include one RelatedInformation per existing +// declaration. +type RelatedInformation struct { + Pos token.Pos + End token.Pos + Message string +} + +// A SuggestedFix is a code change associated with a Diagnostic that a user can choose +// to apply to their code. Usually the SuggestedFix is meant to fix the issue flagged +// by the diagnostic. +// TextEdits for a SuggestedFix should not overlap. TextEdits for a SuggestedFix +// should not contain edits for other packages. +// Experimental: This API is experimental and may change in the future. +type SuggestedFix struct { + // A description for this suggested fix to be shown to a user deciding + // whether to accept it. + Message string + TextEdits []TextEdit +} + +// A TextEdit represents the replacement of the code between Pos and End with the new text. +// Each TextEdit should apply to a single file. End should not be earlier in the file than Pos. +// Experimental: This API is experimental and may change in the future. +type TextEdit struct { + // For a pure insertion, End can either be set to Pos or token.NoPos. + Pos token.Pos + End token.Pos + NewText []byte +} diff --git a/vendor/golang.org/x/tools/go/analysis/doc.go b/vendor/golang.org/x/tools/go/analysis/doc.go new file mode 100644 index 0000000000..ea56b724e8 --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/doc.go @@ -0,0 +1,301 @@ +/* + +Package analysis defines the interface between a modular static +analysis and an analysis driver program. + + +Background + +A static analysis is a function that inspects a package of Go code and +reports a set of diagnostics (typically mistakes in the code), and +perhaps produces other results as well, such as suggested refactorings +or other facts. An analysis that reports mistakes is informally called a +"checker". For example, the printf checker reports mistakes in +fmt.Printf format strings. + +A "modular" analysis is one that inspects one package at a time but can +save information from a lower-level package and use it when inspecting a +higher-level package, analogous to separate compilation in a toolchain. +The printf checker is modular: when it discovers that a function such as +log.Fatalf delegates to fmt.Printf, it records this fact, and checks +calls to that function too, including calls made from another package. + +By implementing a common interface, checkers from a variety of sources +can be easily selected, incorporated, and reused in a wide range of +driver programs including command-line tools (such as vet), text editors and +IDEs, build and test systems (such as go build, Bazel, or Buck), test +frameworks, code review tools, code-base indexers (such as SourceGraph), +documentation viewers (such as godoc), batch pipelines for large code +bases, and so on. + + +Analyzer + +The primary type in the API is Analyzer. An Analyzer statically +describes an analysis function: its name, documentation, flags, +relationship to other analyzers, and of course, its logic. + +To define an analysis, a user declares a (logically constant) variable +of type Analyzer. Here is a typical example from one of the analyzers in +the go/analysis/passes/ subdirectory: + + package unusedresult + + var Analyzer = &analysis.Analyzer{ + Name: "unusedresult", + Doc: "check for unused results of calls to some functions", + Run: run, + ... + } + + func run(pass *analysis.Pass) (interface{}, error) { + ... + } + +An analysis driver is a program such as vet that runs a set of +analyses and prints the diagnostics that they report. +The driver program must import the list of Analyzers it needs. +Typically each Analyzer resides in a separate package. +To add a new Analyzer to an existing driver, add another item to the list: + + import ( "unusedresult"; "nilness"; "printf" ) + + var analyses = []*analysis.Analyzer{ + unusedresult.Analyzer, + nilness.Analyzer, + printf.Analyzer, + } + +A driver may use the name, flags, and documentation to provide on-line +help that describes the analyses it performs. +The doc comment contains a brief one-line summary, +optionally followed by paragraphs of explanation. + +The Analyzer type has more fields besides those shown above: + + type Analyzer struct { + Name string + Doc string + Flags flag.FlagSet + Run func(*Pass) (interface{}, error) + RunDespiteErrors bool + ResultType reflect.Type + Requires []*Analyzer + FactTypes []Fact + } + +The Flags field declares a set of named (global) flag variables that +control analysis behavior. Unlike vet, analysis flags are not declared +directly in the command line FlagSet; it is up to the driver to set the +flag variables. A driver for a single analysis, a, might expose its flag +f directly on the command line as -f, whereas a driver for multiple +analyses might prefix the flag name by the analysis name (-a.f) to avoid +ambiguity. An IDE might expose the flags through a graphical interface, +and a batch pipeline might configure them from a config file. +See the "findcall" analyzer for an example of flags in action. + +The RunDespiteErrors flag indicates whether the analysis is equipped to +handle ill-typed code. If not, the driver will skip the analysis if +there were parse or type errors. +The optional ResultType field specifies the type of the result value +computed by this analysis and made available to other analyses. +The Requires field specifies a list of analyses upon which +this one depends and whose results it may access, and it constrains the +order in which a driver may run analyses. +The FactTypes field is discussed in the section on Modularity. +The analysis package provides a Validate function to perform basic +sanity checks on an Analyzer, such as that its Requires graph is +acyclic, its fact and result types are unique, and so on. + +Finally, the Run field contains a function to be called by the driver to +execute the analysis on a single package. The driver passes it an +instance of the Pass type. + + +Pass + +A Pass describes a single unit of work: the application of a particular +Analyzer to a particular package of Go code. +The Pass provides information to the Analyzer's Run function about the +package being analyzed, and provides operations to the Run function for +reporting diagnostics and other information back to the driver. + + type Pass struct { + Fset *token.FileSet + Files []*ast.File + OtherFiles []string + Pkg *types.Package + TypesInfo *types.Info + ResultOf map[*Analyzer]interface{} + Report func(Diagnostic) + ... + } + +The Fset, Files, Pkg, and TypesInfo fields provide the syntax trees, +type information, and source positions for a single package of Go code. + +The OtherFiles field provides the names, but not the contents, of non-Go +files such as assembly that are part of this package. See the "asmdecl" +or "buildtags" analyzers for examples of loading non-Go files and reporting +diagnostics against them. + +The ResultOf field provides the results computed by the analyzers +required by this one, as expressed in its Analyzer.Requires field. The +driver runs the required analyzers first and makes their results +available in this map. Each Analyzer must return a value of the type +described in its Analyzer.ResultType field. +For example, the "ctrlflow" analyzer returns a *ctrlflow.CFGs, which +provides a control-flow graph for each function in the package (see +golang.org/x/tools/go/cfg); the "inspect" analyzer returns a value that +enables other Analyzers to traverse the syntax trees of the package more +efficiently; and the "buildssa" analyzer constructs an SSA-form +intermediate representation. +Each of these Analyzers extends the capabilities of later Analyzers +without adding a dependency to the core API, so an analysis tool pays +only for the extensions it needs. + +The Report function emits a diagnostic, a message associated with a +source position. For most analyses, diagnostics are their primary +result. +For convenience, Pass provides a helper method, Reportf, to report a new +diagnostic by formatting a string. +Diagnostic is defined as: + + type Diagnostic struct { + Pos token.Pos + Category string // optional + Message string + } + +The optional Category field is a short identifier that classifies the +kind of message when an analysis produces several kinds of diagnostic. + +Most Analyzers inspect typed Go syntax trees, but a few, such as asmdecl +and buildtag, inspect the raw text of Go source files or even non-Go +files such as assembly. To report a diagnostic against a line of a +raw text file, use the following sequence: + + content, err := ioutil.ReadFile(filename) + if err != nil { ... } + tf := fset.AddFile(filename, -1, len(content)) + tf.SetLinesForContent(content) + ... + pass.Reportf(tf.LineStart(line), "oops") + + +Modular analysis with Facts + +To improve efficiency and scalability, large programs are routinely +built using separate compilation: units of the program are compiled +separately, and recompiled only when one of their dependencies changes; +independent modules may be compiled in parallel. The same technique may +be applied to static analyses, for the same benefits. Such analyses are +described as "modular". + +A compiler’s type checker is an example of a modular static analysis. +Many other checkers we would like to apply to Go programs can be +understood as alternative or non-standard type systems. For example, +vet's printf checker infers whether a function has the "printf wrapper" +type, and it applies stricter checks to calls of such functions. In +addition, it records which functions are printf wrappers for use by +later analysis passes to identify other printf wrappers by induction. +A result such as “f is a printf wrapper” that is not interesting by +itself but serves as a stepping stone to an interesting result (such as +a diagnostic) is called a "fact". + +The analysis API allows an analysis to define new types of facts, to +associate facts of these types with objects (named entities) declared +within the current package, or with the package as a whole, and to query +for an existing fact of a given type associated with an object or +package. + +An Analyzer that uses facts must declare their types: + + var Analyzer = &analysis.Analyzer{ + Name: "printf", + FactTypes: []analysis.Fact{new(isWrapper)}, + ... + } + + type isWrapper struct{} // => *types.Func f “is a printf wrapper” + +The driver program ensures that facts for a pass’s dependencies are +generated before analyzing the package and is responsible for propagating +facts from one package to another, possibly across address spaces. +Consequently, Facts must be serializable. The API requires that drivers +use the gob encoding, an efficient, robust, self-describing binary +protocol. A fact type may implement the GobEncoder/GobDecoder interfaces +if the default encoding is unsuitable. Facts should be stateless. + +The Pass type has functions to import and export facts, +associated either with an object or with a package: + + type Pass struct { + ... + ExportObjectFact func(types.Object, Fact) + ImportObjectFact func(types.Object, Fact) bool + + ExportPackageFact func(fact Fact) + ImportPackageFact func(*types.Package, Fact) bool + } + +An Analyzer may only export facts associated with the current package or +its objects, though it may import facts from any package or object that +is an import dependency of the current package. + +Conceptually, ExportObjectFact(obj, fact) inserts fact into a hidden map keyed by +the pair (obj, TypeOf(fact)), and the ImportObjectFact function +retrieves the entry from this map and copies its value into the variable +pointed to by fact. This scheme assumes that the concrete type of fact +is a pointer; this assumption is checked by the Validate function. +See the "printf" analyzer for an example of object facts in action. + +Some driver implementations (such as those based on Bazel and Blaze) do +not currently apply analyzers to packages of the standard library. +Therefore, for best results, analyzer authors should not rely on +analysis facts being available for standard packages. +For example, although the printf checker is capable of deducing during +analysis of the log package that log.Printf is a printf wrapper, +this fact is built in to the analyzer so that it correctly checks +calls to log.Printf even when run in a driver that does not apply +it to standard packages. We would like to remove this limitation in future. + + +Testing an Analyzer + +The analysistest subpackage provides utilities for testing an Analyzer. +In a few lines of code, it is possible to run an analyzer on a package +of testdata files and check that it reported all the expected +diagnostics and facts (and no more). Expectations are expressed using +"// want ..." comments in the input code. + + +Standalone commands + +Analyzers are provided in the form of packages that a driver program is +expected to import. The vet command imports a set of several analyzers, +but users may wish to define their own analysis commands that perform +additional checks. To simplify the task of creating an analysis command, +either for a single analyzer or for a whole suite, we provide the +singlechecker and multichecker subpackages. + +The singlechecker package provides the main function for a command that +runs one analyzer. By convention, each analyzer such as +go/passes/findcall should be accompanied by a singlechecker-based +command such as go/analysis/passes/findcall/cmd/findcall, defined in its +entirety as: + + package main + + import ( + "golang.org/x/tools/go/analysis/passes/findcall" + "golang.org/x/tools/go/analysis/singlechecker" + ) + + func main() { singlechecker.Main(findcall.Analyzer) } + +A tool that provides multiple analyzers can use multichecker in a +similar way, giving it the list of Analyzers. + +*/ +package analysis diff --git a/vendor/golang.org/x/tools/go/analysis/passes/inspect/inspect.go b/vendor/golang.org/x/tools/go/analysis/passes/inspect/inspect.go new file mode 100644 index 0000000000..2856df137c --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/passes/inspect/inspect.go @@ -0,0 +1,49 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package inspect defines an Analyzer that provides an AST inspector +// (golang.org/x/tools/go/ast/inspect.Inspect) for the syntax trees of a +// package. It is only a building block for other analyzers. +// +// Example of use in another analysis: +// +// import ( +// "golang.org/x/tools/go/analysis" +// "golang.org/x/tools/go/analysis/passes/inspect" +// "golang.org/x/tools/go/ast/inspector" +// ) +// +// var Analyzer = &analysis.Analyzer{ +// ... +// Requires: []*analysis.Analyzer{inspect.Analyzer}, +// } +// +// func run(pass *analysis.Pass) (interface{}, error) { +// inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) +// inspect.Preorder(nil, func(n ast.Node) { +// ... +// }) +// return nil +// } +// +package inspect + +import ( + "reflect" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/ast/inspector" +) + +var Analyzer = &analysis.Analyzer{ + Name: "inspect", + Doc: "optimize AST traversal for later passes", + Run: run, + RunDespiteErrors: true, + ResultType: reflect.TypeOf(new(inspector.Inspector)), +} + +func run(pass *analysis.Pass) (interface{}, error) { + return inspector.New(pass.Files), nil +} diff --git a/vendor/golang.org/x/tools/go/analysis/validate.go b/vendor/golang.org/x/tools/go/analysis/validate.go new file mode 100644 index 0000000000..be98143461 --- /dev/null +++ b/vendor/golang.org/x/tools/go/analysis/validate.go @@ -0,0 +1,97 @@ +package analysis + +import ( + "fmt" + "reflect" + "unicode" +) + +// Validate reports an error if any of the analyzers are misconfigured. +// Checks include: +// that the name is a valid identifier; +// that the Requires graph is acyclic; +// that analyzer fact types are unique; +// that each fact type is a pointer. +func Validate(analyzers []*Analyzer) error { + // Map each fact type to its sole generating analyzer. + factTypes := make(map[reflect.Type]*Analyzer) + + // Traverse the Requires graph, depth first. + const ( + white = iota + grey + black + finished + ) + color := make(map[*Analyzer]uint8) + var visit func(a *Analyzer) error + visit = func(a *Analyzer) error { + if a == nil { + return fmt.Errorf("nil *Analyzer") + } + if color[a] == white { + color[a] = grey + + // names + if !validIdent(a.Name) { + return fmt.Errorf("invalid analyzer name %q", a) + } + + if a.Doc == "" { + return fmt.Errorf("analyzer %q is undocumented", a) + } + + // fact types + for _, f := range a.FactTypes { + if f == nil { + return fmt.Errorf("analyzer %s has nil FactType", a) + } + t := reflect.TypeOf(f) + if prev := factTypes[t]; prev != nil { + return fmt.Errorf("fact type %s registered by two analyzers: %v, %v", + t, a, prev) + } + if t.Kind() != reflect.Ptr { + return fmt.Errorf("%s: fact type %s is not a pointer", a, t) + } + factTypes[t] = a + } + + // recursion + for i, req := range a.Requires { + if err := visit(req); err != nil { + return fmt.Errorf("%s.Requires[%d]: %v", a.Name, i, err) + } + } + color[a] = black + } + + return nil + } + for _, a := range analyzers { + if err := visit(a); err != nil { + return err + } + } + + // Reject duplicates among analyzers. + // Precondition: color[a] == black. + // Postcondition: color[a] == finished. + for _, a := range analyzers { + if color[a] == finished { + return fmt.Errorf("duplicate analyzer: %s", a.Name) + } + color[a] = finished + } + + return nil +} + +func validIdent(name string) bool { + for i, r := range name { + if !(r == '_' || unicode.IsLetter(r) || i > 0 && unicode.IsDigit(r)) { + return false + } + } + return name != "" +} diff --git a/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go new file mode 100644 index 0000000000..882e3b3d8a --- /dev/null +++ b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go @@ -0,0 +1,523 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package objectpath defines a naming scheme for types.Objects +// (that is, named entities in Go programs) relative to their enclosing +// package. +// +// Type-checker objects are canonical, so they are usually identified by +// their address in memory (a pointer), but a pointer has meaning only +// within one address space. By contrast, objectpath names allow the +// identity of an object to be sent from one program to another, +// establishing a correspondence between types.Object variables that are +// distinct but logically equivalent. +// +// A single object may have multiple paths. In this example, +// type A struct{ X int } +// type B A +// the field X has two paths due to its membership of both A and B. +// The For(obj) function always returns one of these paths, arbitrarily +// but consistently. +package objectpath + +import ( + "fmt" + "strconv" + "strings" + + "go/types" +) + +// A Path is an opaque name that identifies a types.Object +// relative to its package. Conceptually, the name consists of a +// sequence of destructuring operations applied to the package scope +// to obtain the original object. +// The name does not include the package itself. +type Path string + +// Encoding +// +// An object path is a textual and (with training) human-readable encoding +// of a sequence of destructuring operators, starting from a types.Package. +// The sequences represent a path through the package/object/type graph. +// We classify these operators by their type: +// +// PO package->object Package.Scope.Lookup +// OT object->type Object.Type +// TT type->type Type.{Elem,Key,Params,Results,Underlying} [EKPRU] +// TO type->object Type.{At,Field,Method,Obj} [AFMO] +// +// All valid paths start with a package and end at an object +// and thus may be defined by the regular language: +// +// objectpath = PO (OT TT* TO)* +// +// The concrete encoding follows directly: +// - The only PO operator is Package.Scope.Lookup, which requires an identifier. +// - The only OT operator is Object.Type, +// which we encode as '.' because dot cannot appear in an identifier. +// - The TT operators are encoded as [EKPRU]. +// - The OT operators are encoded as [AFMO]; +// three of these (At,Field,Method) require an integer operand, +// which is encoded as a string of decimal digits. +// These indices are stable across different representations +// of the same package, even source and export data. +// +// In the example below, +// +// package p +// +// type T interface { +// f() (a string, b struct{ X int }) +// } +// +// field X has the path "T.UM0.RA1.F0", +// representing the following sequence of operations: +// +// p.Lookup("T") T +// .Type().Underlying().Method(0). f +// .Type().Results().At(1) b +// .Type().Field(0) X +// +// The encoding is not maximally compact---every R or P is +// followed by an A, for example---but this simplifies the +// encoder and decoder. +// +const ( + // object->type operators + opType = '.' // .Type() (Object) + + // type->type operators + opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map) + opKey = 'K' // .Key() (Map) + opParams = 'P' // .Params() (Signature) + opResults = 'R' // .Results() (Signature) + opUnderlying = 'U' // .Underlying() (Named) + + // type->object operators + opAt = 'A' // .At(i) (Tuple) + opField = 'F' // .Field(i) (Struct) + opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored) + opObj = 'O' // .Obj() (Named) +) + +// The For function returns the path to an object relative to its package, +// or an error if the object is not accessible from the package's Scope. +// +// The For function guarantees to return a path only for the following objects: +// - package-level types +// - exported package-level non-types +// - methods +// - parameter and result variables +// - struct fields +// These objects are sufficient to define the API of their package. +// The objects described by a package's export data are drawn from this set. +// +// For does not return a path for predeclared names, imported package +// names, local names, and unexported package-level names (except +// types). +// +// Example: given this definition, +// +// package p +// +// type T interface { +// f() (a string, b struct{ X int }) +// } +// +// For(X) would return a path that denotes the following sequence of operations: +// +// p.Scope().Lookup("T") (TypeName T) +// .Type().Underlying().Method(0). (method Func f) +// .Type().Results().At(1) (field Var b) +// .Type().Field(0) (field Var X) +// +// where p is the package (*types.Package) to which X belongs. +func For(obj types.Object) (Path, error) { + pkg := obj.Pkg() + + // This table lists the cases of interest. + // + // Object Action + // ------ ------ + // nil reject + // builtin reject + // pkgname reject + // label reject + // var + // package-level accept + // func param/result accept + // local reject + // struct field accept + // const + // package-level accept + // local reject + // func + // package-level accept + // init functions reject + // concrete method accept + // interface method accept + // type + // package-level accept + // local reject + // + // The only accessible package-level objects are members of pkg itself. + // + // The cases are handled in four steps: + // + // 1. reject nil and builtin + // 2. accept package-level objects + // 3. reject obviously invalid objects + // 4. search the API for the path to the param/result/field/method. + + // 1. reference to nil or builtin? + if pkg == nil { + return "", fmt.Errorf("predeclared %s has no path", obj) + } + scope := pkg.Scope() + + // 2. package-level object? + if scope.Lookup(obj.Name()) == obj { + // Only exported objects (and non-exported types) have a path. + // Non-exported types may be referenced by other objects. + if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() { + return "", fmt.Errorf("no path for non-exported %v", obj) + } + return Path(obj.Name()), nil + } + + // 3. Not a package-level object. + // Reject obviously non-viable cases. + switch obj := obj.(type) { + case *types.Const, // Only package-level constants have a path. + *types.TypeName, // Only package-level types have a path. + *types.Label, // Labels are function-local. + *types.PkgName: // PkgNames are file-local. + return "", fmt.Errorf("no path for %v", obj) + + case *types.Var: + // Could be: + // - a field (obj.IsField()) + // - a func parameter or result + // - a local var. + // Sadly there is no way to distinguish + // a param/result from a local + // so we must proceed to the find. + + case *types.Func: + // A func, if not package-level, must be a method. + if recv := obj.Type().(*types.Signature).Recv(); recv == nil { + return "", fmt.Errorf("func is not a method: %v", obj) + } + // TODO(adonovan): opt: if the method is concrete, + // do a specialized version of the rest of this function so + // that it's O(1) not O(|scope|). Basically 'find' is needed + // only for struct fields and interface methods. + + default: + panic(obj) + } + + // 4. Search the API for the path to the var (field/param/result) or method. + + // First inspect package-level named types. + // In the presence of path aliases, these give + // the best paths because non-types may + // refer to types, but not the reverse. + empty := make([]byte, 0, 48) // initial space + for _, name := range scope.Names() { + o := scope.Lookup(name) + tname, ok := o.(*types.TypeName) + if !ok { + continue // handle non-types in second pass + } + + path := append(empty, name...) + path = append(path, opType) + + T := o.Type() + + if tname.IsAlias() { + // type alias + if r := find(obj, T, path); r != nil { + return Path(r), nil + } + } else { + // defined (named) type + if r := find(obj, T.Underlying(), append(path, opUnderlying)); r != nil { + return Path(r), nil + } + } + } + + // Then inspect everything else: + // non-types, and declared methods of defined types. + for _, name := range scope.Names() { + o := scope.Lookup(name) + path := append(empty, name...) + if _, ok := o.(*types.TypeName); !ok { + if o.Exported() { + // exported non-type (const, var, func) + if r := find(obj, o.Type(), append(path, opType)); r != nil { + return Path(r), nil + } + } + continue + } + + // Inspect declared methods of defined types. + if T, ok := o.Type().(*types.Named); ok { + path = append(path, opType) + for i := 0; i < T.NumMethods(); i++ { + m := T.Method(i) + path2 := appendOpArg(path, opMethod, i) + if m == obj { + return Path(path2), nil // found declared method + } + if r := find(obj, m.Type(), append(path2, opType)); r != nil { + return Path(r), nil + } + } + } + } + + return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path()) +} + +func appendOpArg(path []byte, op byte, arg int) []byte { + path = append(path, op) + path = strconv.AppendInt(path, int64(arg), 10) + return path +} + +// find finds obj within type T, returning the path to it, or nil if not found. +func find(obj types.Object, T types.Type, path []byte) []byte { + switch T := T.(type) { + case *types.Basic, *types.Named: + // Named types belonging to pkg were handled already, + // so T must belong to another package. No path. + return nil + case *types.Pointer: + return find(obj, T.Elem(), append(path, opElem)) + case *types.Slice: + return find(obj, T.Elem(), append(path, opElem)) + case *types.Array: + return find(obj, T.Elem(), append(path, opElem)) + case *types.Chan: + return find(obj, T.Elem(), append(path, opElem)) + case *types.Map: + if r := find(obj, T.Key(), append(path, opKey)); r != nil { + return r + } + return find(obj, T.Elem(), append(path, opElem)) + case *types.Signature: + if r := find(obj, T.Params(), append(path, opParams)); r != nil { + return r + } + return find(obj, T.Results(), append(path, opResults)) + case *types.Struct: + for i := 0; i < T.NumFields(); i++ { + f := T.Field(i) + path2 := appendOpArg(path, opField, i) + if f == obj { + return path2 // found field var + } + if r := find(obj, f.Type(), append(path2, opType)); r != nil { + return r + } + } + return nil + case *types.Tuple: + for i := 0; i < T.Len(); i++ { + v := T.At(i) + path2 := appendOpArg(path, opAt, i) + if v == obj { + return path2 // found param/result var + } + if r := find(obj, v.Type(), append(path2, opType)); r != nil { + return r + } + } + return nil + case *types.Interface: + for i := 0; i < T.NumMethods(); i++ { + m := T.Method(i) + path2 := appendOpArg(path, opMethod, i) + if m == obj { + return path2 // found interface method + } + if r := find(obj, m.Type(), append(path2, opType)); r != nil { + return r + } + } + return nil + } + panic(T) +} + +// Object returns the object denoted by path p within the package pkg. +func Object(pkg *types.Package, p Path) (types.Object, error) { + if p == "" { + return nil, fmt.Errorf("empty path") + } + + pathstr := string(p) + var pkgobj, suffix string + if dot := strings.IndexByte(pathstr, opType); dot < 0 { + pkgobj = pathstr + } else { + pkgobj = pathstr[:dot] + suffix = pathstr[dot:] // suffix starts with "." + } + + obj := pkg.Scope().Lookup(pkgobj) + if obj == nil { + return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj) + } + + // abstraction of *types.{Pointer,Slice,Array,Chan,Map} + type hasElem interface { + Elem() types.Type + } + // abstraction of *types.{Interface,Named} + type hasMethods interface { + Method(int) *types.Func + NumMethods() int + } + + // The loop state is the pair (t, obj), + // exactly one of which is non-nil, initially obj. + // All suffixes start with '.' (the only object->type operation), + // followed by optional type->type operations, + // then a type->object operation. + // The cycle then repeats. + var t types.Type + for suffix != "" { + code := suffix[0] + suffix = suffix[1:] + + // Codes [AFM] have an integer operand. + var index int + switch code { + case opAt, opField, opMethod: + rest := strings.TrimLeft(suffix, "0123456789") + numerals := suffix[:len(suffix)-len(rest)] + suffix = rest + i, err := strconv.Atoi(numerals) + if err != nil { + return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code) + } + index = int(i) + case opObj: + // no operand + default: + // The suffix must end with a type->object operation. + if suffix == "" { + return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code) + } + } + + if code == opType { + if t != nil { + return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType) + } + t = obj.Type() + obj = nil + continue + } + + if t == nil { + return nil, fmt.Errorf("invalid path: code %q in object context", code) + } + + // Inv: t != nil, obj == nil + + switch code { + case opElem: + hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t) + } + t = hasElem.Elem() + + case opKey: + mapType, ok := t.(*types.Map) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t) + } + t = mapType.Key() + + case opParams: + sig, ok := t.(*types.Signature) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t) + } + t = sig.Params() + + case opResults: + sig, ok := t.(*types.Signature) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t) + } + t = sig.Results() + + case opUnderlying: + named, ok := t.(*types.Named) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %s, want named)", code, t, t) + } + t = named.Underlying() + + case opAt: + tuple, ok := t.(*types.Tuple) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %s, want tuple)", code, t, t) + } + if n := tuple.Len(); index >= n { + return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) + } + obj = tuple.At(index) + t = nil + + case opField: + structType, ok := t.(*types.Struct) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t) + } + if n := structType.NumFields(); index >= n { + return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n) + } + obj = structType.Field(index) + t = nil + + case opMethod: + hasMethods, ok := t.(hasMethods) // Interface or Named + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %s, want interface or named)", code, t, t) + } + if n := hasMethods.NumMethods(); index >= n { + return nil, fmt.Errorf("method index %d out of range [0-%d)", index, n) + } + obj = hasMethods.Method(index) + t = nil + + case opObj: + named, ok := t.(*types.Named) + if !ok { + return nil, fmt.Errorf("cannot apply %q to %s (got %s, want named)", code, t, t) + } + obj = named.Obj() + t = nil + + default: + return nil, fmt.Errorf("invalid path: unknown code %q", code) + } + } + + if obj.Pkg() != pkg { + return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj) + } + + return obj, nil // success +} diff --git a/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go b/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go new file mode 100644 index 0000000000..26586810c7 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go @@ -0,0 +1,118 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package analysisinternal exposes internal-only fields from go/analysis. +package analysisinternal + +import ( + "bytes" + "fmt" + "go/ast" + "go/token" + "go/types" + "strings" + + "golang.org/x/tools/go/ast/astutil" +) + +func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos { + // Get the end position for the type error. + offset, end := fset.PositionFor(start, false).Offset, start + if offset >= len(src) { + return end + } + if width := bytes.IndexAny(src[offset:], " \n,():;[]+-*"); width > 0 { + end = start + token.Pos(width) + } + return end +} + +func ZeroValue(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { + under := typ + if n, ok := typ.(*types.Named); ok { + under = n.Underlying() + } + switch u := under.(type) { + case *types.Basic: + switch { + case u.Info()&types.IsNumeric != 0: + return &ast.BasicLit{Kind: token.INT, Value: "0"} + case u.Info()&types.IsBoolean != 0: + return &ast.Ident{Name: "false"} + case u.Info()&types.IsString != 0: + return &ast.BasicLit{Kind: token.STRING, Value: `""`} + default: + panic("unknown basic type") + } + case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice: + return ast.NewIdent("nil") + case *types.Struct: + texpr := typeExpr(fset, f, pkg, typ) // typ because we want the name here. + if texpr == nil { + return nil + } + return &ast.CompositeLit{ + Type: texpr, + } + case *types.Array: + texpr := typeExpr(fset, f, pkg, u.Elem()) + if texpr == nil { + return nil + } + return &ast.CompositeLit{ + Type: &ast.ArrayType{ + Elt: texpr, + Len: &ast.BasicLit{Kind: token.INT, Value: fmt.Sprintf("%v", u.Len())}, + }, + } + } + return nil +} + +func typeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { + switch t := typ.(type) { + case *types.Basic: + switch t.Kind() { + case types.UnsafePointer: + return &ast.SelectorExpr{X: ast.NewIdent("unsafe"), Sel: ast.NewIdent("Pointer")} + default: + return ast.NewIdent(t.Name()) + } + case *types.Named: + if t.Obj().Pkg() == pkg { + return ast.NewIdent(t.Obj().Name()) + } + pkgName := t.Obj().Pkg().Name() + // If the file already imports the package under another name, use that. + for _, group := range astutil.Imports(fset, f) { + for _, cand := range group { + if strings.Trim(cand.Path.Value, `"`) == t.Obj().Pkg().Path() { + if cand.Name != nil && cand.Name.Name != "" { + pkgName = cand.Name.Name + } + } + } + } + if pkgName == "." { + return ast.NewIdent(t.Obj().Name()) + } + return &ast.SelectorExpr{ + X: ast.NewIdent(pkgName), + Sel: ast.NewIdent(t.Obj().Name()), + } + default: + return nil // TODO: anonymous structs, but who does that + } +} + +var GetTypeErrors = func(p interface{}) []types.Error { return nil } +var SetTypeErrors = func(p interface{}, errors []types.Error) {} + +type TypeErrorPass string + +const ( + NoNewVars TypeErrorPass = "nonewvars" + NoResultValues TypeErrorPass = "noresultvalues" + UndeclaredName TypeErrorPass = "undeclaredname" +) diff --git a/vendor/honnef.co/go/tools/LICENSE-THIRD-PARTY b/vendor/honnef.co/go/tools/LICENSE-THIRD-PARTY new file mode 100644 index 0000000000..7c241b71ae --- /dev/null +++ b/vendor/honnef.co/go/tools/LICENSE-THIRD-PARTY @@ -0,0 +1,226 @@ +Staticcheck and its related tools make use of third party projects, +either by reusing their code, or by statically linking them into +resulting binaries. These projects are: + +* The Go Programming Language - https://golang.org/ + + Copyright (c) 2009 The Go Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +* github.com/BurntSushi/toml - https://github.com/BurntSushi/toml + + The MIT License (MIT) + + Copyright (c) 2013 TOML authors + + 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. + + +* github.com/google/renameio - https://github.com/google/renameio + + Copyright 2018 Google 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. + + +* github.com/kisielk/gotool – https://github.com/kisielk/gotool + + Copyright (c) 2013 Kamil Kisiel + + 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. + + All the files in this distribution are covered under either the MIT + license (see the file LICENSE) except some files mentioned below. + + match.go, match_test.go: + + Copyright (c) 2009 The Go Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +* github.com/rogpeppe/go-internal - https://github.com/rogpeppe/go-internal + + Copyright (c) 2018 The Go Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +* golang.org/x/mod/module - https://github.com/golang/mod + + Copyright (c) 2009 The Go Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +* golang.org/x/tools/go/analysis - https://github.com/golang/tools + + Copyright (c) 2009 The Go Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/vendor/honnef.co/go/tools/callgraph/callgraph.go b/vendor/honnef.co/go/tools/callgraph/callgraph.go deleted file mode 100644 index d93a20a3a1..0000000000 --- a/vendor/honnef.co/go/tools/callgraph/callgraph.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* - -Package callgraph defines the call graph and various algorithms -and utilities to operate on it. - -A call graph is a labelled directed graph whose nodes represent -functions and whose edge labels represent syntactic function call -sites. The presence of a labelled edge (caller, site, callee) -indicates that caller may call callee at the specified call site. - -A call graph is a multigraph: it may contain multiple edges (caller, -*, callee) connecting the same pair of nodes, so long as the edges -differ by label; this occurs when one function calls another function -from multiple call sites. Also, it may contain multiple edges -(caller, site, *) that differ only by callee; this indicates a -polymorphic call. - -A SOUND call graph is one that overapproximates the dynamic calling -behaviors of the program in all possible executions. One call graph -is more PRECISE than another if it is a smaller overapproximation of -the dynamic behavior. - -All call graphs have a synthetic root node which is responsible for -calling main() and init(). - -Calls to built-in functions (e.g. panic, println) are not represented -in the call graph; they are treated like built-in operators of the -language. - -*/ -package callgraph // import "honnef.co/go/tools/callgraph" - -// TODO(adonovan): add a function to eliminate wrappers from the -// callgraph, preserving topology. -// More generally, we could eliminate "uninteresting" nodes such as -// nodes from packages we don't care about. - -import ( - "fmt" - "go/token" - - "honnef.co/go/tools/ssa" -) - -// A Graph represents a call graph. -// -// A graph may contain nodes that are not reachable from the root. -// If the call graph is sound, such nodes indicate unreachable -// functions. -// -type Graph struct { - Root *Node // the distinguished root node - Nodes map[*ssa.Function]*Node // all nodes by function -} - -// New returns a new Graph with the specified root node. -func New(root *ssa.Function) *Graph { - g := &Graph{Nodes: make(map[*ssa.Function]*Node)} - g.Root = g.CreateNode(root) - return g -} - -// CreateNode returns the Node for fn, creating it if not present. -func (g *Graph) CreateNode(fn *ssa.Function) *Node { - n, ok := g.Nodes[fn] - if !ok { - n = &Node{Func: fn, ID: len(g.Nodes)} - g.Nodes[fn] = n - } - return n -} - -// A Node represents a node in a call graph. -type Node struct { - Func *ssa.Function // the function this node represents - ID int // 0-based sequence number - In []*Edge // unordered set of incoming call edges (n.In[*].Callee == n) - Out []*Edge // unordered set of outgoing call edges (n.Out[*].Caller == n) -} - -func (n *Node) String() string { - return fmt.Sprintf("n%d:%s", n.ID, n.Func) -} - -// A Edge represents an edge in the call graph. -// -// Site is nil for edges originating in synthetic or intrinsic -// functions, e.g. reflect.Call or the root of the call graph. -type Edge struct { - Caller *Node - Site ssa.CallInstruction - Callee *Node -} - -func (e Edge) String() string { - return fmt.Sprintf("%s --> %s", e.Caller, e.Callee) -} - -func (e Edge) Description() string { - var prefix string - switch e.Site.(type) { - case nil: - return "synthetic call" - case *ssa.Go: - prefix = "concurrent " - case *ssa.Defer: - prefix = "deferred " - } - return prefix + e.Site.Common().Description() -} - -func (e Edge) Pos() token.Pos { - if e.Site == nil { - return token.NoPos - } - return e.Site.Pos() -} - -// AddEdge adds the edge (caller, site, callee) to the call graph. -// Elimination of duplicate edges is the caller's responsibility. -func AddEdge(caller *Node, site ssa.CallInstruction, callee *Node) { - e := &Edge{caller, site, callee} - callee.In = append(callee.In, e) - caller.Out = append(caller.Out, e) -} diff --git a/vendor/honnef.co/go/tools/callgraph/static/static.go b/vendor/honnef.co/go/tools/callgraph/static/static.go deleted file mode 100644 index 5444e84113..0000000000 --- a/vendor/honnef.co/go/tools/callgraph/static/static.go +++ /dev/null @@ -1,35 +0,0 @@ -// Package static computes the call graph of a Go program containing -// only static call edges. -package static // import "honnef.co/go/tools/callgraph/static" - -import ( - "honnef.co/go/tools/callgraph" - "honnef.co/go/tools/ssa" - "honnef.co/go/tools/ssa/ssautil" -) - -// CallGraph computes the call graph of the specified program -// considering only static calls. -// -func CallGraph(prog *ssa.Program) *callgraph.Graph { - cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph - - // TODO(adonovan): opt: use only a single pass over the ssa.Program. - // TODO(adonovan): opt: this is slower than RTA (perhaps because - // the lower precision means so many edges are allocated)! - for f := range ssautil.AllFunctions(prog) { - fnode := cg.CreateNode(f) - for _, b := range f.Blocks { - for _, instr := range b.Instrs { - if site, ok := instr.(ssa.CallInstruction); ok { - if g := site.Common().StaticCallee(); g != nil { - gnode := cg.CreateNode(g) - callgraph.AddEdge(fnode, site, gnode) - } - } - } - } - } - - return cg -} diff --git a/vendor/honnef.co/go/tools/callgraph/util.go b/vendor/honnef.co/go/tools/callgraph/util.go deleted file mode 100644 index 7aeda96415..0000000000 --- a/vendor/honnef.co/go/tools/callgraph/util.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package callgraph - -import "honnef.co/go/tools/ssa" - -// This file provides various utilities over call graphs, such as -// visitation and path search. - -// CalleesOf returns a new set containing all direct callees of the -// caller node. -// -func CalleesOf(caller *Node) map[*Node]bool { - callees := make(map[*Node]bool) - for _, e := range caller.Out { - callees[e.Callee] = true - } - return callees -} - -// GraphVisitEdges visits all the edges in graph g in depth-first order. -// The edge function is called for each edge in postorder. If it -// returns non-nil, visitation stops and GraphVisitEdges returns that -// value. -// -func GraphVisitEdges(g *Graph, edge func(*Edge) error) error { - seen := make(map[*Node]bool) - var visit func(n *Node) error - visit = func(n *Node) error { - if !seen[n] { - seen[n] = true - for _, e := range n.Out { - if err := visit(e.Callee); err != nil { - return err - } - if err := edge(e); err != nil { - return err - } - } - } - return nil - } - for _, n := range g.Nodes { - if err := visit(n); err != nil { - return err - } - } - return nil -} - -// PathSearch finds an arbitrary path starting at node start and -// ending at some node for which isEnd() returns true. On success, -// PathSearch returns the path as an ordered list of edges; on -// failure, it returns nil. -// -func PathSearch(start *Node, isEnd func(*Node) bool) []*Edge { - stack := make([]*Edge, 0, 32) - seen := make(map[*Node]bool) - var search func(n *Node) []*Edge - search = func(n *Node) []*Edge { - if !seen[n] { - seen[n] = true - if isEnd(n) { - return stack - } - for _, e := range n.Out { - stack = append(stack, e) // push - if found := search(e.Callee); found != nil { - return found - } - stack = stack[:len(stack)-1] // pop - } - } - return nil - } - return search(start) -} - -// DeleteSyntheticNodes removes from call graph g all nodes for -// synthetic functions (except g.Root and package initializers), -// preserving the topology. In effect, calls to synthetic wrappers -// are "inlined". -// -func (g *Graph) DeleteSyntheticNodes() { - // Measurements on the standard library and go.tools show that - // resulting graph has ~15% fewer nodes and 4-8% fewer edges - // than the input. - // - // Inlining a wrapper of in-degree m, out-degree n adds m*n - // and removes m+n edges. Since most wrappers are monomorphic - // (n=1) this results in a slight reduction. Polymorphic - // wrappers (n>1), e.g. from embedding an interface value - // inside a struct to satisfy some interface, cause an - // increase in the graph, but they seem to be uncommon. - - // Hash all existing edges to avoid creating duplicates. - edges := make(map[Edge]bool) - for _, cgn := range g.Nodes { - for _, e := range cgn.Out { - edges[*e] = true - } - } - for fn, cgn := range g.Nodes { - if cgn == g.Root || fn.Synthetic == "" || isInit(cgn.Func) { - continue // keep - } - for _, eIn := range cgn.In { - for _, eOut := range cgn.Out { - newEdge := Edge{eIn.Caller, eIn.Site, eOut.Callee} - if edges[newEdge] { - continue // don't add duplicate - } - AddEdge(eIn.Caller, eIn.Site, eOut.Callee) - edges[newEdge] = true - } - } - g.DeleteNode(cgn) - } -} - -func isInit(fn *ssa.Function) bool { - return fn.Pkg != nil && fn.Pkg.Func("init") == fn -} - -// DeleteNode removes node n and its edges from the graph g. -// (NB: not efficient for batch deletion.) -func (g *Graph) DeleteNode(n *Node) { - n.deleteIns() - n.deleteOuts() - delete(g.Nodes, n.Func) -} - -// deleteIns deletes all incoming edges to n. -func (n *Node) deleteIns() { - for _, e := range n.In { - removeOutEdge(e) - } - n.In = nil -} - -// deleteOuts deletes all outgoing edges from n. -func (n *Node) deleteOuts() { - for _, e := range n.Out { - removeInEdge(e) - } - n.Out = nil -} - -// removeOutEdge removes edge.Caller's outgoing edge 'edge'. -func removeOutEdge(edge *Edge) { - caller := edge.Caller - n := len(caller.Out) - for i, e := range caller.Out { - if e == edge { - // Replace it with the final element and shrink the slice. - caller.Out[i] = caller.Out[n-1] - caller.Out[n-1] = nil // aid GC - caller.Out = caller.Out[:n-1] - return - } - } - panic("edge not found: " + edge.String()) -} - -// removeInEdge removes edge.Callee's incoming edge 'edge'. -func removeInEdge(edge *Edge) { - caller := edge.Callee - n := len(caller.In) - for i, e := range caller.In { - if e == edge { - // Replace it with the final element and shrink the slice. - caller.In[i] = caller.In[n-1] - caller.In[n-1] = nil // aid GC - caller.In = caller.In[:n-1] - return - } - } - panic("edge not found: " + edge.String()) -} diff --git a/vendor/honnef.co/go/tools/cmd/staticcheck/staticcheck.go b/vendor/honnef.co/go/tools/cmd/staticcheck/staticcheck.go index 6f381850d7..4f504dc39d 100644 --- a/vendor/honnef.co/go/tools/cmd/staticcheck/staticcheck.go +++ b/vendor/honnef.co/go/tools/cmd/staticcheck/staticcheck.go @@ -2,8 +2,10 @@ package main // import "honnef.co/go/tools/cmd/staticcheck" import ( + "log" "os" + "golang.org/x/tools/go/analysis" "honnef.co/go/tools/lint" "honnef.co/go/tools/lint/lintutil" "honnef.co/go/tools/simple" @@ -14,14 +16,29 @@ import ( func main() { fs := lintutil.FlagSet("staticcheck") + wholeProgram := fs.Bool("unused.whole-program", false, "Run unused in whole program mode") + debug := fs.String("debug.unused-graph", "", "Write unused's object graph to `file`") fs.Parse(os.Args[1:]) - checkers := []lint.Checker{ - simple.NewChecker(), - staticcheck.NewChecker(), - stylecheck.NewChecker(), - &unused.Checker{}, + var cs []*analysis.Analyzer + for _, v := range simple.Analyzers { + cs = append(cs, v) + } + for _, v := range staticcheck.Analyzers { + cs = append(cs, v) + } + for _, v := range stylecheck.Analyzers { + cs = append(cs, v) } - lintutil.ProcessFlagSet(checkers, fs) + u := unused.NewChecker(*wholeProgram) + if *debug != "" { + f, err := os.OpenFile(*debug, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) + if err != nil { + log.Fatal(err) + } + u.Debug = f + } + cums := []lint.CumulativeChecker{u} + lintutil.ProcessFlagSet(cs, cums, fs) } diff --git a/vendor/honnef.co/go/tools/config/config.go b/vendor/honnef.co/go/tools/config/config.go index cfde5d51ac..c22093a6d9 100644 --- a/vendor/honnef.co/go/tools/config/config.go +++ b/vendor/honnef.co/go/tools/config/config.go @@ -1,12 +1,63 @@ package config import ( + "bytes" + "fmt" "os" "path/filepath" + "reflect" + "strings" "github.com/BurntSushi/toml" + "golang.org/x/tools/go/analysis" ) +var Analyzer = &analysis.Analyzer{ + Name: "config", + Doc: "loads configuration for the current package tree", + Run: func(pass *analysis.Pass) (interface{}, error) { + if len(pass.Files) == 0 { + cfg := DefaultConfig + return &cfg, nil + } + cache, err := os.UserCacheDir() + if err != nil { + cache = "" + } + var path string + for _, f := range pass.Files { + p := pass.Fset.PositionFor(f.Pos(), true).Filename + // FIXME(dh): using strings.HasPrefix isn't technically + // correct, but it should be good enough for now. + if cache != "" && strings.HasPrefix(p, cache) { + // File in the build cache of the standard Go build system + continue + } + path = p + break + } + + if path == "" { + // The package only consists of generated files. + cfg := DefaultConfig + return &cfg, nil + } + + dir := filepath.Dir(path) + cfg, err := Load(dir) + if err != nil { + return nil, fmt.Errorf("error loading staticcheck.conf: %s", err) + } + return &cfg, nil + }, + RunDespiteErrors: true, + ResultType: reflect.TypeOf((*Config)(nil)), +} + +func For(pass *analysis.Pass) *Config { + return pass.ResultOf[Analyzer].(*Config) +} + func mergeLists(a, b []string) []string { out := make([]string, 0, len(a)+len(b)) for _, el := range b { @@ -73,7 +124,18 @@ type Config struct { HTTPStatusCodeWhitelist []string `toml:"http_status_code_whitelist"` } -var defaultConfig = Config{ +func (c Config) String() string { + buf := &bytes.Buffer{} + + fmt.Fprintf(buf, "Checks: %#v\n", c.Checks) + fmt.Fprintf(buf, "Initialisms: %#v\n", c.Initialisms) + fmt.Fprintf(buf, "DotImportWhitelist: %#v\n", c.DotImportWhitelist) + fmt.Fprintf(buf, "HTTPStatusCodeWhitelist: %#v", c.HTTPStatusCodeWhitelist) + + return buf.String() +} + +var DefaultConfig = Config{ Checks: []string{"all", "-ST1000", "-ST1003", "-ST1016"}, Initialisms: []string{ "ACL", "API", "ASCII", "CPU", "CSS", "DNS", @@ -120,7 +182,7 @@ func parseConfigs(dir string) ([]Config, error) { } dir = ndir } - out = append(out, defaultConfig) + out = append(out, DefaultConfig) if len(out) < 2 { return out, nil } diff --git a/vendor/honnef.co/go/tools/facts/deprecated.go b/vendor/honnef.co/go/tools/facts/deprecated.go new file mode 100644 index 0000000000..8587b0e0ea --- /dev/null +++ b/vendor/honnef.co/go/tools/facts/deprecated.go @@ -0,0 +1,144 @@ +package facts + +import ( + "go/ast" + "go/token" + "go/types" + "reflect" + "strings" + + "golang.org/x/tools/go/analysis" +) + +type IsDeprecated struct{ Msg string } + +func (*IsDeprecated) AFact() {} +func (d *IsDeprecated) String() string { return "Deprecated: " + d.Msg } + +type DeprecatedResult struct { + Objects map[types.Object]*IsDeprecated + Packages map[*types.Package]*IsDeprecated +} + +var Deprecated = &analysis.Analyzer{ + Name: "fact_deprecated", + Doc: "Mark deprecated objects", + Run: deprecated, + FactTypes: []analysis.Fact{(*IsDeprecated)(nil)}, + ResultType: reflect.TypeOf(DeprecatedResult{}), +} + +func deprecated(pass *analysis.Pass) (interface{}, error) { + var names []*ast.Ident + + extractDeprecatedMessage := func(docs []*ast.CommentGroup) string { + for _, doc := range docs { + if doc == nil { + continue + } + parts := strings.Split(doc.Text(), "\n\n") + last := parts[len(parts)-1] + if !strings.HasPrefix(last, "Deprecated: ") { + continue + } + alt := last[len("Deprecated: "):] + alt = strings.Replace(alt, "\n", " ", -1) + return alt + } + return "" + } + doDocs := func(names []*ast.Ident, docs []*ast.CommentGroup) { + alt := extractDeprecatedMessage(docs) + if alt == "" { + return + } + + for _, name := range names { + obj := pass.TypesInfo.ObjectOf(name) + pass.ExportObjectFact(obj, &IsDeprecated{alt}) + } + } + + var docs []*ast.CommentGroup + for _, f := range pass.Files { + docs = append(docs, f.Doc) + } + if alt := extractDeprecatedMessage(docs); alt != "" { + // Don't mark package syscall as deprecated, even though + // it is. A lot of people still use it for simple + // constants like SIGKILL, and I am not comfortable + // telling them to use x/sys for that. + if pass.Pkg.Path() != "syscall" { + pass.ExportPackageFact(&IsDeprecated{alt}) + } + } + + docs = docs[:0] + for _, f := range pass.Files { + fn := func(node ast.Node) bool { + if node == nil { + return true + } + var ret bool + switch node := node.(type) { + case *ast.GenDecl: + switch node.Tok { + case token.TYPE, token.CONST, token.VAR: + docs = append(docs, node.Doc) + return true + default: + return false + } + case *ast.FuncDecl: + docs = append(docs, node.Doc) + names = []*ast.Ident{node.Name} + ret = false + case *ast.TypeSpec: + docs = append(docs, node.Doc) + names = []*ast.Ident{node.Name} + ret = true + case *ast.ValueSpec: + docs = append(docs, node.Doc) + names = node.Names + ret = false + case *ast.File: + return true + case *ast.StructType: + for _, field := range node.Fields.List { + doDocs(field.Names, []*ast.CommentGroup{field.Doc}) + } + return false + case *ast.InterfaceType: + for _, field := range node.Methods.List { + doDocs(field.Names, []*ast.CommentGroup{field.Doc}) + } + return false + default: + return false + } + if len(names) == 0 || len(docs) == 0 { + return ret + } + doDocs(names, docs) + + docs = docs[:0] + names = nil + return ret + } + ast.Inspect(f, fn) + } + + out := DeprecatedResult{ + Objects: map[types.Object]*IsDeprecated{}, + Packages: map[*types.Package]*IsDeprecated{}, + } + + for _, fact := range pass.AllObjectFacts() { + out.Objects[fact.Object] = fact.Fact.(*IsDeprecated) + } + for _, fact := range pass.AllPackageFacts() { + out.Packages[fact.Package] = fact.Fact.(*IsDeprecated) + } + + return out, nil +} diff --git a/vendor/honnef.co/go/tools/facts/generated.go b/vendor/honnef.co/go/tools/facts/generated.go new file mode 100644 index 0000000000..1ed9563a30 --- /dev/null +++ b/vendor/honnef.co/go/tools/facts/generated.go @@ -0,0 +1,86 @@ +package facts + +import ( + "bufio" + "bytes" + "io" + "os" + "reflect" + "strings" + + "golang.org/x/tools/go/analysis" +) + +type Generator int + +// A list of known generators we can detect +const ( + Unknown Generator = iota + Goyacc + Cgo + Stringer +) + +var ( + // used by cgo before Go 1.11 + oldCgo = []byte("// Created by cgo - DO NOT EDIT") + prefix = []byte("// Code generated ") + suffix = []byte(" DO NOT EDIT.") + nl = []byte("\n") + crnl = []byte("\r\n") +) + +func isGenerated(path string) (Generator, bool) { + f, err := os.Open(path) + if err != nil { + return 0, false + } + defer f.Close() + br := bufio.NewReader(f) + for { + s, err := br.ReadBytes('\n') + if err != nil && err != io.EOF { + return 0, false + } + s = bytes.TrimSuffix(s, crnl) + s = bytes.TrimSuffix(s, nl) + if bytes.HasPrefix(s, prefix) && bytes.HasSuffix(s, suffix) { + text := string(s[len(prefix) : len(s)-len(suffix)]) + switch text { + case "by goyacc.": + return Goyacc, true + case "by cmd/cgo;": + return Cgo, true + } + if strings.HasPrefix(text, `by "stringer `) { + return Stringer, true + } + return Unknown, true + } + if bytes.Equal(s, oldCgo) { + return Cgo, true + } + if err == io.EOF { + break + } + } + return 0, false +} + +var Generated = &analysis.Analyzer{ + Name: "isgenerated", + Doc: "annotate file names that have been code generated", + Run: func(pass *analysis.Pass) (interface{}, error) { + m := map[string]Generator{} + for _, f := range pass.Files { + path := pass.Fset.PositionFor(f.Pos(), false).Filename + g, ok := isGenerated(path) + if ok { + m[path] = g + } + } + return m, nil + }, + RunDespiteErrors: true, + ResultType: reflect.TypeOf(map[string]Generator{}), +} diff --git a/vendor/honnef.co/go/tools/facts/purity.go b/vendor/honnef.co/go/tools/facts/purity.go new file mode 100644 index 0000000000..861ca41104 --- /dev/null +++ b/vendor/honnef.co/go/tools/facts/purity.go @@ -0,0 +1,175 @@ +package facts + +import ( + "go/token" + "go/types" + "reflect" + + "golang.org/x/tools/go/analysis" + "honnef.co/go/tools/functions" + "honnef.co/go/tools/internal/passes/buildssa" + "honnef.co/go/tools/ssa" +) + +type IsPure struct{} + +func (*IsPure) AFact() {} +func (d *IsPure) String() string { return "is pure" } + +type PurityResult map[*types.Func]*IsPure + +var Purity = &analysis.Analyzer{ + Name: "fact_purity", + Doc: "Mark pure functions", + Run: purity, + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + FactTypes: []analysis.Fact{(*IsPure)(nil)}, + ResultType: reflect.TypeOf(PurityResult{}), +} + +var pureStdlib = map[string]struct{}{ + "errors.New": {}, + "fmt.Errorf": {}, + "fmt.Sprintf": {}, + "fmt.Sprint": {}, + "sort.Reverse": {}, + "strings.Map": {}, + "strings.Repeat": {}, + "strings.Replace": {}, + "strings.Title": {}, + "strings.ToLower": {}, + "strings.ToLowerSpecial": {}, + "strings.ToTitle": {}, + "strings.ToTitleSpecial": {}, + "strings.ToUpper": {}, + "strings.ToUpperSpecial": {}, + "strings.Trim": {}, + "strings.TrimFunc": {}, + "strings.TrimLeft": {}, + "strings.TrimLeftFunc": {}, + "strings.TrimPrefix": {}, + "strings.TrimRight": {}, + "strings.TrimRightFunc": {}, + "strings.TrimSpace": {}, + "strings.TrimSuffix": {}, + "(*net/http.Request).WithContext": {}, +} + +func purity(pass *analysis.Pass) (interface{}, error) { + seen := map[*ssa.Function]struct{}{} + ssapkg := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).Pkg + var check func(ssafn *ssa.Function) (ret bool) + check = func(ssafn *ssa.Function) (ret bool) { + if ssafn.Object() == nil { + // TODO(dh): support closures + return false + } + if pass.ImportObjectFact(ssafn.Object(), new(IsPure)) { + return true + } + if ssafn.Pkg != ssapkg { + // Function is in another package but wasn't marked as + // pure, ergo it isn't pure + return false + } + // Break recursion + if _, ok := seen[ssafn]; ok { + return false + } + + seen[ssafn] = struct{}{} + defer func() { + if ret { + pass.ExportObjectFact(ssafn.Object(), &IsPure{}) + } + }() + + if functions.IsStub(ssafn) { + return false + } + + if _, ok := pureStdlib[ssafn.Object().(*types.Func).FullName()]; ok { + return true + } + + if ssafn.Signature.Results().Len() == 0 { + // A function with no return values is empty or is doing some + // work we cannot see (for example because of build tags); + // don't consider it pure. + return false + } + + for _, param := range ssafn.Params { + if _, ok := param.Type().Underlying().(*types.Basic); !ok { + return false + } + } + + if ssafn.Blocks == nil { + return false + } + checkCall := func(common *ssa.CallCommon) bool { + if common.IsInvoke() { + return false + } + builtin, ok := common.Value.(*ssa.Builtin) + if !ok { + if common.StaticCallee() != ssafn { + if common.StaticCallee() == nil { + return false + } + if !check(common.StaticCallee()) { + return false + } + } + } else { + switch builtin.Name() { + case "len", "cap", "make", "new": + default: + return false + } + } + return true + } + for _, b := range ssafn.Blocks { + for _, ins := range b.Instrs { + switch ins := ins.(type) { + case *ssa.Call: + if !checkCall(ins.Common()) { + return false + } + case *ssa.Defer: + if !checkCall(&ins.Call) { + return false + } + case *ssa.Select: + return false + case *ssa.Send: + return false + case *ssa.Go: + return false + case *ssa.Panic: + return false + case *ssa.Store: + return false + case *ssa.FieldAddr: + return false + case *ssa.UnOp: + if ins.Op == token.MUL || ins.Op == token.AND { + return false + } + } + } + } + return true + } + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { + check(ssafn) + } + + out := PurityResult{} + for _, fact := range pass.AllObjectFacts() { + out[fact.Object.(*types.Func)] = fact.Fact.(*IsPure) + } + return out, nil +} diff --git a/vendor/honnef.co/go/tools/facts/token.go b/vendor/honnef.co/go/tools/facts/token.go new file mode 100644 index 0000000000..26e76ff73d --- /dev/null +++ b/vendor/honnef.co/go/tools/facts/token.go @@ -0,0 +1,24 @@ +package facts + +import ( + "go/ast" + "go/token" + "reflect" + + "golang.org/x/tools/go/analysis" +) + +var TokenFile = &analysis.Analyzer{ + Name: "tokenfileanalyzer", + Doc: "creates a mapping of *token.File to *ast.File", + Run: func(pass *analysis.Pass) (interface{}, error) { + m := map[*token.File]*ast.File{} + for _, af := range pass.Files { + tf := pass.Fset.File(af.Pos()) + m[tf] = af + } + return m, nil + }, + RunDespiteErrors: true, + ResultType: reflect.TypeOf(map[*token.File]*ast.File{}), +} diff --git a/vendor/honnef.co/go/tools/functions/concrete.go b/vendor/honnef.co/go/tools/functions/concrete.go deleted file mode 100644 index 932acd03ed..0000000000 --- a/vendor/honnef.co/go/tools/functions/concrete.go +++ /dev/null @@ -1,56 +0,0 @@ -package functions - -import ( - "go/token" - "go/types" - - "honnef.co/go/tools/ssa" -) - -func concreteReturnTypes(fn *ssa.Function) []*types.Tuple { - res := fn.Signature.Results() - if res == nil { - return nil - } - ifaces := make([]bool, res.Len()) - any := false - for i := 0; i < res.Len(); i++ { - _, ifaces[i] = res.At(i).Type().Underlying().(*types.Interface) - any = any || ifaces[i] - } - if !any { - return []*types.Tuple{res} - } - var out []*types.Tuple - for _, block := range fn.Blocks { - if len(block.Instrs) == 0 { - continue - } - ret, ok := block.Instrs[len(block.Instrs)-1].(*ssa.Return) - if !ok { - continue - } - vars := make([]*types.Var, res.Len()) - for i, v := range ret.Results { - var typ types.Type - if !ifaces[i] { - typ = res.At(i).Type() - } else if mi, ok := v.(*ssa.MakeInterface); ok { - // TODO(dh): if mi.X is a function call that returns - // an interface, call concreteReturnTypes on that - // function (or, really, go through Descriptions, - // avoid infinite recursion etc, just like nil error - // detection) - - // TODO(dh): support Phi nodes - typ = mi.X.Type() - } else { - typ = res.At(i).Type() - } - vars[i] = types.NewParam(token.NoPos, nil, "", typ) - } - out = append(out, types.NewTuple(vars...)) - } - // TODO(dh): deduplicate out - return out -} diff --git a/vendor/honnef.co/go/tools/functions/functions.go b/vendor/honnef.co/go/tools/functions/functions.go deleted file mode 100644 index 8394041297..0000000000 --- a/vendor/honnef.co/go/tools/functions/functions.go +++ /dev/null @@ -1,150 +0,0 @@ -package functions - -import ( - "go/types" - "sync" - - "honnef.co/go/tools/callgraph" - "honnef.co/go/tools/callgraph/static" - "honnef.co/go/tools/ssa" - "honnef.co/go/tools/staticcheck/vrp" -) - -var stdlibDescs = map[string]Description{ - "errors.New": {Pure: true}, - - "fmt.Errorf": {Pure: true}, - "fmt.Sprintf": {Pure: true}, - "fmt.Sprint": {Pure: true}, - - "sort.Reverse": {Pure: true}, - - "strings.Map": {Pure: true}, - "strings.Repeat": {Pure: true}, - "strings.Replace": {Pure: true}, - "strings.Title": {Pure: true}, - "strings.ToLower": {Pure: true}, - "strings.ToLowerSpecial": {Pure: true}, - "strings.ToTitle": {Pure: true}, - "strings.ToTitleSpecial": {Pure: true}, - "strings.ToUpper": {Pure: true}, - "strings.ToUpperSpecial": {Pure: true}, - "strings.Trim": {Pure: true}, - "strings.TrimFunc": {Pure: true}, - "strings.TrimLeft": {Pure: true}, - "strings.TrimLeftFunc": {Pure: true}, - "strings.TrimPrefix": {Pure: true}, - "strings.TrimRight": {Pure: true}, - "strings.TrimRightFunc": {Pure: true}, - "strings.TrimSpace": {Pure: true}, - "strings.TrimSuffix": {Pure: true}, - - "(*net/http.Request).WithContext": {Pure: true}, - - "math/rand.Read": {NilError: true}, - "(*math/rand.Rand).Read": {NilError: true}, -} - -type Description struct { - // The function is known to be pure - Pure bool - // The function is known to be a stub - Stub bool - // The function is known to never return (panics notwithstanding) - Infinite bool - // Variable ranges - Ranges vrp.Ranges - Loops []Loop - // Function returns an error as its last argument, but it is - // always nil - NilError bool - ConcreteReturnTypes []*types.Tuple -} - -type descriptionEntry struct { - ready chan struct{} - result Description -} - -type Descriptions struct { - CallGraph *callgraph.Graph - mu sync.Mutex - cache map[*ssa.Function]*descriptionEntry -} - -func NewDescriptions(prog *ssa.Program) *Descriptions { - return &Descriptions{ - CallGraph: static.CallGraph(prog), - cache: map[*ssa.Function]*descriptionEntry{}, - } -} - -func (d *Descriptions) Get(fn *ssa.Function) Description { - d.mu.Lock() - fd := d.cache[fn] - if fd == nil { - fd = &descriptionEntry{ - ready: make(chan struct{}), - } - d.cache[fn] = fd - d.mu.Unlock() - - { - fd.result = stdlibDescs[fn.RelString(nil)] - fd.result.Pure = fd.result.Pure || d.IsPure(fn) - fd.result.Stub = fd.result.Stub || d.IsStub(fn) - fd.result.Infinite = fd.result.Infinite || !terminates(fn) - fd.result.Ranges = vrp.BuildGraph(fn).Solve() - fd.result.Loops = findLoops(fn) - fd.result.NilError = fd.result.NilError || IsNilError(fn) - fd.result.ConcreteReturnTypes = concreteReturnTypes(fn) - } - - close(fd.ready) - } else { - d.mu.Unlock() - <-fd.ready - } - return fd.result -} - -func IsNilError(fn *ssa.Function) bool { - // TODO(dh): This is very simplistic, as we only look for constant - // nil returns. A more advanced approach would work transitively. - // An even more advanced approach would be context-aware and - // determine nil errors based on inputs (e.g. io.WriteString to a - // bytes.Buffer will always return nil, but an io.WriteString to - // an os.File might not). Similarly, an os.File opened for reading - // won't error on Close, but other files will. - res := fn.Signature.Results() - if res.Len() == 0 { - return false - } - last := res.At(res.Len() - 1) - if types.TypeString(last.Type(), nil) != "error" { - return false - } - - if fn.Blocks == nil { - return false - } - for _, block := range fn.Blocks { - if len(block.Instrs) == 0 { - continue - } - ins := block.Instrs[len(block.Instrs)-1] - ret, ok := ins.(*ssa.Return) - if !ok { - continue - } - v := ret.Results[len(ret.Results)-1] - c, ok := v.(*ssa.Const) - if !ok { - return false - } - if !c.IsNil() { - return false - } - } - return true -} diff --git a/vendor/honnef.co/go/tools/functions/loops.go b/vendor/honnef.co/go/tools/functions/loops.go index 63011cf3ef..15877a2f96 100644 --- a/vendor/honnef.co/go/tools/functions/loops.go +++ b/vendor/honnef.co/go/tools/functions/loops.go @@ -2,9 +2,9 @@ package functions import "honnef.co/go/tools/ssa" -type Loop map[*ssa.BasicBlock]bool +type Loop struct{ ssa.BlockSet } -func findLoops(fn *ssa.Function) []Loop { +func FindLoops(fn *ssa.Function) []Loop { if fn.Blocks == nil { return nil } @@ -18,12 +18,16 @@ func findLoops(fn *ssa.Function) []Loop { // n is a back-edge to h // h is the loop header if n == h { - sets = append(sets, Loop{n: true}) + set := Loop{} + set.Add(n) + sets = append(sets, set) continue } - set := Loop{h: true, n: true} + set := Loop{} + set.Add(h) + set.Add(n) for _, b := range allPredsBut(n, h, nil) { - set[b] = true + set.Add(b) } sets = append(sets, set) } diff --git a/vendor/honnef.co/go/tools/functions/pure.go b/vendor/honnef.co/go/tools/functions/pure.go index 7028eb8c64..8bc5587713 100644 --- a/vendor/honnef.co/go/tools/functions/pure.go +++ b/vendor/honnef.co/go/tools/functions/pure.go @@ -1,26 +1,31 @@ package functions import ( - "go/token" - "go/types" - - "honnef.co/go/tools/callgraph" - "honnef.co/go/tools/lint/lintdsl" "honnef.co/go/tools/ssa" ) +func filterDebug(instr []ssa.Instruction) []ssa.Instruction { + var out []ssa.Instruction + for _, ins := range instr { + if _, ok := ins.(*ssa.DebugRef); !ok { + out = append(out, ins) + } + } + return out +} + // IsStub reports whether a function is a stub. A function is // considered a stub if it has no instructions or exactly one // instruction, which must be either returning only constant values or // a panic. -func (d *Descriptions) IsStub(fn *ssa.Function) bool { +func IsStub(fn *ssa.Function) bool { if len(fn.Blocks) == 0 { return true } if len(fn.Blocks) > 1 { return false } - instrs := lintdsl.FilterDebug(fn.Blocks[0].Instrs) + instrs := filterDebug(fn.Blocks[0].Instrs) if len(instrs) != 1 { return false } @@ -39,85 +44,3 @@ func (d *Descriptions) IsStub(fn *ssa.Function) bool { return false } } - -func (d *Descriptions) IsPure(fn *ssa.Function) bool { - if fn.Signature.Results().Len() == 0 { - // A function with no return values is empty or is doing some - // work we cannot see (for example because of build tags); - // don't consider it pure. - return false - } - - for _, param := range fn.Params { - if _, ok := param.Type().Underlying().(*types.Basic); !ok { - return false - } - } - - if fn.Blocks == nil { - return false - } - checkCall := func(common *ssa.CallCommon) bool { - if common.IsInvoke() { - return false - } - builtin, ok := common.Value.(*ssa.Builtin) - if !ok { - if common.StaticCallee() != fn { - if common.StaticCallee() == nil { - return false - } - // TODO(dh): ideally, IsPure wouldn't be responsible - // for avoiding infinite recursion, but - // FunctionDescriptions would be. - node := d.CallGraph.CreateNode(common.StaticCallee()) - if callgraph.PathSearch(node, func(other *callgraph.Node) bool { - return other.Func == fn - }) != nil { - return false - } - if !d.Get(common.StaticCallee()).Pure { - return false - } - } - } else { - switch builtin.Name() { - case "len", "cap", "make", "new": - default: - return false - } - } - return true - } - for _, b := range fn.Blocks { - for _, ins := range b.Instrs { - switch ins := ins.(type) { - case *ssa.Call: - if !checkCall(ins.Common()) { - return false - } - case *ssa.Defer: - if !checkCall(&ins.Call) { - return false - } - case *ssa.Select: - return false - case *ssa.Send: - return false - case *ssa.Go: - return false - case *ssa.Panic: - return false - case *ssa.Store: - return false - case *ssa.FieldAddr: - return false - case *ssa.UnOp: - if ins.Op == token.MUL || ins.Op == token.AND { - return false - } - } - } - } - return true -} diff --git a/vendor/honnef.co/go/tools/functions/terminates.go b/vendor/honnef.co/go/tools/functions/terminates.go index 65f9e16dc9..3e9c3a23f3 100644 --- a/vendor/honnef.co/go/tools/functions/terminates.go +++ b/vendor/honnef.co/go/tools/functions/terminates.go @@ -2,10 +2,10 @@ package functions import "honnef.co/go/tools/ssa" -// terminates reports whether fn is supposed to return, that is if it +// Terminates reports whether fn is supposed to return, that is if it // has at least one theoretic path that returns from the function. // Explicit panics do not count as terminating. -func terminates(fn *ssa.Function) bool { +func Terminates(fn *ssa.Function) bool { if fn.Blocks == nil { // assuming that a function terminates is the conservative // choice diff --git a/vendor/honnef.co/go/tools/go/types/typeutil/identical.go b/vendor/honnef.co/go/tools/go/types/typeutil/identical.go index 7eda29463d..c0ca441c32 100644 --- a/vendor/honnef.co/go/tools/go/types/typeutil/identical.go +++ b/vendor/honnef.co/go/tools/go/types/typeutil/identical.go @@ -6,24 +6,70 @@ import ( // Identical reports whether x and y are identical types. // Unlike types.Identical, receivers of Signature types are not ignored. +// Unlike types.Identical, interfaces are compared via pointer equality (except for the empty interface, which gets deduplicated). +// Unlike types.Identical, structs are compared via pointer equality. func Identical(x, y types.Type) (ret bool) { if !types.Identical(x, y) { return false } - sigX, ok := x.(*types.Signature) - if !ok { + + switch x := x.(type) { + case *types.Struct: + y, ok := y.(*types.Struct) + if !ok { + // should be impossible + return true + } + return x == y + case *types.Interface: + // The issue with interfaces, typeutil.Map and types.Identical + // + // types.Identical, when comparing two interfaces, only looks at the set + // of all methods, not differentiating between implicit (embedded) and + // explicit methods. + // + // When we see the following two types, in source order + // + // type I1 interface { foo() } + // type I2 interface { I1 } + // + // then we will first correctly process I1 and its underlying type. When + // we get to I2, we will see that its underlying type is identical to + // that of I1 and not process it again. This, however, means that we will + // not record the fact that I2 embeds I1. If only I2 is reachable via the + // graph root, then I1 will not be considered used. + // + // We choose to be lazy and compare interfaces by their + // pointers. This will obviously miss identical interfaces, + // but this only has a runtime cost, it doesn't affect + // correctness. + y, ok := y.(*types.Interface) + if !ok { + // should be impossible + return true + } + if x.NumEmbeddeds() == 0 && + y.NumEmbeddeds() == 0 && + x.NumMethods() == 0 && + y.NumMethods() == 0 { + // all truly empty interfaces are the same + return true + } + return x == y + case *types.Signature: + y, ok := y.(*types.Signature) + if !ok { + // should be impossible + return true + } + if x.Recv() == y.Recv() { + return true + } + if x.Recv() == nil || y.Recv() == nil { + return false + } + return Identical(x.Recv().Type(), y.Recv().Type()) + default: return true } - sigY, ok := y.(*types.Signature) - if !ok { - // should be impossible - return true - } - if sigX.Recv() == sigY.Recv() { - return true - } - if sigX.Recv() == nil || sigY.Recv() == nil { - return false - } - return Identical(sigX.Recv().Type(), sigY.Recv().Type()) } diff --git a/vendor/honnef.co/go/tools/go/types/typeutil/map.go b/vendor/honnef.co/go/tools/go/types/typeutil/map.go index db0b3bce72..f929353ccb 100644 --- a/vendor/honnef.co/go/tools/go/types/typeutil/map.go +++ b/vendor/honnef.co/go/tools/go/types/typeutil/map.go @@ -23,7 +23,11 @@ import ( // // Not thread-safe. // -// This fork handles Signatures correctly, respecting method receivers. +// This fork handles Signatures correctly, respecting method +// receivers. Furthermore, it doesn't deduplicate interfaces or +// structs. Interfaces aren't deduplicated as not to conflate implicit +// and explicit methods. Structs aren't deduplicated because we track +// fields of each type separately. // type Map struct { hasher Hasher // shared by many Maps diff --git a/vendor/honnef.co/go/tools/internal/cache/cache.go b/vendor/honnef.co/go/tools/internal/cache/cache.go new file mode 100644 index 0000000000..2b33ca1067 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/cache/cache.go @@ -0,0 +1,474 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cache implements a build artifact cache. +// +// This package is a slightly modified fork of Go's +// cmd/go/internal/cache package. +package cache + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "honnef.co/go/tools/internal/renameio" +) + +// An ActionID is a cache action key, the hash of a complete description of a +// repeatable computation (command line, environment variables, +// input file contents, executable contents). +type ActionID [HashSize]byte + +// An OutputID is a cache output key, the hash of an output of a computation. +type OutputID [HashSize]byte + +// A Cache is a package cache, backed by a file system directory tree. +type Cache struct { + dir string + now func() time.Time +} + +// Open opens and returns the cache in the given directory. +// +// It is safe for multiple processes on a single machine to use the +// same cache directory in a local file system simultaneously. +// They will coordinate using operating system file locks and may +// duplicate effort but will not corrupt the cache. +// +// However, it is NOT safe for multiple processes on different machines +// to share a cache directory (for example, if the directory were stored +// in a network file system). File locking is notoriously unreliable in +// network file systems and may not suffice to protect the cache. +// +func Open(dir string) (*Cache, error) { + info, err := os.Stat(dir) + if err != nil { + return nil, err + } + if !info.IsDir() { + return nil, &os.PathError{Op: "open", Path: dir, Err: fmt.Errorf("not a directory")} + } + for i := 0; i < 256; i++ { + name := filepath.Join(dir, fmt.Sprintf("%02x", i)) + if err := os.MkdirAll(name, 0777); err != nil { + return nil, err + } + } + c := &Cache{ + dir: dir, + now: time.Now, + } + return c, nil +} + +// fileName returns the name of the file corresponding to the given id. +func (c *Cache) fileName(id [HashSize]byte, key string) string { + return filepath.Join(c.dir, fmt.Sprintf("%02x", id[0]), fmt.Sprintf("%x", id)+"-"+key) +} + +var errMissing = errors.New("cache entry not found") + +const ( + // action entry file is "v1 \n" + hexSize = HashSize * 2 + entrySize = 2 + 1 + hexSize + 1 + hexSize + 1 + 20 + 1 + 20 + 1 +) + +// verify controls whether to run the cache in verify mode. +// In verify mode, the cache always returns errMissing from Get +// but then double-checks in Put that the data being written +// exactly matches any existing entry. This provides an easy +// way to detect program behavior that would have been different +// had the cache entry been returned from Get. +// +// verify is enabled by setting the environment variable +// GODEBUG=gocacheverify=1. +var verify = false + +// DebugTest is set when GODEBUG=gocachetest=1 is in the environment. +var DebugTest = false + +func init() { initEnv() } + +func initEnv() { + verify = false + debugHash = false + debug := strings.Split(os.Getenv("GODEBUG"), ",") + for _, f := range debug { + if f == "gocacheverify=1" { + verify = true + } + if f == "gocachehash=1" { + debugHash = true + } + if f == "gocachetest=1" { + DebugTest = true + } + } +} + +// Get looks up the action ID in the cache, +// returning the corresponding output ID and file size, if any. +// Note that finding an output ID does not guarantee that the +// saved file for that output ID is still available. +func (c *Cache) Get(id ActionID) (Entry, error) { + if verify { + return Entry{}, errMissing + } + return c.get(id) +} + +type Entry struct { + OutputID OutputID + Size int64 + Time time.Time +} + +// get is Get but does not respect verify mode, so that Put can use it. +func (c *Cache) get(id ActionID) (Entry, error) { + missing := func() (Entry, error) { + return Entry{}, errMissing + } + f, err := os.Open(c.fileName(id, "a")) + if err != nil { + return missing() + } + defer f.Close() + entry := make([]byte, entrySize+1) // +1 to detect whether f is too long + if n, err := io.ReadFull(f, entry); n != entrySize || err != io.ErrUnexpectedEOF { + return missing() + } + if entry[0] != 'v' || entry[1] != '1' || entry[2] != ' ' || entry[3+hexSize] != ' ' || entry[3+hexSize+1+hexSize] != ' ' || entry[3+hexSize+1+hexSize+1+20] != ' ' || entry[entrySize-1] != '\n' { + return missing() + } + eid, entry := entry[3:3+hexSize], entry[3+hexSize:] + eout, entry := entry[1:1+hexSize], entry[1+hexSize:] + esize, entry := entry[1:1+20], entry[1+20:] + //lint:ignore SA4006 See https://github.com/dominikh/go-tools/issues/465 + etime, entry := entry[1:1+20], entry[1+20:] + var buf [HashSize]byte + if _, err := hex.Decode(buf[:], eid); err != nil || buf != id { + return missing() + } + if _, err := hex.Decode(buf[:], eout); err != nil { + return missing() + } + i := 0 + for i < len(esize) && esize[i] == ' ' { + i++ + } + size, err := strconv.ParseInt(string(esize[i:]), 10, 64) + if err != nil || size < 0 { + return missing() + } + i = 0 + for i < len(etime) && etime[i] == ' ' { + i++ + } + tm, err := strconv.ParseInt(string(etime[i:]), 10, 64) + if err != nil || size < 0 { + return missing() + } + + c.used(c.fileName(id, "a")) + + return Entry{buf, size, time.Unix(0, tm)}, nil +} + +// GetFile looks up the action ID in the cache and returns +// the name of the corresponding data file. +func (c *Cache) GetFile(id ActionID) (file string, entry Entry, err error) { + entry, err = c.Get(id) + if err != nil { + return "", Entry{}, err + } + file = c.OutputFile(entry.OutputID) + info, err := os.Stat(file) + if err != nil || info.Size() != entry.Size { + return "", Entry{}, errMissing + } + return file, entry, nil +} + +// GetBytes looks up the action ID in the cache and returns +// the corresponding output bytes. +// GetBytes should only be used for data that can be expected to fit in memory. +func (c *Cache) GetBytes(id ActionID) ([]byte, Entry, error) { + entry, err := c.Get(id) + if err != nil { + return nil, entry, err + } + data, _ := ioutil.ReadFile(c.OutputFile(entry.OutputID)) + if sha256.Sum256(data) != entry.OutputID { + return nil, entry, errMissing + } + return data, entry, nil +} + +// OutputFile returns the name of the cache file storing output with the given OutputID. +func (c *Cache) OutputFile(out OutputID) string { + file := c.fileName(out, "d") + c.used(file) + return file +} + +// Time constants for cache expiration. +// +// We set the mtime on a cache file on each use, but at most one per mtimeInterval (1 hour), +// to avoid causing many unnecessary inode updates. The mtimes therefore +// roughly reflect "time of last use" but may in fact be older by at most an hour. +// +// We scan the cache for entries to delete at most once per trimInterval (1 day). +// +// When we do scan the cache, we delete entries that have not been used for +// at least trimLimit (5 days). Statistics gathered from a month of usage by +// Go developers found that essentially all reuse of cached entries happened +// within 5 days of the previous reuse. See golang.org/issue/22990. +const ( + mtimeInterval = 1 * time.Hour + trimInterval = 24 * time.Hour + trimLimit = 5 * 24 * time.Hour +) + +// used makes a best-effort attempt to update mtime on file, +// so that mtime reflects cache access time. +// +// Because the reflection only needs to be approximate, +// and to reduce the amount of disk activity caused by using +// cache entries, used only updates the mtime if the current +// mtime is more than an hour old. This heuristic eliminates +// nearly all of the mtime updates that would otherwise happen, +// while still keeping the mtimes useful for cache trimming. +func (c *Cache) used(file string) { + info, err := os.Stat(file) + if err == nil && c.now().Sub(info.ModTime()) < mtimeInterval { + return + } + os.Chtimes(file, c.now(), c.now()) +} + +// Trim removes old cache entries that are likely not to be reused. +func (c *Cache) Trim() { + now := c.now() + + // We maintain in dir/trim.txt the time of the last completed cache trim. + // If the cache has been trimmed recently enough, do nothing. + // This is the common case. + data, _ := ioutil.ReadFile(filepath.Join(c.dir, "trim.txt")) + t, err := strconv.ParseInt(strings.TrimSpace(string(data)), 10, 64) + if err == nil && now.Sub(time.Unix(t, 0)) < trimInterval { + return + } + + // Trim each of the 256 subdirectories. + // We subtract an additional mtimeInterval + // to account for the imprecision of our "last used" mtimes. + cutoff := now.Add(-trimLimit - mtimeInterval) + for i := 0; i < 256; i++ { + subdir := filepath.Join(c.dir, fmt.Sprintf("%02x", i)) + c.trimSubdir(subdir, cutoff) + } + + // Ignore errors from here: if we don't write the complete timestamp, the + // cache will appear older than it is, and we'll trim it again next time. + renameio.WriteFile(filepath.Join(c.dir, "trim.txt"), []byte(fmt.Sprintf("%d", now.Unix()))) +} + +// trimSubdir trims a single cache subdirectory. +func (c *Cache) trimSubdir(subdir string, cutoff time.Time) { + // Read all directory entries from subdir before removing + // any files, in case removing files invalidates the file offset + // in the directory scan. Also, ignore error from f.Readdirnames, + // because we don't care about reporting the error and we still + // want to process any entries found before the error. + f, err := os.Open(subdir) + if err != nil { + return + } + names, _ := f.Readdirnames(-1) + f.Close() + + for _, name := range names { + // Remove only cache entries (xxxx-a and xxxx-d). + if !strings.HasSuffix(name, "-a") && !strings.HasSuffix(name, "-d") { + continue + } + entry := filepath.Join(subdir, name) + info, err := os.Stat(entry) + if err == nil && info.ModTime().Before(cutoff) { + os.Remove(entry) + } + } +} + +// putIndexEntry adds an entry to the cache recording that executing the action +// with the given id produces an output with the given output id (hash) and size. +func (c *Cache) putIndexEntry(id ActionID, out OutputID, size int64, allowVerify bool) error { + // Note: We expect that for one reason or another it may happen + // that repeating an action produces a different output hash + // (for example, if the output contains a time stamp or temp dir name). + // While not ideal, this is also not a correctness problem, so we + // don't make a big deal about it. In particular, we leave the action + // cache entries writable specifically so that they can be overwritten. + // + // Setting GODEBUG=gocacheverify=1 does make a big deal: + // in verify mode we are double-checking that the cache entries + // are entirely reproducible. As just noted, this may be unrealistic + // in some cases but the check is also useful for shaking out real bugs. + entry := []byte(fmt.Sprintf("v1 %x %x %20d %20d\n", id, out, size, time.Now().UnixNano())) + if verify && allowVerify { + old, err := c.get(id) + if err == nil && (old.OutputID != out || old.Size != size) { + // panic to show stack trace, so we can see what code is generating this cache entry. + msg := fmt.Sprintf("go: internal cache error: cache verify failed: id=%x changed:<<<\n%s\n>>>\nold: %x %d\nnew: %x %d", id, reverseHash(id), out, size, old.OutputID, old.Size) + panic(msg) + } + } + file := c.fileName(id, "a") + if err := ioutil.WriteFile(file, entry, 0666); err != nil { + // TODO(bcmills): This Remove potentially races with another go command writing to file. + // Can we eliminate it? + os.Remove(file) + return err + } + os.Chtimes(file, c.now(), c.now()) // mainly for tests + + return nil +} + +// Put stores the given output in the cache as the output for the action ID. +// It may read file twice. The content of file must not change between the two passes. +func (c *Cache) Put(id ActionID, file io.ReadSeeker) (OutputID, int64, error) { + return c.put(id, file, true) +} + +// PutNoVerify is like Put but disables the verify check +// when GODEBUG=goverifycache=1 is set. +// It is meant for data that is OK to cache but that we expect to vary slightly from run to run, +// like test output containing times and the like. +func (c *Cache) PutNoVerify(id ActionID, file io.ReadSeeker) (OutputID, int64, error) { + return c.put(id, file, false) +} + +func (c *Cache) put(id ActionID, file io.ReadSeeker, allowVerify bool) (OutputID, int64, error) { + // Compute output ID. + h := sha256.New() + if _, err := file.Seek(0, 0); err != nil { + return OutputID{}, 0, err + } + size, err := io.Copy(h, file) + if err != nil { + return OutputID{}, 0, err + } + var out OutputID + h.Sum(out[:0]) + + // Copy to cached output file (if not already present). + if err := c.copyFile(file, out, size); err != nil { + return out, size, err + } + + // Add to cache index. + return out, size, c.putIndexEntry(id, out, size, allowVerify) +} + +// PutBytes stores the given bytes in the cache as the output for the action ID. +func (c *Cache) PutBytes(id ActionID, data []byte) error { + _, _, err := c.Put(id, bytes.NewReader(data)) + return err +} + +// copyFile copies file into the cache, expecting it to have the given +// output ID and size, if that file is not present already. +func (c *Cache) copyFile(file io.ReadSeeker, out OutputID, size int64) error { + name := c.fileName(out, "d") + info, err := os.Stat(name) + if err == nil && info.Size() == size { + // Check hash. + if f, err := os.Open(name); err == nil { + h := sha256.New() + io.Copy(h, f) + f.Close() + var out2 OutputID + h.Sum(out2[:0]) + if out == out2 { + return nil + } + } + // Hash did not match. Fall through and rewrite file. + } + + // Copy file to cache directory. + mode := os.O_RDWR | os.O_CREATE + if err == nil && info.Size() > size { // shouldn't happen but fix in case + mode |= os.O_TRUNC + } + f, err := os.OpenFile(name, mode, 0666) + if err != nil { + return err + } + defer f.Close() + if size == 0 { + // File now exists with correct size. + // Only one possible zero-length file, so contents are OK too. + // Early return here makes sure there's a "last byte" for code below. + return nil + } + + // From here on, if any of the I/O writing the file fails, + // we make a best-effort attempt to truncate the file f + // before returning, to avoid leaving bad bytes in the file. + + // Copy file to f, but also into h to double-check hash. + if _, err := file.Seek(0, 0); err != nil { + f.Truncate(0) + return err + } + h := sha256.New() + w := io.MultiWriter(f, h) + if _, err := io.CopyN(w, file, size-1); err != nil { + f.Truncate(0) + return err + } + // Check last byte before writing it; writing it will make the size match + // what other processes expect to find and might cause them to start + // using the file. + buf := make([]byte, 1) + if _, err := file.Read(buf); err != nil { + f.Truncate(0) + return err + } + h.Write(buf) + sum := h.Sum(nil) + if !bytes.Equal(sum, out[:]) { + f.Truncate(0) + return fmt.Errorf("file content changed underfoot") + } + + // Commit cache file entry. + if _, err := f.Write(buf); err != nil { + f.Truncate(0) + return err + } + if err := f.Close(); err != nil { + // Data might not have been written, + // but file may look like it is the right size. + // To be extra careful, remove cached file. + os.Remove(name) + return err + } + os.Chtimes(name, c.now(), c.now()) // mainly for tests + + return nil +} diff --git a/vendor/honnef.co/go/tools/internal/cache/default.go b/vendor/honnef.co/go/tools/internal/cache/default.go new file mode 100644 index 0000000000..3034f76a53 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/cache/default.go @@ -0,0 +1,85 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cache + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "sync" +) + +// Default returns the default cache to use. +func Default() (*Cache, error) { + defaultOnce.Do(initDefaultCache) + return defaultCache, defaultDirErr +} + +var ( + defaultOnce sync.Once + defaultCache *Cache +) + +// cacheREADME is a message stored in a README in the cache directory. +// Because the cache lives outside the normal Go trees, we leave the +// README as a courtesy to explain where it came from. +const cacheREADME = `This directory holds cached build artifacts from staticcheck. +` + +// initDefaultCache does the work of finding the default cache +// the first time Default is called. +func initDefaultCache() { + dir := DefaultDir() + if err := os.MkdirAll(dir, 0777); err != nil { + log.Fatalf("failed to initialize build cache at %s: %s\n", dir, err) + } + if _, err := os.Stat(filepath.Join(dir, "README")); err != nil { + // Best effort. + ioutil.WriteFile(filepath.Join(dir, "README"), []byte(cacheREADME), 0666) + } + + c, err := Open(dir) + if err != nil { + log.Fatalf("failed to initialize build cache at %s: %s\n", dir, err) + } + defaultCache = c +} + +var ( + defaultDirOnce sync.Once + defaultDir string + defaultDirErr error +) + +// DefaultDir returns the effective STATICCHECK_CACHE setting. +func DefaultDir() string { + // Save the result of the first call to DefaultDir for later use in + // initDefaultCache. cmd/go/main.go explicitly sets GOCACHE so that + // subprocesses will inherit it, but that means initDefaultCache can't + // otherwise distinguish between an explicit "off" and a UserCacheDir error. + + defaultDirOnce.Do(func() { + defaultDir = os.Getenv("STATICCHECK_CACHE") + if filepath.IsAbs(defaultDir) { + return + } + if defaultDir != "" { + defaultDirErr = fmt.Errorf("STATICCHECK_CACHE is not an absolute path") + return + } + + // Compute default location. + dir, err := os.UserCacheDir() + if err != nil { + defaultDirErr = fmt.Errorf("STATICCHECK_CACHE is not defined and %v", err) + return + } + defaultDir = filepath.Join(dir, "staticcheck") + }) + + return defaultDir +} diff --git a/vendor/honnef.co/go/tools/internal/cache/hash.go b/vendor/honnef.co/go/tools/internal/cache/hash.go new file mode 100644 index 0000000000..a53543ec50 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/cache/hash.go @@ -0,0 +1,176 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cache + +import ( + "bytes" + "crypto/sha256" + "fmt" + "hash" + "io" + "os" + "sync" +) + +var debugHash = false // set when GODEBUG=gocachehash=1 + +// HashSize is the number of bytes in a hash. +const HashSize = 32 + +// A Hash provides access to the canonical hash function used to index the cache. +// The current implementation uses salted SHA256, but clients must not assume this. +type Hash struct { + h hash.Hash + name string // for debugging + buf *bytes.Buffer // for verify +} + +// hashSalt is a salt string added to the beginning of every hash +// created by NewHash. Using the Staticcheck version makes sure that different +// versions of the command do not address the same cache +// entries, so that a bug in one version does not affect the execution +// of other versions. This salt will result in additional ActionID files +// in the cache, but not additional copies of the large output files, +// which are still addressed by unsalted SHA256. +var hashSalt []byte + +func SetSalt(b []byte) { + hashSalt = b +} + +// Subkey returns an action ID corresponding to mixing a parent +// action ID with a string description of the subkey. +func Subkey(parent ActionID, desc string) ActionID { + h := sha256.New() + h.Write([]byte("subkey:")) + h.Write(parent[:]) + h.Write([]byte(desc)) + var out ActionID + h.Sum(out[:0]) + if debugHash { + fmt.Fprintf(os.Stderr, "HASH subkey %x %q = %x\n", parent, desc, out) + } + if verify { + hashDebug.Lock() + hashDebug.m[out] = fmt.Sprintf("subkey %x %q", parent, desc) + hashDebug.Unlock() + } + return out +} + +// NewHash returns a new Hash. +// The caller is expected to Write data to it and then call Sum. +func NewHash(name string) *Hash { + h := &Hash{h: sha256.New(), name: name} + if debugHash { + fmt.Fprintf(os.Stderr, "HASH[%s]\n", h.name) + } + h.Write(hashSalt) + if verify { + h.buf = new(bytes.Buffer) + } + return h +} + +// Write writes data to the running hash. +func (h *Hash) Write(b []byte) (int, error) { + if debugHash { + fmt.Fprintf(os.Stderr, "HASH[%s]: %q\n", h.name, b) + } + if h.buf != nil { + h.buf.Write(b) + } + return h.h.Write(b) +} + +// Sum returns the hash of the data written previously. +func (h *Hash) Sum() [HashSize]byte { + var out [HashSize]byte + h.h.Sum(out[:0]) + if debugHash { + fmt.Fprintf(os.Stderr, "HASH[%s]: %x\n", h.name, out) + } + if h.buf != nil { + hashDebug.Lock() + if hashDebug.m == nil { + hashDebug.m = make(map[[HashSize]byte]string) + } + hashDebug.m[out] = h.buf.String() + hashDebug.Unlock() + } + return out +} + +// In GODEBUG=gocacheverify=1 mode, +// hashDebug holds the input to every computed hash ID, +// so that we can work backward from the ID involved in a +// cache entry mismatch to a description of what should be there. +var hashDebug struct { + sync.Mutex + m map[[HashSize]byte]string +} + +// reverseHash returns the input used to compute the hash id. +func reverseHash(id [HashSize]byte) string { + hashDebug.Lock() + s := hashDebug.m[id] + hashDebug.Unlock() + return s +} + +var hashFileCache struct { + sync.Mutex + m map[string][HashSize]byte +} + +// FileHash returns the hash of the named file. +// It caches repeated lookups for a given file, +// and the cache entry for a file can be initialized +// using SetFileHash. +// The hash used by FileHash is not the same as +// the hash used by NewHash. +func FileHash(file string) ([HashSize]byte, error) { + hashFileCache.Lock() + out, ok := hashFileCache.m[file] + hashFileCache.Unlock() + + if ok { + return out, nil + } + + h := sha256.New() + f, err := os.Open(file) + if err != nil { + if debugHash { + fmt.Fprintf(os.Stderr, "HASH %s: %v\n", file, err) + } + return [HashSize]byte{}, err + } + _, err = io.Copy(h, f) + f.Close() + if err != nil { + if debugHash { + fmt.Fprintf(os.Stderr, "HASH %s: %v\n", file, err) + } + return [HashSize]byte{}, err + } + h.Sum(out[:0]) + if debugHash { + fmt.Fprintf(os.Stderr, "HASH %s: %x\n", file, out) + } + + SetFileHash(file, out) + return out, nil +} + +// SetFileHash sets the hash returned by FileHash for file. +func SetFileHash(file string, sum [HashSize]byte) { + hashFileCache.Lock() + if hashFileCache.m == nil { + hashFileCache.m = make(map[string][HashSize]byte) + } + hashFileCache.m[file] = sum + hashFileCache.Unlock() +} diff --git a/vendor/honnef.co/go/tools/internal/passes/buildssa/buildssa.go b/vendor/honnef.co/go/tools/internal/passes/buildssa/buildssa.go new file mode 100644 index 0000000000..fde918d121 --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/passes/buildssa/buildssa.go @@ -0,0 +1,116 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package buildssa defines an Analyzer that constructs the SSA +// representation of an error-free package and returns the set of all +// functions within it. It does not report any diagnostics itself but +// may be used as an input to other analyzers. +// +// THIS INTERFACE IS EXPERIMENTAL AND MAY BE SUBJECT TO INCOMPATIBLE CHANGE. +package buildssa + +import ( + "go/ast" + "go/types" + "reflect" + + "golang.org/x/tools/go/analysis" + "honnef.co/go/tools/ssa" +) + +var Analyzer = &analysis.Analyzer{ + Name: "buildssa", + Doc: "build SSA-form IR for later passes", + Run: run, + ResultType: reflect.TypeOf(new(SSA)), +} + +// SSA provides SSA-form intermediate representation for all the +// non-blank source functions in the current package. +type SSA struct { + Pkg *ssa.Package + SrcFuncs []*ssa.Function +} + +func run(pass *analysis.Pass) (interface{}, error) { + // Plundered from ssautil.BuildPackage. + + // We must create a new Program for each Package because the + // analysis API provides no place to hang a Program shared by + // all Packages. Consequently, SSA Packages and Functions do not + // have a canonical representation across an analysis session of + // multiple packages. This is unlikely to be a problem in + // practice because the analysis API essentially forces all + // packages to be analysed independently, so any given call to + // Analysis.Run on a package will see only SSA objects belonging + // to a single Program. + + mode := ssa.GlobalDebug + + prog := ssa.NewProgram(pass.Fset, mode) + + // Create SSA packages for all imports. + // Order is not significant. + created := make(map[*types.Package]bool) + var createAll func(pkgs []*types.Package) + createAll = func(pkgs []*types.Package) { + for _, p := range pkgs { + if !created[p] { + created[p] = true + prog.CreatePackage(p, nil, nil, true) + createAll(p.Imports()) + } + } + } + createAll(pass.Pkg.Imports()) + + // Create and build the primary package. + ssapkg := prog.CreatePackage(pass.Pkg, pass.Files, pass.TypesInfo, false) + ssapkg.Build() + + // Compute list of source functions, including literals, + // in source order. + var funcs []*ssa.Function + var addAnons func(f *ssa.Function) + addAnons = func(f *ssa.Function) { + funcs = append(funcs, f) + for _, anon := range f.AnonFuncs { + addAnons(anon) + } + } + addAnons(ssapkg.Members["init"].(*ssa.Function)) + for _, f := range pass.Files { + for _, decl := range f.Decls { + if fdecl, ok := decl.(*ast.FuncDecl); ok { + + // SSA will not build a Function + // for a FuncDecl named blank. + // That's arguably too strict but + // relaxing it would break uniqueness of + // names of package members. + if fdecl.Name.Name == "_" { + continue + } + + // (init functions have distinct Func + // objects named "init" and distinct + // ssa.Functions named "init#1", ...) + + fn := pass.TypesInfo.Defs[fdecl.Name].(*types.Func) + if fn == nil { + panic(fn) + } + + f := ssapkg.Prog.FuncValue(fn) + if f == nil { + panic(fn) + } + + addAnons(f) + } + } + } + + return &SSA{Pkg: ssapkg, SrcFuncs: funcs}, nil +} diff --git a/vendor/honnef.co/go/tools/internal/renameio/renameio.go b/vendor/honnef.co/go/tools/internal/renameio/renameio.go new file mode 100644 index 0000000000..3f3f1708fa --- /dev/null +++ b/vendor/honnef.co/go/tools/internal/renameio/renameio.go @@ -0,0 +1,83 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package renameio writes files atomically by renaming temporary files. +package renameio + +import ( + "bytes" + "io" + "io/ioutil" + "os" + "path/filepath" + "runtime" + "strings" + "time" +) + +const patternSuffix = "*.tmp" + +// Pattern returns a glob pattern that matches the unrenamed temporary files +// created when writing to filename. +func Pattern(filename string) string { + return filepath.Join(filepath.Dir(filename), filepath.Base(filename)+patternSuffix) +} + +// WriteFile is like ioutil.WriteFile, but first writes data to an arbitrary +// file in the same directory as filename, then renames it atomically to the +// final name. +// +// That ensures that the final location, if it exists, is always a complete file. +func WriteFile(filename string, data []byte) (err error) { + return WriteToFile(filename, bytes.NewReader(data)) +} + +// WriteToFile is a variant of WriteFile that accepts the data as an io.Reader +// instead of a slice. +func WriteToFile(filename string, data io.Reader) (err error) { + f, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename)+patternSuffix) + if err != nil { + return err + } + defer func() { + // Only call os.Remove on f.Name() if we failed to rename it: otherwise, + // some other process may have created a new file with the same name after + // that. + if err != nil { + f.Close() + os.Remove(f.Name()) + } + }() + + if _, err := io.Copy(f, data); err != nil { + return err + } + // Sync the file before renaming it: otherwise, after a crash the reader may + // observe a 0-length file instead of the actual contents. + // See https://golang.org/issue/22397#issuecomment-380831736. + if err := f.Sync(); err != nil { + return err + } + if err := f.Close(); err != nil { + return err + } + + var start time.Time + for { + err := os.Rename(f.Name(), filename) + if err == nil || runtime.GOOS != "windows" || !strings.HasSuffix(err.Error(), "Access is denied.") { + return err + } + + // Windows seems to occasionally trigger spurious "Access is denied" errors + // here (see golang.org/issue/31247). We're not sure why. It's probably + // worth a little extra latency to avoid propagating the spurious errors. + if start.IsZero() { + start = time.Now() + } else if time.Since(start) >= 500*time.Millisecond { + return err + } + time.Sleep(5 * time.Millisecond) + } +} diff --git a/vendor/honnef.co/go/tools/internal/sharedcheck/lint.go b/vendor/honnef.co/go/tools/internal/sharedcheck/lint.go index cf797fb1bd..affee66072 100644 --- a/vendor/honnef.co/go/tools/internal/sharedcheck/lint.go +++ b/vendor/honnef.co/go/tools/internal/sharedcheck/lint.go @@ -4,13 +4,14 @@ import ( "go/ast" "go/types" - "honnef.co/go/tools/lint" + "golang.org/x/tools/go/analysis" + "honnef.co/go/tools/internal/passes/buildssa" . "honnef.co/go/tools/lint/lintdsl" "honnef.co/go/tools/ssa" ) -func CheckRangeStringRunes(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { +func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { fn := func(node ast.Node) bool { rng, ok := node.(*ast.RangeStmt) if !ok || !IsBlank(rng.Key) { @@ -59,10 +60,11 @@ func CheckRangeStringRunes(j *lint.Job) { return true } - j.Errorf(rng, "should range over string, not []rune(string)") + pass.Reportf(rng.Pos(), "should range over string, not []rune(string)") return true } Inspect(ssafn.Syntax(), fn) } + return nil, nil } diff --git a/vendor/honnef.co/go/tools/lint/generated.go b/vendor/honnef.co/go/tools/lint/generated.go deleted file mode 100644 index 6553282964..0000000000 --- a/vendor/honnef.co/go/tools/lint/generated.go +++ /dev/null @@ -1,44 +0,0 @@ -package lint - -import ( - "bufio" - "bytes" - "io" - "os" -) - -var ( - // used by cgo before Go 1.11 - oldCgo = []byte("// Created by cgo - DO NOT EDIT") - prefix = []byte("// Code generated ") - suffix = []byte(" DO NOT EDIT.") - nl = []byte("\n") - crnl = []byte("\r\n") -) - -func isGenerated(path string) bool { - f, err := os.Open(path) - if err != nil { - return false - } - defer f.Close() - br := bufio.NewReader(f) - for { - s, err := br.ReadBytes('\n') - if err != nil && err != io.EOF { - return false - } - s = bytes.TrimSuffix(s, crnl) - s = bytes.TrimSuffix(s, nl) - if bytes.HasPrefix(s, prefix) && bytes.HasSuffix(s, suffix) { - return true - } - if bytes.Equal(s, oldCgo) { - return true - } - if err == io.EOF { - break - } - } - return false -} diff --git a/vendor/honnef.co/go/tools/lint/lint.go b/vendor/honnef.co/go/tools/lint/lint.go index c4d9ff6719..de5a8f1288 100644 --- a/vendor/honnef.co/go/tools/lint/lint.go +++ b/vendor/honnef.co/go/tools/lint/lint.go @@ -4,34 +4,53 @@ package lint // import "honnef.co/go/tools/lint" import ( "bytes" "fmt" - "go/ast" + "go/scanner" "go/token" "go/types" - "io" - "os" "path/filepath" - "runtime" "sort" "strings" "sync" - "time" + "sync/atomic" "unicode" - "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/packages" "honnef.co/go/tools/config" - "honnef.co/go/tools/ssa" - "honnef.co/go/tools/ssa/ssautil" ) -type Job struct { - Pkg *Pkg - GoVersion int +type Documentation struct { + Title string + Text string + Since string + NonDefault bool + Options []string +} - check Check - problems []Problem - - duration time.Duration +func (doc *Documentation) String() string { + b := &strings.Builder{} + fmt.Fprintf(b, "%s\n\n", doc.Title) + if doc.Text != "" { + fmt.Fprintf(b, "%s\n\n", doc.Text) + } + fmt.Fprint(b, "Available since\n ") + if doc.Since == "" { + fmt.Fprint(b, "unreleased") + } else { + fmt.Fprintf(b, "%s", doc.Since) + } + if doc.NonDefault { + fmt.Fprint(b, ", non-default") + } + fmt.Fprint(b, "\n") + if len(doc.Options) > 0 { + fmt.Fprintf(b, "\nOptions\n") + for _, opt := range doc.Options { + fmt.Fprintf(b, " %s", opt) + } + fmt.Fprint(b, "\n") + } + return b.String() } type Ignore interface { @@ -42,17 +61,18 @@ type LineIgnore struct { File string Line int Checks []string - matched bool - pos token.Pos + Matched bool + Pos token.Pos } func (li *LineIgnore) Match(p Problem) bool { - if p.Position.Filename != li.File || p.Position.Line != li.Line { + pos := p.Pos + if pos.Filename != li.File || pos.Line != li.Line { return false } for _, c := range li.Checks { if m, _ := filepath.Match(c, p.Check); m { - li.matched = true + li.Matched = true return true } } @@ -61,7 +81,7 @@ func (li *LineIgnore) Match(p Problem) bool { func (li *LineIgnore) String() string { matched := "not matched" - if li.matched { + if li.Matched { matched = "matched" } return fmt.Sprintf("%s:%d %s (%s)", li.File, li.Line, strings.Join(li.Checks, ", "), matched) @@ -73,7 +93,7 @@ type FileIgnore struct { } func (fi *FileIgnore) Match(p Problem) bool { - if p.Position.Filename != fi.File { + if p.Pos.Filename != fi.File { return false } for _, c := range fi.Checks { @@ -84,43 +104,6 @@ func (fi *FileIgnore) Match(p Problem) bool { return false } -type GlobIgnore struct { - Pattern string - Checks []string -} - -func (gi *GlobIgnore) Match(p Problem) bool { - if gi.Pattern != "*" { - pkgpath := p.Package.Types.Path() - if strings.HasSuffix(pkgpath, "_test") { - pkgpath = pkgpath[:len(pkgpath)-len("_test")] - } - name := filepath.Join(pkgpath, filepath.Base(p.Position.Filename)) - if m, _ := filepath.Match(gi.Pattern, name); !m { - return false - } - } - for _, c := range gi.Checks { - if m, _ := filepath.Match(c, p.Check); m { - return true - } - } - return false -} - -type Program struct { - SSA *ssa.Program - InitialPackages []*Pkg - AllPackages []*packages.Package - AllFunctions []*ssa.Function -} - -func (prog *Program) Fset() *token.FileSet { - return prog.InitialPackages[0].Fset -} - -type Func func(*Job) - type Severity uint8 const ( @@ -131,367 +114,245 @@ const ( // Problem represents a problem in some source code. type Problem struct { - Position token.Position // position in source file - Text string // the prose that describes the problem + Pos token.Position + End token.Position + Message string Check string - Package *Pkg Severity Severity } func (p *Problem) String() string { - if p.Check == "" { - return p.Text - } - return fmt.Sprintf("%s (%s)", p.Text, p.Check) -} - -type Checker interface { - Name() string - Prefix() string - Init(*Program) - Checks() []Check -} - -type Check struct { - Fn Func - ID string - FilterGenerated bool - Doc string + return fmt.Sprintf("%s (%s)", p.Message, p.Check) } // A Linter lints Go source code. type Linter struct { - Checkers []Checker - Ignores []Ignore - GoVersion int - ReturnIgnored bool - Config config.Config - - MaxConcurrentJobs int - PrintStats bool - - automaticIgnores []Ignore + Checkers []*analysis.Analyzer + CumulativeCheckers []CumulativeChecker + GoVersion int + Config config.Config + Stats Stats } -func (l *Linter) ignore(p Problem) bool { - ignored := false - for _, ig := range l.automaticIgnores { - // We cannot short-circuit these, as we want to record, for - // each ignore, whether it matched or not. - if ig.Match(p) { - ignored = true - } +type CumulativeChecker interface { + Analyzer() *analysis.Analyzer + Result() []types.Object + ProblemObject(*token.FileSet, types.Object) Problem +} + +func (l *Linter) Lint(cfg *packages.Config, patterns []string) ([]Problem, error) { + var allAnalyzers []*analysis.Analyzer + allAnalyzers = append(allAnalyzers, l.Checkers...) + for _, cum := range l.CumulativeCheckers { + allAnalyzers = append(allAnalyzers, cum.Analyzer()) } - if ignored { - // no need to execute other ignores if we've already had a - // match. - return true - } - for _, ig := range l.Ignores { - // We can short-circuit here, as we aren't tracking any - // information. - if ig.Match(p) { - return true + + // The -checks command line flag overrules all configuration + // files, which means that for `-checks="foo"`, no check other + // than foo can ever be reported to the user. Make use of this + // fact to cull the list of analyses we need to run. + + // replace "inherit" with "all", as we don't want to base the + // list of all checks on the default configuration, which + // disables certain checks. + checks := make([]string, len(l.Config.Checks)) + copy(checks, l.Config.Checks) + for i, c := range checks { + if c == "inherit" { + checks[i] = "all" } } - return false -} - -func (j *Job) File(node Positioner) *ast.File { - return j.Pkg.tokenFileMap[j.Pkg.Fset.File(node.Pos())] -} - -func parseDirective(s string) (cmd string, args []string) { - if !strings.HasPrefix(s, "//lint:") { - return "", nil - } - s = strings.TrimPrefix(s, "//lint:") - fields := strings.Split(s, " ") - return fields[0], fields[1:] -} - -type PerfStats struct { - PackageLoading time.Duration - SSABuild time.Duration - OtherInitWork time.Duration - CheckerInits map[string]time.Duration - Jobs []JobStat -} - -type JobStat struct { - Job string - Duration time.Duration -} - -func (stats *PerfStats) Print(w io.Writer) { - fmt.Fprintln(w, "Package loading:", stats.PackageLoading) - fmt.Fprintln(w, "SSA build:", stats.SSABuild) - fmt.Fprintln(w, "Other init work:", stats.OtherInitWork) - - fmt.Fprintln(w, "Checker inits:") - for checker, d := range stats.CheckerInits { - fmt.Fprintf(w, "\t%s: %s\n", checker, d) - } - fmt.Fprintln(w) - - fmt.Fprintln(w, "Jobs:") - sort.Slice(stats.Jobs, func(i, j int) bool { - return stats.Jobs[i].Duration < stats.Jobs[j].Duration - }) - var total time.Duration - for _, job := range stats.Jobs { - fmt.Fprintf(w, "\t%s: %s\n", job.Job, job.Duration) - total += job.Duration - } - fmt.Fprintf(w, "\tTotal: %s\n", total) -} - -func (l *Linter) Lint(initial []*packages.Package, stats *PerfStats) []Problem { - allPkgs := allPackages(initial) - t := time.Now() - ssaprog, _ := ssautil.Packages(allPkgs, ssa.GlobalDebug) - ssaprog.Build() - if stats != nil { - stats.SSABuild = time.Since(t) - } - runtime.GC() - - t = time.Now() - pkgMap := map[*ssa.Package]*Pkg{} - var pkgs []*Pkg - for _, pkg := range initial { - ssapkg := ssaprog.Package(pkg.Types) - var cfg config.Config - if len(pkg.GoFiles) != 0 { - path := pkg.GoFiles[0] - dir := filepath.Dir(path) - var err error - // OPT(dh): we're rebuilding the entire config tree for - // each package. for example, if we check a/b/c and - // a/b/c/d, we'll process a, a/b, a/b/c, a, a/b, a/b/c, - // a/b/c/d – we should cache configs per package and only - // load the new levels. - cfg, err = config.Load(dir) - if err != nil { - // FIXME(dh): we couldn't load the config, what are we - // supposed to do? probably tell the user somehow - } - cfg = cfg.Merge(l.Config) + allowed := FilterChecks(allAnalyzers, checks) + var allowedAnalyzers []*analysis.Analyzer + for _, c := range l.Checkers { + if allowed[c.Name] { + allowedAnalyzers = append(allowedAnalyzers, c) } - - pkg := &Pkg{ - SSA: ssapkg, - Package: pkg, - Config: cfg, - Generated: map[string]bool{}, - tokenFileMap: map[*token.File]*ast.File{}, - } - pkg.Inspector = inspector.New(pkg.Syntax) - for _, f := range pkg.Syntax { - tf := pkg.Fset.File(f.Pos()) - pkg.tokenFileMap[tf] = f - - path := DisplayPosition(pkg.Fset, f.Pos()).Filename - pkg.Generated[path] = isGenerated(path) - } - pkgMap[ssapkg] = pkg - pkgs = append(pkgs, pkg) } - - prog := &Program{ - SSA: ssaprog, - InitialPackages: pkgs, - AllPackages: allPkgs, - } - - for fn := range ssautil.AllFunctions(ssaprog) { - prog.AllFunctions = append(prog.AllFunctions, fn) - if fn.Pkg == nil { - continue - } - if pkg, ok := pkgMap[fn.Pkg]; ok { - pkg.InitialFunctions = append(pkg.InitialFunctions, fn) + hasCumulative := false + for _, cum := range l.CumulativeCheckers { + a := cum.Analyzer() + if allowed[a.Name] { + hasCumulative = true + allowedAnalyzers = append(allowedAnalyzers, a) } } - var out []Problem - l.automaticIgnores = nil - for _, pkg := range initial { - for _, f := range pkg.Syntax { - found := false - commentLoop: - for _, cg := range f.Comments { - for _, c := range cg.List { - if strings.Contains(c.Text, "//lint:") { - found = true - break commentLoop + r, err := NewRunner(&l.Stats) + if err != nil { + return nil, err + } + r.goVersion = l.GoVersion + + pkgs, err := r.Run(cfg, patterns, allowedAnalyzers, hasCumulative) + if err != nil { + return nil, err + } + + tpkgToPkg := map[*types.Package]*Package{} + for _, pkg := range pkgs { + tpkgToPkg[pkg.Types] = pkg + + for _, e := range pkg.errs { + switch e := e.(type) { + case types.Error: + p := Problem{ + Pos: e.Fset.PositionFor(e.Pos, false), + Message: e.Msg, + Severity: Error, + Check: "compile", + } + pkg.problems = append(pkg.problems, p) + case packages.Error: + msg := e.Msg + if len(msg) != 0 && msg[0] == '\n' { + // TODO(dh): See https://github.com/golang/go/issues/32363 + msg = msg[1:] + } + + var pos token.Position + if e.Pos == "" { + // Under certain conditions (malformed package + // declarations, multiple packages in the same + // directory), go list emits an error on stderr + // instead of JSON. Those errors do not have + // associated position information in + // go/packages.Error, even though the output on + // stderr may contain it. + if p, n, err := parsePos(msg); err == nil { + if abs, err := filepath.Abs(p.Filename); err == nil { + p.Filename = abs + } + pos = p + msg = msg[n+2:] + } + } else { + var err error + pos, _, err = parsePos(e.Pos) + if err != nil { + panic(fmt.Sprintf("internal error: %s", e)) } } - } - if !found { - continue - } - cm := ast.NewCommentMap(pkg.Fset, f, f.Comments) - for node, cgs := range cm { - for _, cg := range cgs { - for _, c := range cg.List { - if !strings.HasPrefix(c.Text, "//lint:") { - continue - } - cmd, args := parseDirective(c.Text) - switch cmd { - case "ignore", "file-ignore": - if len(args) < 2 { - // FIXME(dh): this causes duplicated warnings when using megacheck - p := Problem{ - Position: DisplayPosition(prog.Fset(), c.Pos()), - Text: "malformed linter directive; missing the required reason field?", - Check: "", - Package: nil, - } - out = append(out, p) - continue - } - default: - // unknown directive, ignore - continue - } - checks := strings.Split(args[0], ",") - pos := DisplayPosition(prog.Fset(), node.Pos()) - var ig Ignore - switch cmd { - case "ignore": - ig = &LineIgnore{ - File: pos.Filename, - Line: pos.Line, - Checks: checks, - pos: c.Pos(), - } - case "file-ignore": - ig = &FileIgnore{ - File: pos.Filename, - Checks: checks, - } - } - l.automaticIgnores = append(l.automaticIgnores, ig) + p := Problem{ + Pos: pos, + Message: msg, + Severity: Error, + Check: "compile", + } + pkg.problems = append(pkg.problems, p) + case scanner.ErrorList: + for _, e := range e { + p := Problem{ + Pos: e.Pos, + Message: e.Msg, + Severity: Error, + Check: "compile", } + pkg.problems = append(pkg.problems, p) } - } - } - } - - if stats != nil { - stats.OtherInitWork = time.Since(t) - } - - for _, checker := range l.Checkers { - t := time.Now() - checker.Init(prog) - if stats != nil { - stats.CheckerInits[checker.Name()] = time.Since(t) - } - } - - var jobs []*Job - var allChecks []string - - var wg sync.WaitGroup - for _, checker := range l.Checkers { - for _, check := range checker.Checks() { - allChecks = append(allChecks, check.ID) - if check.Fn == nil { - continue - } - for _, pkg := range pkgs { - j := &Job{ - Pkg: pkg, - check: check, - GoVersion: l.GoVersion, + case error: + p := Problem{ + Pos: token.Position{}, + Message: e.Error(), + Severity: Error, + Check: "compile", } - jobs = append(jobs, j) - wg.Add(1) - go func(check Check, j *Job) { - t := time.Now() - check.Fn(j) - j.duration = time.Since(t) - wg.Done() - }(check, j) + pkg.problems = append(pkg.problems, p) } } } - wg.Wait() - - for _, j := range jobs { - if stats != nil { - stats.Jobs = append(stats.Jobs, JobStat{j.check.ID, j.duration}) - } - for _, p := range j.problems { - if p.Package == nil { - panic(fmt.Sprintf("internal error: problem at position %s has nil package", p.Position)) - } - allowedChecks := FilterChecks(allChecks, p.Package.Config.Checks) - - if l.ignore(p) { - p.Severity = Ignored - } - // TODO(dh): support globs in check white/blacklist - // OPT(dh): this approach doesn't actually disable checks, - // it just discards their results. For the moment, that's - // fine. None of our checks are super expensive. In the - // future, we may want to provide opt-in expensive - // analysis, which shouldn't run at all. It may be easiest - // to implement this in the individual checks. - if (l.ReturnIgnored || p.Severity != Ignored) && allowedChecks[p.Check] { - out = append(out, p) - } - } - } - - for _, ig := range l.automaticIgnores { - ig, ok := ig.(*LineIgnore) - if !ok { - continue - } - if ig.matched { - continue - } - - couldveMatched := false - for _, pkg := range pkgs { - for _, f := range pkg.tokenFileMap { - if prog.Fset().Position(f.Pos()).Filename != ig.File { + atomic.StoreUint32(&r.stats.State, StateCumulative) + var problems []Problem + for _, cum := range l.CumulativeCheckers { + for _, res := range cum.Result() { + pkg := tpkgToPkg[res.Pkg()] + allowedChecks := FilterChecks(allowedAnalyzers, pkg.cfg.Merge(l.Config).Checks) + if allowedChecks[cum.Analyzer().Name] { + pos := DisplayPosition(pkg.Fset, res.Pos()) + // FIXME(dh): why are we ignoring generated files + // here? Surely this is specific to 'unused', not all + // cumulative checkers + if _, ok := pkg.gen[pos.Filename]; ok { continue } - allowedChecks := FilterChecks(allChecks, pkg.Config.Checks) - for _, c := range ig.Checks { - if !allowedChecks[c] { - continue - } - couldveMatched = true - break + p := cum.ProblemObject(pkg.Fset, res) + problems = append(problems, p) + } + } + } + + for _, pkg := range pkgs { + for _, ig := range pkg.ignores { + for i := range pkg.problems { + p := &pkg.problems[i] + if ig.Match(*p) { + p.Severity = Ignored + } + } + for i := range problems { + p := &problems[i] + if ig.Match(*p) { + p.Severity = Ignored } - break } } - if !couldveMatched { - // The ignored checks were disabled for the containing package. - // Don't flag the ignore for not having matched. - continue + if pkg.cfg == nil { + // The package failed to load, otherwise we would have a + // valid config. Pass through all errors. + problems = append(problems, pkg.problems...) + } else { + for _, p := range pkg.problems { + allowedChecks := FilterChecks(allowedAnalyzers, pkg.cfg.Merge(l.Config).Checks) + allowedChecks["compile"] = true + if allowedChecks[p.Check] { + problems = append(problems, p) + } + } } - p := Problem{ - Position: DisplayPosition(prog.Fset(), ig.pos), - Text: "this linter directive didn't match anything; should it be removed?", - Check: "", - Package: nil, + + for _, ig := range pkg.ignores { + ig, ok := ig.(*LineIgnore) + if !ok { + continue + } + if ig.Matched { + continue + } + + couldveMatched := false + allowedChecks := FilterChecks(allowedAnalyzers, pkg.cfg.Merge(l.Config).Checks) + for _, c := range ig.Checks { + if !allowedChecks[c] { + continue + } + couldveMatched = true + break + } + + if !couldveMatched { + // The ignored checks were disabled for the containing package. + // Don't flag the ignore for not having matched. + continue + } + p := Problem{ + Pos: DisplayPosition(pkg.Fset, ig.Pos), + Message: "this linter directive didn't match anything; should it be removed?", + Check: "", + } + problems = append(problems, p) } - out = append(out, p) } - sort.Slice(out, func(i int, j int) bool { - pi, pj := out[i].Position, out[j].Position + if len(problems) == 0 { + return nil, nil + } + + sort.Slice(problems, func(i, j int) bool { + pi := problems[i].Pos + pj := problems[j].Pos if pi.Filename != pj.Filename { return pi.Filename < pj.Filename @@ -503,32 +364,22 @@ func (l *Linter) Lint(initial []*packages.Package, stats *PerfStats) []Problem { return pi.Column < pj.Column } - return out[i].Text < out[j].Text + return problems[i].Message < problems[j].Message }) - if l.PrintStats && stats != nil { - stats.Print(os.Stderr) - } - - if len(out) < 2 { - return out - } - - uniq := make([]Problem, 0, len(out)) - uniq = append(uniq, out[0]) - prev := out[0] - for _, p := range out[1:] { - if prev.Position == p.Position && prev.Text == p.Text { - continue + var out []Problem + out = append(out, problems[0]) + for i, p := range problems[1:] { + // We may encounter duplicate problems because one file + // can be part of many packages. + if problems[i] != p { + out = append(out, p) } - prev = p - uniq = append(uniq, p) } - - return uniq + return out, nil } -func FilterChecks(allChecks []string, checks []string) map[string]bool { +func FilterChecks(allChecks []*analysis.Analyzer, checks []string) map[string]bool { // OPT(dh): this entire computation could be cached per package allowedChecks := map[string]bool{} @@ -541,7 +392,7 @@ func FilterChecks(allChecks []string, checks []string) map[string]bool { if check == "*" || check == "all" { // Match all for _, c := range allChecks { - allowedChecks[c] = b + allowedChecks[c.Name] = b } } else if strings.HasSuffix(check, "*") { // Glob @@ -549,17 +400,17 @@ func FilterChecks(allChecks []string, checks []string) map[string]bool { isCat := strings.IndexFunc(prefix, func(r rune) bool { return unicode.IsNumber(r) }) == -1 for _, c := range allChecks { - idx := strings.IndexFunc(c, func(r rune) bool { return unicode.IsNumber(r) }) + idx := strings.IndexFunc(c.Name, func(r rune) bool { return unicode.IsNumber(r) }) if isCat { // Glob is S*, which should match S1000 but not SA1000 - cat := c[:idx] + cat := c.Name[:idx] if prefix == cat { - allowedChecks[c] = b + allowedChecks[c.Name] = b } } else { // Glob is S1* - if strings.HasPrefix(c, prefix) { - allowedChecks[c] = b + if strings.HasPrefix(c.Name, prefix) { + allowedChecks[c.Name] = b } } } @@ -571,28 +422,18 @@ func FilterChecks(allChecks []string, checks []string) map[string]bool { return allowedChecks } -// Pkg represents a package being linted. -type Pkg struct { - SSA *ssa.Package - InitialFunctions []*ssa.Function - *packages.Package - Config config.Config - Inspector *inspector.Inspector - // TODO(dh): this map should probably map from *ast.File, not string - Generated map[string]bool - - tokenFileMap map[*token.File]*ast.File -} - type Positioner interface { Pos() token.Pos } func DisplayPosition(fset *token.FileSet, p token.Pos) token.Position { + if p == token.NoPos { + return token.Position{} + } + // Only use the adjusted position if it points to another Go file. // This means we'll point to the original file for cgo files, but // we won't point to a YACC grammar file. - pos := fset.PositionFor(p, false) adjPos := fset.PositionFor(p, true) @@ -602,34 +443,6 @@ func DisplayPosition(fset *token.FileSet, p token.Pos) token.Position { return pos } -func (j *Job) Errorf(n Positioner, format string, args ...interface{}) *Problem { - pos := DisplayPosition(j.Pkg.Fset, n.Pos()) - if j.Pkg.Generated[pos.Filename] && j.check.FilterGenerated { - return nil - } - problem := Problem{ - Position: pos, - Text: fmt.Sprintf(format, args...), - Check: j.check.ID, - Package: j.Pkg, - } - j.problems = append(j.problems, problem) - return &j.problems[len(j.problems)-1] -} - -func allPackages(pkgs []*packages.Package) []*packages.Package { - var out []*packages.Package - packages.Visit( - pkgs, - func(pkg *packages.Package) bool { - out = append(out, pkg) - return true - }, - nil, - ) - return out -} - var bufferPool = &sync.Pool{ New: func() interface{} { buf := bytes.NewBuffer(nil) @@ -670,8 +483,7 @@ func writePackage(buf *bytes.Buffer, pkg *types.Package) { if pkg == nil { return } - var s string - s = pkg.Path() + s := pkg.Path() if s != "" { buf.WriteString(s) buf.WriteByte('.') diff --git a/vendor/honnef.co/go/tools/lint/lintdsl/lintdsl.go b/vendor/honnef.co/go/tools/lint/lintdsl/lintdsl.go index ab6800ab99..3b939e95f2 100644 --- a/vendor/honnef.co/go/tools/lint/lintdsl/lintdsl.go +++ b/vendor/honnef.co/go/tools/lint/lintdsl/lintdsl.go @@ -4,6 +4,7 @@ package lintdsl import ( "bytes" + "flag" "fmt" "go/ast" "go/constant" @@ -12,6 +13,8 @@ import ( "go/types" "strings" + "golang.org/x/tools/go/analysis" + "honnef.co/go/tools/facts" "honnef.co/go/tools/lint" "honnef.co/go/tools/ssa" ) @@ -71,16 +74,6 @@ func IsPointerLike(T types.Type) bool { return false } -func IsGenerated(f *ast.File) bool { - comments := f.Comments - if len(comments) > 0 { - comment := comments[0].Text() - return strings.Contains(comment, "Code generated by") || - strings.Contains(comment, "DO NOT EDIT") - } - return false -} - func IsIdent(expr ast.Expr, ident string) bool { id, ok := expr.(*ast.Ident) return ok && id.Name == ident @@ -103,26 +96,26 @@ func IsZero(expr ast.Expr) bool { return IsIntLiteral(expr, "0") } -func IsOfType(j *lint.Job, expr ast.Expr, name string) bool { - return IsType(j.Pkg.TypesInfo.TypeOf(expr), name) +func IsOfType(pass *analysis.Pass, expr ast.Expr, name string) bool { + return IsType(pass.TypesInfo.TypeOf(expr), name) } -func IsInTest(j *lint.Job, node lint.Positioner) bool { +func IsInTest(pass *analysis.Pass, node lint.Positioner) bool { // FIXME(dh): this doesn't work for global variables with // initializers - f := j.Pkg.Fset.File(node.Pos()) + f := pass.Fset.File(node.Pos()) return f != nil && strings.HasSuffix(f.Name(), "_test.go") } -func IsInMain(j *lint.Job, node lint.Positioner) bool { +func IsInMain(pass *analysis.Pass, node lint.Positioner) bool { if node, ok := node.(packager); ok { return node.Package().Pkg.Name() == "main" } - return j.Pkg.Types.Name() == "main" + return pass.Pkg.Name() == "main" } -func SelectorName(j *lint.Job, expr *ast.SelectorExpr) string { - info := j.Pkg.TypesInfo +func SelectorName(pass *analysis.Pass, expr *ast.SelectorExpr) string { + info := pass.TypesInfo sel := info.Selections[expr] if sel == nil { if x, ok := expr.X.(*ast.Ident); ok { @@ -138,16 +131,16 @@ func SelectorName(j *lint.Job, expr *ast.SelectorExpr) string { return fmt.Sprintf("(%s).%s", sel.Recv(), sel.Obj().Name()) } -func IsNil(j *lint.Job, expr ast.Expr) bool { - return j.Pkg.TypesInfo.Types[expr].IsNil() +func IsNil(pass *analysis.Pass, expr ast.Expr) bool { + return pass.TypesInfo.Types[expr].IsNil() } -func BoolConst(j *lint.Job, expr ast.Expr) bool { - val := j.Pkg.TypesInfo.ObjectOf(expr.(*ast.Ident)).(*types.Const).Val() +func BoolConst(pass *analysis.Pass, expr ast.Expr) bool { + val := pass.TypesInfo.ObjectOf(expr.(*ast.Ident)).(*types.Const).Val() return constant.BoolVal(val) } -func IsBoolConst(j *lint.Job, expr ast.Expr) bool { +func IsBoolConst(pass *analysis.Pass, expr ast.Expr) bool { // We explicitly don't support typed bools because more often than // not, custom bool types are used as binary enums and the // explicit comparison is desired. @@ -156,7 +149,7 @@ func IsBoolConst(j *lint.Job, expr ast.Expr) bool { if !ok { return false } - obj := j.Pkg.TypesInfo.ObjectOf(ident) + obj := pass.TypesInfo.ObjectOf(ident) c, ok := obj.(*types.Const) if !ok { return false @@ -171,8 +164,8 @@ func IsBoolConst(j *lint.Job, expr ast.Expr) bool { return true } -func ExprToInt(j *lint.Job, expr ast.Expr) (int64, bool) { - tv := j.Pkg.TypesInfo.Types[expr] +func ExprToInt(pass *analysis.Pass, expr ast.Expr) (int64, bool) { + tv := pass.TypesInfo.Types[expr] if tv.Value == nil { return 0, false } @@ -182,8 +175,8 @@ func ExprToInt(j *lint.Job, expr ast.Expr) (int64, bool) { return constant.Int64Val(tv.Value) } -func ExprToString(j *lint.Job, expr ast.Expr) (string, bool) { - val := j.Pkg.TypesInfo.Types[expr].Value +func ExprToString(pass *analysis.Pass, expr ast.Expr) (string, bool) { + val := pass.TypesInfo.Types[expr].Value if val == nil { return "", false } @@ -212,20 +205,21 @@ func DereferenceR(T types.Type) types.Type { return T } -func IsGoVersion(j *lint.Job, minor int) bool { - return j.GoVersion >= minor +func IsGoVersion(pass *analysis.Pass, minor int) bool { + version := pass.Analyzer.Flags.Lookup("go").Value.(flag.Getter).Get().(int) + return version >= minor } -func CallNameAST(j *lint.Job, call *ast.CallExpr) string { +func CallNameAST(pass *analysis.Pass, call *ast.CallExpr) string { switch fun := call.Fun.(type) { case *ast.SelectorExpr: - fn, ok := j.Pkg.TypesInfo.ObjectOf(fun.Sel).(*types.Func) + fn, ok := pass.TypesInfo.ObjectOf(fun.Sel).(*types.Func) if !ok { return "" } return lint.FuncName(fn) case *ast.Ident: - obj := j.Pkg.TypesInfo.ObjectOf(fun) + obj := pass.TypesInfo.ObjectOf(fun) switch obj := obj.(type) { case *types.Func: return lint.FuncName(obj) @@ -239,35 +233,35 @@ func CallNameAST(j *lint.Job, call *ast.CallExpr) string { } } -func IsCallToAST(j *lint.Job, node ast.Node, name string) bool { +func IsCallToAST(pass *analysis.Pass, node ast.Node, name string) bool { call, ok := node.(*ast.CallExpr) if !ok { return false } - return CallNameAST(j, call) == name + return CallNameAST(pass, call) == name } -func IsCallToAnyAST(j *lint.Job, node ast.Node, names ...string) bool { +func IsCallToAnyAST(pass *analysis.Pass, node ast.Node, names ...string) bool { for _, name := range names { - if IsCallToAST(j, node, name) { + if IsCallToAST(pass, node, name) { return true } } return false } -func Render(j *lint.Job, x interface{}) string { +func Render(pass *analysis.Pass, x interface{}) string { var buf bytes.Buffer - if err := printer.Fprint(&buf, j.Pkg.Fset, x); err != nil { + if err := printer.Fprint(&buf, pass.Fset, x); err != nil { panic(err) } return buf.String() } -func RenderArgs(j *lint.Job, args []ast.Expr) string { +func RenderArgs(pass *analysis.Pass, args []ast.Expr) string { var ss []string for _, arg := range args { - ss = append(ss, Render(j, arg)) + ss = append(ss, Render(pass, arg)) } return strings.Join(ss, ", ") } @@ -359,3 +353,48 @@ func flattenFields(T *types.Struct, path []int, seen map[types.Type]bool) []Fiel } return out } + +func File(pass *analysis.Pass, node lint.Positioner) *ast.File { + pass.Fset.PositionFor(node.Pos(), true) + m := pass.ResultOf[facts.TokenFile].(map[*token.File]*ast.File) + return m[pass.Fset.File(node.Pos())] +} + +// IsGenerated reports whether pos is in a generated file, It ignores +// //line directives. +func IsGenerated(pass *analysis.Pass, pos token.Pos) bool { + _, ok := Generator(pass, pos) + return ok +} + +// Generator returns the generator that generated the file containing +// pos. It ignores //line directives. +func Generator(pass *analysis.Pass, pos token.Pos) (facts.Generator, bool) { + file := pass.Fset.PositionFor(pos, false).Filename + m := pass.ResultOf[facts.Generated].(map[string]facts.Generator) + g, ok := m[file] + return g, ok +} + +func ReportfFG(pass *analysis.Pass, pos token.Pos, f string, args ...interface{}) { + file := lint.DisplayPosition(pass.Fset, pos).Filename + m := pass.ResultOf[facts.Generated].(map[string]facts.Generator) + if _, ok := m[file]; ok { + return + } + pass.Reportf(pos, f, args...) +} + +func ReportNodef(pass *analysis.Pass, node ast.Node, format string, args ...interface{}) { + msg := fmt.Sprintf(format, args...) + pass.Report(analysis.Diagnostic{Pos: node.Pos(), End: node.End(), Message: msg}) +} + +func ReportNodefFG(pass *analysis.Pass, node ast.Node, format string, args ...interface{}) { + file := lint.DisplayPosition(pass.Fset, node.Pos()).Filename + m := pass.ResultOf[facts.Generated].(map[string]facts.Generator) + if _, ok := m[file]; ok { + return + } + ReportNodef(pass, node, format, args...) +} diff --git a/vendor/honnef.co/go/tools/lint/lintutil/format/format.go b/vendor/honnef.co/go/tools/lint/lintutil/format/format.go index 23aa132de8..9385431f88 100644 --- a/vendor/honnef.co/go/tools/lint/lintutil/format/format.go +++ b/vendor/honnef.co/go/tools/lint/lintutil/format/format.go @@ -51,7 +51,7 @@ type Text struct { } func (o Text) Format(p lint.Problem) { - fmt.Fprintf(o.W, "%v: %s\n", relativePositionString(p.Position), p.String()) + fmt.Fprintf(o.W, "%v: %s\n", relativePositionString(p.Pos), p.String()) } type JSON struct { @@ -80,16 +80,22 @@ func (o JSON) Format(p lint.Problem) { Code string `json:"code"` Severity string `json:"severity,omitempty"` Location location `json:"location"` + End location `json:"end"` Message string `json:"message"` }{ Code: p.Check, Severity: severity(p.Severity), Location: location{ - File: p.Position.Filename, - Line: p.Position.Line, - Column: p.Position.Column, + File: p.Pos.Filename, + Line: p.Pos.Line, + Column: p.Pos.Column, }, - Message: p.Text, + End: location{ + File: p.End.Filename, + Line: p.End.Line, + Column: p.End.Column, + }, + Message: p.Message, } _ = json.NewEncoder(o.W).Encode(jp) } @@ -102,20 +108,21 @@ type Stylish struct { } func (o *Stylish) Format(p lint.Problem) { - if p.Position.Filename == "" { - p.Position.Filename = "-" + pos := p.Pos + if pos.Filename == "" { + pos.Filename = "-" } - if p.Position.Filename != o.prevFile { + if pos.Filename != o.prevFile { if o.prevFile != "" { o.tw.Flush() fmt.Fprintln(o.W) } - fmt.Fprintln(o.W, p.Position.Filename) - o.prevFile = p.Position.Filename + fmt.Fprintln(o.W, pos.Filename) + o.prevFile = pos.Filename o.tw = tabwriter.NewWriter(o.W, 0, 4, 2, ' ', 0) } - fmt.Fprintf(o.tw, " (%d, %d)\t%s\t%s\n", p.Position.Line, p.Position.Column, p.Check, p.Text) + fmt.Fprintf(o.tw, " (%d, %d)\t%s\t%s\n", pos.Line, pos.Column, p.Check, p.Message) } func (o *Stylish) Stats(total, errors, warnings int) { diff --git a/vendor/honnef.co/go/tools/lint/lintutil/stats.go b/vendor/honnef.co/go/tools/lint/lintutil/stats.go new file mode 100644 index 0000000000..ba8caf0afd --- /dev/null +++ b/vendor/honnef.co/go/tools/lint/lintutil/stats.go @@ -0,0 +1,7 @@ +// +build !aix,!android,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris + +package lintutil + +import "os" + +var infoSignals = []os.Signal{} diff --git a/vendor/honnef.co/go/tools/lint/lintutil/stats_bsd.go b/vendor/honnef.co/go/tools/lint/lintutil/stats_bsd.go new file mode 100644 index 0000000000..3a62ede031 --- /dev/null +++ b/vendor/honnef.co/go/tools/lint/lintutil/stats_bsd.go @@ -0,0 +1,10 @@ +// +build darwin dragonfly freebsd netbsd openbsd + +package lintutil + +import ( + "os" + "syscall" +) + +var infoSignals = []os.Signal{syscall.SIGINFO} diff --git a/vendor/honnef.co/go/tools/lint/lintutil/stats_posix.go b/vendor/honnef.co/go/tools/lint/lintutil/stats_posix.go new file mode 100644 index 0000000000..53f21c666b --- /dev/null +++ b/vendor/honnef.co/go/tools/lint/lintutil/stats_posix.go @@ -0,0 +1,10 @@ +// +build aix android linux solaris + +package lintutil + +import ( + "os" + "syscall" +) + +var infoSignals = []os.Signal{syscall.SIGUSR1} diff --git a/vendor/honnef.co/go/tools/lint/lintutil/util.go b/vendor/honnef.co/go/tools/lint/lintutil/util.go index 7017118312..fe0279f921 100644 --- a/vendor/honnef.co/go/tools/lint/lintutil/util.go +++ b/vendor/honnef.co/go/tools/lint/lintutil/util.go @@ -8,29 +8,70 @@ package lintutil // import "honnef.co/go/tools/lint/lintutil" import ( + "crypto/sha256" "errors" "flag" "fmt" "go/build" "go/token" + "io" "log" "os" + "os/signal" "regexp" "runtime" - "runtime/debug" "runtime/pprof" "strconv" "strings" - "time" + "sync/atomic" "honnef.co/go/tools/config" + "honnef.co/go/tools/internal/cache" "honnef.co/go/tools/lint" "honnef.co/go/tools/lint/lintutil/format" "honnef.co/go/tools/version" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/packages" ) +func NewVersionFlag() flag.Getter { + tags := build.Default.ReleaseTags + v := tags[len(tags)-1][2:] + version := new(VersionFlag) + if err := version.Set(v); err != nil { + panic(fmt.Sprintf("internal error: %s", err)) + } + return version +} + +type VersionFlag int + +func (v *VersionFlag) String() string { + return fmt.Sprintf("1.%d", *v) + +} + +func (v *VersionFlag) Set(s string) error { + if len(s) < 3 { + return errors.New("invalid Go version") + } + if s[0] != '1' { + return errors.New("invalid Go version") + } + if s[1] != '.' { + return errors.New("invalid Go version") + } + i, err := strconv.Atoi(s[2:]) + *v = VersionFlag(i) + return err +} + +func (v *VersionFlag) Get() interface{} { + return int(*v) +} + func usage(name string, flags *flag.FlagSet) func() { return func() { fmt.Fprintf(os.Stderr, "Usage of %s:\n", name) @@ -43,48 +84,6 @@ func usage(name string, flags *flag.FlagSet) func() { } } -func parseIgnore(s string) ([]lint.Ignore, error) { - var out []lint.Ignore - if len(s) == 0 { - return nil, nil - } - for _, part := range strings.Fields(s) { - p := strings.Split(part, ":") - if len(p) != 2 { - return nil, errors.New("malformed ignore string") - } - path := p[0] - checks := strings.Split(p[1], ",") - out = append(out, &lint.GlobIgnore{Pattern: path, Checks: checks}) - } - return out, nil -} - -type versionFlag int - -func (v *versionFlag) String() string { - return fmt.Sprintf("1.%d", *v) -} - -func (v *versionFlag) Set(s string) error { - if len(s) < 3 { - return errors.New("invalid Go version") - } - if s[0] != '1' { - return errors.New("invalid Go version") - } - if s[1] != '.' { - return errors.New("invalid Go version") - } - i, err := strconv.Atoi(s[2:]) - *v = versionFlag(i) - return err -} - -func (v *versionFlag) Get() interface{} { - return int(*v) -} - type list []string func (list *list) String() string { @@ -105,17 +104,16 @@ func FlagSet(name string) *flag.FlagSet { flags := flag.NewFlagSet("", flag.ExitOnError) flags.Usage = usage(name, flags) flags.String("tags", "", "List of `build tags`") - flags.String("ignore", "", "Deprecated: use linter directives instead") flags.Bool("tests", true, "Include tests") flags.Bool("version", false, "Print version and exit") flags.Bool("show-ignored", false, "Don't filter ignored problems") flags.String("f", "text", "Output `format` (valid choices are 'stylish', 'text' and 'json')") flags.String("explain", "", "Print description of `check`") - flags.Int("debug.max-concurrent-jobs", 0, "Number of jobs to run concurrently") - flags.Bool("debug.print-stats", false, "Print debug statistics") flags.String("debug.cpuprofile", "", "Write CPU profile to `file`") flags.String("debug.memprofile", "", "Write memory profile to `file`") + flags.Bool("debug.version", false, "Print detailed version information about this program") + flags.Bool("debug.no-compile-errors", false, "Don't print compile errors") checks := list{"inherit"} fail := list{"all"} @@ -124,7 +122,7 @@ func FlagSet(name string) *flag.FlagSet { tags := build.Default.ReleaseTags v := tags[len(tags)-1][2:] - version := new(versionFlag) + version := new(VersionFlag) if err := version.Set(v); err != nil { panic(fmt.Sprintf("internal error: %s", err)) } @@ -133,24 +131,17 @@ func FlagSet(name string) *flag.FlagSet { return flags } -func findCheck(cs []lint.Checker, check string) (lint.Check, bool) { +func findCheck(cs []*analysis.Analyzer, check string) (*analysis.Analyzer, bool) { for _, c := range cs { - for _, cc := range c.Checks() { - if cc.ID == check { - return cc, true - } + if c.Name == check { + return c, true } } - return lint.Check{}, false + return nil, false } -func ProcessFlagSet(cs []lint.Checker, fs *flag.FlagSet) { - if _, ok := os.LookupEnv("GOGC"); !ok { - debug.SetGCPercent(50) - } - +func ProcessFlagSet(cs []*analysis.Analyzer, cums []lint.CumulativeChecker, fs *flag.FlagSet) { tags := fs.Lookup("tags").Value.(flag.Getter).Get().(string) - ignore := fs.Lookup("ignore").Value.(flag.Getter).Get().(string) tests := fs.Lookup("tests").Value.(flag.Getter).Get().(bool) goVersion := fs.Lookup("go").Value.(flag.Getter).Get().(int) formatter := fs.Lookup("f").Value.(flag.Getter).Get().(string) @@ -158,10 +149,10 @@ func ProcessFlagSet(cs []lint.Checker, fs *flag.FlagSet) { showIgnored := fs.Lookup("show-ignored").Value.(flag.Getter).Get().(bool) explain := fs.Lookup("explain").Value.(flag.Getter).Get().(string) - maxConcurrentJobs := fs.Lookup("debug.max-concurrent-jobs").Value.(flag.Getter).Get().(int) - printStats := fs.Lookup("debug.print-stats").Value.(flag.Getter).Get().(bool) cpuProfile := fs.Lookup("debug.cpuprofile").Value.(flag.Getter).Get().(string) memProfile := fs.Lookup("debug.memprofile").Value.(flag.Getter).Get().(string) + debugVersion := fs.Lookup("debug.version").Value.(flag.Getter).Get().(bool) + debugNoCompile := fs.Lookup("debug.no-compile-errors").Value.(flag.Getter).Get().(bool) cfg := config.Config{} cfg.Checks = *fs.Lookup("checks").Value.(*list) @@ -188,13 +179,32 @@ func ProcessFlagSet(cs []lint.Checker, fs *flag.FlagSet) { pprof.StartCPUProfile(f) } + if debugVersion { + version.Verbose() + exit(0) + } + if printVersion { version.Print() exit(0) } + // Validate that the tags argument is well-formed. go/packages + // doesn't detect malformed build flags and returns unhelpful + // errors. + tf := buildutil.TagsFlag{} + if err := tf.Set(tags); err != nil { + fmt.Fprintln(os.Stderr, fmt.Errorf("invalid value %q for flag -tags: %s", tags, err)) + exit(1) + } + if explain != "" { - check, ok := findCheck(cs, explain) + var haystack []*analysis.Analyzer + haystack = append(haystack, cs...) + for _, cum := range cums { + haystack = append(haystack, cum.Analyzer()) + } + check, ok := findCheck(haystack, explain) if !ok { fmt.Fprintln(os.Stderr, "Couldn't find check", explain) exit(1) @@ -207,16 +217,11 @@ func ProcessFlagSet(cs []lint.Checker, fs *flag.FlagSet) { exit(0) } - ps, err := Lint(cs, fs.Args(), &Options{ - Tags: strings.Fields(tags), - LintTests: tests, - Ignores: ignore, - GoVersion: goVersion, - ReturnIgnored: showIgnored, - Config: cfg, - - MaxConcurrentJobs: maxConcurrentJobs, - PrintStats: printStats, + ps, err := Lint(cs, cums, fs.Args(), &Options{ + Tags: tags, + LintTests: tests, + GoVersion: goVersion, + Config: cfg, }) if err != nil { fmt.Fprintln(os.Stderr, err) @@ -243,15 +248,22 @@ func ProcessFlagSet(cs []lint.Checker, fs *flag.FlagSet) { ) fail := *fs.Lookup("fail").Value.(*list) - var allChecks []string - for _, p := range ps { - allChecks = append(allChecks, p.Check) + analyzers := make([]*analysis.Analyzer, len(cs), len(cs)+len(cums)) + copy(analyzers, cs) + for _, cum := range cums { + analyzers = append(analyzers, cum.Analyzer()) } - - shouldExit := lint.FilterChecks(allChecks, fail) + shouldExit := lint.FilterChecks(analyzers, fail) + shouldExit["compile"] = true total = len(ps) for _, p := range ps { + if p.Check == "compile" && debugNoCompile { + continue + } + if p.Severity == lint.Ignored && !showIgnored { + continue + } if shouldExit[p.Check] { errors++ } else { @@ -266,80 +278,97 @@ func ProcessFlagSet(cs []lint.Checker, fs *flag.FlagSet) { if errors > 0 { exit(1) } + exit(0) } type Options struct { Config config.Config - Tags []string - LintTests bool - Ignores string - GoVersion int - ReturnIgnored bool - - MaxConcurrentJobs int - PrintStats bool + Tags string + LintTests bool + GoVersion int } -func Lint(cs []lint.Checker, paths []string, opt *Options) ([]lint.Problem, error) { - stats := lint.PerfStats{ - CheckerInits: map[string]time.Duration{}, +func computeSalt() ([]byte, error) { + if version.Version != "devel" { + return []byte(version.Version), nil } + p, err := os.Executable() + if err != nil { + return nil, err + } + f, err := os.Open(p) + if err != nil { + return nil, err + } + defer f.Close() + h := sha256.New() + if _, err := io.Copy(h, f); err != nil { + return nil, err + } + return h.Sum(nil), nil +} + +func Lint(cs []*analysis.Analyzer, cums []lint.CumulativeChecker, paths []string, opt *Options) ([]lint.Problem, error) { + salt, err := computeSalt() + if err != nil { + return nil, fmt.Errorf("could not compute salt for cache: %s", err) + } + cache.SetSalt(salt) if opt == nil { opt = &Options{} } - ignores, err := parseIgnore(opt.Ignores) - if err != nil { - return nil, err - } - - conf := &packages.Config{ - Mode: packages.LoadAllSyntax, - Tests: opt.LintTests, - BuildFlags: []string{ - "-tags=" + strings.Join(opt.Tags, " "), - }, - } - - t := time.Now() - if len(paths) == 0 { - paths = []string{"."} - } - pkgs, err := packages.Load(conf, paths...) - if err != nil { - return nil, err - } - stats.PackageLoading = time.Since(t) - runtime.GC() - - var problems []lint.Problem - workingPkgs := make([]*packages.Package, 0, len(pkgs)) - for _, pkg := range pkgs { - if pkg.IllTyped { - problems = append(problems, compileErrors(pkg)...) - } else { - workingPkgs = append(workingPkgs, pkg) - } - } - - if len(workingPkgs) == 0 { - return problems, nil - } l := &lint.Linter{ - Checkers: cs, - Ignores: ignores, - GoVersion: opt.GoVersion, - ReturnIgnored: opt.ReturnIgnored, - Config: opt.Config, - - MaxConcurrentJobs: opt.MaxConcurrentJobs, - PrintStats: opt.PrintStats, + Checkers: cs, + CumulativeCheckers: cums, + GoVersion: opt.GoVersion, + Config: opt.Config, + } + cfg := &packages.Config{} + if opt.LintTests { + cfg.Tests = true + } + if opt.Tags != "" { + cfg.BuildFlags = append(cfg.BuildFlags, "-tags", opt.Tags) } - problems = append(problems, l.Lint(workingPkgs, &stats)...) - return problems, nil + printStats := func() { + // Individual stats are read atomically, but overall there + // is no synchronisation. For printing rough progress + // information, this doesn't matter. + switch atomic.LoadUint32(&l.Stats.State) { + case lint.StateInitializing: + fmt.Fprintln(os.Stderr, "Status: initializing") + case lint.StateGraph: + fmt.Fprintln(os.Stderr, "Status: loading package graph") + case lint.StateProcessing: + fmt.Fprintf(os.Stderr, "Packages: %d/%d initial, %d/%d total; Workers: %d/%d; Problems: %d\n", + atomic.LoadUint32(&l.Stats.ProcessedInitialPackages), + atomic.LoadUint32(&l.Stats.InitialPackages), + atomic.LoadUint32(&l.Stats.ProcessedPackages), + atomic.LoadUint32(&l.Stats.TotalPackages), + atomic.LoadUint32(&l.Stats.ActiveWorkers), + atomic.LoadUint32(&l.Stats.TotalWorkers), + atomic.LoadUint32(&l.Stats.Problems), + ) + case lint.StateCumulative: + fmt.Fprintln(os.Stderr, "Status: processing cumulative checkers") + } + } + if len(infoSignals) > 0 { + ch := make(chan os.Signal, 1) + signal.Notify(ch, infoSignals...) + defer signal.Stop(ch) + go func() { + for range ch { + printStats() + } + }() + } + + return l.Lint(cfg, paths) } var posRe = regexp.MustCompile(`^(.+?):(\d+)(?::(\d+)?)?$`) @@ -361,34 +390,3 @@ func parsePos(pos string) token.Position { Column: col, } } - -func compileErrors(pkg *packages.Package) []lint.Problem { - if !pkg.IllTyped { - return nil - } - if len(pkg.Errors) == 0 { - // transitively ill-typed - var ps []lint.Problem - for _, imp := range pkg.Imports { - ps = append(ps, compileErrors(imp)...) - } - return ps - } - var ps []lint.Problem - for _, err := range pkg.Errors { - p := lint.Problem{ - Position: parsePos(err.Pos), - Text: err.Msg, - Check: "compile", - } - ps = append(ps, p) - } - return ps -} - -func ProcessArgs(name string, cs []lint.Checker, args []string) { - flags := FlagSet(name) - flags.Parse(args) - - ProcessFlagSet(cs, flags) -} diff --git a/vendor/honnef.co/go/tools/lint/runner.go b/vendor/honnef.co/go/tools/lint/runner.go new file mode 100644 index 0000000000..3b22a63fa2 --- /dev/null +++ b/vendor/honnef.co/go/tools/lint/runner.go @@ -0,0 +1,970 @@ +package lint + +/* +Parallelism + +Runner implements parallel processing of packages by spawning one +goroutine per package in the dependency graph, without any semaphores. +Each goroutine initially waits on the completion of all of its +dependencies, thus establishing correct order of processing. Once all +dependencies finish processing, the goroutine will load the package +from export data or source – this loading is guarded by a semaphore, +sized according to the number of CPU cores. This way, we only have as +many packages occupying memory and CPU resources as there are actual +cores to process them. + +This combination of unbounded goroutines but bounded package loading +means that if we have many parallel, independent subgraphs, they will +all execute in parallel, while not wasting resources for long linear +chains or trying to process more subgraphs in parallel than the system +can handle. + +*/ + +import ( + "bytes" + "encoding/gob" + "encoding/hex" + "fmt" + "go/ast" + "go/token" + "go/types" + "reflect" + "regexp" + "runtime" + "sort" + "strconv" + "strings" + "sync" + "sync/atomic" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/packages" + "golang.org/x/tools/go/types/objectpath" + "honnef.co/go/tools/config" + "honnef.co/go/tools/facts" + "honnef.co/go/tools/internal/cache" + "honnef.co/go/tools/loader" +) + +// If enabled, abuse of the go/analysis API will lead to panics +const sanityCheck = true + +// OPT(dh): for a dependency tree A->B->C->D, if we have cached data +// for B, there should be no need to load C and D individually. Go's +// export data for B contains all the data we need on types, and our +// fact cache could store the union of B, C and D in B. +// +// This may change unused's behavior, however, as it may observe fewer +// interfaces from transitive dependencies. + +type Package struct { + dependents uint64 + + *packages.Package + Imports []*Package + initial bool + fromSource bool + hash string + done chan struct{} + + resultsMu sync.Mutex + // results maps analyzer IDs to analyzer results + results []*result + + cfg *config.Config + gen map[string]facts.Generator + problems []Problem + ignores []Ignore + errs []error + + // these slices are indexed by analysis + facts []map[types.Object][]analysis.Fact + pkgFacts [][]analysis.Fact + + canClearTypes bool +} + +func (pkg *Package) decUse() { + atomic.AddUint64(&pkg.dependents, ^uint64(0)) + if atomic.LoadUint64(&pkg.dependents) == 0 { + // nobody depends on this package anymore + if pkg.canClearTypes { + pkg.Types = nil + } + pkg.facts = nil + pkg.pkgFacts = nil + + for _, imp := range pkg.Imports { + imp.decUse() + } + } +} + +type result struct { + v interface{} + err error + ready chan struct{} +} + +type Runner struct { + ld loader.Loader + cache *cache.Cache + + analyzerIDs analyzerIDs + + // limits parallelism of loading packages + loadSem chan struct{} + + goVersion int + stats *Stats +} + +type analyzerIDs struct { + m map[*analysis.Analyzer]int +} + +func (ids analyzerIDs) get(a *analysis.Analyzer) int { + id, ok := ids.m[a] + if !ok { + panic(fmt.Sprintf("no analyzer ID for %s", a.Name)) + } + return id +} + +type Fact struct { + Path string + Fact analysis.Fact +} + +type analysisAction struct { + analyzer *analysis.Analyzer + analyzerID int + pkg *Package + newPackageFacts []analysis.Fact + problems []Problem + + pkgFacts map[*types.Package][]analysis.Fact +} + +func (ac *analysisAction) String() string { + return fmt.Sprintf("%s @ %s", ac.analyzer, ac.pkg) +} + +func (ac *analysisAction) allObjectFacts() []analysis.ObjectFact { + out := make([]analysis.ObjectFact, 0, len(ac.pkg.facts[ac.analyzerID])) + for obj, facts := range ac.pkg.facts[ac.analyzerID] { + for _, fact := range facts { + out = append(out, analysis.ObjectFact{ + Object: obj, + Fact: fact, + }) + } + } + return out +} + +func (ac *analysisAction) allPackageFacts() []analysis.PackageFact { + out := make([]analysis.PackageFact, 0, len(ac.pkgFacts)) + for pkg, facts := range ac.pkgFacts { + for _, fact := range facts { + out = append(out, analysis.PackageFact{ + Package: pkg, + Fact: fact, + }) + } + } + return out +} + +func (ac *analysisAction) importObjectFact(obj types.Object, fact analysis.Fact) bool { + if sanityCheck && len(ac.analyzer.FactTypes) == 0 { + panic("analysis doesn't export any facts") + } + for _, f := range ac.pkg.facts[ac.analyzerID][obj] { + if reflect.TypeOf(f) == reflect.TypeOf(fact) { + reflect.ValueOf(fact).Elem().Set(reflect.ValueOf(f).Elem()) + return true + } + } + return false +} + +func (ac *analysisAction) importPackageFact(pkg *types.Package, fact analysis.Fact) bool { + if sanityCheck && len(ac.analyzer.FactTypes) == 0 { + panic("analysis doesn't export any facts") + } + for _, f := range ac.pkgFacts[pkg] { + if reflect.TypeOf(f) == reflect.TypeOf(fact) { + reflect.ValueOf(fact).Elem().Set(reflect.ValueOf(f).Elem()) + return true + } + } + return false +} + +func (ac *analysisAction) exportObjectFact(obj types.Object, fact analysis.Fact) { + if sanityCheck && len(ac.analyzer.FactTypes) == 0 { + panic("analysis doesn't export any facts") + } + ac.pkg.facts[ac.analyzerID][obj] = append(ac.pkg.facts[ac.analyzerID][obj], fact) +} + +func (ac *analysisAction) exportPackageFact(fact analysis.Fact) { + if sanityCheck && len(ac.analyzer.FactTypes) == 0 { + panic("analysis doesn't export any facts") + } + ac.pkgFacts[ac.pkg.Types] = append(ac.pkgFacts[ac.pkg.Types], fact) + ac.newPackageFacts = append(ac.newPackageFacts, fact) +} + +func (ac *analysisAction) report(pass *analysis.Pass, d analysis.Diagnostic) { + p := Problem{ + Pos: DisplayPosition(pass.Fset, d.Pos), + End: DisplayPosition(pass.Fset, d.End), + Message: d.Message, + Check: pass.Analyzer.Name, + } + ac.problems = append(ac.problems, p) +} + +func (r *Runner) runAnalysis(ac *analysisAction) (ret interface{}, err error) { + ac.pkg.resultsMu.Lock() + res := ac.pkg.results[r.analyzerIDs.get(ac.analyzer)] + if res != nil { + ac.pkg.resultsMu.Unlock() + <-res.ready + return res.v, res.err + } else { + res = &result{ + ready: make(chan struct{}), + } + ac.pkg.results[r.analyzerIDs.get(ac.analyzer)] = res + ac.pkg.resultsMu.Unlock() + + defer func() { + res.v = ret + res.err = err + close(res.ready) + }() + + pass := new(analysis.Pass) + *pass = analysis.Pass{ + Analyzer: ac.analyzer, + Fset: ac.pkg.Fset, + Files: ac.pkg.Syntax, + // type information may be nil or may be populated. if it is + // nil, it will get populated later. + Pkg: ac.pkg.Types, + TypesInfo: ac.pkg.TypesInfo, + TypesSizes: ac.pkg.TypesSizes, + ResultOf: map[*analysis.Analyzer]interface{}{}, + ImportObjectFact: ac.importObjectFact, + ImportPackageFact: ac.importPackageFact, + ExportObjectFact: ac.exportObjectFact, + ExportPackageFact: ac.exportPackageFact, + Report: func(d analysis.Diagnostic) { + ac.report(pass, d) + }, + AllObjectFacts: ac.allObjectFacts, + AllPackageFacts: ac.allPackageFacts, + } + + if !ac.pkg.initial { + // Don't report problems in dependencies + pass.Report = func(analysis.Diagnostic) {} + } + return r.runAnalysisUser(pass, ac) + } +} + +func (r *Runner) loadCachedFacts(a *analysis.Analyzer, pkg *Package) ([]Fact, bool) { + if len(a.FactTypes) == 0 { + return nil, true + } + + var facts []Fact + // Look in the cache for facts + aID, err := passActionID(pkg, a) + if err != nil { + return nil, false + } + aID = cache.Subkey(aID, "facts") + b, _, err := r.cache.GetBytes(aID) + if err != nil { + // No cached facts, analyse this package like a user-provided one, but ignore diagnostics + return nil, false + } + + if err := gob.NewDecoder(bytes.NewReader(b)).Decode(&facts); err != nil { + // Cached facts are broken, analyse this package like a user-provided one, but ignore diagnostics + return nil, false + } + return facts, true +} + +type dependencyError struct { + dep string + err error +} + +func (err dependencyError) nested() dependencyError { + if o, ok := err.err.(dependencyError); ok { + return o.nested() + } + return err +} + +func (err dependencyError) Error() string { + if o, ok := err.err.(dependencyError); ok { + return o.Error() + } + return fmt.Sprintf("error running dependency %s: %s", err.dep, err.err) +} + +func (r *Runner) makeAnalysisAction(a *analysis.Analyzer, pkg *Package) *analysisAction { + aid := r.analyzerIDs.get(a) + ac := &analysisAction{ + analyzer: a, + analyzerID: aid, + pkg: pkg, + } + + if len(a.FactTypes) == 0 { + return ac + } + + // Merge all package facts of dependencies + ac.pkgFacts = map[*types.Package][]analysis.Fact{} + seen := map[*Package]struct{}{} + var dfs func(*Package) + dfs = func(pkg *Package) { + if _, ok := seen[pkg]; ok { + return + } + seen[pkg] = struct{}{} + s := pkg.pkgFacts[aid] + ac.pkgFacts[pkg.Types] = s[0:len(s):len(s)] + for _, imp := range pkg.Imports { + dfs(imp) + } + } + dfs(pkg) + + return ac +} + +// analyzes that we always want to run, even if they're not being run +// explicitly or as dependencies. these are necessary for the inner +// workings of the runner. +var injectedAnalyses = []*analysis.Analyzer{facts.Generated, config.Analyzer} + +func (r *Runner) runAnalysisUser(pass *analysis.Pass, ac *analysisAction) (interface{}, error) { + if !ac.pkg.fromSource { + panic(fmt.Sprintf("internal error: %s was not loaded from source", ac.pkg)) + } + + // User-provided package, analyse it + // First analyze it with dependencies + for _, req := range ac.analyzer.Requires { + acReq := r.makeAnalysisAction(req, ac.pkg) + ret, err := r.runAnalysis(acReq) + if err != nil { + // We couldn't run a dependency, no point in going on + return nil, dependencyError{req.Name, err} + } + + pass.ResultOf[req] = ret + } + + // Then with this analyzer + ret, err := ac.analyzer.Run(pass) + if err != nil { + return nil, err + } + + if len(ac.analyzer.FactTypes) > 0 { + // Merge new facts into the package and persist them. + var facts []Fact + for _, fact := range ac.newPackageFacts { + id := r.analyzerIDs.get(ac.analyzer) + ac.pkg.pkgFacts[id] = append(ac.pkg.pkgFacts[id], fact) + facts = append(facts, Fact{"", fact}) + } + for obj, afacts := range ac.pkg.facts[ac.analyzerID] { + if obj.Pkg() != ac.pkg.Package.Types { + continue + } + path, err := objectpath.For(obj) + if err != nil { + continue + } + for _, fact := range afacts { + facts = append(facts, Fact{string(path), fact}) + } + } + + buf := &bytes.Buffer{} + if err := gob.NewEncoder(buf).Encode(facts); err != nil { + return nil, err + } + aID, err := passActionID(ac.pkg, ac.analyzer) + if err != nil { + return nil, err + } + aID = cache.Subkey(aID, "facts") + if err := r.cache.PutBytes(aID, buf.Bytes()); err != nil { + return nil, err + } + } + + return ret, nil +} + +func NewRunner(stats *Stats) (*Runner, error) { + cache, err := cache.Default() + if err != nil { + return nil, err + } + + return &Runner{ + cache: cache, + stats: stats, + }, nil +} + +// Run loads packages corresponding to patterns and analyses them with +// analyzers. It returns the loaded packages, which contain reported +// diagnostics as well as extracted ignore directives. +// +// Note that diagnostics have not been filtered at this point yet, to +// accomodate cumulative analyzes that require additional steps to +// produce diagnostics. +func (r *Runner) Run(cfg *packages.Config, patterns []string, analyzers []*analysis.Analyzer, hasCumulative bool) ([]*Package, error) { + r.analyzerIDs = analyzerIDs{m: map[*analysis.Analyzer]int{}} + id := 0 + seen := map[*analysis.Analyzer]struct{}{} + var dfs func(a *analysis.Analyzer) + dfs = func(a *analysis.Analyzer) { + if _, ok := seen[a]; ok { + return + } + seen[a] = struct{}{} + r.analyzerIDs.m[a] = id + id++ + for _, f := range a.FactTypes { + gob.Register(f) + } + for _, req := range a.Requires { + dfs(req) + } + } + for _, a := range analyzers { + if v := a.Flags.Lookup("go"); v != nil { + v.Value.Set(fmt.Sprintf("1.%d", r.goVersion)) + } + dfs(a) + } + for _, a := range injectedAnalyses { + dfs(a) + } + + var dcfg packages.Config + if cfg != nil { + dcfg = *cfg + } + + atomic.StoreUint32(&r.stats.State, StateGraph) + initialPkgs, err := r.ld.Graph(dcfg, patterns...) + if err != nil { + return nil, err + } + + defer r.cache.Trim() + + var allPkgs []*Package + m := map[*packages.Package]*Package{} + packages.Visit(initialPkgs, nil, func(l *packages.Package) { + m[l] = &Package{ + Package: l, + results: make([]*result, len(r.analyzerIDs.m)), + facts: make([]map[types.Object][]analysis.Fact, len(r.analyzerIDs.m)), + pkgFacts: make([][]analysis.Fact, len(r.analyzerIDs.m)), + done: make(chan struct{}), + // every package needs itself + dependents: 1, + canClearTypes: !hasCumulative, + } + allPkgs = append(allPkgs, m[l]) + for i := range m[l].facts { + m[l].facts[i] = map[types.Object][]analysis.Fact{} + } + for _, err := range l.Errors { + m[l].errs = append(m[l].errs, err) + } + for _, v := range l.Imports { + m[v].dependents++ + m[l].Imports = append(m[l].Imports, m[v]) + } + + m[l].hash, err = packageHash(m[l]) + if err != nil { + m[l].errs = append(m[l].errs, err) + } + }) + + pkgs := make([]*Package, len(initialPkgs)) + for i, l := range initialPkgs { + pkgs[i] = m[l] + pkgs[i].initial = true + } + + atomic.StoreUint32(&r.stats.InitialPackages, uint32(len(initialPkgs))) + atomic.StoreUint32(&r.stats.TotalPackages, uint32(len(allPkgs))) + atomic.StoreUint32(&r.stats.State, StateProcessing) + + var wg sync.WaitGroup + wg.Add(len(allPkgs)) + r.loadSem = make(chan struct{}, runtime.GOMAXPROCS(-1)) + atomic.StoreUint32(&r.stats.TotalWorkers, uint32(cap(r.loadSem))) + for _, pkg := range allPkgs { + pkg := pkg + go func() { + r.processPkg(pkg, analyzers) + + if pkg.initial { + atomic.AddUint32(&r.stats.ProcessedInitialPackages, 1) + } + atomic.AddUint32(&r.stats.Problems, uint32(len(pkg.problems))) + wg.Done() + }() + } + wg.Wait() + + return pkgs, nil +} + +var posRe = regexp.MustCompile(`^(.+?):(\d+)(?::(\d+)?)?`) + +func parsePos(pos string) (token.Position, int, error) { + if pos == "-" || pos == "" { + return token.Position{}, 0, nil + } + parts := posRe.FindStringSubmatch(pos) + if parts == nil { + return token.Position{}, 0, fmt.Errorf("malformed position %q", pos) + } + file := parts[1] + line, _ := strconv.Atoi(parts[2]) + col, _ := strconv.Atoi(parts[3]) + return token.Position{ + Filename: file, + Line: line, + Column: col, + }, len(parts[0]), nil +} + +// loadPkg loads a Go package. If the package is in the set of initial +// packages, it will be loaded from source, otherwise it will be +// loaded from export data. In the case that the package was loaded +// from export data, cached facts will also be loaded. +// +// Currently, only cached facts for this package will be loaded, not +// for any of its dependencies. +func (r *Runner) loadPkg(pkg *Package, analyzers []*analysis.Analyzer) error { + if pkg.Types != nil { + panic(fmt.Sprintf("internal error: %s has already been loaded", pkg.Package)) + } + + // Load type information + if pkg.initial { + // Load package from source + pkg.fromSource = true + return r.ld.LoadFromSource(pkg.Package) + } + + // Load package from export data + if err := r.ld.LoadFromExport(pkg.Package); err != nil { + // We asked Go to give us up to date export data, yet + // we can't load it. There must be something wrong. + // + // Attempt loading from source. This should fail (because + // otherwise there would be export data); we just want to + // get the compile errors. If loading from source succeeds + // we discard the result, anyway. Otherwise we'll fail + // when trying to reload from export data later. + // + // FIXME(dh): we no longer reload from export data, so + // theoretically we should be able to continue + pkg.fromSource = true + if err := r.ld.LoadFromSource(pkg.Package); err != nil { + return err + } + // Make sure this package can't be imported successfully + pkg.Package.Errors = append(pkg.Package.Errors, packages.Error{ + Pos: "-", + Msg: fmt.Sprintf("could not load export data: %s", err), + Kind: packages.ParseError, + }) + return fmt.Errorf("could not load export data: %s", err) + } + + failed := false + seen := make([]bool, len(r.analyzerIDs.m)) + var dfs func(*analysis.Analyzer) + dfs = func(a *analysis.Analyzer) { + if seen[r.analyzerIDs.get(a)] { + return + } + seen[r.analyzerIDs.get(a)] = true + + if len(a.FactTypes) > 0 { + facts, ok := r.loadCachedFacts(a, pkg) + if !ok { + failed = true + return + } + + for _, f := range facts { + if f.Path == "" { + // This is a package fact + pkg.pkgFacts[r.analyzerIDs.get(a)] = append(pkg.pkgFacts[r.analyzerIDs.get(a)], f.Fact) + continue + } + obj, err := objectpath.Object(pkg.Types, objectpath.Path(f.Path)) + if err != nil { + // Be lenient about these errors. For example, when + // analysing io/ioutil from source, we may get a fact + // for methods on the devNull type, and objectpath + // will happily create a path for them. However, when + // we later load io/ioutil from export data, the path + // no longer resolves. + // + // If an exported type embeds the unexported type, + // then (part of) the unexported type will become part + // of the type information and our path will resolve + // again. + continue + } + pkg.facts[r.analyzerIDs.get(a)][obj] = append(pkg.facts[r.analyzerIDs.get(a)][obj], f.Fact) + } + } + + for _, req := range a.Requires { + dfs(req) + } + } + for _, a := range analyzers { + dfs(a) + } + + if failed { + pkg.fromSource = true + // XXX we added facts to the maps, we need to get rid of those + return r.ld.LoadFromSource(pkg.Package) + } + + return nil +} + +type analysisError struct { + analyzer *analysis.Analyzer + pkg *Package + err error +} + +func (err analysisError) Error() string { + return fmt.Sprintf("error running analyzer %s on %s: %s", err.analyzer, err.pkg, err.err) +} + +// processPkg processes a package. This involves loading the package, +// either from export data or from source. For packages loaded from +// source, the provides analyzers will be run on the package. +func (r *Runner) processPkg(pkg *Package, analyzers []*analysis.Analyzer) { + defer func() { + // Clear information we no longer need. Make sure to do this + // when returning from processPkg so that we clear + // dependencies, not just initial packages. + pkg.TypesInfo = nil + pkg.Syntax = nil + pkg.results = nil + + atomic.AddUint32(&r.stats.ProcessedPackages, 1) + pkg.decUse() + close(pkg.done) + }() + + // Ensure all packages have the generated map and config. This is + // required by interna of the runner. Analyses that themselves + // make use of either have an explicit dependency so that other + // runners work correctly, too. + analyzers = append(analyzers[0:len(analyzers):len(analyzers)], injectedAnalyses...) + + if len(pkg.errs) != 0 { + return + } + + for _, imp := range pkg.Imports { + <-imp.done + if len(imp.errs) > 0 { + if imp.initial { + // Don't print the error of the dependency since it's + // an initial package and we're already printing the + // error. + pkg.errs = append(pkg.errs, fmt.Errorf("could not analyze dependency %s of %s", imp, pkg)) + } else { + var s string + for _, err := range imp.errs { + s += "\n\t" + err.Error() + } + pkg.errs = append(pkg.errs, fmt.Errorf("could not analyze dependency %s of %s: %s", imp, pkg, s)) + } + return + } + } + if pkg.PkgPath == "unsafe" { + pkg.Types = types.Unsafe + return + } + + r.loadSem <- struct{}{} + atomic.AddUint32(&r.stats.ActiveWorkers, 1) + defer func() { + <-r.loadSem + atomic.AddUint32(&r.stats.ActiveWorkers, ^uint32(0)) + }() + if err := r.loadPkg(pkg, analyzers); err != nil { + pkg.errs = append(pkg.errs, err) + return + } + + // A package's object facts is the union of all of its dependencies. + for _, imp := range pkg.Imports { + for ai, m := range imp.facts { + for obj, facts := range m { + pkg.facts[ai][obj] = facts[0:len(facts):len(facts)] + } + } + } + + if !pkg.fromSource { + // Nothing left to do for the package. + return + } + + // Run analyses on initial packages and those missing facts + var wg sync.WaitGroup + wg.Add(len(analyzers)) + errs := make([]error, len(analyzers)) + var acs []*analysisAction + for i, a := range analyzers { + i := i + a := a + ac := r.makeAnalysisAction(a, pkg) + acs = append(acs, ac) + go func() { + defer wg.Done() + // Only initial packages and packages with missing + // facts will have been loaded from source. + if pkg.initial || r.hasFacts(a) { + if _, err := r.runAnalysis(ac); err != nil { + errs[i] = analysisError{a, pkg, err} + return + } + } + }() + } + wg.Wait() + + depErrors := map[dependencyError]int{} + for _, err := range errs { + if err == nil { + continue + } + switch err := err.(type) { + case analysisError: + switch err := err.err.(type) { + case dependencyError: + depErrors[err.nested()]++ + default: + pkg.errs = append(pkg.errs, err) + } + default: + pkg.errs = append(pkg.errs, err) + } + } + for err, count := range depErrors { + pkg.errs = append(pkg.errs, + fmt.Errorf("could not run %s@%s, preventing %d analyzers from running: %s", err.dep, pkg, count, err.err)) + } + + // We can't process ignores at this point because `unused` needs + // to see more than one package to make its decision. + ignores, problems := parseDirectives(pkg.Package) + pkg.ignores = append(pkg.ignores, ignores...) + pkg.problems = append(pkg.problems, problems...) + for _, ac := range acs { + pkg.problems = append(pkg.problems, ac.problems...) + } + + if pkg.initial { + // Only initial packages have these analyzers run, and only + // initial packages need these. + if pkg.results[r.analyzerIDs.get(config.Analyzer)].v != nil { + pkg.cfg = pkg.results[r.analyzerIDs.get(config.Analyzer)].v.(*config.Config) + } + pkg.gen = pkg.results[r.analyzerIDs.get(facts.Generated)].v.(map[string]facts.Generator) + } + + // In a previous version of the code, we would throw away all type + // information and reload it from export data. That was + // nonsensical. The *types.Package doesn't keep any information + // live that export data wouldn't also. We only need to discard + // the AST and the TypesInfo maps; that happens after we return + // from processPkg. +} + +// hasFacts reports whether an analysis exports any facts. An analysis +// that has a transitive dependency that exports facts is considered +// to be exporting facts. +func (r *Runner) hasFacts(a *analysis.Analyzer) bool { + ret := false + seen := make([]bool, len(r.analyzerIDs.m)) + var dfs func(*analysis.Analyzer) + dfs = func(a *analysis.Analyzer) { + if seen[r.analyzerIDs.get(a)] { + return + } + seen[r.analyzerIDs.get(a)] = true + if len(a.FactTypes) > 0 { + ret = true + } + for _, req := range a.Requires { + if ret { + break + } + dfs(req) + } + } + dfs(a) + return ret +} + +func parseDirective(s string) (cmd string, args []string) { + if !strings.HasPrefix(s, "//lint:") { + return "", nil + } + s = strings.TrimPrefix(s, "//lint:") + fields := strings.Split(s, " ") + return fields[0], fields[1:] +} + +// parseDirectives extracts all linter directives from the source +// files of the package. Malformed directives are returned as problems. +func parseDirectives(pkg *packages.Package) ([]Ignore, []Problem) { + var ignores []Ignore + var problems []Problem + + for _, f := range pkg.Syntax { + found := false + commentLoop: + for _, cg := range f.Comments { + for _, c := range cg.List { + if strings.Contains(c.Text, "//lint:") { + found = true + break commentLoop + } + } + } + if !found { + continue + } + cm := ast.NewCommentMap(pkg.Fset, f, f.Comments) + for node, cgs := range cm { + for _, cg := range cgs { + for _, c := range cg.List { + if !strings.HasPrefix(c.Text, "//lint:") { + continue + } + cmd, args := parseDirective(c.Text) + switch cmd { + case "ignore", "file-ignore": + if len(args) < 2 { + p := Problem{ + Pos: DisplayPosition(pkg.Fset, c.Pos()), + Message: "malformed linter directive; missing the required reason field?", + Severity: Error, + Check: "compile", + } + problems = append(problems, p) + continue + } + default: + // unknown directive, ignore + continue + } + checks := strings.Split(args[0], ",") + pos := DisplayPosition(pkg.Fset, node.Pos()) + var ig Ignore + switch cmd { + case "ignore": + ig = &LineIgnore{ + File: pos.Filename, + Line: pos.Line, + Checks: checks, + Pos: c.Pos(), + } + case "file-ignore": + ig = &FileIgnore{ + File: pos.Filename, + Checks: checks, + } + } + ignores = append(ignores, ig) + } + } + } + } + + return ignores, problems +} + +// packageHash computes a package's hash. The hash is based on all Go +// files that make up the package, as well as the hashes of imported +// packages. +func packageHash(pkg *Package) (string, error) { + key := cache.NewHash("package hash") + fmt.Fprintf(key, "pkgpath %s\n", pkg.PkgPath) + for _, f := range pkg.CompiledGoFiles { + h, err := cache.FileHash(f) + if err != nil { + return "", err + } + fmt.Fprintf(key, "file %s %x\n", f, h) + } + + imps := make([]*Package, len(pkg.Imports)) + copy(imps, pkg.Imports) + sort.Slice(imps, func(i, j int) bool { + return imps[i].PkgPath < imps[j].PkgPath + }) + for _, dep := range imps { + if dep.PkgPath == "unsafe" { + continue + } + + fmt.Fprintf(key, "import %s %s\n", dep.PkgPath, dep.hash) + } + h := key.Sum() + return hex.EncodeToString(h[:]), nil +} + +// passActionID computes an ActionID for an analysis pass. +func passActionID(pkg *Package, analyzer *analysis.Analyzer) (cache.ActionID, error) { + key := cache.NewHash("action ID") + fmt.Fprintf(key, "pkgpath %s\n", pkg.PkgPath) + fmt.Fprintf(key, "pkghash %s\n", pkg.hash) + fmt.Fprintf(key, "analyzer %s\n", analyzer.Name) + + return key.Sum(), nil +} diff --git a/vendor/honnef.co/go/tools/lint/stats.go b/vendor/honnef.co/go/tools/lint/stats.go new file mode 100644 index 0000000000..2f65085593 --- /dev/null +++ b/vendor/honnef.co/go/tools/lint/stats.go @@ -0,0 +1,20 @@ +package lint + +const ( + StateInitializing = 0 + StateGraph = 1 + StateProcessing = 2 + StateCumulative = 3 +) + +type Stats struct { + State uint32 + + InitialPackages uint32 + TotalPackages uint32 + ProcessedPackages uint32 + ProcessedInitialPackages uint32 + Problems uint32 + ActiveWorkers uint32 + TotalWorkers uint32 +} diff --git a/vendor/honnef.co/go/tools/loader/loader.go b/vendor/honnef.co/go/tools/loader/loader.go new file mode 100644 index 0000000000..9c6885d485 --- /dev/null +++ b/vendor/honnef.co/go/tools/loader/loader.go @@ -0,0 +1,197 @@ +package loader + +import ( + "fmt" + "go/ast" + "go/parser" + "go/scanner" + "go/token" + "go/types" + "log" + "os" + "sync" + + "golang.org/x/tools/go/gcexportdata" + "golang.org/x/tools/go/packages" +) + +type Loader struct { + exportMu sync.RWMutex +} + +// Graph resolves patterns and returns packages with all the +// information required to later load type information, and optionally +// syntax trees. +// +// The provided config can set any setting with the exception of Mode. +func (ld *Loader) Graph(cfg packages.Config, patterns ...string) ([]*packages.Package, error) { + cfg.Mode = packages.NeedName | packages.NeedImports | packages.NeedDeps | packages.NeedExportsFile | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedTypesSizes + pkgs, err := packages.Load(&cfg, patterns...) + if err != nil { + return nil, err + } + fset := token.NewFileSet() + packages.Visit(pkgs, nil, func(pkg *packages.Package) { + pkg.Fset = fset + }) + return pkgs, nil +} + +// LoadFromExport loads a package from export data. All of its +// dependencies must have been loaded already. +func (ld *Loader) LoadFromExport(pkg *packages.Package) error { + ld.exportMu.Lock() + defer ld.exportMu.Unlock() + + pkg.IllTyped = true + for path, pkg := range pkg.Imports { + if pkg.Types == nil { + return fmt.Errorf("dependency %q hasn't been loaded yet", path) + } + } + if pkg.ExportFile == "" { + return fmt.Errorf("no export data for %q", pkg.ID) + } + f, err := os.Open(pkg.ExportFile) + if err != nil { + return err + } + defer f.Close() + + r, err := gcexportdata.NewReader(f) + if err != nil { + return err + } + + view := make(map[string]*types.Package) // view seen by gcexportdata + seen := make(map[*packages.Package]bool) // all visited packages + var visit func(pkgs map[string]*packages.Package) + visit = func(pkgs map[string]*packages.Package) { + for _, pkg := range pkgs { + if !seen[pkg] { + seen[pkg] = true + view[pkg.PkgPath] = pkg.Types + visit(pkg.Imports) + } + } + } + visit(pkg.Imports) + tpkg, err := gcexportdata.Read(r, pkg.Fset, view, pkg.PkgPath) + if err != nil { + return err + } + pkg.Types = tpkg + pkg.IllTyped = false + return nil +} + +// LoadFromSource loads a package from source. All of its dependencies +// must have been loaded already. +func (ld *Loader) LoadFromSource(pkg *packages.Package) error { + ld.exportMu.RLock() + defer ld.exportMu.RUnlock() + + pkg.IllTyped = true + pkg.Types = types.NewPackage(pkg.PkgPath, pkg.Name) + + // OPT(dh): many packages have few files, much fewer than there + // are CPU cores. Additionally, parsing each individual file is + // very fast. A naive parallel implementation of this loop won't + // be faster, and tends to be slower due to extra scheduling, + // bookkeeping and potentially false sharing of cache lines. + pkg.Syntax = make([]*ast.File, len(pkg.CompiledGoFiles)) + for i, file := range pkg.CompiledGoFiles { + f, err := parser.ParseFile(pkg.Fset, file, nil, parser.ParseComments) + if err != nil { + pkg.Errors = append(pkg.Errors, convertError(err)...) + return err + } + pkg.Syntax[i] = f + } + pkg.TypesInfo = &types.Info{ + Types: make(map[ast.Expr]types.TypeAndValue), + Defs: make(map[*ast.Ident]types.Object), + Uses: make(map[*ast.Ident]types.Object), + Implicits: make(map[ast.Node]types.Object), + Scopes: make(map[ast.Node]*types.Scope), + Selections: make(map[*ast.SelectorExpr]*types.Selection), + } + + importer := func(path string) (*types.Package, error) { + if path == "unsafe" { + return types.Unsafe, nil + } + imp := pkg.Imports[path] + if imp == nil { + return nil, nil + } + if len(imp.Errors) > 0 { + return nil, imp.Errors[0] + } + return imp.Types, nil + } + tc := &types.Config{ + Importer: importerFunc(importer), + Error: func(err error) { + pkg.Errors = append(pkg.Errors, convertError(err)...) + }, + } + err := types.NewChecker(tc, pkg.Fset, pkg.Types, pkg.TypesInfo).Files(pkg.Syntax) + if err != nil { + return err + } + pkg.IllTyped = false + return nil +} + +func convertError(err error) []packages.Error { + var errs []packages.Error + // taken from go/packages + switch err := err.(type) { + case packages.Error: + // from driver + errs = append(errs, err) + + case *os.PathError: + // from parser + errs = append(errs, packages.Error{ + Pos: err.Path + ":1", + Msg: err.Err.Error(), + Kind: packages.ParseError, + }) + + case scanner.ErrorList: + // from parser + for _, err := range err { + errs = append(errs, packages.Error{ + Pos: err.Pos.String(), + Msg: err.Msg, + Kind: packages.ParseError, + }) + } + + case types.Error: + // from type checker + errs = append(errs, packages.Error{ + Pos: err.Fset.Position(err.Pos).String(), + Msg: err.Msg, + Kind: packages.TypeError, + }) + + default: + // unexpected impoverished error from parser? + errs = append(errs, packages.Error{ + Pos: "-", + Msg: err.Error(), + Kind: packages.UnknownError, + }) + + // If you see this error message, please file a bug. + log.Printf("internal error: error %q (%T) without position", err, err) + } + return errs +} + +type importerFunc func(path string) (*types.Package, error) + +func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) } diff --git a/vendor/honnef.co/go/tools/simple/analysis.go b/vendor/honnef.co/go/tools/simple/analysis.go new file mode 100644 index 0000000000..abb1648fab --- /dev/null +++ b/vendor/honnef.co/go/tools/simple/analysis.go @@ -0,0 +1,223 @@ +package simple + +import ( + "flag" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "honnef.co/go/tools/facts" + "honnef.co/go/tools/internal/passes/buildssa" + "honnef.co/go/tools/lint/lintutil" +) + +func newFlagSet() flag.FlagSet { + fs := flag.NewFlagSet("", flag.PanicOnError) + fs.Var(lintutil.NewVersionFlag(), "go", "Target Go version") + return *fs +} + +var Analyzers = map[string]*analysis.Analyzer{ + "S1000": { + Name: "S1000", + Run: LintSingleCaseSelect, + Doc: Docs["S1000"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1001": { + Name: "S1001", + Run: LintLoopCopy, + Doc: Docs["S1001"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1002": { + Name: "S1002", + Run: LintIfBoolCmp, + Doc: Docs["S1002"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1003": { + Name: "S1003", + Run: LintStringsContains, + Doc: Docs["S1003"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1004": { + Name: "S1004", + Run: LintBytesCompare, + Doc: Docs["S1004"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1005": { + Name: "S1005", + Run: LintUnnecessaryBlank, + Doc: Docs["S1005"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1006": { + Name: "S1006", + Run: LintForTrue, + Doc: Docs["S1006"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1007": { + Name: "S1007", + Run: LintRegexpRaw, + Doc: Docs["S1007"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1008": { + Name: "S1008", + Run: LintIfReturn, + Doc: Docs["S1008"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1009": { + Name: "S1009", + Run: LintRedundantNilCheckWithLen, + Doc: Docs["S1009"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1010": { + Name: "S1010", + Run: LintSlicing, + Doc: Docs["S1010"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1011": { + Name: "S1011", + Run: LintLoopAppend, + Doc: Docs["S1011"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1012": { + Name: "S1012", + Run: LintTimeSince, + Doc: Docs["S1012"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1016": { + Name: "S1016", + Run: LintSimplerStructConversion, + Doc: Docs["S1016"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1017": { + Name: "S1017", + Run: LintTrim, + Doc: Docs["S1017"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1018": { + Name: "S1018", + Run: LintLoopSlide, + Doc: Docs["S1018"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1019": { + Name: "S1019", + Run: LintMakeLenCap, + Doc: Docs["S1019"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1020": { + Name: "S1020", + Run: LintAssertNotNil, + Doc: Docs["S1020"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1021": { + Name: "S1021", + Run: LintDeclareAssign, + Doc: Docs["S1021"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1023": { + Name: "S1023", + Run: LintRedundantBreak, + Doc: Docs["S1023"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1024": { + Name: "S1024", + Run: LintTimeUntil, + Doc: Docs["S1024"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1025": { + Name: "S1025", + Run: LintRedundantSprintf, + Doc: Docs["S1025"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1028": { + Name: "S1028", + Run: LintErrorsNewSprintf, + Doc: Docs["S1028"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1029": { + Name: "S1029", + Run: LintRangeStringRunes, + Doc: Docs["S1029"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "S1030": { + Name: "S1030", + Run: LintBytesBufferConversions, + Doc: Docs["S1030"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1031": { + Name: "S1031", + Run: LintNilCheckAroundRange, + Doc: Docs["S1031"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1032": { + Name: "S1032", + Run: LintSortHelpers, + Doc: Docs["S1032"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1033": { + Name: "S1033", + Run: LintGuardedDelete, + Doc: Docs["S1033"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "S1034": { + Name: "S1034", + Run: LintSimplifyTypeSwitch, + Doc: Docs["S1034"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, +} diff --git a/vendor/honnef.co/go/tools/simple/doc.go b/vendor/honnef.co/go/tools/simple/doc.go index ea437da564..eb0072de5d 100644 --- a/vendor/honnef.co/go/tools/simple/doc.go +++ b/vendor/honnef.co/go/tools/simple/doc.go @@ -1,426 +1,425 @@ package simple -var docS1000 = `Use plain channel send or receive +import "honnef.co/go/tools/lint" -Select statements with a single case can be replaced with a simple send or receive. +var Docs = map[string]*lint.Documentation{ + "S1000": &lint.Documentation{ + Title: `Use plain channel send or receive instead of single-case select`, + Text: `Select statements with a single case can be replaced with a simple +send or receive. Before: -select { -case x := <-ch: - fmt.Println(x) -} + select { + case x := <-ch: + fmt.Println(x) + } After: -x := <-ch -fmt.Println(x) + x := <-ch + fmt.Println(x)`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docS1001 = `Replace with copy() - -Use copy() for copying elements from one slice to another. + "S1001": &lint.Documentation{ + Title: `Replace for loop with call to copy`, + Text: `Use copy() for copying elements from one slice to another. Before: -for i, x := range src { - dst[i] = x -} + for i, x := range src { + dst[i] = x + } After: -copy(dst, src) + copy(dst, src)`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "S1002": &lint.Documentation{ + Title: `Omit comparison with boolean constant`, + Text: `Before: -var docS1002 = `Omit comparison with boolean constant + if x == true {} + +After: + + if x {}`, + Since: "2017.1", + }, + + "S1003": &lint.Documentation{ + Title: `Replace call to strings.Index with strings.Contains`, + Text: `Before: + + if strings.Index(x, y) != -1 {} + +After: + + if strings.Contains(x, y) {}`, + Since: "2017.1", + }, + + "S1004": &lint.Documentation{ + Title: `Replace call to bytes.Compare with bytes.Equal`, + Text: `Before: + + if bytes.Compare(x, y) == 0 {} + +After: + + if bytes.Equal(x, y) {}`, + Since: "2017.1", + }, + + "S1005": &lint.Documentation{ + Title: `Drop unnecessary use of the blank identifier`, + Text: `In many cases, assigning to the blank identifier is unnecessary. Before: -if x == true {} + for _ = range s {} + x, _ = someMap[key] + _ = <-ch After: -if x {} + for range s{} + x = someMap[key] + <-ch`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "S1006": &lint.Documentation{ + Title: `Use for { ... } for infinite loops`, + Text: `For infinite loops, using for { ... } is the most idiomatic choice.`, + Since: "2017.1", + }, -var docS1003 = `Replace with strings.Contains + "S1007": &lint.Documentation{ + Title: `Simplify regular expression by using raw string literal`, + Text: `Raw string literals use ` + "`" + ` instead of " and do not support +any escape sequences. This means that the backslash (\) can be used +freely, without the need of escaping. + +Since regular expressions have their own escape sequences, raw strings +can improve their readability. Before: -if strings.Index(x, y) != -1 {} + regexp.Compile("\\A(\\w+) profile: total \\d+\\n\\z") After: -if strings.Contains(x, y) {} + regexp.Compile(` + "`" + `\A(\w+) profile: total \d+\n\z` + "`" + `)`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "S1008": &lint.Documentation{ + Title: `Simplify returning boolean expression`, + Text: `Before: -var docS1004 = `Replace with bytes.Equal + if { + return true + } + return false + +After: + + return `, + Since: "2017.1", + }, + + "S1009": &lint.Documentation{ + Title: `Omit redundant nil check on slices`, + Text: `The len function is defined for all slices, even nil ones, which have +a length of zero. It is not necessary to check if a slice is not nil +before checking that its length is not zero. Before: -if bytes.Compare(x, y) == 0 {} + if x != nil && len(x) != 0 {} After: -if bytes.Equal(x, y) {} + if len(x) != 0 {}`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "S1010": &lint.Documentation{ + Title: `Omit default slice index`, + Text: `When slicing, the second index defaults to the length of the value, +making s[n:len(s)] and s[n:] equivalent.`, + Since: "2017.1", + }, -var docS1005 = `Drop unnecessary use of the blank identifier + "S1011": &lint.Documentation{ + Title: `Use a single append to concatenate two slices`, + Text: `Before: -In many cases, assigning to the blank identifier is unnecessary. + for _, e := range y { + x = append(x, e) + } + +After: + + x = append(x, y...)`, + Since: "2017.1", + }, + + "S1012": &lint.Documentation{ + Title: `Replace time.Now().Sub(x) with time.Since(x)`, + Text: `The time.Since helper has the same effect as using time.Now().Sub(x) +but is easier to read. Before: -for _ = range s {} -x, _ = someMap[key] -_ = <-ch + time.Now().Sub(x) After: -for range s{} -x = someMap[key] -<-ch + time.Since(x)`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docS1006 = `Replace with for { ... } - -For infinite loops, using for { ... } is the most idiomatic choice. - -Available since - 2017.1 -` - -var docS1007 = `Simplify regular expression by using raw string literal - -Raw string literals use ` + "`" + ` instead of " and do not support any escape sequences. This means that the backslash (\) can be used freely, without the need of escaping. - -Since regular expressions have their own escape sequences, raw strings can improve their readability. + "S1016": &lint.Documentation{ + Title: `Use a type conversion instead of manually copying struct fields`, + Text: `Two struct types with identical fields can be converted between each +other. In older versions of Go, the fields had to have identical +struct tags. Since Go 1.8, however, struct tags are ignored during +conversions. It is thus not necessary to manually copy every field +individually. Before: -regexp.Compile("\\A(\\w+) profile: total \\d+\\n\\z") + var x T1 + y := T2{ + Field1: x.Field1, + Field2: x.Field2, + } After: -regexp.Compile(` + "`" + `\A(\w+) profile: total \d+\n\z` + "`" + `) + var x T1 + y := T2(x)`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docS1008 = `Simplify returning boolean expression + "S1017": &lint.Documentation{ + Title: `Replace manual trimming with strings.TrimPrefix`, + Text: `Instead of using strings.HasPrefix and manual slicing, use the +strings.TrimPrefix function. If the string doesn't start with the +prefix, the original string will be returned. Using strings.TrimPrefix +reduces complexity, and avoids common bugs, such as off-by-one +mistakes. Before: -if { - return true -} -return false + if strings.HasPrefix(str, prefix) { + str = str[len(prefix):] + } After: -return + str = strings.TrimPrefix(str, prefix)`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docS1009 = `Omit redundant nil check on slices - -The len function is defined for all slices, even nil ones, which have a length of zero. It is not necessary to check if a slice is not nil before checking that its length is not zero. + "S1018": &lint.Documentation{ + Title: `Use copy for sliding elements`, + Text: `copy() permits using the same source and destination slice, even with +overlapping ranges. This makes it ideal for sliding elements in a +slice. Before: -if x != nil && len(x) != 0 {} + for i := 0; i < n; i++ { + bs[i] = bs[offset+i] + } After: -if len(x) != 0 {} + copy(bs[:n], bs[offset:])`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "S1019": &lint.Documentation{ + Title: `Simplify make call by omitting redundant arguments`, + Text: `The make function has default values for the length and capacity +arguments. For channels and maps, the length defaults to zero. +Additionally, for slices the capacity defaults to the length.`, + Since: "2017.1", + }, -var docS1010 = `Omit default slice index + "S1020": &lint.Documentation{ + Title: `Omit redundant nil check in type assertion`, + Text: `Before: -When slicing, the second index defaults to the length of the value, making s[n:len(s)] and s[n:] equivalent. + if _, ok := i.(T); ok && i != nil {} -Available since - 2017.1 -` +After: -var docS1011 = `Use a single append to concatenate two slices + if _, ok := i.(T); ok {}`, + Since: "2017.1", + }, + + "S1021": &lint.Documentation{ + Title: `Merge variable declaration and assignment`, + Text: `Before: + + var x uint + x = 1 + +After: + + var x uint = 1`, + Since: "2017.1", + }, + + "S1023": &lint.Documentation{ + Title: `Omit redundant control flow`, + Text: `Functions that have no return value do not need a return statement as +the final statement of the function. + +Switches in Go do not have automatic fallthrough, unlike languages +like C. It is not necessary to have a break statement as the final +statement in a case block.`, + Since: "2017.1", + }, + + "S1024": &lint.Documentation{ + Title: `Replace x.Sub(time.Now()) with time.Until(x)`, + Text: `The time.Until helper has the same effect as using x.Sub(time.Now()) +but is easier to read. Before: -for _, e := range y { - x = append(x, e) -} + x.Sub(time.Now()) After: -x = append(x, y...) + time.Until(x)`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docS1012 = `Replace with time.Since(x) - -The time.Since helper has the same effect as using time.Now().Sub(x) but is easier to read. - -Before: - -time.Now().Sub(x) - -After: - -time.Since(x) - -Available since - 2017.1 -` - -var docS1016 = `Use a type conversion - -Two struct types with identical fields can be converted between each other. In older versions of Go, the fields had to have identical struct tags. Since Go 1.8, however, struct tags are ignored during conversions. It is thus not necessary to manually copy every field individually. - -Before: - -var x T1 -y := T2{ - Field1: x.Field1, - Field2: x.Field2, -} - -After: - -var x T1 -y := T2(x) - -Available since - 2017.1 -` - -var docS1017 = `Replace with strings.TrimPrefix - -Instead of using strings.HasPrefix and manual slicing, use the strings.TrimPrefix function. If the string doesn't start with the prefix, the original string will be returned. Using strings.TrimPrefix reduces complexity, and avoids common bugs, such as off-by-one mistakes. - -Before: - -if strings.HasPrefix(str, prefix) { - str = str[len(prefix):] -} - -After: - -str = strings.TrimPrefix(str, prefix) - -Available since - 2017.1 -` - -var docS1018 = `Replace with copy() - -copy() permits using the same source and destination slice, even with overlapping ranges. This makes it ideal for sliding elements in a slice. - -Before: - -for i := 0; i < n; i++ { - bs[i] = bs[offset+i] -} - -After: - -copy(bs[:n], bs[offset:]) - -Available since - 2017.1 -` - -var docS1019 = `Simplify make call - -The make function has default values for the length and capacity arguments. For channels and maps, the length defaults to zero. Additionally, for slices the capacity defaults to the length. - -Available since - 2017.1 -` - -var docS1020 = `Omit redundant nil check in type assertion - -Before: - -if _, ok := i.(T); ok && i != nil {} - -After: - -if _, ok := i.(T); ok {} - -Available since - 2017.1 -` - -var docS1021 = `Merge variable declaration and assignment - -Before: - -var x uint -x = 1 - -After: - -var x uint = 1 - -Available since - 2017.1 -` -var docS1023 = `Omit redundant control flow - -Functions that have no return value do not need a return statement as the final statement of the function. - -Switches in Go do not have automatic fallthrough, unlike languages like C. It is not necessary to have a break statement as the final statement in a case block. - -Available since - 2017.1 -` - -var docS1024 = `Replace with time.Until(x) - -The time.Until helper has the same effect as using x.Sub(time.Now()) but is easier to read. - -Before: - -x.Sub(time.Now()) - -After: - -time.Until(x) - -Available since - 2017.1 -` - -var docS1025 = `Don't use fmt.Sprintf("%s", x) unnecessarily - -In many instances, there are easier and more efficient ways of getting a value's string representation. Whenever a value's underlying type is a string already, or the type has a String method, they should be used directly. + "S1025": &lint.Documentation{ + Title: `Don't use fmt.Sprintf("%s", x) unnecessarily`, + Text: `In many instances, there are easier and more efficient ways of getting +a value's string representation. Whenever a value's underlying type is +a string already, or the type has a String method, they should be used +directly. Given the following shared definitions -type T1 string -type T2 int + type T1 string + type T2 int -func (T2) String() string { return "Hello, world" } + func (T2) String() string { return "Hello, world" } -var x string -var y T1 -var z T2 + var x string + var y T1 + var z T2 we can simplify the following -fmt.Sprintf("%s", x) -fmt.Sprintf("%s", y) -fmt.Sprintf("%s", z) + fmt.Sprintf("%s", x) + fmt.Sprintf("%s", y) + fmt.Sprintf("%s", z) to -x -string(y) -z.String() + x + string(y) + z.String()`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "S1028": &lint.Documentation{ + Title: `Simplify error construction with fmt.Errorf`, + Text: `Before: -var docS1028 = `replace with fmt.Errorf - -Before: - -errors.New(fmt.Sprintf(...)) + errors.New(fmt.Sprintf(...)) After: -fmt.Errorf(...) + fmt.Errorf(...)`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docS1029 = `Range over the string - -Ranging over a string will yield byte offsets and runes. If the offset isn't used, this is functionally equivalent to converting the string to a slice of runes and ranging over that. Ranging directly over the string will be more performant, however, as it avoids allocating a new slice, the size of which depends on the length of the string. + "S1029": &lint.Documentation{ + Title: `Range over the string directly`, + Text: `Ranging over a string will yield byte offsets and runes. If the offset +isn't used, this is functionally equivalent to converting the string +to a slice of runes and ranging over that. Ranging directly over the +string will be more performant, however, as it avoids allocating a new +slice, the size of which depends on the length of the string. Before: -for _, r := range []rune(s) {} + for _, r := range []rune(s) {} After: -for _, r := range s {} + for _, r := range s {}`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "S1030": &lint.Documentation{ + Title: `Use bytes.Buffer.String or bytes.Buffer.Bytes`, + Text: `bytes.Buffer has both a String and a Bytes method. It is never +necessary to use string(buf.Bytes()) or []byte(buf.String()) – simply +use the other method.`, + Since: "2017.1", + }, -var docS1030 = `Use bytes.Buffer.String or bytes.Buffer.Bytes - -bytes.Buffer has both a String and a Bytes method. It is never necessary to use string(buf.Bytes()) or []byte(buf.String()) – simply use the other method. - -Available since - 2017.1 -` - -var docS1031 = `Omit redundant nil check around loop - -You can use range on nil slices and maps, the loop will simply never execute. This makes an additional nil check around the loop unnecessary. + "S1031": &lint.Documentation{ + Title: `Omit redundant nil check around loop`, + Text: `You can use range on nil slices and maps, the loop will simply never +execute. This makes an additional nil check around the loop +unnecessary. Before: -if s != nil { - for _, x := range s { - ... - } + if s != nil { + for _, x := range s { + ... + } + } + +After: + + for _, x := range s { + ... + }`, + Since: "2017.1", + }, + + "S1032": &lint.Documentation{ + Title: `Use sort.Ints(x), sort.Float64s(x), and sort.Strings(x)`, + Text: `The sort.Ints, sort.Float64s and sort.Strings functions are easier to +read than sort.Sort(sort.IntSlice(x)), sort.Sort(sort.Float64Slice(x)) +and sort.Sort(sort.StringSlice(x)). + +Before: + + sort.Sort(sort.StringSlice(x)) + +After: + + sort.Strings(x)`, + Since: "2019.1", + }, + + "S1033": &lint.Documentation{ + Title: `Unnecessary guard around call to delete`, + Text: `Calling delete on a nil map is a no-op.`, + Since: "2019.2", + }, + + "S1034": &lint.Documentation{ + Title: `Use result of type assertion to simplify cases`, + Since: "2019.2", + }, } - -After: - -for _, x := range s { - ... -} - -Available since - 2017.1 -` - -var docS1032 = `Replace with sort.Ints(x), sort.Float64s(x), sort.Strings(x) - -The sort.Ints, sort.Float64s and sort.Strings functions are easier to read than sort.Sort(sort.IntSlice(x)), sort.Sort(sort.Float64Slice(x)) and sort.Sort(sort.StringSlice(x)). - -Before: - -sort.Sort(sort.StringSlice(x)) - -After: - -sort.Strings(x) - -Available since - 2019.1 -` diff --git a/vendor/honnef.co/go/tools/simple/lint.go b/vendor/honnef.co/go/tools/simple/lint.go index db805770ce..c78a7bb7a4 100644 --- a/vendor/honnef.co/go/tools/simple/lint.go +++ b/vendor/honnef.co/go/tools/simple/lint.go @@ -8,67 +8,21 @@ import ( "go/token" "go/types" "reflect" + "sort" "strings" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/go/types/typeutil" . "honnef.co/go/tools/arg" + "honnef.co/go/tools/internal/passes/buildssa" "honnef.co/go/tools/internal/sharedcheck" "honnef.co/go/tools/lint" . "honnef.co/go/tools/lint/lintdsl" - - "golang.org/x/tools/go/types/typeutil" ) -type Checker struct { - CheckGenerated bool - MS *typeutil.MethodSetCache -} - -func NewChecker() *Checker { - return &Checker{ - MS: &typeutil.MethodSetCache{}, - } -} - -func (*Checker) Name() string { return "gosimple" } -func (*Checker) Prefix() string { return "S" } - -func (c *Checker) Init(prog *lint.Program) {} - -func (c *Checker) Checks() []lint.Check { - return []lint.Check{ - {ID: "S1000", FilterGenerated: true, Fn: c.LintSingleCaseSelect, Doc: docS1000}, - {ID: "S1001", FilterGenerated: true, Fn: c.LintLoopCopy, Doc: docS1001}, - {ID: "S1002", FilterGenerated: true, Fn: c.LintIfBoolCmp, Doc: docS1002}, - {ID: "S1003", FilterGenerated: true, Fn: c.LintStringsContains, Doc: docS1003}, - {ID: "S1004", FilterGenerated: true, Fn: c.LintBytesCompare, Doc: docS1004}, - {ID: "S1005", FilterGenerated: true, Fn: c.LintUnnecessaryBlank, Doc: docS1005}, - {ID: "S1006", FilterGenerated: true, Fn: c.LintForTrue, Doc: docS1006}, - {ID: "S1007", FilterGenerated: true, Fn: c.LintRegexpRaw, Doc: docS1007}, - {ID: "S1008", FilterGenerated: true, Fn: c.LintIfReturn, Doc: docS1008}, - {ID: "S1009", FilterGenerated: true, Fn: c.LintRedundantNilCheckWithLen, Doc: docS1009}, - {ID: "S1010", FilterGenerated: true, Fn: c.LintSlicing, Doc: docS1010}, - {ID: "S1011", FilterGenerated: true, Fn: c.LintLoopAppend, Doc: docS1011}, - {ID: "S1012", FilterGenerated: true, Fn: c.LintTimeSince, Doc: docS1012}, - {ID: "S1016", FilterGenerated: true, Fn: c.LintSimplerStructConversion, Doc: docS1016}, - {ID: "S1017", FilterGenerated: true, Fn: c.LintTrim, Doc: docS1017}, - {ID: "S1018", FilterGenerated: true, Fn: c.LintLoopSlide, Doc: docS1018}, - {ID: "S1019", FilterGenerated: true, Fn: c.LintMakeLenCap, Doc: docS1019}, - {ID: "S1020", FilterGenerated: true, Fn: c.LintAssertNotNil, Doc: docS1020}, - {ID: "S1021", FilterGenerated: true, Fn: c.LintDeclareAssign, Doc: docS1021}, - {ID: "S1023", FilterGenerated: true, Fn: c.LintRedundantBreak, Doc: docS1023}, - {ID: "S1024", FilterGenerated: true, Fn: c.LintTimeUntil, Doc: docS1024}, - {ID: "S1025", FilterGenerated: true, Fn: c.LintRedundantSprintf, Doc: docS1025}, - {ID: "S1028", FilterGenerated: true, Fn: c.LintErrorsNewSprintf, Doc: docS1028}, - {ID: "S1029", FilterGenerated: false, Fn: c.LintRangeStringRunes, Doc: docS1029}, - {ID: "S1030", FilterGenerated: true, Fn: c.LintBytesBufferConversions, Doc: docS1030}, - {ID: "S1031", FilterGenerated: true, Fn: c.LintNilCheckAroundRange, Doc: docS1031}, - {ID: "S1032", FilterGenerated: true, Fn: c.LintSortHelpers, Doc: docS1032}, - {ID: "S1033", FilterGenerated: true, Fn: c.LintGuardedDelete, Doc: ``}, - {ID: "S1034", FilterGenerated: true, Fn: c.LintSimplifyTypeSwitch, Doc: ``}, - } -} - -func (c *Checker) LintSingleCaseSelect(j *lint.Job) { +func LintSingleCaseSelect(pass *analysis.Pass) (interface{}, error) { isSingleSelect := func(node ast.Node) bool { v, ok := node.(*ast.SelectStmt) if !ok { @@ -92,7 +46,7 @@ func (c *Checker) LintSingleCaseSelect(j *lint.Job) { return } seen[v.Body.List[0]] = struct{}{} - j.Errorf(node, "should use for range instead of for { select {} }") + ReportNodefFG(pass, node, "should use for range instead of for { select {} }") case *ast.SelectStmt: if _, ok := seen[v]; ok { return @@ -100,13 +54,14 @@ func (c *Checker) LintSingleCaseSelect(j *lint.Job) { if !isSingleSelect(v) { return } - j.Errorf(node, "should use a simple channel send/receive instead of select with a single case") + ReportNodefFG(pass, node, "should use a simple channel send/receive instead of select with a single case") } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.ForStmt)(nil), (*ast.SelectStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil), (*ast.SelectStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) LintLoopCopy(j *lint.Job) { +func LintLoopCopy(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { loop := node.(*ast.RangeStmt) @@ -128,7 +83,7 @@ func (c *Checker) LintLoopCopy(j *lint.Job) { return } - if _, ok := j.Pkg.TypesInfo.TypeOf(lhs.X).(*types.Slice); !ok { + if _, ok := pass.TypesInfo.TypeOf(lhs.X).(*types.Slice); !ok { return } lidx, ok := lhs.Index.(*ast.Ident) @@ -139,16 +94,16 @@ func (c *Checker) LintLoopCopy(j *lint.Job) { if !ok { return } - if j.Pkg.TypesInfo.TypeOf(lhs) == nil || j.Pkg.TypesInfo.TypeOf(stmt.Rhs[0]) == nil { + if pass.TypesInfo.TypeOf(lhs) == nil || pass.TypesInfo.TypeOf(stmt.Rhs[0]) == nil { return } - if j.Pkg.TypesInfo.ObjectOf(lidx) != j.Pkg.TypesInfo.ObjectOf(key) { + if pass.TypesInfo.ObjectOf(lidx) != pass.TypesInfo.ObjectOf(key) { return } - if !types.Identical(j.Pkg.TypesInfo.TypeOf(lhs), j.Pkg.TypesInfo.TypeOf(stmt.Rhs[0])) { + if !types.Identical(pass.TypesInfo.TypeOf(lhs), pass.TypesInfo.TypeOf(stmt.Rhs[0])) { return } - if _, ok := j.Pkg.TypesInfo.TypeOf(loop.X).(*types.Slice); !ok { + if _, ok := pass.TypesInfo.TypeOf(loop.X).(*types.Slice); !ok { return } @@ -162,7 +117,7 @@ func (c *Checker) LintLoopCopy(j *lint.Job) { if !ok { return } - if j.Pkg.TypesInfo.ObjectOf(ridx) != j.Pkg.TypesInfo.ObjectOf(key) { + if pass.TypesInfo.ObjectOf(ridx) != pass.TypesInfo.ObjectOf(key) { return } } else if rhs, ok := stmt.Rhs[0].(*ast.Ident); ok { @@ -170,38 +125,39 @@ func (c *Checker) LintLoopCopy(j *lint.Job) { if !ok { return } - if j.Pkg.TypesInfo.ObjectOf(rhs) != j.Pkg.TypesInfo.ObjectOf(value) { + if pass.TypesInfo.ObjectOf(rhs) != pass.TypesInfo.ObjectOf(value) { return } } else { return } - j.Errorf(loop, "should use copy() instead of a loop") + ReportNodefFG(pass, loop, "should use copy() instead of a loop") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.RangeStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.RangeStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) LintIfBoolCmp(j *lint.Job) { +func LintIfBoolCmp(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { expr := node.(*ast.BinaryExpr) if expr.Op != token.EQL && expr.Op != token.NEQ { return } - x := IsBoolConst(j, expr.X) - y := IsBoolConst(j, expr.Y) + x := IsBoolConst(pass, expr.X) + y := IsBoolConst(pass, expr.Y) if !x && !y { return } var other ast.Expr var val bool if x { - val = BoolConst(j, expr.X) + val = BoolConst(pass, expr.X) other = expr.Y } else { - val = BoolConst(j, expr.Y) + val = BoolConst(pass, expr.Y) other = expr.X } - basic, ok := j.Pkg.TypesInfo.TypeOf(other).Underlying().(*types.Basic) + basic, ok := pass.TypesInfo.TypeOf(other).Underlying().(*types.Basic) if !ok || basic.Kind() != types.Bool { return } @@ -209,21 +165,22 @@ func (c *Checker) LintIfBoolCmp(j *lint.Job) { if (expr.Op == token.EQL && !val) || (expr.Op == token.NEQ && val) { op = "!" } - r := op + Render(j, other) + r := op + Render(pass, other) l1 := len(r) r = strings.TrimLeft(r, "!") if (l1-len(r))%2 == 1 { r = "!" + r } - if IsInTest(j, node) { + if IsInTest(pass, node) { return } - j.Errorf(expr, "should omit comparison to bool constant, can be simplified to %s", r) + ReportNodefFG(pass, expr, "should omit comparison to bool constant, can be simplified to %s", r) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) LintBytesBufferConversions(j *lint.Job) { +func LintBytesBufferConversions(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) if len(call.Args) != 1 { @@ -239,18 +196,19 @@ func (c *Checker) LintBytesBufferConversions(j *lint.Job) { return } - typ := j.Pkg.TypesInfo.TypeOf(call.Fun) - if typ == types.Universe.Lookup("string").Type() && IsCallToAST(j, call.Args[0], "(*bytes.Buffer).Bytes") { - j.Errorf(call, "should use %v.String() instead of %v", Render(j, sel.X), Render(j, call)) - } else if typ, ok := typ.(*types.Slice); ok && typ.Elem() == types.Universe.Lookup("byte").Type() && IsCallToAST(j, call.Args[0], "(*bytes.Buffer).String") { - j.Errorf(call, "should use %v.Bytes() instead of %v", Render(j, sel.X), Render(j, call)) + typ := pass.TypesInfo.TypeOf(call.Fun) + if typ == types.Universe.Lookup("string").Type() && IsCallToAST(pass, call.Args[0], "(*bytes.Buffer).Bytes") { + ReportNodefFG(pass, call, "should use %v.String() instead of %v", Render(pass, sel.X), Render(pass, call)) + } else if typ, ok := typ.(*types.Slice); ok && typ.Elem() == types.Universe.Lookup("byte").Type() && IsCallToAST(pass, call.Args[0], "(*bytes.Buffer).String") { + ReportNodefFG(pass, call, "should use %v.Bytes() instead of %v", Render(pass, sel.X), Render(pass, call)) } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) LintStringsContains(j *lint.Job) { +func LintStringsContains(pass *analysis.Pass) (interface{}, error) { // map of value to token to bool value allowed := map[int64]map[token.Token]bool{ -1: {token.GTR: true, token.NEQ: true, token.EQL: false}, @@ -264,7 +222,7 @@ func (c *Checker) LintStringsContains(j *lint.Job) { return } - value, ok := ExprToInt(j, expr.Y) + value, ok := ExprToInt(pass, expr.Y) if !ok { return } @@ -310,12 +268,13 @@ func (c *Checker) LintStringsContains(j *lint.Job) { if !b { prefix = "!" } - j.Errorf(node, "should use %s%s.%s(%s) instead", prefix, pkgIdent.Name, newFunc, RenderArgs(j, call.Args)) + ReportNodefFG(pass, node, "should use %s%s.%s(%s) instead", prefix, pkgIdent.Name, newFunc, RenderArgs(pass, call.Args)) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) LintBytesCompare(j *lint.Job) { +func LintBytesCompare(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { expr := node.(*ast.BinaryExpr) if expr.Op != token.NEQ && expr.Op != token.EQL { @@ -325,42 +284,44 @@ func (c *Checker) LintBytesCompare(j *lint.Job) { if !ok { return } - if !IsCallToAST(j, call, "bytes.Compare") { + if !IsCallToAST(pass, call, "bytes.Compare") { return } - value, ok := ExprToInt(j, expr.Y) + value, ok := ExprToInt(pass, expr.Y) if !ok || value != 0 { return } - args := RenderArgs(j, call.Args) + args := RenderArgs(pass, call.Args) prefix := "" if expr.Op == token.NEQ { prefix = "!" } - j.Errorf(node, "should use %sbytes.Equal(%s) instead", prefix, args) + ReportNodefFG(pass, node, "should use %sbytes.Equal(%s) instead", prefix, args) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) LintForTrue(j *lint.Job) { +func LintForTrue(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { loop := node.(*ast.ForStmt) if loop.Init != nil || loop.Post != nil { return } - if !IsBoolConst(j, loop.Cond) || !BoolConst(j, loop.Cond) { + if !IsBoolConst(pass, loop.Cond) || !BoolConst(pass, loop.Cond) { return } - j.Errorf(loop, "should use for {} instead of for true {}") + ReportNodefFG(pass, loop, "should use for {} instead of for true {}") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) LintRegexpRaw(j *lint.Job) { +func LintRegexpRaw(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) - if !IsCallToAST(j, call, "regexp.MustCompile") && - !IsCallToAST(j, call, "regexp.Compile") { + if !IsCallToAST(pass, call, "regexp.MustCompile") && + !IsCallToAST(pass, call, "regexp.Compile") { return } sel, ok := call.Fun.(*ast.SelectorExpr) @@ -408,12 +369,13 @@ func (c *Checker) LintRegexpRaw(j *lint.Job) { } } - j.Errorf(call, "should use raw string (`...`) with regexp.%s to avoid having to escape twice", sel.Sel.Name) + ReportNodefFG(pass, call, "should use raw string (`...`) with regexp.%s to avoid having to escape twice", sel.Sel.Name) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) LintIfReturn(j *lint.Job) { +func LintIfReturn(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { block := node.(*ast.BlockStmt) l := len(block.List) @@ -455,7 +417,7 @@ func (c *Checker) LintIfReturn(j *lint.Job) { if len(ret1.Results) != 1 { return } - if !IsBoolConst(j, ret1.Results[0]) { + if !IsBoolConst(pass, ret1.Results[0]) { return } @@ -466,12 +428,20 @@ func (c *Checker) LintIfReturn(j *lint.Job) { if len(ret2.Results) != 1 { return } - if !IsBoolConst(j, ret2.Results[0]) { + if !IsBoolConst(pass, ret2.Results[0]) { return } - j.Errorf(n1, "should use 'return ' instead of 'if { return }; return '") + + if ret1.Results[0].(*ast.Ident).Name == ret2.Results[0].(*ast.Ident).Name { + // we want the function to return true and false, not the + // same value both times. + return + } + + ReportNodefFG(pass, n1, "should use 'return ' instead of 'if { return }; return '") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BlockStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BlockStmt)(nil)}, fn) + return nil, nil } // LintRedundantNilCheckWithLen checks for the following reduntant nil-checks: @@ -482,7 +452,7 @@ func (c *Checker) LintIfReturn(j *lint.Job) { // if x != nil && len(x) > N {} // if x != nil && len(x) >= N {} (where N != 0) // -func (c *Checker) LintRedundantNilCheckWithLen(j *lint.Job) { +func LintRedundantNilCheckWithLen(pass *analysis.Pass) (interface{}, error) { isConstZero := func(expr ast.Expr) (isConst bool, isZero bool) { _, ok := expr.(*ast.BasicLit) if ok { @@ -492,7 +462,7 @@ func (c *Checker) LintRedundantNilCheckWithLen(j *lint.Job) { if !ok { return false, false } - c, ok := j.Pkg.TypesInfo.ObjectOf(id).(*types.Const) + c, ok := pass.TypesInfo.ObjectOf(id).(*types.Const) if !ok { return false, false } @@ -522,7 +492,7 @@ func (c *Checker) LintRedundantNilCheckWithLen(j *lint.Job) { if !ok { return } - if !IsNil(j, x.Y) { + if !IsNil(pass, x.Y) { return } @@ -585,7 +555,7 @@ func (c *Checker) LintRedundantNilCheckWithLen(j *lint.Job) { // finally check that xx type is one of array, slice, map or chan // this is to prevent false positive in case if xx is a pointer to an array var nilType string - switch j.Pkg.TypesInfo.TypeOf(xx).(type) { + switch pass.TypesInfo.TypeOf(xx).(type) { case *types.Slice: nilType = "nil slices" case *types.Map: @@ -595,12 +565,13 @@ func (c *Checker) LintRedundantNilCheckWithLen(j *lint.Job) { default: return } - j.Errorf(expr, "should omit nil check; len() for %s is defined as zero", nilType) + ReportNodefFG(pass, expr, "should omit nil check; len() for %s is defined as zero", nilType) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) LintSlicing(j *lint.Job) { +func LintSlicing(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { n := node.(*ast.SliceExpr) if n.Max != nil { @@ -618,26 +589,27 @@ func (c *Checker) LintSlicing(j *lint.Job) { if !ok || fun.Name != "len" { return } - if _, ok := j.Pkg.TypesInfo.ObjectOf(fun).(*types.Builtin); !ok { + if _, ok := pass.TypesInfo.ObjectOf(fun).(*types.Builtin); !ok { return } arg, ok := call.Args[Arg("len.v")].(*ast.Ident) if !ok || arg.Obj != s.Obj { return } - j.Errorf(n, "should omit second index in slice, s[a:len(s)] is identical to s[a:]") + ReportNodefFG(pass, n, "should omit second index in slice, s[a:len(s)] is identical to s[a:]") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.SliceExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.SliceExpr)(nil)}, fn) + return nil, nil } -func refersTo(j *lint.Job, expr ast.Expr, ident *ast.Ident) bool { +func refersTo(pass *analysis.Pass, expr ast.Expr, ident *ast.Ident) bool { found := false fn := func(node ast.Node) bool { ident2, ok := node.(*ast.Ident) if !ok { return true } - if j.Pkg.TypesInfo.ObjectOf(ident) == j.Pkg.TypesInfo.ObjectOf(ident2) { + if pass.TypesInfo.ObjectOf(ident) == pass.TypesInfo.ObjectOf(ident2) { found = true return false } @@ -647,7 +619,7 @@ func refersTo(j *lint.Job, expr ast.Expr, ident *ast.Ident) bool { return found } -func (c *Checker) LintLoopAppend(j *lint.Job) { +func LintLoopAppend(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { loop := node.(*ast.RangeStmt) if !IsBlank(loop.Key) { @@ -667,7 +639,7 @@ func (c *Checker) LintLoopAppend(j *lint.Job) { if stmt.Tok != token.ASSIGN || len(stmt.Lhs) != 1 || len(stmt.Rhs) != 1 { return } - if refersTo(j, stmt.Lhs[0], val) { + if refersTo(pass, stmt.Lhs[0], val) { return } call, ok := stmt.Rhs[0].(*ast.CallExpr) @@ -681,14 +653,14 @@ func (c *Checker) LintLoopAppend(j *lint.Job) { if !ok { return } - obj := j.Pkg.TypesInfo.ObjectOf(fun) + obj := pass.TypesInfo.ObjectOf(fun) fn, ok := obj.(*types.Builtin) if !ok || fn.Name() != "append" { return } - src := j.Pkg.TypesInfo.TypeOf(loop.X) - dst := j.Pkg.TypesInfo.TypeOf(call.Args[Arg("append.slice")]) + src := pass.TypesInfo.TypeOf(loop.X) + dst := pass.TypesInfo.TypeOf(call.Args[Arg("append.slice")]) // TODO(dominikh) remove nil check once Go issue #15173 has // been fixed if src == nil { @@ -698,7 +670,7 @@ func (c *Checker) LintLoopAppend(j *lint.Job) { return } - if Render(j, stmt.Lhs[0]) != Render(j, call.Args[Arg("append.slice")]) { + if Render(pass, stmt.Lhs[0]) != Render(pass, call.Args[Arg("append.slice")]) { return } @@ -706,51 +678,54 @@ func (c *Checker) LintLoopAppend(j *lint.Job) { if !ok { return } - if j.Pkg.TypesInfo.ObjectOf(val) != j.Pkg.TypesInfo.ObjectOf(el) { + if pass.TypesInfo.ObjectOf(val) != pass.TypesInfo.ObjectOf(el) { return } - j.Errorf(loop, "should replace loop with %s = append(%s, %s...)", - Render(j, stmt.Lhs[0]), Render(j, call.Args[Arg("append.slice")]), Render(j, loop.X)) + ReportNodefFG(pass, loop, "should replace loop with %s = append(%s, %s...)", + Render(pass, stmt.Lhs[0]), Render(pass, call.Args[Arg("append.slice")]), Render(pass, loop.X)) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.RangeStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.RangeStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) LintTimeSince(j *lint.Job) { +func LintTimeSince(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) sel, ok := call.Fun.(*ast.SelectorExpr) if !ok { return } - if !IsCallToAST(j, sel.X, "time.Now") { + if !IsCallToAST(pass, sel.X, "time.Now") { return } if sel.Sel.Name != "Sub" { return } - j.Errorf(call, "should use time.Since instead of time.Now().Sub") + ReportNodefFG(pass, call, "should use time.Since instead of time.Now().Sub") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) LintTimeUntil(j *lint.Job) { - if !IsGoVersion(j, 8) { - return +func LintTimeUntil(pass *analysis.Pass) (interface{}, error) { + if !IsGoVersion(pass, 8) { + return nil, nil } fn := func(node ast.Node) { call := node.(*ast.CallExpr) - if !IsCallToAST(j, call, "(time.Time).Sub") { + if !IsCallToAST(pass, call, "(time.Time).Sub") { return } - if !IsCallToAST(j, call.Args[Arg("(time.Time).Sub.u")], "time.Now") { + if !IsCallToAST(pass, call.Args[Arg("(time.Time).Sub.u")], "time.Now") { return } - j.Errorf(call, "should use time.Until instead of t.Sub(time.Now())") + ReportNodefFG(pass, call, "should use time.Until instead of t.Sub(time.Now())") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) LintUnnecessaryBlank(j *lint.Job) { +func LintUnnecessaryBlank(pass *analysis.Pass) (interface{}, error) { fn1 := func(node ast.Node) { assign := node.(*ast.AssignStmt) if len(assign.Lhs) != 2 || len(assign.Rhs) != 1 { @@ -763,7 +738,7 @@ func (c *Checker) LintUnnecessaryBlank(j *lint.Job) { case *ast.IndexExpr: // The type-checker should make sure that it's a map, but // let's be safe. - if _, ok := j.Pkg.TypesInfo.TypeOf(rhs.X).Underlying().(*types.Map); !ok { + if _, ok := pass.TypesInfo.TypeOf(rhs.X).Underlying().(*types.Map); !ok { return } case *ast.UnaryExpr: @@ -775,7 +750,7 @@ func (c *Checker) LintUnnecessaryBlank(j *lint.Job) { } cp := *assign cp.Lhs = cp.Lhs[0:1] - j.Errorf(assign, "should write %s instead of %s", Render(j, &cp), Render(j, assign)) + ReportNodefFG(pass, assign, "should write %s instead of %s", Render(pass, &cp), Render(pass, assign)) } fn2 := func(node ast.Node) { @@ -795,7 +770,7 @@ func (c *Checker) LintUnnecessaryBlank(j *lint.Job) { if expr.Op != token.ARROW { continue } - j.Errorf(lh, "'_ = <-ch' can be simplified to '<-ch'") + ReportNodefFG(pass, lh, "'_ = <-ch' can be simplified to '<-ch'") } } @@ -804,22 +779,23 @@ func (c *Checker) LintUnnecessaryBlank(j *lint.Job) { // for x, _ if !IsBlank(rs.Key) && IsBlank(rs.Value) { - j.Errorf(rs.Value, "should omit value from range; this loop is equivalent to `for %s %s range ...`", Render(j, rs.Key), rs.Tok) + ReportNodefFG(pass, rs.Value, "should omit value from range; this loop is equivalent to `for %s %s range ...`", Render(pass, rs.Key), rs.Tok) } // for _, _ || for _ if IsBlank(rs.Key) && (IsBlank(rs.Value) || rs.Value == nil) { - j.Errorf(rs.Key, "should omit values from range; this loop is equivalent to `for range ...`") + ReportNodefFG(pass, rs.Key, "should omit values from range; this loop is equivalent to `for range ...`") } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn1) - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn2) - if IsGoVersion(j, 4) { - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.RangeStmt)(nil)}, fn3) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn1) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn2) + if IsGoVersion(pass, 4) { + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.RangeStmt)(nil)}, fn3) } + return nil, nil } -func (c *Checker) LintSimplerStructConversion(j *lint.Job) { +func LintSimplerStructConversion(pass *analysis.Pass) (interface{}, error) { var skip ast.Node fn := func(node ast.Node) { // Do not suggest type conversion between pointers @@ -838,7 +814,7 @@ func (c *Checker) LintSimplerStructConversion(j *lint.Job) { if !ok { return } - typ1, _ := j.Pkg.TypesInfo.TypeOf(lit.Type).(*types.Named) + typ1, _ := pass.TypesInfo.TypeOf(lit.Type).(*types.Named) if typ1 == nil { return } @@ -858,7 +834,7 @@ func (c *Checker) LintSimplerStructConversion(j *lint.Job) { if !ok { return nil, nil, false } - typ := j.Pkg.TypesInfo.TypeOf(sel.X) + typ := pass.TypesInfo.TypeOf(sel.X) return typ, ident, typ != nil } if len(lit.Elts) == 0 { @@ -926,7 +902,7 @@ func (c *Checker) LintSimplerStructConversion(j *lint.Job) { if typ1 == typ2 { return } - if IsGoVersion(j, 8) { + if IsGoVersion(pass, 8) { if !types.IdenticalIgnoreTags(s1, s2) { return } @@ -935,13 +911,14 @@ func (c *Checker) LintSimplerStructConversion(j *lint.Job) { return } } - j.Errorf(node, "should convert %s (type %s) to %s instead of using struct literal", + ReportNodefFG(pass, node, "should convert %s (type %s) to %s instead of using struct literal", ident.Name, typ2.Obj().Name(), typ1.Obj().Name()) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.UnaryExpr)(nil), (*ast.CompositeLit)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.UnaryExpr)(nil), (*ast.CompositeLit)(nil)}, fn) + return nil, nil } -func (c *Checker) LintTrim(j *lint.Job) { +func LintTrim(pass *analysis.Pass) (interface{}, error) { sameNonDynamic := func(node1, node2 ast.Node) bool { if reflect.TypeOf(node1) != reflect.TypeOf(node2) { return false @@ -951,9 +928,9 @@ func (c *Checker) LintTrim(j *lint.Job) { case *ast.Ident: return node1.Obj == node2.(*ast.Ident).Obj case *ast.SelectorExpr: - return Render(j, node1) == Render(j, node2) + return Render(pass, node1) == Render(pass, node2) case *ast.IndexExpr: - return Render(j, node1) == Render(j, node2) + return Render(pass, node1) == Render(pass, node2) } return false } @@ -991,22 +968,22 @@ func (c *Checker) LintTrim(j *lint.Job) { return } switch { - case IsCallToAST(j, condCall, "strings.HasPrefix"): + case IsCallToAST(pass, condCall, "strings.HasPrefix"): pkg = "strings" fun = "HasPrefix" - case IsCallToAST(j, condCall, "strings.HasSuffix"): + case IsCallToAST(pass, condCall, "strings.HasSuffix"): pkg = "strings" fun = "HasSuffix" - case IsCallToAST(j, condCall, "strings.Contains"): + case IsCallToAST(pass, condCall, "strings.Contains"): pkg = "strings" fun = "Contains" - case IsCallToAST(j, condCall, "bytes.HasPrefix"): + case IsCallToAST(pass, condCall, "bytes.HasPrefix"): pkg = "bytes" fun = "HasPrefix" - case IsCallToAST(j, condCall, "bytes.HasSuffix"): + case IsCallToAST(pass, condCall, "bytes.HasSuffix"): pkg = "bytes" fun = "HasSuffix" - case IsCallToAST(j, condCall, "bytes.Contains"): + case IsCallToAST(pass, condCall, "bytes.Contains"): pkg = "bytes" fun = "Contains" default: @@ -1032,13 +1009,13 @@ func (c *Checker) LintTrim(j *lint.Job) { if len(rhs.Args) < 2 || !sameNonDynamic(condCall.Args[0], rhs.Args[0]) || !sameNonDynamic(condCall.Args[1], rhs.Args[1]) { return } - if IsCallToAST(j, condCall, "strings.HasPrefix") && IsCallToAST(j, rhs, "strings.TrimPrefix") || - IsCallToAST(j, condCall, "strings.HasSuffix") && IsCallToAST(j, rhs, "strings.TrimSuffix") || - IsCallToAST(j, condCall, "strings.Contains") && IsCallToAST(j, rhs, "strings.Replace") || - IsCallToAST(j, condCall, "bytes.HasPrefix") && IsCallToAST(j, rhs, "bytes.TrimPrefix") || - IsCallToAST(j, condCall, "bytes.HasSuffix") && IsCallToAST(j, rhs, "bytes.TrimSuffix") || - IsCallToAST(j, condCall, "bytes.Contains") && IsCallToAST(j, rhs, "bytes.Replace") { - j.Errorf(ifstmt, "should replace this if statement with an unconditional %s", CallNameAST(j, rhs)) + if IsCallToAST(pass, condCall, "strings.HasPrefix") && IsCallToAST(pass, rhs, "strings.TrimPrefix") || + IsCallToAST(pass, condCall, "strings.HasSuffix") && IsCallToAST(pass, rhs, "strings.TrimSuffix") || + IsCallToAST(pass, condCall, "strings.Contains") && IsCallToAST(pass, rhs, "strings.Replace") || + IsCallToAST(pass, condCall, "bytes.HasPrefix") && IsCallToAST(pass, rhs, "bytes.TrimPrefix") || + IsCallToAST(pass, condCall, "bytes.HasSuffix") && IsCallToAST(pass, rhs, "bytes.TrimSuffix") || + IsCallToAST(pass, condCall, "bytes.Contains") && IsCallToAST(pass, rhs, "bytes.Replace") { + ReportNodefFG(pass, ifstmt, "should replace this if statement with an unconditional %s", CallNameAST(pass, rhs)) } return case *ast.SliceExpr: @@ -1063,7 +1040,7 @@ func (c *Checker) LintTrim(j *lint.Job) { index = slice.Low case "HasSuffix": if slice.Low != nil { - n, ok := ExprToInt(j, slice.Low) + n, ok := ExprToInt(pass, slice.Low) if !ok || n != 0 { return } @@ -1092,8 +1069,8 @@ func (c *Checker) LintTrim(j *lint.Job) { if !ok { return } - s1, ok1 := ExprToString(j, lit) - s2, ok2 := ExprToString(j, condCall.Args[1]) + s1, ok1 := ExprToString(pass, lit) + s2, ok2 := ExprToString(pass, condCall.Args[1]) if !ok1 || !ok2 || s1 != s2 { return } @@ -1109,8 +1086,8 @@ func (c *Checker) LintTrim(j *lint.Job) { if pkg != "strings" { return } - string, ok1 := ExprToString(j, condCall.Args[1]) - int, ok2 := ExprToInt(j, slice.Low) + string, ok1 := ExprToString(pass, condCall.Args[1]) + int, ok2 := ExprToInt(pass, slice.Low) if !ok1 || !ok2 || int != int64(len(string)) { return } @@ -1136,13 +1113,14 @@ func (c *Checker) LintTrim(j *lint.Job) { case "HasSuffix": replacement = "TrimSuffix" } - j.Errorf(ifstmt, "should replace this if statement with an unconditional %s.%s", pkg, replacement) + ReportNodefFG(pass, ifstmt, "should replace this if statement with an unconditional %s.%s", pkg, replacement) } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) LintLoopSlide(j *lint.Job) { +func LintLoopSlide(pass *analysis.Pass) (interface{}, error) { // TODO(dh): detect bs[i+offset] in addition to bs[offset+i] // TODO(dh): consider merging this function with LintLoopCopy // TODO(dh): detect length that is an expression, not a variable name @@ -1176,7 +1154,7 @@ func (c *Checker) LintLoopSlide(j *lint.Job) { return } postvar, ok := post.X.(*ast.Ident) - if !ok || j.Pkg.TypesInfo.ObjectOf(postvar) != j.Pkg.TypesInfo.ObjectOf(initvar) { + if !ok || pass.TypesInfo.ObjectOf(postvar) != pass.TypesInfo.ObjectOf(initvar) { return } bin, ok := loop.Cond.(*ast.BinaryExpr) @@ -1184,7 +1162,7 @@ func (c *Checker) LintLoopSlide(j *lint.Job) { return } binx, ok := bin.X.(*ast.Ident) - if !ok || j.Pkg.TypesInfo.ObjectOf(binx) != j.Pkg.TypesInfo.ObjectOf(initvar) { + if !ok || pass.TypesInfo.ObjectOf(binx) != pass.TypesInfo.ObjectOf(initvar) { return } biny, ok := bin.Y.(*ast.Ident) @@ -1213,8 +1191,8 @@ func (c *Checker) LintLoopSlide(j *lint.Job) { if !ok { return } - obj1 := j.Pkg.TypesInfo.ObjectOf(bs1) - obj2 := j.Pkg.TypesInfo.ObjectOf(bs2) + obj1 := pass.TypesInfo.ObjectOf(bs1) + obj2 := pass.TypesInfo.ObjectOf(bs2) if obj1 != obj2 { return } @@ -1223,7 +1201,7 @@ func (c *Checker) LintLoopSlide(j *lint.Job) { } index1, ok := lhs.Index.(*ast.Ident) - if !ok || j.Pkg.TypesInfo.ObjectOf(index1) != j.Pkg.TypesInfo.ObjectOf(initvar) { + if !ok || pass.TypesInfo.ObjectOf(index1) != pass.TypesInfo.ObjectOf(initvar) { return } index2, ok := rhs.Index.(*ast.BinaryExpr) @@ -1235,16 +1213,17 @@ func (c *Checker) LintLoopSlide(j *lint.Job) { return } add2, ok := index2.Y.(*ast.Ident) - if !ok || j.Pkg.TypesInfo.ObjectOf(add2) != j.Pkg.TypesInfo.ObjectOf(initvar) { + if !ok || pass.TypesInfo.ObjectOf(add2) != pass.TypesInfo.ObjectOf(initvar) { return } - j.Errorf(loop, "should use copy(%s[:%s], %s[%s:]) instead", Render(j, bs1), Render(j, biny), Render(j, bs1), Render(j, add1)) + ReportNodefFG(pass, loop, "should use copy(%s[:%s], %s[%s:]) instead", Render(pass, bs1), Render(pass, biny), Render(pass, bs1), Render(pass, add1)) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) LintMakeLenCap(j *lint.Job) { +func LintMakeLenCap(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) if fn, ok := call.Fun.(*ast.Ident); !ok || fn.Name != "make" { @@ -1254,25 +1233,26 @@ func (c *Checker) LintMakeLenCap(j *lint.Job) { switch len(call.Args) { case 2: // make(T, len) - if _, ok := j.Pkg.TypesInfo.TypeOf(call.Args[Arg("make.t")]).Underlying().(*types.Slice); ok { + if _, ok := pass.TypesInfo.TypeOf(call.Args[Arg("make.t")]).Underlying().(*types.Slice); ok { break } if IsZero(call.Args[Arg("make.size[0]")]) { - j.Errorf(call.Args[Arg("make.size[0]")], "should use make(%s) instead", Render(j, call.Args[Arg("make.t")])) + ReportNodefFG(pass, call.Args[Arg("make.size[0]")], "should use make(%s) instead", Render(pass, call.Args[Arg("make.t")])) } case 3: // make(T, len, cap) - if Render(j, call.Args[Arg("make.size[0]")]) == Render(j, call.Args[Arg("make.size[1]")]) { - j.Errorf(call.Args[Arg("make.size[0]")], + if Render(pass, call.Args[Arg("make.size[0]")]) == Render(pass, call.Args[Arg("make.size[1]")]) { + ReportNodefFG(pass, call.Args[Arg("make.size[0]")], "should use make(%s, %s) instead", - Render(j, call.Args[Arg("make.t")]), Render(j, call.Args[Arg("make.size[0]")])) + Render(pass, call.Args[Arg("make.t")]), Render(pass, call.Args[Arg("make.size[0]")])) } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) LintAssertNotNil(j *lint.Job) { +func LintAssertNotNil(pass *analysis.Pass) (interface{}, error) { isNilCheck := func(ident *ast.Ident, expr ast.Expr) bool { xbinop, ok := expr.(*ast.BinaryExpr) if !ok || xbinop.Op != token.NEQ { @@ -1282,7 +1262,7 @@ func (c *Checker) LintAssertNotNil(j *lint.Job) { if !ok || xident.Obj != ident.Obj { return false } - if !IsNil(j, xbinop.Y) { + if !IsNil(pass, xbinop.Y) { return false } return true @@ -1320,7 +1300,7 @@ func (c *Checker) LintAssertNotNil(j *lint.Job) { !(isNilCheck(assertIdent, binop.Y) && isOKCheck(assignIdent, binop.X)) { return } - j.Errorf(ifstmt, "when %s is true, %s can't be nil", Render(j, assignIdent), Render(j, assertIdent)) + ReportNodefFG(pass, ifstmt, "when %s is true, %s can't be nil", Render(pass, assignIdent), Render(pass, assertIdent)) } fn2 := func(node ast.Node) { // Check that outer ifstmt is an 'if x != nil {}' @@ -1345,7 +1325,7 @@ func (c *Checker) LintAssertNotNil(j *lint.Job) { if !ok { return } - if !IsNil(j, binop.Y) { + if !IsNil(pass, binop.Y) { return } @@ -1376,13 +1356,14 @@ func (c *Checker) LintAssertNotNil(j *lint.Job) { if !isOKCheck(assignIdent, ifstmt.Cond) { return } - j.Errorf(ifstmt, "when %s is true, %s can't be nil", Render(j, assignIdent), Render(j, assertIdent)) + ReportNodefFG(pass, ifstmt, "when %s is true, %s can't be nil", Render(pass, assignIdent), Render(pass, assertIdent)) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn1) - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn2) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn1) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn2) + return nil, nil } -func (c *Checker) LintDeclareAssign(j *lint.Job) { +func LintDeclareAssign(pass *analysis.Pass) (interface{}, error) { hasMultipleAssignments := func(root ast.Node, ident *ast.Ident) bool { num := 0 ast.Inspect(root, func(node ast.Node) bool { @@ -1440,20 +1421,21 @@ func (c *Checker) LintDeclareAssign(j *lint.Job) { continue } - if refersTo(j, assign.Rhs[0], ident) { + if refersTo(pass, assign.Rhs[0], ident) { continue } if hasMultipleAssignments(block, ident) { continue } - j.Errorf(decl, "should merge variable declaration with assignment on next line") + ReportNodefFG(pass, decl, "should merge variable declaration with assignment on next line") } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BlockStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BlockStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) LintRedundantBreak(j *lint.Job) { +func LintRedundantBreak(pass *analysis.Pass) (interface{}, error) { fn1 := func(node ast.Node) { clause := node.(*ast.CaseClause) if len(clause.Body) < 2 { @@ -1463,7 +1445,7 @@ func (c *Checker) LintRedundantBreak(j *lint.Job) { if !ok || branch.Tok != token.BREAK || branch.Label != nil { return } - j.Errorf(branch, "redundant break statement") + ReportNodefFG(pass, branch, "redundant break statement") } fn2 := func(node ast.Node) { var ret *ast.FieldList @@ -1490,14 +1472,15 @@ func (c *Checker) LintRedundantBreak(j *lint.Job) { } // we don't need to check rst.Results as we already // checked x.Type.Results to be nil. - j.Errorf(rst, "redundant return statement") + ReportNodefFG(pass, rst, "redundant return statement") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CaseClause)(nil)}, fn1) - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)}, fn2) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CaseClause)(nil)}, fn1) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)}, fn2) + return nil, nil } -func isStringer(T types.Type) bool { - ms := types.NewMethodSet(T) +func isStringer(T types.Type, msCache *typeutil.MethodSetCache) bool { + ms := msCache.MethodSet(T) sel := ms.Lookup(nil, "String") if sel == nil { return false @@ -1520,56 +1503,59 @@ func isStringer(T types.Type) bool { return true } -func (c *Checker) LintRedundantSprintf(j *lint.Job) { +func LintRedundantSprintf(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) - if !IsCallToAST(j, call, "fmt.Sprintf") { + if !IsCallToAST(pass, call, "fmt.Sprintf") { return } if len(call.Args) != 2 { return } - if s, ok := ExprToString(j, call.Args[Arg("fmt.Sprintf.format")]); !ok || s != "%s" { + if s, ok := ExprToString(pass, call.Args[Arg("fmt.Sprintf.format")]); !ok || s != "%s" { return } arg := call.Args[Arg("fmt.Sprintf.a[0]")] - typ := j.Pkg.TypesInfo.TypeOf(arg) + typ := pass.TypesInfo.TypeOf(arg) - if isStringer(typ) { - j.Errorf(call, "should use String() instead of fmt.Sprintf") + ssapkg := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).Pkg + if isStringer(typ, &ssapkg.Prog.MethodSets) { + ReportNodef(pass, call, "should use String() instead of fmt.Sprintf") return } if typ.Underlying() == types.Universe.Lookup("string").Type() { if typ == types.Universe.Lookup("string").Type() { - j.Errorf(call, "the argument is already a string, there's no need to use fmt.Sprintf") + ReportNodefFG(pass, call, "the argument is already a string, there's no need to use fmt.Sprintf") } else { - j.Errorf(call, "the argument's underlying type is a string, should use a simple conversion instead of fmt.Sprintf") + ReportNodefFG(pass, call, "the argument's underlying type is a string, should use a simple conversion instead of fmt.Sprintf") } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) LintErrorsNewSprintf(j *lint.Job) { +func LintErrorsNewSprintf(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { - if !IsCallToAST(j, node, "errors.New") { + if !IsCallToAST(pass, node, "errors.New") { return } call := node.(*ast.CallExpr) - if !IsCallToAST(j, call.Args[Arg("errors.New.text")], "fmt.Sprintf") { + if !IsCallToAST(pass, call.Args[Arg("errors.New.text")], "fmt.Sprintf") { return } - j.Errorf(node, "should use fmt.Errorf(...) instead of errors.New(fmt.Sprintf(...))") + ReportNodefFG(pass, node, "should use fmt.Errorf(...) instead of errors.New(fmt.Sprintf(...))") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) LintRangeStringRunes(j *lint.Job) { - sharedcheck.CheckRangeStringRunes(j) +func LintRangeStringRunes(pass *analysis.Pass) (interface{}, error) { + return sharedcheck.CheckRangeStringRunes(pass) } -func (c *Checker) LintNilCheckAroundRange(j *lint.Job) { +func LintNilCheckAroundRange(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { ifstmt := node.(*ast.IfStmt) cond, ok := ifstmt.Cond.(*ast.BinaryExpr) @@ -1577,7 +1563,7 @@ func (c *Checker) LintNilCheckAroundRange(j *lint.Job) { return } - if cond.Op != token.NEQ || !IsNil(j, cond.Y) || len(ifstmt.Body.List) != 1 { + if cond.Op != token.NEQ || !IsNil(pass, cond.Y) || len(ifstmt.Body.List) != 1 { return } @@ -1596,15 +1582,16 @@ func (c *Checker) LintNilCheckAroundRange(j *lint.Job) { if ifXIdent.Obj != rangeXIdent.Obj { return } - switch j.Pkg.TypesInfo.TypeOf(rangeXIdent).(type) { + switch pass.TypesInfo.TypeOf(rangeXIdent).(type) { case *types.Slice, *types.Map: - j.Errorf(node, "unnecessary nil check around range") + ReportNodefFG(pass, node, "unnecessary nil check around range") } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn) + return nil, nil } -func isPermissibleSort(j *lint.Job, node ast.Node) bool { +func isPermissibleSort(pass *analysis.Pass, node ast.Node) bool { call := node.(*ast.CallExpr) typeconv, ok := call.Args[0].(*ast.CallExpr) if !ok { @@ -1615,7 +1602,7 @@ func isPermissibleSort(j *lint.Job, node ast.Node) bool { if !ok { return true } - name := SelectorName(j, sel) + name := SelectorName(pass, sel) switch name { case "sort.IntSlice", "sort.Float64Slice", "sort.StringSlice": default: @@ -1625,7 +1612,12 @@ func isPermissibleSort(j *lint.Job, node ast.Node) bool { return false } -func (c *Checker) LintSortHelpers(j *lint.Job) { +func LintSortHelpers(pass *analysis.Pass) (interface{}, error) { + type Error struct { + node ast.Node + msg string + } + var allErrors []Error fn := func(node ast.Node) { var body *ast.BlockStmt switch node := node.(type) { @@ -1640,27 +1632,23 @@ func (c *Checker) LintSortHelpers(j *lint.Job) { return } - type Error struct { - node lint.Positioner - msg string - } var errors []Error permissible := false fnSorts := func(node ast.Node) bool { if permissible { return false } - if !IsCallToAST(j, node, "sort.Sort") { + if !IsCallToAST(pass, node, "sort.Sort") { return true } - if isPermissibleSort(j, node) { + if isPermissibleSort(pass, node) { permissible = true return false } call := node.(*ast.CallExpr) typeconv := call.Args[Arg("sort.Sort.data")].(*ast.CallExpr) sel := typeconv.Fun.(*ast.SelectorExpr) - name := SelectorName(j, sel) + name := SelectorName(pass, sel) switch name { case "sort.IntSlice": @@ -1677,15 +1665,24 @@ func (c *Checker) LintSortHelpers(j *lint.Job) { if permissible { return } - for _, err := range errors { - j.Errorf(err.node, "%s", err.msg) - } - return + allErrors = append(allErrors, errors...) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.FuncLit)(nil), (*ast.FuncDecl)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.FuncLit)(nil), (*ast.FuncDecl)(nil)}, fn) + sort.Slice(allErrors, func(i, j int) bool { + return allErrors[i].node.Pos() < allErrors[j].node.Pos() + }) + var prev token.Pos + for _, err := range allErrors { + if err.node.Pos() == prev { + continue + } + prev = err.node.Pos() + ReportNodefFG(pass, err.node, "%s", err.msg) + } + return nil, nil } -func (c *Checker) LintGuardedDelete(j *lint.Job) { +func LintGuardedDelete(pass *analysis.Pass) (interface{}, error) { isCommaOkMapIndex := func(stmt ast.Stmt) (b *ast.Ident, m ast.Expr, key ast.Expr, ok bool) { // Has to be of the form `_, = [] @@ -1707,7 +1704,7 @@ func (c *Checker) LintGuardedDelete(j *lint.Job) { if !ok { return nil, nil, nil, false } - if _, ok := j.Pkg.TypesInfo.TypeOf(index.X).(*types.Map); !ok { + if _, ok := pass.TypesInfo.TypeOf(index.X).(*types.Map); !ok { return nil, nil, nil, false } key = index.Index @@ -1729,25 +1726,26 @@ func (c *Checker) LintGuardedDelete(j *lint.Job) { if !ok { return } - if !IsCallToAST(j, call, "delete") { + if !IsCallToAST(pass, call, "delete") { return } b, m, key, ok := isCommaOkMapIndex(stmt.Init) if !ok { return } - if cond, ok := stmt.Cond.(*ast.Ident); !ok || j.Pkg.TypesInfo.ObjectOf(cond) != j.Pkg.TypesInfo.ObjectOf(b) { + if cond, ok := stmt.Cond.(*ast.Ident); !ok || pass.TypesInfo.ObjectOf(cond) != pass.TypesInfo.ObjectOf(b) { return } - if Render(j, call.Args[0]) != Render(j, m) || Render(j, call.Args[1]) != Render(j, key) { + if Render(pass, call.Args[0]) != Render(pass, m) || Render(pass, call.Args[1]) != Render(pass, key) { return } - j.Errorf(stmt, "unnecessary guard around call to delete") + ReportNodefFG(pass, stmt, "unnecessary guard around call to delete") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) LintSimplifyTypeSwitch(j *lint.Job) { +func LintSimplifyTypeSwitch(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { stmt := node.(*ast.TypeSwitchStmt) if stmt.Init != nil { @@ -1764,7 +1762,7 @@ func (c *Checker) LintSimplifyTypeSwitch(j *lint.Job) { if !ok { return } - x := j.Pkg.TypesInfo.ObjectOf(ident) + x := pass.TypesInfo.ObjectOf(ident) var allOffenders []ast.Node for _, clause := range stmt.Body.List { clause := clause.(*ast.CaseClause) @@ -1783,12 +1781,12 @@ func (c *Checker) LintSimplifyTypeSwitch(j *lint.Job) { hasUnrelatedAssertion = true return false } - if j.Pkg.TypesInfo.ObjectOf(ident) != x { + if pass.TypesInfo.ObjectOf(ident) != x { hasUnrelatedAssertion = true return false } - if !types.Identical(j.Pkg.TypesInfo.TypeOf(clause.List[0]), j.Pkg.TypesInfo.TypeOf(assert2.Type)) { + if !types.Identical(pass.TypesInfo.TypeOf(clause.List[0]), pass.TypesInfo.TypeOf(assert2.Type)) { hasUnrelatedAssertion = true return false } @@ -1807,11 +1805,12 @@ func (c *Checker) LintSimplifyTypeSwitch(j *lint.Job) { if len(allOffenders) != 0 { at := "" for _, offender := range allOffenders { - pos := lint.DisplayPosition(j.Pkg.Fset, offender.Pos()) + pos := lint.DisplayPosition(pass.Fset, offender.Pos()) at += "\n\t" + pos.String() } - j.Errorf(expr, "assigning the result of this type assertion to a variable (switch %s := %s.(type)) could eliminate the following type assertions:%s", Render(j, ident), Render(j, ident), at) + ReportNodefFG(pass, expr, "assigning the result of this type assertion to a variable (switch %s := %s.(type)) could eliminate the following type assertions:%s", Render(pass, ident), Render(pass, ident), at) } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.TypeSwitchStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.TypeSwitchStmt)(nil)}, fn) + return nil, nil } diff --git a/vendor/honnef.co/go/tools/ssa/builder.go b/vendor/honnef.co/go/tools/ssa/builder.go index 032819a2a1..317ac06116 100644 --- a/vendor/honnef.co/go/tools/ssa/builder.go +++ b/vendor/honnef.co/go/tools/ssa/builder.go @@ -32,7 +32,7 @@ package ssa import ( "fmt" "go/ast" - exact "go/constant" + "go/constant" "go/token" "go/types" "os" @@ -58,12 +58,12 @@ var ( tString = types.Typ[types.String] tUntypedNil = types.Typ[types.UntypedNil] tRangeIter = &opaqueType{nil, "iter"} // the type of all "range" iterators - tEface = types.NewInterface(nil, nil).Complete() + tEface = types.NewInterfaceType(nil, nil).Complete() // SSA Value constants. vZero = intConst(0) vOne = intConst(1) - vTrue = NewConst(exact.MakeBool(true), tBool) + vTrue = NewConst(constant.MakeBool(true), tBool) ) // builder holds state associated with the package currently being built. @@ -131,11 +131,11 @@ func (b *builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value { switch e.Op { case token.LAND: b.cond(fn, e.X, rhs, done) - short = NewConst(exact.MakeBool(false), t) + short = NewConst(constant.MakeBool(false), t) case token.LOR: b.cond(fn, e.X, done, rhs) - short = NewConst(exact.MakeBool(true), t) + short = NewConst(constant.MakeBool(true), t) } // Is rhs unreachable? @@ -969,10 +969,10 @@ func (b *builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) { c.Args = b.emitCallArgs(fn, sig, e, c.Args) } -// assignOp emits to fn code to perform loc += incr or loc -= incr. -func (b *builder) assignOp(fn *Function, loc lvalue, incr Value, op token.Token, pos token.Pos) { +// assignOp emits to fn code to perform loc = val. +func (b *builder) assignOp(fn *Function, loc lvalue, val Value, op token.Token, pos token.Pos) { oldv := loc.load(fn) - loc.store(fn, emitArith(fn, op, oldv, emitConv(fn, incr, oldv.Type()), loc.typ(), pos)) + loc.store(fn, emitArith(fn, op, oldv, emitConv(fn, val, oldv.Type()), loc.typ(), pos)) } // localValueSpec emits to fn code to define all of the vars in the @@ -1998,7 +1998,7 @@ start: op = token.SUB } loc := b.addr(fn, s.X, false) - b.assignOp(fn, loc, NewConst(exact.MakeInt64(1), loc.typ()), op, s.Pos()) + b.assignOp(fn, loc, NewConst(constant.MakeInt64(1), loc.typ()), op, s.Pos()) case *ast.AssignStmt: switch s.Tok { diff --git a/vendor/honnef.co/go/tools/ssa/const.go b/vendor/honnef.co/go/tools/ssa/const.go index ca99adc3f1..f95d9e1140 100644 --- a/vendor/honnef.co/go/tools/ssa/const.go +++ b/vendor/honnef.co/go/tools/ssa/const.go @@ -8,7 +8,7 @@ package ssa import ( "fmt" - exact "go/constant" + "go/constant" "go/token" "go/types" "strconv" @@ -17,14 +17,14 @@ import ( // NewConst returns a new constant of the specified value and type. // val must be valid according to the specification of Const.Value. // -func NewConst(val exact.Value, typ types.Type) *Const { +func NewConst(val constant.Value, typ types.Type) *Const { return &Const{typ, val} } // intConst returns an 'int' constant that evaluates to i. // (i is an int64 in case the host is narrower than the target.) func intConst(i int64) *Const { - return NewConst(exact.MakeInt64(i), tInt) + return NewConst(constant.MakeInt64(i), tInt) } // nilConst returns a nil constant of the specified type, which may @@ -36,7 +36,7 @@ func nilConst(typ types.Type) *Const { // stringConst returns a 'string' constant that evaluates to s. func stringConst(s string) *Const { - return NewConst(exact.MakeString(s), tString) + return NewConst(constant.MakeString(s), tString) } // zeroConst returns a new "zero" constant of the specified type, @@ -48,11 +48,11 @@ func zeroConst(t types.Type) *Const { case *types.Basic: switch { case t.Info()&types.IsBoolean != 0: - return NewConst(exact.MakeBool(false), t) + return NewConst(constant.MakeBool(false), t) case t.Info()&types.IsNumeric != 0: - return NewConst(exact.MakeInt64(0), t) + return NewConst(constant.MakeInt64(0), t) case t.Info()&types.IsString != 0: - return NewConst(exact.MakeString(""), t) + return NewConst(constant.MakeString(""), t) case t.Kind() == types.UnsafePointer: fallthrough case t.Kind() == types.UntypedNil: @@ -74,8 +74,8 @@ func (c *Const) RelString(from *types.Package) string { var s string if c.Value == nil { s = "nil" - } else if c.Value.Kind() == exact.String { - s = exact.StringVal(c.Value) + } else if c.Value.Kind() == constant.String { + s = constant.StringVal(c.Value) const max = 20 // TODO(adonovan): don't cut a rune in half. if len(s) > max { @@ -121,14 +121,14 @@ func (c *Const) IsNil() bool { // a signed 64-bit integer. // func (c *Const) Int64() int64 { - switch x := exact.ToInt(c.Value); x.Kind() { - case exact.Int: - if i, ok := exact.Int64Val(x); ok { + switch x := constant.ToInt(c.Value); x.Kind() { + case constant.Int: + if i, ok := constant.Int64Val(x); ok { return i } return 0 - case exact.Float: - f, _ := exact.Float64Val(x) + case constant.Float: + f, _ := constant.Float64Val(x) return int64(f) } panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) @@ -138,14 +138,14 @@ func (c *Const) Int64() int64 { // an unsigned 64-bit integer. // func (c *Const) Uint64() uint64 { - switch x := exact.ToInt(c.Value); x.Kind() { - case exact.Int: - if u, ok := exact.Uint64Val(x); ok { + switch x := constant.ToInt(c.Value); x.Kind() { + case constant.Int: + if u, ok := constant.Uint64Val(x); ok { return u } return 0 - case exact.Float: - f, _ := exact.Float64Val(x) + case constant.Float: + f, _ := constant.Float64Val(x) return uint64(f) } panic(fmt.Sprintf("unexpected constant value: %T", c.Value)) @@ -155,7 +155,7 @@ func (c *Const) Uint64() uint64 { // a float64. // func (c *Const) Float64() float64 { - f, _ := exact.Float64Val(c.Value) + f, _ := constant.Float64Val(c.Value) return f } @@ -163,7 +163,7 @@ func (c *Const) Float64() float64 { // fit a complex128. // func (c *Const) Complex128() complex128 { - re, _ := exact.Float64Val(exact.Real(c.Value)) - im, _ := exact.Float64Val(exact.Imag(c.Value)) + re, _ := constant.Float64Val(constant.Real(c.Value)) + im, _ := constant.Float64Val(constant.Imag(c.Value)) return complex(re, im) } diff --git a/vendor/honnef.co/go/tools/ssa/create.go b/vendor/honnef.co/go/tools/ssa/create.go index 69ac12b1bf..85163a0c5a 100644 --- a/vendor/honnef.co/go/tools/ssa/create.go +++ b/vendor/honnef.co/go/tools/ssa/create.go @@ -251,12 +251,19 @@ func (prog *Program) AllPackages() []*Package { return pkgs } -// ImportedPackage returns the importable SSA Package whose import -// path is path, or nil if no such SSA package has been created. +// ImportedPackage returns the importable Package whose PkgPath +// is path, or nil if no such Package has been created. // -// Not all packages are importable. For example, no import -// declaration can resolve to the x_test package created by 'go test' -// or the ad-hoc main package created 'go build foo.go'. +// A parameter to CreatePackage determines whether a package should be +// considered importable. For example, no import declaration can resolve +// to the ad-hoc main package created by 'go build foo.go'. +// +// TODO(adonovan): rethink this function and the "importable" concept; +// most packages are importable. This function assumes that all +// types.Package.Path values are unique within the ssa.Program, which is +// false---yet this function remains very convenient. +// Clients should use (*Program).Package instead where possible. +// SSA doesn't really need a string-keyed map of packages. // func (prog *Program) ImportedPackage(path string) *Package { return prog.imported[path] diff --git a/vendor/honnef.co/go/tools/ssa/doc.go b/vendor/honnef.co/go/tools/ssa/doc.go index 57474dd202..0f71fda001 100644 --- a/vendor/honnef.co/go/tools/ssa/doc.go +++ b/vendor/honnef.co/go/tools/ssa/doc.go @@ -23,11 +23,13 @@ // such as multi-way branch can be reconstructed as needed; see // ssautil.Switches() for an example. // -// To construct an SSA-form program, call ssautil.CreateProgram on a -// loader.Program, a set of type-checked packages created from -// parsed Go source files. The resulting ssa.Program contains all the -// packages and their members, but SSA code is not created for -// function bodies until a subsequent call to (*Package).Build. +// The simplest way to create the SSA representation of a package is +// to load typed syntax trees using golang.org/x/tools/go/packages, then +// invoke the ssautil.Packages helper function. See ExampleLoadPackages +// and ExampleWholeProgram for examples. +// The resulting ssa.Program contains all the packages and their +// members, but SSA code is not created for function bodies until a +// subsequent call to (*Package).Build or (*Program).Build. // // The builder initially builds a naive SSA form in which all local // variables are addresses of stack locations with explicit loads and diff --git a/vendor/honnef.co/go/tools/ssa/dom.go b/vendor/honnef.co/go/tools/ssa/dom.go index 12ef4308f3..a036be87c4 100644 --- a/vendor/honnef.co/go/tools/ssa/dom.go +++ b/vendor/honnef.co/go/tools/ssa/dom.go @@ -53,7 +53,7 @@ func (a byDomPreorder) Less(i, j int) bool { return a[i].dom.pre < a[j].dom.pre // func (f *Function) DomPreorder() []*BasicBlock { n := len(f.Blocks) - order := make(byDomPreorder, n, n) + order := make(byDomPreorder, n) copy(order, f.Blocks) sort.Sort(order) return order @@ -123,7 +123,7 @@ func buildDomTree(f *Function) { n := len(f.Blocks) // Allocate space for 5 contiguous [n]*BasicBlock arrays: // sdom, parent, ancestor, preorder, buckets. - space := make([]*BasicBlock, 5*n, 5*n) + space := make([]*BasicBlock, 5*n) lt := ltState{ sdom: space[0:n], parent: space[n : 2*n], @@ -310,6 +310,7 @@ func sanityCheckDomTree(f *Function) { // Printing functions ---------------------------------------- // printDomTree prints the dominator tree as text, using indentation. +//lint:ignore U1000 used during debugging func printDomTreeText(buf *bytes.Buffer, v *BasicBlock, indent int) { fmt.Fprintf(buf, "%*s%s\n", 4*indent, "", v) for _, child := range v.dom.children { @@ -319,6 +320,7 @@ func printDomTreeText(buf *bytes.Buffer, v *BasicBlock, indent int) { // printDomTreeDot prints the dominator tree of f in AT&T GraphViz // (.dot) format. +//lint:ignore U1000 used during debugging func printDomTreeDot(buf *bytes.Buffer, f *Function) { fmt.Fprintln(buf, "//", f) fmt.Fprintln(buf, "digraph domtree {") diff --git a/vendor/honnef.co/go/tools/ssa/emit.go b/vendor/honnef.co/go/tools/ssa/emit.go index 1036988adc..6bf9ec32da 100644 --- a/vendor/honnef.co/go/tools/ssa/emit.go +++ b/vendor/honnef.co/go/tools/ssa/emit.go @@ -127,6 +127,7 @@ func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value { x = emitConv(f, x, y.Type()) } else if _, ok := y.(*Const); ok { y = emitConv(f, y, x.Type()) + //lint:ignore SA9003 no-op } else { // other cases, e.g. channels. No-op. } diff --git a/vendor/honnef.co/go/tools/ssa/func.go b/vendor/honnef.co/go/tools/ssa/func.go index 53635ba011..222eea6418 100644 --- a/vendor/honnef.co/go/tools/ssa/func.go +++ b/vendor/honnef.co/go/tools/ssa/func.go @@ -328,6 +328,70 @@ func (f *Function) finishBody() { } f.Locals = f.Locals[:j] + // comma-ok receiving from a time.Tick channel will never return + // ok == false, so any branching on the value of ok can be + // replaced with an unconditional jump. This will primarily match + // `for range time.Tick(x)` loops, but it can also match + // user-written code. + for _, block := range f.Blocks { + if len(block.Instrs) < 3 { + continue + } + if len(block.Succs) != 2 { + continue + } + var instrs []*Instruction + for i, ins := range block.Instrs { + if _, ok := ins.(*DebugRef); ok { + continue + } + instrs = append(instrs, &block.Instrs[i]) + } + + for i, ins := range instrs { + unop, ok := (*ins).(*UnOp) + if !ok || unop.Op != token.ARROW { + continue + } + call, ok := unop.X.(*Call) + if !ok { + continue + } + if call.Common().IsInvoke() { + continue + } + + // OPT(dh): surely there is a more efficient way of doing + // this, than using FullName. We should already have + // resolved time.Tick somewhere? + v, ok := call.Common().Value.(*Function) + if !ok { + continue + } + t, ok := v.Object().(*types.Func) + if !ok { + continue + } + if t.FullName() != "time.Tick" { + continue + } + ex, ok := (*instrs[i+1]).(*Extract) + if !ok || ex.Tuple != unop || ex.Index != 1 { + continue + } + + ifstmt, ok := (*instrs[i+2]).(*If) + if !ok || ifstmt.Cond != ex { + continue + } + + *instrs[i+2] = NewJump(block) + succ := block.Succs[1] + block.Succs = block.Succs[0:1] + succ.RemovePred(block) + } + } + optimizeBlocks(f) buildReferrers(f) diff --git a/vendor/honnef.co/go/tools/ssa/lift.go b/vendor/honnef.co/go/tools/ssa/lift.go index 048e9b0326..531358fa3b 100644 --- a/vendor/honnef.co/go/tools/ssa/lift.go +++ b/vendor/honnef.co/go/tools/ssa/lift.go @@ -341,10 +341,10 @@ func phiHasDirectReferrer(phi *Phi) bool { return false } -type blockSet struct{ big.Int } // (inherit methods from Int) +type BlockSet struct{ big.Int } // (inherit methods from Int) // add adds b to the set and returns true if the set changed. -func (s *blockSet) add(b *BasicBlock) bool { +func (s *BlockSet) Add(b *BasicBlock) bool { i := b.Index if s.Bit(i) != 0 { return false @@ -353,9 +353,13 @@ func (s *blockSet) add(b *BasicBlock) bool { return true } +func (s *BlockSet) Has(b *BasicBlock) bool { + return s.Bit(b.Index) == 1 +} + // take removes an arbitrary element from a set s and // returns its index, or returns -1 if empty. -func (s *blockSet) take() int { +func (s *BlockSet) Take() int { l := s.BitLen() for i := 0; i < l; i++ { if s.Bit(i) == 1 { @@ -403,7 +407,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) bool // Compute defblocks, the set of blocks containing a // definition of the alloc cell. - var defblocks blockSet + var defblocks BlockSet for _, instr := range *alloc.Referrers() { // Bail out if we discover the alloc is not liftable; // the only operations permitted to use the alloc are @@ -416,7 +420,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) bool if instr.Addr != alloc { panic("Alloc.Referrers is inconsistent") } - defblocks.add(instr.Block()) + defblocks.Add(instr.Block()) case *UnOp: if instr.Op != token.MUL { return false // not a load @@ -431,7 +435,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) bool } } // The Alloc itself counts as a (zero) definition of the cell. - defblocks.add(alloc.Block()) + defblocks.Add(alloc.Block()) if debugLifting { fmt.Fprintln(os.Stderr, "\tlifting ", alloc, alloc.Name()) @@ -448,18 +452,18 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) bool // // TODO(adonovan): opt: recycle slice storage for W, // hasAlready, defBlocks across liftAlloc calls. - var hasAlready blockSet + var hasAlready BlockSet // Initialize W and work to defblocks. - var work blockSet = defblocks // blocks seen - var W blockSet // blocks to do + var work BlockSet = defblocks // blocks seen + var W BlockSet // blocks to do W.Set(&defblocks.Int) // Traverse iterated dominance frontier, inserting φ-nodes. - for i := W.take(); i != -1; i = W.take() { + for i := W.Take(); i != -1; i = W.Take() { u := fn.Blocks[i] for _, v := range df[u.Index] { - if hasAlready.add(v) { + if hasAlready.Add(v) { // Create φ-node. // It will be prepended to v.Instrs later, if needed. phi := &Phi{ @@ -478,8 +482,8 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) bool } newPhis[v] = append(newPhis[v], newPhi{phi, alloc}) - if work.add(v) { - W.add(v) + if work.Add(v) { + W.Add(v) } } } diff --git a/vendor/honnef.co/go/tools/ssa/methods.go b/vendor/honnef.co/go/tools/ssa/methods.go index 080dca968e..9cf383916b 100644 --- a/vendor/honnef.co/go/tools/ssa/methods.go +++ b/vendor/honnef.co/go/tools/ssa/methods.go @@ -23,14 +23,14 @@ import ( // func (prog *Program) MethodValue(sel *types.Selection) *Function { if sel.Kind() != types.MethodVal { - panic(fmt.Sprintf("Method(%s) kind != MethodVal", sel)) + panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel)) } T := sel.Recv() if isInterface(T) { return nil // abstract method } if prog.mode&LogSource != 0 { - defer logStack("Method %s %v", T, sel)() + defer logStack("MethodValue %s %v", T, sel)() } prog.methodsMu.Lock() diff --git a/vendor/honnef.co/go/tools/ssa/sanity.go b/vendor/honnef.co/go/tools/ssa/sanity.go index c56b2682c0..1d29b66b02 100644 --- a/vendor/honnef.co/go/tools/ssa/sanity.go +++ b/vendor/honnef.co/go/tools/ssa/sanity.go @@ -410,8 +410,8 @@ func (s *sanity) checkFunction(fn *Function) bool { s.errorf("nil Prog") } - fn.String() // must not crash - fn.RelString(fn.pkg()) // must not crash + _ = fn.String() // must not crash + _ = fn.RelString(fn.pkg()) // must not crash // All functions have a package, except delegates (which are // shared across packages, or duplicated as weak symbols in a @@ -448,6 +448,18 @@ func (s *sanity) checkFunction(fn *Function) bool { if p.Parent() != fn { s.errorf("Param %s at index %d has wrong parent", p.Name(), i) } + // Check common suffix of Signature and Params match type. + if sig := fn.Signature; sig != nil { + j := i - len(fn.Params) + sig.Params().Len() // index within sig.Params + if j < 0 { + continue + } + if !types.Identical(p.Type(), sig.Params().At(j).Type()) { + s.errorf("Param %s at index %d has wrong type (%s, versus %s in Signature)", p.Name(), i, p.Type(), sig.Params().At(j).Type()) + + } + } + s.checkReferrerList(p) } for i, fv := range fn.FreeVars { @@ -490,7 +502,7 @@ func sanityCheckPackage(pkg *Package) { if pkg.Pkg == nil { panic(fmt.Sprintf("Package %s has no Object", pkg)) } - pkg.String() // must not crash + _ = pkg.String() // must not crash for name, mem := range pkg.Members { if name != mem.Name() { diff --git a/vendor/honnef.co/go/tools/ssa/source.go b/vendor/honnef.co/go/tools/ssa/source.go index 6d2223edaa..8d9cca1703 100644 --- a/vendor/honnef.co/go/tools/ssa/source.go +++ b/vendor/honnef.co/go/tools/ssa/source.go @@ -150,7 +150,7 @@ func findNamedFunc(pkg *Package, pos token.Pos) *Function { // (modulo "untyped" bools resulting from comparisons). // // (Tip: to find the ssa.Value given a source position, use -// importer.PathEnclosingInterval to locate the ast.Node, then +// astutil.PathEnclosingInterval to locate the ast.Node, then // EnclosingFunction to locate the Function, then ValueForExpr to find // the ssa.Value.) // diff --git a/vendor/honnef.co/go/tools/ssa/ssa.go b/vendor/honnef.co/go/tools/ssa/ssa.go index 8825e7b599..aeddd65e58 100644 --- a/vendor/honnef.co/go/tools/ssa/ssa.go +++ b/vendor/honnef.co/go/tools/ssa/ssa.go @@ -10,7 +10,7 @@ package ssa import ( "fmt" "go/ast" - exact "go/constant" + "go/constant" "go/token" "go/types" "sync" @@ -405,7 +405,7 @@ type Parameter struct { // of the same type and value. // // Value holds the exact value of the constant, independent of its -// Type(), using the same representation as package go/exact uses for +// Type(), using the same representation as package go/constant uses for // constants, or nil for a typed nil value. // // Pos() returns token.NoPos. @@ -417,7 +417,7 @@ type Parameter struct { // type Const struct { typ types.Type - Value exact.Value + Value constant.Value } // A Global is a named Value holding the address of a package-level @@ -572,8 +572,8 @@ type BinOp struct { register // One of: // ADD SUB MUL QUO REM + - * / % - // AND OR XOR SHL SHR AND_NOT & | ^ << >> &~ - // EQL LSS GTR NEQ LEQ GEQ == != < <= < >= + // AND OR XOR SHL SHR AND_NOT & | ^ << >> &^ + // EQL NEQ LSS LEQ GTR GEQ == != < <= < >= Op token.Token X, Y Value } @@ -680,10 +680,10 @@ type ChangeInterface struct { // value of a concrete type. // // Use Program.MethodSets.MethodSet(X.Type()) to find the method-set -// of X, and Program.Method(m) to find the implementation of a method. +// of X, and Program.MethodValue(m) to find the implementation of a method. // // To construct the zero value of an interface type T, use: -// NewConst(exact.MakeNil(), T, pos) +// NewConst(constant.MakeNil(), T, pos) // // Pos() returns the ast.CallExpr.Lparen, if the instruction arose // from an explicit conversion in the source. @@ -813,7 +813,7 @@ type Slice struct { type FieldAddr struct { register X Value // *struct - Field int // index into X.Type().Deref().(*types.Struct).Fields + Field int // field is X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct).Field(Field) } // The Field instruction yields the Field of struct X. diff --git a/vendor/honnef.co/go/tools/ssa/ssautil/load.go b/vendor/honnef.co/go/tools/ssa/ssautil/load.go deleted file mode 100644 index 3b8694a13d..0000000000 --- a/vendor/honnef.co/go/tools/ssa/ssautil/load.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssautil - -// This file defines utility functions for constructing programs in SSA form. - -import ( - "go/ast" - "go/token" - "go/types" - - "golang.org/x/tools/go/loader" - "golang.org/x/tools/go/packages" - "honnef.co/go/tools/ssa" -) - -// Packages creates an SSA program for a set of packages loaded from -// source syntax using the golang.org/x/tools/go/packages.Load function. -// It creates and returns an SSA package for each well-typed package in -// the initial list. The resulting list of packages has the same length -// as initial, and contains a nil if SSA could not be constructed for -// the corresponding initial package. -// -// Code for bodies of functions is not built until Build is called -// on the resulting Program. -// -// The mode parameter controls diagnostics and checking during SSA construction. -// -func Packages(initial []*packages.Package, mode ssa.BuilderMode) (*ssa.Program, []*ssa.Package) { - var fset *token.FileSet - if len(initial) > 0 { - fset = initial[0].Fset - } - - prog := ssa.NewProgram(fset, mode) - seen := make(map[*packages.Package]*ssa.Package) - var create func(p *packages.Package) *ssa.Package - create = func(p *packages.Package) *ssa.Package { - ssapkg, ok := seen[p] - if !ok { - if p.Types == nil || p.IllTyped { - // not well typed - seen[p] = nil - return nil - } - - ssapkg = prog.CreatePackage(p.Types, p.Syntax, p.TypesInfo, true) - seen[p] = ssapkg - - for _, imp := range p.Imports { - create(imp) - } - } - return ssapkg - } - - var ssapkgs []*ssa.Package - for _, p := range initial { - ssapkgs = append(ssapkgs, create(p)) - } - return prog, ssapkgs -} - -// CreateProgram returns a new program in SSA form, given a program -// loaded from source. An SSA package is created for each transitively -// error-free package of lprog. -// -// Code for bodies of functions is not built until Build is called -// on the result. -// -// mode controls diagnostics and checking during SSA construction. -// -func CreateProgram(lprog *loader.Program, mode ssa.BuilderMode) *ssa.Program { - prog := ssa.NewProgram(lprog.Fset, mode) - - for _, info := range lprog.AllPackages { - if info.TransitivelyErrorFree { - prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable) - } - } - - return prog -} - -// BuildPackage builds an SSA program with IR for a single package. -// -// It populates pkg by type-checking the specified file ASTs. All -// dependencies are loaded using the importer specified by tc, which -// typically loads compiler export data; SSA code cannot be built for -// those packages. BuildPackage then constructs an ssa.Program with all -// dependency packages created, and builds and returns the SSA package -// corresponding to pkg. -// -// The caller must have set pkg.Path() to the import path. -// -// The operation fails if there were any type-checking or import errors. -// -// See ../ssa/example_test.go for an example. -// -func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) { - if fset == nil { - panic("no token.FileSet") - } - if pkg.Path() == "" { - panic("package has no import path") - } - - info := &types.Info{ - Types: make(map[ast.Expr]types.TypeAndValue), - Defs: make(map[*ast.Ident]types.Object), - Uses: make(map[*ast.Ident]types.Object), - Implicits: make(map[ast.Node]types.Object), - Scopes: make(map[ast.Node]*types.Scope), - Selections: make(map[*ast.SelectorExpr]*types.Selection), - } - if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil { - return nil, nil, err - } - - prog := ssa.NewProgram(fset, mode) - - // Create SSA packages for all imports. - // Order is not significant. - created := make(map[*types.Package]bool) - var createAll func(pkgs []*types.Package) - createAll = func(pkgs []*types.Package) { - for _, p := range pkgs { - if !created[p] { - created[p] = true - prog.CreatePackage(p, nil, nil, true) - createAll(p.Imports()) - } - } - } - createAll(pkg.Imports()) - - // Create and build the primary package. - ssapkg := prog.CreatePackage(pkg, files, info, false) - ssapkg.Build() - return ssapkg, info, nil -} diff --git a/vendor/honnef.co/go/tools/ssa/ssautil/switch.go b/vendor/honnef.co/go/tools/ssa/ssautil/switch.go deleted file mode 100644 index 9c2f5d06ef..0000000000 --- a/vendor/honnef.co/go/tools/ssa/ssautil/switch.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssautil - -// This file implements discovery of switch and type-switch constructs -// from low-level control flow. -// -// Many techniques exist for compiling a high-level switch with -// constant cases to efficient machine code. The optimal choice will -// depend on the data type, the specific case values, the code in the -// body of each case, and the hardware. -// Some examples: -// - a lookup table (for a switch that maps constants to constants) -// - a computed goto -// - a binary tree -// - a perfect hash -// - a two-level switch (to partition constant strings by their first byte). - -import ( - "bytes" - "fmt" - "go/token" - "go/types" - - "honnef.co/go/tools/ssa" -) - -// A ConstCase represents a single constant comparison. -// It is part of a Switch. -type ConstCase struct { - Block *ssa.BasicBlock // block performing the comparison - Body *ssa.BasicBlock // body of the case - Value *ssa.Const // case comparand -} - -// A TypeCase represents a single type assertion. -// It is part of a Switch. -type TypeCase struct { - Block *ssa.BasicBlock // block performing the type assert - Body *ssa.BasicBlock // body of the case - Type types.Type // case type - Binding ssa.Value // value bound by this case -} - -// A Switch is a logical high-level control flow operation -// (a multiway branch) discovered by analysis of a CFG containing -// only if/else chains. It is not part of the ssa.Instruction set. -// -// One of ConstCases and TypeCases has length >= 2; -// the other is nil. -// -// In a value switch, the list of cases may contain duplicate constants. -// A type switch may contain duplicate types, or types assignable -// to an interface type also in the list. -// TODO(adonovan): eliminate such duplicates. -// -type Switch struct { - Start *ssa.BasicBlock // block containing start of if/else chain - X ssa.Value // the switch operand - ConstCases []ConstCase // ordered list of constant comparisons - TypeCases []TypeCase // ordered list of type assertions - Default *ssa.BasicBlock // successor if all comparisons fail -} - -func (sw *Switch) String() string { - // We represent each block by the String() of its - // first Instruction, e.g. "print(42:int)". - var buf bytes.Buffer - if sw.ConstCases != nil { - fmt.Fprintf(&buf, "switch %s {\n", sw.X.Name()) - for _, c := range sw.ConstCases { - fmt.Fprintf(&buf, "case %s: %s\n", c.Value, c.Body.Instrs[0]) - } - } else { - fmt.Fprintf(&buf, "switch %s.(type) {\n", sw.X.Name()) - for _, c := range sw.TypeCases { - fmt.Fprintf(&buf, "case %s %s: %s\n", - c.Binding.Name(), c.Type, c.Body.Instrs[0]) - } - } - if sw.Default != nil { - fmt.Fprintf(&buf, "default: %s\n", sw.Default.Instrs[0]) - } - fmt.Fprintf(&buf, "}") - return buf.String() -} - -// Switches examines the control-flow graph of fn and returns the -// set of inferred value and type switches. A value switch tests an -// ssa.Value for equality against two or more compile-time constant -// values. Switches involving link-time constants (addresses) are -// ignored. A type switch type-asserts an ssa.Value against two or -// more types. -// -// The switches are returned in dominance order. -// -// The resulting switches do not necessarily correspond to uses of the -// 'switch' keyword in the source: for example, a single source-level -// switch statement with non-constant cases may result in zero, one or -// many Switches, one per plural sequence of constant cases. -// Switches may even be inferred from if/else- or goto-based control flow. -// (In general, the control flow constructs of the source program -// cannot be faithfully reproduced from the SSA representation.) -// -func Switches(fn *ssa.Function) []Switch { - // Traverse the CFG in dominance order, so we don't - // enter an if/else-chain in the middle. - var switches []Switch - seen := make(map[*ssa.BasicBlock]bool) // TODO(adonovan): opt: use ssa.blockSet - for _, b := range fn.DomPreorder() { - if x, k := isComparisonBlock(b); x != nil { - // Block b starts a switch. - sw := Switch{Start: b, X: x} - valueSwitch(&sw, k, seen) - if len(sw.ConstCases) > 1 { - switches = append(switches, sw) - } - } - - if y, x, T := isTypeAssertBlock(b); y != nil { - // Block b starts a type switch. - sw := Switch{Start: b, X: x} - typeSwitch(&sw, y, T, seen) - if len(sw.TypeCases) > 1 { - switches = append(switches, sw) - } - } - } - return switches -} - -func valueSwitch(sw *Switch, k *ssa.Const, seen map[*ssa.BasicBlock]bool) { - b := sw.Start - x := sw.X - for x == sw.X { - if seen[b] { - break - } - seen[b] = true - - sw.ConstCases = append(sw.ConstCases, ConstCase{ - Block: b, - Body: b.Succs[0], - Value: k, - }) - b = b.Succs[1] - if len(b.Instrs) > 2 { - // Block b contains not just 'if x == k', - // so it may have side effects that - // make it unsafe to elide. - break - } - if len(b.Preds) != 1 { - // Block b has multiple predecessors, - // so it cannot be treated as a case. - break - } - x, k = isComparisonBlock(b) - } - sw.Default = b -} - -func typeSwitch(sw *Switch, y ssa.Value, T types.Type, seen map[*ssa.BasicBlock]bool) { - b := sw.Start - x := sw.X - for x == sw.X { - if seen[b] { - break - } - seen[b] = true - - sw.TypeCases = append(sw.TypeCases, TypeCase{ - Block: b, - Body: b.Succs[0], - Type: T, - Binding: y, - }) - b = b.Succs[1] - if len(b.Instrs) > 4 { - // Block b contains not just - // {TypeAssert; Extract #0; Extract #1; If} - // so it may have side effects that - // make it unsafe to elide. - break - } - if len(b.Preds) != 1 { - // Block b has multiple predecessors, - // so it cannot be treated as a case. - break - } - y, x, T = isTypeAssertBlock(b) - } - sw.Default = b -} - -// isComparisonBlock returns the operands (v, k) if a block ends with -// a comparison v==k, where k is a compile-time constant. -// -func isComparisonBlock(b *ssa.BasicBlock) (v ssa.Value, k *ssa.Const) { - if n := len(b.Instrs); n >= 2 { - if i, ok := b.Instrs[n-1].(*ssa.If); ok { - if binop, ok := i.Cond.(*ssa.BinOp); ok && binop.Block() == b && binop.Op == token.EQL { - if k, ok := binop.Y.(*ssa.Const); ok { - return binop.X, k - } - if k, ok := binop.X.(*ssa.Const); ok { - return binop.Y, k - } - } - } - } - return -} - -// isTypeAssertBlock returns the operands (y, x, T) if a block ends with -// a type assertion "if y, ok := x.(T); ok {". -// -func isTypeAssertBlock(b *ssa.BasicBlock) (y, x ssa.Value, T types.Type) { - if n := len(b.Instrs); n >= 4 { - if i, ok := b.Instrs[n-1].(*ssa.If); ok { - if ext1, ok := i.Cond.(*ssa.Extract); ok && ext1.Block() == b && ext1.Index == 1 { - if ta, ok := ext1.Tuple.(*ssa.TypeAssert); ok && ta.Block() == b { - // hack: relies upon instruction ordering. - if ext0, ok := b.Instrs[n-3].(*ssa.Extract); ok { - return ext0, ta.X, ta.AssertedType - } - } - } - } - } - return -} diff --git a/vendor/honnef.co/go/tools/ssa/ssautil/visit.go b/vendor/honnef.co/go/tools/ssa/ssautil/visit.go deleted file mode 100644 index 5c14845f54..0000000000 --- a/vendor/honnef.co/go/tools/ssa/ssautil/visit.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ssautil // import "honnef.co/go/tools/ssa/ssautil" - -import "honnef.co/go/tools/ssa" - -// This file defines utilities for visiting the SSA representation of -// a Program. -// -// TODO(adonovan): test coverage. - -// AllFunctions finds and returns the set of functions potentially -// needed by program prog, as determined by a simple linker-style -// reachability algorithm starting from the members and method-sets of -// each package. The result may include anonymous functions and -// synthetic wrappers. -// -// Precondition: all packages are built. -// -func AllFunctions(prog *ssa.Program) map[*ssa.Function]bool { - visit := visitor{ - prog: prog, - seen: make(map[*ssa.Function]bool), - } - visit.program() - return visit.seen -} - -type visitor struct { - prog *ssa.Program - seen map[*ssa.Function]bool -} - -func (visit *visitor) program() { - for _, pkg := range visit.prog.AllPackages() { - for _, mem := range pkg.Members { - if fn, ok := mem.(*ssa.Function); ok { - visit.function(fn) - } - } - } - for _, T := range visit.prog.RuntimeTypes() { - mset := visit.prog.MethodSets.MethodSet(T) - for i, n := 0, mset.Len(); i < n; i++ { - visit.function(visit.prog.MethodValue(mset.At(i))) - } - } -} - -func (visit *visitor) function(fn *ssa.Function) { - if !visit.seen[fn] { - visit.seen[fn] = true - var buf [10]*ssa.Value // avoid alloc in common case - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - for _, op := range instr.Operands(buf[:0]) { - if fn, ok := (*op).(*ssa.Function); ok { - visit.function(fn) - } - } - } - } - } -} - -// MainPackages returns the subset of the specified packages -// named "main" that define a main function. -// The result may include synthetic "testmain" packages. -func MainPackages(pkgs []*ssa.Package) []*ssa.Package { - var mains []*ssa.Package - for _, pkg := range pkgs { - if pkg.Pkg.Name() == "main" && pkg.Func("main") != nil { - mains = append(mains, pkg) - } - } - return mains -} diff --git a/vendor/honnef.co/go/tools/ssa/staticcheck.conf b/vendor/honnef.co/go/tools/ssa/staticcheck.conf new file mode 100644 index 0000000000..d7b38bc356 --- /dev/null +++ b/vendor/honnef.co/go/tools/ssa/staticcheck.conf @@ -0,0 +1,3 @@ +# ssa/... is mostly imported from upstream and we don't want to +# deviate from it too much, hence disabling SA1019 +checks = ["inherit", "-SA1019"] diff --git a/vendor/honnef.co/go/tools/ssa/testmain.go b/vendor/honnef.co/go/tools/ssa/testmain.go index ea232ada95..8ec15ba505 100644 --- a/vendor/honnef.co/go/tools/ssa/testmain.go +++ b/vendor/honnef.co/go/tools/ssa/testmain.go @@ -8,8 +8,8 @@ package ssa // tests of the supplied packages. // It is closely coupled to $GOROOT/src/cmd/go/test.go and $GOROOT/src/testing. // -// TODO(adonovan): this file no longer needs to live in the ssa package. -// Move it to ssautil. +// TODO(adonovan): throws this all away now that x/tools/go/packages +// provides access to the actual synthetic test main files. import ( "bytes" @@ -26,6 +26,8 @@ import ( // FindTests returns the Test, Benchmark, and Example functions // (as defined by "go test") defined in the specified package, // and its TestMain function, if any. +// +// Deprecated: use x/tools/go/packages to access synthetic testmain packages. func FindTests(pkg *Package) (tests, benchmarks, examples []*Function, main *Function) { prog := pkg.Prog @@ -109,6 +111,8 @@ func isTest(name, prefix string) bool { // // Subsequent calls to prog.AllPackages include the new package. // The package pkg must belong to the program prog. +// +// Deprecated: use x/tools/go/packages to access synthetic testmain packages. func (prog *Program) CreateTestMainPackage(pkg *Package) *Package { if pkg.Prog != prog { log.Fatal("Package does not belong to Program") diff --git a/vendor/honnef.co/go/tools/ssa/wrappers.go b/vendor/honnef.co/go/tools/ssa/wrappers.go index 701dd90d7d..a4ae71d8cf 100644 --- a/vendor/honnef.co/go/tools/ssa/wrappers.go +++ b/vendor/honnef.co/go/tools/ssa/wrappers.go @@ -141,13 +141,9 @@ func makeWrapper(prog *Program, sel *types.Selection) *Function { // start is the index of the first regular parameter to use. // func createParams(fn *Function, start int) { - var last *Parameter tparams := fn.Signature.Params() for i, n := start, tparams.Len(); i < n; i++ { - last = fn.addParamObj(tparams.At(i)) - } - if fn.Signature.Variadic() { - last.typ = types.NewSlice(last.typ) + fn.addParamObj(tparams.At(i)) } } diff --git a/vendor/honnef.co/go/tools/staticcheck/analysis.go b/vendor/honnef.co/go/tools/staticcheck/analysis.go new file mode 100644 index 0000000000..442aebe5a1 --- /dev/null +++ b/vendor/honnef.co/go/tools/staticcheck/analysis.go @@ -0,0 +1,525 @@ +package staticcheck + +import ( + "flag" + + "honnef.co/go/tools/facts" + "honnef.co/go/tools/internal/passes/buildssa" + "honnef.co/go/tools/lint/lintutil" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +func newFlagSet() flag.FlagSet { + fs := flag.NewFlagSet("", flag.PanicOnError) + fs.Var(lintutil.NewVersionFlag(), "go", "Target Go version") + return *fs +} + +var Analyzers = map[string]*analysis.Analyzer{ + "SA1000": { + Name: "SA1000", + Run: callChecker(checkRegexpRules), + Doc: Docs["SA1000"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA1001": { + Name: "SA1001", + Run: CheckTemplate, + Doc: Docs["SA1001"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA1002": { + Name: "SA1002", + Run: callChecker(checkTimeParseRules), + Doc: Docs["SA1002"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA1003": { + Name: "SA1003", + Run: callChecker(checkEncodingBinaryRules), + Doc: Docs["SA1003"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA1004": { + Name: "SA1004", + Run: CheckTimeSleepConstant, + Doc: Docs["SA1004"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA1005": { + Name: "SA1005", + Run: CheckExec, + Doc: Docs["SA1005"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA1006": { + Name: "SA1006", + Run: CheckUnsafePrintf, + Doc: Docs["SA1006"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA1007": { + Name: "SA1007", + Run: callChecker(checkURLsRules), + Doc: Docs["SA1007"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA1008": { + Name: "SA1008", + Run: CheckCanonicalHeaderKey, + Doc: Docs["SA1008"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA1010": { + Name: "SA1010", + Run: callChecker(checkRegexpFindAllRules), + Doc: Docs["SA1010"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA1011": { + Name: "SA1011", + Run: callChecker(checkUTF8CutsetRules), + Doc: Docs["SA1011"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA1012": { + Name: "SA1012", + Run: CheckNilContext, + Doc: Docs["SA1012"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA1013": { + Name: "SA1013", + Run: CheckSeeker, + Doc: Docs["SA1013"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA1014": { + Name: "SA1014", + Run: callChecker(checkUnmarshalPointerRules), + Doc: Docs["SA1014"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA1015": { + Name: "SA1015", + Run: CheckLeakyTimeTick, + Doc: Docs["SA1015"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "SA1016": { + Name: "SA1016", + Run: CheckUntrappableSignal, + Doc: Docs["SA1016"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA1017": { + Name: "SA1017", + Run: callChecker(checkUnbufferedSignalChanRules), + Doc: Docs["SA1017"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA1018": { + Name: "SA1018", + Run: callChecker(checkStringsReplaceZeroRules), + Doc: Docs["SA1018"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA1019": { + Name: "SA1019", + Run: CheckDeprecated, + Doc: Docs["SA1019"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Deprecated}, + Flags: newFlagSet(), + }, + "SA1020": { + Name: "SA1020", + Run: callChecker(checkListenAddressRules), + Doc: Docs["SA1020"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA1021": { + Name: "SA1021", + Run: callChecker(checkBytesEqualIPRules), + Doc: Docs["SA1021"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA1023": { + Name: "SA1023", + Run: CheckWriterBufferModified, + Doc: Docs["SA1023"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "SA1024": { + Name: "SA1024", + Run: callChecker(checkUniqueCutsetRules), + Doc: Docs["SA1024"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA1025": { + Name: "SA1025", + Run: CheckTimerResetReturnValue, + Doc: Docs["SA1025"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "SA1026": { + Name: "SA1026", + Run: callChecker(checkUnsupportedMarshal), + Doc: Docs["SA1026"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA1027": { + Name: "SA1027", + Run: callChecker(checkAtomicAlignment), + Doc: Docs["SA1027"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + + "SA2000": { + Name: "SA2000", + Run: CheckWaitgroupAdd, + Doc: Docs["SA2000"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA2001": { + Name: "SA2001", + Run: CheckEmptyCriticalSection, + Doc: Docs["SA2001"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA2002": { + Name: "SA2002", + Run: CheckConcurrentTesting, + Doc: Docs["SA2002"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "SA2003": { + Name: "SA2003", + Run: CheckDeferLock, + Doc: Docs["SA2003"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + + "SA3000": { + Name: "SA3000", + Run: CheckTestMainExit, + Doc: Docs["SA3000"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA3001": { + Name: "SA3001", + Run: CheckBenchmarkN, + Doc: Docs["SA3001"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + + "SA4000": { + Name: "SA4000", + Run: CheckLhsRhsIdentical, + Doc: Docs["SA4000"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.TokenFile, facts.Generated}, + Flags: newFlagSet(), + }, + "SA4001": { + Name: "SA4001", + Run: CheckIneffectiveCopy, + Doc: Docs["SA4001"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA4002": { + Name: "SA4002", + Run: CheckDiffSizeComparison, + Doc: Docs["SA4002"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA4003": { + Name: "SA4003", + Run: CheckExtremeComparison, + Doc: Docs["SA4003"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA4004": { + Name: "SA4004", + Run: CheckIneffectiveLoop, + Doc: Docs["SA4004"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA4006": { + Name: "SA4006", + Run: CheckUnreadVariableValues, + Doc: Docs["SA4006"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "SA4008": { + Name: "SA4008", + Run: CheckLoopCondition, + Doc: Docs["SA4008"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "SA4009": { + Name: "SA4009", + Run: CheckArgOverwritten, + Doc: Docs["SA4009"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "SA4010": { + Name: "SA4010", + Run: CheckIneffectiveAppend, + Doc: Docs["SA4010"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "SA4011": { + Name: "SA4011", + Run: CheckScopedBreak, + Doc: Docs["SA4011"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA4012": { + Name: "SA4012", + Run: CheckNaNComparison, + Doc: Docs["SA4012"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "SA4013": { + Name: "SA4013", + Run: CheckDoubleNegation, + Doc: Docs["SA4013"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA4014": { + Name: "SA4014", + Run: CheckRepeatedIfElse, + Doc: Docs["SA4014"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA4015": { + Name: "SA4015", + Run: callChecker(checkMathIntRules), + Doc: Docs["SA4015"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA4016": { + Name: "SA4016", + Run: CheckSillyBitwiseOps, + Doc: Docs["SA4016"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, facts.TokenFile}, + Flags: newFlagSet(), + }, + "SA4017": { + Name: "SA4017", + Run: CheckPureFunctions, + Doc: Docs["SA4017"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, facts.Purity}, + Flags: newFlagSet(), + }, + "SA4018": { + Name: "SA4018", + Run: CheckSelfAssignment, + Doc: Docs["SA4018"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated, facts.TokenFile}, + Flags: newFlagSet(), + }, + "SA4019": { + Name: "SA4019", + Run: CheckDuplicateBuildConstraints, + Doc: Docs["SA4019"].String(), + Requires: []*analysis.Analyzer{facts.Generated}, + Flags: newFlagSet(), + }, + "SA4020": { + Name: "SA4020", + Run: CheckUnreachableTypeCases, + Doc: Docs["SA4020"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA4021": { + Name: "SA4021", + Run: CheckSingleArgAppend, + Doc: Docs["SA4021"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated, facts.TokenFile}, + Flags: newFlagSet(), + }, + + "SA5000": { + Name: "SA5000", + Run: CheckNilMaps, + Doc: Docs["SA5000"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "SA5001": { + Name: "SA5001", + Run: CheckEarlyDefer, + Doc: Docs["SA5001"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA5002": { + Name: "SA5002", + Run: CheckInfiniteEmptyLoop, + Doc: Docs["SA5002"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA5003": { + Name: "SA5003", + Run: CheckDeferInInfiniteLoop, + Doc: Docs["SA5003"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA5004": { + Name: "SA5004", + Run: CheckLoopEmptyDefault, + Doc: Docs["SA5004"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA5005": { + Name: "SA5005", + Run: CheckCyclicFinalizer, + Doc: Docs["SA5005"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "SA5007": { + Name: "SA5007", + Run: CheckInfiniteRecursion, + Doc: Docs["SA5007"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "SA5008": { + Name: "SA5008", + Run: CheckStructTags, + Doc: Docs["SA5008"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA5009": { + Name: "SA5009", + Run: callChecker(checkPrintfRules), + Doc: Docs["SA5009"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + + "SA6000": { + Name: "SA6000", + Run: callChecker(checkRegexpMatchLoopRules), + Doc: Docs["SA6000"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA6001": { + Name: "SA6001", + Run: CheckMapBytesKey, + Doc: Docs["SA6001"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "SA6002": { + Name: "SA6002", + Run: callChecker(checkSyncPoolValueRules), + Doc: Docs["SA6002"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer}, + Flags: newFlagSet(), + }, + "SA6003": { + Name: "SA6003", + Run: CheckRangeStringRunes, + Doc: Docs["SA6003"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "SA6005": { + Name: "SA6005", + Run: CheckToLowerToUpperComparison, + Doc: Docs["SA6005"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + + "SA9001": { + Name: "SA9001", + Run: CheckDubiousDeferInChannelRangeLoop, + Doc: Docs["SA9001"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA9002": { + Name: "SA9002", + Run: CheckNonOctalFileMode, + Doc: Docs["SA9002"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + "SA9003": { + Name: "SA9003", + Run: CheckEmptyBranch, + Doc: Docs["SA9003"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, facts.TokenFile, facts.Generated}, + Flags: newFlagSet(), + }, + "SA9004": { + Name: "SA9004", + Run: CheckMissingEnumTypesInDeclaration, + Doc: Docs["SA9004"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, + // Filtering generated code because it may include empty structs generated from data models. + "SA9005": { + Name: "SA9005", + Run: callChecker(checkNoopMarshal), + Doc: Docs["SA9005"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, valueRangesAnalyzer, facts.Generated, facts.TokenFile}, + Flags: newFlagSet(), + }, +} diff --git a/vendor/honnef.co/go/tools/staticcheck/doc.go b/vendor/honnef.co/go/tools/staticcheck/doc.go index 07a39ef45a..4a87d4a24c 100644 --- a/vendor/honnef.co/go/tools/staticcheck/doc.go +++ b/vendor/honnef.co/go/tools/staticcheck/doc.go @@ -1,39 +1,37 @@ package staticcheck -var docSA1000 = `Invalid regular expression +import "honnef.co/go/tools/lint" -Available since - 2017.1 -` +var Docs = map[string]*lint.Documentation{ + "SA1000": &lint.Documentation{ + Title: `Invalid regular expression`, + Since: "2017.1", + }, -var docSA1001 = `Invalid template + "SA1001": &lint.Documentation{ + Title: `Invalid template`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA1002": &lint.Documentation{ + Title: `Invalid format in time.Parse`, + Since: "2017.1", + }, -var docSA1002 = `Invalid format in time.Parse - -Available since - 2017.1 -` - -var docSA1003 = `Unsupported argument to functions in encoding/binary - -The encoding/binary package can only serialize types with known sizes. -This precludes the use of the 'int' and 'uint' types, as their sizes + "SA1003": &lint.Documentation{ + Title: `Unsupported argument to functions in encoding/binary`, + Text: `The encoding/binary package can only serialize types with known sizes. +This precludes the use of the int and uint types, as their sizes differ on different architectures. Furthermore, it doesn't support serializing maps, channels, strings, or functions. -Before Go 1.8, bool wasn't supported, either. +Before Go 1.8, bool wasn't supported, either.`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docSA1004 = `Suspiciously small untyped constant in time.Sleep - -The time.Sleep function takes a time.Duration as its only argument. + "SA1004": &lint.Documentation{ + Title: `Suspiciously small untyped constant in time.Sleep`, + Text: `The time.Sleep function takes a time.Duration as its only argument. Durations are expressed in nanoseconds. Thus, calling time.Sleep(1) will sleep for 1 nanosecond. This is a common source of bugs, as sleep functions in other languages often accept seconds or milliseconds. @@ -43,16 +41,14 @@ large durations. These can be combined with arithmetic to express arbitrary durations, for example '5 * time.Second' for 5 seconds. If you truly meant to sleep for a tiny amount of time, use -'n * time.Nanosecond" to signal to staticcheck that you did mean to sleep -for some amount of nanoseconds. +'n * time.Nanosecond' to signal to staticcheck that you did mean to sleep +for some amount of nanoseconds.`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docSA1005 = `Invalid first argument to exec.Command - -os/exec runs programs directly (using variants of the fork and exec + "SA1005": &lint.Documentation{ + Title: `Invalid first argument to exec.Command`, + Text: `os/exec runs programs directly (using variants of the fork and exec system calls on Unix systems). This shouldn't be confused with running a command in a shell. The shell will allow for features such as input redirection, pipes, and general scripting. The shell is also @@ -69,15 +65,13 @@ If you want to run a command in a shell, consider using something like the following – but be aware that not all systems, particularly Windows, will have a /bin/sh program: - exec.Command("/bin/sh", "-c", "ls | grep Awesome") + exec.Command("/bin/sh", "-c", "ls | grep Awesome")`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docSA1006 = `Printf with dynamic first argument and no further arguments - -Using fmt.Printf with a dynamic first argument can lead to unexpected + "SA1006": &lint.Documentation{ + Title: `Printf with dynamic first argument and no further arguments`, + Text: `Using fmt.Printf with a dynamic first argument can lead to unexpected output. The first argument is a format string, where certain character combinations have special meaning. If, for example, a user were to enter a string such as @@ -86,335 +80,325 @@ enter a string such as and you printed it with -fmt.Printf(s) + fmt.Printf(s) it would lead to the following output: - Interest rate: 5%!(NOVERB). + Interest rate: 5%!(NOVERB). Similarly, forming the first parameter via string concatenation with user input should be avoided for the same reason. When printing user input, either use a variant of fmt.Print, or use the %s Printf verb -and pass the string as an argument. +and pass the string as an argument.`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA1007": &lint.Documentation{ + Title: `Invalid URL in net/url.Parse`, + Since: "2017.1", + }, -var docSA1007 = `Invalid URL in net/url.Parse + "SA1008": &lint.Documentation{ + Title: `Non-canonical key in http.Header map`, + Text: `Keys in http.Header maps are canonical, meaning they follow a specific +combination of uppercase and lowercase letters. Methods such as +http.Header.Add and http.Header.Del convert inputs into this canonical +form before manipulating the map. -Available since - 2017.1 -` +When manipulating http.Header maps directly, as opposed to using the +provided methods, care should be taken to stick to canonical form in +order to avoid inconsistencies. The following piece of code +demonstrates one such inconsistency: -var docSA1008 = `Non-canonical key in http.Header map + h := http.Header{} + h["etag"] = []string{"1234"} + h.Add("etag", "5678") + fmt.Println(h) -Available since - 2017.1 -` + // Output: + // map[Etag:[5678] etag:[1234]] -var docSA1010 = `(*regexp.Regexp).FindAll called with n == 0, which will always return zero results +The easiest way of obtaining the canonical form of a key is to use +http.CanonicalHeaderKey.`, + Since: "2017.1", + }, -If n >= 0, the function returns at most n matches/submatches. To -return all results, specify a negative number. + "SA1010": &lint.Documentation{ + Title: `(*regexp.Regexp).FindAll called with n == 0, which will always return zero results`, + Text: `If n >= 0, the function returns at most n matches/submatches. To +return all results, specify a negative number.`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA1011": &lint.Documentation{ + Title: `Various methods in the strings package expect valid UTF-8, but invalid input is provided`, + Since: "2017.1", + }, -var docSA1011 = `Various methods in the strings package expect valid UTF-8, but invalid input is provided + "SA1012": &lint.Documentation{ + Title: `A nil context.Context is being passed to a function, consider using context.TODO instead`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA1013": &lint.Documentation{ + Title: `io.Seeker.Seek is being called with the whence constant as the first argument, but it should be the second`, + Since: "2017.1", + }, -var docSA1012 = `A nil context.Context is being passed to a function, consider using context.TODO instead + "SA1014": &lint.Documentation{ + Title: `Non-pointer value passed to Unmarshal or Decode`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA1015": &lint.Documentation{ + Title: `Using time.Tick in a way that will leak. Consider using time.NewTicker, and only use time.Tick in tests, commands and endless functions`, + Since: "2017.1", + }, -var docSA1013 = `io.Seeker.Seek is being called with the whence constant as the first argument, but it should be the second - -Available since - 2017.1 -` - -var docSA1014 = `Non-pointer value passed to Unmarshal or Decode - -Available since - 2017.1 -` - -var docSA1015 = `Using time.Tick in a way that will leak. Consider using time.NewTicker, and only use time.Tick in tests, commands and endless functions - -Available since - 2017.1 -` - -var docSA1016 = `Trapping a signal that cannot be trapped - -Not all signals can be intercepted by a process. Speficially, on + "SA1016": &lint.Documentation{ + Title: `Trapping a signal that cannot be trapped`, + Text: `Not all signals can be intercepted by a process. Speficially, on UNIX-like systems, the syscall.SIGKILL and syscall.SIGSTOP signals are never passed to the process, but instead handled directly by the -kernel. It is therefore pointless to try and handle these signals. +kernel. It is therefore pointless to try and handle these signals.`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docSA1017 = `Channels used with os/signal.Notify should be buffered - -The os/signal package uses non-blocking channel sends when delivering + "SA1017": &lint.Documentation{ + Title: `Channels used with os/signal.Notify should be buffered`, + Text: `The os/signal package uses non-blocking channel sends when delivering signals. If the receiving end of the channel isn't ready and the channel is either unbuffered or full, the signal will be dropped. To avoid missing signals, the channel should be buffered and of the appropriate size. For a channel used for notification of just one -signal value, a buffer of size 1 is sufficient. +signal value, a buffer of size 1 is sufficient.`, + Since: "2017.1", + }, + "SA1018": &lint.Documentation{ + Title: `strings.Replace called with n == 0, which does nothing`, + Text: `With n == 0, zero instances will be replaced. To replace all +instances, use a negative number, or use strings.ReplaceAll.`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA1019": &lint.Documentation{ + Title: `Using a deprecated function, variable, constant or field`, + Since: "2017.1", + }, -var docSA1018 = `strings.Replace called with n == 0, which does nothing + "SA1020": &lint.Documentation{ + Title: `Using an invalid host:port pair with a net.Listen-related function`, + Since: "2017.1", + }, -With n == 0, zero instances will be replaced. To replace all -instances, use a negative number, or use strings.ReplaceAll. - -Available since - 2017.1 -` - -var docSA1019 = `Using a deprecated function, variable, constant or field - -Available since - 2017.1 -` - -var docSA1020 = `Using an invalid host:port pair with a net.Listen-related function - -Available since - 2017.1 -` - -var docSA1021 = `Using bytes.Equal to compare two net.IP - -A net.IP stores an IPv4 or IPv6 address as a slice of bytes. The + "SA1021": &lint.Documentation{ + Title: `Using bytes.Equal to compare two net.IP`, + Text: `A net.IP stores an IPv4 or IPv6 address as a slice of bytes. The length of the slice for an IPv4 address, however, can be either 4 or 16 bytes long, using different ways of representing IPv4 addresses. In order to correctly compare two net.IPs, the net.IP.Equal method should -be used, as it takes both representations into account. +be used, as it takes both representations into account.`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA1023": &lint.Documentation{ + Title: `Modifying the buffer in an io.Writer implementation`, + Text: `Write must not modify the slice data, even temporarily.`, + Since: "2017.1", + }, -var docSA1023 = `Modifying the buffer in an io.Writer implementation + "SA1024": &lint.Documentation{ + Title: `A string cutset contains duplicate characters`, + Text: `The strings.TrimLeft and strings.TrimRight functions take cutsets, not +prefixes. A cutset is treated as a set of characters to remove from a +string. For example, -Write must not modify the slice data, even temporarily. + strings.TrimLeft("42133word", "1234")) -Available since - 2017.1 -` +will result in the string "word" – any characters that are 1, 2, 3 or +4 are cut from the left of the string. -var docSA1024 = `A string cutset contains duplicate characters, suggesting TrimPrefix or TrimSuffix should be used instead of TrimLeft or TrimRight +In order to remove one string from another, use strings.TrimPrefix instead.`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA1025": &lint.Documentation{ + Title: `It is not possible to use (*time.Timer).Reset's return value correctly`, + Since: "2019.1", + }, -var docSA1025 = `It is not possible to use Reset's return value correctly + "SA1026": &lint.Documentation{ + Title: `Cannot marshal channels or functions`, + Since: "2019.2", + }, -Available since - 2019.1 -` - -var docSA1026 = `Cannot marshal channels or functions - -Available since - Unreleased -` - -var docSA1027 = `Atomic access to 64-bit variable must be 64-bit aligned - -On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to + "SA1027": &lint.Documentation{ + Title: `Atomic access to 64-bit variable must be 64-bit aligned`, + Text: `On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically. The first word in a variable or in an allocated struct, array, or slice can be relied upon to be 64-bit aligned. You can use the structlayout tool to inspect the alignment of fields -in a struct. +in a struct.`, + Since: "2019.2", + }, -Available since - Unreleased -` + "SA2000": &lint.Documentation{ + Title: `sync.WaitGroup.Add called inside the goroutine, leading to a race condition`, + Since: "2017.1", + }, -var docSA2000 = `sync.WaitGroup.Add called inside the goroutine, leading to a race condition + "SA2001": &lint.Documentation{ + Title: `Empty critical section, did you mean to defer the unlock?`, + Text: `Empty critical sections of the kind -Available since - 2017.1 -` + mu.Lock() + mu.Unlock() -var docSA2001 = `Empty critical section, did you mean to defer the unlock? +are very often a typo, and the following was intended instead: -Available since - 2017.1 -` + mu.Lock() + defer mu.Unlock() -var docSA2002 = `Called testing.T.FailNow or SkipNow in a goroutine, which isn't allowed +Do note that sometimes empty critical sections can be useful, as a +form of signaling to wait on another goroutine. Many times, there are +simpler ways of achieving the same effect. When that isn't the case, +the code should be amply commented to avoid confusion. Combining such +comments with a //lint:ignore directive can be used to suppress this +rare false positive.`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA2002": &lint.Documentation{ + Title: `Called testing.T.FailNow or SkipNow in a goroutine, which isn't allowed`, + Since: "2017.1", + }, -var docSA2003 = `Deferred Lock right after locking, likely meant to defer Unlock instead + "SA2003": &lint.Documentation{ + Title: `Deferred Lock right after locking, likely meant to defer Unlock instead`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docSA3000 = `TestMain doesn't call os.Exit, hiding test failures - -Test executables (and in turn 'go test') exit with a non-zero status + "SA3000": &lint.Documentation{ + Title: `TestMain doesn't call os.Exit, hiding test failures`, + Text: `Test executables (and in turn 'go test') exit with a non-zero status code if any tests failed. When specifying your own TestMain function, it is your responsibility to arrange for this, by calling os.Exit with the correct code. The correct code is returned by (*testing.M).Run, so the usual way of implementing TestMain is to end it with -os.Exit(m.Run()). +os.Exit(m.Run()).`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docSA3001 = `Assigning to b.N in benchmarks distorts the results - -The testing package dynamically sets b.N to improve the reliability of + "SA3001": &lint.Documentation{ + Title: `Assigning to b.N in benchmarks distorts the results`, + Text: `The testing package dynamically sets b.N to improve the reliability of benchmarks and uses it in computations to determine the duration of a single operation. Benchmark code must not alter b.N as this would -falsify results. +falsify results.`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA4000": &lint.Documentation{ + Title: `Boolean expression has identical expressions on both sides`, + Since: "2017.1", + }, -var docSA4000 = `Boolean expression has identical expressions on both sides + "SA4001": &lint.Documentation{ + Title: `&*x gets simplified to x, it does not copy x`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA4002": &lint.Documentation{ + Title: `Comparing strings with known different sizes has predictable results`, + Since: "2017.1", + }, -var docSA4001 = `&*x gets simplified to x, it does not copy x + "SA4003": &lint.Documentation{ + Title: `Comparing unsigned values against negative values is pointless`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA4004": &lint.Documentation{ + Title: `The loop exits unconditionally after one iteration`, + Since: "2017.1", + }, -var docSA4002 = `Comparing strings with known different sizes has predictable results + "SA4005": &lint.Documentation{ + Title: `Field assignment that will never be observed. Did you mean to use a pointer receiver?`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA4006": &lint.Documentation{ + Title: `A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?`, + Since: "2017.1", + }, -var docSA4003 = `Comparing unsigned values against negative values is pointless + "SA4008": &lint.Documentation{ + Title: `The variable in the loop condition never changes, are you incrementing the wrong variable?`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA4009": &lint.Documentation{ + Title: `A function argument is overwritten before its first use`, + Since: "2017.1", + }, -var docSA4004 = `The loop exits unconditionally after one iteration + "SA4010": &lint.Documentation{ + Title: `The result of append will never be observed anywhere`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA4011": &lint.Documentation{ + Title: `Break statement with no effect. Did you mean to break out of an outer loop?`, + Since: "2017.1", + }, -var docSA4005 = `Field assignment that will never be observed. Did you mean to use a pointer receiver? + "SA4012": &lint.Documentation{ + Title: `Comparing a value against NaN even though no value is equal to NaN`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA4013": &lint.Documentation{ + Title: `Negating a boolean twice (!!b) is the same as writing b. This is either redundant, or a typo.`, + Since: "2017.1", + }, -var docSA4006 = `A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code? + "SA4014": &lint.Documentation{ + Title: `An if/else if chain has repeated conditions and no side-effects; if the condition didn't match the first time, it won't match the second time, either`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA4015": &lint.Documentation{ + Title: `Calling functions like math.Ceil on floats converted from integers doesn't do anything useful`, + Since: "2017.1", + }, -var docSA4008 = `The variable in the loop condition never changes, are you incrementing the wrong variable? + "SA4016": &lint.Documentation{ + Title: `Certain bitwise operations, such as x ^ 0, do not do anything useful`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA4017": &lint.Documentation{ + Title: `A pure function's return value is discarded, making the call pointless`, + Since: "2017.1", + }, -var docSA4009 = `A function argument is overwritten before its first use + "SA4018": &lint.Documentation{ + Title: `Self-assignment of variables`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA4019": &lint.Documentation{ + Title: `Multiple, identical build constraints in the same file`, + Since: "2017.1", + }, -var docSA4010 = `The result of append will never be observed anywhere - -Available since - 2017.1 -` - -var docSA4011 = `Break statement with no effect. Did you mean to break out of an outer loop? - -Available since - 2017.1 -` - -var docSA4012 = `Comparing a value against NaN even though no value is equal to NaN - -Available since - 2017.1 -` - -var docSA4013 = `Negating a boolean twice (!!b) is the same as writing b. This is either redundant, or a typo. - -Available since - 2017.1 -` - -var docSA4014 = `An if/else if chain has repeated conditions and no side-effects; if the condition didn't match the first time, it won't match the second time, either - -Available since - 2017.1 -` - -var docSA4015 = `Calling functions like math.Ceil on floats converted from integers doesn't do anything useful - -Available since - 2017.1 -` - -var docSA4016 = `Certain bitwise operations, such as x ^ 0, do not do anything useful - -Available since - 2017.1 -` - -var docSA4017 = `A pure function's return value is discarded, making the call pointless - -Available since - 2017.1 -` - -var docSA4018 = `Self-assignment of variables - -Available since - 2017.1 -` - -var docSA4019 = `Multiple, identical build constraints in the same file - -Available since - 2017.1 -` - -var docSA4020 = `Unreachable case clause in a type switch - -In a type switch like the following + "SA4020": &lint.Documentation{ + Title: `Unreachable case clause in a type switch`, + Text: `In a type switch like the following type T struct{} func (T) Read(b []byte) (int, error) { return 0, nil } @@ -449,7 +433,7 @@ Another example: Even though T has a Close method and thus implements io.ReadCloser, io.Reader will always match first. The method set of io.Reader is a subset of io.ReadCloser. Thus it is impossible to match the second -case without mtching the first case. +case without matching the first case. Structurally equivalent interfaces @@ -479,55 +463,46 @@ the following type switch will have an unreachable case clause: } T will always match before V because they are structurally equivalent -and therefore doSomething()'s return value implements both. +and therefore doSomething()'s return value implements both.`, + Since: "2019.2", + }, -Available since - Unreleased -` + "SA4021": &lint.Documentation{ + Title: `x = append(y) is equivalent to x = y`, + Since: "2019.2", + }, -var docSA4021 = `x = append(y) is equivalent to x = y + "SA5000": &lint.Documentation{ + Title: `Assignment to nil map`, + Since: "2017.1", + }, -Available since - Unreleased -` + "SA5001": &lint.Documentation{ + Title: `Defering Close before checking for a possible error`, + Since: "2017.1", + }, -var docSA5000 = `Assignment to nil map + "SA5002": &lint.Documentation{ + Title: `The empty for loop (for {}) spins and can block the scheduler`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docSA5001 = `Defering Close before checking for a possible error - -Available since - 2017.1 -` - -var docSA5002 = `The empty for loop (for {}) spins and can block the scheduler - -Available since - 2017.1 -` - -var docSA5003 = `Defers in infinite loops will never execute - -Defers are scoped to the surrounding function, not the surrounding + "SA5003": &lint.Documentation{ + Title: `Defers in infinite loops will never execute`, + Text: `Defers are scoped to the surrounding function, not the surrounding block. In a function that never returns, i.e. one containing an -infinite loop, defers will never execute. +infinite loop, defers will never execute.`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA5004": &lint.Documentation{ + Title: `for { select { ... with an empty default branch spins`, + Since: "2017.1", + }, -var docSA5004 = `for { select { ... with an empty default branch spins - -Available since - 2017.1 -` - -var docSA5005 = `The finalizer references the finalized object, preventing garbage collection - -A finalizer is a function associated with an object that runs when the + "SA5005": &lint.Documentation{ + Title: `The finalizer references the finalized object, preventing garbage collection`, + Text: `A finalizer is a function associated with an object that runs when the garbage collector is ready to collect said object, that is when the object is no longer referenced by anything. @@ -537,21 +512,18 @@ collector from collecting the object. The finalizer will never run, and the object will never be collected, leading to a memory leak. That is why the finalizer should instead use its first argument to operate on the object. That way, the number of references can temporarily go -to zero before the object is being passed to the finalizer. +to zero before the object is being passed to the finalizer.`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA5006": &lint.Documentation{ + Title: `Slice index out of bounds`, + Since: "2017.1", + }, -var docSA5006 = `Slice index out of bounds - -Available since - 2017.1 -` - -var docSA5007 = `Infinite recursive call - -A function that calls itself recursively needs to have an exit + "SA5007": &lint.Documentation{ + Title: `Infinite recursive call`, + Text: `A function that calls itself recursively needs to have an exit condition. Otherwise it will recurse forever, until the system runs out of memory. @@ -559,21 +531,29 @@ This issue can be caused by simple bugs such as forgetting to add an exit condition. It can also happen "on purpose". Some languages have tail call optimization which makes certain infinite recursive calls safe to use. Go, however, does not implement TCO, and as such a loop -should be used instead. +should be used instead.`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA5008": &lint.Documentation{ + Title: `Invalid struct tag`, + Since: "2019.2", + }, -var docSA6000 = `Using regexp.Match or related in a loop, should use regexp.Compile + "SA5009": &lint.Documentation{ + Title: `Invalid Printf call`, + Since: "2019.2", + }, -Available since - 2017.1 -` + "SA6000": &lint.Documentation{ + Title: `Using regexp.Match or related in a loop, should use regexp.Compile`, + Since: "2017.1", + }, -var docSA6001 = `Missing an optimization opportunity when indexing maps by byte slices + "SA6001": &lint.Documentation{ + Title: `Missing an optimization opportunity when indexing maps by byte slices`, -Map keys must be comparable, which precludes the use of byte slices. + Text: `Map keys must be comparable, which precludes the use of byte slices. This usually leads to using string keys and converting byte slices to strings. @@ -596,15 +576,13 @@ because the first version needs to copy and allocate, while the second one does not. For some history on this optimization, check out commit -f5f5a8b6209f84961687d993b93ea0d397f5d5bf in the Go repository. +f5f5a8b6209f84961687d993b93ea0d397f5d5bf in the Go repository.`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docSA6002 = `Storing non-pointer values in sync.Pool allocates memory - -A sync.Pool is used to avoid unnecessary allocations and reduce the + "SA6002": &lint.Documentation{ + Title: `Storing non-pointer values in sync.Pool allocates memory`, + Text: `A sync.Pool is used to avoid unnecessary allocations and reduce the amount of work the garbage collector has to do. When passing a value that is not a pointer to a function that accepts @@ -615,15 +593,13 @@ an array). In order to avoid the extra allocation, one should store a pointer to the slice instead. See the comments on https://go-review.googlesource.com/c/go/+/24371 -that discuss this problem. +that discuss this problem.`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docSA6003 = `Converting a string to a slice of runes before ranging over it - -You may want to loop over the runes in a string. Instead of converting + "SA6003": &lint.Documentation{ + Title: `Converting a string to a slice of runes before ranging over it`, + Text: `You may want to loop over the runes in a string. Instead of converting the string to a slice of runes and looping over that, you can loop over the string itself. That is, @@ -639,15 +615,13 @@ and avoid unnecessary memory allocations. Do note that if you are interested in the indices, ranging over a string and over a slice of runes will yield different indices. The first one yields byte offsets, while the second one yields indices in -the slice of runes. +the slice of runes.`, + Since: "2017.1", + }, -Available since - 2017.1 -` - -var docSA6005 = `Inefficient string comparison with strings.ToLower or strings.ToUpper - -Converting two strings to the same case and comparing them like so + "SA6005": &lint.Documentation{ + Title: `Inefficient string comparison with strings.ToLower or strings.ToUpper`, + Text: `Converting two strings to the same case and comparing them like so if strings.ToLower(s1) == strings.ToLower(s2) { ... @@ -665,33 +639,29 @@ strings and can return as soon as the first non-matching character has been found. For a more in-depth explanation of this issue, see -https://blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/ +https://blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/`, + Since: "2019.2", + }, -Available since - Unreleased -` + "SA9001": &lint.Documentation{ + Title: `Defers in range loops may not run when you expect them to`, + Since: "2017.1", + }, -var docSA9001 = `Defers in 'for range' loops may not run when you expect them to + "SA9002": &lint.Documentation{ + Title: `Using a non-octal os.FileMode that looks like it was meant to be in octal.`, + Since: "2017.1", + }, -Available since - 2017.1 -` + "SA9003": &lint.Documentation{ + Title: `Empty body in an if or else branch`, + Since: "2017.1", + }, -var docSA9002 = `Using a non-octal os.FileMode that looks like it was meant to be in octal. + "SA9004": &lint.Documentation{ + Title: `Only the first constant has an explicit type`, -Available since - 2017.1 -` - -var docSA9003 = `Empty body in an if or else branch - -Available since - 2017.1 -` - -var docSA9004 = `Only the first constant has an explicit type - -In a constant declaration such as the following: + Text: `In a constant declaration such as the following: const ( First byte = 1 @@ -727,7 +697,7 @@ Wrong type in variable declarations The most obvious issue with such incorrect enumerations expresses itself as a compile error: -package pkg + package pkg const ( EnumFirst uint8 = 1 @@ -776,22 +746,19 @@ This code will output an enum 2 -as EnumSecond has no explicit type, and thus defaults to int. +as EnumSecond has no explicit type, and thus defaults to int.`, + Since: "2019.1", + }, -Available since - 2019.1 -` - -var docSA9005 = `Trying to marshal a struct with no public fields nor custom marshaling - -The encoding/json and encoding/xml packages only operate on exported + "SA9005": &lint.Documentation{ + Title: `Trying to marshal a struct with no public fields nor custom marshaling`, + Text: `The encoding/json and encoding/xml packages only operate on exported fields in structs, not unexported ones. It is usually an error to try to (un)marshal structs that only consist of unexported fields. This check will not flag calls involving types that define custom marshaling behavior, e.g. via MarshalJSON methods. It will also not -flag empty structs. - -Available since - Unreleased -` +flag empty structs.`, + Since: "2019.2", + }, +} diff --git a/vendor/honnef.co/go/tools/staticcheck/knowledge.go b/vendor/honnef.co/go/tools/staticcheck/knowledge.go new file mode 100644 index 0000000000..4c12b866a2 --- /dev/null +++ b/vendor/honnef.co/go/tools/staticcheck/knowledge.go @@ -0,0 +1,25 @@ +package staticcheck + +import ( + "reflect" + + "golang.org/x/tools/go/analysis" + "honnef.co/go/tools/internal/passes/buildssa" + "honnef.co/go/tools/ssa" + "honnef.co/go/tools/staticcheck/vrp" +) + +var valueRangesAnalyzer = &analysis.Analyzer{ + Name: "vrp", + Doc: "calculate value ranges of functions", + Run: func(pass *analysis.Pass) (interface{}, error) { + m := map[*ssa.Function]vrp.Ranges{} + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { + vr := vrp.BuildGraph(ssafn).Solve() + m[ssafn] = vr + } + return m, nil + }, + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + ResultType: reflect.TypeOf(map[*ssa.Function]vrp.Ranges{}), +} diff --git a/vendor/honnef.co/go/tools/staticcheck/lint.go b/vendor/honnef.co/go/tools/staticcheck/lint.go index 1300eff8c3..1558cbf941 100644 --- a/vendor/honnef.co/go/tools/staticcheck/lint.go +++ b/vendor/honnef.co/go/tools/staticcheck/lint.go @@ -15,13 +15,14 @@ import ( "sort" "strconv" "strings" - "sync" texttemplate "text/template" "unicode" . "honnef.co/go/tools/arg" "honnef.co/go/tools/deprecated" + "honnef.co/go/tools/facts" "honnef.co/go/tools/functions" + "honnef.co/go/tools/internal/passes/buildssa" "honnef.co/go/tools/internal/sharedcheck" "honnef.co/go/tools/lint" . "honnef.co/go/tools/lint/lintdsl" @@ -30,8 +31,11 @@ import ( "honnef.co/go/tools/ssautil" "honnef.co/go/tools/staticcheck/vrp" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/packages" + "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/go/types/typeutil" ) func validRegexp(call *Call) { @@ -106,7 +110,7 @@ var ( checkEncodingBinaryRules = map[string]CallCheck{ "encoding/binary.Write": func(call *Call) { arg := call.Args[Arg("encoding/binary.Write.data")] - if !CanBinaryMarshal(call.Job, arg.Value) { + if !CanBinaryMarshal(call.Pass, arg.Value) { arg.Invalid(fmt.Sprintf("value of type %s cannot be used with binary.Write", arg.Value.Value.Type())) } }, @@ -311,8 +315,12 @@ var verbs = [...]verbFlag{ } func checkPrintfCallImpl(call *Call, f ssa.Value, args []ssa.Value) { - var elem func(T types.Type, verb rune) ([]types.Type, bool) - elem = func(T types.Type, verb rune) ([]types.Type, bool) { + var msCache *typeutil.MethodSetCache + if f.Parent() != nil { + msCache = &f.Parent().Prog.MethodSets + } + + elem := func(T types.Type, verb rune) ([]types.Type, bool) { if verbs[verb]&noRecurse != 0 { return []types.Type{T}, false } @@ -436,7 +444,7 @@ func checkPrintfCallImpl(call *Call, f ssa.Value, args []ssa.Value) { return true } - ms := types.NewMethodSet(T) + ms := msCache.MethodSet(T) if isFormatter(T, ms) { // the value is responsible for formatting itself return true @@ -623,7 +631,7 @@ func checkPrintfCallImpl(call *Call, f ssa.Value, args []ssa.Value) { } func checkAtomicAlignmentImpl(call *Call) { - sizes := call.Job.Pkg.TypesSizes + sizes := call.Pass.TypesSizes if sizes.Sizeof(types.Typ[types.Uintptr]) != 4 { // Not running on a 32-bit platform return @@ -650,6 +658,9 @@ func checkAtomicAlignmentImpl(call *Call) { func checkNoopMarshalImpl(argN int, meths ...string) CallCheck { return func(call *Call) { + if IsGenerated(call.Pass, call.Instr.Pos()) { + return + } arg := call.Args[argN] T := arg.Value.Value.Type() Ts, ok := Dereference(T).Underlying().(*types.Struct) @@ -666,7 +677,7 @@ func checkNoopMarshalImpl(argN int, meths ...string) CallCheck { } } // OPT(dh): we could use a method set cache here - ms := types.NewMethodSet(T) + ms := call.Instr.Parent().Prog.MethodSets.MethodSet(T) // TODO(dh): we're not checking the signature, which can cause false negatives. // This isn't a huge problem, however, since vet complains about incorrect signatures. for _, meth := range meths { @@ -681,14 +692,15 @@ func checkNoopMarshalImpl(argN int, meths ...string) CallCheck { func checkUnsupportedMarshalImpl(argN int, tag string, meths ...string) CallCheck { // TODO(dh): flag slices and maps of unsupported types return func(call *Call) { + msCache := &call.Instr.Parent().Prog.MethodSets + arg := call.Args[argN] T := arg.Value.Value.Type() Ts, ok := Dereference(T).Underlying().(*types.Struct) if !ok { return } - // OPT(dh): we could use a method set cache here - ms := types.NewMethodSet(T) + ms := msCache.MethodSet(T) // TODO(dh): we're not checking the signature, which can cause false negatives. // This isn't a huge problem, however, since vet complains about incorrect signatures. for _, meth := range meths { @@ -704,8 +716,7 @@ func checkUnsupportedMarshalImpl(argN int, tag string, meths ...string) CallChec if reflect.StructTag(field.Tag).Get(tag) == "-" { continue } - // OPT(dh): we could use a method set cache here - ms := types.NewMethodSet(field.Var.Type()) + ms := msCache.MethodSet(field.Var.Type()) // TODO(dh): we're not checking the signature, which can cause false negatives. // This isn't a huge problem, however, since vet complains about incorrect signatures. for _, meth := range meths { @@ -731,343 +742,61 @@ func fieldPath(start types.Type, indices []int) string { return p } -type Checker struct { - CheckGenerated bool - funcDescs *functions.Descriptions - deprecatedPkgs map[*types.Package]string - deprecatedObjs map[types.Object]string -} - -func NewChecker() *Checker { - return &Checker{} -} - -func (*Checker) Name() string { return "staticcheck" } -func (*Checker) Prefix() string { return "SA" } - -func (c *Checker) Checks() []lint.Check { - return []lint.Check{ - {ID: "SA1000", FilterGenerated: false, Fn: c.callChecker(checkRegexpRules), Doc: docSA1000}, - {ID: "SA1001", FilterGenerated: false, Fn: c.CheckTemplate, Doc: docSA1001}, - {ID: "SA1002", FilterGenerated: false, Fn: c.callChecker(checkTimeParseRules), Doc: docSA1002}, - {ID: "SA1003", FilterGenerated: false, Fn: c.callChecker(checkEncodingBinaryRules), Doc: docSA1003}, - {ID: "SA1004", FilterGenerated: false, Fn: c.CheckTimeSleepConstant, Doc: docSA1004}, - {ID: "SA1005", FilterGenerated: false, Fn: c.CheckExec, Doc: docSA1005}, - {ID: "SA1006", FilterGenerated: false, Fn: c.CheckUnsafePrintf, Doc: docSA1006}, - {ID: "SA1007", FilterGenerated: false, Fn: c.callChecker(checkURLsRules), Doc: docSA1007}, - {ID: "SA1008", FilterGenerated: false, Fn: c.CheckCanonicalHeaderKey, Doc: docSA1008}, - {ID: "SA1010", FilterGenerated: false, Fn: c.callChecker(checkRegexpFindAllRules), Doc: docSA1010}, - {ID: "SA1011", FilterGenerated: false, Fn: c.callChecker(checkUTF8CutsetRules), Doc: docSA1011}, - {ID: "SA1012", FilterGenerated: false, Fn: c.CheckNilContext, Doc: docSA1012}, - {ID: "SA1013", FilterGenerated: false, Fn: c.CheckSeeker, Doc: docSA1013}, - {ID: "SA1014", FilterGenerated: false, Fn: c.callChecker(checkUnmarshalPointerRules), Doc: docSA1014}, - {ID: "SA1015", FilterGenerated: false, Fn: c.CheckLeakyTimeTick, Doc: docSA1015}, - {ID: "SA1016", FilterGenerated: false, Fn: c.CheckUntrappableSignal, Doc: docSA1016}, - {ID: "SA1017", FilterGenerated: false, Fn: c.callChecker(checkUnbufferedSignalChanRules), Doc: docSA1017}, - {ID: "SA1018", FilterGenerated: false, Fn: c.callChecker(checkStringsReplaceZeroRules), Doc: docSA1018}, - {ID: "SA1019", FilterGenerated: false, Fn: c.CheckDeprecated, Doc: docSA1019}, - {ID: "SA1020", FilterGenerated: false, Fn: c.callChecker(checkListenAddressRules), Doc: docSA1020}, - {ID: "SA1021", FilterGenerated: false, Fn: c.callChecker(checkBytesEqualIPRules), Doc: docSA1021}, - {ID: "SA1023", FilterGenerated: false, Fn: c.CheckWriterBufferModified, Doc: docSA1023}, - {ID: "SA1024", FilterGenerated: false, Fn: c.callChecker(checkUniqueCutsetRules), Doc: docSA1024}, - {ID: "SA1025", FilterGenerated: false, Fn: c.CheckTimerResetReturnValue, Doc: docSA1025}, - {ID: "SA1026", FilterGenerated: false, Fn: c.callChecker(checkUnsupportedMarshal), Doc: docSA1026}, - {ID: "SA1027", FilterGenerated: false, Fn: c.callChecker(checkAtomicAlignment), Doc: docSA1027}, - - {ID: "SA2000", FilterGenerated: false, Fn: c.CheckWaitgroupAdd, Doc: docSA2000}, - {ID: "SA2001", FilterGenerated: false, Fn: c.CheckEmptyCriticalSection, Doc: docSA2001}, - {ID: "SA2002", FilterGenerated: false, Fn: c.CheckConcurrentTesting, Doc: docSA2002}, - {ID: "SA2003", FilterGenerated: false, Fn: c.CheckDeferLock, Doc: docSA2003}, - - {ID: "SA3000", FilterGenerated: false, Fn: c.CheckTestMainExit, Doc: docSA3000}, - {ID: "SA3001", FilterGenerated: false, Fn: c.CheckBenchmarkN, Doc: docSA3001}, - - {ID: "SA4000", FilterGenerated: false, Fn: c.CheckLhsRhsIdentical, Doc: docSA4000}, - {ID: "SA4001", FilterGenerated: false, Fn: c.CheckIneffectiveCopy, Doc: docSA4001}, - {ID: "SA4002", FilterGenerated: false, Fn: c.CheckDiffSizeComparison, Doc: docSA4002}, - {ID: "SA4003", FilterGenerated: false, Fn: c.CheckExtremeComparison, Doc: docSA4003}, - {ID: "SA4004", FilterGenerated: false, Fn: c.CheckIneffectiveLoop, Doc: docSA4004}, - {ID: "SA4006", FilterGenerated: false, Fn: c.CheckUnreadVariableValues, Doc: docSA4006}, - {ID: "SA4008", FilterGenerated: false, Fn: c.CheckLoopCondition, Doc: docSA4008}, - {ID: "SA4009", FilterGenerated: false, Fn: c.CheckArgOverwritten, Doc: docSA4009}, - {ID: "SA4010", FilterGenerated: false, Fn: c.CheckIneffectiveAppend, Doc: docSA4010}, - {ID: "SA4011", FilterGenerated: false, Fn: c.CheckScopedBreak, Doc: docSA4011}, - {ID: "SA4012", FilterGenerated: false, Fn: c.CheckNaNComparison, Doc: docSA4012}, - {ID: "SA4013", FilterGenerated: false, Fn: c.CheckDoubleNegation, Doc: docSA4013}, - {ID: "SA4014", FilterGenerated: false, Fn: c.CheckRepeatedIfElse, Doc: docSA4014}, - {ID: "SA4015", FilterGenerated: false, Fn: c.callChecker(checkMathIntRules), Doc: docSA4015}, - {ID: "SA4016", FilterGenerated: false, Fn: c.CheckSillyBitwiseOps, Doc: docSA4016}, - {ID: "SA4017", FilterGenerated: false, Fn: c.CheckPureFunctions, Doc: docSA4017}, - {ID: "SA4018", FilterGenerated: true, Fn: c.CheckSelfAssignment, Doc: docSA4018}, - {ID: "SA4019", FilterGenerated: true, Fn: c.CheckDuplicateBuildConstraints, Doc: docSA4019}, - {ID: "SA4020", FilterGenerated: false, Fn: c.CheckUnreachableTypeCases, Doc: docSA4020}, - {ID: "SA4021", FilterGenerated: true, Fn: c.CheckSingleArgAppend, Doc: docSA4021}, - - {ID: "SA5000", FilterGenerated: false, Fn: c.CheckNilMaps, Doc: docSA5000}, - {ID: "SA5001", FilterGenerated: false, Fn: c.CheckEarlyDefer, Doc: docSA5001}, - {ID: "SA5002", FilterGenerated: false, Fn: c.CheckInfiniteEmptyLoop, Doc: docSA5002}, - {ID: "SA5003", FilterGenerated: false, Fn: c.CheckDeferInInfiniteLoop, Doc: docSA5003}, - {ID: "SA5004", FilterGenerated: false, Fn: c.CheckLoopEmptyDefault, Doc: docSA5004}, - {ID: "SA5005", FilterGenerated: false, Fn: c.CheckCyclicFinalizer, Doc: docSA5005}, - {ID: "SA5007", FilterGenerated: false, Fn: c.CheckInfiniteRecursion, Doc: docSA5007}, - {ID: "SA5008", FilterGenerated: false, Fn: c.CheckStructTags, Doc: ``}, - {ID: "SA5009", FilterGenerated: false, Fn: c.callChecker(checkPrintfRules), Doc: ``}, - - {ID: "SA6000", FilterGenerated: false, Fn: c.callChecker(checkRegexpMatchLoopRules), Doc: docSA6000}, - {ID: "SA6001", FilterGenerated: false, Fn: c.CheckMapBytesKey, Doc: docSA6001}, - {ID: "SA6002", FilterGenerated: false, Fn: c.callChecker(checkSyncPoolValueRules), Doc: docSA6002}, - {ID: "SA6003", FilterGenerated: false, Fn: c.CheckRangeStringRunes, Doc: docSA6003}, - // {ID: "SA6004", FilterGenerated: false, Fn: c.CheckSillyRegexp, Doc: docSA6004}, - {ID: "SA6005", FilterGenerated: false, Fn: c.CheckToLowerToUpperComparison, Doc: docSA6005}, - - {ID: "SA9001", FilterGenerated: false, Fn: c.CheckDubiousDeferInChannelRangeLoop, Doc: docSA9001}, - {ID: "SA9002", FilterGenerated: false, Fn: c.CheckNonOctalFileMode, Doc: docSA9002}, - {ID: "SA9003", FilterGenerated: false, Fn: c.CheckEmptyBranch, Doc: docSA9003}, - {ID: "SA9004", FilterGenerated: false, Fn: c.CheckMissingEnumTypesInDeclaration, Doc: docSA9004}, - // Filtering generated code because it may include empty structs generated from data models. - {ID: "SA9005", FilterGenerated: true, Fn: c.callChecker(checkNoopMarshal), Doc: docSA9005}, - } - - // "SA5006": c.CheckSliceOutOfBounds, - // "SA4007": c.CheckPredeterminedBooleanExprs, -} - -func (c *Checker) findDeprecated(prog *lint.Program) { - var names []*ast.Ident - - extractDeprecatedMessage := func(docs []*ast.CommentGroup) string { - for _, doc := range docs { - if doc == nil { - continue - } - parts := strings.Split(doc.Text(), "\n\n") - last := parts[len(parts)-1] - if !strings.HasPrefix(last, "Deprecated: ") { - continue - } - alt := last[len("Deprecated: "):] - alt = strings.Replace(alt, "\n", " ", -1) - return alt - } - return "" - } - doDocs := func(pkg *packages.Package, names []*ast.Ident, docs []*ast.CommentGroup) { - alt := extractDeprecatedMessage(docs) - if alt == "" { - return - } - - for _, name := range names { - obj := pkg.TypesInfo.ObjectOf(name) - c.deprecatedObjs[obj] = alt - } - } - - for _, pkg := range prog.AllPackages { - var docs []*ast.CommentGroup - for _, f := range pkg.Syntax { - docs = append(docs, f.Doc) - } - if alt := extractDeprecatedMessage(docs); alt != "" { - // Don't mark package syscall as deprecated, even though - // it is. A lot of people still use it for simple - // constants like SIGKILL, and I am not comfortable - // telling them to use x/sys for that. - if pkg.PkgPath != "syscall" { - c.deprecatedPkgs[pkg.Types] = alt - } - } - - docs = docs[:0] - for _, f := range pkg.Syntax { - fn := func(node ast.Node) bool { - if node == nil { - return true - } - var ret bool - switch node := node.(type) { - case *ast.GenDecl: - switch node.Tok { - case token.TYPE, token.CONST, token.VAR: - docs = append(docs, node.Doc) - return true - default: - return false - } - case *ast.FuncDecl: - docs = append(docs, node.Doc) - names = []*ast.Ident{node.Name} - ret = false - case *ast.TypeSpec: - docs = append(docs, node.Doc) - names = []*ast.Ident{node.Name} - ret = true - case *ast.ValueSpec: - docs = append(docs, node.Doc) - names = node.Names - ret = false - case *ast.File: - return true - case *ast.StructType: - for _, field := range node.Fields.List { - doDocs(pkg, field.Names, []*ast.CommentGroup{field.Doc}) - } - return false - case *ast.InterfaceType: - for _, field := range node.Methods.List { - doDocs(pkg, field.Names, []*ast.CommentGroup{field.Doc}) - } - return false - default: - return false - } - if len(names) == 0 || len(docs) == 0 { - return ret - } - doDocs(pkg, names, docs) - - docs = docs[:0] - names = nil - return ret - } - ast.Inspect(f, fn) - } - } -} - -func (c *Checker) Init(prog *lint.Program) { - wg := &sync.WaitGroup{} - wg.Add(2) - go func() { - c.funcDescs = functions.NewDescriptions(prog.SSA) - for _, fn := range prog.AllFunctions { - if fn.Blocks != nil { - applyStdlibKnowledge(fn) - ssa.OptimizeBlocks(fn) - } - } - wg.Done() - }() - - go func() { - c.deprecatedPkgs = map[*types.Package]string{} - c.deprecatedObjs = map[types.Object]string{} - c.findDeprecated(prog) - wg.Done() - }() - - wg.Wait() -} - -func (c *Checker) isInLoop(b *ssa.BasicBlock) bool { - sets := c.funcDescs.Get(b.Parent()).Loops +func isInLoop(b *ssa.BasicBlock) bool { + sets := functions.FindLoops(b.Parent()) for _, set := range sets { - if set[b] { + if set.Has(b) { return true } } return false } -func applyStdlibKnowledge(fn *ssa.Function) { - if len(fn.Blocks) == 0 { - return - } - - // comma-ok receiving from a time.Tick channel will never return - // ok == false, so any branching on the value of ok can be - // replaced with an unconditional jump. This will primarily match - // `for range time.Tick(x)` loops, but it can also match - // user-written code. - for _, block := range fn.Blocks { - if len(block.Instrs) < 3 { - continue - } - if len(block.Succs) != 2 { - continue - } - var instrs []*ssa.Instruction - for i, ins := range block.Instrs { - if _, ok := ins.(*ssa.DebugRef); ok { - continue - } - instrs = append(instrs, &block.Instrs[i]) - } - - for i, ins := range instrs { - unop, ok := (*ins).(*ssa.UnOp) - if !ok || unop.Op != token.ARROW { - continue - } - call, ok := unop.X.(*ssa.Call) - if !ok { - continue - } - if !IsCallTo(call.Common(), "time.Tick") { - continue - } - ex, ok := (*instrs[i+1]).(*ssa.Extract) - if !ok || ex.Tuple != unop || ex.Index != 1 { - continue - } - - ifstmt, ok := (*instrs[i+2]).(*ssa.If) - if !ok || ifstmt.Cond != ex { - continue - } - - *instrs[i+2] = ssa.NewJump(block) - succ := block.Succs[1] - block.Succs = block.Succs[0:1] - succ.RemovePred(block) - } - } -} - -func (c *Checker) CheckUntrappableSignal(j *lint.Job) { +func CheckUntrappableSignal(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) - if !IsCallToAnyAST(j, call, + if !IsCallToAnyAST(pass, call, "os/signal.Ignore", "os/signal.Notify", "os/signal.Reset") { return } for _, arg := range call.Args { - if conv, ok := arg.(*ast.CallExpr); ok && isName(j, conv.Fun, "os.Signal") { + if conv, ok := arg.(*ast.CallExpr); ok && isName(pass, conv.Fun, "os.Signal") { arg = conv.Args[0] } - if isName(j, arg, "os.Kill") || isName(j, arg, "syscall.SIGKILL") { - j.Errorf(arg, "%s cannot be trapped (did you mean syscall.SIGTERM?)", Render(j, arg)) + if isName(pass, arg, "os.Kill") || isName(pass, arg, "syscall.SIGKILL") { + ReportNodef(pass, arg, "%s cannot be trapped (did you mean syscall.SIGTERM?)", Render(pass, arg)) } - if isName(j, arg, "syscall.SIGSTOP") { - j.Errorf(arg, "%s signal cannot be trapped", Render(j, arg)) + if isName(pass, arg, "syscall.SIGSTOP") { + ReportNodef(pass, arg, "%s signal cannot be trapped", Render(pass, arg)) } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckTemplate(j *lint.Job) { +func CheckTemplate(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) var kind string - if IsCallToAST(j, call, "(*text/template.Template).Parse") { + if IsCallToAST(pass, call, "(*text/template.Template).Parse") { kind = "text" - } else if IsCallToAST(j, call, "(*html/template.Template).Parse") { + } else if IsCallToAST(pass, call, "(*html/template.Template).Parse") { kind = "html" } else { return } sel := call.Fun.(*ast.SelectorExpr) - if !IsCallToAST(j, sel.X, "text/template.New") && - !IsCallToAST(j, sel.X, "html/template.New") { + if !IsCallToAST(pass, sel.X, "text/template.New") && + !IsCallToAST(pass, sel.X, "html/template.New") { // TODO(dh): this is a cheap workaround for templates with // different delims. A better solution with less false // negatives would use data flow analysis to see where the // template comes from and where it has been return } - s, ok := ExprToString(j, call.Args[Arg("(*text/template.Template).Parse.text")]) + s, ok := ExprToString(pass, call.Args[Arg("(*text/template.Template).Parse.text")]) if !ok { return } @@ -1081,17 +810,18 @@ func (c *Checker) CheckTemplate(j *lint.Job) { if err != nil { // TODO(dominikh): whitelist other parse errors, if any if strings.Contains(err.Error(), "unexpected") { - j.Errorf(call.Args[Arg("(*text/template.Template).Parse.text")], "%s", err) + ReportNodef(pass, call.Args[Arg("(*text/template.Template).Parse.text")], "%s", err) } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckTimeSleepConstant(j *lint.Job) { +func CheckTimeSleepConstant(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) - if !IsCallToAST(j, call, "time.Sleep") { + if !IsCallToAST(pass, call, "time.Sleep") { return } lit, ok := call.Args[Arg("time.Sleep.d")].(*ast.BasicLit) @@ -1112,13 +842,14 @@ func (c *Checker) CheckTimeSleepConstant(j *lint.Job) { if n != 1 { recommendation = fmt.Sprintf("time.Sleep(%d * time.Nanosecond)", n) } - j.Errorf(call.Args[Arg("time.Sleep.d")], + ReportNodef(pass, call.Args[Arg("time.Sleep.d")], "sleeping for %d nanoseconds is probably a bug. Be explicit if it isn't: %s", n, recommendation) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckWaitgroupAdd(j *lint.Job) { +func CheckWaitgroupAdd(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { g := node.(*ast.GoStmt) fun, ok := g.Call.Fun.(*ast.FuncLit) @@ -1132,27 +863,16 @@ func (c *Checker) CheckWaitgroupAdd(j *lint.Job) { if !ok { return } - call, ok := stmt.X.(*ast.CallExpr) - if !ok { - return - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - return - } - fn, ok := j.Pkg.TypesInfo.ObjectOf(sel.Sel).(*types.Func) - if !ok { - return - } - if lint.FuncName(fn) == "(*sync.WaitGroup).Add" { - j.Errorf(sel, "should call %s before starting the goroutine to avoid a race", - Render(j, stmt)) + if IsCallToAST(pass, stmt.X, "(*sync.WaitGroup).Add") { + ReportNodef(pass, stmt, "should call %s before starting the goroutine to avoid a race", + Render(pass, stmt)) } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.GoStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.GoStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckInfiniteEmptyLoop(j *lint.Job) { +func CheckInfiniteEmptyLoop(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { loop := node.(*ast.ForStmt) if len(loop.Body.List) != 0 || loop.Post != nil { @@ -1181,21 +901,22 @@ func (c *Checker) CheckInfiniteEmptyLoop(j *lint.Job) { return } if ident, ok := loop.Cond.(*ast.Ident); ok { - if k, ok := j.Pkg.TypesInfo.ObjectOf(ident).(*types.Const); ok { + if k, ok := pass.TypesInfo.ObjectOf(ident).(*types.Const); ok { if !constant.BoolVal(k.Val()) { // don't flag `for false {}` loops. They're a debug aid. return } } } - j.Errorf(loop, "loop condition never changes or has a race condition") + ReportNodef(pass, loop, "loop condition never changes or has a race condition") } - j.Errorf(loop, "this loop will spin, using 100%% CPU") + ReportNodef(pass, loop, "this loop will spin, using 100%% CPU") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckDeferInInfiniteLoop(j *lint.Job) { +func CheckDeferInInfiniteLoop(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { mightExit := false var defers []ast.Stmt @@ -1207,6 +928,7 @@ func (c *Checker) CheckDeferInInfiniteLoop(j *lint.Job) { switch stmt := node.(type) { case *ast.ReturnStmt: mightExit = true + return false case *ast.BranchStmt: // TODO(dominikh): if this sees a break in a switch or // select, it doesn't check if it breaks the loop or @@ -1214,6 +936,7 @@ func (c *Checker) CheckDeferInInfiniteLoop(j *lint.Job) { // negatives. if stmt.Tok == token.BREAK { mightExit = true + return false } case *ast.DeferStmt: defers = append(defers, stmt) @@ -1228,16 +951,17 @@ func (c *Checker) CheckDeferInInfiniteLoop(j *lint.Job) { return } for _, stmt := range defers { - j.Errorf(stmt, "defers in this infinite loop will never run") + ReportNodef(pass, stmt, "defers in this infinite loop will never run") } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckDubiousDeferInChannelRangeLoop(j *lint.Job) { +func CheckDubiousDeferInChannelRangeLoop(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { loop := node.(*ast.RangeStmt) - typ := j.Pkg.TypesInfo.TypeOf(loop.X) + typ := pass.TypesInfo.TypeOf(loop.X) _, ok := typ.Underlying().(*types.Chan) if !ok { return @@ -1245,7 +969,7 @@ func (c *Checker) CheckDubiousDeferInChannelRangeLoop(j *lint.Job) { fn2 := func(node ast.Node) bool { switch stmt := node.(type) { case *ast.DeferStmt: - j.Errorf(stmt, "defers in this range loop won't run unless the channel gets closed") + ReportNodef(pass, stmt, "defers in this range loop won't run unless the channel gets closed") case *ast.FuncLit: // Don't look into function bodies return false @@ -1254,23 +978,48 @@ func (c *Checker) CheckDubiousDeferInChannelRangeLoop(j *lint.Job) { } ast.Inspect(loop.Body, fn2) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.RangeStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.RangeStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckTestMainExit(j *lint.Job) { - fn := func(node ast.Node) { - if !isTestMain(j, node) { - return +func CheckTestMainExit(pass *analysis.Pass) (interface{}, error) { + var ( + fnmain ast.Node + callsExit bool + callsRun bool + arg types.Object + ) + fn := func(node ast.Node, push bool) bool { + if !push { + if fnmain != nil && node == fnmain { + if !callsExit && callsRun { + ReportNodef(pass, fnmain, "TestMain should call os.Exit to set exit code") + } + fnmain = nil + callsExit = false + callsRun = false + arg = nil + } + return true } - arg := j.Pkg.TypesInfo.ObjectOf(node.(*ast.FuncDecl).Type.Params.List[0].Names[0]) - callsRun := false - fn2 := func(node ast.Node) bool { - call, ok := node.(*ast.CallExpr) - if !ok { + switch node := node.(type) { + case *ast.FuncDecl: + if fnmain != nil { return true } - sel, ok := call.Fun.(*ast.SelectorExpr) + if !isTestMain(pass, node) { + return false + } + fnmain = node + arg = pass.TypesInfo.ObjectOf(node.Type.Params.List[0].Names[0]) + return true + case *ast.CallExpr: + if IsCallToAST(pass, node, "os.Exit") { + callsExit = true + return false + } + sel, ok := node.Fun.(*ast.SelectorExpr) if !ok { return true } @@ -1278,7 +1027,7 @@ func (c *Checker) CheckTestMainExit(j *lint.Job) { if !ok { return true } - if arg != j.Pkg.TypesInfo.ObjectOf(ident) { + if arg != pass.TypesInfo.ObjectOf(ident) { return true } if sel.Sel.Name == "Run" { @@ -1286,30 +1035,16 @@ func (c *Checker) CheckTestMainExit(j *lint.Job) { return false } return true - } - ast.Inspect(node.(*ast.FuncDecl).Body, fn2) - - callsExit := false - fn3 := func(node ast.Node) bool { - if IsCallToAST(j, node, "os.Exit") { - callsExit = true - return false - } + default: + // unreachable return true } - ast.Inspect(node.(*ast.FuncDecl).Body, fn3) - if !callsExit && callsRun { - j.Errorf(node, "TestMain should call os.Exit to set exit code") - } } - j.Pkg.Inspector.Preorder(nil, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.FuncDecl)(nil), (*ast.CallExpr)(nil)}, fn) + return nil, nil } -func isTestMain(j *lint.Job, node ast.Node) bool { - decl, ok := node.(*ast.FuncDecl) - if !ok { - return false - } +func isTestMain(pass *analysis.Pass, decl *ast.FuncDecl) bool { if decl.Name.Name != "TestMain" { return false } @@ -1320,29 +1055,30 @@ func isTestMain(j *lint.Job, node ast.Node) bool { if len(arg.Names) != 1 { return false } - return IsOfType(j, arg.Type, "*testing.M") + return IsOfType(pass, arg.Type, "*testing.M") } -func (c *Checker) CheckExec(j *lint.Job) { +func CheckExec(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) - if !IsCallToAST(j, call, "os/exec.Command") { + if !IsCallToAST(pass, call, "os/exec.Command") { return } - val, ok := ExprToString(j, call.Args[Arg("os/exec.Command.name")]) + val, ok := ExprToString(pass, call.Args[Arg("os/exec.Command.name")]) if !ok { return } if !strings.Contains(val, " ") || strings.Contains(val, `\`) || strings.Contains(val, "/") { return } - j.Errorf(call.Args[Arg("os/exec.Command.name")], + ReportNodef(pass, call.Args[Arg("os/exec.Command.name")], "first argument to exec.Command looks like a shell command, but a program name or path are expected") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckLoopEmptyDefault(j *lint.Job) { +func CheckLoopEmptyDefault(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { loop := node.(*ast.ForStmt) if len(loop.Body.List) != 1 || loop.Cond != nil || loop.Init != nil { @@ -1354,19 +1090,20 @@ func (c *Checker) CheckLoopEmptyDefault(j *lint.Job) { } for _, c := range sel.Body.List { if comm, ok := c.(*ast.CommClause); ok && comm.Comm == nil && len(comm.Body) == 0 { - j.Errorf(comm, "should not have an empty default case in a for+select loop. The loop will spin.") + ReportNodef(pass, comm, "should not have an empty default case in a for+select loop. The loop will spin.") } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckLhsRhsIdentical(j *lint.Job) { +func CheckLhsRhsIdentical(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { op := node.(*ast.BinaryExpr) switch op.Op { case token.EQL, token.NEQ: - if basic, ok := j.Pkg.TypesInfo.TypeOf(op.X).Underlying().(*types.Basic); ok { + if basic, ok := pass.TypesInfo.TypeOf(op.X).Underlying().(*types.Basic); ok { if kind := basic.Kind(); kind == types.Float32 || kind == types.Float64 { // f == f and f != f might be used to check for NaN return @@ -1380,12 +1117,12 @@ func (c *Checker) CheckLhsRhsIdentical(j *lint.Job) { return } - if Render(j, op.X) != Render(j, op.Y) { + if Render(pass, op.X) != Render(pass, op.Y) { return } l1, ok1 := op.X.(*ast.BasicLit) l2, ok2 := op.Y.(*ast.BasicLit) - if ok1 && ok2 && l1.Kind == token.INT && l2.Kind == l1.Kind && l1.Value == "0" && l2.Value == l1.Value && IsGenerated(j.File(l1)) { + if ok1 && ok2 && l1.Kind == token.INT && l2.Kind == l1.Kind && l1.Value == "0" && l2.Value == l1.Value && IsGenerated(pass, l1.Pos()) { // cgo generates the following function call: // _cgoCheckPointer(_cgoBase0, 0 == 0) – it uses 0 == 0 // instead of true in case the user shadowed the @@ -1398,12 +1135,13 @@ func (c *Checker) CheckLhsRhsIdentical(j *lint.Job) { // 0 == 0 are slim. return } - j.Errorf(op, "identical expressions on the left and right side of the '%s' operator", op.Op) + ReportNodef(pass, op, "identical expressions on the left and right side of the '%s' operator", op.Op) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckScopedBreak(j *lint.Job) { +func CheckScopedBreak(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { var body *ast.BlockStmt switch node := node.(type) { @@ -1453,21 +1191,22 @@ func (c *Checker) CheckScopedBreak(j *lint.Job) { if !ok || branch.Tok != token.BREAK || branch.Label != nil { continue } - j.Errorf(branch, "ineffective break statement. Did you mean to break out of the outer loop?") + ReportNodef(pass, branch, "ineffective break statement. Did you mean to break out of the outer loop?") } } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.ForStmt)(nil), (*ast.RangeStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil), (*ast.RangeStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckUnsafePrintf(j *lint.Job) { +func CheckUnsafePrintf(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) var arg int - if IsCallToAnyAST(j, call, "fmt.Printf", "fmt.Sprintf", "log.Printf") { + if IsCallToAnyAST(pass, call, "fmt.Printf", "fmt.Sprintf", "log.Printf") { arg = Arg("fmt.Printf.format") - } else if IsCallToAnyAST(j, call, "fmt.Fprintf") { + } else if IsCallToAnyAST(pass, call, "fmt.Fprintf") { arg = Arg("fmt.Fprintf.format") } else { return @@ -1480,13 +1219,14 @@ func (c *Checker) CheckUnsafePrintf(j *lint.Job) { default: return } - j.Errorf(call.Args[arg], + ReportNodef(pass, call.Args[arg], "printf-style function with dynamic format string and no further arguments should use print-style function instead") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckEarlyDefer(j *lint.Job) { +func CheckEarlyDefer(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { block := node.(*ast.BlockStmt) if len(block.List) < 2 { @@ -1513,7 +1253,7 @@ func (c *Checker) CheckEarlyDefer(j *lint.Job) { if !ok { continue } - sig, ok := j.Pkg.TypesInfo.TypeOf(call.Fun).(*types.Signature) + sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature) if !ok { continue } @@ -1548,10 +1288,11 @@ func (c *Checker) CheckEarlyDefer(j *lint.Job) { if sel.Sel.Name != "Close" { continue } - j.Errorf(def, "should check returned error before deferring %s", Render(j, def.Call)) + ReportNodef(pass, def, "should check returned error before deferring %s", Render(pass, def.Call)) } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BlockStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BlockStmt)(nil)}, fn) + return nil, nil } func selectorX(sel *ast.SelectorExpr) ast.Node { @@ -1563,7 +1304,7 @@ func selectorX(sel *ast.SelectorExpr) ast.Node { } } -func (c *Checker) CheckEmptyCriticalSection(j *lint.Job) { +func CheckEmptyCriticalSection(pass *analysis.Pass) (interface{}, error) { // Initially it might seem like this check would be easier to // implement in SSA. After all, we're only checking for two // consecutive method calls. In reality, however, there may be any @@ -1589,7 +1330,7 @@ func (c *Checker) CheckEmptyCriticalSection(j *lint.Job) { return nil, "", false } - fn, ok := j.Pkg.TypesInfo.ObjectOf(sel.Sel).(*types.Func) + fn, ok := pass.TypesInfo.ObjectOf(sel.Sel).(*types.Func) if !ok { return nil, "", false } @@ -1610,44 +1351,47 @@ func (c *Checker) CheckEmptyCriticalSection(j *lint.Job) { sel1, method1, ok1 := mutexParams(block.List[i]) sel2, method2, ok2 := mutexParams(block.List[i+1]) - if !ok1 || !ok2 || Render(j, sel1) != Render(j, sel2) { + if !ok1 || !ok2 || Render(pass, sel1) != Render(pass, sel2) { continue } if (method1 == "Lock" && method2 == "Unlock") || (method1 == "RLock" && method2 == "RUnlock") { - j.Errorf(block.List[i+1], "empty critical section") + ReportNodef(pass, block.List[i+1], "empty critical section") } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BlockStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BlockStmt)(nil)}, fn) + return nil, nil } // cgo produces code like fn(&*_Cvar_kSomeCallbacks) which we don't // want to flag. var cgoIdent = regexp.MustCompile(`^_C(func|var)_.+$`) -func (c *Checker) CheckIneffectiveCopy(j *lint.Job) { +func CheckIneffectiveCopy(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { if unary, ok := node.(*ast.UnaryExpr); ok { if star, ok := unary.X.(*ast.StarExpr); ok && unary.Op == token.AND { ident, ok := star.X.(*ast.Ident) if !ok || !cgoIdent.MatchString(ident.Name) { - j.Errorf(unary, "&*x will be simplified to x. It will not copy x.") + ReportNodef(pass, unary, "&*x will be simplified to x. It will not copy x.") } } } if star, ok := node.(*ast.StarExpr); ok { if unary, ok := star.X.(*ast.UnaryExpr); ok && unary.Op == token.AND { - j.Errorf(star, "*&x will be simplified to x. It will not copy x.") + ReportNodef(pass, star, "*&x will be simplified to x. It will not copy x.") } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.UnaryExpr)(nil), (*ast.StarExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.UnaryExpr)(nil), (*ast.StarExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckDiffSizeComparison(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { +func CheckDiffSizeComparison(pass *analysis.Pass) (interface{}, error) { + ranges := pass.ResultOf[valueRangesAnalyzer].(map[*ssa.Function]vrp.Ranges) + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { for _, b := range ssafn.Blocks { for _, ins := range b.Instrs { binop, ok := ins.(*ssa.BinOp) @@ -1662,22 +1406,26 @@ func (c *Checker) CheckDiffSizeComparison(j *lint.Job) { if !ok1 && !ok2 { continue } - r := c.funcDescs.Get(ssafn).Ranges + r := ranges[ssafn] r1, ok1 := r.Get(binop.X).(vrp.StringInterval) r2, ok2 := r.Get(binop.Y).(vrp.StringInterval) if !ok1 || !ok2 { continue } if r1.Length.Intersection(r2.Length).Empty() { - j.Errorf(binop, "comparing strings of different sizes for equality will always return false") + pass.Reportf(binop.Pos(), "comparing strings of different sizes for equality will always return false") } } } } + return nil, nil } -func (c *Checker) CheckCanonicalHeaderKey(j *lint.Job) { - fn := func(node ast.Node, _ bool) bool { +func CheckCanonicalHeaderKey(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node, push bool) bool { + if !push { + return false + } assign, ok := node.(*ast.AssignStmt) if ok { // TODO(dh): This risks missing some Header reads, for @@ -1688,7 +1436,7 @@ func (c *Checker) CheckCanonicalHeaderKey(j *lint.Job) { if !ok { continue } - if IsOfType(j, op.X, "net/http.Header") { + if IsOfType(pass, op.X, "net/http.Header") { return false } } @@ -1698,23 +1446,24 @@ func (c *Checker) CheckCanonicalHeaderKey(j *lint.Job) { if !ok { return true } - if !IsOfType(j, op.X, "net/http.Header") { + if !IsOfType(pass, op.X, "net/http.Header") { return true } - s, ok := ExprToString(j, op.Index) + s, ok := ExprToString(pass, op.Index) if !ok { return true } if s == http.CanonicalHeaderKey(s) { return true } - j.Errorf(op, "keys in http.Header are canonicalized, %q is not canonical; fix the constant or use http.CanonicalHeaderKey", s) + ReportNodef(pass, op, "keys in http.Header are canonicalized, %q is not canonical; fix the constant or use http.CanonicalHeaderKey", s) return true } - j.Pkg.Inspector.Nodes([]ast.Node{(*ast.AssignStmt)(nil), (*ast.IndexExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.AssignStmt)(nil), (*ast.IndexExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckBenchmarkN(j *lint.Job) { +func CheckBenchmarkN(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { assign := node.(*ast.AssignStmt) if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { @@ -1727,16 +1476,17 @@ func (c *Checker) CheckBenchmarkN(j *lint.Job) { if sel.Sel.Name != "N" { return } - if !IsOfType(j, sel.X, "*testing.B") { + if !IsOfType(pass, sel.X, "*testing.B") { return } - j.Errorf(assign, "should not assign to %s", Render(j, sel)) + ReportNodef(pass, assign, "should not assign to %s", Render(pass, sel)) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckUnreadVariableValues(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { +func CheckUnreadVariableValues(pass *analysis.Pass) (interface{}, error) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { if IsExample(ssafn) { continue } @@ -1744,6 +1494,35 @@ func (c *Checker) CheckUnreadVariableValues(j *lint.Job) { if node == nil { continue } + if gen, ok := Generator(pass, node.Pos()); ok && gen == facts.Goyacc { + // Don't flag unused values in code generated by goyacc. + // There may be hundreds of those due to the way the state + // machine is constructed. + continue + } + + switchTags := map[ssa.Value]struct{}{} + ast.Inspect(node, func(node ast.Node) bool { + s, ok := node.(*ast.SwitchStmt) + if !ok { + return true + } + v, _ := ssafn.ValueForExpr(s.Tag) + switchTags[v] = struct{}{} + return true + }) + + hasUse := func(v ssa.Value) bool { + if _, ok := switchTags[v]; ok { + return true + } + refs := v.Referrers() + if refs == nil { + // TODO investigate why refs can be nil + return true + } + return len(FilterDebug(*refs)) > 0 + } ast.Inspect(node, func(node ast.Node) bool { assign, ok := node.(*ast.AssignStmt) @@ -1767,16 +1546,12 @@ func (c *Checker) CheckUnreadVariableValues(j *lint.Job) { if !ok { continue } - exrefs := ex.Referrers() - if exrefs == nil { - continue - } - if len(FilterDebug(*exrefs)) == 0 { + if !hasUse(ex) { lhs := assign.Lhs[ex.Index] if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" { continue } - j.Errorf(lhs, "this value of %s is never used", lhs) + ReportNodef(pass, lhs, "this value of %s is never used", lhs) } } return true @@ -1791,22 +1566,18 @@ func (c *Checker) CheckUnreadVariableValues(j *lint.Job) { continue } - refs := val.Referrers() - if refs == nil { - // TODO investigate why refs can be nil - return true - } - if len(FilterDebug(*refs)) == 0 { - j.Errorf(lhs, "this value of %s is never used", lhs) + if !hasUse(val) { + ReportNodef(pass, lhs, "this value of %s is never used", lhs) } } return true }) } + return nil, nil } -func (c *Checker) CheckPredeterminedBooleanExprs(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { +func CheckPredeterminedBooleanExprs(pass *analysis.Pass) (interface{}, error) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { for _, block := range ssafn.Blocks { for _, ins := range block.Instrs { ssabinop, ok := ins.(*ssa.BinOp) @@ -1841,16 +1612,17 @@ func (c *Checker) CheckPredeterminedBooleanExprs(j *lint.Job) { } b := trues != 0 if trues == 0 || trues == len(xs)*len(ys) { - j.Errorf(ssabinop, "binary expression is always %t for all possible values (%s %s %s)", + pass.Reportf(ssabinop.Pos(), "binary expression is always %t for all possible values (%s %s %s)", b, xs, ssabinop.Op, ys) } } } } + return nil, nil } -func (c *Checker) CheckNilMaps(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { +func CheckNilMaps(pass *analysis.Pass) (interface{}, error) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { for _, block := range ssafn.Blocks { for _, ins := range block.Instrs { mu, ok := ins.(*ssa.MapUpdate) @@ -1864,24 +1636,25 @@ func (c *Checker) CheckNilMaps(j *lint.Job) { if c.Value != nil { continue } - j.Errorf(mu, "assignment to nil map") + pass.Reportf(mu.Pos(), "assignment to nil map") } } } + return nil, nil } -func (c *Checker) CheckExtremeComparison(j *lint.Job) { +func CheckExtremeComparison(pass *analysis.Pass) (interface{}, error) { isobj := func(expr ast.Expr, name string) bool { sel, ok := expr.(*ast.SelectorExpr) if !ok { return false } - return IsObject(j.Pkg.TypesInfo.ObjectOf(sel.Sel), name) + return IsObject(pass.TypesInfo.ObjectOf(sel.Sel), name) } fn := func(node ast.Node) { expr := node.(*ast.BinaryExpr) - tx := j.Pkg.TypesInfo.TypeOf(expr.X) + tx := pass.TypesInfo.TypeOf(expr.X) basic, ok := tx.Underlying().(*types.Basic) if !ok { return @@ -1921,35 +1694,36 @@ func (c *Checker) CheckExtremeComparison(j *lint.Job) { if (expr.Op == token.GTR || expr.Op == token.GEQ) && isobj(expr.Y, max) || (expr.Op == token.LSS || expr.Op == token.LEQ) && isobj(expr.X, max) { - j.Errorf(expr, "no value of type %s is greater than %s", basic, max) + ReportNodef(pass, expr, "no value of type %s is greater than %s", basic, max) } if expr.Op == token.LEQ && isobj(expr.Y, max) || expr.Op == token.GEQ && isobj(expr.X, max) { - j.Errorf(expr, "every value of type %s is <= %s", basic, max) + ReportNodef(pass, expr, "every value of type %s is <= %s", basic, max) } if (basic.Info() & types.IsUnsigned) != 0 { if (expr.Op == token.LSS || expr.Op == token.LEQ) && IsIntLiteral(expr.Y, "0") || (expr.Op == token.GTR || expr.Op == token.GEQ) && IsIntLiteral(expr.X, "0") { - j.Errorf(expr, "no value of type %s is less than 0", basic) + ReportNodef(pass, expr, "no value of type %s is less than 0", basic) } if expr.Op == token.GEQ && IsIntLiteral(expr.Y, "0") || expr.Op == token.LEQ && IsIntLiteral(expr.X, "0") { - j.Errorf(expr, "every value of type %s is >= 0", basic) + ReportNodef(pass, expr, "every value of type %s is >= 0", basic) } } else { if (expr.Op == token.LSS || expr.Op == token.LEQ) && isobj(expr.Y, min) || (expr.Op == token.GTR || expr.Op == token.GEQ) && isobj(expr.X, min) { - j.Errorf(expr, "no value of type %s is less than %s", basic, min) + ReportNodef(pass, expr, "no value of type %s is less than %s", basic, min) } if expr.Op == token.GEQ && isobj(expr.Y, min) || expr.Op == token.LEQ && isobj(expr.X, min) { - j.Errorf(expr, "every value of type %s is >= %s", basic, min) + ReportNodef(pass, expr, "every value of type %s is >= %s", basic, min) } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + return nil, nil } func consts(val ssa.Value, out []*ssa.Const, visitedPhis map[string]bool) ([]*ssa.Const, bool) { @@ -1993,8 +1767,8 @@ func consts(val ssa.Value, out []*ssa.Const, visitedPhis map[string]bool) ([]*ss return uniq, true } -func (c *Checker) CheckLoopCondition(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { +func CheckLoopCondition(pass *analysis.Pass) (interface{}, error) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { fn := func(node ast.Node) bool { loop, ok := node.(*ast.ForStmt) if !ok { @@ -2050,16 +1824,17 @@ func (c *Checker) CheckLoopCondition(j *lint.Job) { case *ssa.UnOp: return true } - j.Errorf(cond, "variable in loop condition never changes") + ReportNodef(pass, cond, "variable in loop condition never changes") return true } Inspect(ssafn.Syntax(), fn) } + return nil, nil } -func (c *Checker) CheckArgOverwritten(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { +func CheckArgOverwritten(pass *analysis.Pass) (interface{}, error) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { fn := func(node ast.Node) bool { var typ *ast.FuncType var body *ast.BlockStmt @@ -2079,7 +1854,7 @@ func (c *Checker) CheckArgOverwritten(j *lint.Job) { } for _, field := range typ.Params.List { for _, arg := range field.Names { - obj := j.Pkg.TypesInfo.ObjectOf(arg) + obj := pass.TypesInfo.ObjectOf(arg) var ssaobj *ssa.Parameter for _, param := range ssafn.Params { if param.Object() == obj { @@ -2109,7 +1884,7 @@ func (c *Checker) CheckArgOverwritten(j *lint.Job) { if !ok { continue } - if j.Pkg.TypesInfo.ObjectOf(ident) == obj { + if pass.TypesInfo.ObjectOf(ident) == obj { assigned = true return false } @@ -2117,7 +1892,7 @@ func (c *Checker) CheckArgOverwritten(j *lint.Job) { return true }) if assigned { - j.Errorf(arg, "argument %s is overwritten before first use", arg) + ReportNodef(pass, arg, "argument %s is overwritten before first use", arg) } } } @@ -2125,9 +1900,10 @@ func (c *Checker) CheckArgOverwritten(j *lint.Job) { } Inspect(ssafn.Syntax(), fn) } + return nil, nil } -func (c *Checker) CheckIneffectiveLoop(j *lint.Job) { +func CheckIneffectiveLoop(pass *analysis.Pass) (interface{}, error) { // This check detects some, but not all unconditional loop exits. // We give up in the following cases: // @@ -2167,7 +1943,7 @@ func (c *Checker) CheckIneffectiveLoop(j *lint.Job) { body = node.Body loop = node case *ast.RangeStmt: - typ := j.Pkg.TypesInfo.TypeOf(node.X) + typ := pass.TypesInfo.TypeOf(node.X) if _, ok := typ.Underlying().(*types.Map); ok { // looping once over a map is a valid pattern for // getting an arbitrary element. @@ -2227,24 +2003,25 @@ func (c *Checker) CheckIneffectiveLoop(j *lint.Job) { return true }) if unconditionalExit != nil { - j.Errorf(unconditionalExit, "the surrounding loop is unconditionally terminated") + ReportNodef(pass, unconditionalExit, "the surrounding loop is unconditionally terminated") } return true }) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckNilContext(j *lint.Job) { +func CheckNilContext(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) if len(call.Args) == 0 { return } - if typ, ok := j.Pkg.TypesInfo.TypeOf(call.Args[0]).(*types.Basic); !ok || typ.Kind() != types.UntypedNil { + if typ, ok := pass.TypesInfo.TypeOf(call.Args[0]).(*types.Basic); !ok || typ.Kind() != types.UntypedNil { return } - sig, ok := j.Pkg.TypesInfo.TypeOf(call.Fun).(*types.Signature) + sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature) if !ok { return } @@ -2254,13 +2031,14 @@ func (c *Checker) CheckNilContext(j *lint.Job) { if !IsType(sig.Params().At(0).Type(), "context.Context") { return } - j.Errorf(call.Args[0], + ReportNodef(pass, call.Args[0], "do not pass a nil Context, even if a function permits it; pass context.TODO if you are unsure about which Context to use") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckSeeker(j *lint.Job) { +func CheckSeeker(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) sel, ok := call.Fun.(*ast.SelectorExpr) @@ -2289,12 +2067,13 @@ func (c *Checker) CheckSeeker(j *lint.Job) { if pkg.Name != "io" { return } - j.Errorf(call, "the first argument of io.Seeker is the offset, but an io.Seek* constant is being used instead") + ReportNodef(pass, call, "the first argument of io.Seeker is the offset, but an io.Seek* constant is being used instead") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckIneffectiveAppend(j *lint.Job) { +func CheckIneffectiveAppend(pass *analysis.Pass) (interface{}, error) { isAppend := func(ins ssa.Value) bool { call, ok := ins.(*ssa.Call) if !ok { @@ -2309,7 +2088,7 @@ func (c *Checker) CheckIneffectiveAppend(j *lint.Job) { return true } - for _, ssafn := range j.Pkg.InitialFunctions { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { for _, block := range ssafn.Blocks { for _, ins := range block.Instrs { val, ok := ins.(ssa.Value) @@ -2353,15 +2132,16 @@ func (c *Checker) CheckIneffectiveAppend(j *lint.Job) { } walkRefs(*refs) if !isUsed { - j.Errorf(ins, "this result of append is never used, except maybe in other appends") + pass.Reportf(ins.Pos(), "this result of append is never used, except maybe in other appends") } } } } + return nil, nil } -func (c *Checker) CheckConcurrentTesting(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { +func CheckConcurrentTesting(pass *analysis.Pass) (interface{}, error) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { for _, block := range ssafn.Blocks { for _, ins := range block.Instrs { gostmt, ok := ins.(*ssa.Go) @@ -2410,53 +2190,68 @@ func (c *Checker) CheckConcurrentTesting(j *lint.Job) { default: continue } - j.Errorf(gostmt, "the goroutine calls T.%s, which must be called in the same goroutine as the test", name) + pass.Reportf(gostmt.Pos(), "the goroutine calls T.%s, which must be called in the same goroutine as the test", name) } } } } } + return nil, nil } -func (c *Checker) CheckCyclicFinalizer(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { - node := c.funcDescs.CallGraph.CreateNode(ssafn) - for _, edge := range node.Out { - if edge.Callee.Func.RelString(nil) != "runtime.SetFinalizer" { - continue - } - arg0 := edge.Site.Common().Args[Arg("runtime.SetFinalizer.obj")] - if iface, ok := arg0.(*ssa.MakeInterface); ok { - arg0 = iface.X - } - unop, ok := arg0.(*ssa.UnOp) - if !ok { - continue - } - v, ok := unop.X.(*ssa.Alloc) - if !ok { - continue - } - arg1 := edge.Site.Common().Args[Arg("runtime.SetFinalizer.finalizer")] - if iface, ok := arg1.(*ssa.MakeInterface); ok { - arg1 = iface.X - } - mc, ok := arg1.(*ssa.MakeClosure) - if !ok { - continue - } - for _, b := range mc.Bindings { - if b == v { - pos := lint.DisplayPosition(j.Pkg.Fset, mc.Fn.Pos()) - j.Errorf(edge.Site, "the finalizer closes over the object, preventing the finalizer from ever running (at %s)", pos) +func eachCall(ssafn *ssa.Function, fn func(caller *ssa.Function, site ssa.CallInstruction, callee *ssa.Function)) { + for _, b := range ssafn.Blocks { + for _, instr := range b.Instrs { + if site, ok := instr.(ssa.CallInstruction); ok { + if g := site.Common().StaticCallee(); g != nil { + fn(ssafn, site, g) } } } } } -func (c *Checker) CheckSliceOutOfBounds(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { +func CheckCyclicFinalizer(pass *analysis.Pass) (interface{}, error) { + fn := func(caller *ssa.Function, site ssa.CallInstruction, callee *ssa.Function) { + if callee.RelString(nil) != "runtime.SetFinalizer" { + return + } + arg0 := site.Common().Args[Arg("runtime.SetFinalizer.obj")] + if iface, ok := arg0.(*ssa.MakeInterface); ok { + arg0 = iface.X + } + unop, ok := arg0.(*ssa.UnOp) + if !ok { + return + } + v, ok := unop.X.(*ssa.Alloc) + if !ok { + return + } + arg1 := site.Common().Args[Arg("runtime.SetFinalizer.finalizer")] + if iface, ok := arg1.(*ssa.MakeInterface); ok { + arg1 = iface.X + } + mc, ok := arg1.(*ssa.MakeClosure) + if !ok { + return + } + for _, b := range mc.Bindings { + if b == v { + pos := lint.DisplayPosition(pass.Fset, mc.Fn.Pos()) + pass.Reportf(site.Pos(), "the finalizer closes over the object, preventing the finalizer from ever running (at %s)", pos) + } + } + } + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { + eachCall(ssafn, fn) + } + return nil, nil +} + +/* +func CheckSliceOutOfBounds(pass *analysis.Pass) (interface{}, error) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { for _, block := range ssafn.Blocks { for _, ins := range block.Instrs { ia, ok := ins.(*ssa.IndexAddr) @@ -2472,15 +2267,17 @@ func (c *Checker) CheckSliceOutOfBounds(j *lint.Job) { continue } if idxr.Lower.Cmp(sr.Length.Upper) >= 0 { - j.Errorf(ia, "index out of bounds") + ReportNodef(pass, ia, "index out of bounds") } } } } + return nil, nil } +*/ -func (c *Checker) CheckDeferLock(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { +func CheckDeferLock(pass *analysis.Pass) (interface{}, error) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { for _, block := range ssafn.Blocks { instrs := FilterDebug(block.Instrs) if len(instrs) < 2 { @@ -2512,13 +2309,14 @@ func (c *Checker) CheckDeferLock(j *lint.Job) { case "RLock": alt = "RUnlock" } - j.Errorf(nins, "deferring %s right after having locked already; did you mean to defer %s?", name, alt) + pass.Reportf(nins.Pos(), "deferring %s right after having locked already; did you mean to defer %s?", name, alt) } } } + return nil, nil } -func (c *Checker) CheckNaNComparison(j *lint.Job) { +func CheckNaNComparison(pass *analysis.Pass) (interface{}, error) { isNaN := func(v ssa.Value) bool { call, ok := v.(*ssa.Call) if !ok { @@ -2526,7 +2324,7 @@ func (c *Checker) CheckNaNComparison(j *lint.Job) { } return IsCallTo(call.Common(), "math.NaN") } - for _, ssafn := range j.Pkg.InitialFunctions { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { for _, block := range ssafn.Blocks { for _, ins := range block.Instrs { ins, ok := ins.(*ssa.BinOp) @@ -2534,27 +2332,27 @@ func (c *Checker) CheckNaNComparison(j *lint.Job) { continue } if isNaN(ins.X) || isNaN(ins.Y) { - j.Errorf(ins, "no value is equal to NaN, not even NaN itself") + pass.Reportf(ins.Pos(), "no value is equal to NaN, not even NaN itself") } } } } + return nil, nil } -func (c *Checker) CheckInfiniteRecursion(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { - node := c.funcDescs.CallGraph.CreateNode(ssafn) - for _, edge := range node.Out { - if edge.Callee != node { - continue +func CheckInfiniteRecursion(pass *analysis.Pass) (interface{}, error) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { + eachCall(ssafn, func(caller *ssa.Function, site ssa.CallInstruction, callee *ssa.Function) { + if callee != ssafn { + return } - if _, ok := edge.Site.(*ssa.Go); ok { + if _, ok := site.(*ssa.Go); ok { // Recursively spawning goroutines doesn't consume // stack space infinitely, so don't flag it. - continue + return } - block := edge.Site.Block() + block := site.Block() canReturn := false for _, b := range ssafn.Blocks { if block.Dominates(b) { @@ -2569,11 +2367,12 @@ func (c *Checker) CheckInfiniteRecursion(j *lint.Job) { } } if canReturn { - continue + return } - j.Errorf(edge.Site, "infinite recursive call") - } + pass.Reportf(site.Pos(), "infinite recursive call") + }) } + return nil, nil } func objectName(obj types.Object) string { @@ -2591,20 +2390,20 @@ func objectName(obj types.Object) string { return name } -func isName(j *lint.Job, expr ast.Expr, name string) bool { +func isName(pass *analysis.Pass, expr ast.Expr, name string) bool { var obj types.Object switch expr := expr.(type) { case *ast.Ident: - obj = j.Pkg.TypesInfo.ObjectOf(expr) + obj = pass.TypesInfo.ObjectOf(expr) case *ast.SelectorExpr: - obj = j.Pkg.TypesInfo.ObjectOf(expr.Sel) + obj = pass.TypesInfo.ObjectOf(expr.Sel) } return objectName(obj) == name } -func (c *Checker) CheckLeakyTimeTick(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { - if IsInMain(j, ssafn) || IsInTest(j, ssafn) { +func CheckLeakyTimeTick(pass *analysis.Pass) (interface{}, error) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { + if IsInMain(pass, ssafn) || IsInTest(pass, ssafn) { continue } for _, block := range ssafn.Blocks { @@ -2613,16 +2412,17 @@ func (c *Checker) CheckLeakyTimeTick(j *lint.Job) { if !ok || !IsCallTo(call.Common(), "time.Tick") { continue } - if c.funcDescs.Get(call.Parent()).Infinite { + if !functions.Terminates(call.Parent()) { continue } - j.Errorf(call, "using time.Tick leaks the underlying ticker, consider using it only in endless functions, tests and the main package, and use time.NewTicker here") + pass.Reportf(call.Pos(), "using time.Tick leaks the underlying ticker, consider using it only in endless functions, tests and the main package, and use time.NewTicker here") } } } + return nil, nil } -func (c *Checker) CheckDoubleNegation(j *lint.Job) { +func CheckDoubleNegation(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { unary1 := node.(*ast.UnaryExpr) unary2, ok := unary1.X.(*ast.UnaryExpr) @@ -2632,9 +2432,10 @@ func (c *Checker) CheckDoubleNegation(j *lint.Job) { if unary1.Op != token.NOT || unary2.Op != token.NOT { return } - j.Errorf(unary1, "negating a boolean twice has no effect; is this a typo?") + ReportNodef(pass, unary1, "negating a boolean twice has no effect; is this a typo?") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.UnaryExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.UnaryExpr)(nil)}, fn) + return nil, nil } func hasSideEffects(node ast.Node) bool { @@ -2655,7 +2456,7 @@ func hasSideEffects(node ast.Node) bool { return dynamic } -func (c *Checker) CheckRepeatedIfElse(j *lint.Job) { +func CheckRepeatedIfElse(pass *analysis.Pass) (interface{}, error) { seen := map[ast.Node]bool{} var collectConds func(ifstmt *ast.IfStmt, inits []ast.Stmt, conds []ast.Expr) ([]ast.Stmt, []ast.Expr) @@ -2686,18 +2487,19 @@ func (c *Checker) CheckRepeatedIfElse(j *lint.Job) { } counts := map[string]int{} for _, cond := range conds { - s := Render(j, cond) + s := Render(pass, cond) counts[s]++ if counts[s] == 2 { - j.Errorf(cond, "this condition occurs multiple times in this if/else if chain") + ReportNodef(pass, cond, "this condition occurs multiple times in this if/else if chain") } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckSillyBitwiseOps(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { +func CheckSillyBitwiseOps(pass *analysis.Pass) (interface{}, error) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { for _, block := range ssafn.Blocks { for _, ins := range block.Instrs { ins, ok := ins.(*ssa.BinOp) @@ -2715,7 +2517,7 @@ func (c *Checker) CheckSillyBitwiseOps(j *lint.Job) { // of a pattern, x<<0, x<<8, x<<16, ... continue } - path, _ := astutil.PathEnclosingInterval(j.File(ins), ins.Pos(), ins.Pos()) + path, _ := astutil.PathEnclosingInterval(File(pass, ins), ins.Pos(), ins.Pos()) if len(path) == 0 { continue } @@ -2725,19 +2527,20 @@ func (c *Checker) CheckSillyBitwiseOps(j *lint.Job) { switch ins.Op { case token.AND: - j.Errorf(ins, "x & 0 always equals 0") + pass.Reportf(ins.Pos(), "x & 0 always equals 0") case token.OR, token.XOR: - j.Errorf(ins, "x %s 0 always equals x", ins.Op) + pass.Reportf(ins.Pos(), "x %s 0 always equals x", ins.Op) } } } } + return nil, nil } -func (c *Checker) CheckNonOctalFileMode(j *lint.Job) { +func CheckNonOctalFileMode(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { call := node.(*ast.CallExpr) - sig, ok := j.Pkg.TypesInfo.TypeOf(call.Fun).(*types.Signature) + sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature) if !ok { return } @@ -2764,17 +2567,20 @@ func (c *Checker) CheckNonOctalFileMode(j *lint.Job) { if err != nil { continue } - j.Errorf(call.Args[i], "file mode '%s' evaluates to %#o; did you mean '0%s'?", lit.Value, v, lit.Value) + ReportNodef(pass, call.Args[i], "file mode '%s' evaluates to %#o; did you mean '0%s'?", lit.Value, v, lit.Value) } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckPureFunctions(j *lint.Job) { +func CheckPureFunctions(pass *analysis.Pass) (interface{}, error) { + pure := pass.ResultOf[facts.Purity].(facts.PurityResult) + fnLoop: - for _, ssafn := range j.Pkg.InitialFunctions { - if IsInTest(j, ssafn) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { + if IsInTest(pass, ssafn) { params := ssafn.Signature.Params() for i := 0; i < params.Len(); i++ { param := params.At(i) @@ -2805,157 +2611,157 @@ fnLoop: if callee == nil { continue } - if c.funcDescs.Get(callee).Pure && !c.funcDescs.Get(callee).Stub { - j.Errorf(ins, "%s is a pure function but its return value is ignored", callee.Name()) + if callee.Object() == nil { + // TODO(dh): support anonymous functions + continue + } + if _, ok := pure[callee.Object().(*types.Func)]; ok { + pass.Reportf(ins.Pos(), "%s is a pure function but its return value is ignored", callee.Name()) continue } } } } + return nil, nil } -func (c *Checker) isDeprecated(j *lint.Job, ident *ast.Ident) (bool, string) { - obj := j.Pkg.TypesInfo.ObjectOf(ident) - if obj.Pkg() == nil { - return false, "" - } - alt := c.deprecatedObjs[obj] - return alt != "", alt -} +func CheckDeprecated(pass *analysis.Pass) (interface{}, error) { + deprs := pass.ResultOf[facts.Deprecated].(facts.DeprecatedResult) -func (c *Checker) CheckDeprecated(j *lint.Job) { // Selectors can appear outside of function literals, e.g. when // declaring package level variables. - var ssafn *ssa.Function + var tfn types.Object stack := 0 fn := func(node ast.Node, push bool) bool { if !push { stack-- - } else { - stack++ + return false } + stack++ if stack == 1 { - ssafn = nil + tfn = nil } if fn, ok := node.(*ast.FuncDecl); ok { - ssafn = j.Pkg.SSA.Prog.FuncValue(j.Pkg.TypesInfo.ObjectOf(fn.Name).(*types.Func)) + tfn = pass.TypesInfo.ObjectOf(fn.Name) } sel, ok := node.(*ast.SelectorExpr) if !ok { return true } - obj := j.Pkg.TypesInfo.ObjectOf(sel.Sel) + obj := pass.TypesInfo.ObjectOf(sel.Sel) if obj.Pkg() == nil { return true } - nodePkg := j.Pkg.Types - if nodePkg == obj.Pkg() || obj.Pkg().Path()+"_test" == nodePkg.Path() { + if pass.Pkg == obj.Pkg() || obj.Pkg().Path()+"_test" == pass.Pkg.Path() { // Don't flag stuff in our own package return true } - if ok, alt := c.isDeprecated(j, sel.Sel); ok { + if depr, ok := deprs.Objects[obj]; ok { // Look for the first available alternative, not the first // version something was deprecated in. If a function was // deprecated in Go 1.6, an alternative has been available // already in 1.0, and we're targeting 1.2, it still // makes sense to use the alternative from 1.0, to be // future-proof. - minVersion := deprecated.Stdlib[SelectorName(j, sel)].AlternativeAvailableSince - if !IsGoVersion(j, minVersion) { + minVersion := deprecated.Stdlib[SelectorName(pass, sel)].AlternativeAvailableSince + if !IsGoVersion(pass, minVersion) { return true } - if ssafn != nil { - if _, ok := c.deprecatedObjs[ssafn.Object()]; ok { + if tfn != nil { + if _, ok := deprs.Objects[tfn]; ok { // functions that are deprecated may use deprecated // symbols return true } } - j.Errorf(sel, "%s is deprecated: %s", Render(j, sel), alt) + ReportNodef(pass, sel, "%s is deprecated: %s", Render(pass, sel), depr.Msg) return true } return true } - for _, f := range j.Pkg.Syntax { - ast.Inspect(f, func(node ast.Node) bool { - if node, ok := node.(*ast.ImportSpec); ok { - p := node.Path.Value - path := p[1 : len(p)-1] - imp := j.Pkg.Imports[path] - if alt := c.deprecatedPkgs[imp.Types]; alt != "" { - j.Errorf(node, "Package %s is deprecated: %s", path, alt) - } - } - return true - }) + + imps := map[string]*types.Package{} + for _, imp := range pass.Pkg.Imports() { + imps[imp.Path()] = imp } - j.Pkg.Inspector.Nodes(nil, fn) -} - -func (c *Checker) callChecker(rules map[string]CallCheck) func(j *lint.Job) { - return func(j *lint.Job) { - c.checkCalls(j, rules) - } -} - -func (c *Checker) checkCalls(j *lint.Job, rules map[string]CallCheck) { - for _, ssafn := range j.Pkg.InitialFunctions { - node := c.funcDescs.CallGraph.CreateNode(ssafn) - for _, edge := range node.Out { - callee := edge.Callee.Func - obj, ok := callee.Object().(*types.Func) - if !ok { - continue - } - - r, ok := rules[lint.FuncName(obj)] - if !ok { - continue - } - var args []*Argument - ssaargs := edge.Site.Common().Args - if callee.Signature.Recv() != nil { - ssaargs = ssaargs[1:] - } - for _, arg := range ssaargs { - if iarg, ok := arg.(*ssa.MakeInterface); ok { - arg = iarg.X - } - vr := c.funcDescs.Get(edge.Site.Parent()).Ranges[arg] - args = append(args, &Argument{Value: Value{arg, vr}}) - } - call := &Call{ - Job: j, - Instr: edge.Site, - Args: args, - Checker: c, - Parent: edge.Site.Parent(), - } - r(call) - for idx, arg := range call.Args { - _ = idx - for _, e := range arg.invalids { - // path, _ := astutil.PathEnclosingInterval(f.File, edge.Site.Pos(), edge.Site.Pos()) - // if len(path) < 2 { - // continue - // } - // astcall, ok := path[0].(*ast.CallExpr) - // if !ok { - // continue - // } - // j.Errorf(astcall.Args[idx], "%s", e) - - j.Errorf(edge.Site, "%s", e) - } - } - for _, e := range call.invalids { - j.Errorf(call.Instr.Common(), "%s", e) - } + fn2 := func(node ast.Node) { + spec := node.(*ast.ImportSpec) + p := spec.Path.Value + path := p[1 : len(p)-1] + imp := imps[path] + if depr, ok := deprs.Packages[imp]; ok { + ReportNodef(pass, spec, "Package %s is deprecated: %s", path, depr.Msg) } } + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes(nil, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ImportSpec)(nil)}, fn2) + return nil, nil +} + +func callChecker(rules map[string]CallCheck) func(pass *analysis.Pass) (interface{}, error) { + return func(pass *analysis.Pass) (interface{}, error) { + return checkCalls(pass, rules) + } +} + +func checkCalls(pass *analysis.Pass, rules map[string]CallCheck) (interface{}, error) { + ranges := pass.ResultOf[valueRangesAnalyzer].(map[*ssa.Function]vrp.Ranges) + fn := func(caller *ssa.Function, site ssa.CallInstruction, callee *ssa.Function) { + obj, ok := callee.Object().(*types.Func) + if !ok { + return + } + + r, ok := rules[lint.FuncName(obj)] + if !ok { + return + } + var args []*Argument + ssaargs := site.Common().Args + if callee.Signature.Recv() != nil { + ssaargs = ssaargs[1:] + } + for _, arg := range ssaargs { + if iarg, ok := arg.(*ssa.MakeInterface); ok { + arg = iarg.X + } + vr := ranges[site.Parent()][arg] + args = append(args, &Argument{Value: Value{arg, vr}}) + } + call := &Call{ + Pass: pass, + Instr: site, + Args: args, + Parent: site.Parent(), + } + r(call) + for idx, arg := range call.Args { + _ = idx + for _, e := range arg.invalids { + // path, _ := astutil.PathEnclosingInterval(f.File, edge.Site.Pos(), edge.Site.Pos()) + // if len(path) < 2 { + // continue + // } + // astcall, ok := path[0].(*ast.CallExpr) + // if !ok { + // continue + // } + // pass.Reportf(astcall.Args[idx], "%s", e) + + pass.Reportf(site.Pos(), "%s", e) + } + } + for _, e := range call.invalids { + pass.Reportf(call.Instr.Common().Pos(), "%s", e) + } + } + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { + eachCall(ssafn, fn) + } + return nil, nil } func shortCallName(call *ssa.CallCommon) string { @@ -2975,12 +2781,12 @@ func shortCallName(call *ssa.CallCommon) string { return "" } -func (c *Checker) CheckWriterBufferModified(j *lint.Job) { +func CheckWriterBufferModified(pass *analysis.Pass) (interface{}, error) { // TODO(dh): this might be a good candidate for taint analysis. // Taint the argument as MUST_NOT_MODIFY, then propagate that // through functions like bytes.Split - for _, ssafn := range j.Pkg.InitialFunctions { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { sig := ssafn.Signature if ssafn.Name() != "Write" || sig.Recv() == nil || sig.Params().Len() != 1 || sig.Results().Len() != 2 { continue @@ -3010,7 +2816,7 @@ func (c *Checker) CheckWriterBufferModified(j *lint.Job) { if addr.X != ssafn.Params[1] { continue } - j.Errorf(ins, "io.Writer.Write must not modify the provided buffer, not even temporarily") + pass.Reportf(ins.Pos(), "io.Writer.Write must not modify the provided buffer, not even temporarily") case *ssa.Call: if !IsCallTo(ins.Common(), "append") { continue @@ -3018,11 +2824,12 @@ func (c *Checker) CheckWriterBufferModified(j *lint.Job) { if ins.Common().Args[0] != ssafn.Params[1] { continue } - j.Errorf(ins, "io.Writer.Write must not modify the provided buffer, not even temporarily") + pass.Reportf(ins.Pos(), "io.Writer.Write must not modify the provided buffer, not even temporarily") } } } } + return nil, nil } func loopedRegexp(name string) CallCheck { @@ -3030,21 +2837,18 @@ func loopedRegexp(name string) CallCheck { if len(extractConsts(call.Args[0].Value.Value)) == 0 { return } - if !call.Checker.isInLoop(call.Instr.Block()) { + if !isInLoop(call.Instr.Block()) { return } call.Invalid(fmt.Sprintf("calling %s in a loop has poor performance, consider using regexp.Compile", name)) } } -func (c *Checker) CheckEmptyBranch(j *lint.Job) { - for _, ssafn := range j.Pkg.InitialFunctions { +func CheckEmptyBranch(pass *analysis.Pass) (interface{}, error) { + for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { if ssafn.Syntax() == nil { continue } - if IsGenerated(j.File(ssafn.Syntax())) { - continue - } if IsExample(ssafn) { continue } @@ -3058,20 +2862,21 @@ func (c *Checker) CheckEmptyBranch(j *lint.Job) { if !ok || len(b.List) != 0 { return true } - j.Errorf(ifstmt.Else, "empty branch") + ReportfFG(pass, ifstmt.Else.Pos(), "empty branch") } if len(ifstmt.Body.List) != 0 { return true } - j.Errorf(ifstmt, "empty branch") + ReportfFG(pass, ifstmt.Pos(), "empty branch") return true } Inspect(ssafn.Syntax(), fn) } + return nil, nil } -func (c *Checker) CheckMapBytesKey(j *lint.Job) { - for _, fn := range j.Pkg.InitialFunctions { +func CheckMapBytesKey(pass *analysis.Pass) (interface{}, error) { + for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { for _, b := range fn.Blocks { insLoop: for _, ins := range b.Instrs { @@ -3115,31 +2920,33 @@ func (c *Checker) CheckMapBytesKey(j *lint.Job) { if !ident { continue } - j.Errorf(conv, "m[string(key)] would be more efficient than k := string(key); m[k]") + pass.Reportf(conv.Pos(), "m[string(key)] would be more efficient than k := string(key); m[k]") } } } + return nil, nil } -func (c *Checker) CheckRangeStringRunes(j *lint.Job) { - sharedcheck.CheckRangeStringRunes(j) +func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) { + return sharedcheck.CheckRangeStringRunes(pass) } -func (c *Checker) CheckSelfAssignment(j *lint.Job) { +func CheckSelfAssignment(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { assign := node.(*ast.AssignStmt) if assign.Tok != token.ASSIGN || len(assign.Lhs) != len(assign.Rhs) { return } for i, stmt := range assign.Lhs { - rlh := Render(j, stmt) - rrh := Render(j, assign.Rhs[i]) + rlh := Render(pass, stmt) + rrh := Render(pass, assign.Rhs[i]) if rlh == rrh { - j.Errorf(assign, "self-assignment of %s to %s", rrh, rlh) + ReportfFG(pass, assign.Pos(), "self-assignment of %s to %s", rrh, rlh) } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn) + return nil, nil } func buildTagsIdentical(s1, s2 []string) bool { @@ -3160,8 +2967,8 @@ func buildTagsIdentical(s1, s2 []string) bool { return true } -func (c *Checker) CheckDuplicateBuildConstraints(job *lint.Job) { - for _, f := range job.Pkg.Syntax { +func CheckDuplicateBuildConstraints(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { constraints := buildTags(f) for i, constraint1 := range constraints { for j, constraint2 := range constraints { @@ -3169,19 +2976,20 @@ func (c *Checker) CheckDuplicateBuildConstraints(job *lint.Job) { continue } if buildTagsIdentical(constraint1, constraint2) { - job.Errorf(f, "identical build constraints %q and %q", + ReportfFG(pass, f.Pos(), "identical build constraints %q and %q", strings.Join(constraint1, " "), strings.Join(constraint2, " ")) } } } } + return nil, nil } -func (c *Checker) CheckSillyRegexp(j *lint.Job) { +func CheckSillyRegexp(pass *analysis.Pass) (interface{}, error) { // We could use the rule checking engine for this, but the // arguments aren't really invalid. - for _, fn := range j.Pkg.InitialFunctions { + for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { for _, b := range fn.Blocks { for _, ins := range b.Instrs { call, ok := ins.(*ssa.Call) @@ -3205,13 +3013,14 @@ func (c *Checker) CheckSillyRegexp(j *lint.Job) { if re.Op != syntax.OpLiteral && re.Op != syntax.OpEmptyMatch { continue } - j.Errorf(call, "regular expression does not contain any meta characters") + pass.Reportf(call.Pos(), "regular expression does not contain any meta characters") } } } + return nil, nil } -func (c *Checker) CheckMissingEnumTypesInDeclaration(j *lint.Job) { +func CheckMissingEnumTypesInDeclaration(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { decl := node.(*ast.GenDecl) if !decl.Lparen.IsValid() { @@ -3221,7 +3030,7 @@ func (c *Checker) CheckMissingEnumTypesInDeclaration(j *lint.Job) { return } - groups := GroupSpecs(j.Pkg.Fset, decl.Specs) + groups := GroupSpecs(pass.Fset, decl.Specs) groupLoop: for _, group := range groups { if len(group) < 2 { @@ -3254,14 +3063,15 @@ func (c *Checker) CheckMissingEnumTypesInDeclaration(j *lint.Job) { continue groupLoop } } - j.Errorf(group[0], "only the first constant in this group has an explicit type") + ReportNodef(pass, group[0], "only the first constant in this group has an explicit type") } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.GenDecl)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.GenDecl)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckTimerResetReturnValue(j *lint.Job) { - for _, fn := range j.Pkg.InitialFunctions { +func CheckTimerResetReturnValue(pass *analysis.Pass) (interface{}, error) { + for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { for _, block := range fn.Blocks { for _, ins := range block.Instrs { call, ok := ins.(*ssa.Call) @@ -3314,15 +3124,16 @@ func (c *Checker) CheckTimerResetReturnValue(j *lint.Job) { } if found { - j.Errorf(call, "it is not possible to use Reset's return value correctly, as there is a race condition between draining the channel and the new timer expiring") + pass.Reportf(call.Pos(), "it is not possible to use Reset's return value correctly, as there is a race condition between draining the channel and the new timer expiring") } } } } } + return nil, nil } -func (c *Checker) CheckToLowerToUpperComparison(j *lint.Job) { +func CheckToLowerToUpperComparison(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { binExpr := node.(*ast.BinaryExpr) @@ -3342,9 +3153,9 @@ func (c *Checker) CheckToLowerToUpperComparison(j *lint.Job) { ) var call string - if IsCallToAST(j, binExpr.X, lo) && IsCallToAST(j, binExpr.Y, lo) { + if IsCallToAST(pass, binExpr.X, lo) && IsCallToAST(pass, binExpr.Y, lo) { call = lo - } else if IsCallToAST(j, binExpr.X, up) && IsCallToAST(j, binExpr.Y, up) { + } else if IsCallToAST(pass, binExpr.X, up) && IsCallToAST(pass, binExpr.Y, up) { call = up } else { return @@ -3355,13 +3166,14 @@ func (c *Checker) CheckToLowerToUpperComparison(j *lint.Job) { bang = "!" } - j.Errorf(binExpr, "should use %sstrings.EqualFold(a, b) instead of %s(a) %s %s(b)", bang, call, binExpr.Op, call) + ReportNodef(pass, binExpr, "should use %sstrings.EqualFold(a, b) instead of %s(a) %s %s(b)", bang, call, binExpr.Op, call) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckUnreachableTypeCases(j *lint.Job) { +func CheckUnreachableTypeCases(pass *analysis.Pass) (interface{}, error) { // Check if T subsumes V in a type switch. T subsumes V if T is an interface and T's method set is a subset of V's method set. subsumes := func(T, V types.Type) bool { tIface, ok := T.Underlying().(*types.Interface) @@ -3404,7 +3216,7 @@ func (c *Checker) CheckUnreachableTypeCases(j *lint.Job) { Ts := make([]types.Type, len(cc.List)) for i, expr := range cc.List { - Ts[i] = j.Pkg.TypesInfo.TypeOf(expr) + Ts[i] = pass.TypesInfo.TypeOf(expr) } ccs = append(ccs, ccAndTypes{cc: cc, types: Ts}) @@ -3419,30 +3231,32 @@ func (c *Checker) CheckUnreachableTypeCases(j *lint.Job) { for i, cc := range ccs[:len(ccs)-1] { for _, next := range ccs[i+1:] { if T, V, yes := subsumesAny(cc.types, next.types); yes { - j.Errorf(next.cc, "unreachable case clause: %s will always match before %s", T.String(), V.String()) + ReportNodef(pass, next.cc, "unreachable case clause: %s will always match before %s", T.String(), V.String()) } } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.TypeSwitchStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.TypeSwitchStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckSingleArgAppend(j *lint.Job) { +func CheckSingleArgAppend(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { - if !IsCallToAST(j, node, "append") { + if !IsCallToAST(pass, node, "append") { return } call := node.(*ast.CallExpr) if len(call.Args) != 1 { return } - j.Errorf(call, "x = append(y) is equivalent to x = y") + ReportfFG(pass, call.Pos(), "x = append(y) is equivalent to x = y") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckStructTags(j *lint.Job) { +func CheckStructTags(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { for _, field := range node.(*ast.StructType).Fields.List { if field.Tag == nil { @@ -3450,35 +3264,36 @@ func (c *Checker) CheckStructTags(j *lint.Job) { } tags, err := parseStructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]) if err != nil { - j.Errorf(field.Tag, "unparseable struct tag: %s", err) + ReportNodef(pass, field.Tag, "unparseable struct tag: %s", err) continue } for k, v := range tags { if len(v) > 1 { - j.Errorf(field.Tag, "duplicate struct tag %q", k) + ReportNodef(pass, field.Tag, "duplicate struct tag %q", k) continue } switch k { case "json": - checkJSONTag(j, field, v[0]) + checkJSONTag(pass, field, v[0]) case "xml": - checkXMLTag(j, field, v[0]) + checkXMLTag(pass, field, v[0]) } } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.StructType)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.StructType)(nil)}, fn) + return nil, nil } -func checkJSONTag(j *lint.Job, field *ast.Field, tag string) { +func checkJSONTag(pass *analysis.Pass, field *ast.Field, tag string) { + //lint:ignore SA9003 TODO(dh): should we flag empty tags? if len(tag) == 0 { - // TODO(dh): should we flag empty tags? } fields := strings.Split(tag, ",") for _, r := range fields[0] { if !unicode.IsLetter(r) && !unicode.IsDigit(r) && !strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", r) { - j.Errorf(field.Tag, "invalid JSON field name %q", fields[0]) + ReportNodef(pass, field.Tag, "invalid JSON field name %q", fields[0]) } } var co, cs, ci int @@ -3491,31 +3306,31 @@ func checkJSONTag(j *lint.Job, field *ast.Field, tag string) { case "string": cs++ // only for string, floating point, integer and bool - T := Dereference(j.Pkg.TypesInfo.TypeOf(field.Type).Underlying()).Underlying() + T := Dereference(pass.TypesInfo.TypeOf(field.Type).Underlying()).Underlying() basic, ok := T.(*types.Basic) if !ok || (basic.Info()&(types.IsBoolean|types.IsInteger|types.IsFloat|types.IsString)) == 0 { - j.Errorf(field.Tag, "the JSON string option only applies to fields of type string, floating point, integer or bool, or pointers to those") + ReportNodef(pass, field.Tag, "the JSON string option only applies to fields of type string, floating point, integer or bool, or pointers to those") } case "inline": ci++ default: - j.Errorf(field.Tag, "unknown JSON option %q", s) + ReportNodef(pass, field.Tag, "unknown JSON option %q", s) } } if co > 1 { - j.Errorf(field.Tag, `duplicate JSON option "omitempty"`) + ReportNodef(pass, field.Tag, `duplicate JSON option "omitempty"`) } if cs > 1 { - j.Errorf(field.Tag, `duplicate JSON option "string"`) + ReportNodef(pass, field.Tag, `duplicate JSON option "string"`) } if ci > 1 { - j.Errorf(field.Tag, `duplicate JSON option "inline"`) + ReportNodef(pass, field.Tag, `duplicate JSON option "inline"`) } } -func checkXMLTag(j *lint.Job, field *ast.Field, tag string) { +func checkXMLTag(pass *analysis.Pass, field *ast.Field, tag string) { + //lint:ignore SA9003 TODO(dh): should we flag empty tags? if len(tag) == 0 { - // TODO(dh): should we flag empty tags? } fields := strings.Split(tag, ",") counts := map[string]int{} @@ -3531,15 +3346,15 @@ func checkXMLTag(j *lint.Job, field *ast.Field, tag string) { counts[s]++ case "": default: - j.Errorf(field.Tag, "unknown XML option %q", s) + ReportNodef(pass, field.Tag, "unknown XML option %q", s) } } for k, v := range counts { if v > 1 { - j.Errorf(field.Tag, "duplicate XML option %q", k) + ReportNodef(pass, field.Tag, "duplicate XML option %q", k) } } if len(exclusives) > 1 { - j.Errorf(field.Tag, "XML options %s are mutually exclusive", strings.Join(exclusives, " and ")) + ReportNodef(pass, field.Tag, "XML options %s are mutually exclusive", strings.Join(exclusives, " and ")) } } diff --git a/vendor/honnef.co/go/tools/staticcheck/rules.go b/vendor/honnef.co/go/tools/staticcheck/rules.go index d6af573c21..0152cac1af 100644 --- a/vendor/honnef.co/go/tools/staticcheck/rules.go +++ b/vendor/honnef.co/go/tools/staticcheck/rules.go @@ -13,7 +13,7 @@ import ( "time" "unicode/utf8" - "honnef.co/go/tools/lint" + "golang.org/x/tools/go/analysis" . "honnef.co/go/tools/lint/lintdsl" "honnef.co/go/tools/ssa" "honnef.co/go/tools/staticcheck/vrp" @@ -26,12 +26,11 @@ const ( ) type Call struct { - Job *lint.Job + Pass *analysis.Pass Instr ssa.CallInstruction Args []*Argument - Checker *Checker - Parent *ssa.Function + Parent *ssa.Function invalids []string } @@ -184,7 +183,7 @@ func ConvertedFromInt(v Value) bool { return true } -func validEncodingBinaryType(j *lint.Job, typ types.Type) bool { +func validEncodingBinaryType(pass *analysis.Pass, typ types.Type) bool { typ = typ.Underlying() switch typ := typ.(type) { case *types.Basic: @@ -194,19 +193,19 @@ func validEncodingBinaryType(j *lint.Job, typ types.Type) bool { types.Float32, types.Float64, types.Complex64, types.Complex128, types.Invalid: return true case types.Bool: - return IsGoVersion(j, 8) + return IsGoVersion(pass, 8) } return false case *types.Struct: n := typ.NumFields() for i := 0; i < n; i++ { - if !validEncodingBinaryType(j, typ.Field(i).Type()) { + if !validEncodingBinaryType(pass, typ.Field(i).Type()) { return false } } return true case *types.Array: - return validEncodingBinaryType(j, typ.Elem()) + return validEncodingBinaryType(pass, typ.Elem()) case *types.Interface: // we can't determine if it's a valid type or not return true @@ -214,7 +213,7 @@ func validEncodingBinaryType(j *lint.Job, typ types.Type) bool { return false } -func CanBinaryMarshal(j *lint.Job, v Value) bool { +func CanBinaryMarshal(pass *analysis.Pass, v Value) bool { typ := v.Value.Type().Underlying() if ttyp, ok := typ.(*types.Pointer); ok { typ = ttyp.Elem().Underlying() @@ -227,7 +226,7 @@ func CanBinaryMarshal(j *lint.Job, v Value) bool { } } - return validEncodingBinaryType(j, typ) + return validEncodingBinaryType(pass, typ) } func RepeatZeroTimes(name string, arg int) CallCheck { diff --git a/vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go b/vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go index a0011ed082..3c138e5122 100644 --- a/vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go +++ b/vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go @@ -722,16 +722,22 @@ func (g *Graph) widen(c Constraint, consts []Z) bool { } nlc := NInfinity nuc := PInfinity - for _, co := range consts { - if co.Cmp(ni.Lower) <= 0 { - nlc = co - break + + // Don't get stuck widening for an absurd amount of time due + // to an excess number of constants, as may be present in + // table-based scanners. + if len(consts) < 1000 { + for _, co := range consts { + if co.Cmp(ni.Lower) <= 0 { + nlc = co + break + } } - } - for _, co := range consts { - if co.Cmp(ni.Upper) >= 0 { - nuc = co - break + for _, co := range consts { + if co.Cmp(ni.Upper) >= 0 { + nuc = co + break + } } } diff --git a/vendor/honnef.co/go/tools/stylecheck/analysis.go b/vendor/honnef.co/go/tools/stylecheck/analysis.go new file mode 100644 index 0000000000..f252487f73 --- /dev/null +++ b/vendor/honnef.co/go/tools/stylecheck/analysis.go @@ -0,0 +1,111 @@ +package stylecheck + +import ( + "flag" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "honnef.co/go/tools/config" + "honnef.co/go/tools/facts" + "honnef.co/go/tools/internal/passes/buildssa" + "honnef.co/go/tools/lint/lintutil" +) + +func newFlagSet() flag.FlagSet { + fs := flag.NewFlagSet("", flag.PanicOnError) + fs.Var(lintutil.NewVersionFlag(), "go", "Target Go version") + return *fs +} + +var Analyzers = map[string]*analysis.Analyzer{ + "ST1000": { + Name: "ST1000", + Run: CheckPackageComment, + Doc: Docs["ST1000"].String(), + Requires: []*analysis.Analyzer{}, + Flags: newFlagSet(), + }, + "ST1001": { + Name: "ST1001", + Run: CheckDotImports, + Doc: Docs["ST1001"].String(), + Requires: []*analysis.Analyzer{facts.Generated, config.Analyzer}, + Flags: newFlagSet(), + }, + "ST1003": { + Name: "ST1003", + Run: CheckNames, + Doc: Docs["ST1003"].String(), + Requires: []*analysis.Analyzer{facts.Generated, config.Analyzer}, + Flags: newFlagSet(), + }, + "ST1005": { + Name: "ST1005", + Run: CheckErrorStrings, + Doc: Docs["ST1005"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "ST1006": { + Name: "ST1006", + Run: CheckReceiverNames, + Doc: Docs["ST1006"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer, facts.Generated}, + Flags: newFlagSet(), + }, + "ST1008": { + Name: "ST1008", + Run: CheckErrorReturn, + Doc: Docs["ST1008"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "ST1011": { + Name: "ST1011", + Run: CheckTimeNames, + Doc: Docs["ST1011"].String(), + Flags: newFlagSet(), + }, + "ST1012": { + Name: "ST1012", + Run: CheckErrorVarNames, + Doc: Docs["ST1012"].String(), + Requires: []*analysis.Analyzer{config.Analyzer}, + Flags: newFlagSet(), + }, + "ST1013": { + Name: "ST1013", + Run: CheckHTTPStatusCodes, + Doc: Docs["ST1013"].String(), + Requires: []*analysis.Analyzer{facts.Generated, facts.TokenFile, config.Analyzer}, + Flags: newFlagSet(), + }, + "ST1015": { + Name: "ST1015", + Run: CheckDefaultCaseOrder, + Doc: Docs["ST1015"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated, facts.TokenFile}, + Flags: newFlagSet(), + }, + "ST1016": { + Name: "ST1016", + Run: CheckReceiverNamesIdentical, + Doc: Docs["ST1016"].String(), + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + Flags: newFlagSet(), + }, + "ST1017": { + Name: "ST1017", + Run: CheckYodaConditions, + Doc: Docs["ST1017"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated, facts.TokenFile}, + Flags: newFlagSet(), + }, + "ST1018": { + Name: "ST1018", + Run: CheckInvisibleCharacters, + Doc: Docs["ST1018"].String(), + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Flags: newFlagSet(), + }, +} diff --git a/vendor/honnef.co/go/tools/stylecheck/doc.go b/vendor/honnef.co/go/tools/stylecheck/doc.go index efc65092e0..9097214d9b 100644 --- a/vendor/honnef.co/go/tools/stylecheck/doc.go +++ b/vendor/honnef.co/go/tools/stylecheck/doc.go @@ -1,18 +1,20 @@ package stylecheck -var docST1000 = `Incorrect or missing package comment +import "honnef.co/go/tools/lint" -Packages must have a package comment that is formatted according to +var Docs = map[string]*lint.Documentation{ + "ST1000": &lint.Documentation{ + Title: `Incorrect or missing package comment`, + Text: `Packages must have a package comment that is formatted according to the guidelines laid out in -https://github.com/golang/go/wiki/CodeReviewComments#package-comments. +https://github.com/golang/go/wiki/CodeReviewComments#package-comments.`, + Since: "2019.1", + NonDefault: true, + }, -Available since - 2019.1, non-default -` - -var docST1001 = `Dot imports are discouraged - -Dot imports that aren't in external test packages are discouraged. + "ST1001": &lint.Documentation{ + Title: `Dot imports are discouraged`, + Text: `Dot imports that aren't in external test packages are discouraged. The dot_import_whitelist option can be used to whitelist certain imports. @@ -35,36 +37,29 @@ Quoting Go Code Review Comments: it is not. Except for this one case, do not use import . in your programs. It makes the programs much harder to read because it is unclear whether a name like Quux is a top-level identifier in the - current package or in an imported package. + current package or in an imported package.`, + Since: "2019.1", + Options: []string{"dot_import_whitelist"}, + }, -Available since - 2019.1 - -Options - dot_import_whitelist -` - -var docST1003 = `Poorly chosen identifier - -Identifiers, such as variable and package names, follow certain rules. + "ST1003": &lint.Documentation{ + Title: `Poorly chosen identifier`, + Text: `Identifiers, such as variable and package names, follow certain rules. See the following links for details: - http://golang.org/doc/effective_go.html#package-names - http://golang.org/doc/effective_go.html#mixed-caps - https://github.com/golang/go/wiki/CodeReviewComments#initialisms - https://github.com/golang/go/wiki/CodeReviewComments#variable-names +- https://golang.org/doc/effective_go.html#package-names +- https://golang.org/doc/effective_go.html#mixed-caps +- https://github.com/golang/go/wiki/CodeReviewComments#initialisms +- https://github.com/golang/go/wiki/CodeReviewComments#variable-names`, + Since: "2019.1", + NonDefault: true, + Options: []string{"initialisms"}, + }, -Available since - 2019.1, non-default - -Options - initialisms -` - -var docST1005 = `Incorrectly formatted error string - -Error strings follow a set of guidelines to ensure uniformity and good + "ST1005": &lint.Documentation{ + Title: `Incorrectly formatted error string`, + Text: `Error strings follow a set of guidelines to ensure uniformity and good composability. Quoting Go Code Review Comments: @@ -74,15 +69,13 @@ Quoting Go Code Review Comments: usually printed following other context. That is, use fmt.Errorf("something bad") not fmt.Errorf("Something bad"), so that log.Printf("Reading %s: %v", filename, err) formats without a - spurious capital letter mid-message. + spurious capital letter mid-message.`, + Since: "2019.1", + }, -Available since - 2019.1 -` - -var docST1006 = `Poorly chosen receiver name - -Quoting Go Code Review Comments: + "ST1006": &lint.Documentation{ + Title: `Poorly chosen receiver name`, + Text: `Quoting Go Code Review Comments: The name of a method's receiver should be a reflection of its identity; often a one or two letter abbreviation of its type @@ -94,77 +87,68 @@ Quoting Go Code Review Comments: documentary purpose. It can be very short as it will appear on almost every line of every method of the type; familiarity admits brevity. Be consistent, too: if you call the receiver "c" in one - method, don't call it "cl" in another. + method, don't call it "cl" in another.`, + Since: "2019.1", + }, -Available since - 2019.1 -` + "ST1008": &lint.Documentation{ + Title: `A function's error value should be its last return value`, + Text: `A function's error value should be its last return value.`, + Since: `2019.1`, + }, -var docST1008 = `A function's error value should be its last return value - -A function's error value should be its last return value. - -Available since - 2019.1 -` - -var docST1011 = `Poorly chosen name for variable of type time.Duration - -time.Duration values represent an amount of time, which is represented + "ST1011": &lint.Documentation{ + Title: `Poorly chosen name for variable of type time.Duration`, + Text: `time.Duration values represent an amount of time, which is represented as a count of nanoseconds. An expression like 5 * time.Microsecond yields the value 5000. It is therefore not appropriate to suffix a variable of type time.Duration with any time unit, such as Msec or -Milli. +Milli.`, + Since: `2019.1`, + }, -Available since - 2019.1 -` + "ST1012": &lint.Documentation{ + Title: `Poorly chosen name for error variable`, + Text: `Error variables that are part of an API should be called errFoo or +ErrFoo.`, + Since: "2019.1", + }, -var docST1012 = `Poorly chosen name for error variable - -Error variables that are part of an API should be called errFoo or -ErrFoo. - -Available since - 2019.1 -` - -var docST1013 = `Should use constants for HTTP error codes, not magic numbers - -HTTP has a tremendous number of status codes. While some of those are + "ST1013": &lint.Documentation{ + Title: `Should use constants for HTTP error codes, not magic numbers`, + Text: `HTTP has a tremendous number of status codes. While some of those are well known (200, 400, 404, 500), most of them are not. The net/http package provides constants for all status codes that are part of the various specifications. It is recommended to use these constants instead of hard-coding magic numbers, to vastly improve the -readability of your code. +readability of your code.`, + Since: "2019.1", + Options: []string{"http_status_code_whitelist"}, + }, -Available since - 2019.1 + "ST1015": &lint.Documentation{ + Title: `A switch's default case should be the first or last case`, + Since: "2019.1", + }, -Options - http_status_code_whitelist -` + "ST1016": &lint.Documentation{ + Title: `Use consistent method receiver names`, + Since: "2019.1", + NonDefault: true, + }, -var docST1015 = `A switch's default case should be the first or last case + "ST1017": &lint.Documentation{ + Title: `Don't use Yoda conditions`, + Text: `Yoda conditions are conditions of the kind 'if 42 == x', where the +literal is on the left side of the comparison. These are a common +idiom in languages in which assignment is an expression, to avoid bugs +of the kind 'if (x = 42)'. In Go, which doesn't allow for this kind of +bug, we prefer the more idiomatic 'if x == 42'.`, + Since: "2019.2", + }, -Available since - 2019.1 -` - -var docST1016 = `Use consistent method receiver names - -Available since - 2019.1, non-default -` - -var docST1017 = `Don't use Yoda conditions - -Available since - Unreleased -` - -var docST1018 = `Avoid zero-width and control characters in string literals - -Available since - Unreleased -` + "ST1018": &lint.Documentation{ + Title: `Avoid zero-width and control characters in string literals`, + Since: "2019.2", + }, +} diff --git a/vendor/honnef.co/go/tools/stylecheck/lint.go b/vendor/honnef.co/go/tools/stylecheck/lint.go index 120d97f035..1699d5898c 100644 --- a/vendor/honnef.co/go/tools/stylecheck/lint.go +++ b/vendor/honnef.co/go/tools/stylecheck/lint.go @@ -11,49 +11,18 @@ import ( "unicode" "unicode/utf8" - "honnef.co/go/tools/lint" + "honnef.co/go/tools/config" + "honnef.co/go/tools/internal/passes/buildssa" . "honnef.co/go/tools/lint/lintdsl" "honnef.co/go/tools/ssa" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" ) -type Checker struct { - CheckGenerated bool -} - -func NewChecker() *Checker { - return &Checker{} -} - -func (*Checker) Name() string { return "stylecheck" } -func (*Checker) Prefix() string { return "ST" } -func (c *Checker) Init(prog *lint.Program) {} - -func (c *Checker) Checks() []lint.Check { - return []lint.Check{ - {ID: "ST1000", FilterGenerated: false, Fn: c.CheckPackageComment, Doc: docST1000}, - {ID: "ST1001", FilterGenerated: true, Fn: c.CheckDotImports, Doc: docST1001}, - // {ID: "ST1002", FilterGenerated: true, Fn: c.CheckBlankImports, Doc: docST1002}, - {ID: "ST1003", FilterGenerated: true, Fn: c.CheckNames, Doc: docST1003}, - // {ID: "ST1004", FilterGenerated: false, Fn: nil, , Doc: docST1004}, - {ID: "ST1005", FilterGenerated: false, Fn: c.CheckErrorStrings, Doc: docST1005}, - {ID: "ST1006", FilterGenerated: false, Fn: c.CheckReceiverNames, Doc: docST1006}, - // {ID: "ST1007", FilterGenerated: true, Fn: c.CheckIncDec, Doc: docST1007}, - {ID: "ST1008", FilterGenerated: false, Fn: c.CheckErrorReturn, Doc: docST1008}, - // {ID: "ST1009", FilterGenerated: false, Fn: c.CheckUnexportedReturn, Doc: docST1009}, - // {ID: "ST1010", FilterGenerated: false, Fn: c.CheckContextFirstArg, Doc: docST1010}, - {ID: "ST1011", FilterGenerated: false, Fn: c.CheckTimeNames, Doc: docST1011}, - {ID: "ST1012", FilterGenerated: false, Fn: c.CheckErrorVarNames, Doc: docST1012}, - {ID: "ST1013", FilterGenerated: true, Fn: c.CheckHTTPStatusCodes, Doc: docST1013}, - {ID: "ST1015", FilterGenerated: true, Fn: c.CheckDefaultCaseOrder, Doc: docST1015}, - {ID: "ST1016", FilterGenerated: false, Fn: c.CheckReceiverNamesIdentical, Doc: docST1016}, - {ID: "ST1017", FilterGenerated: true, Fn: c.CheckYodaConditions, Doc: docST1017}, - {ID: "ST1018", FilterGenerated: false, Fn: c.CheckInvisibleCharacters, Doc: docST1018}, - } -} - -func (c *Checker) CheckPackageComment(j *lint.Job) { +func CheckPackageComment(pass *analysis.Pass) (interface{}, error) { // - At least one file in a non-main package should have a package comment // // - The comment should be of the form @@ -62,57 +31,59 @@ func (c *Checker) CheckPackageComment(j *lint.Job) { // which case they get appended. But that doesn't happen a lot in // the real world. - if j.Pkg.Name == "main" { - return + if pass.Pkg.Name() == "main" { + return nil, nil } hasDocs := false - for _, f := range j.Pkg.Syntax { - if IsInTest(j, f) { + for _, f := range pass.Files { + if IsInTest(pass, f) { continue } if f.Doc != nil && len(f.Doc.List) > 0 { hasDocs = true prefix := "Package " + f.Name.Name + " " if !strings.HasPrefix(strings.TrimSpace(f.Doc.Text()), prefix) { - j.Errorf(f.Doc, `package comment should be of the form "%s..."`, prefix) + ReportNodef(pass, f.Doc, `package comment should be of the form "%s..."`, prefix) } f.Doc.Text() } } if !hasDocs { - for _, f := range j.Pkg.Syntax { - if IsInTest(j, f) { + for _, f := range pass.Files { + if IsInTest(pass, f) { continue } - j.Errorf(f, "at least one file in a package should have a package comment") + ReportNodef(pass, f, "at least one file in a package should have a package comment") } } + return nil, nil } -func (c *Checker) CheckDotImports(j *lint.Job) { - for _, f := range j.Pkg.Syntax { +func CheckDotImports(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { imports: for _, imp := range f.Imports { path := imp.Path.Value path = path[1 : len(path)-1] - for _, w := range j.Pkg.Config.DotImportWhitelist { + for _, w := range config.For(pass).DotImportWhitelist { if w == path { continue imports } } - if imp.Name != nil && imp.Name.Name == "." && !IsInTest(j, f) { - j.Errorf(imp, "should not use dot imports") + if imp.Name != nil && imp.Name.Name == "." && !IsInTest(pass, f) { + ReportNodefFG(pass, imp, "should not use dot imports") } } } + return nil, nil } -func (c *Checker) CheckBlankImports(j *lint.Job) { - fset := j.Pkg.Fset - for _, f := range j.Pkg.Syntax { - if IsInMain(j, f) || IsInTest(j, f) { +func CheckBlankImports(pass *analysis.Pass) (interface{}, error) { + fset := pass.Fset + for _, f := range pass.Files { + if IsInMain(pass, f) || IsInTest(pass, f) { continue } @@ -161,13 +132,14 @@ func (c *Checker) CheckBlankImports(j *lint.Job) { } if imp.Doc == nil && imp.Comment == nil && !skip[imp] { - j.Errorf(imp, "a blank import should be only in a main or test package, or have a comment justifying it") + ReportNodef(pass, imp, "a blank import should be only in a main or test package, or have a comment justifying it") } } } + return nil, nil } -func (c *Checker) CheckIncDec(j *lint.Job) { +func CheckIncDec(pass *analysis.Pass) (interface{}, error) { // TODO(dh): this can be noisy for function bodies that look like this: // x += 3 // ... @@ -192,14 +164,15 @@ func (c *Checker) CheckIncDec(j *lint.Job) { suffix = "--" } - j.Errorf(assign, "should replace %s with %s%s", Render(j, assign), Render(j, assign.Lhs[0]), suffix) + ReportNodef(pass, assign, "should replace %s with %s%s", Render(pass, assign), Render(pass, assign.Lhs[0]), suffix) } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckErrorReturn(j *lint.Job) { +func CheckErrorReturn(pass *analysis.Pass) (interface{}, error) { fnLoop: - for _, fn := range j.Pkg.InitialFunctions { + for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { sig := fn.Type().(*types.Signature) rets := sig.Results() if rets == nil || rets.Len() < 2 { @@ -213,21 +186,22 @@ fnLoop: } for i := rets.Len() - 2; i >= 0; i-- { if rets.At(i).Type() == types.Universe.Lookup("error").Type() { - j.Errorf(rets.At(i), "error should be returned as the last argument") + pass.Reportf(rets.At(i).Pos(), "error should be returned as the last argument") continue fnLoop } } } + return nil, nil } // CheckUnexportedReturn checks that exported functions on exported // types do not return unexported types. -func (c *Checker) CheckUnexportedReturn(j *lint.Job) { - for _, fn := range j.Pkg.InitialFunctions { +func CheckUnexportedReturn(pass *analysis.Pass) (interface{}, error) { + for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { if fn.Synthetic != "" || fn.Parent() != nil { continue } - if !ast.IsExported(fn.Name()) || IsInMain(j, fn) || IsInTest(j, fn) { + if !ast.IsExported(fn.Name()) || IsInMain(pass, fn) || IsInTest(pass, fn) { continue } sig := fn.Type().(*types.Signature) @@ -239,14 +213,16 @@ func (c *Checker) CheckUnexportedReturn(j *lint.Job) { if named, ok := DereferenceR(res.At(i).Type()).(*types.Named); ok && !ast.IsExported(named.Obj().Name()) && named != types.Universe.Lookup("error").Type() { - j.Errorf(fn, "should not return unexported type") + pass.Reportf(fn.Pos(), "should not return unexported type") } } } + return nil, nil } -func (c *Checker) CheckReceiverNames(j *lint.Job) { - for _, m := range j.Pkg.SSA.Members { +func CheckReceiverNames(pass *analysis.Pass) (interface{}, error) { + ssapkg := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).Pkg + for _, m := range ssapkg.Members { if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() { ms := typeutil.IntuitiveMethodSet(T.Type(), nil) for _, sel := range ms { @@ -257,18 +233,20 @@ func (c *Checker) CheckReceiverNames(j *lint.Job) { continue } if recv.Name() == "self" || recv.Name() == "this" { - j.Errorf(recv, `receiver name should be a reflection of its identity; don't use generic names such as "this" or "self"`) + ReportfFG(pass, recv.Pos(), `receiver name should be a reflection of its identity; don't use generic names such as "this" or "self"`) } if recv.Name() == "_" { - j.Errorf(recv, "receiver name should not be an underscore, omit the name if it is unused") + ReportfFG(pass, recv.Pos(), "receiver name should not be an underscore, omit the name if it is unused") } } } } + return nil, nil } -func (c *Checker) CheckReceiverNamesIdentical(j *lint.Job) { - for _, m := range j.Pkg.SSA.Members { +func CheckReceiverNamesIdentical(pass *analysis.Pass) (interface{}, error) { + ssapkg := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).Pkg + for _, m := range ssapkg.Members { names := map[string]int{} var firstFn *types.Func @@ -296,16 +274,17 @@ func (c *Checker) CheckReceiverNamesIdentical(j *lint.Job) { seen = append(seen, fmt.Sprintf("%dx %q", count, name)) } - j.Errorf(firstFn, "methods on the same type should have the same receiver name (seen %s)", strings.Join(seen, ", ")) + pass.Reportf(firstFn.Pos(), "methods on the same type should have the same receiver name (seen %s)", strings.Join(seen, ", ")) } } + return nil, nil } -func (c *Checker) CheckContextFirstArg(j *lint.Job) { +func CheckContextFirstArg(pass *analysis.Pass) (interface{}, error) { // TODO(dh): this check doesn't apply to test helpers. Example from the stdlib: // func helperCommandContext(t *testing.T, ctx context.Context, s ...string) (cmd *exec.Cmd) { fnLoop: - for _, fn := range j.Pkg.InitialFunctions { + for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { if fn.Synthetic != "" || fn.Parent() != nil { continue } @@ -319,28 +298,29 @@ fnLoop: for i := 1; i < params.Len(); i++ { param := params.At(i) if types.TypeString(param.Type(), nil) == "context.Context" { - j.Errorf(param, "context.Context should be the first argument of a function") + pass.Reportf(param.Pos(), "context.Context should be the first argument of a function") continue fnLoop } } } + return nil, nil } -func (c *Checker) CheckErrorStrings(j *lint.Job) { +func CheckErrorStrings(pass *analysis.Pass) (interface{}, error) { objNames := map[*ssa.Package]map[string]bool{} - ssapkg := j.Pkg.SSA + ssapkg := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).Pkg objNames[ssapkg] = map[string]bool{} for _, m := range ssapkg.Members { if typ, ok := m.(*ssa.Type); ok { objNames[ssapkg][typ.Name()] = true } } - for _, fn := range j.Pkg.InitialFunctions { + for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { objNames[fn.Package()][fn.Name()] = true } - for _, fn := range j.Pkg.InitialFunctions { - if IsInTest(j, fn) { + for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs { + if IsInTest(pass, fn) { // We don't care about malformed error messages in tests; // they're usually for direct human consumption, not part // of an API @@ -368,7 +348,7 @@ func (c *Checker) CheckErrorStrings(j *lint.Job) { } switch s[len(s)-1] { case '.', ':', '!', '\n': - j.Errorf(call, "error strings should not end with punctuation or a newline") + pass.Reportf(call.Pos(), "error strings should not end with punctuation or a newline") } idx := strings.IndexByte(s, ' ') if idx == -1 { @@ -402,13 +382,14 @@ func (c *Checker) CheckErrorStrings(j *lint.Job) { // // It could still be a proper noun, though. - j.Errorf(call, "error strings should not be capitalized") + pass.Reportf(call.Pos(), "error strings should not be capitalized") } } } + return nil, nil } -func (c *Checker) CheckTimeNames(j *lint.Job) { +func CheckTimeNames(pass *analysis.Pass) (interface{}, error) { suffixes := []string{ "Sec", "Secs", "Seconds", "Msec", "Msecs", @@ -423,31 +404,32 @@ func (c *Checker) CheckTimeNames(j *lint.Job) { for _, name := range names { for _, suffix := range suffixes { if strings.HasSuffix(name.Name, suffix) { - j.Errorf(name, "var %s is of type %v; don't use unit-specific suffix %q", name.Name, T, suffix) + ReportNodef(pass, name, "var %s is of type %v; don't use unit-specific suffix %q", name.Name, T, suffix) break } } } } - for _, f := range j.Pkg.Syntax { + for _, f := range pass.Files { ast.Inspect(f, func(node ast.Node) bool { switch node := node.(type) { case *ast.ValueSpec: - T := j.Pkg.TypesInfo.TypeOf(node.Type) + T := pass.TypesInfo.TypeOf(node.Type) fn(T, node.Names) case *ast.FieldList: for _, field := range node.List { - T := j.Pkg.TypesInfo.TypeOf(field.Type) + T := pass.TypesInfo.TypeOf(field.Type) fn(T, field.Names) } } return true }) } + return nil, nil } -func (c *Checker) CheckErrorVarNames(j *lint.Job) { - for _, f := range j.Pkg.Syntax { +func CheckErrorVarNames(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { for _, decl := range f.Decls { gen, ok := decl.(*ast.GenDecl) if !ok || gen.Tok != token.VAR { @@ -461,7 +443,7 @@ func (c *Checker) CheckErrorVarNames(j *lint.Job) { for i, name := range spec.Names { val := spec.Values[i] - if !IsCallToAST(j, val, "errors.New") && !IsCallToAST(j, val, "fmt.Errorf") { + if !IsCallToAST(pass, val, "errors.New") && !IsCallToAST(pass, val, "fmt.Errorf") { continue } @@ -470,12 +452,13 @@ func (c *Checker) CheckErrorVarNames(j *lint.Job) { prefix = "Err" } if !strings.HasPrefix(name.Name, prefix) { - j.Errorf(name, "error var %s should have name of the form %sFoo", name.Name, prefix) + ReportNodef(pass, name, "error var %s should have name of the form %sFoo", name.Name, prefix) } } } } } + return nil, nil } var httpStatusCodes = map[int]string{ @@ -540,19 +523,22 @@ var httpStatusCodes = map[int]string{ 511: "StatusNetworkAuthenticationRequired", } -func (c *Checker) CheckHTTPStatusCodes(j *lint.Job) { +func CheckHTTPStatusCodes(pass *analysis.Pass) (interface{}, error) { whitelist := map[string]bool{} - for _, code := range j.Pkg.Config.HTTPStatusCodeWhitelist { + for _, code := range config.For(pass).HTTPStatusCodeWhitelist { whitelist[code] = true } fn := func(node ast.Node) bool { + if node == nil { + return true + } call, ok := node.(*ast.CallExpr) if !ok { return true } var arg int - switch CallNameAST(j, call) { + switch CallNameAST(pass, call) { case "net/http.Error": arg = 2 case "net/http.Redirect": @@ -580,29 +566,32 @@ func (c *Checker) CheckHTTPStatusCodes(j *lint.Job) { if !ok { return true } - j.Errorf(lit, "should use constant http.%s instead of numeric literal %d", s, n) + ReportNodefFG(pass, lit, "should use constant http.%s instead of numeric literal %d", s, n) return true } - for _, f := range j.Pkg.Syntax { + // OPT(dh): replace with inspector + for _, f := range pass.Files { ast.Inspect(f, fn) } + return nil, nil } -func (c *Checker) CheckDefaultCaseOrder(j *lint.Job) { +func CheckDefaultCaseOrder(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { stmt := node.(*ast.SwitchStmt) list := stmt.Body.List for i, c := range list { if c.(*ast.CaseClause).List == nil && i != 0 && i != len(list)-1 { - j.Errorf(c, "default case should be first or last in switch statement") + ReportNodefFG(pass, c, "default case should be first or last in switch statement") break } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.SwitchStmt)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.SwitchStmt)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckYodaConditions(j *lint.Job) { +func CheckYodaConditions(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { cond := node.(*ast.BinaryExpr) if cond.Op != token.EQL && cond.Op != token.NEQ { @@ -615,12 +604,13 @@ func (c *Checker) CheckYodaConditions(j *lint.Job) { // Don't flag lit == lit conditions, just in case return } - j.Errorf(cond, "don't use Yoda conditions") + ReportNodefFG(pass, cond, "don't use Yoda conditions") } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn) + return nil, nil } -func (c *Checker) CheckInvisibleCharacters(j *lint.Job) { +func CheckInvisibleCharacters(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { lit := node.(*ast.BasicLit) if lit.Kind != token.STRING { @@ -628,11 +618,12 @@ func (c *Checker) CheckInvisibleCharacters(j *lint.Job) { } for _, r := range lit.Value { if unicode.Is(unicode.Cf, r) { - j.Errorf(lit, "string literal contains the Unicode format character %U, consider using the %q escape sequence", r, r) + ReportNodef(pass, lit, "string literal contains the Unicode format character %U, consider using the %q escape sequence", r, r) } else if unicode.Is(unicode.Cc, r) && r != '\n' && r != '\t' && r != '\r' { - j.Errorf(lit, "string literal contains the Unicode control character %U, consider using the %q escape sequence", r, r) + ReportNodef(pass, lit, "string literal contains the Unicode control character %U, consider using the %q escape sequence", r, r) } } } - j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BasicLit)(nil)}, fn) + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BasicLit)(nil)}, fn) + return nil, nil } diff --git a/vendor/honnef.co/go/tools/stylecheck/names.go b/vendor/honnef.co/go/tools/stylecheck/names.go index 1c0718fddb..160f9d7ff7 100644 --- a/vendor/honnef.co/go/tools/stylecheck/names.go +++ b/vendor/honnef.co/go/tools/stylecheck/names.go @@ -9,7 +9,8 @@ import ( "strings" "unicode" - "honnef.co/go/tools/lint" + "golang.org/x/tools/go/analysis" + "honnef.co/go/tools/config" . "honnef.co/go/tools/lint/lintdsl" ) @@ -21,7 +22,7 @@ var knownNameExceptions = map[string]bool{ "kWh": true, } -func (c *Checker) CheckNames(j *lint.Job) { +func CheckNames(pass *analysis.Pass) (interface{}, error) { // A large part of this function is copied from // github.com/golang/lint, Copyright (c) 2013 The Go Authors, // licensed under the BSD 3-clause license. @@ -45,7 +46,7 @@ func (c *Checker) CheckNames(j *lint.Job) { // Handle two common styles from other languages that don't belong in Go. if len(id.Name) >= 5 && allCaps(id.Name) && strings.Contains(id.Name, "_") { - j.Errorf(id, "should not use ALL_CAPS in Go names; use CamelCase instead") + ReportfFG(pass, id.Pos(), "should not use ALL_CAPS in Go names; use CamelCase instead") return } @@ -55,10 +56,10 @@ func (c *Checker) CheckNames(j *lint.Job) { } if len(id.Name) > 2 && strings.Contains(id.Name[1:len(id.Name)-1], "_") { - j.Errorf(id, "should not use underscores in Go names; %s %s should be %s", thing, id.Name, should) + ReportfFG(pass, id.Pos(), "should not use underscores in Go names; %s %s should be %s", thing, id.Name, should) return } - j.Errorf(id, "%s %s should be %s", thing, id.Name, should) + ReportfFG(pass, id.Pos(), "%s %s should be %s", thing, id.Name, should) } checkList := func(fl *ast.FieldList, thing string, initialisms map[string]bool) { if fl == nil { @@ -71,17 +72,18 @@ func (c *Checker) CheckNames(j *lint.Job) { } } - initialisms := make(map[string]bool, len(j.Pkg.Config.Initialisms)) - for _, word := range j.Pkg.Config.Initialisms { + il := config.For(pass).Initialisms + initialisms := make(map[string]bool, len(il)) + for _, word := range il { initialisms[word] = true } - for _, f := range j.Pkg.Syntax { + for _, f := range pass.Files { // Package names need slightly different handling than other names. if !strings.HasSuffix(f.Name.Name, "_test") && strings.Contains(f.Name.Name, "_") { - j.Errorf(f, "should not use underscores in package names") + ReportfFG(pass, f.Pos(), "should not use underscores in package names") } if strings.IndexFunc(f.Name.Name, unicode.IsUpper) != -1 { - j.Errorf(f, "should not use MixedCaps in package name; %s should be %s", f.Name.Name, strings.ToLower(f.Name.Name)) + ReportfFG(pass, f.Pos(), "should not use MixedCaps in package name; %s should be %s", f.Name.Name, strings.ToLower(f.Name.Name)) } ast.Inspect(f, func(node ast.Node) bool { @@ -104,7 +106,7 @@ func (c *Checker) CheckNames(j *lint.Job) { return true } - if IsInTest(j, v) && (strings.HasPrefix(v.Name.Name, "Example") || strings.HasPrefix(v.Name.Name, "Test") || strings.HasPrefix(v.Name.Name, "Benchmark")) { + if IsInTest(pass, v) && (strings.HasPrefix(v.Name.Name, "Example") || strings.HasPrefix(v.Name.Name, "Test") || strings.HasPrefix(v.Name.Name, "Benchmark")) { return true } @@ -173,6 +175,7 @@ func (c *Checker) CheckNames(j *lint.Job) { return true }) } + return nil, nil } // lintName returns a different name if it should be different. diff --git a/vendor/honnef.co/go/tools/unused/edge.go b/vendor/honnef.co/go/tools/unused/edge.go new file mode 100644 index 0000000000..02e0d09cf2 --- /dev/null +++ b/vendor/honnef.co/go/tools/unused/edge.go @@ -0,0 +1,54 @@ +package unused + +//go:generate stringer -type edgeKind +type edgeKind uint64 + +func (e edgeKind) is(o edgeKind) bool { + return e&o != 0 +} + +const ( + edgeAlias edgeKind = 1 << iota + edgeBlankField + edgeAnonymousStruct + edgeCgoExported + edgeConstGroup + edgeElementType + edgeEmbeddedInterface + edgeExportedConstant + edgeExportedField + edgeExportedFunction + edgeExportedMethod + edgeExportedType + edgeExportedVariable + edgeExtendsExportedFields + edgeExtendsExportedMethodSet + edgeFieldAccess + edgeFunctionArgument + edgeFunctionResult + edgeFunctionSignature + edgeImplements + edgeInstructionOperand + edgeInterfaceCall + edgeInterfaceMethod + edgeKeyType + edgeLinkname + edgeMainFunction + edgeNamedType + edgeNetRPCRegister + edgeNoCopySentinel + edgeProvidesMethod + edgeReceiver + edgeRuntimeFunction + edgeSignature + edgeStructConversion + edgeTestSink + edgeTupleElement + edgeType + edgeTypeName + edgeUnderlyingType + edgePointerType + edgeUnsafeConversion + edgeUsedConstant + edgeVarDecl +) diff --git a/vendor/honnef.co/go/tools/unused/edgekind_string.go b/vendor/honnef.co/go/tools/unused/edgekind_string.go new file mode 100644 index 0000000000..7629636cf1 --- /dev/null +++ b/vendor/honnef.co/go/tools/unused/edgekind_string.go @@ -0,0 +1,109 @@ +// Code generated by "stringer -type edgeKind"; DO NOT EDIT. + +package unused + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[edgeAlias-1] + _ = x[edgeBlankField-2] + _ = x[edgeAnonymousStruct-4] + _ = x[edgeCgoExported-8] + _ = x[edgeConstGroup-16] + _ = x[edgeElementType-32] + _ = x[edgeEmbeddedInterface-64] + _ = x[edgeExportedConstant-128] + _ = x[edgeExportedField-256] + _ = x[edgeExportedFunction-512] + _ = x[edgeExportedMethod-1024] + _ = x[edgeExportedType-2048] + _ = x[edgeExportedVariable-4096] + _ = x[edgeExtendsExportedFields-8192] + _ = x[edgeExtendsExportedMethodSet-16384] + _ = x[edgeFieldAccess-32768] + _ = x[edgeFunctionArgument-65536] + _ = x[edgeFunctionResult-131072] + _ = x[edgeFunctionSignature-262144] + _ = x[edgeImplements-524288] + _ = x[edgeInstructionOperand-1048576] + _ = x[edgeInterfaceCall-2097152] + _ = x[edgeInterfaceMethod-4194304] + _ = x[edgeKeyType-8388608] + _ = x[edgeLinkname-16777216] + _ = x[edgeMainFunction-33554432] + _ = x[edgeNamedType-67108864] + _ = x[edgeNetRPCRegister-134217728] + _ = x[edgeNoCopySentinel-268435456] + _ = x[edgeProvidesMethod-536870912] + _ = x[edgeReceiver-1073741824] + _ = x[edgeRuntimeFunction-2147483648] + _ = x[edgeSignature-4294967296] + _ = x[edgeStructConversion-8589934592] + _ = x[edgeTestSink-17179869184] + _ = x[edgeTupleElement-34359738368] + _ = x[edgeType-68719476736] + _ = x[edgeTypeName-137438953472] + _ = x[edgeUnderlyingType-274877906944] + _ = x[edgePointerType-549755813888] + _ = x[edgeUnsafeConversion-1099511627776] + _ = x[edgeUsedConstant-2199023255552] + _ = x[edgeVarDecl-4398046511104] +} + +const _edgeKind_name = "edgeAliasedgeBlankFieldedgeAnonymousStructedgeCgoExportededgeConstGroupedgeElementTypeedgeEmbeddedInterfaceedgeExportedConstantedgeExportedFieldedgeExportedFunctionedgeExportedMethodedgeExportedTypeedgeExportedVariableedgeExtendsExportedFieldsedgeExtendsExportedMethodSetedgeFieldAccessedgeFunctionArgumentedgeFunctionResultedgeFunctionSignatureedgeImplementsedgeInstructionOperandedgeInterfaceCalledgeInterfaceMethodedgeKeyTypeedgeLinknameedgeMainFunctionedgeNamedTypeedgeNetRPCRegisteredgeNoCopySentineledgeProvidesMethodedgeReceiveredgeRuntimeFunctionedgeSignatureedgeStructConversionedgeTestSinkedgeTupleElementedgeTypeedgeTypeNameedgeUnderlyingTypeedgePointerTypeedgeUnsafeConversionedgeUsedConstantedgeVarDecl" + +var _edgeKind_map = map[edgeKind]string{ + 1: _edgeKind_name[0:9], + 2: _edgeKind_name[9:23], + 4: _edgeKind_name[23:42], + 8: _edgeKind_name[42:57], + 16: _edgeKind_name[57:71], + 32: _edgeKind_name[71:86], + 64: _edgeKind_name[86:107], + 128: _edgeKind_name[107:127], + 256: _edgeKind_name[127:144], + 512: _edgeKind_name[144:164], + 1024: _edgeKind_name[164:182], + 2048: _edgeKind_name[182:198], + 4096: _edgeKind_name[198:218], + 8192: _edgeKind_name[218:243], + 16384: _edgeKind_name[243:271], + 32768: _edgeKind_name[271:286], + 65536: _edgeKind_name[286:306], + 131072: _edgeKind_name[306:324], + 262144: _edgeKind_name[324:345], + 524288: _edgeKind_name[345:359], + 1048576: _edgeKind_name[359:381], + 2097152: _edgeKind_name[381:398], + 4194304: _edgeKind_name[398:417], + 8388608: _edgeKind_name[417:428], + 16777216: _edgeKind_name[428:440], + 33554432: _edgeKind_name[440:456], + 67108864: _edgeKind_name[456:469], + 134217728: _edgeKind_name[469:487], + 268435456: _edgeKind_name[487:505], + 536870912: _edgeKind_name[505:523], + 1073741824: _edgeKind_name[523:535], + 2147483648: _edgeKind_name[535:554], + 4294967296: _edgeKind_name[554:567], + 8589934592: _edgeKind_name[567:587], + 17179869184: _edgeKind_name[587:599], + 34359738368: _edgeKind_name[599:615], + 68719476736: _edgeKind_name[615:623], + 137438953472: _edgeKind_name[623:635], + 274877906944: _edgeKind_name[635:653], + 549755813888: _edgeKind_name[653:668], + 1099511627776: _edgeKind_name[668:688], + 2199023255552: _edgeKind_name[688:704], + 4398046511104: _edgeKind_name[704:715], +} + +func (i edgeKind) String() string { + if str, ok := _edgeKind_map[i]; ok { + return str + } + return "edgeKind(" + strconv.FormatInt(int64(i), 10) + ")" +} diff --git a/vendor/honnef.co/go/tools/unused/unused.go b/vendor/honnef.co/go/tools/unused/unused.go index f69bddae8f..152d3692dd 100644 --- a/vendor/honnef.co/go/tools/unused/unused.go +++ b/vendor/honnef.co/go/tools/unused/unused.go @@ -8,13 +8,24 @@ import ( "io" "strings" "sync" + "sync/atomic" + "golang.org/x/tools/go/analysis" "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildssa" "honnef.co/go/tools/lint" "honnef.co/go/tools/lint/lintdsl" "honnef.co/go/tools/ssa" ) +// The graph we construct omits nodes along a path that do not +// contribute any new information to the solution. For example, the +// full graph for a function with a receiver would be Func -> +// Signature -> Var -> Type. However, since signatures cannot be +// unused, and receivers are always considered used, we can compact +// the graph down to Func -> Type. This makes the graph smaller, but +// harder to debug. + // TODO(dh): conversions between structs mark fields as used, but the // conversion itself isn't part of that subgraph. even if the function // containing the conversion is unused, the fields will be marked as @@ -40,6 +51,10 @@ import ( - (2.3) all their aliases. we can't easily track uses of aliases because go/types turns them into uses of the aliased types. assume that if a type is used, so are all of its aliases. + - (2.4) the pointer type. this aids with eagerly implementing + interfaces. if a method that implements an interface is defined on + a pointer receiver, and the pointer type is never used, but the + named type is, then we still want to mark the method as used. - variables and constants use: - their types @@ -114,9 +129,16 @@ import ( https://github.com/dominikh/go-tools/issues/365 +- (11.1) anonymous struct types use all their fields. we cannot + deduplicate struct types, as that leads to order-dependent + reportings. we can't not deduplicate struct types while still + tracking fields, because then each instance of the unnamed type in + the data flow chain will get its own fields, causing false + positives. Thus, we only accurately track fields of named struct + types, and assume that unnamed struct types use all their fields. + - Differences in whole program mode: - - (e1) all packages share a single graph - (e2) types aim to implement all exported interfaces from all packages - (e3) exported identifiers aren't automatically used. for fields and methods this poses extra issues due to reflection. We assume @@ -131,28 +153,6 @@ func assert(b bool) { } } -type Checker struct { - WholeProgram bool - Debug io.Writer - - interfaces []*types.Interface - initialPackages []*lint.Pkg - scopes map[*types.Scope]*ssa.Function - - seenMu sync.Mutex - seen map[token.Position]struct{} - out []types.Object -} - -func (*Checker) Name() string { return "unused" } -func (*Checker) Prefix() string { return "U" } - -func (l *Checker) Checks() []lint.Check { - return []lint.Check{ - {ID: "U1000", FilterGenerated: true, Fn: l.Lint}, - } -} - func typString(obj types.Object) string { switch obj := obj.(type) { case *types.Func: @@ -413,77 +413,123 @@ var runtimeFuncs = map[string]bool{ "write": true, } -func (c *Checker) Init(prog *lint.Program) { - for _, pkg := range prog.AllPackages { - c.interfaces = append(c.interfaces, interfacesFromExportData(pkg.Types)...) - } - c.initialPackages = prog.InitialPackages - c.seen = map[token.Position]struct{}{} - - c.scopes = map[*types.Scope]*ssa.Function{} - for _, pkg := range prog.InitialPackages { - for _, fn := range pkg.InitialFunctions { - if fn.Object() != nil { - scope := fn.Object().(*types.Func).Scope() - c.scopes[scope] = fn - } - } - } - - // This is a hack to work in the confines of "one package per - // job". We do all the actual work in the Init function, and only - // report results in the actual checker function. - var out []types.Object - if c.WholeProgram { - // (e1) all packages share a single graph - out = c.processPkgs(prog.InitialPackages...) - } else { - var wg sync.WaitGroup - var mu sync.Mutex - for _, pkg := range prog.InitialPackages { - pkg := pkg - wg.Add(1) - go func() { - res := c.processPkgs(pkg) - mu.Lock() - out = append(out, res...) - mu.Unlock() - wg.Done() - }() - } - wg.Wait() - } - out2 := make([]types.Object, 0, len(out)) - for _, v := range out { - if _, ok := c.seen[prog.Fset().Position(v.Pos())]; !ok { - out2 = append(out2, v) - } - } - c.out = out2 +type pkg struct { + Fset *token.FileSet + Files []*ast.File + Pkg *types.Package + TypesInfo *types.Info + TypesSizes types.Sizes + SSA *ssa.Package + SrcFuncs []*ssa.Function } -func (c *Checker) Lint(j *lint.Job) { - // The actual work is being done in Init. We only report existing - // results here. - unused := c.out - for _, u := range unused { - if u.Pkg() != j.Pkg.Types { - continue +type Checker struct { + WholeProgram bool + Debug io.Writer + + mu sync.Mutex + initialPackages map[*types.Package]struct{} + allPackages map[*types.Package]struct{} + graph *Graph +} + +func NewChecker(wholeProgram bool) *Checker { + return &Checker{ + initialPackages: map[*types.Package]struct{}{}, + allPackages: map[*types.Package]struct{}{}, + WholeProgram: wholeProgram, + } +} + +func (c *Checker) Analyzer() *analysis.Analyzer { + name := "U1000" + if c.WholeProgram { + name = "U1001" + } + return &analysis.Analyzer{ + Name: name, + Doc: "Unused code", + Run: c.Run, + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + } +} + +func (c *Checker) Run(pass *analysis.Pass) (interface{}, error) { + c.mu.Lock() + if c.graph == nil { + c.graph = NewGraph() + c.graph.wholeProgram = c.WholeProgram + c.graph.fset = pass.Fset + } + + var visit func(pkg *types.Package) + visit = func(pkg *types.Package) { + if _, ok := c.allPackages[pkg]; ok { + return } - name := u.Name() - if sig, ok := u.Type().(*types.Signature); ok && sig.Recv() != nil { - switch sig.Recv().Type().(type) { - case *types.Named, *types.Pointer: - typ := types.TypeString(sig.Recv().Type(), func(*types.Package) string { return "" }) - if len(typ) > 0 && typ[0] == '*' { - name = fmt.Sprintf("(%s).%s", typ, u.Name()) - } else if len(typ) > 0 { - name = fmt.Sprintf("%s.%s", typ, u.Name()) - } + c.allPackages[pkg] = struct{}{} + for _, imp := range pkg.Imports() { + visit(imp) + } + } + visit(pass.Pkg) + + c.initialPackages[pass.Pkg] = struct{}{} + c.mu.Unlock() + + ssapkg := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) + pkg := &pkg{ + Fset: pass.Fset, + Files: pass.Files, + Pkg: pass.Pkg, + TypesInfo: pass.TypesInfo, + TypesSizes: pass.TypesSizes, + SSA: ssapkg.Pkg, + SrcFuncs: ssapkg.SrcFuncs, + } + + c.processPkg(c.graph, pkg) + + return nil, nil +} + +func (c *Checker) ProblemObject(fset *token.FileSet, obj types.Object) lint.Problem { + name := obj.Name() + if sig, ok := obj.Type().(*types.Signature); ok && sig.Recv() != nil { + switch sig.Recv().Type().(type) { + case *types.Named, *types.Pointer: + typ := types.TypeString(sig.Recv().Type(), func(*types.Package) string { return "" }) + if len(typ) > 0 && typ[0] == '*' { + name = fmt.Sprintf("(%s).%s", typ, obj.Name()) + } else if len(typ) > 0 { + name = fmt.Sprintf("%s.%s", typ, obj.Name()) } } - j.Errorf(u, "%s %s is unused", typString(u), name) } + + checkName := "U1000" + if c.WholeProgram { + checkName = "U1001" + } + return lint.Problem{ + Pos: lint.DisplayPosition(fset, obj.Pos()), + Message: fmt.Sprintf("%s %s is unused", typString(obj), name), + Check: checkName, + } +} + +func (c *Checker) Result() []types.Object { + out := c.results() + + out2 := make([]types.Object, 0, len(out)) + for _, v := range out { + if _, ok := c.initialPackages[v.Pkg()]; !ok { + continue + } + out2 = append(out2, v) + } + + return out2 } func (c *Checker) debugf(f string, v ...interface{}) { @@ -497,26 +543,9 @@ func (graph *Graph) quieten(node *Node) { return } switch obj := node.obj.(type) { - case *ssa.Function: - sig := obj.Type().(*types.Signature) - if sig.Recv() != nil { - if node, ok := graph.nodeMaybe(sig.Recv()); ok { - node.quiet = true - } - } - for i := 0; i < sig.Params().Len(); i++ { - if node, ok := graph.nodeMaybe(sig.Params().At(i)); ok { - node.quiet = true - } - } - for i := 0; i < sig.Results().Len(); i++ { - if node, ok := graph.nodeMaybe(sig.Results().At(i)); ok { - node.quiet = true - } - } case *types.Named: for i := 0; i < obj.NumMethods(); i++ { - m := graph.pkg.Prog.FuncValue(obj.Method(i)) + m := obj.Method(i) if node, ok := graph.nodeMaybe(m); ok { node.quiet = true } @@ -537,30 +566,25 @@ func (graph *Graph) quieten(node *Node) { } } -func (c *Checker) processPkgs(pkgs ...*lint.Pkg) []types.Object { - graph := NewGraph() - graph.wholeProgram = c.WholeProgram - graph.scopes = c.scopes - graph.initialPackages = c.initialPackages +func (c *Checker) results() []types.Object { + if c.graph == nil { + // We never analyzed any packages + return nil + } var out []types.Object - for _, pkg := range pkgs { - if pkg.PkgPath == "unsafe" { - continue - } - graph.entry(pkg) - } - if c.WholeProgram { var ifaces []*types.Interface var notIfaces []types.Type // implement as many interfaces as possible - graph.seenTypes.Iterate(func(t types.Type, _ interface{}) { + c.graph.seenTypes.Iterate(func(t types.Type, _ interface{}) { switch t := t.(type) { case *types.Interface: - ifaces = append(ifaces, t) + if t.NumMethods() > 0 { + ifaces = append(ifaces, t) + } default: if _, ok := t.Underlying().(*types.Interface); !ok { notIfaces = append(notIfaces, t) @@ -568,17 +592,28 @@ func (c *Checker) processPkgs(pkgs ...*lint.Pkg) []types.Object { } }) - // OPT(dh): this is not terribly efficient - ifaces = append(ifaces, c.interfaces...) + for pkg := range c.allPackages { + for _, iface := range interfacesFromExportData(pkg) { + if iface.NumMethods() > 0 { + ifaces = append(ifaces, iface) + } + } + } + ctx := &context{ + g: c.graph, + seenTypes: &c.graph.seenTypes, + } // (8.0) handle interfaces // (e2) types aim to implement all exported interfaces from all packages for _, t := range notIfaces { - ms := graph.msCache.MethodSet(t) + // OPT(dh): it is unfortunate that we do not have access + // to a populated method set at this point. + ms := types.NewMethodSet(t) for _, iface := range ifaces { - if sels, ok := graph.implements(t, iface, ms); ok { + if sels, ok := c.graph.implements(t, iface, ms); ok { for _, sel := range sels { - graph.useMethod(t, sel, t, "implements") + c.graph.useMethod(ctx, t, sel, t, edgeImplements) } } } @@ -590,54 +625,46 @@ func (c *Checker) processPkgs(pkgs ...*lint.Pkg) []types.Object { if node.obj == nil { c.debugf("n%d [label=\"Root\"];\n", node.id) } else { - c.debugf("n%d [label=%q];\n", node.id, node.obj) + c.debugf("n%d [label=%q];\n", node.id, fmt.Sprintf("(%T) %s", node.obj, node.obj)) } - for used, reasons := range node.used { - for _, reason := range reasons { - c.debugf("n%d -> n%d [label=%q];\n", node.id, used.id, reason) + for _, e := range node.used { + for i := edgeKind(1); i < 64; i++ { + if e.kind.is(1 << i) { + c.debugf("n%d -> n%d [label=%q];\n", node.id, e.node.id, edgeKind(1< 1 { - cg := &ConstGroup{} - g.see(cg) - for _, spec := range specs { - for _, name := range spec.(*ast.ValueSpec).Names { - obj := pkg.TypesInfo.ObjectOf(name) - // (10.1) const groups - g.seeAndUse(obj, cg, "const group") - g.use(cg, obj, "const group") + stack = append(stack, n) + switch n := n.(type) { + case *ast.FuncDecl: + fn = pkg.TypesInfo.ObjectOf(n.Name).(*types.Func) + fns = append(fns, fn) + ctx.see(fn) + case *ast.GenDecl: + switch n.Tok { + case token.CONST: + groups := lintdsl.GroupSpecs(pkg.Fset, n.Specs) + for _, specs := range groups { + if len(specs) > 1 { + cg := &ConstGroup{} + ctx.see(cg) + for _, spec := range specs { + for _, name := range spec.(*ast.ValueSpec).Names { + obj := pkg.TypesInfo.ObjectOf(name) + // (10.1) const groups + ctx.seeAndUse(obj, cg, edgeConstGroup) + ctx.use(cg, obj, edgeConstGroup) + } + } + } + } + case token.VAR: + for _, spec := range n.Specs { + v := spec.(*ast.ValueSpec) + for _, name := range v.Names { + T := pkg.TypesInfo.TypeOf(name) + if fn != nil { + ctx.seeAndUse(T, fn, edgeVarDecl) + } else { + // TODO(dh): we likely want to make + // the type used by the variable, not + // the package containing the + // variable. But then we have to take + // special care of blank identifiers. + ctx.seeAndUse(T, nil, edgeVarDecl) + } + g.typ(ctx, T, nil) + } + } + case token.TYPE: + for _, spec := range n.Specs { + // go/types doesn't provide a way to go from a + // types.Named to the named type it was based on + // (the t1 in type t2 t1). Therefore we walk the + // AST and process GenDecls. + // + // (2.2) named types use the type they're based on + v := spec.(*ast.TypeSpec) + T := pkg.TypesInfo.TypeOf(v.Type) + obj := pkg.TypesInfo.ObjectOf(v.Name) + ctx.see(obj) + ctx.see(T) + ctx.use(T, obj, edgeType) + g.typ(ctx, obj.Type(), nil) + g.typ(ctx, T, nil) + + if v.Assign != 0 { + aliasFor := obj.(*types.TypeName).Type() + // (2.3) named types use all their aliases. we can't easily track uses of aliases + if isIrrelevant(aliasFor) { + // We do not track the type this is an + // alias for (for example builtins), so + // just mark the alias used. + // + // FIXME(dh): what about aliases declared inside functions? + ctx.use(obj, nil, edgeAlias) + } else { + ctx.see(aliasFor) + ctx.seeAndUse(obj, aliasFor, edgeAlias) } } } } - case token.VAR: - for _, spec := range n.Specs { - v := spec.(*ast.ValueSpec) - for _, name := range v.Names { - T := pkg.TypesInfo.TypeOf(name) - if fn != nil { - g.seeAndUse(T, fn, "var decl") - } else { - g.seeAndUse(T, nil, "var decl") - } - g.typ(T) - } - } - case token.TYPE: - for _, spec := range n.Specs { - // go/types doesn't provide a way to go from a - // types.Named to the named type it was based on - // (the t1 in type t2 t1). Therefore we walk the - // AST and process GenDecls. - // - // (2.2) named types use the type they're based on - v := spec.(*ast.TypeSpec) - T := pkg.TypesInfo.TypeOf(v.Type) - obj := pkg.TypesInfo.ObjectOf(v.Name) - g.see(obj) - g.see(T) - g.use(T, obj, "type") - g.typ(obj.Type()) - g.typ(T) - - if v.Assign != 0 { - aliasFor := obj.(*types.TypeName).Type() - // (2.3) named types use all their aliases. we can't easily track uses of aliases - if isIrrelevant(aliasFor) { - // We do not track the type this is an - // alias for (for example builtins), so - // just mark the alias used. - // - // FIXME(dh): what about aliases declared inside functions? - g.use(obj, nil, "alias") - } else { - g.see(aliasFor) - g.seeAndUse(obj, aliasFor, "alias") - } - } - } } - default: - panic(fmt.Sprintf("unreachable: %T", n)) - } - }) + return true + }) + } - for _, m := range g.pkg.Members { + for _, m := range pkg.SSA.Members { switch m := m.(type) { case *ssa.NamedConst: // nothing to do, we collect all constants from Defs case *ssa.Global: if m.Object() != nil { - g.see(m.Object()) - if g.trackExportedIdentifier(m.Object()) { + ctx.see(m.Object()) + if g.trackExportedIdentifier(ctx, m.Object()) { // (1.3) packages use exported variables (unless in package main) - g.use(m.Object(), nil, "exported top-level variable") + ctx.use(m.Object(), nil, edgeExportedVariable) } } case *ssa.Function: - g.see(m) + mObj := owningObject(m) + if mObj != nil { + ctx.see(mObj) + } + //lint:ignore SA9003 handled implicitly if m.Name() == "init" { // (1.5) packages use init functions - g.use(m, nil, "init function") + // + // This is handled implicitly. The generated init + // function has no object, thus everything in it will + // be owned by the package. } // This branch catches top-level functions, not methods. - if m.Object() != nil && g.trackExportedIdentifier(m.Object()) { + if m.Object() != nil && g.trackExportedIdentifier(ctx, m.Object()) { // (1.2) packages use exported functions (unless in package main) - g.use(m, nil, "exported top-level function") + ctx.use(mObj, nil, edgeExportedFunction) } - if m.Name() == "main" && g.pkg.Pkg.Name() == "main" { + if m.Name() == "main" && pkg.Pkg.Name() == "main" { // (1.7) packages use the main function iff in the main package - g.use(m, nil, "main function") + ctx.use(mObj, nil, edgeMainFunction) } - if g.pkg.Pkg.Path() == "runtime" && runtimeFuncs[m.Name()] { + if pkg.Pkg.Path() == "runtime" && runtimeFuncs[m.Name()] { // (9.8) runtime functions that may be called from user code via the compiler - g.use(m, nil, "runtime function") + ctx.use(mObj, nil, edgeRuntimeFunction) } if m.Syntax() != nil { doc := m.Syntax().(*ast.FuncDecl).Doc @@ -1208,29 +1337,29 @@ func (g *Graph) entry(pkg *lint.Pkg) { for _, cmt := range doc.List { if strings.HasPrefix(cmt.Text, "//go:cgo_export_") { // (1.6) packages use functions exported to cgo - g.use(m, nil, "cgo exported") + ctx.use(mObj, nil, edgeCgoExported) } } } } - g.function(m) + g.function(ctx, m) case *ssa.Type: if m.Object() != nil { - g.see(m.Object()) - if g.trackExportedIdentifier(m.Object()) { + ctx.see(m.Object()) + if g.trackExportedIdentifier(ctx, m.Object()) { // (1.1) packages use exported named types (unless in package main) - g.use(m.Object(), nil, "exported top-level type") + ctx.use(m.Object(), nil, edgeExportedType) } } - g.typ(m.Type()) + g.typ(ctx, m.Type(), nil) default: panic(fmt.Sprintf("unreachable: %T", m)) } } if !g.wholeProgram { - // When not in whole program mode we process one package per - // graph, which means g.seenTypes only contains types of + // When not in whole program mode we reset seenTypes after each package, + // which means g.seenTypes only contains types of // interest to us. In whole program mode, we're better off // processing all interfaces at once, globally, both for // performance reasons and because in whole program mode we @@ -1240,7 +1369,7 @@ func (g *Graph) entry(pkg *lint.Pkg) { var ifaces []*types.Interface var notIfaces []types.Type - g.seenTypes.Iterate(func(t types.Type, _ interface{}) { + ctx.seenTypes.Iterate(func(t types.Type, _ interface{}) { switch t := t.(type) { case *types.Interface: // OPT(dh): (8.1) we only need interfaces that have unexported methods @@ -1254,11 +1383,11 @@ func (g *Graph) entry(pkg *lint.Pkg) { // (8.0) handle interfaces for _, t := range notIfaces { - ms := g.msCache.MethodSet(t) + ms := pkg.SSA.Prog.MethodSets.MethodSet(t) for _, iface := range ifaces { if sels, ok := g.implements(t, iface, ms); ok { for _, sel := range sels { - g.useMethod(t, sel, t, "implements") + g.useMethod(ctx, t, sel, t, edgeImplements) } } } @@ -1266,7 +1395,7 @@ func (g *Graph) entry(pkg *lint.Pkg) { } } -func (g *Graph) useMethod(t types.Type, sel *types.Selection, by interface{}, reason string) { +func (g *Graph) useMethod(ctx *context, t types.Type, sel *types.Selection, by interface{}, kind edgeKind) { obj := sel.Obj() path := sel.Index() assert(obj != nil) @@ -1275,66 +1404,94 @@ func (g *Graph) useMethod(t types.Type, sel *types.Selection, by interface{}, re for _, idx := range path[:len(path)-1] { next := base.Field(idx) // (6.3) structs use embedded fields that help implement interfaces - g.seeAndUse(next, base, "provides method") + ctx.see(base) + ctx.seeAndUse(next, base, edgeProvidesMethod) base, _ = lintdsl.Dereference(next.Type()).Underlying().(*types.Struct) } } - if fn := g.pkg.Prog.FuncValue(obj.(*types.Func)); fn != nil { - // actual function - g.seeAndUse(fn, by, reason) - } else { - // interface method - g.seeAndUse(obj, by, reason) - } + ctx.seeAndUse(obj, by, kind) } -func (g *Graph) function(fn *ssa.Function) { - if fn.Package() != nil && fn.Package() != g.pkg { +func owningObject(fn *ssa.Function) types.Object { + if fn.Object() != nil { + return fn.Object() + } + if fn.Parent() != nil { + return owningObject(fn.Parent()) + } + return nil +} + +func (g *Graph) function(ctx *context, fn *ssa.Function) { + if fn.Package() != nil && fn.Package() != ctx.pkg.SSA { return } - if _, ok := g.seenFns[fn]; ok { + + name := fn.RelString(nil) + if _, ok := ctx.seenFns[name]; ok { return } - g.seenFns[fn] = struct{}{} + ctx.seenFns[name] = struct{}{} // (4.1) functions use all their arguments, return parameters and receivers - g.seeAndUse(fn.Signature, fn, "function signature") - g.signature(fn.Signature) - g.instructions(fn) + g.signature(ctx, fn.Signature, owningObject(fn)) + g.instructions(ctx, fn) for _, anon := range fn.AnonFuncs { // (4.2) functions use anonymous functions defined beneath them - g.seeAndUse(anon, fn, "anonymous function") - g.function(anon) + // + // This fact is expressed implicitly. Anonymous functions have + // no types.Object, so their owner is the surrounding + // function. + g.function(ctx, anon) } } -func (g *Graph) typ(t types.Type) { - if g.seenTypes.At(t) != nil { +func (g *Graph) typ(ctx *context, t types.Type, parent types.Type) { + if g.wholeProgram { + g.mu.Lock() + } + if ctx.seenTypes.At(t) != nil { + if g.wholeProgram { + g.mu.Unlock() + } return } + if g.wholeProgram { + g.mu.Unlock() + } if t, ok := t.(*types.Named); ok && t.Obj().Pkg() != nil { - if t.Obj().Pkg() != g.pkg.Pkg { + if t.Obj().Pkg() != ctx.pkg.Pkg { return } } - g.seenTypes.Set(t, struct{}{}) + + if g.wholeProgram { + g.mu.Lock() + } + ctx.seenTypes.Set(t, struct{}{}) + if g.wholeProgram { + g.mu.Unlock() + } if isIrrelevant(t) { return } - g.see(t) + ctx.see(t) switch t := t.(type) { case *types.Struct: for i := 0; i < t.NumFields(); i++ { - g.see(t.Field(i)) + ctx.see(t.Field(i)) if t.Field(i).Exported() { // (6.2) structs use exported fields - g.use(t.Field(i), t, "exported struct field") + ctx.use(t.Field(i), t, edgeExportedField) } else if t.Field(i).Name() == "_" { - g.use(t.Field(i), t, "blank field") + ctx.use(t.Field(i), t, edgeBlankField) } else if isNoCopyType(t.Field(i).Type()) { // (6.1) structs use fields of type NoCopy sentinel - g.use(t.Field(i), t, "NoCopy sentinel") + ctx.use(t.Field(i), t, edgeNoCopySentinel) + } else if parent == nil { + // (11.1) anonymous struct types use all their fields. + ctx.use(t.Field(i), t, edgeAnonymousStruct) } if t.Field(i).Anonymous() { // (e3) exported identifiers aren't automatically used. @@ -1346,11 +1503,11 @@ func (g *Graph) typ(t types.Type) { // the pointer type to get the full method set T = types.NewPointer(T) } - ms := g.msCache.MethodSet(T) + ms := ctx.pkg.SSA.Prog.MethodSets.MethodSet(T) for j := 0; j < ms.Len(); j++ { if ms.At(j).Obj().Exported() { // (6.4) structs use embedded fields that have exported methods (recursively) - g.use(t.Field(i), t, "extends exported method set") + ctx.use(t.Field(i), t, edgeExtendsExportedMethodSet) break } } @@ -1381,106 +1538,116 @@ func (g *Graph) typ(t types.Type) { // does the embedded field contribute exported fields? if hasExportedField(t.Field(i).Type()) { // (6.5) structs use embedded structs that have exported fields (recursively) - g.use(t.Field(i), t, "extends exported fields") + ctx.use(t.Field(i), t, edgeExtendsExportedFields) } } - g.variable(t.Field(i)) + g.variable(ctx, t.Field(i)) } case *types.Basic: // Nothing to do case *types.Named: // (9.3) types use their underlying and element types - g.seeAndUse(t.Underlying(), t, "underlying type") - g.seeAndUse(t.Obj(), t, "type name") - g.seeAndUse(t, t.Obj(), "named type") + ctx.seeAndUse(t.Underlying(), t, edgeUnderlyingType) + ctx.seeAndUse(t.Obj(), t, edgeTypeName) + ctx.seeAndUse(t, t.Obj(), edgeNamedType) - for i := 0; i < t.NumMethods(); i++ { - meth := g.pkg.Prog.FuncValue(t.Method(i)) - g.see(meth) - // don't use trackExportedIdentifier here, we care about - // all exported methods, even in package main or in tests. - if meth.Object() != nil && meth.Object().Exported() && !g.wholeProgram { - // (2.1) named types use exported methods - g.use(meth, t, "exported method") - } - g.function(meth) + // (2.4) named types use the pointer type + if _, ok := t.Underlying().(*types.Interface); !ok && t.NumMethods() > 0 { + ctx.seeAndUse(types.NewPointer(t), t, edgePointerType) } - g.typ(t.Underlying()) + for i := 0; i < t.NumMethods(); i++ { + ctx.see(t.Method(i)) + // don't use trackExportedIdentifier here, we care about + // all exported methods, even in package main or in tests. + if t.Method(i).Exported() && !g.wholeProgram { + // (2.1) named types use exported methods + ctx.use(t.Method(i), t, edgeExportedMethod) + } + g.function(ctx, ctx.pkg.SSA.Prog.FuncValue(t.Method(i))) + } + + g.typ(ctx, t.Underlying(), t) case *types.Slice: // (9.3) types use their underlying and element types - g.seeAndUse(t.Elem(), t, "element type") - g.typ(t.Elem()) + ctx.seeAndUse(t.Elem(), t, edgeElementType) + g.typ(ctx, t.Elem(), nil) case *types.Map: // (9.3) types use their underlying and element types - g.seeAndUse(t.Elem(), t, "element type") + ctx.seeAndUse(t.Elem(), t, edgeElementType) // (9.3) types use their underlying and element types - g.seeAndUse(t.Key(), t, "key type") - g.typ(t.Elem()) - g.typ(t.Key()) + ctx.seeAndUse(t.Key(), t, edgeKeyType) + g.typ(ctx, t.Elem(), nil) + g.typ(ctx, t.Key(), nil) case *types.Signature: - g.signature(t) + g.signature(ctx, t, nil) case *types.Interface: for i := 0; i < t.NumMethods(); i++ { m := t.Method(i) // (8.3) All interface methods are marked as used - g.seeAndUse(m, t, "interface method") - g.seeAndUse(m.Type().(*types.Signature), m, "signature") - g.signature(m.Type().(*types.Signature)) + ctx.seeAndUse(m, t, edgeInterfaceMethod) + ctx.seeAndUse(m.Type().(*types.Signature), m, edgeSignature) + g.signature(ctx, m.Type().(*types.Signature), nil) } for i := 0; i < t.NumEmbeddeds(); i++ { tt := t.EmbeddedType(i) // (8.4) All embedded interfaces are marked as used - g.seeAndUse(tt, t, "embedded interface") + ctx.seeAndUse(tt, t, edgeEmbeddedInterface) } case *types.Array: // (9.3) types use their underlying and element types - g.seeAndUse(t.Elem(), t, "element type") - g.typ(t.Elem()) + ctx.seeAndUse(t.Elem(), t, edgeElementType) + g.typ(ctx, t.Elem(), nil) case *types.Pointer: // (9.3) types use their underlying and element types - g.seeAndUse(t.Elem(), t, "element type") - g.typ(t.Elem()) + ctx.seeAndUse(t.Elem(), t, edgeElementType) + g.typ(ctx, t.Elem(), nil) case *types.Chan: // (9.3) types use their underlying and element types - g.seeAndUse(t.Elem(), t, "element type") - g.typ(t.Elem()) + ctx.seeAndUse(t.Elem(), t, edgeElementType) + g.typ(ctx, t.Elem(), nil) case *types.Tuple: for i := 0; i < t.Len(); i++ { // (9.3) types use their underlying and element types - g.seeAndUse(t.At(i), t, "tuple element") - g.variable(t.At(i)) + ctx.seeAndUse(t.At(i).Type(), t, edgeTupleElement|edgeType) + g.typ(ctx, t.At(i).Type(), nil) } default: panic(fmt.Sprintf("unreachable: %T", t)) } } -func (g *Graph) variable(v *types.Var) { +func (g *Graph) variable(ctx *context, v *types.Var) { // (9.2) variables use their types - g.seeAndUse(v.Type(), v, "variable type") - g.typ(v.Type()) + ctx.seeAndUse(v.Type(), v, edgeType) + g.typ(ctx, v.Type(), nil) } -func (g *Graph) signature(sig *types.Signature) { +func (g *Graph) signature(ctx *context, sig *types.Signature, fn types.Object) { + var user interface{} = fn + if fn == nil { + user = sig + ctx.see(sig) + } if sig.Recv() != nil { - g.seeAndUse(sig.Recv(), sig, "receiver") - g.variable(sig.Recv()) + ctx.seeAndUse(sig.Recv().Type(), user, edgeReceiver|edgeType) + g.typ(ctx, sig.Recv().Type(), nil) } for i := 0; i < sig.Params().Len(); i++ { param := sig.Params().At(i) - g.seeAndUse(param, sig, "function argument") - g.variable(param) + ctx.seeAndUse(param.Type(), user, edgeFunctionArgument|edgeType) + g.typ(ctx, param.Type(), nil) } for i := 0; i < sig.Results().Len(); i++ { param := sig.Results().At(i) - g.seeAndUse(param, sig, "function result") - g.variable(param) + ctx.seeAndUse(param.Type(), user, edgeFunctionResult|edgeType) + g.typ(ctx, param.Type(), nil) } } -func (g *Graph) instructions(fn *ssa.Function) { +func (g *Graph) instructions(ctx *context, fn *ssa.Function) { + fnObj := owningObject(fn) for _, b := range fn.Blocks { for _, instr := range b.Instrs { ops := instr.Operands(nil) @@ -1499,16 +1666,18 @@ func (g *Graph) instructions(fn *ssa.Function) { // (4.5) functions use functions they call // (9.5) instructions use their operands // (4.4) functions use functions they return. we assume that someone else will call the returned function - g.seeAndUse(v, fn, "instruction operand") - g.function(v) + if owningObject(v) != nil { + ctx.seeAndUse(owningObject(v), fnObj, edgeInstructionOperand) + } + g.function(ctx, v) case *ssa.Const: // (9.6) instructions use their operands' types - g.seeAndUse(v.Type(), fn, "constant's type") - g.typ(v.Type()) + ctx.seeAndUse(v.Type(), fnObj, edgeType) + g.typ(ctx, v.Type(), nil) case *ssa.Global: if v.Object() != nil { // (9.5) instructions use their operands - g.seeAndUse(v.Object(), fn, "instruction operand") + ctx.seeAndUse(v.Object(), fnObj, edgeInstructionOperand) } } }) @@ -1519,8 +1688,8 @@ func (g *Graph) instructions(fn *ssa.Function) { // (4.8) instructions use their types // (9.4) conversions use the type they convert to - g.seeAndUse(v.Type(), fn, "instruction") - g.typ(v.Type()) + ctx.seeAndUse(v.Type(), fnObj, edgeType) + g.typ(ctx, v.Type(), nil) } } switch instr := instr.(type) { @@ -1528,12 +1697,12 @@ func (g *Graph) instructions(fn *ssa.Function) { st := instr.X.Type().Underlying().(*types.Struct) field := st.Field(instr.Field) // (4.7) functions use fields they access - g.seeAndUse(field, fn, "field access") + ctx.seeAndUse(field, fnObj, edgeFieldAccess) case *ssa.FieldAddr: st := lintdsl.Dereference(instr.X.Type()).Underlying().(*types.Struct) field := st.Field(instr.Field) // (4.7) functions use fields they access - g.seeAndUse(field, fn, "field access") + ctx.seeAndUse(field, fnObj, edgeFieldAccess) case *ssa.Store: // nothing to do, handled generically by operands case *ssa.Call: @@ -1559,10 +1728,10 @@ func (g *Graph) instructions(fn *ssa.Function) { walkPhi(arg, func(v ssa.Value) { if v, ok := v.(*ssa.MakeInterface); ok { walkPhi(v.X, func(vv ssa.Value) { - ms := g.msCache.MethodSet(vv.Type()) + ms := ctx.pkg.SSA.Prog.MethodSets.MethodSet(vv.Type()) for i := 0; i < ms.Len(); i++ { if ms.At(i).Obj().Exported() { - g.useMethod(vv.Type(), ms.At(i), fn, "net/rpc.Register") + g.useMethod(ctx, vv.Type(), ms.At(i), fnObj, edgeNetRPCRegister) } } }) @@ -1572,7 +1741,7 @@ func (g *Graph) instructions(fn *ssa.Function) { } } else { // (4.5) functions use functions/interface methods they call - g.seeAndUse(c.Method, fn, "interface call") + ctx.seeAndUse(c.Method, fnObj, edgeInterfaceCall) } case *ssa.Return: // nothing to do, handled generically by operands @@ -1589,14 +1758,14 @@ func (g *Graph) instructions(fn *ssa.Function) { assert(s1.NumFields() == s2.NumFields()) for i := 0; i < s1.NumFields(); i++ { - g.see(s1.Field(i)) - g.see(s2.Field(i)) + ctx.see(s1.Field(i)) + ctx.see(s2.Field(i)) // (5.1) when converting between two equivalent structs, the fields in // either struct use each other. the fields are relevant for the // conversion, but only if the fields are also accessed outside the // conversion. - g.seeAndUse(s1.Field(i), s2.Field(i), "struct conversion") - g.seeAndUse(s2.Field(i), s1.Field(i), "struct conversion") + ctx.seeAndUse(s1.Field(i), s2.Field(i), edgeStructConversion) + ctx.seeAndUse(s2.Field(i), s1.Field(i), edgeStructConversion) } } case *ssa.MakeInterface: @@ -1612,7 +1781,7 @@ func (g *Graph) instructions(fn *ssa.Function) { if st, ok := ptr.Elem().Underlying().(*types.Struct); ok { for i := 0; i < st.NumFields(); i++ { // (5.2) when converting to or from unsafe.Pointer, mark all fields as used. - g.seeAndUse(st.Field(i), fn, "unsafe conversion") + ctx.seeAndUse(st.Field(i), fnObj, edgeUnsafeConversion) } } } @@ -1623,7 +1792,7 @@ func (g *Graph) instructions(fn *ssa.Function) { if st, ok := ptr.Elem().Underlying().(*types.Struct); ok { for i := 0; i < st.NumFields(); i++ { // (5.2) when converting to or from unsafe.Pointer, mark all fields as used. - g.seeAndUse(st.Field(i), fn, "unsafe conversion") + ctx.seeAndUse(st.Field(i), fnObj, edgeUnsafeConversion) } } } diff --git a/vendor/honnef.co/go/tools/version/buildinfo.go b/vendor/honnef.co/go/tools/version/buildinfo.go new file mode 100644 index 0000000000..b6034bb7dc --- /dev/null +++ b/vendor/honnef.co/go/tools/version/buildinfo.go @@ -0,0 +1,46 @@ +// +build go1.12 + +package version + +import ( + "fmt" + "runtime/debug" +) + +func printBuildInfo() { + if info, ok := debug.ReadBuildInfo(); ok { + fmt.Println("Main module:") + printModule(&info.Main) + fmt.Println("Dependencies:") + for _, dep := range info.Deps { + printModule(dep) + } + } else { + fmt.Println("Built without Go modules") + } +} + +func buildInfoVersion() (string, bool) { + info, ok := debug.ReadBuildInfo() + if !ok { + return "", false + } + if info.Main.Version == "(devel)" { + return "", false + } + return info.Main.Version, true +} + +func printModule(m *debug.Module) { + fmt.Printf("\t%s", m.Path) + if m.Version != "(devel)" { + fmt.Printf("@%s", m.Version) + } + if m.Sum != "" { + fmt.Printf(" (sum: %s)", m.Sum) + } + if m.Replace != nil { + fmt.Printf(" (replace: %s)", m.Replace.Path) + } + fmt.Println() +} diff --git a/vendor/honnef.co/go/tools/version/buildinfo111.go b/vendor/honnef.co/go/tools/version/buildinfo111.go new file mode 100644 index 0000000000..06aae1e65b --- /dev/null +++ b/vendor/honnef.co/go/tools/version/buildinfo111.go @@ -0,0 +1,6 @@ +// +build !go1.12 + +package version + +func printBuildInfo() {} +func buildInfoVersion() (string, bool) { return "", false } diff --git a/vendor/honnef.co/go/tools/version/version.go b/vendor/honnef.co/go/tools/version/version.go index 232cf7e74f..468e8efd6e 100644 --- a/vendor/honnef.co/go/tools/version/version.go +++ b/vendor/honnef.co/go/tools/version/version.go @@ -4,14 +4,39 @@ import ( "fmt" "os" "path/filepath" + "runtime" ) -const Version = "devel" +const Version = "2019.2.3" + +// version returns a version descriptor and reports whether the +// version is a known release. +func version() (string, bool) { + if Version != "devel" { + return Version, true + } + v, ok := buildInfoVersion() + if ok { + return v, false + } + return "devel", false +} func Print() { - if Version == "devel" { + v, release := version() + + if release { + fmt.Printf("%s %s\n", filepath.Base(os.Args[0]), v) + } else if v == "devel" { fmt.Printf("%s (no version)\n", filepath.Base(os.Args[0])) } else { - fmt.Printf("%s %s\n", filepath.Base(os.Args[0]), Version) + fmt.Printf("%s (devel, %s)\n", filepath.Base(os.Args[0]), v) } } + +func Verbose() { + Print() + fmt.Println() + fmt.Println("Compiled with Go version:", runtime.Version()) + printBuildInfo() +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 10cfaeb8ac..a7e858c2d9 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -199,7 +199,7 @@ github.com/spf13/cobra github.com/spf13/jwalterweatherman # github.com/spf13/pflag v1.0.5 github.com/spf13/pflag -# github.com/spf13/viper v1.6.3 +# github.com/spf13/viper v1.7.0 github.com/spf13/viper # github.com/stretchr/testify v1.5.1 github.com/stretchr/testify/assert @@ -260,6 +260,8 @@ golang.org/x/text/unicode/bidi golang.org/x/text/unicode/norm golang.org/x/text/width # golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef +golang.org/x/tools/go/analysis +golang.org/x/tools/go/analysis/passes/inspect golang.org/x/tools/go/ast/astutil golang.org/x/tools/go/ast/inspector golang.org/x/tools/go/buildutil @@ -269,8 +271,10 @@ golang.org/x/tools/go/internal/gcimporter golang.org/x/tools/go/internal/packagesdriver golang.org/x/tools/go/loader golang.org/x/tools/go/packages +golang.org/x/tools/go/types/objectpath golang.org/x/tools/go/types/typeutil golang.org/x/tools/imports +golang.org/x/tools/internal/analysisinternal golang.org/x/tools/internal/fastwalk golang.org/x/tools/internal/gocommand golang.org/x/tools/internal/gopathwalk @@ -321,24 +325,26 @@ gopkg.in/gomail.v2 gopkg.in/ini.v1 # gopkg.in/yaml.v2 v2.2.8 gopkg.in/yaml.v2 -# honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a +# honnef.co/go/tools v0.0.1-2019.2.3 honnef.co/go/tools/arg -honnef.co/go/tools/callgraph -honnef.co/go/tools/callgraph/static honnef.co/go/tools/cmd/staticcheck honnef.co/go/tools/config honnef.co/go/tools/deprecated +honnef.co/go/tools/facts honnef.co/go/tools/functions honnef.co/go/tools/go/types/typeutil +honnef.co/go/tools/internal/cache +honnef.co/go/tools/internal/passes/buildssa +honnef.co/go/tools/internal/renameio honnef.co/go/tools/internal/sharedcheck honnef.co/go/tools/lint honnef.co/go/tools/lint/lintdsl honnef.co/go/tools/lint/lintutil honnef.co/go/tools/lint/lintutil/format +honnef.co/go/tools/loader honnef.co/go/tools/printf honnef.co/go/tools/simple honnef.co/go/tools/ssa -honnef.co/go/tools/ssa/ssautil honnef.co/go/tools/ssautil honnef.co/go/tools/staticcheck honnef.co/go/tools/staticcheck/vrp