diff --git a/go.mod b/go.mod
index 72c3e627b..ce42378c8 100644
--- a/go.mod
+++ b/go.mod
@@ -3,21 +3,45 @@ module stash.appscode.dev/mongodb
go 1.12
require (
+ cloud.google.com/go v0.56.0 // indirect
+ github.com/Azure/go-autorest/autorest v0.10.2 // indirect
+ github.com/Azure/go-autorest/autorest/adal v0.8.3 // indirect
github.com/appscode/go v0.0.0-20200928211031-cc0c23082d91
github.com/codeskyblue/go-sh v0.0.0-20190412065543-76bd3d59ff27
github.com/go-openapi/spec v0.19.8
+ github.com/go-openapi/swag v0.19.9 // indirect
github.com/gogo/protobuf v1.3.1
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
+ github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 // indirect
github.com/google/gofuzz v1.1.0
+ github.com/googleapis/gnostic v0.4.0 // indirect
+ github.com/gophercloud/gophercloud v0.11.0 // indirect
+ github.com/grpc-ecosystem/grpc-gateway v1.14.6 // indirect
+ github.com/hashicorp/golang-lru v0.5.4 // indirect
+ github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
+ github.com/kr/pretty v0.2.0 // indirect
+ github.com/mailru/easyjson v0.7.1 // indirect
+ github.com/mattn/go-colorable v0.1.6 // indirect
+ github.com/mitchellh/mapstructure v1.2.2 // indirect
github.com/pkg/errors v0.9.1
+ github.com/prometheus/client_golang v1.6.0 // indirect
github.com/spf13/cobra v1.0.0
github.com/yudai/gojsondiff v1.0.0
go.bytebuilders.dev/license-verifier/kubernetes v0.3.0
+ go.uber.org/atomic v1.6.0 // indirect
+ go.uber.org/zap v1.13.0 // indirect
+ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a // indirect
+ golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
+ golang.org/x/tools v0.0.0-20200603131246-cc40288be839 // indirect
+ google.golang.org/appengine v1.6.6 // indirect
+ google.golang.org/genproto v0.0.0-20200603110839-e855014d5736 // indirect
+ gopkg.in/yaml.v2 v2.3.0 // indirect
k8s.io/api v0.18.9
k8s.io/apiextensions-apiserver v0.18.9
k8s.io/apimachinery v0.18.9
k8s.io/client-go v12.0.0+incompatible
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6
+ k8s.io/utils v0.0.0-20200414100711-2df71ebbae66 // indirect
kmodules.xyz/client-go v0.0.0-20201021051118-03dac1aea508
kmodules.xyz/crd-schema-fuzz v0.0.0-20200922204806-c1426cd7fcf4
kmodules.xyz/custom-resources v0.0.0-20201008012351-6d8090f759d4
diff --git a/go.sum b/go.sum
index d36acf333..5e390c933 100644
--- a/go.sum
+++ b/go.sum
@@ -70,6 +70,7 @@ github.com/aliyun/aliyun-oss-go-sdk v2.0.4+incompatible/go.mod h1:T/Aws4fEfogEE9
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/appscode/docker-registry-client v0.0.0-20180426150142-1bb02bb202b0/go.mod h1:W9bsWfSbgJXUXzBZ+tSBV2g+zzT7ANPHQFsSXRHYKsA=
github.com/appscode/go v0.0.0-20200323182826-54e98e09185a h1:cZ80NKoLRaW1PVCWXAJE+YFkBAmLZ8BnrJmH0ClY1Gs=
@@ -303,6 +304,8 @@ github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE=
+github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
github.com/go-openapi/validate v0.17.2/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
@@ -339,6 +342,8 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4er
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE=
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7 h1:5ZkaAPbicIKTF2I64qf5Fh8Aa83Q/dnOafMYV0OMwjA=
+github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
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=
@@ -401,6 +406,8 @@ github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEo
github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gophercloud/gophercloud v0.6.0 h1:Xb2lcqZtml1XjgYZxbeayEemq7ASbeTp09m36gQFpEU=
github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM=
+github.com/gophercloud/gophercloud v0.11.0 h1:pYMP9UZBdQa3lsfIZ1tZor4EbtxiuB6BHhocenkiH/E=
+github.com/gophercloud/gophercloud v0.11.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
@@ -424,6 +431,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.4/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.12.1 h1:zCy2xE9ablevUOrUZc3Dl72Dt+ya2FNAvC2yLYMHzi4=
github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c=
+github.com/grpc-ecosystem/grpc-gateway v1.14.6 h1:8ERzHx8aj1Sc47mu9n/AksaKCSWrMchFtkdrS4BIj5o=
+github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@@ -456,6 +465,8 @@ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
+github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
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=
@@ -527,9 +538,13 @@ github.com/kmodules/kubernetes v1.19.0-alpha.0.0.20200922200158-8b13196d8dc4 h1:
github.com/kmodules/kubernetes v1.19.0-alpha.0.0.20200922200158-8b13196d8dc4/go.mod h1:lTc732ZplaxM3c4QXZez/NbDziB4/fgeywvu0z1RkrE=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
@@ -562,11 +577,15 @@ 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/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
+github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
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-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.0-20191113090002-7c0f6868bffe/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
@@ -577,6 +596,8 @@ github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
@@ -612,6 +633,8 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/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/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
+github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
@@ -852,6 +875,7 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3Ifn
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.bytebuilders.dev/license-verifier v0.3.0 h1:OyxDrV07KfKNh0f+rtvPQYC6BedITIeTIzexW+YiUSs=
go.bytebuilders.dev/license-verifier v0.3.0/go.mod h1:0zzm7+djZ/Ff41BNZYWH80t9mMb42T3Nx2KqQGV9N8s=
go.bytebuilders.dev/license-verifier/kubernetes v0.3.0 h1:qj/pfjtG0uvsTasaNt2DkdH6xXcbLeoRS6HQLCi0k+k=
@@ -877,11 +901,19 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
+go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/automaxprocs v1.2.0/go.mod h1:YfO3fm683kQpzETxlTGZhGIVmXAhaw3gxeBADbpZtnU=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
+go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190927031335-2835ba2e683f/go.mod h1:fYw7AShPAhGMdXqA9gRadk/CcMsvLlClpE5oBwnS3dM=
golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -905,6 +937,7 @@ golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -933,6 +966,8 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -971,7 +1006,9 @@ golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ym
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191112182307-2180aed22343 h1:00ohfJ4K98s3m6BGUoBd8nyfp4Yl0GoIKvw5abItTjI=
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd h1:QPwSajcTUrFriMF1nJ3XzgoqakqQEsnZf9LdXdi2nkI=
@@ -982,6 +1019,8 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/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/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
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=
@@ -989,6 +1028,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180117170059-2c42eef0765b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1033,7 +1074,10 @@ golang.org/x/sys v0.0.0-20191025021431-6c3a3bfe00ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056 h1:dHtDnRWQtSx0Hjq9kvKFpBh9uPPKfQN70NZZmvssGwk=
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f h1:mOhmO9WsBaJCNmaZHPtHs9wOcdqdKCjF6OPJlmDM3KI=
@@ -1053,6 +1097,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZe
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
+golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1086,14 +1132,19 @@ golang.org/x/tools v0.0.0-20190918214516-5a1a30219888/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/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-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191111182352-50fa39b762bc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f h1:kDxGY2VmgABOe55qheT/TFqUMtcTHnomIPS1iv3G4Ms=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
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=
+golang.org/x/tools v0.0.0-20200603131246-cc40288be839 h1:SxYgZ5FbVts/fm9UsuLycOG8MRWJPm7krdhgPQSayUs=
+golang.org/x/tools v0.0.0-20200603131246-cc40288be839/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1123,6 +1174,8 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
+google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
+google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9 h1:6XzpBoANz1NqMNfDXzc2QmHmbb1vyMsvRfoP5rM+K1I=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
@@ -1166,8 +1219,11 @@ gopkg.in/yaml.v2 v2.2.3/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=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
diff --git a/vendor/github.com/BurntSushi/toml/.gitignore b/vendor/github.com/BurntSushi/toml/.gitignore
new file mode 100644
index 000000000..0cd380037
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/.gitignore
@@ -0,0 +1,5 @@
+TAGS
+tags
+.*.swp
+tomlcheck/tomlcheck
+toml.test
diff --git a/vendor/github.com/BurntSushi/toml/.travis.yml b/vendor/github.com/BurntSushi/toml/.travis.yml
new file mode 100644
index 000000000..8b8afc4f0
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/.travis.yml
@@ -0,0 +1,15 @@
+language: go
+go:
+ - 1.1
+ - 1.2
+ - 1.3
+ - 1.4
+ - 1.5
+ - 1.6
+ - tip
+install:
+ - go install ./...
+ - go get github.com/BurntSushi/toml-test
+script:
+ - export PATH="$PATH:$HOME/gopath/bin"
+ - make test
diff --git a/vendor/github.com/BurntSushi/toml/COMPATIBLE b/vendor/github.com/BurntSushi/toml/COMPATIBLE
new file mode 100644
index 000000000..6efcfd0ce
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/COMPATIBLE
@@ -0,0 +1,3 @@
+Compatible with TOML version
+[v0.4.0](https://github.com/toml-lang/toml/blob/v0.4.0/versions/en/toml-v0.4.0.md)
+
diff --git a/vendor/github.com/BurntSushi/toml/COPYING b/vendor/github.com/BurntSushi/toml/COPYING
new file mode 100644
index 000000000..01b574320
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/COPYING
@@ -0,0 +1,21 @@
+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.
diff --git a/vendor/github.com/BurntSushi/toml/Makefile b/vendor/github.com/BurntSushi/toml/Makefile
new file mode 100644
index 000000000..3600848d3
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/Makefile
@@ -0,0 +1,19 @@
+install:
+ go install ./...
+
+test: install
+ go test -v
+ toml-test toml-test-decoder
+ toml-test -encoder toml-test-encoder
+
+fmt:
+ gofmt -w *.go */*.go
+ colcheck *.go */*.go
+
+tags:
+ find ./ -name '*.go' -print0 | xargs -0 gotags > TAGS
+
+push:
+ git push origin master
+ git push github master
+
diff --git a/vendor/github.com/BurntSushi/toml/README.md b/vendor/github.com/BurntSushi/toml/README.md
new file mode 100644
index 000000000..7c1b37ecc
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/README.md
@@ -0,0 +1,218 @@
+## TOML parser and encoder for Go with reflection
+
+TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
+reflection interface similar to Go's standard library `json` and `xml`
+packages. This package also supports the `encoding.TextUnmarshaler` and
+`encoding.TextMarshaler` interfaces so that you can define custom data
+representations. (There is an example of this below.)
+
+Spec: https://github.com/toml-lang/toml
+
+Compatible with TOML version
+[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)
+
+Documentation: https://godoc.org/github.com/BurntSushi/toml
+
+Installation:
+
+```bash
+go get github.com/BurntSushi/toml
+```
+
+Try the toml validator:
+
+```bash
+go get github.com/BurntSushi/toml/cmd/tomlv
+tomlv some-toml-file.toml
+```
+
+[![Build Status](https://travis-ci.org/BurntSushi/toml.svg?branch=master)](https://travis-ci.org/BurntSushi/toml) [![GoDoc](https://godoc.org/github.com/BurntSushi/toml?status.svg)](https://godoc.org/github.com/BurntSushi/toml)
+
+### Testing
+
+This package passes all tests in
+[toml-test](https://github.com/BurntSushi/toml-test) for both the decoder
+and the encoder.
+
+### Examples
+
+This package works similarly to how the Go standard library handles `XML`
+and `JSON`. Namely, data is loaded into Go values via reflection.
+
+For the simplest example, consider some TOML file as just a list of keys
+and values:
+
+```toml
+Age = 25
+Cats = [ "Cauchy", "Plato" ]
+Pi = 3.14
+Perfection = [ 6, 28, 496, 8128 ]
+DOB = 1987-07-05T05:45:00Z
+```
+
+Which could be defined in Go as:
+
+```go
+type Config struct {
+ Age int
+ Cats []string
+ Pi float64
+ Perfection []int
+ DOB time.Time // requires `import time`
+}
+```
+
+And then decoded with:
+
+```go
+var conf Config
+if _, err := toml.Decode(tomlData, &conf); err != nil {
+ // handle error
+}
+```
+
+You can also use struct tags if your struct field name doesn't map to a TOML
+key value directly:
+
+```toml
+some_key_NAME = "wat"
+```
+
+```go
+type TOML struct {
+ ObscureKey string `toml:"some_key_NAME"`
+}
+```
+
+### Using the `encoding.TextUnmarshaler` interface
+
+Here's an example that automatically parses duration strings into
+`time.Duration` values:
+
+```toml
+[[song]]
+name = "Thunder Road"
+duration = "4m49s"
+
+[[song]]
+name = "Stairway to Heaven"
+duration = "8m03s"
+```
+
+Which can be decoded with:
+
+```go
+type song struct {
+ Name string
+ Duration duration
+}
+type songs struct {
+ Song []song
+}
+var favorites songs
+if _, err := toml.Decode(blob, &favorites); err != nil {
+ log.Fatal(err)
+}
+
+for _, s := range favorites.Song {
+ fmt.Printf("%s (%s)\n", s.Name, s.Duration)
+}
+```
+
+And you'll also need a `duration` type that satisfies the
+`encoding.TextUnmarshaler` interface:
+
+```go
+type duration struct {
+ time.Duration
+}
+
+func (d *duration) UnmarshalText(text []byte) error {
+ var err error
+ d.Duration, err = time.ParseDuration(string(text))
+ return err
+}
+```
+
+### More complex usage
+
+Here's an example of how to load the example from the official spec page:
+
+```toml
+# This is a TOML document. Boom.
+
+title = "TOML Example"
+
+[owner]
+name = "Tom Preston-Werner"
+organization = "GitHub"
+bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
+dob = 1979-05-27T07:32:00Z # First class dates? Why not?
+
+[database]
+server = "192.168.1.1"
+ports = [ 8001, 8001, 8002 ]
+connection_max = 5000
+enabled = true
+
+[servers]
+
+ # You can indent as you please. Tabs or spaces. TOML don't care.
+ [servers.alpha]
+ ip = "10.0.0.1"
+ dc = "eqdc10"
+
+ [servers.beta]
+ ip = "10.0.0.2"
+ dc = "eqdc10"
+
+[clients]
+data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
+
+# Line breaks are OK when inside arrays
+hosts = [
+ "alpha",
+ "omega"
+]
+```
+
+And the corresponding Go types are:
+
+```go
+type tomlConfig struct {
+ Title string
+ Owner ownerInfo
+ DB database `toml:"database"`
+ Servers map[string]server
+ Clients clients
+}
+
+type ownerInfo struct {
+ Name string
+ Org string `toml:"organization"`
+ Bio string
+ DOB time.Time
+}
+
+type database struct {
+ Server string
+ Ports []int
+ ConnMax int `toml:"connection_max"`
+ Enabled bool
+}
+
+type server struct {
+ IP string
+ DC string
+}
+
+type clients struct {
+ Data [][]interface{}
+ Hosts []string
+}
+```
+
+Note that a case insensitive match will be tried if an exact match can't be
+found.
+
+A working example of the above can be found in `_examples/example.{go,toml}`.
diff --git a/vendor/github.com/BurntSushi/toml/decode.go b/vendor/github.com/BurntSushi/toml/decode.go
new file mode 100644
index 000000000..b0fd51d5b
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/decode.go
@@ -0,0 +1,509 @@
+package toml
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "math"
+ "reflect"
+ "strings"
+ "time"
+)
+
+func e(format string, args ...interface{}) error {
+ return fmt.Errorf("toml: "+format, args...)
+}
+
+// Unmarshaler is the interface implemented by objects that can unmarshal a
+// TOML description of themselves.
+type Unmarshaler interface {
+ UnmarshalTOML(interface{}) error
+}
+
+// Unmarshal decodes the contents of `p` in TOML format into a pointer `v`.
+func Unmarshal(p []byte, v interface{}) error {
+ _, err := Decode(string(p), v)
+ return err
+}
+
+// Primitive is a TOML value that hasn't been decoded into a Go value.
+// When using the various `Decode*` functions, the type `Primitive` may
+// be given to any value, and its decoding will be delayed.
+//
+// A `Primitive` value can be decoded using the `PrimitiveDecode` function.
+//
+// The underlying representation of a `Primitive` value is subject to change.
+// Do not rely on it.
+//
+// N.B. Primitive values are still parsed, so using them will only avoid
+// the overhead of reflection. They can be useful when you don't know the
+// exact type of TOML data until run time.
+type Primitive struct {
+ undecoded interface{}
+ context Key
+}
+
+// DEPRECATED!
+//
+// Use MetaData.PrimitiveDecode instead.
+func PrimitiveDecode(primValue Primitive, v interface{}) error {
+ md := MetaData{decoded: make(map[string]bool)}
+ return md.unify(primValue.undecoded, rvalue(v))
+}
+
+// PrimitiveDecode is just like the other `Decode*` functions, except it
+// decodes a TOML value that has already been parsed. Valid primitive values
+// can *only* be obtained from values filled by the decoder functions,
+// including this method. (i.e., `v` may contain more `Primitive`
+// values.)
+//
+// Meta data for primitive values is included in the meta data returned by
+// the `Decode*` functions with one exception: keys returned by the Undecoded
+// method will only reflect keys that were decoded. Namely, any keys hidden
+// behind a Primitive will be considered undecoded. Executing this method will
+// update the undecoded keys in the meta data. (See the example.)
+func (md *MetaData) PrimitiveDecode(primValue Primitive, v interface{}) error {
+ md.context = primValue.context
+ defer func() { md.context = nil }()
+ return md.unify(primValue.undecoded, rvalue(v))
+}
+
+// Decode will decode the contents of `data` in TOML format into a pointer
+// `v`.
+//
+// TOML hashes correspond to Go structs or maps. (Dealer's choice. They can be
+// used interchangeably.)
+//
+// TOML arrays of tables correspond to either a slice of structs or a slice
+// of maps.
+//
+// TOML datetimes correspond to Go `time.Time` values.
+//
+// All other TOML types (float, string, int, bool and array) correspond
+// to the obvious Go types.
+//
+// An exception to the above rules is if a type implements the
+// encoding.TextUnmarshaler interface. In this case, any primitive TOML value
+// (floats, strings, integers, booleans and datetimes) will be converted to
+// a byte string and given to the value's UnmarshalText method. See the
+// Unmarshaler example for a demonstration with time duration strings.
+//
+// Key mapping
+//
+// TOML keys can map to either keys in a Go map or field names in a Go
+// struct. The special `toml` struct tag may be used to map TOML keys to
+// struct fields that don't match the key name exactly. (See the example.)
+// A case insensitive match to struct names will be tried if an exact match
+// can't be found.
+//
+// The mapping between TOML values and Go values is loose. That is, there
+// may exist TOML values that cannot be placed into your representation, and
+// there may be parts of your representation that do not correspond to
+// TOML values. This loose mapping can be made stricter by using the IsDefined
+// and/or Undecoded methods on the MetaData returned.
+//
+// This decoder will not handle cyclic types. If a cyclic type is passed,
+// `Decode` will not terminate.
+func Decode(data string, v interface{}) (MetaData, error) {
+ rv := reflect.ValueOf(v)
+ if rv.Kind() != reflect.Ptr {
+ return MetaData{}, e("Decode of non-pointer %s", reflect.TypeOf(v))
+ }
+ if rv.IsNil() {
+ return MetaData{}, e("Decode of nil %s", reflect.TypeOf(v))
+ }
+ p, err := parse(data)
+ if err != nil {
+ return MetaData{}, err
+ }
+ md := MetaData{
+ p.mapping, p.types, p.ordered,
+ make(map[string]bool, len(p.ordered)), nil,
+ }
+ return md, md.unify(p.mapping, indirect(rv))
+}
+
+// DecodeFile is just like Decode, except it will automatically read the
+// contents of the file at `fpath` and decode it for you.
+func DecodeFile(fpath string, v interface{}) (MetaData, error) {
+ bs, err := ioutil.ReadFile(fpath)
+ if err != nil {
+ return MetaData{}, err
+ }
+ return Decode(string(bs), v)
+}
+
+// DecodeReader is just like Decode, except it will consume all bytes
+// from the reader and decode it for you.
+func DecodeReader(r io.Reader, v interface{}) (MetaData, error) {
+ bs, err := ioutil.ReadAll(r)
+ if err != nil {
+ return MetaData{}, err
+ }
+ return Decode(string(bs), v)
+}
+
+// unify performs a sort of type unification based on the structure of `rv`,
+// which is the client representation.
+//
+// Any type mismatch produces an error. Finding a type that we don't know
+// how to handle produces an unsupported type error.
+func (md *MetaData) unify(data interface{}, rv reflect.Value) error {
+
+ // Special case. Look for a `Primitive` value.
+ if rv.Type() == reflect.TypeOf((*Primitive)(nil)).Elem() {
+ // Save the undecoded data and the key context into the primitive
+ // value.
+ context := make(Key, len(md.context))
+ copy(context, md.context)
+ rv.Set(reflect.ValueOf(Primitive{
+ undecoded: data,
+ context: context,
+ }))
+ return nil
+ }
+
+ // Special case. Unmarshaler Interface support.
+ if rv.CanAddr() {
+ if v, ok := rv.Addr().Interface().(Unmarshaler); ok {
+ return v.UnmarshalTOML(data)
+ }
+ }
+
+ // Special case. Handle time.Time values specifically.
+ // TODO: Remove this code when we decide to drop support for Go 1.1.
+ // This isn't necessary in Go 1.2 because time.Time satisfies the encoding
+ // interfaces.
+ if rv.Type().AssignableTo(rvalue(time.Time{}).Type()) {
+ return md.unifyDatetime(data, rv)
+ }
+
+ // Special case. Look for a value satisfying the TextUnmarshaler interface.
+ if v, ok := rv.Interface().(TextUnmarshaler); ok {
+ return md.unifyText(data, v)
+ }
+ // BUG(burntsushi)
+ // The behavior here is incorrect whenever a Go type satisfies the
+ // encoding.TextUnmarshaler interface but also corresponds to a TOML
+ // hash or array. In particular, the unmarshaler should only be applied
+ // to primitive TOML values. But at this point, it will be applied to
+ // all kinds of values and produce an incorrect error whenever those values
+ // are hashes or arrays (including arrays of tables).
+
+ k := rv.Kind()
+
+ // laziness
+ if k >= reflect.Int && k <= reflect.Uint64 {
+ return md.unifyInt(data, rv)
+ }
+ switch k {
+ case reflect.Ptr:
+ elem := reflect.New(rv.Type().Elem())
+ err := md.unify(data, reflect.Indirect(elem))
+ if err != nil {
+ return err
+ }
+ rv.Set(elem)
+ return nil
+ case reflect.Struct:
+ return md.unifyStruct(data, rv)
+ case reflect.Map:
+ return md.unifyMap(data, rv)
+ case reflect.Array:
+ return md.unifyArray(data, rv)
+ case reflect.Slice:
+ return md.unifySlice(data, rv)
+ case reflect.String:
+ return md.unifyString(data, rv)
+ case reflect.Bool:
+ return md.unifyBool(data, rv)
+ case reflect.Interface:
+ // we only support empty interfaces.
+ if rv.NumMethod() > 0 {
+ return e("unsupported type %s", rv.Type())
+ }
+ return md.unifyAnything(data, rv)
+ case reflect.Float32:
+ fallthrough
+ case reflect.Float64:
+ return md.unifyFloat64(data, rv)
+ }
+ return e("unsupported type %s", rv.Kind())
+}
+
+func (md *MetaData) unifyStruct(mapping interface{}, rv reflect.Value) error {
+ tmap, ok := mapping.(map[string]interface{})
+ if !ok {
+ if mapping == nil {
+ return nil
+ }
+ return e("type mismatch for %s: expected table but found %T",
+ rv.Type().String(), mapping)
+ }
+
+ for key, datum := range tmap {
+ var f *field
+ fields := cachedTypeFields(rv.Type())
+ for i := range fields {
+ ff := &fields[i]
+ if ff.name == key {
+ f = ff
+ break
+ }
+ if f == nil && strings.EqualFold(ff.name, key) {
+ f = ff
+ }
+ }
+ if f != nil {
+ subv := rv
+ for _, i := range f.index {
+ subv = indirect(subv.Field(i))
+ }
+ if isUnifiable(subv) {
+ md.decoded[md.context.add(key).String()] = true
+ md.context = append(md.context, key)
+ if err := md.unify(datum, subv); err != nil {
+ return err
+ }
+ md.context = md.context[0 : len(md.context)-1]
+ } else if f.name != "" {
+ // Bad user! No soup for you!
+ return e("cannot write unexported field %s.%s",
+ rv.Type().String(), f.name)
+ }
+ }
+ }
+ return nil
+}
+
+func (md *MetaData) unifyMap(mapping interface{}, rv reflect.Value) error {
+ tmap, ok := mapping.(map[string]interface{})
+ if !ok {
+ if tmap == nil {
+ return nil
+ }
+ return badtype("map", mapping)
+ }
+ if rv.IsNil() {
+ rv.Set(reflect.MakeMap(rv.Type()))
+ }
+ for k, v := range tmap {
+ md.decoded[md.context.add(k).String()] = true
+ md.context = append(md.context, k)
+
+ rvkey := indirect(reflect.New(rv.Type().Key()))
+ rvval := reflect.Indirect(reflect.New(rv.Type().Elem()))
+ if err := md.unify(v, rvval); err != nil {
+ return err
+ }
+ md.context = md.context[0 : len(md.context)-1]
+
+ rvkey.SetString(k)
+ rv.SetMapIndex(rvkey, rvval)
+ }
+ return nil
+}
+
+func (md *MetaData) unifyArray(data interface{}, rv reflect.Value) error {
+ datav := reflect.ValueOf(data)
+ if datav.Kind() != reflect.Slice {
+ if !datav.IsValid() {
+ return nil
+ }
+ return badtype("slice", data)
+ }
+ sliceLen := datav.Len()
+ if sliceLen != rv.Len() {
+ return e("expected array length %d; got TOML array of length %d",
+ rv.Len(), sliceLen)
+ }
+ return md.unifySliceArray(datav, rv)
+}
+
+func (md *MetaData) unifySlice(data interface{}, rv reflect.Value) error {
+ datav := reflect.ValueOf(data)
+ if datav.Kind() != reflect.Slice {
+ if !datav.IsValid() {
+ return nil
+ }
+ return badtype("slice", data)
+ }
+ n := datav.Len()
+ if rv.IsNil() || rv.Cap() < n {
+ rv.Set(reflect.MakeSlice(rv.Type(), n, n))
+ }
+ rv.SetLen(n)
+ return md.unifySliceArray(datav, rv)
+}
+
+func (md *MetaData) unifySliceArray(data, rv reflect.Value) error {
+ sliceLen := data.Len()
+ for i := 0; i < sliceLen; i++ {
+ v := data.Index(i).Interface()
+ sliceval := indirect(rv.Index(i))
+ if err := md.unify(v, sliceval); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (md *MetaData) unifyDatetime(data interface{}, rv reflect.Value) error {
+ if _, ok := data.(time.Time); ok {
+ rv.Set(reflect.ValueOf(data))
+ return nil
+ }
+ return badtype("time.Time", data)
+}
+
+func (md *MetaData) unifyString(data interface{}, rv reflect.Value) error {
+ if s, ok := data.(string); ok {
+ rv.SetString(s)
+ return nil
+ }
+ return badtype("string", data)
+}
+
+func (md *MetaData) unifyFloat64(data interface{}, rv reflect.Value) error {
+ if num, ok := data.(float64); ok {
+ switch rv.Kind() {
+ case reflect.Float32:
+ fallthrough
+ case reflect.Float64:
+ rv.SetFloat(num)
+ default:
+ panic("bug")
+ }
+ return nil
+ }
+ return badtype("float", data)
+}
+
+func (md *MetaData) unifyInt(data interface{}, rv reflect.Value) error {
+ if num, ok := data.(int64); ok {
+ if rv.Kind() >= reflect.Int && rv.Kind() <= reflect.Int64 {
+ switch rv.Kind() {
+ case reflect.Int, reflect.Int64:
+ // No bounds checking necessary.
+ case reflect.Int8:
+ if num < math.MinInt8 || num > math.MaxInt8 {
+ return e("value %d is out of range for int8", num)
+ }
+ case reflect.Int16:
+ if num < math.MinInt16 || num > math.MaxInt16 {
+ return e("value %d is out of range for int16", num)
+ }
+ case reflect.Int32:
+ if num < math.MinInt32 || num > math.MaxInt32 {
+ return e("value %d is out of range for int32", num)
+ }
+ }
+ rv.SetInt(num)
+ } else if rv.Kind() >= reflect.Uint && rv.Kind() <= reflect.Uint64 {
+ unum := uint64(num)
+ switch rv.Kind() {
+ case reflect.Uint, reflect.Uint64:
+ // No bounds checking necessary.
+ case reflect.Uint8:
+ if num < 0 || unum > math.MaxUint8 {
+ return e("value %d is out of range for uint8", num)
+ }
+ case reflect.Uint16:
+ if num < 0 || unum > math.MaxUint16 {
+ return e("value %d is out of range for uint16", num)
+ }
+ case reflect.Uint32:
+ if num < 0 || unum > math.MaxUint32 {
+ return e("value %d is out of range for uint32", num)
+ }
+ }
+ rv.SetUint(unum)
+ } else {
+ panic("unreachable")
+ }
+ return nil
+ }
+ return badtype("integer", data)
+}
+
+func (md *MetaData) unifyBool(data interface{}, rv reflect.Value) error {
+ if b, ok := data.(bool); ok {
+ rv.SetBool(b)
+ return nil
+ }
+ return badtype("boolean", data)
+}
+
+func (md *MetaData) unifyAnything(data interface{}, rv reflect.Value) error {
+ rv.Set(reflect.ValueOf(data))
+ return nil
+}
+
+func (md *MetaData) unifyText(data interface{}, v TextUnmarshaler) error {
+ var s string
+ switch sdata := data.(type) {
+ case TextMarshaler:
+ text, err := sdata.MarshalText()
+ if err != nil {
+ return err
+ }
+ s = string(text)
+ case fmt.Stringer:
+ s = sdata.String()
+ case string:
+ s = sdata
+ case bool:
+ s = fmt.Sprintf("%v", sdata)
+ case int64:
+ s = fmt.Sprintf("%d", sdata)
+ case float64:
+ s = fmt.Sprintf("%f", sdata)
+ default:
+ return badtype("primitive (string-like)", data)
+ }
+ if err := v.UnmarshalText([]byte(s)); err != nil {
+ return err
+ }
+ return nil
+}
+
+// rvalue returns a reflect.Value of `v`. All pointers are resolved.
+func rvalue(v interface{}) reflect.Value {
+ return indirect(reflect.ValueOf(v))
+}
+
+// indirect returns the value pointed to by a pointer.
+// Pointers are followed until the value is not a pointer.
+// New values are allocated for each nil pointer.
+//
+// An exception to this rule is if the value satisfies an interface of
+// interest to us (like encoding.TextUnmarshaler).
+func indirect(v reflect.Value) reflect.Value {
+ if v.Kind() != reflect.Ptr {
+ if v.CanSet() {
+ pv := v.Addr()
+ if _, ok := pv.Interface().(TextUnmarshaler); ok {
+ return pv
+ }
+ }
+ return v
+ }
+ if v.IsNil() {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ return indirect(reflect.Indirect(v))
+}
+
+func isUnifiable(rv reflect.Value) bool {
+ if rv.CanSet() {
+ return true
+ }
+ if _, ok := rv.Interface().(TextUnmarshaler); ok {
+ return true
+ }
+ return false
+}
+
+func badtype(expected string, data interface{}) error {
+ return e("cannot load TOML value of type %T into a Go %s", data, expected)
+}
diff --git a/vendor/github.com/BurntSushi/toml/decode_meta.go b/vendor/github.com/BurntSushi/toml/decode_meta.go
new file mode 100644
index 000000000..b9914a679
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/decode_meta.go
@@ -0,0 +1,121 @@
+package toml
+
+import "strings"
+
+// MetaData allows access to meta information about TOML data that may not
+// be inferrable via reflection. In particular, whether a key has been defined
+// and the TOML type of a key.
+type MetaData struct {
+ mapping map[string]interface{}
+ types map[string]tomlType
+ keys []Key
+ decoded map[string]bool
+ context Key // Used only during decoding.
+}
+
+// IsDefined returns true if the key given exists in the TOML data. The key
+// should be specified hierarchially. e.g.,
+//
+// // access the TOML key 'a.b.c'
+// IsDefined("a", "b", "c")
+//
+// IsDefined will return false if an empty key given. Keys are case sensitive.
+func (md *MetaData) IsDefined(key ...string) bool {
+ if len(key) == 0 {
+ return false
+ }
+
+ var hash map[string]interface{}
+ var ok bool
+ var hashOrVal interface{} = md.mapping
+ for _, k := range key {
+ if hash, ok = hashOrVal.(map[string]interface{}); !ok {
+ return false
+ }
+ if hashOrVal, ok = hash[k]; !ok {
+ return false
+ }
+ }
+ return true
+}
+
+// Type returns a string representation of the type of the key specified.
+//
+// Type will return the empty string if given an empty key or a key that
+// does not exist. Keys are case sensitive.
+func (md *MetaData) Type(key ...string) string {
+ fullkey := strings.Join(key, ".")
+ if typ, ok := md.types[fullkey]; ok {
+ return typ.typeString()
+ }
+ return ""
+}
+
+// Key is the type of any TOML key, including key groups. Use (MetaData).Keys
+// to get values of this type.
+type Key []string
+
+func (k Key) String() string {
+ return strings.Join(k, ".")
+}
+
+func (k Key) maybeQuotedAll() string {
+ var ss []string
+ for i := range k {
+ ss = append(ss, k.maybeQuoted(i))
+ }
+ return strings.Join(ss, ".")
+}
+
+func (k Key) maybeQuoted(i int) string {
+ quote := false
+ for _, c := range k[i] {
+ if !isBareKeyChar(c) {
+ quote = true
+ break
+ }
+ }
+ if quote {
+ return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\""
+ }
+ return k[i]
+}
+
+func (k Key) add(piece string) Key {
+ newKey := make(Key, len(k)+1)
+ copy(newKey, k)
+ newKey[len(k)] = piece
+ return newKey
+}
+
+// Keys returns a slice of every key in the TOML data, including key groups.
+// Each key is itself a slice, where the first element is the top of the
+// hierarchy and the last is the most specific.
+//
+// The list will have the same order as the keys appeared in the TOML data.
+//
+// All keys returned are non-empty.
+func (md *MetaData) Keys() []Key {
+ return md.keys
+}
+
+// Undecoded returns all keys that have not been decoded in the order in which
+// they appear in the original TOML document.
+//
+// This includes keys that haven't been decoded because of a Primitive value.
+// Once the Primitive value is decoded, the keys will be considered decoded.
+//
+// Also note that decoding into an empty interface will result in no decoding,
+// and so no keys will be considered decoded.
+//
+// In this sense, the Undecoded keys correspond to keys in the TOML document
+// that do not have a concrete type in your representation.
+func (md *MetaData) Undecoded() []Key {
+ undecoded := make([]Key, 0, len(md.keys))
+ for _, key := range md.keys {
+ if !md.decoded[key.String()] {
+ undecoded = append(undecoded, key)
+ }
+ }
+ return undecoded
+}
diff --git a/vendor/github.com/BurntSushi/toml/doc.go b/vendor/github.com/BurntSushi/toml/doc.go
new file mode 100644
index 000000000..b371f396e
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/doc.go
@@ -0,0 +1,27 @@
+/*
+Package toml provides facilities for decoding and encoding TOML configuration
+files via reflection. There is also support for delaying decoding with
+the Primitive type, and querying the set of keys in a TOML document with the
+MetaData type.
+
+The specification implemented: https://github.com/toml-lang/toml
+
+The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify
+whether a file is a valid TOML document. It can also be used to print the
+type of each key in a TOML document.
+
+Testing
+
+There are two important types of tests used for this package. The first is
+contained inside '*_test.go' files and uses the standard Go unit testing
+framework. These tests are primarily devoted to holistically testing the
+decoder and encoder.
+
+The second type of testing is used to verify the implementation's adherence
+to the TOML specification. These tests have been factored into their own
+project: https://github.com/BurntSushi/toml-test
+
+The reason the tests are in a separate project is so that they can be used by
+any implementation of TOML. Namely, it is language agnostic.
+*/
+package toml
diff --git a/vendor/github.com/BurntSushi/toml/encode.go b/vendor/github.com/BurntSushi/toml/encode.go
new file mode 100644
index 000000000..d905c21a2
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/encode.go
@@ -0,0 +1,568 @@
+package toml
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+)
+
+type tomlEncodeError struct{ error }
+
+var (
+ errArrayMixedElementTypes = errors.New(
+ "toml: cannot encode array with mixed element types")
+ errArrayNilElement = errors.New(
+ "toml: cannot encode array with nil element")
+ errNonString = errors.New(
+ "toml: cannot encode a map with non-string key type")
+ errAnonNonStruct = errors.New(
+ "toml: cannot encode an anonymous field that is not a struct")
+ errArrayNoTable = errors.New(
+ "toml: TOML array element cannot contain a table")
+ errNoKey = errors.New(
+ "toml: top-level values must be Go maps or structs")
+ errAnything = errors.New("") // used in testing
+)
+
+var quotedReplacer = strings.NewReplacer(
+ "\t", "\\t",
+ "\n", "\\n",
+ "\r", "\\r",
+ "\"", "\\\"",
+ "\\", "\\\\",
+)
+
+// Encoder controls the encoding of Go values to a TOML document to some
+// io.Writer.
+//
+// The indentation level can be controlled with the Indent field.
+type Encoder struct {
+ // A single indentation level. By default it is two spaces.
+ Indent string
+
+ // hasWritten is whether we have written any output to w yet.
+ hasWritten bool
+ w *bufio.Writer
+}
+
+// NewEncoder returns a TOML encoder that encodes Go values to the io.Writer
+// given. By default, a single indentation level is 2 spaces.
+func NewEncoder(w io.Writer) *Encoder {
+ return &Encoder{
+ w: bufio.NewWriter(w),
+ Indent: " ",
+ }
+}
+
+// Encode writes a TOML representation of the Go value to the underlying
+// io.Writer. If the value given cannot be encoded to a valid TOML document,
+// then an error is returned.
+//
+// The mapping between Go values and TOML values should be precisely the same
+// as for the Decode* functions. Similarly, the TextMarshaler interface is
+// supported by encoding the resulting bytes as strings. (If you want to write
+// arbitrary binary data then you will need to use something like base64 since
+// TOML does not have any binary types.)
+//
+// When encoding TOML hashes (i.e., Go maps or structs), keys without any
+// sub-hashes are encoded first.
+//
+// If a Go map is encoded, then its keys are sorted alphabetically for
+// deterministic output. More control over this behavior may be provided if
+// there is demand for it.
+//
+// Encoding Go values without a corresponding TOML representation---like map
+// types with non-string keys---will cause an error to be returned. Similarly
+// for mixed arrays/slices, arrays/slices with nil elements, embedded
+// non-struct types and nested slices containing maps or structs.
+// (e.g., [][]map[string]string is not allowed but []map[string]string is OK
+// and so is []map[string][]string.)
+func (enc *Encoder) Encode(v interface{}) error {
+ rv := eindirect(reflect.ValueOf(v))
+ if err := enc.safeEncode(Key([]string{}), rv); err != nil {
+ return err
+ }
+ return enc.w.Flush()
+}
+
+func (enc *Encoder) safeEncode(key Key, rv reflect.Value) (err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ if terr, ok := r.(tomlEncodeError); ok {
+ err = terr.error
+ return
+ }
+ panic(r)
+ }
+ }()
+ enc.encode(key, rv)
+ return nil
+}
+
+func (enc *Encoder) encode(key Key, rv reflect.Value) {
+ // Special case. Time needs to be in ISO8601 format.
+ // Special case. If we can marshal the type to text, then we used that.
+ // Basically, this prevents the encoder for handling these types as
+ // generic structs (or whatever the underlying type of a TextMarshaler is).
+ switch rv.Interface().(type) {
+ case time.Time, TextMarshaler:
+ enc.keyEqElement(key, rv)
+ return
+ }
+
+ k := rv.Kind()
+ switch k {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
+ reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
+ reflect.Uint64,
+ reflect.Float32, reflect.Float64, reflect.String, reflect.Bool:
+ enc.keyEqElement(key, rv)
+ case reflect.Array, reflect.Slice:
+ if typeEqual(tomlArrayHash, tomlTypeOfGo(rv)) {
+ enc.eArrayOfTables(key, rv)
+ } else {
+ enc.keyEqElement(key, rv)
+ }
+ case reflect.Interface:
+ if rv.IsNil() {
+ return
+ }
+ enc.encode(key, rv.Elem())
+ case reflect.Map:
+ if rv.IsNil() {
+ return
+ }
+ enc.eTable(key, rv)
+ case reflect.Ptr:
+ if rv.IsNil() {
+ return
+ }
+ enc.encode(key, rv.Elem())
+ case reflect.Struct:
+ enc.eTable(key, rv)
+ default:
+ panic(e("unsupported type for key '%s': %s", key, k))
+ }
+}
+
+// eElement encodes any value that can be an array element (primitives and
+// arrays).
+func (enc *Encoder) eElement(rv reflect.Value) {
+ switch v := rv.Interface().(type) {
+ case time.Time:
+ // Special case time.Time as a primitive. Has to come before
+ // TextMarshaler below because time.Time implements
+ // encoding.TextMarshaler, but we need to always use UTC.
+ enc.wf(v.UTC().Format("2006-01-02T15:04:05Z"))
+ return
+ case TextMarshaler:
+ // Special case. Use text marshaler if it's available for this value.
+ if s, err := v.MarshalText(); err != nil {
+ encPanic(err)
+ } else {
+ enc.writeQuoted(string(s))
+ }
+ return
+ }
+ switch rv.Kind() {
+ case reflect.Bool:
+ enc.wf(strconv.FormatBool(rv.Bool()))
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
+ reflect.Int64:
+ enc.wf(strconv.FormatInt(rv.Int(), 10))
+ case reflect.Uint, reflect.Uint8, reflect.Uint16,
+ reflect.Uint32, reflect.Uint64:
+ enc.wf(strconv.FormatUint(rv.Uint(), 10))
+ case reflect.Float32:
+ enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 32)))
+ case reflect.Float64:
+ enc.wf(floatAddDecimal(strconv.FormatFloat(rv.Float(), 'f', -1, 64)))
+ case reflect.Array, reflect.Slice:
+ enc.eArrayOrSliceElement(rv)
+ case reflect.Interface:
+ enc.eElement(rv.Elem())
+ case reflect.String:
+ enc.writeQuoted(rv.String())
+ default:
+ panic(e("unexpected primitive type: %s", rv.Kind()))
+ }
+}
+
+// By the TOML spec, all floats must have a decimal with at least one
+// number on either side.
+func floatAddDecimal(fstr string) string {
+ if !strings.Contains(fstr, ".") {
+ return fstr + ".0"
+ }
+ return fstr
+}
+
+func (enc *Encoder) writeQuoted(s string) {
+ enc.wf("\"%s\"", quotedReplacer.Replace(s))
+}
+
+func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
+ length := rv.Len()
+ enc.wf("[")
+ for i := 0; i < length; i++ {
+ elem := rv.Index(i)
+ enc.eElement(elem)
+ if i != length-1 {
+ enc.wf(", ")
+ }
+ }
+ enc.wf("]")
+}
+
+func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
+ if len(key) == 0 {
+ encPanic(errNoKey)
+ }
+ for i := 0; i < rv.Len(); i++ {
+ trv := rv.Index(i)
+ if isNil(trv) {
+ continue
+ }
+ panicIfInvalidKey(key)
+ enc.newline()
+ enc.wf("%s[[%s]]", enc.indentStr(key), key.maybeQuotedAll())
+ enc.newline()
+ enc.eMapOrStruct(key, trv)
+ }
+}
+
+func (enc *Encoder) eTable(key Key, rv reflect.Value) {
+ panicIfInvalidKey(key)
+ if len(key) == 1 {
+ // Output an extra newline between top-level tables.
+ // (The newline isn't written if nothing else has been written though.)
+ enc.newline()
+ }
+ if len(key) > 0 {
+ enc.wf("%s[%s]", enc.indentStr(key), key.maybeQuotedAll())
+ enc.newline()
+ }
+ enc.eMapOrStruct(key, rv)
+}
+
+func (enc *Encoder) eMapOrStruct(key Key, rv reflect.Value) {
+ switch rv := eindirect(rv); rv.Kind() {
+ case reflect.Map:
+ enc.eMap(key, rv)
+ case reflect.Struct:
+ enc.eStruct(key, rv)
+ default:
+ panic("eTable: unhandled reflect.Value Kind: " + rv.Kind().String())
+ }
+}
+
+func (enc *Encoder) eMap(key Key, rv reflect.Value) {
+ rt := rv.Type()
+ if rt.Key().Kind() != reflect.String {
+ encPanic(errNonString)
+ }
+
+ // Sort keys so that we have deterministic output. And write keys directly
+ // underneath this key first, before writing sub-structs or sub-maps.
+ var mapKeysDirect, mapKeysSub []string
+ for _, mapKey := range rv.MapKeys() {
+ k := mapKey.String()
+ if typeIsHash(tomlTypeOfGo(rv.MapIndex(mapKey))) {
+ mapKeysSub = append(mapKeysSub, k)
+ } else {
+ mapKeysDirect = append(mapKeysDirect, k)
+ }
+ }
+
+ var writeMapKeys = func(mapKeys []string) {
+ sort.Strings(mapKeys)
+ for _, mapKey := range mapKeys {
+ mrv := rv.MapIndex(reflect.ValueOf(mapKey))
+ if isNil(mrv) {
+ // Don't write anything for nil fields.
+ continue
+ }
+ enc.encode(key.add(mapKey), mrv)
+ }
+ }
+ writeMapKeys(mapKeysDirect)
+ writeMapKeys(mapKeysSub)
+}
+
+func (enc *Encoder) eStruct(key Key, rv reflect.Value) {
+ // Write keys for fields directly under this key first, because if we write
+ // a field that creates a new table, then all keys under it will be in that
+ // table (not the one we're writing here).
+ rt := rv.Type()
+ var fieldsDirect, fieldsSub [][]int
+ var addFields func(rt reflect.Type, rv reflect.Value, start []int)
+ addFields = func(rt reflect.Type, rv reflect.Value, start []int) {
+ for i := 0; i < rt.NumField(); i++ {
+ f := rt.Field(i)
+ // skip unexported fields
+ if f.PkgPath != "" && !f.Anonymous {
+ continue
+ }
+ frv := rv.Field(i)
+ if f.Anonymous {
+ t := f.Type
+ switch t.Kind() {
+ case reflect.Struct:
+ // Treat anonymous struct fields with
+ // tag names as though they are not
+ // anonymous, like encoding/json does.
+ if getOptions(f.Tag).name == "" {
+ addFields(t, frv, f.Index)
+ continue
+ }
+ case reflect.Ptr:
+ if t.Elem().Kind() == reflect.Struct &&
+ getOptions(f.Tag).name == "" {
+ if !frv.IsNil() {
+ addFields(t.Elem(), frv.Elem(), f.Index)
+ }
+ continue
+ }
+ // Fall through to the normal field encoding logic below
+ // for non-struct anonymous fields.
+ }
+ }
+
+ if typeIsHash(tomlTypeOfGo(frv)) {
+ fieldsSub = append(fieldsSub, append(start, f.Index...))
+ } else {
+ fieldsDirect = append(fieldsDirect, append(start, f.Index...))
+ }
+ }
+ }
+ addFields(rt, rv, nil)
+
+ var writeFields = func(fields [][]int) {
+ for _, fieldIndex := range fields {
+ sft := rt.FieldByIndex(fieldIndex)
+ sf := rv.FieldByIndex(fieldIndex)
+ if isNil(sf) {
+ // Don't write anything for nil fields.
+ continue
+ }
+
+ opts := getOptions(sft.Tag)
+ if opts.skip {
+ continue
+ }
+ keyName := sft.Name
+ if opts.name != "" {
+ keyName = opts.name
+ }
+ if opts.omitempty && isEmpty(sf) {
+ continue
+ }
+ if opts.omitzero && isZero(sf) {
+ continue
+ }
+
+ enc.encode(key.add(keyName), sf)
+ }
+ }
+ writeFields(fieldsDirect)
+ writeFields(fieldsSub)
+}
+
+// tomlTypeName returns the TOML type name of the Go value's type. It is
+// used to determine whether the types of array elements are mixed (which is
+// forbidden). If the Go value is nil, then it is illegal for it to be an array
+// element, and valueIsNil is returned as true.
+
+// Returns the TOML type of a Go value. The type may be `nil`, which means
+// no concrete TOML type could be found.
+func tomlTypeOfGo(rv reflect.Value) tomlType {
+ if isNil(rv) || !rv.IsValid() {
+ return nil
+ }
+ switch rv.Kind() {
+ case reflect.Bool:
+ return tomlBool
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
+ reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
+ reflect.Uint64:
+ return tomlInteger
+ case reflect.Float32, reflect.Float64:
+ return tomlFloat
+ case reflect.Array, reflect.Slice:
+ if typeEqual(tomlHash, tomlArrayType(rv)) {
+ return tomlArrayHash
+ }
+ return tomlArray
+ case reflect.Ptr, reflect.Interface:
+ return tomlTypeOfGo(rv.Elem())
+ case reflect.String:
+ return tomlString
+ case reflect.Map:
+ return tomlHash
+ case reflect.Struct:
+ switch rv.Interface().(type) {
+ case time.Time:
+ return tomlDatetime
+ case TextMarshaler:
+ return tomlString
+ default:
+ return tomlHash
+ }
+ default:
+ panic("unexpected reflect.Kind: " + rv.Kind().String())
+ }
+}
+
+// tomlArrayType returns the element type of a TOML array. The type returned
+// may be nil if it cannot be determined (e.g., a nil slice or a zero length
+// slize). This function may also panic if it finds a type that cannot be
+// expressed in TOML (such as nil elements, heterogeneous arrays or directly
+// nested arrays of tables).
+func tomlArrayType(rv reflect.Value) tomlType {
+ if isNil(rv) || !rv.IsValid() || rv.Len() == 0 {
+ return nil
+ }
+ firstType := tomlTypeOfGo(rv.Index(0))
+ if firstType == nil {
+ encPanic(errArrayNilElement)
+ }
+
+ rvlen := rv.Len()
+ for i := 1; i < rvlen; i++ {
+ elem := rv.Index(i)
+ switch elemType := tomlTypeOfGo(elem); {
+ case elemType == nil:
+ encPanic(errArrayNilElement)
+ case !typeEqual(firstType, elemType):
+ encPanic(errArrayMixedElementTypes)
+ }
+ }
+ // If we have a nested array, then we must make sure that the nested
+ // array contains ONLY primitives.
+ // This checks arbitrarily nested arrays.
+ if typeEqual(firstType, tomlArray) || typeEqual(firstType, tomlArrayHash) {
+ nest := tomlArrayType(eindirect(rv.Index(0)))
+ if typeEqual(nest, tomlHash) || typeEqual(nest, tomlArrayHash) {
+ encPanic(errArrayNoTable)
+ }
+ }
+ return firstType
+}
+
+type tagOptions struct {
+ skip bool // "-"
+ name string
+ omitempty bool
+ omitzero bool
+}
+
+func getOptions(tag reflect.StructTag) tagOptions {
+ t := tag.Get("toml")
+ if t == "-" {
+ return tagOptions{skip: true}
+ }
+ var opts tagOptions
+ parts := strings.Split(t, ",")
+ opts.name = parts[0]
+ for _, s := range parts[1:] {
+ switch s {
+ case "omitempty":
+ opts.omitempty = true
+ case "omitzero":
+ opts.omitzero = true
+ }
+ }
+ return opts
+}
+
+func isZero(rv reflect.Value) bool {
+ switch rv.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return rv.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ return rv.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return rv.Float() == 0.0
+ }
+ return false
+}
+
+func isEmpty(rv reflect.Value) bool {
+ switch rv.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
+ return rv.Len() == 0
+ case reflect.Bool:
+ return !rv.Bool()
+ }
+ return false
+}
+
+func (enc *Encoder) newline() {
+ if enc.hasWritten {
+ enc.wf("\n")
+ }
+}
+
+func (enc *Encoder) keyEqElement(key Key, val reflect.Value) {
+ if len(key) == 0 {
+ encPanic(errNoKey)
+ }
+ panicIfInvalidKey(key)
+ enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
+ enc.eElement(val)
+ enc.newline()
+}
+
+func (enc *Encoder) wf(format string, v ...interface{}) {
+ if _, err := fmt.Fprintf(enc.w, format, v...); err != nil {
+ encPanic(err)
+ }
+ enc.hasWritten = true
+}
+
+func (enc *Encoder) indentStr(key Key) string {
+ return strings.Repeat(enc.Indent, len(key)-1)
+}
+
+func encPanic(err error) {
+ panic(tomlEncodeError{err})
+}
+
+func eindirect(v reflect.Value) reflect.Value {
+ switch v.Kind() {
+ case reflect.Ptr, reflect.Interface:
+ return eindirect(v.Elem())
+ default:
+ return v
+ }
+}
+
+func isNil(rv reflect.Value) bool {
+ switch rv.Kind() {
+ case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+ return rv.IsNil()
+ default:
+ return false
+ }
+}
+
+func panicIfInvalidKey(key Key) {
+ for _, k := range key {
+ if len(k) == 0 {
+ encPanic(e("Key '%s' is not a valid table name. Key names "+
+ "cannot be empty.", key.maybeQuotedAll()))
+ }
+ }
+}
+
+func isValidKeyName(s string) bool {
+ return len(s) != 0
+}
diff --git a/vendor/github.com/BurntSushi/toml/encoding_types.go b/vendor/github.com/BurntSushi/toml/encoding_types.go
new file mode 100644
index 000000000..d36e1dd60
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/encoding_types.go
@@ -0,0 +1,19 @@
+// +build go1.2
+
+package toml
+
+// In order to support Go 1.1, we define our own TextMarshaler and
+// TextUnmarshaler types. For Go 1.2+, we just alias them with the
+// standard library interfaces.
+
+import (
+ "encoding"
+)
+
+// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
+// so that Go 1.1 can be supported.
+type TextMarshaler encoding.TextMarshaler
+
+// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
+// here so that Go 1.1 can be supported.
+type TextUnmarshaler encoding.TextUnmarshaler
diff --git a/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go b/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go
new file mode 100644
index 000000000..e8d503d04
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/encoding_types_1.1.go
@@ -0,0 +1,18 @@
+// +build !go1.2
+
+package toml
+
+// These interfaces were introduced in Go 1.2, so we add them manually when
+// compiling for Go 1.1.
+
+// TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here
+// so that Go 1.1 can be supported.
+type TextMarshaler interface {
+ MarshalText() (text []byte, err error)
+}
+
+// TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined
+// here so that Go 1.1 can be supported.
+type TextUnmarshaler interface {
+ UnmarshalText(text []byte) error
+}
diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go
new file mode 100644
index 000000000..e0a742a88
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/lex.go
@@ -0,0 +1,953 @@
+package toml
+
+import (
+ "fmt"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+type itemType int
+
+const (
+ itemError itemType = iota
+ itemNIL // used in the parser to indicate no type
+ itemEOF
+ itemText
+ itemString
+ itemRawString
+ itemMultilineString
+ itemRawMultilineString
+ itemBool
+ itemInteger
+ itemFloat
+ itemDatetime
+ itemArray // the start of an array
+ itemArrayEnd
+ itemTableStart
+ itemTableEnd
+ itemArrayTableStart
+ itemArrayTableEnd
+ itemKeyStart
+ itemCommentStart
+ itemInlineTableStart
+ itemInlineTableEnd
+)
+
+const (
+ eof = 0
+ comma = ','
+ tableStart = '['
+ tableEnd = ']'
+ arrayTableStart = '['
+ arrayTableEnd = ']'
+ tableSep = '.'
+ keySep = '='
+ arrayStart = '['
+ arrayEnd = ']'
+ commentStart = '#'
+ stringStart = '"'
+ stringEnd = '"'
+ rawStringStart = '\''
+ rawStringEnd = '\''
+ inlineTableStart = '{'
+ inlineTableEnd = '}'
+)
+
+type stateFn func(lx *lexer) stateFn
+
+type lexer struct {
+ input string
+ start int
+ pos int
+ line int
+ state stateFn
+ items chan item
+
+ // Allow for backing up up to three runes.
+ // This is necessary because TOML contains 3-rune tokens (""" and ''').
+ prevWidths [3]int
+ nprev int // how many of prevWidths are in use
+ // If we emit an eof, we can still back up, but it is not OK to call
+ // next again.
+ atEOF bool
+
+ // A stack of state functions used to maintain context.
+ // The idea is to reuse parts of the state machine in various places.
+ // For example, values can appear at the top level or within arbitrarily
+ // nested arrays. The last state on the stack is used after a value has
+ // been lexed. Similarly for comments.
+ stack []stateFn
+}
+
+type item struct {
+ typ itemType
+ val string
+ line int
+}
+
+func (lx *lexer) nextItem() item {
+ for {
+ select {
+ case item := <-lx.items:
+ return item
+ default:
+ lx.state = lx.state(lx)
+ }
+ }
+}
+
+func lex(input string) *lexer {
+ lx := &lexer{
+ input: input,
+ state: lexTop,
+ line: 1,
+ items: make(chan item, 10),
+ stack: make([]stateFn, 0, 10),
+ }
+ return lx
+}
+
+func (lx *lexer) push(state stateFn) {
+ lx.stack = append(lx.stack, state)
+}
+
+func (lx *lexer) pop() stateFn {
+ if len(lx.stack) == 0 {
+ return lx.errorf("BUG in lexer: no states to pop")
+ }
+ last := lx.stack[len(lx.stack)-1]
+ lx.stack = lx.stack[0 : len(lx.stack)-1]
+ return last
+}
+
+func (lx *lexer) current() string {
+ return lx.input[lx.start:lx.pos]
+}
+
+func (lx *lexer) emit(typ itemType) {
+ lx.items <- item{typ, lx.current(), lx.line}
+ lx.start = lx.pos
+}
+
+func (lx *lexer) emitTrim(typ itemType) {
+ lx.items <- item{typ, strings.TrimSpace(lx.current()), lx.line}
+ lx.start = lx.pos
+}
+
+func (lx *lexer) next() (r rune) {
+ if lx.atEOF {
+ panic("next called after EOF")
+ }
+ if lx.pos >= len(lx.input) {
+ lx.atEOF = true
+ return eof
+ }
+
+ if lx.input[lx.pos] == '\n' {
+ lx.line++
+ }
+ lx.prevWidths[2] = lx.prevWidths[1]
+ lx.prevWidths[1] = lx.prevWidths[0]
+ if lx.nprev < 3 {
+ lx.nprev++
+ }
+ r, w := utf8.DecodeRuneInString(lx.input[lx.pos:])
+ lx.prevWidths[0] = w
+ lx.pos += w
+ return r
+}
+
+// ignore skips over the pending input before this point.
+func (lx *lexer) ignore() {
+ lx.start = lx.pos
+}
+
+// backup steps back one rune. Can be called only twice between calls to next.
+func (lx *lexer) backup() {
+ if lx.atEOF {
+ lx.atEOF = false
+ return
+ }
+ if lx.nprev < 1 {
+ panic("backed up too far")
+ }
+ w := lx.prevWidths[0]
+ lx.prevWidths[0] = lx.prevWidths[1]
+ lx.prevWidths[1] = lx.prevWidths[2]
+ lx.nprev--
+ lx.pos -= w
+ if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' {
+ lx.line--
+ }
+}
+
+// accept consumes the next rune if it's equal to `valid`.
+func (lx *lexer) accept(valid rune) bool {
+ if lx.next() == valid {
+ return true
+ }
+ lx.backup()
+ return false
+}
+
+// peek returns but does not consume the next rune in the input.
+func (lx *lexer) peek() rune {
+ r := lx.next()
+ lx.backup()
+ return r
+}
+
+// skip ignores all input that matches the given predicate.
+func (lx *lexer) skip(pred func(rune) bool) {
+ for {
+ r := lx.next()
+ if pred(r) {
+ continue
+ }
+ lx.backup()
+ lx.ignore()
+ return
+ }
+}
+
+// errorf stops all lexing by emitting an error and returning `nil`.
+// Note that any value that is a character is escaped if it's a special
+// character (newlines, tabs, etc.).
+func (lx *lexer) errorf(format string, values ...interface{}) stateFn {
+ lx.items <- item{
+ itemError,
+ fmt.Sprintf(format, values...),
+ lx.line,
+ }
+ return nil
+}
+
+// lexTop consumes elements at the top level of TOML data.
+func lexTop(lx *lexer) stateFn {
+ r := lx.next()
+ if isWhitespace(r) || isNL(r) {
+ return lexSkip(lx, lexTop)
+ }
+ switch r {
+ case commentStart:
+ lx.push(lexTop)
+ return lexCommentStart
+ case tableStart:
+ return lexTableStart
+ case eof:
+ if lx.pos > lx.start {
+ return lx.errorf("unexpected EOF")
+ }
+ lx.emit(itemEOF)
+ return nil
+ }
+
+ // At this point, the only valid item can be a key, so we back up
+ // and let the key lexer do the rest.
+ lx.backup()
+ lx.push(lexTopEnd)
+ return lexKeyStart
+}
+
+// lexTopEnd is entered whenever a top-level item has been consumed. (A value
+// or a table.) It must see only whitespace, and will turn back to lexTop
+// upon a newline. If it sees EOF, it will quit the lexer successfully.
+func lexTopEnd(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case r == commentStart:
+ // a comment will read to a newline for us.
+ lx.push(lexTop)
+ return lexCommentStart
+ case isWhitespace(r):
+ return lexTopEnd
+ case isNL(r):
+ lx.ignore()
+ return lexTop
+ case r == eof:
+ lx.emit(itemEOF)
+ return nil
+ }
+ return lx.errorf("expected a top-level item to end with a newline, "+
+ "comment, or EOF, but got %q instead", r)
+}
+
+// lexTable lexes the beginning of a table. Namely, it makes sure that
+// it starts with a character other than '.' and ']'.
+// It assumes that '[' has already been consumed.
+// It also handles the case that this is an item in an array of tables.
+// e.g., '[[name]]'.
+func lexTableStart(lx *lexer) stateFn {
+ if lx.peek() == arrayTableStart {
+ lx.next()
+ lx.emit(itemArrayTableStart)
+ lx.push(lexArrayTableEnd)
+ } else {
+ lx.emit(itemTableStart)
+ lx.push(lexTableEnd)
+ }
+ return lexTableNameStart
+}
+
+func lexTableEnd(lx *lexer) stateFn {
+ lx.emit(itemTableEnd)
+ return lexTopEnd
+}
+
+func lexArrayTableEnd(lx *lexer) stateFn {
+ if r := lx.next(); r != arrayTableEnd {
+ return lx.errorf("expected end of table array name delimiter %q, "+
+ "but got %q instead", arrayTableEnd, r)
+ }
+ lx.emit(itemArrayTableEnd)
+ return lexTopEnd
+}
+
+func lexTableNameStart(lx *lexer) stateFn {
+ lx.skip(isWhitespace)
+ switch r := lx.peek(); {
+ case r == tableEnd || r == eof:
+ return lx.errorf("unexpected end of table name " +
+ "(table names cannot be empty)")
+ case r == tableSep:
+ return lx.errorf("unexpected table separator " +
+ "(table names cannot be empty)")
+ case r == stringStart || r == rawStringStart:
+ lx.ignore()
+ lx.push(lexTableNameEnd)
+ return lexValue // reuse string lexing
+ default:
+ return lexBareTableName
+ }
+}
+
+// lexBareTableName lexes the name of a table. It assumes that at least one
+// valid character for the table has already been read.
+func lexBareTableName(lx *lexer) stateFn {
+ r := lx.next()
+ if isBareKeyChar(r) {
+ return lexBareTableName
+ }
+ lx.backup()
+ lx.emit(itemText)
+ return lexTableNameEnd
+}
+
+// lexTableNameEnd reads the end of a piece of a table name, optionally
+// consuming whitespace.
+func lexTableNameEnd(lx *lexer) stateFn {
+ lx.skip(isWhitespace)
+ switch r := lx.next(); {
+ case isWhitespace(r):
+ return lexTableNameEnd
+ case r == tableSep:
+ lx.ignore()
+ return lexTableNameStart
+ case r == tableEnd:
+ return lx.pop()
+ default:
+ return lx.errorf("expected '.' or ']' to end table name, "+
+ "but got %q instead", r)
+ }
+}
+
+// lexKeyStart consumes a key name up until the first non-whitespace character.
+// lexKeyStart will ignore whitespace.
+func lexKeyStart(lx *lexer) stateFn {
+ r := lx.peek()
+ switch {
+ case r == keySep:
+ return lx.errorf("unexpected key separator %q", keySep)
+ case isWhitespace(r) || isNL(r):
+ lx.next()
+ return lexSkip(lx, lexKeyStart)
+ case r == stringStart || r == rawStringStart:
+ lx.ignore()
+ lx.emit(itemKeyStart)
+ lx.push(lexKeyEnd)
+ return lexValue // reuse string lexing
+ default:
+ lx.ignore()
+ lx.emit(itemKeyStart)
+ return lexBareKey
+ }
+}
+
+// lexBareKey consumes the text of a bare key. Assumes that the first character
+// (which is not whitespace) has not yet been consumed.
+func lexBareKey(lx *lexer) stateFn {
+ switch r := lx.next(); {
+ case isBareKeyChar(r):
+ return lexBareKey
+ case isWhitespace(r):
+ lx.backup()
+ lx.emit(itemText)
+ return lexKeyEnd
+ case r == keySep:
+ lx.backup()
+ lx.emit(itemText)
+ return lexKeyEnd
+ default:
+ return lx.errorf("bare keys cannot contain %q", r)
+ }
+}
+
+// lexKeyEnd consumes the end of a key and trims whitespace (up to the key
+// separator).
+func lexKeyEnd(lx *lexer) stateFn {
+ switch r := lx.next(); {
+ case r == keySep:
+ return lexSkip(lx, lexValue)
+ case isWhitespace(r):
+ return lexSkip(lx, lexKeyEnd)
+ default:
+ return lx.errorf("expected key separator %q, but got %q instead",
+ keySep, r)
+ }
+}
+
+// lexValue starts the consumption of a value anywhere a value is expected.
+// lexValue will ignore whitespace.
+// After a value is lexed, the last state on the next is popped and returned.
+func lexValue(lx *lexer) stateFn {
+ // We allow whitespace to precede a value, but NOT newlines.
+ // In array syntax, the array states are responsible for ignoring newlines.
+ r := lx.next()
+ switch {
+ case isWhitespace(r):
+ return lexSkip(lx, lexValue)
+ case isDigit(r):
+ lx.backup() // avoid an extra state and use the same as above
+ return lexNumberOrDateStart
+ }
+ switch r {
+ case arrayStart:
+ lx.ignore()
+ lx.emit(itemArray)
+ return lexArrayValue
+ case inlineTableStart:
+ lx.ignore()
+ lx.emit(itemInlineTableStart)
+ return lexInlineTableValue
+ case stringStart:
+ if lx.accept(stringStart) {
+ if lx.accept(stringStart) {
+ lx.ignore() // Ignore """
+ return lexMultilineString
+ }
+ lx.backup()
+ }
+ lx.ignore() // ignore the '"'
+ return lexString
+ case rawStringStart:
+ if lx.accept(rawStringStart) {
+ if lx.accept(rawStringStart) {
+ lx.ignore() // Ignore """
+ return lexMultilineRawString
+ }
+ lx.backup()
+ }
+ lx.ignore() // ignore the "'"
+ return lexRawString
+ case '+', '-':
+ return lexNumberStart
+ case '.': // special error case, be kind to users
+ return lx.errorf("floats must start with a digit, not '.'")
+ }
+ if unicode.IsLetter(r) {
+ // Be permissive here; lexBool will give a nice error if the
+ // user wrote something like
+ // x = foo
+ // (i.e. not 'true' or 'false' but is something else word-like.)
+ lx.backup()
+ return lexBool
+ }
+ return lx.errorf("expected value but found %q instead", r)
+}
+
+// lexArrayValue consumes one value in an array. It assumes that '[' or ','
+// have already been consumed. All whitespace and newlines are ignored.
+func lexArrayValue(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case isWhitespace(r) || isNL(r):
+ return lexSkip(lx, lexArrayValue)
+ case r == commentStart:
+ lx.push(lexArrayValue)
+ return lexCommentStart
+ case r == comma:
+ return lx.errorf("unexpected comma")
+ case r == arrayEnd:
+ // NOTE(caleb): The spec isn't clear about whether you can have
+ // a trailing comma or not, so we'll allow it.
+ return lexArrayEnd
+ }
+
+ lx.backup()
+ lx.push(lexArrayValueEnd)
+ return lexValue
+}
+
+// lexArrayValueEnd consumes everything between the end of an array value and
+// the next value (or the end of the array): it ignores whitespace and newlines
+// and expects either a ',' or a ']'.
+func lexArrayValueEnd(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case isWhitespace(r) || isNL(r):
+ return lexSkip(lx, lexArrayValueEnd)
+ case r == commentStart:
+ lx.push(lexArrayValueEnd)
+ return lexCommentStart
+ case r == comma:
+ lx.ignore()
+ return lexArrayValue // move on to the next value
+ case r == arrayEnd:
+ return lexArrayEnd
+ }
+ return lx.errorf(
+ "expected a comma or array terminator %q, but got %q instead",
+ arrayEnd, r,
+ )
+}
+
+// lexArrayEnd finishes the lexing of an array.
+// It assumes that a ']' has just been consumed.
+func lexArrayEnd(lx *lexer) stateFn {
+ lx.ignore()
+ lx.emit(itemArrayEnd)
+ return lx.pop()
+}
+
+// lexInlineTableValue consumes one key/value pair in an inline table.
+// It assumes that '{' or ',' have already been consumed. Whitespace is ignored.
+func lexInlineTableValue(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case isWhitespace(r):
+ return lexSkip(lx, lexInlineTableValue)
+ case isNL(r):
+ return lx.errorf("newlines not allowed within inline tables")
+ case r == commentStart:
+ lx.push(lexInlineTableValue)
+ return lexCommentStart
+ case r == comma:
+ return lx.errorf("unexpected comma")
+ case r == inlineTableEnd:
+ return lexInlineTableEnd
+ }
+ lx.backup()
+ lx.push(lexInlineTableValueEnd)
+ return lexKeyStart
+}
+
+// lexInlineTableValueEnd consumes everything between the end of an inline table
+// key/value pair and the next pair (or the end of the table):
+// it ignores whitespace and expects either a ',' or a '}'.
+func lexInlineTableValueEnd(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case isWhitespace(r):
+ return lexSkip(lx, lexInlineTableValueEnd)
+ case isNL(r):
+ return lx.errorf("newlines not allowed within inline tables")
+ case r == commentStart:
+ lx.push(lexInlineTableValueEnd)
+ return lexCommentStart
+ case r == comma:
+ lx.ignore()
+ return lexInlineTableValue
+ case r == inlineTableEnd:
+ return lexInlineTableEnd
+ }
+ return lx.errorf("expected a comma or an inline table terminator %q, "+
+ "but got %q instead", inlineTableEnd, r)
+}
+
+// lexInlineTableEnd finishes the lexing of an inline table.
+// It assumes that a '}' has just been consumed.
+func lexInlineTableEnd(lx *lexer) stateFn {
+ lx.ignore()
+ lx.emit(itemInlineTableEnd)
+ return lx.pop()
+}
+
+// lexString consumes the inner contents of a string. It assumes that the
+// beginning '"' has already been consumed and ignored.
+func lexString(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case r == eof:
+ return lx.errorf("unexpected EOF")
+ case isNL(r):
+ return lx.errorf("strings cannot contain newlines")
+ case r == '\\':
+ lx.push(lexString)
+ return lexStringEscape
+ case r == stringEnd:
+ lx.backup()
+ lx.emit(itemString)
+ lx.next()
+ lx.ignore()
+ return lx.pop()
+ }
+ return lexString
+}
+
+// lexMultilineString consumes the inner contents of a string. It assumes that
+// the beginning '"""' has already been consumed and ignored.
+func lexMultilineString(lx *lexer) stateFn {
+ switch lx.next() {
+ case eof:
+ return lx.errorf("unexpected EOF")
+ case '\\':
+ return lexMultilineStringEscape
+ case stringEnd:
+ if lx.accept(stringEnd) {
+ if lx.accept(stringEnd) {
+ lx.backup()
+ lx.backup()
+ lx.backup()
+ lx.emit(itemMultilineString)
+ lx.next()
+ lx.next()
+ lx.next()
+ lx.ignore()
+ return lx.pop()
+ }
+ lx.backup()
+ }
+ }
+ return lexMultilineString
+}
+
+// lexRawString consumes a raw string. Nothing can be escaped in such a string.
+// It assumes that the beginning "'" has already been consumed and ignored.
+func lexRawString(lx *lexer) stateFn {
+ r := lx.next()
+ switch {
+ case r == eof:
+ return lx.errorf("unexpected EOF")
+ case isNL(r):
+ return lx.errorf("strings cannot contain newlines")
+ case r == rawStringEnd:
+ lx.backup()
+ lx.emit(itemRawString)
+ lx.next()
+ lx.ignore()
+ return lx.pop()
+ }
+ return lexRawString
+}
+
+// lexMultilineRawString consumes a raw string. Nothing can be escaped in such
+// a string. It assumes that the beginning "'''" has already been consumed and
+// ignored.
+func lexMultilineRawString(lx *lexer) stateFn {
+ switch lx.next() {
+ case eof:
+ return lx.errorf("unexpected EOF")
+ case rawStringEnd:
+ if lx.accept(rawStringEnd) {
+ if lx.accept(rawStringEnd) {
+ lx.backup()
+ lx.backup()
+ lx.backup()
+ lx.emit(itemRawMultilineString)
+ lx.next()
+ lx.next()
+ lx.next()
+ lx.ignore()
+ return lx.pop()
+ }
+ lx.backup()
+ }
+ }
+ return lexMultilineRawString
+}
+
+// lexMultilineStringEscape consumes an escaped character. It assumes that the
+// preceding '\\' has already been consumed.
+func lexMultilineStringEscape(lx *lexer) stateFn {
+ // Handle the special case first:
+ if isNL(lx.next()) {
+ return lexMultilineString
+ }
+ lx.backup()
+ lx.push(lexMultilineString)
+ return lexStringEscape(lx)
+}
+
+func lexStringEscape(lx *lexer) stateFn {
+ r := lx.next()
+ switch r {
+ case 'b':
+ fallthrough
+ case 't':
+ fallthrough
+ case 'n':
+ fallthrough
+ case 'f':
+ fallthrough
+ case 'r':
+ fallthrough
+ case '"':
+ fallthrough
+ case '\\':
+ return lx.pop()
+ case 'u':
+ return lexShortUnicodeEscape
+ case 'U':
+ return lexLongUnicodeEscape
+ }
+ return lx.errorf("invalid escape character %q; only the following "+
+ "escape characters are allowed: "+
+ `\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r)
+}
+
+func lexShortUnicodeEscape(lx *lexer) stateFn {
+ var r rune
+ for i := 0; i < 4; i++ {
+ r = lx.next()
+ if !isHexadecimal(r) {
+ return lx.errorf(`expected four hexadecimal digits after '\u', `+
+ "but got %q instead", lx.current())
+ }
+ }
+ return lx.pop()
+}
+
+func lexLongUnicodeEscape(lx *lexer) stateFn {
+ var r rune
+ for i := 0; i < 8; i++ {
+ r = lx.next()
+ if !isHexadecimal(r) {
+ return lx.errorf(`expected eight hexadecimal digits after '\U', `+
+ "but got %q instead", lx.current())
+ }
+ }
+ return lx.pop()
+}
+
+// lexNumberOrDateStart consumes either an integer, a float, or datetime.
+func lexNumberOrDateStart(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexNumberOrDate
+ }
+ switch r {
+ case '_':
+ return lexNumber
+ case 'e', 'E':
+ return lexFloat
+ case '.':
+ return lx.errorf("floats must start with a digit, not '.'")
+ }
+ return lx.errorf("expected a digit but got %q", r)
+}
+
+// lexNumberOrDate consumes either an integer, float or datetime.
+func lexNumberOrDate(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexNumberOrDate
+ }
+ switch r {
+ case '-':
+ return lexDatetime
+ case '_':
+ return lexNumber
+ case '.', 'e', 'E':
+ return lexFloat
+ }
+
+ lx.backup()
+ lx.emit(itemInteger)
+ return lx.pop()
+}
+
+// lexDatetime consumes a Datetime, to a first approximation.
+// The parser validates that it matches one of the accepted formats.
+func lexDatetime(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexDatetime
+ }
+ switch r {
+ case '-', 'T', ':', '.', 'Z', '+':
+ return lexDatetime
+ }
+
+ lx.backup()
+ lx.emit(itemDatetime)
+ return lx.pop()
+}
+
+// lexNumberStart consumes either an integer or a float. It assumes that a sign
+// has already been read, but that *no* digits have been consumed.
+// lexNumberStart will move to the appropriate integer or float states.
+func lexNumberStart(lx *lexer) stateFn {
+ // We MUST see a digit. Even floats have to start with a digit.
+ r := lx.next()
+ if !isDigit(r) {
+ if r == '.' {
+ return lx.errorf("floats must start with a digit, not '.'")
+ }
+ return lx.errorf("expected a digit but got %q", r)
+ }
+ return lexNumber
+}
+
+// lexNumber consumes an integer or a float after seeing the first digit.
+func lexNumber(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexNumber
+ }
+ switch r {
+ case '_':
+ return lexNumber
+ case '.', 'e', 'E':
+ return lexFloat
+ }
+
+ lx.backup()
+ lx.emit(itemInteger)
+ return lx.pop()
+}
+
+// lexFloat consumes the elements of a float. It allows any sequence of
+// float-like characters, so floats emitted by the lexer are only a first
+// approximation and must be validated by the parser.
+func lexFloat(lx *lexer) stateFn {
+ r := lx.next()
+ if isDigit(r) {
+ return lexFloat
+ }
+ switch r {
+ case '_', '.', '-', '+', 'e', 'E':
+ return lexFloat
+ }
+
+ lx.backup()
+ lx.emit(itemFloat)
+ return lx.pop()
+}
+
+// lexBool consumes a bool string: 'true' or 'false.
+func lexBool(lx *lexer) stateFn {
+ var rs []rune
+ for {
+ r := lx.next()
+ if !unicode.IsLetter(r) {
+ lx.backup()
+ break
+ }
+ rs = append(rs, r)
+ }
+ s := string(rs)
+ switch s {
+ case "true", "false":
+ lx.emit(itemBool)
+ return lx.pop()
+ }
+ return lx.errorf("expected value but found %q instead", s)
+}
+
+// lexCommentStart begins the lexing of a comment. It will emit
+// itemCommentStart and consume no characters, passing control to lexComment.
+func lexCommentStart(lx *lexer) stateFn {
+ lx.ignore()
+ lx.emit(itemCommentStart)
+ return lexComment
+}
+
+// lexComment lexes an entire comment. It assumes that '#' has been consumed.
+// It will consume *up to* the first newline character, and pass control
+// back to the last state on the stack.
+func lexComment(lx *lexer) stateFn {
+ r := lx.peek()
+ if isNL(r) || r == eof {
+ lx.emit(itemText)
+ return lx.pop()
+ }
+ lx.next()
+ return lexComment
+}
+
+// lexSkip ignores all slurped input and moves on to the next state.
+func lexSkip(lx *lexer, nextState stateFn) stateFn {
+ return func(lx *lexer) stateFn {
+ lx.ignore()
+ return nextState
+ }
+}
+
+// isWhitespace returns true if `r` is a whitespace character according
+// to the spec.
+func isWhitespace(r rune) bool {
+ return r == '\t' || r == ' '
+}
+
+func isNL(r rune) bool {
+ return r == '\n' || r == '\r'
+}
+
+func isDigit(r rune) bool {
+ return r >= '0' && r <= '9'
+}
+
+func isHexadecimal(r rune) bool {
+ return (r >= '0' && r <= '9') ||
+ (r >= 'a' && r <= 'f') ||
+ (r >= 'A' && r <= 'F')
+}
+
+func isBareKeyChar(r rune) bool {
+ return (r >= 'A' && r <= 'Z') ||
+ (r >= 'a' && r <= 'z') ||
+ (r >= '0' && r <= '9') ||
+ r == '_' ||
+ r == '-'
+}
+
+func (itype itemType) String() string {
+ switch itype {
+ case itemError:
+ return "Error"
+ case itemNIL:
+ return "NIL"
+ case itemEOF:
+ return "EOF"
+ case itemText:
+ return "Text"
+ case itemString, itemRawString, itemMultilineString, itemRawMultilineString:
+ return "String"
+ case itemBool:
+ return "Bool"
+ case itemInteger:
+ return "Integer"
+ case itemFloat:
+ return "Float"
+ case itemDatetime:
+ return "DateTime"
+ case itemTableStart:
+ return "TableStart"
+ case itemTableEnd:
+ return "TableEnd"
+ case itemKeyStart:
+ return "KeyStart"
+ case itemArray:
+ return "Array"
+ case itemArrayEnd:
+ return "ArrayEnd"
+ case itemCommentStart:
+ return "CommentStart"
+ }
+ panic(fmt.Sprintf("BUG: Unknown type '%d'.", int(itype)))
+}
+
+func (item item) String() string {
+ return fmt.Sprintf("(%s, %s)", item.typ.String(), item.val)
+}
diff --git a/vendor/github.com/BurntSushi/toml/parse.go b/vendor/github.com/BurntSushi/toml/parse.go
new file mode 100644
index 000000000..50869ef92
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/parse.go
@@ -0,0 +1,592 @@
+package toml
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+ "unicode"
+ "unicode/utf8"
+)
+
+type parser struct {
+ mapping map[string]interface{}
+ types map[string]tomlType
+ lx *lexer
+
+ // A list of keys in the order that they appear in the TOML data.
+ ordered []Key
+
+ // the full key for the current hash in scope
+ context Key
+
+ // the base key name for everything except hashes
+ currentKey string
+
+ // rough approximation of line number
+ approxLine int
+
+ // A map of 'key.group.names' to whether they were created implicitly.
+ implicits map[string]bool
+}
+
+type parseError string
+
+func (pe parseError) Error() string {
+ return string(pe)
+}
+
+func parse(data string) (p *parser, err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ var ok bool
+ if err, ok = r.(parseError); ok {
+ return
+ }
+ panic(r)
+ }
+ }()
+
+ p = &parser{
+ mapping: make(map[string]interface{}),
+ types: make(map[string]tomlType),
+ lx: lex(data),
+ ordered: make([]Key, 0),
+ implicits: make(map[string]bool),
+ }
+ for {
+ item := p.next()
+ if item.typ == itemEOF {
+ break
+ }
+ p.topLevel(item)
+ }
+
+ return p, nil
+}
+
+func (p *parser) panicf(format string, v ...interface{}) {
+ msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s",
+ p.approxLine, p.current(), fmt.Sprintf(format, v...))
+ panic(parseError(msg))
+}
+
+func (p *parser) next() item {
+ it := p.lx.nextItem()
+ if it.typ == itemError {
+ p.panicf("%s", it.val)
+ }
+ return it
+}
+
+func (p *parser) bug(format string, v ...interface{}) {
+ panic(fmt.Sprintf("BUG: "+format+"\n\n", v...))
+}
+
+func (p *parser) expect(typ itemType) item {
+ it := p.next()
+ p.assertEqual(typ, it.typ)
+ return it
+}
+
+func (p *parser) assertEqual(expected, got itemType) {
+ if expected != got {
+ p.bug("Expected '%s' but got '%s'.", expected, got)
+ }
+}
+
+func (p *parser) topLevel(item item) {
+ switch item.typ {
+ case itemCommentStart:
+ p.approxLine = item.line
+ p.expect(itemText)
+ case itemTableStart:
+ kg := p.next()
+ p.approxLine = kg.line
+
+ var key Key
+ for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() {
+ key = append(key, p.keyString(kg))
+ }
+ p.assertEqual(itemTableEnd, kg.typ)
+
+ p.establishContext(key, false)
+ p.setType("", tomlHash)
+ p.ordered = append(p.ordered, key)
+ case itemArrayTableStart:
+ kg := p.next()
+ p.approxLine = kg.line
+
+ var key Key
+ for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() {
+ key = append(key, p.keyString(kg))
+ }
+ p.assertEqual(itemArrayTableEnd, kg.typ)
+
+ p.establishContext(key, true)
+ p.setType("", tomlArrayHash)
+ p.ordered = append(p.ordered, key)
+ case itemKeyStart:
+ kname := p.next()
+ p.approxLine = kname.line
+ p.currentKey = p.keyString(kname)
+
+ val, typ := p.value(p.next())
+ p.setValue(p.currentKey, val)
+ p.setType(p.currentKey, typ)
+ p.ordered = append(p.ordered, p.context.add(p.currentKey))
+ p.currentKey = ""
+ default:
+ p.bug("Unexpected type at top level: %s", item.typ)
+ }
+}
+
+// Gets a string for a key (or part of a key in a table name).
+func (p *parser) keyString(it item) string {
+ switch it.typ {
+ case itemText:
+ return it.val
+ case itemString, itemMultilineString,
+ itemRawString, itemRawMultilineString:
+ s, _ := p.value(it)
+ return s.(string)
+ default:
+ p.bug("Unexpected key type: %s", it.typ)
+ panic("unreachable")
+ }
+}
+
+// value translates an expected value from the lexer into a Go value wrapped
+// as an empty interface.
+func (p *parser) value(it item) (interface{}, tomlType) {
+ switch it.typ {
+ case itemString:
+ return p.replaceEscapes(it.val), p.typeOfPrimitive(it)
+ case itemMultilineString:
+ trimmed := stripFirstNewline(stripEscapedWhitespace(it.val))
+ return p.replaceEscapes(trimmed), p.typeOfPrimitive(it)
+ case itemRawString:
+ return it.val, p.typeOfPrimitive(it)
+ case itemRawMultilineString:
+ return stripFirstNewline(it.val), p.typeOfPrimitive(it)
+ case itemBool:
+ switch it.val {
+ case "true":
+ return true, p.typeOfPrimitive(it)
+ case "false":
+ return false, p.typeOfPrimitive(it)
+ }
+ p.bug("Expected boolean value, but got '%s'.", it.val)
+ case itemInteger:
+ if !numUnderscoresOK(it.val) {
+ p.panicf("Invalid integer %q: underscores must be surrounded by digits",
+ it.val)
+ }
+ val := strings.Replace(it.val, "_", "", -1)
+ num, err := strconv.ParseInt(val, 10, 64)
+ if err != nil {
+ // Distinguish integer values. Normally, it'd be a bug if the lexer
+ // provides an invalid integer, but it's possible that the number is
+ // out of range of valid values (which the lexer cannot determine).
+ // So mark the former as a bug but the latter as a legitimate user
+ // error.
+ if e, ok := err.(*strconv.NumError); ok &&
+ e.Err == strconv.ErrRange {
+
+ p.panicf("Integer '%s' is out of the range of 64-bit "+
+ "signed integers.", it.val)
+ } else {
+ p.bug("Expected integer value, but got '%s'.", it.val)
+ }
+ }
+ return num, p.typeOfPrimitive(it)
+ case itemFloat:
+ parts := strings.FieldsFunc(it.val, func(r rune) bool {
+ switch r {
+ case '.', 'e', 'E':
+ return true
+ }
+ return false
+ })
+ for _, part := range parts {
+ if !numUnderscoresOK(part) {
+ p.panicf("Invalid float %q: underscores must be "+
+ "surrounded by digits", it.val)
+ }
+ }
+ if !numPeriodsOK(it.val) {
+ // As a special case, numbers like '123.' or '1.e2',
+ // which are valid as far as Go/strconv are concerned,
+ // must be rejected because TOML says that a fractional
+ // part consists of '.' followed by 1+ digits.
+ p.panicf("Invalid float %q: '.' must be followed "+
+ "by one or more digits", it.val)
+ }
+ val := strings.Replace(it.val, "_", "", -1)
+ num, err := strconv.ParseFloat(val, 64)
+ if err != nil {
+ if e, ok := err.(*strconv.NumError); ok &&
+ e.Err == strconv.ErrRange {
+
+ p.panicf("Float '%s' is out of the range of 64-bit "+
+ "IEEE-754 floating-point numbers.", it.val)
+ } else {
+ p.panicf("Invalid float value: %q", it.val)
+ }
+ }
+ return num, p.typeOfPrimitive(it)
+ case itemDatetime:
+ var t time.Time
+ var ok bool
+ var err error
+ for _, format := range []string{
+ "2006-01-02T15:04:05Z07:00",
+ "2006-01-02T15:04:05",
+ "2006-01-02",
+ } {
+ t, err = time.ParseInLocation(format, it.val, time.Local)
+ if err == nil {
+ ok = true
+ break
+ }
+ }
+ if !ok {
+ p.panicf("Invalid TOML Datetime: %q.", it.val)
+ }
+ return t, p.typeOfPrimitive(it)
+ case itemArray:
+ array := make([]interface{}, 0)
+ types := make([]tomlType, 0)
+
+ for it = p.next(); it.typ != itemArrayEnd; it = p.next() {
+ if it.typ == itemCommentStart {
+ p.expect(itemText)
+ continue
+ }
+
+ val, typ := p.value(it)
+ array = append(array, val)
+ types = append(types, typ)
+ }
+ return array, p.typeOfArray(types)
+ case itemInlineTableStart:
+ var (
+ hash = make(map[string]interface{})
+ outerContext = p.context
+ outerKey = p.currentKey
+ )
+
+ p.context = append(p.context, p.currentKey)
+ p.currentKey = ""
+ for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
+ if it.typ != itemKeyStart {
+ p.bug("Expected key start but instead found %q, around line %d",
+ it.val, p.approxLine)
+ }
+ if it.typ == itemCommentStart {
+ p.expect(itemText)
+ continue
+ }
+
+ // retrieve key
+ k := p.next()
+ p.approxLine = k.line
+ kname := p.keyString(k)
+
+ // retrieve value
+ p.currentKey = kname
+ val, typ := p.value(p.next())
+ // make sure we keep metadata up to date
+ p.setType(kname, typ)
+ p.ordered = append(p.ordered, p.context.add(p.currentKey))
+ hash[kname] = val
+ }
+ p.context = outerContext
+ p.currentKey = outerKey
+ return hash, tomlHash
+ }
+ p.bug("Unexpected value type: %s", it.typ)
+ panic("unreachable")
+}
+
+// numUnderscoresOK checks whether each underscore in s is surrounded by
+// characters that are not underscores.
+func numUnderscoresOK(s string) bool {
+ accept := false
+ for _, r := range s {
+ if r == '_' {
+ if !accept {
+ return false
+ }
+ accept = false
+ continue
+ }
+ accept = true
+ }
+ return accept
+}
+
+// numPeriodsOK checks whether every period in s is followed by a digit.
+func numPeriodsOK(s string) bool {
+ period := false
+ for _, r := range s {
+ if period && !isDigit(r) {
+ return false
+ }
+ period = r == '.'
+ }
+ return !period
+}
+
+// establishContext sets the current context of the parser,
+// where the context is either a hash or an array of hashes. Which one is
+// set depends on the value of the `array` parameter.
+//
+// Establishing the context also makes sure that the key isn't a duplicate, and
+// will create implicit hashes automatically.
+func (p *parser) establishContext(key Key, array bool) {
+ var ok bool
+
+ // Always start at the top level and drill down for our context.
+ hashContext := p.mapping
+ keyContext := make(Key, 0)
+
+ // We only need implicit hashes for key[0:-1]
+ for _, k := range key[0 : len(key)-1] {
+ _, ok = hashContext[k]
+ keyContext = append(keyContext, k)
+
+ // No key? Make an implicit hash and move on.
+ if !ok {
+ p.addImplicit(keyContext)
+ hashContext[k] = make(map[string]interface{})
+ }
+
+ // If the hash context is actually an array of tables, then set
+ // the hash context to the last element in that array.
+ //
+ // Otherwise, it better be a table, since this MUST be a key group (by
+ // virtue of it not being the last element in a key).
+ switch t := hashContext[k].(type) {
+ case []map[string]interface{}:
+ hashContext = t[len(t)-1]
+ case map[string]interface{}:
+ hashContext = t
+ default:
+ p.panicf("Key '%s' was already created as a hash.", keyContext)
+ }
+ }
+
+ p.context = keyContext
+ if array {
+ // If this is the first element for this array, then allocate a new
+ // list of tables for it.
+ k := key[len(key)-1]
+ if _, ok := hashContext[k]; !ok {
+ hashContext[k] = make([]map[string]interface{}, 0, 5)
+ }
+
+ // Add a new table. But make sure the key hasn't already been used
+ // for something else.
+ if hash, ok := hashContext[k].([]map[string]interface{}); ok {
+ hashContext[k] = append(hash, make(map[string]interface{}))
+ } else {
+ p.panicf("Key '%s' was already created and cannot be used as "+
+ "an array.", keyContext)
+ }
+ } else {
+ p.setValue(key[len(key)-1], make(map[string]interface{}))
+ }
+ p.context = append(p.context, key[len(key)-1])
+}
+
+// setValue sets the given key to the given value in the current context.
+// It will make sure that the key hasn't already been defined, account for
+// implicit key groups.
+func (p *parser) setValue(key string, value interface{}) {
+ var tmpHash interface{}
+ var ok bool
+
+ hash := p.mapping
+ keyContext := make(Key, 0)
+ for _, k := range p.context {
+ keyContext = append(keyContext, k)
+ if tmpHash, ok = hash[k]; !ok {
+ p.bug("Context for key '%s' has not been established.", keyContext)
+ }
+ switch t := tmpHash.(type) {
+ case []map[string]interface{}:
+ // The context is a table of hashes. Pick the most recent table
+ // defined as the current hash.
+ hash = t[len(t)-1]
+ case map[string]interface{}:
+ hash = t
+ default:
+ p.bug("Expected hash to have type 'map[string]interface{}', but "+
+ "it has '%T' instead.", tmpHash)
+ }
+ }
+ keyContext = append(keyContext, key)
+
+ if _, ok := hash[key]; ok {
+ // Typically, if the given key has already been set, then we have
+ // to raise an error since duplicate keys are disallowed. However,
+ // it's possible that a key was previously defined implicitly. In this
+ // case, it is allowed to be redefined concretely. (See the
+ // `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.)
+ //
+ // But we have to make sure to stop marking it as an implicit. (So that
+ // another redefinition provokes an error.)
+ //
+ // Note that since it has already been defined (as a hash), we don't
+ // want to overwrite it. So our business is done.
+ if p.isImplicit(keyContext) {
+ p.removeImplicit(keyContext)
+ return
+ }
+
+ // Otherwise, we have a concrete key trying to override a previous
+ // key, which is *always* wrong.
+ p.panicf("Key '%s' has already been defined.", keyContext)
+ }
+ hash[key] = value
+}
+
+// setType sets the type of a particular value at a given key.
+// It should be called immediately AFTER setValue.
+//
+// Note that if `key` is empty, then the type given will be applied to the
+// current context (which is either a table or an array of tables).
+func (p *parser) setType(key string, typ tomlType) {
+ keyContext := make(Key, 0, len(p.context)+1)
+ for _, k := range p.context {
+ keyContext = append(keyContext, k)
+ }
+ if len(key) > 0 { // allow type setting for hashes
+ keyContext = append(keyContext, key)
+ }
+ p.types[keyContext.String()] = typ
+}
+
+// addImplicit sets the given Key as having been created implicitly.
+func (p *parser) addImplicit(key Key) {
+ p.implicits[key.String()] = true
+}
+
+// removeImplicit stops tagging the given key as having been implicitly
+// created.
+func (p *parser) removeImplicit(key Key) {
+ p.implicits[key.String()] = false
+}
+
+// isImplicit returns true if the key group pointed to by the key was created
+// implicitly.
+func (p *parser) isImplicit(key Key) bool {
+ return p.implicits[key.String()]
+}
+
+// current returns the full key name of the current context.
+func (p *parser) current() string {
+ if len(p.currentKey) == 0 {
+ return p.context.String()
+ }
+ if len(p.context) == 0 {
+ return p.currentKey
+ }
+ return fmt.Sprintf("%s.%s", p.context, p.currentKey)
+}
+
+func stripFirstNewline(s string) string {
+ if len(s) == 0 || s[0] != '\n' {
+ return s
+ }
+ return s[1:]
+}
+
+func stripEscapedWhitespace(s string) string {
+ esc := strings.Split(s, "\\\n")
+ if len(esc) > 1 {
+ for i := 1; i < len(esc); i++ {
+ esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace)
+ }
+ }
+ return strings.Join(esc, "")
+}
+
+func (p *parser) replaceEscapes(str string) string {
+ var replaced []rune
+ s := []byte(str)
+ r := 0
+ for r < len(s) {
+ if s[r] != '\\' {
+ c, size := utf8.DecodeRune(s[r:])
+ r += size
+ replaced = append(replaced, c)
+ continue
+ }
+ r += 1
+ if r >= len(s) {
+ p.bug("Escape sequence at end of string.")
+ return ""
+ }
+ switch s[r] {
+ default:
+ p.bug("Expected valid escape code after \\, but got %q.", s[r])
+ return ""
+ case 'b':
+ replaced = append(replaced, rune(0x0008))
+ r += 1
+ case 't':
+ replaced = append(replaced, rune(0x0009))
+ r += 1
+ case 'n':
+ replaced = append(replaced, rune(0x000A))
+ r += 1
+ case 'f':
+ replaced = append(replaced, rune(0x000C))
+ r += 1
+ case 'r':
+ replaced = append(replaced, rune(0x000D))
+ r += 1
+ case '"':
+ replaced = append(replaced, rune(0x0022))
+ r += 1
+ case '\\':
+ replaced = append(replaced, rune(0x005C))
+ r += 1
+ case 'u':
+ // At this point, we know we have a Unicode escape of the form
+ // `uXXXX` at [r, r+5). (Because the lexer guarantees this
+ // for us.)
+ escaped := p.asciiEscapeToUnicode(s[r+1 : r+5])
+ replaced = append(replaced, escaped)
+ r += 5
+ case 'U':
+ // At this point, we know we have a Unicode escape of the form
+ // `uXXXX` at [r, r+9). (Because the lexer guarantees this
+ // for us.)
+ escaped := p.asciiEscapeToUnicode(s[r+1 : r+9])
+ replaced = append(replaced, escaped)
+ r += 9
+ }
+ }
+ return string(replaced)
+}
+
+func (p *parser) asciiEscapeToUnicode(bs []byte) rune {
+ s := string(bs)
+ hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32)
+ if err != nil {
+ p.bug("Could not parse '%s' as a hexadecimal number, but the "+
+ "lexer claims it's OK: %s", s, err)
+ }
+ if !utf8.ValidRune(rune(hex)) {
+ p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s)
+ }
+ return rune(hex)
+}
+
+func isStringType(ty itemType) bool {
+ return ty == itemString || ty == itemMultilineString ||
+ ty == itemRawString || ty == itemRawMultilineString
+}
diff --git a/vendor/github.com/BurntSushi/toml/session.vim b/vendor/github.com/BurntSushi/toml/session.vim
new file mode 100644
index 000000000..562164be0
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/session.vim
@@ -0,0 +1 @@
+au BufWritePost *.go silent!make tags > /dev/null 2>&1
diff --git a/vendor/github.com/BurntSushi/toml/type_check.go b/vendor/github.com/BurntSushi/toml/type_check.go
new file mode 100644
index 000000000..c73f8afc1
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/type_check.go
@@ -0,0 +1,91 @@
+package toml
+
+// tomlType represents any Go type that corresponds to a TOML type.
+// While the first draft of the TOML spec has a simplistic type system that
+// probably doesn't need this level of sophistication, we seem to be militating
+// toward adding real composite types.
+type tomlType interface {
+ typeString() string
+}
+
+// typeEqual accepts any two types and returns true if they are equal.
+func typeEqual(t1, t2 tomlType) bool {
+ if t1 == nil || t2 == nil {
+ return false
+ }
+ return t1.typeString() == t2.typeString()
+}
+
+func typeIsHash(t tomlType) bool {
+ return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash)
+}
+
+type tomlBaseType string
+
+func (btype tomlBaseType) typeString() string {
+ return string(btype)
+}
+
+func (btype tomlBaseType) String() string {
+ return btype.typeString()
+}
+
+var (
+ tomlInteger tomlBaseType = "Integer"
+ tomlFloat tomlBaseType = "Float"
+ tomlDatetime tomlBaseType = "Datetime"
+ tomlString tomlBaseType = "String"
+ tomlBool tomlBaseType = "Bool"
+ tomlArray tomlBaseType = "Array"
+ tomlHash tomlBaseType = "Hash"
+ tomlArrayHash tomlBaseType = "ArrayHash"
+)
+
+// typeOfPrimitive returns a tomlType of any primitive value in TOML.
+// Primitive values are: Integer, Float, Datetime, String and Bool.
+//
+// Passing a lexer item other than the following will cause a BUG message
+// to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime.
+func (p *parser) typeOfPrimitive(lexItem item) tomlType {
+ switch lexItem.typ {
+ case itemInteger:
+ return tomlInteger
+ case itemFloat:
+ return tomlFloat
+ case itemDatetime:
+ return tomlDatetime
+ case itemString:
+ return tomlString
+ case itemMultilineString:
+ return tomlString
+ case itemRawString:
+ return tomlString
+ case itemRawMultilineString:
+ return tomlString
+ case itemBool:
+ return tomlBool
+ }
+ p.bug("Cannot infer primitive type of lex item '%s'.", lexItem)
+ panic("unreachable")
+}
+
+// typeOfArray returns a tomlType for an array given a list of types of its
+// values.
+//
+// In the current spec, if an array is homogeneous, then its type is always
+// "Array". If the array is not homogeneous, an error is generated.
+func (p *parser) typeOfArray(types []tomlType) tomlType {
+ // Empty arrays are cool.
+ if len(types) == 0 {
+ return tomlArray
+ }
+
+ theType := types[0]
+ for _, t := range types[1:] {
+ if !typeEqual(theType, t) {
+ p.panicf("Array contains values of type '%s' and '%s', but "+
+ "arrays must be homogeneous.", theType, t)
+ }
+ }
+ return tomlArray
+}
diff --git a/vendor/github.com/BurntSushi/toml/type_fields.go b/vendor/github.com/BurntSushi/toml/type_fields.go
new file mode 100644
index 000000000..608997c22
--- /dev/null
+++ b/vendor/github.com/BurntSushi/toml/type_fields.go
@@ -0,0 +1,242 @@
+package toml
+
+// Struct field handling is adapted from code in encoding/json:
+//
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the Go distribution.
+
+import (
+ "reflect"
+ "sort"
+ "sync"
+)
+
+// A field represents a single field found in a struct.
+type field struct {
+ name string // the name of the field (`toml` tag included)
+ tag bool // whether field has a `toml` tag
+ index []int // represents the depth of an anonymous field
+ typ reflect.Type // the type of the field
+}
+
+// byName sorts field by name, breaking ties with depth,
+// then breaking ties with "name came from toml tag", then
+// breaking ties with index sequence.
+type byName []field
+
+func (x byName) Len() int { return len(x) }
+
+func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+func (x byName) Less(i, j int) bool {
+ if x[i].name != x[j].name {
+ return x[i].name < x[j].name
+ }
+ if len(x[i].index) != len(x[j].index) {
+ return len(x[i].index) < len(x[j].index)
+ }
+ if x[i].tag != x[j].tag {
+ return x[i].tag
+ }
+ return byIndex(x).Less(i, j)
+}
+
+// byIndex sorts field by index sequence.
+type byIndex []field
+
+func (x byIndex) Len() int { return len(x) }
+
+func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+func (x byIndex) Less(i, j int) bool {
+ for k, xik := range x[i].index {
+ if k >= len(x[j].index) {
+ return false
+ }
+ if xik != x[j].index[k] {
+ return xik < x[j].index[k]
+ }
+ }
+ return len(x[i].index) < len(x[j].index)
+}
+
+// typeFields returns a list of fields that TOML should recognize for the given
+// type. The algorithm is breadth-first search over the set of structs to
+// include - the top struct and then any reachable anonymous structs.
+func typeFields(t reflect.Type) []field {
+ // Anonymous fields to explore at the current level and the next.
+ current := []field{}
+ next := []field{{typ: t}}
+
+ // Count of queued names for current level and the next.
+ count := map[reflect.Type]int{}
+ nextCount := map[reflect.Type]int{}
+
+ // Types already visited at an earlier level.
+ visited := map[reflect.Type]bool{}
+
+ // Fields found.
+ var fields []field
+
+ for len(next) > 0 {
+ current, next = next, current[:0]
+ count, nextCount = nextCount, map[reflect.Type]int{}
+
+ for _, f := range current {
+ if visited[f.typ] {
+ continue
+ }
+ visited[f.typ] = true
+
+ // Scan f.typ for fields to include.
+ for i := 0; i < f.typ.NumField(); i++ {
+ sf := f.typ.Field(i)
+ if sf.PkgPath != "" && !sf.Anonymous { // unexported
+ continue
+ }
+ opts := getOptions(sf.Tag)
+ if opts.skip {
+ continue
+ }
+ index := make([]int, len(f.index)+1)
+ copy(index, f.index)
+ index[len(f.index)] = i
+
+ ft := sf.Type
+ if ft.Name() == "" && ft.Kind() == reflect.Ptr {
+ // Follow pointer.
+ ft = ft.Elem()
+ }
+
+ // Record found field and index sequence.
+ if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
+ tagged := opts.name != ""
+ name := opts.name
+ if name == "" {
+ name = sf.Name
+ }
+ fields = append(fields, field{name, tagged, index, ft})
+ if count[f.typ] > 1 {
+ // If there were multiple instances, add a second,
+ // so that the annihilation code will see a duplicate.
+ // It only cares about the distinction between 1 or 2,
+ // so don't bother generating any more copies.
+ fields = append(fields, fields[len(fields)-1])
+ }
+ continue
+ }
+
+ // Record new anonymous struct to explore in next round.
+ nextCount[ft]++
+ if nextCount[ft] == 1 {
+ f := field{name: ft.Name(), index: index, typ: ft}
+ next = append(next, f)
+ }
+ }
+ }
+ }
+
+ sort.Sort(byName(fields))
+
+ // Delete all fields that are hidden by the Go rules for embedded fields,
+ // except that fields with TOML tags are promoted.
+
+ // The fields are sorted in primary order of name, secondary order
+ // of field index length. Loop over names; for each name, delete
+ // hidden fields by choosing the one dominant field that survives.
+ out := fields[:0]
+ for advance, i := 0, 0; i < len(fields); i += advance {
+ // One iteration per name.
+ // Find the sequence of fields with the name of this first field.
+ fi := fields[i]
+ name := fi.name
+ for advance = 1; i+advance < len(fields); advance++ {
+ fj := fields[i+advance]
+ if fj.name != name {
+ break
+ }
+ }
+ if advance == 1 { // Only one field with this name
+ out = append(out, fi)
+ continue
+ }
+ dominant, ok := dominantField(fields[i : i+advance])
+ if ok {
+ out = append(out, dominant)
+ }
+ }
+
+ fields = out
+ sort.Sort(byIndex(fields))
+
+ return fields
+}
+
+// dominantField looks through the fields, all of which are known to
+// have the same name, to find the single field that dominates the
+// others using Go's embedding rules, modified by the presence of
+// TOML tags. If there are multiple top-level fields, the boolean
+// will be false: This condition is an error in Go and we skip all
+// the fields.
+func dominantField(fields []field) (field, bool) {
+ // The fields are sorted in increasing index-length order. The winner
+ // must therefore be one with the shortest index length. Drop all
+ // longer entries, which is easy: just truncate the slice.
+ length := len(fields[0].index)
+ tagged := -1 // Index of first tagged field.
+ for i, f := range fields {
+ if len(f.index) > length {
+ fields = fields[:i]
+ break
+ }
+ if f.tag {
+ if tagged >= 0 {
+ // Multiple tagged fields at the same level: conflict.
+ // Return no field.
+ return field{}, false
+ }
+ tagged = i
+ }
+ }
+ if tagged >= 0 {
+ return fields[tagged], true
+ }
+ // All remaining fields have the same length. If there's more than one,
+ // we have a conflict (two fields named "X" at the same level) and we
+ // return no field.
+ if len(fields) > 1 {
+ return field{}, false
+ }
+ return fields[0], true
+}
+
+var fieldCache struct {
+ sync.RWMutex
+ m map[reflect.Type][]field
+}
+
+// cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
+func cachedTypeFields(t reflect.Type) []field {
+ fieldCache.RLock()
+ f := fieldCache.m[t]
+ fieldCache.RUnlock()
+ if f != nil {
+ return f
+ }
+
+ // Compute fields without lock.
+ // Might duplicate effort but won't hold other computations back.
+ f = typeFields(t)
+ if f == nil {
+ f = []field{}
+ }
+
+ fieldCache.Lock()
+ if fieldCache.m == nil {
+ fieldCache.m = map[reflect.Type][]field{}
+ }
+ fieldCache.m[t] = f
+ fieldCache.Unlock()
+ return f
+}
diff --git a/vendor/github.com/go-openapi/swag/.travis.yml b/vendor/github.com/go-openapi/swag/.travis.yml
index aa26d8763..f1a3f80b3 100644
--- a/vendor/github.com/go-openapi/swag/.travis.yml
+++ b/vendor/github.com/go-openapi/swag/.travis.yml
@@ -1,12 +1,10 @@
after_success:
- bash <(curl -s https://codecov.io/bash)
go:
-- 1.11.x
-- 1.12.x
+- 1.13.x
+- 1.14.x
install:
- GO111MODULE=off go get -u gotest.tools/gotestsum
-env:
-- GO111MODULE=on
language: go
notifications:
slack:
diff --git a/vendor/github.com/go-openapi/swag/convert.go b/vendor/github.com/go-openapi/swag/convert.go
index 7da35c316..fc085aeb8 100644
--- a/vendor/github.com/go-openapi/swag/convert.go
+++ b/vendor/github.com/go-openapi/swag/convert.go
@@ -88,7 +88,7 @@ func ConvertFloat64(str string) (float64, error) {
return strconv.ParseFloat(str, 64)
}
-// ConvertInt8 turn a string into int8 boolean
+// ConvertInt8 turn a string into an int8
func ConvertInt8(str string) (int8, error) {
i, err := strconv.ParseInt(str, 10, 8)
if err != nil {
@@ -97,7 +97,7 @@ func ConvertInt8(str string) (int8, error) {
return int8(i), nil
}
-// ConvertInt16 turn a string into a int16
+// ConvertInt16 turn a string into an int16
func ConvertInt16(str string) (int16, error) {
i, err := strconv.ParseInt(str, 10, 16)
if err != nil {
@@ -106,7 +106,7 @@ func ConvertInt16(str string) (int16, error) {
return int16(i), nil
}
-// ConvertInt32 turn a string into a int32
+// ConvertInt32 turn a string into an int32
func ConvertInt32(str string) (int32, error) {
i, err := strconv.ParseInt(str, 10, 32)
if err != nil {
@@ -115,12 +115,12 @@ func ConvertInt32(str string) (int32, error) {
return int32(i), nil
}
-// ConvertInt64 turn a string into a int64
+// ConvertInt64 turn a string into an int64
func ConvertInt64(str string) (int64, error) {
return strconv.ParseInt(str, 10, 64)
}
-// ConvertUint8 turn a string into a uint8
+// ConvertUint8 turn a string into an uint8
func ConvertUint8(str string) (uint8, error) {
i, err := strconv.ParseUint(str, 10, 8)
if err != nil {
@@ -129,7 +129,7 @@ func ConvertUint8(str string) (uint8, error) {
return uint8(i), nil
}
-// ConvertUint16 turn a string into a uint16
+// ConvertUint16 turn a string into an uint16
func ConvertUint16(str string) (uint16, error) {
i, err := strconv.ParseUint(str, 10, 16)
if err != nil {
@@ -138,7 +138,7 @@ func ConvertUint16(str string) (uint16, error) {
return uint16(i), nil
}
-// ConvertUint32 turn a string into a uint32
+// ConvertUint32 turn a string into an uint32
func ConvertUint32(str string) (uint32, error) {
i, err := strconv.ParseUint(str, 10, 32)
if err != nil {
@@ -147,7 +147,7 @@ func ConvertUint32(str string) (uint32, error) {
return uint32(i), nil
}
-// ConvertUint64 turn a string into a uint64
+// ConvertUint64 turn a string into an uint64
func ConvertUint64(str string) (uint64, error) {
return strconv.ParseUint(str, 10, 64)
}
diff --git a/vendor/github.com/go-openapi/swag/convert_types.go b/vendor/github.com/go-openapi/swag/convert_types.go
index c95e4e78b..c49cc473a 100644
--- a/vendor/github.com/go-openapi/swag/convert_types.go
+++ b/vendor/github.com/go-openapi/swag/convert_types.go
@@ -181,12 +181,12 @@ func IntValueMap(src map[string]*int) map[string]int {
return dst
}
-// Int32 returns a pointer to of the int64 value passed in.
+// Int32 returns a pointer to of the int32 value passed in.
func Int32(v int32) *int32 {
return &v
}
-// Int32Value returns the value of the int64 pointer passed in or
+// Int32Value returns the value of the int32 pointer passed in or
// 0 if the pointer is nil.
func Int32Value(v *int32) int32 {
if v != nil {
@@ -195,7 +195,7 @@ func Int32Value(v *int32) int32 {
return 0
}
-// Int32Slice converts a slice of int64 values into a slice of
+// Int32Slice converts a slice of int32 values into a slice of
// int32 pointers
func Int32Slice(src []int32) []*int32 {
dst := make([]*int32, len(src))
@@ -299,13 +299,80 @@ func Int64ValueMap(src map[string]*int64) map[string]int64 {
return dst
}
-// Uint returns a pouinter to of the uint value passed in.
+// Uint16 returns a pointer to of the uint16 value passed in.
+func Uint16(v uint16) *uint16 {
+ return &v
+}
+
+// Uint16Value returns the value of the uint16 pointer passed in or
+// 0 if the pointer is nil.
+func Uint16Value(v *uint16) uint16 {
+ if v != nil {
+ return *v
+ }
+
+ return 0
+}
+
+// Uint16Slice converts a slice of uint16 values into a slice of
+// uint16 pointers
+func Uint16Slice(src []uint16) []*uint16 {
+ dst := make([]*uint16, len(src))
+ for i := 0; i < len(src); i++ {
+ dst[i] = &(src[i])
+ }
+
+ return dst
+}
+
+// Uint16ValueSlice converts a slice of uint16 pointers into a slice of
+// uint16 values
+func Uint16ValueSlice(src []*uint16) []uint16 {
+ dst := make([]uint16, len(src))
+
+ for i := 0; i < len(src); i++ {
+ if src[i] != nil {
+ dst[i] = *(src[i])
+ }
+ }
+
+ return dst
+}
+
+// Uint16Map converts a string map of uint16 values into a string
+// map of uint16 pointers
+func Uint16Map(src map[string]uint16) map[string]*uint16 {
+ dst := make(map[string]*uint16)
+
+ for k, val := range src {
+ v := val
+ dst[k] = &v
+ }
+
+ return dst
+}
+
+// Uint16ValueMap converts a string map of uint16 pointers into a string
+// map of uint16 values
+func Uint16ValueMap(src map[string]*uint16) map[string]uint16 {
+ dst := make(map[string]uint16)
+
+ for k, val := range src {
+ if val != nil {
+ dst[k] = *val
+ }
+ }
+
+ return dst
+}
+
+// Uint returns a pointer to of the uint value passed in.
func Uint(v uint) *uint {
return &v
}
-// UintValue returns the value of the uint pouinter passed in or
-// 0 if the pouinter is nil.
+// UintValue returns the value of the uint pointer passed in or
+// 0 if the pointer is nil.
func UintValue(v *uint) uint {
if v != nil {
return *v
@@ -313,8 +380,8 @@ func UintValue(v *uint) uint {
return 0
}
-// UintSlice converts a slice of uint values uinto a slice of
-// uint pouinters
+// UintSlice converts a slice of uint values into a slice of
+// uint pointers
func UintSlice(src []uint) []*uint {
dst := make([]*uint, len(src))
for i := 0; i < len(src); i++ {
@@ -323,7 +390,7 @@ func UintSlice(src []uint) []*uint {
return dst
}
-// UintValueSlice converts a slice of uint pouinters uinto a slice of
+// UintValueSlice converts a slice of uint pointers into a slice of
// uint values
func UintValueSlice(src []*uint) []uint {
dst := make([]uint, len(src))
@@ -335,8 +402,8 @@ func UintValueSlice(src []*uint) []uint {
return dst
}
-// UintMap converts a string map of uint values uinto a string
-// map of uint pouinters
+// UintMap converts a string map of uint values into a string
+// map of uint pointers
func UintMap(src map[string]uint) map[string]*uint {
dst := make(map[string]*uint)
for k, val := range src {
@@ -346,7 +413,7 @@ func UintMap(src map[string]uint) map[string]*uint {
return dst
}
-// UintValueMap converts a string map of uint pouinters uinto a string
+// UintValueMap converts a string map of uint pointers into a string
// map of uint values
func UintValueMap(src map[string]*uint) map[string]uint {
dst := make(map[string]uint)
@@ -358,13 +425,13 @@ func UintValueMap(src map[string]*uint) map[string]uint {
return dst
}
-// Uint32 returns a pouinter to of the uint64 value passed in.
+// Uint32 returns a pointer to of the uint32 value passed in.
func Uint32(v uint32) *uint32 {
return &v
}
-// Uint32Value returns the value of the uint64 pouinter passed in or
-// 0 if the pouinter is nil.
+// Uint32Value returns the value of the uint32 pointer passed in or
+// 0 if the pointer is nil.
func Uint32Value(v *uint32) uint32 {
if v != nil {
return *v
@@ -372,8 +439,8 @@ func Uint32Value(v *uint32) uint32 {
return 0
}
-// Uint32Slice converts a slice of uint64 values uinto a slice of
-// uint32 pouinters
+// Uint32Slice converts a slice of uint32 values into a slice of
+// uint32 pointers
func Uint32Slice(src []uint32) []*uint32 {
dst := make([]*uint32, len(src))
for i := 0; i < len(src); i++ {
@@ -382,7 +449,7 @@ func Uint32Slice(src []uint32) []*uint32 {
return dst
}
-// Uint32ValueSlice converts a slice of uint32 pouinters uinto a slice of
+// Uint32ValueSlice converts a slice of uint32 pointers into a slice of
// uint32 values
func Uint32ValueSlice(src []*uint32) []uint32 {
dst := make([]uint32, len(src))
@@ -394,8 +461,8 @@ func Uint32ValueSlice(src []*uint32) []uint32 {
return dst
}
-// Uint32Map converts a string map of uint32 values uinto a string
-// map of uint32 pouinters
+// Uint32Map converts a string map of uint32 values into a string
+// map of uint32 pointers
func Uint32Map(src map[string]uint32) map[string]*uint32 {
dst := make(map[string]*uint32)
for k, val := range src {
@@ -405,7 +472,7 @@ func Uint32Map(src map[string]uint32) map[string]*uint32 {
return dst
}
-// Uint32ValueMap converts a string map of uint32 pouinters uinto a string
+// Uint32ValueMap converts a string map of uint32 pointers into a string
// map of uint32 values
func Uint32ValueMap(src map[string]*uint32) map[string]uint32 {
dst := make(map[string]uint32)
@@ -417,13 +484,13 @@ func Uint32ValueMap(src map[string]*uint32) map[string]uint32 {
return dst
}
-// Uint64 returns a pouinter to of the uint64 value passed in.
+// Uint64 returns a pointer to of the uint64 value passed in.
func Uint64(v uint64) *uint64 {
return &v
}
-// Uint64Value returns the value of the uint64 pouinter passed in or
-// 0 if the pouinter is nil.
+// Uint64Value returns the value of the uint64 pointer passed in or
+// 0 if the pointer is nil.
func Uint64Value(v *uint64) uint64 {
if v != nil {
return *v
@@ -431,8 +498,8 @@ func Uint64Value(v *uint64) uint64 {
return 0
}
-// Uint64Slice converts a slice of uint64 values uinto a slice of
-// uint64 pouinters
+// Uint64Slice converts a slice of uint64 values into a slice of
+// uint64 pointers
func Uint64Slice(src []uint64) []*uint64 {
dst := make([]*uint64, len(src))
for i := 0; i < len(src); i++ {
@@ -441,7 +508,7 @@ func Uint64Slice(src []uint64) []*uint64 {
return dst
}
-// Uint64ValueSlice converts a slice of uint64 pouinters uinto a slice of
+// Uint64ValueSlice converts a slice of uint64 pointers into a slice of
// uint64 values
func Uint64ValueSlice(src []*uint64) []uint64 {
dst := make([]uint64, len(src))
@@ -453,8 +520,8 @@ func Uint64ValueSlice(src []*uint64) []uint64 {
return dst
}
-// Uint64Map converts a string map of uint64 values uinto a string
-// map of uint64 pouinters
+// Uint64Map converts a string map of uint64 values into a string
+// map of uint64 pointers
func Uint64Map(src map[string]uint64) map[string]*uint64 {
dst := make(map[string]*uint64)
for k, val := range src {
@@ -464,7 +531,7 @@ func Uint64Map(src map[string]uint64) map[string]*uint64 {
return dst
}
-// Uint64ValueMap converts a string map of uint64 pouinters uinto a string
+// Uint64ValueMap converts a string map of uint64 pointers into a string
// map of uint64 values
func Uint64ValueMap(src map[string]*uint64) map[string]uint64 {
dst := make(map[string]uint64)
@@ -476,6 +543,74 @@ func Uint64ValueMap(src map[string]*uint64) map[string]uint64 {
return dst
}
+// Float32 returns a pointer to of the float32 value passed in.
+func Float32(v float32) *float32 {
+ return &v
+}
+
+// Float32Value returns the value of the float32 pointer passed in or
+// 0 if the pointer is nil.
+func Float32Value(v *float32) float32 {
+ if v != nil {
+ return *v
+ }
+
+ return 0
+}
+
+// Float32Slice converts a slice of float32 values into a slice of
+// float32 pointers
+func Float32Slice(src []float32) []*float32 {
+ dst := make([]*float32, len(src))
+
+ for i := 0; i < len(src); i++ {
+ dst[i] = &(src[i])
+ }
+
+ return dst
+}
+
+// Float32ValueSlice converts a slice of float32 pointers into a slice of
+// float32 values
+func Float32ValueSlice(src []*float32) []float32 {
+ dst := make([]float32, len(src))
+
+ for i := 0; i < len(src); i++ {
+ if src[i] != nil {
+ dst[i] = *(src[i])
+ }
+ }
+
+ return dst
+}
+
+// Float32Map converts a string map of float32 values into a string
+// map of float32 pointers
+func Float32Map(src map[string]float32) map[string]*float32 {
+ dst := make(map[string]*float32)
+
+ for k, val := range src {
+ v := val
+ dst[k] = &v
+ }
+
+ return dst
+}
+
+// Float32ValueMap converts a string map of float32 pointers into a string
+// map of float32 values
+func Float32ValueMap(src map[string]*float32) map[string]float32 {
+ dst := make(map[string]float32)
+
+ for k, val := range src {
+ if val != nil {
+ dst[k] = *val
+ }
+ }
+
+ return dst
+}
+
// Float64 returns a pointer to of the float64 value passed in.
func Float64(v float64) *float64 {
return &v
diff --git a/vendor/github.com/go-openapi/swag/go.mod b/vendor/github.com/go-openapi/swag/go.mod
index 15bbb0822..4aef463e4 100644
--- a/vendor/github.com/go-openapi/swag/go.mod
+++ b/vendor/github.com/go-openapi/swag/go.mod
@@ -6,9 +6,11 @@ require (
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63
github.com/stretchr/testify v1.3.0
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
- gopkg.in/yaml.v2 v2.2.2
+ gopkg.in/yaml.v2 v2.2.4
)
replace github.com/golang/lint => golang.org/x/lint v0.0.0-20190409202823-959b441ac422
replace sourcegraph.com/sourcegraph/go-diff => github.com/sourcegraph/go-diff v0.5.1
+
+go 1.13
diff --git a/vendor/github.com/go-openapi/swag/go.sum b/vendor/github.com/go-openapi/swag/go.sum
index 33469f54a..e8a80bacf 100644
--- a/vendor/github.com/go-openapi/swag/go.sum
+++ b/vendor/github.com/go-openapi/swag/go.sum
@@ -16,5 +16,5 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
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/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
-gopkg.in/yaml.v2 v2.2.2/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=
diff --git a/vendor/github.com/go-openapi/swag/json.go b/vendor/github.com/go-openapi/swag/json.go
index edf93d84c..7e9902ca3 100644
--- a/vendor/github.com/go-openapi/swag/json.go
+++ b/vendor/github.com/go-openapi/swag/json.go
@@ -51,7 +51,7 @@ type ejUnmarshaler interface {
UnmarshalEasyJSON(w *jlexer.Lexer)
}
-// WriteJSON writes json data, prefers finding an appropriate interface to short-circuit the marshaller
+// WriteJSON writes json data, prefers finding an appropriate interface to short-circuit the marshaler
// so it takes the fastest option available.
func WriteJSON(data interface{}) ([]byte, error) {
if d, ok := data.(ejMarshaler); ok {
@@ -65,8 +65,8 @@ func WriteJSON(data interface{}) ([]byte, error) {
return json.Marshal(data)
}
-// ReadJSON reads json data, prefers finding an appropriate interface to short-circuit the unmarshaller
-// so it takes the fastes option available
+// ReadJSON reads json data, prefers finding an appropriate interface to short-circuit the unmarshaler
+// so it takes the fastest option available
func ReadJSON(data []byte, value interface{}) error {
trimmedData := bytes.Trim(data, "\x00")
if d, ok := value.(ejUnmarshaler); ok {
@@ -189,7 +189,7 @@ func FromDynamicJSON(data, target interface{}) error {
return json.Unmarshal(b, target)
}
-// NameProvider represents an object capabale of translating from go property names
+// NameProvider represents an object capable of translating from go property names
// to json property names
// This type is thread-safe.
type NameProvider struct {
diff --git a/vendor/github.com/go-openapi/swag/loading.go b/vendor/github.com/go-openapi/swag/loading.go
index 70f4fb361..04160b89b 100644
--- a/vendor/github.com/go-openapi/swag/loading.go
+++ b/vendor/github.com/go-openapi/swag/loading.go
@@ -27,6 +27,15 @@ import (
// LoadHTTPTimeout the default timeout for load requests
var LoadHTTPTimeout = 30 * time.Second
+// LoadHTTPBasicAuthUsername the username to use when load requests require basic auth
+var LoadHTTPBasicAuthUsername = ""
+
+// LoadHTTPBasicAuthPassword the password to use when load requests require basic auth
+var LoadHTTPBasicAuthPassword = ""
+
+// LoadHTTPCustomHeaders an optional collection of custom HTTP headers for load requests
+var LoadHTTPCustomHeaders = map[string]string{}
+
// LoadFromFileOrHTTP loads the bytes from a file or a remote http server based on the path passed in
func LoadFromFileOrHTTP(path string) ([]byte, error) {
return LoadStrategy(path, ioutil.ReadFile, loadHTTPBytes(LoadHTTPTimeout))(path)
@@ -59,6 +68,15 @@ func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) {
if err != nil {
return nil, err
}
+
+ if LoadHTTPBasicAuthUsername != "" && LoadHTTPBasicAuthPassword != "" {
+ req.SetBasicAuth(LoadHTTPBasicAuthUsername, LoadHTTPBasicAuthPassword)
+ }
+
+ for key, val := range LoadHTTPCustomHeaders {
+ req.Header.Set(key, val)
+ }
+
resp, err := client.Do(req)
defer func() {
if resp != nil {
diff --git a/vendor/github.com/gophercloud/gophercloud/.zuul.yaml b/vendor/github.com/gophercloud/gophercloud/.zuul.yaml
index 135e3b203..b6649a3d6 100644
--- a/vendor/github.com/gophercloud/gophercloud/.zuul.yaml
+++ b/vendor/github.com/gophercloud/gophercloud/.zuul.yaml
@@ -12,6 +12,7 @@
description: |
Run gophercloud acceptance test on master branch
run: .zuul/playbooks/gophercloud-acceptance-test/run.yaml
+ nodeset: ubuntu-bionic
- job:
name: gophercloud-acceptance-test-ironic
@@ -19,6 +20,7 @@
description: |
Run gophercloud ironic acceptance test on master branch
run: .zuul/playbooks/gophercloud-acceptance-test-ironic/run.yaml
+ nodeset: ubuntu-bionic
- job:
name: gophercloud-acceptance-test-stein
@@ -74,16 +76,6 @@
global_env:
OS_BRANCH: stable/newton
-- job:
- name: gophercloud-acceptance-test-mitaka
- parent: gophercloud-acceptance-test
- description: |
- Run gophercloud acceptance test on mitaka branch
- vars:
- global_env:
- OS_BRANCH: stable/mitaka
- nodeset: ubuntu-trusty
-
- project:
name: gophercloud/gophercloud
check:
@@ -91,9 +83,6 @@
- gophercloud-unittest
- gophercloud-acceptance-test
- gophercloud-acceptance-test-ironic
- recheck-mitaka:
- jobs:
- - gophercloud-acceptance-test-mitaka
recheck-newton:
jobs:
- gophercloud-acceptance-test-newton
diff --git a/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md b/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md
index e1044e68a..23af89a3f 100644
--- a/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md
+++ b/vendor/github.com/gophercloud/gophercloud/CHANGELOG.md
@@ -1,4 +1,217 @@
-## 0.7.0 (Unreleased)
+## 0.12.0 (Unreleased)
+
+## 0.11.0 (May 14, 2020)
+
+UPGRADE NOTES
+
+* Object storage container and object names are now URL encoded [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930)
+* All responses now have access to the returned headers. Please report any issues this has caused [GH-1942](https://github.com/gophercloud/gophercloud/pull/1942)
+* Changes have been made to the internal HTTP client to ensure response bodies are handled in a way that enables connections to be re-used more efficiently [GH-1952](https://github.com/gophercloud/gophercloud/pull/1952)
+
+IMPROVEMENTS
+
+* Added `objectstorage/v1/containers.BulkDelete` [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930)
+* Added `objectstorage/v1/objects.BulkDelete` [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930)
+* Object storage container and object names are now URL encoded [GH-1930](https://github.com/gophercloud/gophercloud/pull/1930)
+* All responses now have access to the returned headers [GH-1942](https://github.com/gophercloud/gophercloud/pull/1942)
+* Added `compute/v2/extensions/injectnetworkinfo.InjectNetworkInfo` [GH-1941](https://github.com/gophercloud/gophercloud/pull/1941)
+* Added `compute/v2/extensions/resetnetwork.ResetNetwork` [GH-1941](https://github.com/gophercloud/gophercloud/pull/1941)
+* Added `identity/v3/extensions/trusts.ListRoles` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939)
+* Added `identity/v3/extensions/trusts.GetRole` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939)
+* Added `identity/v3/extensions/trusts.CheckRole` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939)
+* Added `identity/v3/extensions/oauth1.Create` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `identity/v3/extensions/oauth1.CreateConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `identity/v3/extensions/oauth1.DeleteConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `identity/v3/extensions/oauth1.ListConsumers` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `identity/v3/extensions/oauth1.GetConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `identity/v3/extensions/oauth1.UpdateConsumer` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `identity/v3/extensions/oauth1.RequestToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `identity/v3/extensions/oauth1.AuthorizeToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `identity/v3/extensions/oauth1.CreateAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `identity/v3/extensions/oauth1.GetAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `identity/v3/extensions/oauth1.RevokeAccessToken` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `identity/v3/extensions/oauth1.ListAccessTokens` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `identity/v3/extensions/oauth1.ListAccessTokenRoles` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `identity/v3/extensions/oauth1.GetAccessTokenRole` [GH-1935](https://github.com/gophercloud/gophercloud/pull/1935)
+* Added `networking/v2/extensions/agents.Update` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954)
+* Added `networking/v2/extensions/agents.Delete` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954)
+* Added `networking/v2/extensions/agents.ScheduleDHCPNetwork` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954)
+* Added `networking/v2/extensions/agents.RemoveDHCPNetwork` [GH-1954](https://github.com/gophercloud/gophercloud/pull/1954)
+* Added `identity/v3/projects.CreateOpts.Extra` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951)
+* Added `identity/v3/projects.CreateOpts.Options` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951)
+* Added `identity/v3/projects.UpdateOpts.Extra` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951)
+* Added `identity/v3/projects.UpdateOpts.Options` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951)
+* Added `identity/v3/projects.Project.Extra` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951)
+* Added `identity/v3/projects.Options.Options` [GH-1951](https://github.com/gophercloud/gophercloud/pull/1951)
+* Added `imageservice/v2/images.Image.OpenStackImageImportMethods` [GH-1962](https://github.com/gophercloud/gophercloud/pull/1962)
+* Added `imageservice/v2/images.Image.OpenStackImageStoreIDs` [GH-1962](https://github.com/gophercloud/gophercloud/pull/1962)
+
+BUG FIXES
+
+* Changed`identity/v3/extensions/trusts.Trust.RemainingUses` from `bool` to `int` [GH-1939](https://github.com/gophercloud/gophercloud/pull/1939)
+* Changed `identity/v3/applicationcredentials.CreateOpts.ExpiresAt` from `string` to `*time.Time` [GH-1937](https://github.com/gophercloud/gophercloud/pull/1937)
+* Fixed issue with unmarshalling/decoding slices of composed structs [GH-1964](https://github.com/gophercloud/gophercloud/pull/1964)
+
+## 0.10.0 (April 12, 2020)
+
+UPGRADE NOTES
+
+* The various `IDFromName` convenience functions have been moved to https://github.com/gophercloud/utils [GH-1897](https://github.com/gophercloud/gophercloud/pull/1897)
+* `sharedfilesystems/v2/shares.GetExportLocations` was renamed to `sharedfilesystems/v2/shares.ListExportLocations` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932)
+
+IMPROVEMENTS
+
+* Added `blockstorage/extensions/volumeactions.SetBootable` [GH-1891](https://github.com/gophercloud/gophercloud/pull/1891)
+* Added `blockstorage/extensions/backups.Export` [GH-1894](https://github.com/gophercloud/gophercloud/pull/1894)
+* Added `blockstorage/extensions/backups.Import` [GH-1894](https://github.com/gophercloud/gophercloud/pull/1894)
+* Added `placement/v1/resourceproviders.GetTraits` [GH-1899](https://github.com/gophercloud/gophercloud/pull/1899)
+* Added the ability to authenticate with Amazon EC2 Credentials [GH-1900](https://github.com/gophercloud/gophercloud/pull/1900)
+* Added ability to list Nova services by binary and host [GH-1904](https://github.com/gophercloud/gophercloud/pull/1904)
+* Added `compute/v2/extensions/services.Update` [GH-1902](https://github.com/gophercloud/gophercloud/pull/1902)
+* Added system scope to v3 authentication [GH-1908](https://github.com/gophercloud/gophercloud/pull/1908)
+* Added `identity/v3/extensions/ec2tokens.ValidateS3Token` [GH-1906](https://github.com/gophercloud/gophercloud/pull/1906)
+* Added `containerinfra/v1/clusters.Cluster.HealthStatus` [GH-1910](https://github.com/gophercloud/gophercloud/pull/1910)
+* Added `containerinfra/v1/clusters.Cluster.HealthStatusReason` [GH-1910](https://github.com/gophercloud/gophercloud/pull/1910)
+* Added `loadbalancer/v2/amphorae.Failover` [GH-1912](https://github.com/gophercloud/gophercloud/pull/1912)
+* Added `identity/v3/extensions/ec2credentials.List` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916)
+* Added `identity/v3/extensions/ec2credentials.Get` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916)
+* Added `identity/v3/extensions/ec2credentials.Create` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916)
+* Added `identity/v3/extensions/ec2credentials.Delete` [GH-1916](https://github.com/gophercloud/gophercloud/pull/1916)
+* Added `ErrUnexpectedResponseCode.ResponseHeader` [GH-1919](https://github.com/gophercloud/gophercloud/pull/1919)
+* Added support for TOTP authentication [GH-1922](https://github.com/gophercloud/gophercloud/pull/1922)
+* `sharedfilesystems/v2/shares.GetExportLocations` was renamed to `sharedfilesystems/v2/shares.ListExportLocations` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932)
+* Added `sharedfilesystems/v2/shares.GetExportLocation` [GH-1932](https://github.com/gophercloud/gophercloud/pull/1932)
+* Added `sharedfilesystems/v2/shares.Revert` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931)
+* Added `sharedfilesystems/v2/shares.ResetStatus` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931)
+* Added `sharedfilesystems/v2/shares.ForceDelete` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931)
+* Added `sharedfilesystems/v2/shares.Unmanage` [GH-1931](https://github.com/gophercloud/gophercloud/pull/1931)
+* Added `blockstorage/v3/attachments.Create` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934)
+* Added `blockstorage/v3/attachments.List` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934)
+* Added `blockstorage/v3/attachments.Get` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934)
+* Added `blockstorage/v3/attachments.Update` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934)
+* Added `blockstorage/v3/attachments.Delete` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934)
+* Added `blockstorage/v3/attachments.Complete` [GH-1934](https://github.com/gophercloud/gophercloud/pull/1934)
+
+BUG FIXES
+
+* Fixed issue with Orchestration `get_file` only being able to read JSON and YAML files [GH-1915](https://github.com/gophercloud/gophercloud/pull/1915)
+
+## 0.9.0 (March 10, 2020)
+
+UPGRADE NOTES
+
+* The way we implement new API result fields added by microversions has changed. Previously, we would declare a dedicated `ExtractFoo` function in a file called `microversions.go`. Now, we are declaring those fields inline of the original result struct as a pointer. [GH-1854](https://github.com/gophercloud/gophercloud/pull/1854)
+
+* `compute/v2/servers.CreateOpts.Networks` has changed from `[]Network` to `interface{}` in order to support creating servers that have no networks. [GH-1884](https://github.com/gophercloud/gophercloud/pull/1884)
+
+IMPROVEMENTS
+
+* Added `compute/v2/extensions/instanceactions.List` [GH-1848](https://github.com/gophercloud/gophercloud/pull/1848)
+* Added `compute/v2/extensions/instanceactions.Get` [GH-1848](https://github.com/gophercloud/gophercloud/pull/1848)
+* Added `networking/v2/ports.List.FixedIPs` [GH-1849](https://github.com/gophercloud/gophercloud/pull/1849)
+* Added `identity/v3/extensions/trusts.List` [GH-1855](https://github.com/gophercloud/gophercloud/pull/1855)
+* Added `identity/v3/extensions/trusts.Get` [GH-1855](https://github.com/gophercloud/gophercloud/pull/1855)
+* Added `identity/v3/extensions/trusts.Trust.ExpiresAt` [GH-1857](https://github.com/gophercloud/gophercloud/pull/1857)
+* Added `identity/v3/extensions/trusts.Trust.DeletedAt` [GH-1857](https://github.com/gophercloud/gophercloud/pull/1857)
+* Added `compute/v2/extensions/instanceactions.InstanceActionDetail` [GH-1851](https://github.com/gophercloud/gophercloud/pull/1851)
+* Added `compute/v2/extensions/instanceactions.Event` [GH-1851](https://github.com/gophercloud/gophercloud/pull/1851)
+* Added `compute/v2/extensions/instanceactions.ListOpts` [GH-1858](https://github.com/gophercloud/gophercloud/pull/1858)
+* Added `objectstorage/v1/containers.UpdateOpts.TempURLKey` [GH-1864](https://github.com/gophercloud/gophercloud/pull/1864)
+* Added `objectstorage/v1/containers.UpdateOpts.TempURLKey2` [GH-1864](https://github.com/gophercloud/gophercloud/pull/1864)
+* Added `placement/v1/resourceproviders.GetUsages` [GH-1862](https://github.com/gophercloud/gophercloud/pull/1862)
+* Added `placement/v1/resourceproviders.GetInventories` [GH-1862](https://github.com/gophercloud/gophercloud/pull/1862)
+* Added `imageservice/v2/images.ReplaceImageMinRam` [GH-1867](https://github.com/gophercloud/gophercloud/pull/1867)
+* Added `objectstorage/v1/containers.UpdateOpts.TempURLKey` [GH-1865](https://github.com/gophercloud/gophercloud/pull/1865)
+* Added `objectstorage/v1/containers.CreateOpts.TempURLKey2` [GH-1865](https://github.com/gophercloud/gophercloud/pull/1865)
+* Added `blockstorage/extensions/volumetransfers.List` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869)
+* Added `blockstorage/extensions/volumetransfers.Create` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869)
+* Added `blockstorage/extensions/volumetransfers.Accept` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869)
+* Added `blockstorage/extensions/volumetransfers.Get` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869)
+* Added `blockstorage/extensions/volumetransfers.Delete` [GH-1869](https://github.com/gophercloud/gophercloud/pull/1869)
+* Added `blockstorage/extensions/backups.RestoreFromBackup` [GH-1871](https://github.com/gophercloud/gophercloud/pull/1871)
+* Added `blockstorage/v3/volumes.CreateOpts.BackupID` [GH-1871](https://github.com/gophercloud/gophercloud/pull/1871)
+* Added `blockstorage/v3/volumes.Volume.BackupID` [GH-1871](https://github.com/gophercloud/gophercloud/pull/1871)
+* Added `identity/v3/projects.ListOpts.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
+* Added `identity/v3/projects.ListOpts.TagsAny` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
+* Added `identity/v3/projects.ListOpts.NotTags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
+* Added `identity/v3/projects.ListOpts.NotTagsAny` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
+* Added `identity/v3/projects.CreateOpts.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
+* Added `identity/v3/projects.UpdateOpts.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
+* Added `identity/v3/projects.Project.Tags` [GH-1882](https://github.com/gophercloud/gophercloud/pull/1882)
+* Changed `compute/v2/servers.CreateOpts.Networks` from `[]Network` to `interface{}` to support creating servers with no networks. [GH-1884](https://github.com/gophercloud/gophercloud/pull/1884)
+
+
+BUG FIXES
+
+* Added support for `int64` headers, which were previously being silently dropped [GH-1860](https://github.com/gophercloud/gophercloud/pull/1860)
+* Allow image properties with empty values [GH-1875](https://github.com/gophercloud/gophercloud/pull/1875)
+* Fixed `compute/v2/extensions/extendedserverattributes.ServerAttributesExt.Userdata` JSON tag [GH-1881](https://github.com/gophercloud/gophercloud/pull/1881)
+
+## 0.8.0 (February 8, 2020)
+
+UPGRADE NOTES
+
+* The behavior of `keymanager/v1/acls.SetOpts` has changed. Instead of a struct, it is now `[]SetOpt`. See [GH-1816](https://github.com/gophercloud/gophercloud/pull/1816) for implementation details.
+
+IMPROVEMENTS
+
+* The result of `containerinfra/v1/clusters.Resize` now returns only the UUID when calling `Extract`. This is a backwards-breaking change from the previous struct that was returned [GH-1649](https://github.com/gophercloud/gophercloud/pull/1649)
+* Added `compute/v2/extensions/shelveunshelve.Shelve` [GH-1799](https://github.com/gophercloud/gophercloud/pull/1799)
+* Added `compute/v2/extensions/shelveunshelve.ShelveOffload` [GH-1799](https://github.com/gophercloud/gophercloud/pull/1799)
+* Added `compute/v2/extensions/shelveunshelve.Unshelve` [GH-1799](https://github.com/gophercloud/gophercloud/pull/1799)
+* Added `containerinfra/v1/nodegroups.Get` [GH-1774](https://github.com/gophercloud/gophercloud/pull/1774)
+* Added `containerinfra/v1/nodegroups.List` [GH-1774](https://github.com/gophercloud/gophercloud/pull/1774)
+* Added `orchestration/v1/resourcetypes.List` [GH-1806](https://github.com/gophercloud/gophercloud/pull/1806)
+* Added `orchestration/v1/resourcetypes.GetSchema` [GH-1806](https://github.com/gophercloud/gophercloud/pull/1806)
+* Added `orchestration/v1/resourcetypes.GenerateTemplate` [GH-1806](https://github.com/gophercloud/gophercloud/pull/1806)
+* Added `keymanager/v1/acls.SetOpt` and changed `keymanager/v1/acls.SetOpts` to `[]SetOpt` [GH-1816](https://github.com/gophercloud/gophercloud/pull/1816)
+* Added `blockstorage/apiversions.List` [GH-458](https://github.com/gophercloud/gophercloud/pull/458)
+* Added `blockstorage/apiversions.Get` [GH-458](https://github.com/gophercloud/gophercloud/pull/458)
+* Added `StatusCodeError` interface and `GetStatusCode` convenience method [GH-1820](https://github.com/gophercloud/gophercloud/pull/1820)
+* Added pagination support to `compute/v2/extensions/usage.SingleTenant` [GH-1819](https://github.com/gophercloud/gophercloud/pull/1819)
+* Added pagination support to `compute/v2/extensions/usage.AllTenants` [GH-1819](https://github.com/gophercloud/gophercloud/pull/1819)
+* Added `placement/v1/resourceproviders.List` [GH-1815](https://github.com/gophercloud/gophercloud/pull/1815)
+* Allow `CreateMemberOptsBuilder` to be passed in `loadbalancer/v2/pools.Create` [GH-1822](https://github.com/gophercloud/gophercloud/pull/1822)
+* Added `Backup` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824)
+* Added `MonitorAddress` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824)
+* Added `MonitorPort` to `loadbalancer/v2/pools.CreateMemberOpts` [GH-1824](https://github.com/gophercloud/gophercloud/pull/1824)
+* Changed `Impersonation` to a non-required field in `identity/v3/extensions/trusts.CreateOpts` [GH-1818](https://github.com/gophercloud/gophercloud/pull/1818)
+* Added `InsertHeaders` to `loadbalancer/v2/listeners.UpdateOpts` [GH-1835]
+* Added `NUMATopology` to `baremetalintrospection/v1/introspection.Data` [GH-1842](https://github.com/gophercloud/gophercloud/pull/1842)
+* Added `placement/v1/resourceproviders.Create` [GH-1841](https://github.com/gophercloud/gophercloud/pull/1841)
+* Added `blockstorage/extensions/volumeactions.UploadImageOpts.Visibility` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873)
+* Added `blockstorage/extensions/volumeactions.UploadImageOpts.Protected` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873)
+* Added `blockstorage/extensions/volumeactions.VolumeImage.Visibility` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873)
+* Added `blockstorage/extensions/volumeactions.VolumeImage.Protected` [GH-1873](https://github.com/gophercloud/gophercloud/pull/1873)
+
+BUG FIXES
+
+* Changed `sort_key` to `sort_keys` in ` workflow/v2/crontriggers.ListOpts` [GH-1809](https://github.com/gophercloud/gophercloud/pull/1809)
+* Allow `blockstorage/extensions/schedulerstats.Capabilities.MaxOverSubscriptionRatio` to accept both string and int/float responses [GH-1817](https://github.com/gophercloud/gophercloud/pull/1817)
+* Fixed bug in `NewLoadBalancerV2` for situations when the LBaaS service was advertised without a `/v2.0` endpoint [GH-1829](https://github.com/gophercloud/gophercloud/pull/1829)
+* Fixed JSON tags in `baremetal/v1/ports.UpdateOperation` [GH-1840](https://github.com/gophercloud/gophercloud/pull/1840)
+* Fixed JSON tags in `networking/v2/extensions/lbaas/vips.commonResult.Extract()` [GH-1840](https://github.com/gophercloud/gophercloud/pull/1840)
+
+## 0.7.0 (December 3, 2019)
+
+IMPROVEMENTS
+
+* Allow a token to be used directly for authentication instead of generating a new token based on a given token [GH-1752](https://github.com/gophercloud/gophercloud/pull/1752)
+* Moved `tags.ServerTagsExt` to servers.TagsExt` [GH-1760](https://github.com/gophercloud/gophercloud/pull/1760)
+* Added `tags`, `tags-any`, `not-tags`, and `not-tags-any` to `compute/v2/servers.ListOpts` [GH-1759](https://github.com/gophercloud/gophercloud/pull/1759)
+* Added `AccessRule` to `identity/v3/applicationcredentials` [GH-1758](https://github.com/gophercloud/gophercloud/pull/1758)
+* Gophercloud no longer returns an error when multiple endpoints are found. Instead, it will choose the first endpoint and discard the others [GH-1766](https://github.com/gophercloud/gophercloud/pull/1766)
+* Added `networking/v2/extensions/fwaas_v2/rules.Create` [GH-1768](https://github.com/gophercloud/gophercloud/pull/1768)
+* Added `networking/v2/extensions/fwaas_v2/rules.Delete` [GH-1771](https://github.com/gophercloud/gophercloud/pull/1771)
+* Added `loadbalancer/v2/providers.List` [GH-1765](https://github.com/gophercloud/gophercloud/pull/1765)
+* Added `networking/v2/extensions/fwaas_v2/rules.Get` [GH-1772](https://github.com/gophercloud/gophercloud/pull/1772)
+* Added `networking/v2/extensions/fwaas_v2/rules.Update` [GH-1776](https://github.com/gophercloud/gophercloud/pull/1776)
+* Added `networking/v2/extensions/fwaas_v2/rules.List` [GH-1783](https://github.com/gophercloud/gophercloud/pull/1783)
+* Added `MaxRetriesDown` into `loadbalancer/v2/monitors.CreateOpts` [GH-1785](https://github.com/gophercloud/gophercloud/pull/1785)
+* Added `MaxRetriesDown` into `loadbalancer/v2/monitors.UpdateOpts` [GH-1786](https://github.com/gophercloud/gophercloud/pull/1786)
+* Added `MaxRetriesDown` into `loadbalancer/v2/monitors.Monitor` [GH-1787](https://github.com/gophercloud/gophercloud/pull/1787)
+* Added `MaxRetriesDown` into `loadbalancer/v2/monitors.ListOpts` [GH-1788](https://github.com/gophercloud/gophercloud/pull/1788)
+* Updated `go.mod` dependencies, specifically to account for CVE-2019-11840 with `golang.org/x/crypto` [GH-1793](https://github.com/gophercloud/gophercloud/pull/1788)
## 0.6.0 (October 17, 2019)
diff --git a/vendor/github.com/gophercloud/gophercloud/auth_options.go b/vendor/github.com/gophercloud/gophercloud/auth_options.go
index 5ffa8d1e0..4f301305e 100644
--- a/vendor/github.com/gophercloud/gophercloud/auth_options.go
+++ b/vendor/github.com/gophercloud/gophercloud/auth_options.go
@@ -45,6 +45,9 @@ type AuthOptions struct {
Password string `json:"password,omitempty"`
+ // Passcode is used in TOTP authentication method
+ Passcode string `json:"passcode,omitempty"`
+
// At most one of DomainID and DomainName must be provided if using Username
// with Identity V3. Otherwise, either are optional.
DomainID string `json:"-"`
@@ -98,6 +101,7 @@ type AuthScope struct {
ProjectName string
DomainID string
DomainName string
+ System bool
}
// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
@@ -133,6 +137,8 @@ func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
return map[string]interface{}{"auth": authMap}, nil
}
+// ToTokenV3CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
+// interface in the v3 tokens package
func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) {
type domainReq struct {
ID *string `json:"id,omitempty"`
@@ -148,7 +154,8 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
type userReq struct {
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
- Password string `json:"password,omitempty"`
+ Password *string `json:"password,omitempty"`
+ Passcode *string `json:"passcode,omitempty"`
Domain *domainReq `json:"domain,omitempty"`
}
@@ -167,11 +174,16 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
Secret *string `json:"secret,omitempty"`
}
+ type totpReq struct {
+ User *userReq `json:"user,omitempty"`
+ }
+
type identityReq struct {
Methods []string `json:"methods"`
Password *passwordReq `json:"password,omitempty"`
Token *tokenReq `json:"token,omitempty"`
ApplicationCredential *applicationCredentialReq `json:"application_credential,omitempty"`
+ TOTP *totpReq `json:"totp,omitempty"`
}
type authReq struct {
@@ -186,7 +198,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
// if insufficient or incompatible information is present.
var req request
- if opts.Password == "" {
+ if opts.Password == "" && opts.Passcode == "" {
if opts.TokenID != "" {
// Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
// parameters.
@@ -274,7 +286,14 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
}
} else {
// Password authentication.
- req.Auth.Identity.Methods = []string{"password"}
+ if opts.Password != "" {
+ req.Auth.Identity.Methods = append(req.Auth.Identity.Methods, "password")
+ }
+
+ // TOTP authentication.
+ if opts.Passcode != "" {
+ req.Auth.Identity.Methods = append(req.Auth.Identity.Methods, "totp")
+ }
// At least one of Username and UserID must be specified.
if opts.Username == "" && opts.UserID == "" {
@@ -298,23 +317,46 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
}
// Configure the request for Username and Password authentication with a DomainID.
- req.Auth.Identity.Password = &passwordReq{
- User: userReq{
- Name: &opts.Username,
- Password: opts.Password,
- Domain: &domainReq{ID: &opts.DomainID},
- },
+ if opts.Password != "" {
+ req.Auth.Identity.Password = &passwordReq{
+ User: userReq{
+ Name: &opts.Username,
+ Password: &opts.Password,
+ Domain: &domainReq{ID: &opts.DomainID},
+ },
+ }
+ }
+ if opts.Passcode != "" {
+ req.Auth.Identity.TOTP = &totpReq{
+ User: &userReq{
+ Name: &opts.Username,
+ Passcode: &opts.Passcode,
+ Domain: &domainReq{ID: &opts.DomainID},
+ },
+ }
}
}
if opts.DomainName != "" {
// Configure the request for Username and Password authentication with a DomainName.
- req.Auth.Identity.Password = &passwordReq{
- User: userReq{
- Name: &opts.Username,
- Password: opts.Password,
- Domain: &domainReq{Name: &opts.DomainName},
- },
+ if opts.Password != "" {
+ req.Auth.Identity.Password = &passwordReq{
+ User: userReq{
+ Name: &opts.Username,
+ Password: &opts.Password,
+ Domain: &domainReq{Name: &opts.DomainName},
+ },
+ }
+ }
+
+ if opts.Passcode != "" {
+ req.Auth.Identity.TOTP = &totpReq{
+ User: &userReq{
+ Name: &opts.Username,
+ Passcode: &opts.Passcode,
+ Domain: &domainReq{Name: &opts.DomainName},
+ },
+ }
}
}
}
@@ -329,8 +371,22 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
}
// Configure the request for UserID and Password authentication.
- req.Auth.Identity.Password = &passwordReq{
- User: userReq{ID: &opts.UserID, Password: opts.Password},
+ if opts.Password != "" {
+ req.Auth.Identity.Password = &passwordReq{
+ User: userReq{
+ ID: &opts.UserID,
+ Password: &opts.Password,
+ },
+ }
+ }
+
+ if opts.Passcode != "" {
+ req.Auth.Identity.TOTP = &totpReq{
+ User: &userReq{
+ ID: &opts.UserID,
+ Passcode: &opts.Passcode,
+ },
+ }
}
}
}
@@ -347,6 +403,8 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
return b, nil
}
+// ToTokenV3ScopeMap builds a scope from AuthOptions and satisfies interface in
+// the v3 tokens package.
func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
// For backwards compatibility.
// If AuthOptions.Scope was not set, try to determine it.
@@ -364,6 +422,14 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
}
}
+ if opts.Scope.System {
+ return map[string]interface{}{
+ "system": map[string]interface{}{
+ "all": true,
+ },
+ }, nil
+ }
+
if opts.Scope.ProjectName != "" {
// ProjectName provided: either DomainID or DomainName must also be supplied.
// ProjectID may not be supplied.
@@ -433,5 +499,16 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
}
func (opts AuthOptions) CanReauth() bool {
+ if opts.Passcode != "" {
+ // cannot reauth using TOTP passcode
+ return false
+ }
+
return opts.AllowReauth
}
+
+// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder
+// interface in the v3 tokens package.
+func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) {
+ return nil, nil
+}
diff --git a/vendor/github.com/gophercloud/gophercloud/errors.go b/vendor/github.com/gophercloud/gophercloud/errors.go
index 0bcb3af7f..77cabf6a9 100644
--- a/vendor/github.com/gophercloud/gophercloud/errors.go
+++ b/vendor/github.com/gophercloud/gophercloud/errors.go
@@ -2,6 +2,7 @@ package gophercloud
import (
"fmt"
+ "net/http"
"strings"
)
@@ -77,11 +78,12 @@ func (e ErrMissingAnyoneOfEnvironmentVariables) Error() string {
// those listed in OkCodes is encountered.
type ErrUnexpectedResponseCode struct {
BaseError
- URL string
- Method string
- Expected []int
- Actual int
- Body []byte
+ URL string
+ Method string
+ Expected []int
+ Actual int
+ Body []byte
+ ResponseHeader http.Header
}
func (e ErrUnexpectedResponseCode) Error() string {
@@ -92,6 +94,23 @@ func (e ErrUnexpectedResponseCode) Error() string {
return e.choseErrString()
}
+// GetStatusCode returns the actual status code of the error.
+func (e ErrUnexpectedResponseCode) GetStatusCode() int {
+ return e.Actual
+}
+
+// StatusCodeError is a convenience interface to easily allow access to the
+// status code field of the various ErrDefault* types.
+//
+// By using this interface, you only have to make a single type cast of
+// the returned error to err.(StatusCodeError) and then call GetStatusCode()
+// instead of having a large switch statement checking for each of the
+// ErrDefault* types.
+type StatusCodeError interface {
+ Error() string
+ GetStatusCode() int
+}
+
// ErrDefault400 is the default error type returned on a 400 HTTP response code.
type ErrDefault400 struct {
ErrUnexpectedResponseCode
diff --git a/vendor/github.com/gophercloud/gophercloud/go.mod b/vendor/github.com/gophercloud/gophercloud/go.mod
index 1eebf17ed..46e16507d 100644
--- a/vendor/github.com/gophercloud/gophercloud/go.mod
+++ b/vendor/github.com/gophercloud/gophercloud/go.mod
@@ -1,7 +1,13 @@
module github.com/gophercloud/gophercloud
require (
- golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
- gopkg.in/yaml.v2 v2.2.4
+ golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e
+ golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 // indirect
+ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
+ golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 // indirect
+ golang.org/x/text v0.3.2 // indirect
+ golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371 // indirect
+ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 // indirect
+ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
+ gopkg.in/yaml.v2 v2.2.7
)
-
diff --git a/vendor/github.com/gophercloud/gophercloud/go.sum b/vendor/github.com/gophercloud/gophercloud/go.sum
index 27dc9b305..9a0b94de1 100644
--- a/vendor/github.com/gophercloud/gophercloud/go.sum
+++ b/vendor/github.com/gophercloud/gophercloud/go.sum
@@ -1,8 +1,26 @@
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-20191202143827-86a70503ff7e h1:egKlR8l7Nu9vHGWbcUV8lqR4987UfUbBd7GbhqGzNYU=
+golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+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-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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191203134012-c197fd4bf371/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
+gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go b/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go
index 0e8d90ff8..c801de555 100644
--- a/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/auth_env.go
@@ -38,6 +38,7 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
username := os.Getenv("OS_USERNAME")
userID := os.Getenv("OS_USERID")
password := os.Getenv("OS_PASSWORD")
+ passcode := os.Getenv("OS_PASSCODE")
tenantID := os.Getenv("OS_TENANT_ID")
tenantName := os.Getenv("OS_TENANT_NAME")
domainID := os.Getenv("OS_DOMAIN_ID")
@@ -73,8 +74,9 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
}
}
- if password == "" && applicationCredentialID == "" && applicationCredentialName == "" {
+ if password == "" && passcode == "" && applicationCredentialID == "" && applicationCredentialName == "" {
err := gophercloud.ErrMissingEnvironmentVariable{
+ // silently ignore TOTP passcode warning, since it is not a common auth method
EnvironmentVariable: "OS_PASSWORD",
}
return nilOptions, err
@@ -112,6 +114,7 @@ func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
UserID: userID,
Username: username,
Password: password,
+ Passcode: passcode,
TenantID: tenantID,
TenantName: tenantName,
DomainID: domainID,
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/client.go b/vendor/github.com/gophercloud/gophercloud/openstack/client.go
index 50f239711..655a9f6b9 100644
--- a/vendor/github.com/gophercloud/gophercloud/openstack/client.go
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/client.go
@@ -3,9 +3,12 @@ package openstack
import (
"fmt"
"reflect"
+ "strings"
"github.com/gophercloud/gophercloud"
tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
+ "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens"
+ "github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1"
tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
"github.com/gophercloud/gophercloud/openstack/utils"
)
@@ -67,7 +70,7 @@ Example:
ao, err := openstack.AuthOptionsFromEnv()
provider, err := openstack.AuthenticatedClient(ao)
- client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{
+ client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
*/
@@ -187,16 +190,61 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au
v3Client.Endpoint = endpoint
}
- result := tokens3.Create(v3Client, opts)
-
- err = client.SetTokenAndAuthResult(result)
- if err != nil {
- return err
+ var catalog *tokens3.ServiceCatalog
+
+ var tokenID string
+ // passthroughToken allows to passthrough the token without a scope
+ var passthroughToken bool
+ switch v := opts.(type) {
+ case *gophercloud.AuthOptions:
+ tokenID = v.TokenID
+ passthroughToken = (v.Scope == nil || *v.Scope == gophercloud.AuthScope{})
+ case *tokens3.AuthOptions:
+ tokenID = v.TokenID
+ passthroughToken = (v.Scope == tokens3.Scope{})
}
- catalog, err := result.ExtractServiceCatalog()
- if err != nil {
- return err
+ if tokenID != "" && passthroughToken {
+ // passing through the token ID without requesting a new scope
+ if opts.CanReauth() {
+ return fmt.Errorf("cannot use AllowReauth, when the token ID is defined and auth scope is not set")
+ }
+
+ v3Client.SetToken(tokenID)
+ result := tokens3.Get(v3Client, tokenID)
+ if result.Err != nil {
+ return result.Err
+ }
+
+ err = client.SetTokenAndAuthResult(result)
+ if err != nil {
+ return err
+ }
+
+ catalog, err = result.ExtractServiceCatalog()
+ if err != nil {
+ return err
+ }
+ } else {
+ var result tokens3.CreateResult
+ switch opts.(type) {
+ case *ec2tokens.AuthOptions:
+ result = ec2tokens.Create(v3Client, opts)
+ case *oauth1.AuthOptions:
+ result = oauth1.Create(v3Client, opts)
+ default:
+ result = tokens3.Create(v3Client, opts)
+ }
+
+ err = client.SetTokenAndAuthResult(result)
+ if err != nil {
+ return err
+ }
+
+ catalog, err = result.ExtractServiceCatalog()
+ if err != nil {
+ return err
+ }
}
if opts.CanReauth() {
@@ -217,6 +265,14 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.Au
o := *ot
o.AllowReauth = false
tao = &o
+ case *ec2tokens.AuthOptions:
+ o := *ot
+ o.AllowReauth = false
+ tao = &o
+ case *oauth1.AuthOptions:
+ o := *ot
+ o.AllowReauth = false
+ tao = &o
default:
tao = opts
}
@@ -395,7 +451,11 @@ func NewImageServiceV2(client *gophercloud.ProviderClient, eo gophercloud.Endpoi
// load balancer service.
func NewLoadBalancerV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
sc, err := initClientOpts(client, eo, "load-balancer")
- sc.ResourceBase = sc.Endpoint + "v2.0/"
+
+ // Fixes edge case having an OpenStack lb endpoint with trailing version number.
+ endpoint := strings.Replace(sc.Endpoint, "v2.0/", "", -1)
+
+ sc.ResourceBase = endpoint + "v2.0/"
return sc, err
}
@@ -436,3 +496,8 @@ func NewContainerInfraV1(client *gophercloud.ProviderClient, eo gophercloud.Endp
func NewWorkflowV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
return initClientOpts(client, eo, "workflowv2")
}
+
+// NewPlacementV1 creates a ServiceClient that may be used with the placement package.
+func NewPlacementV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
+ return initClientOpts(client, eo, "placement")
+}
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/doc.go
index cedf1f4d3..af4bd512b 100644
--- a/vendor/github.com/gophercloud/gophercloud/openstack/doc.go
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/doc.go
@@ -7,7 +7,7 @@ Example of Creating a Service Client
ao, err := openstack.AuthOptionsFromEnv()
provider, err := openstack.AuthenticatedClient(ao)
- client, err := openstack.NewNetworkV2(client, gophercloud.EndpointOpts{
+ client, err := openstack.NewNetworkV2(provider, gophercloud.EndpointOpts{
Region: os.Getenv("OS_REGION_NAME"),
})
*/
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go b/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go
index 12c8aebcf..509700790 100644
--- a/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/endpoint_location.go
@@ -29,11 +29,12 @@ func V2EndpointURL(catalog *tokens2.ServiceCatalog, opts gophercloud.EndpointOpt
}
}
- // Report an error if the options were ambiguous.
+ // If multiple endpoints were found, use the first result
+ // and disregard the other endpoints.
+ //
+ // This behavior matches the Python library. See GH-1764.
if len(endpoints) > 1 {
- err := &ErrMultipleMatchingEndpointsV2{}
- err.Endpoints = endpoints
- return "", err
+ endpoints = endpoints[0:1]
}
// Extract the appropriate URL from the matching Endpoint.
@@ -91,9 +92,12 @@ func V3EndpointURL(catalog *tokens3.ServiceCatalog, opts gophercloud.EndpointOpt
}
}
- // Report an error if the options were ambiguous.
+ // If multiple endpoints were found, use the first result
+ // and disregard the other endpoints.
+ //
+ // This behavior matches the Python library. See GH-1764.
if len(endpoints) > 1 {
- return "", ErrMultipleMatchingEndpointsV3{Endpoints: endpoints}
+ endpoints = endpoints[0:1]
}
// Extract the URL from the matching Endpoint.
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/errors.go b/vendor/github.com/gophercloud/gophercloud/openstack/errors.go
index df410b1c6..cba6ae5f0 100644
--- a/vendor/github.com/gophercloud/gophercloud/openstack/errors.go
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/errors.go
@@ -4,8 +4,6 @@ import (
"fmt"
"github.com/gophercloud/gophercloud"
- tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
- tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
)
// ErrEndpointNotFound is the error when no suitable endpoint can be found
@@ -24,28 +22,6 @@ func (e ErrInvalidAvailabilityProvided) Error() string {
return fmt.Sprintf("Unexpected availability in endpoint query: %s", e.Value)
}
-// ErrMultipleMatchingEndpointsV2 is the error when more than one endpoint
-// for the given options is found in the v2 catalog
-type ErrMultipleMatchingEndpointsV2 struct {
- gophercloud.BaseError
- Endpoints []tokens2.Endpoint
-}
-
-func (e ErrMultipleMatchingEndpointsV2) Error() string {
- return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints)
-}
-
-// ErrMultipleMatchingEndpointsV3 is the error when more than one endpoint
-// for the given options is found in the v3 catalog
-type ErrMultipleMatchingEndpointsV3 struct {
- gophercloud.BaseError
- Endpoints []tokens3.Endpoint
-}
-
-func (e ErrMultipleMatchingEndpointsV3) Error() string {
- return fmt.Sprintf("Discovered %d matching endpoints: %#v", len(e.Endpoints), e.Endpoints)
-}
-
// ErrNoAuthURL is the error when the OS_AUTH_URL environment variable is not
// found
type ErrNoAuthURL struct{ gophercloud.ErrInvalidInput }
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go
index f21a58f10..f16df38e5 100644
--- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tenants/requests.go
@@ -60,15 +60,17 @@ func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r Create
r.Err = err
return
}
- _, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
+ resp, err := client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200, 201},
})
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}
// Get requests details on a single tenant by ID.
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
- _, r.Err = client.Get(getURL(client, id), &r.Body, nil)
+ resp, err := client.Get(getURL(client, id), &r.Body, nil)
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}
@@ -103,14 +105,16 @@ func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder
r.Err = err
return
}
- _, r.Err = client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{
+ resp, err := client.Put(updateURL(client, id), &b, &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}
// Delete is the operation responsible for permanently deleting a tenant.
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
- _, r.Err = client.Delete(deleteURL(client, id), nil)
+ resp, err := client.Delete(deleteURL(client, id), nil)
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go
index ab32368cc..2b64f108c 100644
--- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v2/tokens/requests.go
@@ -87,17 +87,19 @@ func Create(client *gophercloud.ServiceClient, auth AuthOptionsBuilder) (r Creat
r.Err = err
return
}
- _, r.Err = client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{
+ resp, err := client.Post(CreateURL(client), b, &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200, 203},
MoreHeaders: map[string]string{"X-Auth-Token": ""},
})
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}
// Get validates and retrieves information for user's token.
func Get(client *gophercloud.ServiceClient, token string) (r GetResult) {
- _, r.Err = client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{
+ resp, err := client.Get(GetURL(client, token), &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200, 203},
})
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/doc.go
new file mode 100644
index 000000000..1f6f807fe
--- /dev/null
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/doc.go
@@ -0,0 +1,41 @@
+/*
+Package tokens provides information and interaction with the EC2 token API
+resource for the OpenStack Identity service.
+
+For more information, see:
+https://docs.openstack.org/api-ref/identity/v2-ext/
+
+Example to Create a Token From an EC2 access and secret keys
+
+ var authOptions tokens.AuthOptionsBuilder
+ authOptions = &ec2tokens.AuthOptions{
+ Access: "a7f1e798b7c2417cba4a02de97dc3cdc",
+ Secret: "18f4f6761ada4e3795fa5273c30349b9",
+ }
+
+ token, err := ec2tokens.Create(identityClient, authOptions).ExtractToken()
+ if err != nil {
+ panic(err)
+ }
+
+Example to auth a client using EC2 access and secret keys
+
+ client, err := openstack.NewClient("http://localhost:5000/v3")
+ if err != nil {
+ panic(err)
+ }
+
+ var authOptions tokens.AuthOptionsBuilder
+ authOptions = &ec2tokens.AuthOptions{
+ Access: "a7f1e798b7c2417cba4a02de97dc3cdc",
+ Secret: "18f4f6761ada4e3795fa5273c30349b9",
+ AllowReauth: true,
+ }
+
+ err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{})
+ if err != nil {
+ panic(err)
+ }
+
+*/
+package ec2tokens
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/requests.go
new file mode 100644
index 000000000..32ba0e621
--- /dev/null
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/requests.go
@@ -0,0 +1,377 @@
+package ec2tokens
+
+import (
+ "crypto/hmac"
+ "crypto/sha1"
+ "crypto/sha256"
+ "encoding/hex"
+ "fmt"
+ "math/rand"
+ "net/url"
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/gophercloud/gophercloud"
+ "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
+)
+
+const (
+ // EC2CredentialsAwsRequestV4 is a constant, used to generate AWS
+ // Credential V4.
+ EC2CredentialsAwsRequestV4 = "aws4_request"
+ // EC2CredentialsHmacSha1V2 is a HMAC SHA1 signature method. Used to
+ // generate AWS Credential V2.
+ EC2CredentialsHmacSha1V2 = "HmacSHA1"
+ // EC2CredentialsHmacSha256V2 is a HMAC SHA256 signature method. Used
+ // to generate AWS Credential V2.
+ EC2CredentialsHmacSha256V2 = "HmacSHA256"
+ // EC2CredentialsAwsHmacV4 is an AWS signature V4 signing method.
+ // More details:
+ // https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
+ EC2CredentialsAwsHmacV4 = "AWS4-HMAC-SHA256"
+ // EC2CredentialsTimestampFormatV4 is an AWS signature V4 timestamp
+ // format.
+ EC2CredentialsTimestampFormatV4 = "20060102T150405Z"
+ // EC2CredentialsDateFormatV4 is an AWS signature V4 date format.
+ EC2CredentialsDateFormatV4 = "20060102"
+)
+
+// AuthOptions represents options for authenticating a user using EC2 credentials.
+type AuthOptions struct {
+ // Access is the EC2 Credential Access ID.
+ Access string `json:"access" required:"true"`
+ // Secret is the EC2 Credential Secret, used to calculate signature.
+ // Not used, when a Signature is is.
+ Secret string `json:"-"`
+ // Host is a HTTP request Host header. Used to calculate an AWS
+ // signature V2. For signature V4 set the Host inside Headers map.
+ // Optional.
+ Host string `json:"host"`
+ // Path is a HTTP request path. Optional.
+ Path string `json:"path"`
+ // Verb is a HTTP request method. Optional.
+ Verb string `json:"verb"`
+ // Headers is a map of HTTP request headers. Optional.
+ Headers map[string]string `json:"headers"`
+ // Region is a region name to calculate an AWS signature V4. Optional.
+ Region string `json:"-"`
+ // Service is a service name to calculate an AWS signature V4. Optional.
+ Service string `json:"-"`
+ // Params is a map of GET method parameters. Optional.
+ Params map[string]string `json:"params"`
+ // AllowReauth allows Gophercloud to re-authenticate automatically
+ // if/when your token expires.
+ AllowReauth bool `json:"-"`
+ // Signature can be either a []byte (encoded to base64 automatically) or
+ // a string. You can set the singature explicitly, when you already know
+ // it. In this case default Params won't be automatically set. Optional.
+ Signature interface{} `json:"signature"`
+ // BodyHash is a HTTP request body sha256 hash. When nil and Signature
+ // is not set, a random hash is generated. Optional.
+ BodyHash *string `json:"body_hash"`
+ // Timestamp is a timestamp to calculate a V4 signature. Optional.
+ Timestamp *time.Time `json:"-"`
+ // Token is a []byte string (encoded to base64 automatically) which was
+ // signed by an EC2 secret key. Used by S3 tokens for validation only.
+ // Token must be set with a Signature. If a Signature is not provided,
+ // a Token will be generated automatically along with a Signature.
+ Token []byte `json:"token,omitempty"`
+}
+
+// EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string
+// for an AWS signature V2.
+// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L133
+func EC2CredentialsBuildCanonicalQueryStringV2(params map[string]string) string {
+ var keys []string
+ for k := range params {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ var pairs []string
+ for _, k := range keys {
+ pairs = append(pairs, fmt.Sprintf("%s=%s", k, url.QueryEscape(params[k])))
+ }
+
+ return strings.Join(pairs, "&")
+}
+
+// EC2CredentialsBuildStringToSignV2 builds a string to sign an AWS signature
+// V2.
+// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L148
+func EC2CredentialsBuildStringToSignV2(opts AuthOptions) []byte {
+ stringToSign := strings.Join([]string{
+ opts.Verb,
+ opts.Host,
+ opts.Path,
+ }, "\n")
+
+ return []byte(strings.Join([]string{
+ stringToSign,
+ EC2CredentialsBuildCanonicalQueryStringV2(opts.Params),
+ }, "\n"))
+}
+
+// EC2CredentialsBuildCanonicalQueryStringV2 builds a canonical query string
+// for an AWS signature V4.
+// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L244
+func EC2CredentialsBuildCanonicalQueryStringV4(verb string, params map[string]string) string {
+ if verb == "POST" {
+ return ""
+ }
+ return EC2CredentialsBuildCanonicalQueryStringV2(params)
+}
+
+// EC2CredentialsBuildCanonicalHeadersV4 builds a canonical string based on
+// "headers" map and "signedHeaders" string parameters.
+// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L216
+func EC2CredentialsBuildCanonicalHeadersV4(headers map[string]string, signedHeaders string) string {
+ headersLower := make(map[string]string, len(headers))
+ for k, v := range headers {
+ headersLower[strings.ToLower(k)] = v
+ }
+
+ var headersList []string
+ for _, h := range strings.Split(signedHeaders, ";") {
+ if v, ok := headersLower[h]; ok {
+ headersList = append(headersList, h+":"+v)
+ }
+ }
+
+ return strings.Join(headersList, "\n") + "\n"
+}
+
+// EC2CredentialsBuildSignatureKeyV4 builds a HMAC 256 signature key based on
+// input parameters.
+// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L169
+func EC2CredentialsBuildSignatureKeyV4(secret, region, service string, date time.Time) []byte {
+ kDate := sumHMAC256([]byte("AWS4"+secret), []byte(date.Format(EC2CredentialsDateFormatV4)))
+ kRegion := sumHMAC256(kDate, []byte(region))
+ kService := sumHMAC256(kRegion, []byte(service))
+ return sumHMAC256(kService, []byte(EC2CredentialsAwsRequestV4))
+}
+
+// EC2CredentialsBuildStringToSignV4 builds an AWS v4 signature string to sign
+// based on input parameters.
+// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L251
+func EC2CredentialsBuildStringToSignV4(opts AuthOptions, signedHeaders string, bodyHash string, date time.Time) []byte {
+ scope := strings.Join([]string{
+ date.Format(EC2CredentialsDateFormatV4),
+ opts.Region,
+ opts.Service,
+ EC2CredentialsAwsRequestV4,
+ }, "/")
+
+ canonicalRequest := strings.Join([]string{
+ opts.Verb,
+ opts.Path,
+ EC2CredentialsBuildCanonicalQueryStringV4(opts.Verb, opts.Params),
+ EC2CredentialsBuildCanonicalHeadersV4(opts.Headers, signedHeaders),
+ signedHeaders,
+ bodyHash,
+ }, "\n")
+ hash := sha256.Sum256([]byte(canonicalRequest))
+
+ return []byte(strings.Join([]string{
+ EC2CredentialsAwsHmacV4,
+ date.Format(EC2CredentialsTimestampFormatV4),
+ scope,
+ hex.EncodeToString(hash[:]),
+ }, "\n"))
+}
+
+// EC2CredentialsBuildSignatureV4 builds an AWS v4 signature based on input
+// parameters.
+// https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L285..L286
+func EC2CredentialsBuildSignatureV4(key []byte, stringToSign []byte) string {
+ return hex.EncodeToString(sumHMAC256(key, stringToSign))
+}
+
+// EC2CredentialsBuildAuthorizationHeaderV4 builds an AWS v4 Authorization
+// header based on auth parameters, date and signature
+func EC2CredentialsBuildAuthorizationHeaderV4(opts AuthOptions, signedHeaders string, signature string, date time.Time) string {
+ return fmt.Sprintf("%s Credential=%s/%s/%s/%s/%s, SignedHeaders=%s, Signature=%s",
+ EC2CredentialsAwsHmacV4,
+ opts.Access,
+ date.Format(EC2CredentialsDateFormatV4),
+ opts.Region,
+ opts.Service,
+ EC2CredentialsAwsRequestV4,
+ signedHeaders,
+ signature)
+}
+
+// ToTokenV3ScopeMap is a dummy method to satisfy tokens.AuthOptionsBuilder
+// interface.
+func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
+ return nil, nil
+}
+
+// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder
+// interface in the v3 tokens package.
+func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) {
+ return nil, nil
+}
+
+// CanReauth is a method method to satisfy tokens.AuthOptionsBuilder interface
+func (opts *AuthOptions) CanReauth() bool {
+ return opts.AllowReauth
+}
+
+// ToTokenV3CreateMap formats an AuthOptions into a create request.
+func (opts *AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) {
+ b, err := gophercloud.BuildRequestBody(opts, "credentials")
+ if err != nil {
+ return nil, err
+ }
+
+ if opts.Signature != nil {
+ return b, nil
+ }
+
+ // calculate signature, when it is not set
+ c, _ := b["credentials"].(map[string]interface{})
+ h := interfaceToMap(c, "headers")
+ p := interfaceToMap(c, "params")
+
+ // detect and process a signature v2
+ if v, ok := p["SignatureVersion"]; ok && v == "2" {
+ if _, ok := c["body_hash"]; ok {
+ delete(c, "body_hash")
+ }
+ if _, ok := c["headers"]; ok {
+ delete(c, "headers")
+ }
+ if v, ok := p["SignatureMethod"]; ok {
+ // params is a map of strings
+ strToSign := EC2CredentialsBuildStringToSignV2(*opts)
+ switch v {
+ case EC2CredentialsHmacSha1V2:
+ // keystone uses this method only when HmacSHA256 is not available on the server side
+ // https://github.com/openstack/python-keystoneclient/blob/stable/train/keystoneclient/contrib/ec2/utils.py#L151..L156
+ c["signature"] = sumHMAC1([]byte(opts.Secret), strToSign)
+ return b, nil
+ case EC2CredentialsHmacSha256V2:
+ c["signature"] = sumHMAC256([]byte(opts.Secret), strToSign)
+ return b, nil
+ }
+ return nil, fmt.Errorf("unsupported signature method: %s", v)
+ }
+ return nil, fmt.Errorf("signature method must be provided")
+ } else if ok {
+ return nil, fmt.Errorf("unsupported signature version: %s", v)
+ }
+
+ // it is not a signature v2, but a signature v4
+ date := time.Now().UTC()
+ if opts.Timestamp != nil {
+ date = *opts.Timestamp
+ }
+ if v, _ := c["body_hash"]; v == nil {
+ // when body_hash is not set, generate a random one
+ c["body_hash"] = randomBodyHash()
+ }
+
+ signedHeaders, _ := h["X-Amz-SignedHeaders"]
+
+ stringToSign := EC2CredentialsBuildStringToSignV4(*opts, signedHeaders, c["body_hash"].(string), date)
+ key := EC2CredentialsBuildSignatureKeyV4(opts.Secret, opts.Region, opts.Service, date)
+ c["signature"] = EC2CredentialsBuildSignatureV4(key, stringToSign)
+ h["X-Amz-Date"] = date.Format(EC2CredentialsTimestampFormatV4)
+ h["Authorization"] = EC2CredentialsBuildAuthorizationHeaderV4(*opts, signedHeaders, c["signature"].(string), date)
+
+ // token is only used for S3 tokens validation and will be removed when using EC2 validation
+ c["token"] = stringToSign
+
+ return b, nil
+}
+
+// Create authenticates and either generates a new token from EC2 credentials
+func Create(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) {
+ b, err := opts.ToTokenV3CreateMap(nil)
+ if err != nil {
+ r.Err = err
+ return
+ }
+
+ // delete "token" element, since it is used in s3tokens
+ deleteBodyElements(b, "token")
+
+ resp, err := c.Post(ec2tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{
+ MoreHeaders: map[string]string{"X-Auth-Token": ""},
+ OkCodes: []int{200},
+ })
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+ return
+}
+
+// ValidateS3Token authenticates an S3 request using EC2 credentials. Doesn't
+// generate a new token ID, but returns a tokens.CreateResult.
+func ValidateS3Token(c *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) {
+ b, err := opts.ToTokenV3CreateMap(nil)
+ if err != nil {
+ r.Err = err
+ return
+ }
+
+ // delete unused element, since it is used in ec2tokens only
+ deleteBodyElements(b, "body_hash", "headers", "host", "params", "path", "verb")
+
+ resp, err := c.Post(s3tokensURL(c), b, &r.Body, &gophercloud.RequestOpts{
+ MoreHeaders: map[string]string{"X-Auth-Token": ""},
+ OkCodes: []int{200},
+ })
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+ return
+}
+
+// The following are small helper functions used to help build the signature.
+
+// sumHMAC1 is a func to implement the HMAC SHA1 signature method.
+func sumHMAC1(key []byte, data []byte) []byte {
+ hash := hmac.New(sha1.New, key)
+ hash.Write(data)
+ return hash.Sum(nil)
+}
+
+// sumHMAC256 is a func to implement the HMAC SHA256 signature method.
+func sumHMAC256(key []byte, data []byte) []byte {
+ hash := hmac.New(sha256.New, key)
+ hash.Write(data)
+ return hash.Sum(nil)
+}
+
+// randomBodyHash is a func to generate a random sha256 hexdigest.
+func randomBodyHash() string {
+ h := make([]byte, 64)
+ rand.Read(h)
+ return hex.EncodeToString(h)
+}
+
+// interfaceToMap is a func used to represent a "credentials" map element as a
+// "map[string]string"
+func interfaceToMap(c map[string]interface{}, key string) map[string]string {
+ // convert map[string]interface{} to map[string]string
+ m := make(map[string]string)
+ if v, _ := c[key].(map[string]interface{}); v != nil {
+ for k, v := range v {
+ m[k] = v.(string)
+ }
+ }
+
+ c[key] = m
+
+ return m
+}
+
+// deleteBodyElements deletes map body elements
+func deleteBodyElements(b map[string]interface{}, elements ...string) {
+ if c, ok := b["credentials"].(map[string]interface{}); ok {
+ for _, k := range elements {
+ if _, ok := c[k]; ok {
+ delete(c, k)
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/urls.go
new file mode 100644
index 000000000..84b33b282
--- /dev/null
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens/urls.go
@@ -0,0 +1,11 @@
+package ec2tokens
+
+import "github.com/gophercloud/gophercloud"
+
+func ec2tokensURL(c *gophercloud.ServiceClient) string {
+ return c.ServiceURL("ec2tokens")
+}
+
+func s3tokensURL(c *gophercloud.ServiceClient) string {
+ return c.ServiceURL("s3tokens")
+}
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/doc.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/doc.go
new file mode 100644
index 000000000..c5b0831ca
--- /dev/null
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/doc.go
@@ -0,0 +1,123 @@
+/*
+Package oauth1 enables management of OpenStack OAuth1 tokens and Authentication.
+
+Example to Create an OAuth1 Consumer
+
+ createConsumerOpts := oauth1.CreateConsumerOpts{
+ Description: "My consumer",
+ }
+ consumer, err := oauth1.CreateConsumer(identityClient, createConsumerOpts).Extract()
+ if err != nil {
+ panic(err)
+ }
+
+ // NOTE: Consumer secret is available only on create response
+ fmt.Printf("Consumer: %+v\n", consumer)
+
+Example to Request an unauthorized OAuth1 token
+
+ requestTokenOpts := oauth1.RequestTokenOpts{
+ OAuthConsumerKey: consumer.ID,
+ OAuthConsumerSecret: consumer.Secret,
+ OAuthSignatureMethod: oauth1.HMACSHA1,
+ RequestedProjectID: projectID,
+ }
+ requestToken, err := oauth1.RequestToken(identityClient, requestTokenOpts).Extract()
+ if err != nil {
+ panic(err)
+ }
+
+ // NOTE: Request token secret is available only on request response
+ fmt.Printf("Request token: %+v\n", requestToken)
+
+Example to Authorize an unauthorized OAuth1 token
+
+ authorizeTokenOpts := oauth1.AuthorizeTokenOpts{
+ Roles: []oauth1.Role{
+ {Name: "member"},
+ },
+ }
+ authToken, err := oauth1.AuthorizeToken(identityClient, requestToken.OAuthToken, authorizeTokenOpts).Extract()
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("Verifier ID of the unauthorized Token: %+v\n", authToken.OAuthVerifier)
+
+Example to Create an OAuth1 Access Token
+
+ accessTokenOpts := oauth1.CreateAccessTokenOpts{
+ OAuthConsumerKey: consumer.ID,
+ OAuthConsumerSecret: consumer.Secret,
+ OAuthToken: requestToken.OAuthToken,
+ OAuthTokenSecret: requestToken.OAuthTokenSecret,
+ OAuthVerifier: authToken.OAuthVerifier,
+ OAuthSignatureMethod: oauth1.HMACSHA1,
+ }
+ accessToken, err := oauth1.CreateAccessToken(identityClient, accessTokenOpts).Extract()
+ if err != nil {
+ panic(err)
+ }
+
+ // NOTE: Access token secret is available only on create response
+ fmt.Printf("OAuth1 Access Token: %+v\n", accessToken)
+
+Example to List User's OAuth1 Access Tokens
+
+ allPages, err := oauth1.ListAccessTokens(identityClient, userID).AllPages()
+ if err != nil {
+ panic(err)
+ }
+ accessTokens, err := oauth1.ExtractAccessTokens(allPages)
+ if err != nil {
+ panic(err)
+ }
+
+ for _, accessToken := range accessTokens {
+ fmt.Printf("Access Token: %+v\n", accessToken)
+ }
+
+Example to Authenticate a client using OAuth1 method
+
+ client, err := openstack.NewClient("http://localhost:5000/v3")
+ if err != nil {
+ panic(err)
+ }
+
+ authOptions := &oauth1.AuthOptions{
+ // consumer token, created earlier
+ OAuthConsumerKey: consumer.ID,
+ OAuthConsumerSecret: consumer.Secret,
+ // access token, created earlier
+ OAuthToken: accessToken.OAuthToken,
+ OAuthTokenSecret: accessToken.OAuthTokenSecret,
+ OAuthSignatureMethod: oauth1.HMACSHA1,
+ }
+ err = openstack.AuthenticateV3(client, authOptions, gophercloud.EndpointOpts{})
+ if err != nil {
+ panic(err)
+ }
+
+Example to Create a Token using OAuth1 method
+
+ var oauth1Token struct {
+ tokens.Token
+ oauth1.TokenExt
+ }
+
+ createOpts := &oauth1.AuthOptions{
+ // consumer token, created earlier
+ OAuthConsumerKey: consumer.ID,
+ OAuthConsumerSecret: consumer.Secret,
+ // access token, created earlier
+ OAuthToken: accessToken.OAuthToken,
+ OAuthTokenSecret: accessToken.OAuthTokenSecret,
+ OAuthSignatureMethod: oauth1.HMACSHA1,
+ }
+ err := tokens.Create(identityClient, createOpts).ExtractInto(&oauth1Token)
+ if err != nil {
+ panic(err)
+ }
+
+*/
+package oauth1
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/requests.go
new file mode 100644
index 000000000..028b5a45b
--- /dev/null
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/requests.go
@@ -0,0 +1,587 @@
+package oauth1
+
+import (
+ "crypto/hmac"
+ "crypto/sha1"
+ "encoding/base64"
+ "fmt"
+ "io/ioutil"
+ "math/rand"
+ "net/url"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/gophercloud/gophercloud"
+ "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
+ "github.com/gophercloud/gophercloud/pagination"
+)
+
+// Type SignatureMethod is a OAuth1 SignatureMethod type.
+type SignatureMethod string
+
+const (
+ // HMACSHA1 is a recommended OAuth1 signature method.
+ HMACSHA1 SignatureMethod = "HMAC-SHA1"
+
+ // PLAINTEXT signature method is not recommended to be used in
+ // production environment.
+ PLAINTEXT SignatureMethod = "PLAINTEXT"
+
+ // OAuth1TokenContentType is a supported content type for an OAuth1
+ // token.
+ OAuth1TokenContentType = "application/x-www-form-urlencoded"
+)
+
+// AuthOptions represents options for authenticating a user using OAuth1 tokens.
+type AuthOptions struct {
+ // OAuthConsumerKey is the OAuth1 Consumer Key.
+ OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"`
+
+ // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate
+ // an OAuth1 request signature.
+ OAuthConsumerSecret string `required:"true"`
+
+ // OAuthToken is the OAuth1 Request Token.
+ OAuthToken string `q:"oauth_token" required:"true"`
+
+ // OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate
+ // an OAuth1 request signature.
+ OAuthTokenSecret string `required:"true"`
+
+ // OAuthSignatureMethod is the OAuth1 signature method the Consumer used
+ // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT".
+ // "PLAINTEXT" is not recommended for production usage.
+ OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"`
+
+ // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix
+ // timestamp will be used.
+ OAuthTimestamp *time.Time
+
+ // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string,
+ // uniquely generated for each request. Will be generated automatically
+ // when it is not set.
+ OAuthNonce string `q:"oauth_nonce"`
+
+ // AllowReauth allows Gophercloud to re-authenticate automatically
+ // if/when your token expires.
+ AllowReauth bool
+}
+
+// ToTokenV3HeadersMap builds the headers required for an OAuth1-based create
+// request.
+func (opts AuthOptions) ToTokenV3HeadersMap(headerOpts map[string]interface{}) (map[string]string, error) {
+ q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "")
+ if err != nil {
+ return nil, err
+ }
+
+ signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret}
+
+ method := headerOpts["method"].(string)
+ u := headerOpts["url"].(string)
+ stringToSign := buildStringToSign(method, u, q.Query())
+ signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys))
+
+ authHeader := buildAuthHeader(q.Query(), signature)
+
+ headers := map[string]string{
+ "Authorization": authHeader,
+ "X-Auth-Token": "",
+ }
+
+ return headers, nil
+}
+
+// ToTokenV3ScopeMap allows AuthOptions to satisfy the tokens.AuthOptionsBuilder
+// interface.
+func (opts AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
+ return nil, nil
+}
+
+// CanReauth allows AuthOptions to satisfy the tokens.AuthOptionsBuilder
+// interface.
+func (opts AuthOptions) CanReauth() bool {
+ return opts.AllowReauth
+}
+
+// ToTokenV3CreateMap builds a create request body.
+func (opts AuthOptions) ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error) {
+ // identityReq defines the "identity" portion of an OAuth1-based authentication
+ // create request body.
+ type identityReq struct {
+ Methods []string `json:"methods"`
+ OAuth1 struct{} `json:"oauth1"`
+ }
+
+ // authReq defines the "auth" portion of an OAuth1-based authentication
+ // create request body.
+ type authReq struct {
+ Identity identityReq `json:"identity"`
+ }
+
+ // oauth1Request defines how an OAuth1-based authentication create
+ // request body looks.
+ type oauth1Request struct {
+ Auth authReq `json:"auth"`
+ }
+
+ var req oauth1Request
+
+ req.Auth.Identity.Methods = []string{"oauth1"}
+ return gophercloud.BuildRequestBody(req, "")
+}
+
+// Create authenticates and either generates a new OpenStack token from an
+// OAuth1 token.
+func Create(client *gophercloud.ServiceClient, opts tokens.AuthOptionsBuilder) (r tokens.CreateResult) {
+ b, err := opts.ToTokenV3CreateMap(nil)
+ if err != nil {
+ r.Err = err
+ return
+ }
+
+ headerOpts := map[string]interface{}{
+ "method": "POST",
+ "url": authURL(client),
+ }
+
+ h, err := opts.ToTokenV3HeadersMap(headerOpts)
+ if err != nil {
+ r.Err = err
+ return
+ }
+
+ resp, err := client.Post(authURL(client), b, &r.Body, &gophercloud.RequestOpts{
+ MoreHeaders: h,
+ OkCodes: []int{201},
+ })
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+ return
+}
+
+// CreateConsumerOptsBuilder allows extensions to add additional parameters to
+// the CreateConsumer request.
+type CreateConsumerOptsBuilder interface {
+ ToOAuth1CreateConsumerMap() (map[string]interface{}, error)
+}
+
+// CreateConsumerOpts provides options used to create a new Consumer.
+type CreateConsumerOpts struct {
+ // Description is the consumer description.
+ Description string `json:"description"`
+}
+
+// ToOAuth1CreateConsumerMap formats a CreateConsumerOpts into a create request.
+func (opts CreateConsumerOpts) ToOAuth1CreateConsumerMap() (map[string]interface{}, error) {
+ return gophercloud.BuildRequestBody(opts, "consumer")
+}
+
+// Create creates a new Consumer.
+func CreateConsumer(client *gophercloud.ServiceClient, opts CreateConsumerOptsBuilder) (r CreateConsumerResult) {
+ b, err := opts.ToOAuth1CreateConsumerMap()
+ if err != nil {
+ r.Err = err
+ return
+ }
+ resp, err := client.Post(consumersURL(client), b, &r.Body, &gophercloud.RequestOpts{
+ OkCodes: []int{201},
+ })
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+ return
+}
+
+// Delete deletes a Consumer.
+func DeleteConsumer(client *gophercloud.ServiceClient, id string) (r DeleteConsumerResult) {
+ resp, err := client.Delete(consumerURL(client, id), nil)
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+ return
+}
+
+// List enumerates Consumers.
+func ListConsumers(client *gophercloud.ServiceClient) pagination.Pager {
+ return pagination.NewPager(client, consumersURL(client), func(r pagination.PageResult) pagination.Page {
+ return ConsumersPage{pagination.LinkedPageBase{PageResult: r}}
+ })
+}
+
+// GetConsumer retrieves details on a single Consumer by ID.
+func GetConsumer(client *gophercloud.ServiceClient, id string) (r GetConsumerResult) {
+ resp, err := client.Get(consumerURL(client, id), &r.Body, nil)
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+ return
+}
+
+// UpdateConsumerOpts provides options used to update a consumer.
+type UpdateConsumerOpts struct {
+ // Description is the consumer description.
+ Description string `json:"description"`
+}
+
+// ToOAuth1UpdateConsumerMap formats an UpdateConsumerOpts into a consumer update
+// request.
+func (opts UpdateConsumerOpts) ToOAuth1UpdateConsumerMap() (map[string]interface{}, error) {
+ return gophercloud.BuildRequestBody(opts, "consumer")
+}
+
+// UpdateConsumer updates an existing Consumer.
+func UpdateConsumer(client *gophercloud.ServiceClient, id string, opts UpdateConsumerOpts) (r UpdateConsumerResult) {
+ b, err := opts.ToOAuth1UpdateConsumerMap()
+ if err != nil {
+ r.Err = err
+ return
+ }
+ resp, err := client.Patch(consumerURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
+ OkCodes: []int{200},
+ })
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+ return
+}
+
+// RequestTokenOptsBuilder allows extensions to add additional parameters to the
+// RequestToken request.
+type RequestTokenOptsBuilder interface {
+ ToOAuth1RequestTokenHeaders(string, string) (map[string]string, error)
+}
+
+// RequestTokenOpts provides options used to get a consumer unauthorized
+// request token.
+type RequestTokenOpts struct {
+ // OAuthConsumerKey is the OAuth1 Consumer Key.
+ OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"`
+
+ // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate
+ // an OAuth1 request signature.
+ OAuthConsumerSecret string `required:"true"`
+
+ // OAuthSignatureMethod is the OAuth1 signature method the Consumer used
+ // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT".
+ // "PLAINTEXT" is not recommended for production usage.
+ OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"`
+
+ // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix
+ // timestamp will be used.
+ OAuthTimestamp *time.Time
+
+ // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string,
+ // uniquely generated for each request. Will be generated automatically
+ // when it is not set.
+ OAuthNonce string `q:"oauth_nonce"`
+
+ // RequestedProjectID is a Project ID a consumer user requested an
+ // access to.
+ RequestedProjectID string `h:"Requested-Project-Id"`
+}
+
+// ToOAuth1RequestTokenHeaders formats a RequestTokenOpts into a map of request
+// headers.
+func (opts RequestTokenOpts) ToOAuth1RequestTokenHeaders(method, u string) (map[string]string, error) {
+ q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "oob")
+ if err != nil {
+ return nil, err
+ }
+
+ h, err := gophercloud.BuildHeaders(opts)
+ if err != nil {
+ return nil, err
+ }
+
+ signatureKeys := []string{opts.OAuthConsumerSecret}
+ stringToSign := buildStringToSign(method, u, q.Query())
+ signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys))
+ authHeader := buildAuthHeader(q.Query(), signature)
+
+ h["Authorization"] = authHeader
+
+ return h, nil
+}
+
+// RequestToken requests an unauthorized OAuth1 Token.
+func RequestToken(client *gophercloud.ServiceClient, opts RequestTokenOptsBuilder) (r TokenResult) {
+ h, err := opts.ToOAuth1RequestTokenHeaders("POST", requestTokenURL(client))
+ if err != nil {
+ r.Err = err
+ return
+ }
+
+ resp, err := client.Post(requestTokenURL(client), nil, nil, &gophercloud.RequestOpts{
+ MoreHeaders: h,
+ OkCodes: []int{201},
+ KeepResponseBody: true,
+ })
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+ if r.Err != nil {
+ return
+ }
+ defer resp.Body.Close()
+ if v := r.Header.Get("Content-Type"); v != OAuth1TokenContentType {
+ r.Err = fmt.Errorf("unsupported Content-Type: %q", v)
+ return
+ }
+ r.Body, r.Err = ioutil.ReadAll(resp.Body)
+ return
+}
+
+// AuthorizeTokenOptsBuilder allows extensions to add additional parameters to
+// the AuthorizeToken request.
+type AuthorizeTokenOptsBuilder interface {
+ ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error)
+}
+
+// AuthorizeTokenOpts provides options used to authorize a request token.
+type AuthorizeTokenOpts struct {
+ Roles []Role `json:"roles"`
+}
+
+// Role is a struct representing a role object in a AuthorizeTokenOpts struct.
+type Role struct {
+ ID string `json:"id,omitempty"`
+ Name string `json:"name,omitempty"`
+}
+
+// ToOAuth1AuthorizeTokenMap formats an AuthorizeTokenOpts into an authorize token
+// request.
+func (opts AuthorizeTokenOpts) ToOAuth1AuthorizeTokenMap() (map[string]interface{}, error) {
+ for _, r := range opts.Roles {
+ if r == (Role{}) {
+ return nil, fmt.Errorf("role must not be empty")
+ }
+ }
+ return gophercloud.BuildRequestBody(opts, "")
+}
+
+// AuthorizeToken authorizes an unauthorized consumer token.
+func AuthorizeToken(client *gophercloud.ServiceClient, id string, opts AuthorizeTokenOptsBuilder) (r AuthorizeTokenResult) {
+ b, err := opts.ToOAuth1AuthorizeTokenMap()
+ if err != nil {
+ r.Err = err
+ return
+ }
+ resp, err := client.Put(authorizeTokenURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
+ OkCodes: []int{200},
+ })
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+ return
+}
+
+// CreateAccessTokenOptsBuilder allows extensions to add additional parameters
+// to the CreateAccessToken request.
+type CreateAccessTokenOptsBuilder interface {
+ ToOAuth1CreateAccessTokenHeaders(string, string) (map[string]string, error)
+}
+
+// CreateAccessTokenOpts provides options used to create an OAuth1 token.
+type CreateAccessTokenOpts struct {
+ // OAuthConsumerKey is the OAuth1 Consumer Key.
+ OAuthConsumerKey string `q:"oauth_consumer_key" required:"true"`
+
+ // OAuthConsumerSecret is the OAuth1 Consumer Secret. Used to generate
+ // an OAuth1 request signature.
+ OAuthConsumerSecret string `required:"true"`
+
+ // OAuthToken is the OAuth1 Request Token.
+ OAuthToken string `q:"oauth_token" required:"true"`
+
+ // OAuthTokenSecret is the OAuth1 Request Token Secret. Used to generate
+ // an OAuth1 request signature.
+ OAuthTokenSecret string `required:"true"`
+
+ // OAuthVerifier is the OAuth1 verification code.
+ OAuthVerifier string `q:"oauth_verifier" required:"true"`
+
+ // OAuthSignatureMethod is the OAuth1 signature method the Consumer used
+ // to sign the request. Supported values are "HMAC-SHA1" or "PLAINTEXT".
+ // "PLAINTEXT" is not recommended for production usage.
+ OAuthSignatureMethod SignatureMethod `q:"oauth_signature_method" required:"true"`
+
+ // OAuthTimestamp is an OAuth1 request timestamp. If nil, current Unix
+ // timestamp will be used.
+ OAuthTimestamp *time.Time
+
+ // OAuthNonce is an OAuth1 request nonce. Nonce must be a random string,
+ // uniquely generated for each request. Will be generated automatically
+ // when it is not set.
+ OAuthNonce string `q:"oauth_nonce"`
+}
+
+// ToOAuth1CreateAccessTokenHeaders formats a CreateAccessTokenOpts into a map of
+// request headers.
+func (opts CreateAccessTokenOpts) ToOAuth1CreateAccessTokenHeaders(method, u string) (map[string]string, error) {
+ q, err := buildOAuth1QueryString(opts, opts.OAuthTimestamp, "")
+ if err != nil {
+ return nil, err
+ }
+
+ signatureKeys := []string{opts.OAuthConsumerSecret, opts.OAuthTokenSecret}
+ stringToSign := buildStringToSign(method, u, q.Query())
+ signature := url.QueryEscape(signString(opts.OAuthSignatureMethod, stringToSign, signatureKeys))
+ authHeader := buildAuthHeader(q.Query(), signature)
+
+ headers := map[string]string{
+ "Authorization": authHeader,
+ }
+
+ return headers, nil
+}
+
+// CreateAccessToken creates a new OAuth1 Access Token
+func CreateAccessToken(client *gophercloud.ServiceClient, opts CreateAccessTokenOptsBuilder) (r TokenResult) {
+ h, err := opts.ToOAuth1CreateAccessTokenHeaders("POST", createAccessTokenURL(client))
+ if err != nil {
+ r.Err = err
+ return
+ }
+
+ resp, err := client.Post(createAccessTokenURL(client), nil, nil, &gophercloud.RequestOpts{
+ MoreHeaders: h,
+ OkCodes: []int{201},
+ KeepResponseBody: true,
+ })
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+ if r.Err != nil {
+ return
+ }
+ defer resp.Body.Close()
+ if v := r.Header.Get("Content-Type"); v != OAuth1TokenContentType {
+ r.Err = fmt.Errorf("unsupported Content-Type: %q", v)
+ return
+ }
+ r.Body, r.Err = ioutil.ReadAll(resp.Body)
+ return
+}
+
+// GetAccessToken retrieves details on a single OAuth1 access token by an ID.
+func GetAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r GetAccessTokenResult) {
+ resp, err := client.Get(userAccessTokenURL(client, userID, id), &r.Body, nil)
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+ return
+}
+
+// RevokeAccessToken revokes an OAuth1 access token.
+func RevokeAccessToken(client *gophercloud.ServiceClient, userID string, id string) (r RevokeAccessTokenResult) {
+ resp, err := client.Delete(userAccessTokenURL(client, userID, id), nil)
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+ return
+}
+
+// ListAccessTokens enumerates authorized access tokens.
+func ListAccessTokens(client *gophercloud.ServiceClient, userID string) pagination.Pager {
+ url := userAccessTokensURL(client, userID)
+ return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
+ return AccessTokensPage{pagination.LinkedPageBase{PageResult: r}}
+ })
+}
+
+// ListAccessTokenRoles enumerates authorized access token roles.
+func ListAccessTokenRoles(client *gophercloud.ServiceClient, userID string, id string) pagination.Pager {
+ url := userAccessTokenRolesURL(client, userID, id)
+ return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
+ return AccessTokenRolesPage{pagination.LinkedPageBase{PageResult: r}}
+ })
+}
+
+// GetAccessTokenRole retrieves details on a single OAuth1 access token role by
+// an ID.
+func GetAccessTokenRole(client *gophercloud.ServiceClient, userID string, id string, roleID string) (r GetAccessTokenRoleResult) {
+ resp, err := client.Get(userAccessTokenRoleURL(client, userID, id, roleID), &r.Body, nil)
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
+ return
+}
+
+// The following are small helper functions used to help build the signature.
+
+// buildOAuth1QueryString builds a URLEncoded parameters string specific for
+// OAuth1-based requests.
+func buildOAuth1QueryString(opts interface{}, timestamp *time.Time, callback string) (*url.URL, error) {
+ q, err := gophercloud.BuildQueryString(opts)
+ if err != nil {
+ return nil, err
+ }
+
+ query := q.Query()
+
+ if timestamp != nil {
+ // use provided timestamp
+ query.Set("oauth_timestamp", strconv.FormatInt(timestamp.Unix(), 10))
+ } else {
+ // use current timestamp
+ query.Set("oauth_timestamp", strconv.FormatInt(time.Now().UTC().Unix(), 10))
+ }
+
+ if query.Get("oauth_nonce") == "" {
+ // when nonce is not set, generate a random one
+ query.Set("oauth_nonce", strconv.FormatInt(rand.Int63(), 10)+query.Get("oauth_timestamp"))
+ }
+
+ if callback != "" {
+ query.Set("oauth_callback", callback)
+ }
+ query.Set("oauth_version", "1.0")
+
+ return &url.URL{RawQuery: query.Encode()}, nil
+}
+
+// buildStringToSign builds a string to be signed.
+func buildStringToSign(method string, u string, query url.Values) []byte {
+ parsedURL, _ := url.Parse(u)
+ p := parsedURL.Port()
+ s := parsedURL.Scheme
+
+ // Default scheme port must be stripped
+ if s == "http" && p == "80" || s == "https" && p == "443" {
+ parsedURL.Host = strings.TrimSuffix(parsedURL.Host, ":"+p)
+ }
+
+ // Ensure that URL doesn't contain queries
+ parsedURL.RawQuery = ""
+
+ v := strings.Join(
+ []string{method, url.QueryEscape(parsedURL.String()), url.QueryEscape(query.Encode())}, "&")
+
+ return []byte(v)
+}
+
+// signString signs a string using an OAuth1 signature method.
+func signString(signatureMethod SignatureMethod, strToSign []byte, signatureKeys []string) string {
+ var key []byte
+ for i, k := range signatureKeys {
+ key = append(key, []byte(url.QueryEscape(k))...)
+ if i == 0 {
+ key = append(key, '&')
+ }
+ }
+
+ var signedString string
+ switch signatureMethod {
+ case PLAINTEXT:
+ signedString = string(key)
+ default:
+ h := hmac.New(sha1.New, key)
+ h.Write(strToSign)
+ signedString = base64.StdEncoding.EncodeToString(h.Sum(nil))
+ }
+
+ return signedString
+}
+
+// buildAuthHeader generates an OAuth1 Authorization header with a signature
+// calculated using an OAuth1 signature method.
+func buildAuthHeader(query url.Values, signature string) string {
+ var authHeader []string
+ var keys []string
+ for k := range query {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
+ for _, k := range keys {
+ for _, v := range query[k] {
+ authHeader = append(authHeader, fmt.Sprintf("%s=%q", k, url.QueryEscape(v)))
+ }
+ }
+
+ authHeader = append(authHeader, fmt.Sprintf("oauth_signature=%q", signature))
+
+ return "OAuth " + strings.Join(authHeader, ", ")
+}
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/results.go
new file mode 100644
index 000000000..bb109e98e
--- /dev/null
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/results.go
@@ -0,0 +1,305 @@
+package oauth1
+
+import (
+ "encoding/json"
+ "net/url"
+ "time"
+
+ "github.com/gophercloud/gophercloud"
+ "github.com/gophercloud/gophercloud/pagination"
+)
+
+// Consumer represents a delegated authorization request between two
+// identities.
+type Consumer struct {
+ ID string `json:"id"`
+ Secret string `json:"secret"`
+ Description string `json:"description"`
+}
+
+type consumerResult struct {
+ gophercloud.Result
+}
+
+// CreateConsumerResult is the response from a Create operation. Call its
+// Extract method to interpret it as a Consumer.
+type CreateConsumerResult struct {
+ consumerResult
+}
+
+// UpdateConsumerResult is the response from a Create operation. Call its
+// Extract method to interpret it as a Consumer.
+type UpdateConsumerResult struct {
+ consumerResult
+}
+
+// DeleteConsumerResult is the response from a Delete operation. Call its
+// ExtractErr to determine if the request succeeded or failed.
+type DeleteConsumerResult struct {
+ gophercloud.ErrResult
+}
+
+// ConsumersPage is a single page of Region results.
+type ConsumersPage struct {
+ pagination.LinkedPageBase
+}
+
+// GetConsumerResult is the response from a Get operation. Call its Extract
+// method to interpret it as a Consumer.
+type GetConsumerResult struct {
+ consumerResult
+}
+
+// IsEmpty determines whether or not a page of Consumers contains any results.
+func (c ConsumersPage) IsEmpty() (bool, error) {
+ consumers, err := ExtractConsumers(c)
+ return len(consumers) == 0, err
+}
+
+// NextPageURL extracts the "next" link from the links section of the result.
+func (c ConsumersPage) NextPageURL() (string, error) {
+ var s struct {
+ Links struct {
+ Next string `json:"next"`
+ Previous string `json:"previous"`
+ } `json:"links"`
+ }
+ err := c.ExtractInto(&s)
+ if err != nil {
+ return "", err
+ }
+ return s.Links.Next, err
+}
+
+// ExtractConsumers returns a slice of Consumers contained in a single page of
+// results.
+func ExtractConsumers(r pagination.Page) ([]Consumer, error) {
+ var s struct {
+ Consumers []Consumer `json:"consumers"`
+ }
+ err := (r.(ConsumersPage)).ExtractInto(&s)
+ return s.Consumers, err
+}
+
+// Extract interprets any consumer result as a Consumer.
+func (c consumerResult) Extract() (*Consumer, error) {
+ var s struct {
+ Consumer *Consumer `json:"consumer"`
+ }
+ err := c.ExtractInto(&s)
+ return s.Consumer, err
+}
+
+// Token contains an OAuth1 token.
+type Token struct {
+ // OAuthToken is the key value for the oauth token that the Identity API returns.
+ OAuthToken string `q:"oauth_token"`
+ // OAuthTokenSecret is the secret value associated with the OAuth Token.
+ OAuthTokenSecret string `q:"oauth_token_secret"`
+ // OAUthExpiresAt is the date and time when an OAuth token expires.
+ OAUthExpiresAt *time.Time `q:"-"`
+}
+
+// TokenResult is a struct to handle
+// "Content-Type: application/x-www-form-urlencoded" response.
+type TokenResult struct {
+ gophercloud.Result
+ Body []byte
+}
+
+// Extract interprets any OAuth1 token result as a Token.
+func (r TokenResult) Extract() (*Token, error) {
+ if r.Err != nil {
+ return nil, r.Err
+ }
+
+ values, err := url.ParseQuery(string(r.Body))
+ if err != nil {
+ return nil, err
+ }
+
+ token := &Token{
+ OAuthToken: values.Get("oauth_token"),
+ OAuthTokenSecret: values.Get("oauth_token_secret"),
+ }
+
+ if v := values.Get("oauth_expires_at"); v != "" {
+ if t, err := time.Parse(gophercloud.RFC3339Milli, v); err != nil {
+ return nil, err
+ } else {
+ token.OAUthExpiresAt = &t
+ }
+ }
+
+ return token, nil
+}
+
+// AuthorizedToken contains an OAuth1 authorized token info.
+type AuthorizedToken struct {
+ // OAuthVerifier is the ID of the token verifier.
+ OAuthVerifier string `json:"oauth_verifier"`
+}
+
+type AuthorizeTokenResult struct {
+ gophercloud.Result
+}
+
+// Extract interprets AuthorizeTokenResult result as a AuthorizedToken.
+func (r AuthorizeTokenResult) Extract() (*AuthorizedToken, error) {
+ var s struct {
+ AuthorizedToken *AuthorizedToken `json:"token"`
+ }
+ err := r.ExtractInto(&s)
+ return s.AuthorizedToken, err
+}
+
+// AccessToken represents an AccessToken response as a struct.
+type AccessToken struct {
+ ID string `json:"id"`
+ ConsumerID string `json:"consumer_id"`
+ ProjectID string `json:"project_id"`
+ AuthorizingUserID string `json:"authorizing_user_id"`
+ ExpiresAt *time.Time `json:"-"`
+}
+
+func (r *AccessToken) UnmarshalJSON(b []byte) error {
+ type tmp AccessToken
+ var s struct {
+ tmp
+ ExpiresAt *gophercloud.JSONRFC3339Milli `json:"expires_at"`
+ }
+ err := json.Unmarshal(b, &s)
+ if err != nil {
+ return err
+ }
+ *r = AccessToken(s.tmp)
+
+ if s.ExpiresAt != nil {
+ t := time.Time(*s.ExpiresAt)
+ r.ExpiresAt = &t
+ }
+
+ return nil
+}
+
+type GetAccessTokenResult struct {
+ gophercloud.Result
+}
+
+// Extract interprets any GetAccessTokenResult result as an AccessToken.
+func (r GetAccessTokenResult) Extract() (*AccessToken, error) {
+ var s struct {
+ AccessToken *AccessToken `json:"access_token"`
+ }
+ err := r.ExtractInto(&s)
+ return s.AccessToken, err
+}
+
+// RevokeAccessTokenResult is the response from a Delete operation. Call its
+// ExtractErr to determine if the request succeeded or failed.
+type RevokeAccessTokenResult struct {
+ gophercloud.ErrResult
+}
+
+// AccessTokensPage is a single page of Access Tokens results.
+type AccessTokensPage struct {
+ pagination.LinkedPageBase
+}
+
+// IsEmpty determines whether or not a an AccessTokensPage contains any results.
+func (r AccessTokensPage) IsEmpty() (bool, error) {
+ accessTokens, err := ExtractAccessTokens(r)
+ return len(accessTokens) == 0, err
+}
+
+// NextPageURL extracts the "next" link from the links section of the result.
+func (r AccessTokensPage) NextPageURL() (string, error) {
+ var s struct {
+ Links struct {
+ Next string `json:"next"`
+ Previous string `json:"previous"`
+ } `json:"links"`
+ }
+ err := r.ExtractInto(&s)
+ if err != nil {
+ return "", err
+ }
+ return s.Links.Next, err
+}
+
+// ExtractAccessTokens returns a slice of AccessTokens contained in a single
+// page of results.
+func ExtractAccessTokens(r pagination.Page) ([]AccessToken, error) {
+ var s struct {
+ AccessTokens []AccessToken `json:"access_tokens"`
+ }
+ err := (r.(AccessTokensPage)).ExtractInto(&s)
+ return s.AccessTokens, err
+}
+
+// AccessTokenRole represents an Access Token Role struct.
+type AccessTokenRole struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ DomainID string `json:"domain_id"`
+}
+
+// AccessTokenRolesPage is a single page of Access Token roles results.
+type AccessTokenRolesPage struct {
+ pagination.LinkedPageBase
+}
+
+// IsEmpty determines whether or not a an AccessTokensPage contains any results.
+func (r AccessTokenRolesPage) IsEmpty() (bool, error) {
+ accessTokenRoles, err := ExtractAccessTokenRoles(r)
+ return len(accessTokenRoles) == 0, err
+}
+
+// NextPageURL extracts the "next" link from the links section of the result.
+func (r AccessTokenRolesPage) NextPageURL() (string, error) {
+ var s struct {
+ Links struct {
+ Next string `json:"next"`
+ Previous string `json:"previous"`
+ } `json:"links"`
+ }
+ err := r.ExtractInto(&s)
+ if err != nil {
+ return "", err
+ }
+ return s.Links.Next, err
+}
+
+// ExtractAccessTokenRoles returns a slice of AccessTokenRole contained in a
+// single page of results.
+func ExtractAccessTokenRoles(r pagination.Page) ([]AccessTokenRole, error) {
+ var s struct {
+ AccessTokenRoles []AccessTokenRole `json:"roles"`
+ }
+ err := (r.(AccessTokenRolesPage)).ExtractInto(&s)
+ return s.AccessTokenRoles, err
+}
+
+type GetAccessTokenRoleResult struct {
+ gophercloud.Result
+}
+
+// Extract interprets any GetAccessTokenRoleResult result as an AccessTokenRole.
+func (r GetAccessTokenRoleResult) Extract() (*AccessTokenRole, error) {
+ var s struct {
+ AccessTokenRole *AccessTokenRole `json:"role"`
+ }
+ err := r.ExtractInto(&s)
+ return s.AccessTokenRole, err
+}
+
+// OAuth1 is an OAuth1 object, returned in OAuth1 token result.
+type OAuth1 struct {
+ AccessTokenID string `json:"access_token_id"`
+ ConsumerID string `json:"consumer_id"`
+}
+
+// TokenExt represents an extension of the base token result.
+type TokenExt struct {
+ OAuth1 OAuth1 `json:"OS-OAUTH1"`
+}
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/urls.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/urls.go
new file mode 100644
index 000000000..9b51d53b3
--- /dev/null
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1/urls.go
@@ -0,0 +1,43 @@
+package oauth1
+
+import "github.com/gophercloud/gophercloud"
+
+func consumersURL(c *gophercloud.ServiceClient) string {
+ return c.ServiceURL("OS-OAUTH1", "consumers")
+}
+
+func consumerURL(c *gophercloud.ServiceClient, id string) string {
+ return c.ServiceURL("OS-OAUTH1", "consumers", id)
+}
+
+func requestTokenURL(c *gophercloud.ServiceClient) string {
+ return c.ServiceURL("OS-OAUTH1", "request_token")
+}
+
+func authorizeTokenURL(c *gophercloud.ServiceClient, id string) string {
+ return c.ServiceURL("OS-OAUTH1", "authorize", id)
+}
+
+func createAccessTokenURL(c *gophercloud.ServiceClient) string {
+ return c.ServiceURL("OS-OAUTH1", "access_token")
+}
+
+func userAccessTokensURL(c *gophercloud.ServiceClient, userID string) string {
+ return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens")
+}
+
+func userAccessTokenURL(c *gophercloud.ServiceClient, userID string, id string) string {
+ return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id)
+}
+
+func userAccessTokenRolesURL(c *gophercloud.ServiceClient, userID string, id string) string {
+ return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id, "roles")
+}
+
+func userAccessTokenRoleURL(c *gophercloud.ServiceClient, userID string, id string, roleID string) string {
+ return c.ServiceURL("users", userID, "OS-OAUTH1", "access_tokens", id, "roles", roleID)
+}
+
+func authURL(c *gophercloud.ServiceClient) string {
+ return c.ServiceURL("auth", "tokens")
+}
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go
index e4d766b23..d8c455d16 100644
--- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/requests.go
@@ -8,6 +8,7 @@ type Scope struct {
ProjectName string
DomainID string
DomainName string
+ System bool
}
// AuthOptionsBuilder provides the ability for extensions to add additional
@@ -16,6 +17,7 @@ type AuthOptionsBuilder interface {
// ToTokenV3CreateMap assembles the Create request body, returning an error
// if parameters are missing or inconsistent.
ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error)
+ ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error)
ToTokenV3ScopeMap() (map[string]interface{}, error)
CanReauth() bool
}
@@ -36,6 +38,9 @@ type AuthOptions struct {
Password string `json:"password,omitempty"`
+ // Passcode is used in TOTP authentication method
+ Passcode string `json:"passcode,omitempty"`
+
// At most one of DomainID and DomainName must be provided if using Username
// with Identity V3. Otherwise, either are optional.
DomainID string `json:"-"`
@@ -67,6 +72,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
Username: opts.Username,
UserID: opts.UserID,
Password: opts.Password,
+ Passcode: opts.Passcode,
DomainID: opts.DomainID,
DomainName: opts.DomainName,
AllowReauth: opts.AllowReauth,
@@ -79,7 +85,7 @@ func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[s
return gophercloudAuthOpts.ToTokenV3CreateMap(scope)
}
-// ToTokenV3CreateMap builds a scope request body from AuthOptions.
+// ToTokenV3ScopeMap builds a scope request body from AuthOptions.
func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
scope := gophercloud.AuthScope(opts.Scope)
@@ -93,10 +99,21 @@ func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
}
func (opts *AuthOptions) CanReauth() bool {
+ if opts.Passcode != "" {
+ // cannot reauth using TOTP passcode
+ return false
+ }
+
return opts.AllowReauth
}
-func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string {
+// ToTokenV3HeadersMap allows AuthOptions to satisfy the AuthOptionsBuilder
+// interface in the v3 tokens package.
+func (opts *AuthOptions) ToTokenV3HeadersMap(map[string]interface{}) (map[string]string, error) {
+ return nil, nil
+}
+
+func subjectTokenHeaders(subjectToken string) map[string]string {
return map[string]string{
"X-Subject-Token": subjectToken,
}
@@ -120,30 +137,24 @@ func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResu
resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{
MoreHeaders: map[string]string{"X-Auth-Token": ""},
})
- r.Err = err
- if resp != nil {
- r.Header = resp.Header
- }
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}
// Get validates and retrieves information about another token.
func Get(c *gophercloud.ServiceClient, token string) (r GetResult) {
resp, err := c.Get(tokenURL(c), &r.Body, &gophercloud.RequestOpts{
- MoreHeaders: subjectTokenHeaders(c, token),
+ MoreHeaders: subjectTokenHeaders(token),
OkCodes: []int{200, 203},
})
- if resp != nil {
- r.Header = resp.Header
- }
- r.Err = err
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}
// Validate determines if a specified token is valid or not.
func Validate(c *gophercloud.ServiceClient, token string) (bool, error) {
resp, err := c.Head(tokenURL(c), &gophercloud.RequestOpts{
- MoreHeaders: subjectTokenHeaders(c, token),
+ MoreHeaders: subjectTokenHeaders(token),
OkCodes: []int{200, 204, 404},
})
if err != nil {
@@ -155,8 +166,9 @@ func Validate(c *gophercloud.ServiceClient, token string) (bool, error) {
// Revoke immediately makes specified token invalid.
func Revoke(c *gophercloud.ServiceClient, token string) (r RevokeResult) {
- _, r.Err = c.Delete(tokenURL(c), &gophercloud.RequestOpts{
- MoreHeaders: subjectTokenHeaders(c, token),
+ resp, err := c.Delete(tokenURL(c), &gophercloud.RequestOpts{
+ MoreHeaders: subjectTokenHeaders(token),
})
+ _, r.Header, r.Err = gophercloud.ParseResponse(resp, err)
return
}
diff --git a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
index 8af4d634c..f1e17e9f7 100644
--- a/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
+++ b/vendor/github.com/gophercloud/gophercloud/openstack/identity/v3/tokens/results.go
@@ -109,6 +109,13 @@ func (r CreateResult) ExtractTokenID() (string, error) {
return r.Header.Get("X-Subject-Token"), r.Err
}
+// ExtractTokenID implements the gophercloud.AuthResult interface. The returned
+// string is the same as the ID field of the Token struct returned from
+// ExtractToken().
+func (r GetResult) ExtractTokenID() (string, error) {
+ return r.Header.Get("X-Subject-Token"), r.Err
+}
+
// ExtractServiceCatalog returns the ServiceCatalog that was generated along
// with the user's Token.
func (r commonResult) ExtractServiceCatalog() (*ServiceCatalog, error) {
diff --git a/vendor/github.com/gophercloud/gophercloud/pagination/http.go b/vendor/github.com/gophercloud/gophercloud/pagination/http.go
index 757295c42..df3503159 100644
--- a/vendor/github.com/gophercloud/gophercloud/pagination/http.go
+++ b/vendor/github.com/gophercloud/gophercloud/pagination/http.go
@@ -54,7 +54,8 @@ func PageResultFromParsed(resp *http.Response, body interface{}) PageResult {
// Request performs an HTTP request and extracts the http.Response from the result.
func Request(client *gophercloud.ServiceClient, headers map[string]string, url string) (*http.Response, error) {
return client.Get(url, nil, &gophercloud.RequestOpts{
- MoreHeaders: headers,
- OkCodes: []int{200, 204, 300},
+ MoreHeaders: headers,
+ OkCodes: []int{200, 204, 300},
+ KeepResponseBody: true,
})
}
diff --git a/vendor/github.com/gophercloud/gophercloud/params.go b/vendor/github.com/gophercloud/gophercloud/params.go
index b9986660c..219c020a2 100644
--- a/vendor/github.com/gophercloud/gophercloud/params.go
+++ b/vendor/github.com/gophercloud/gophercloud/params.go
@@ -450,6 +450,8 @@ func BuildHeaders(opts interface{}) (map[string]string, error) {
optsMap[tags[0]] = v.String()
case reflect.Int:
optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10)
+ case reflect.Int64:
+ optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10)
case reflect.Bool:
optsMap[tags[0]] = strconv.FormatBool(v.Bool())
}
diff --git a/vendor/github.com/gophercloud/gophercloud/provider_client.go b/vendor/github.com/gophercloud/gophercloud/provider_client.go
index 885bf07a7..43574402e 100644
--- a/vendor/github.com/gophercloud/gophercloud/provider_client.go
+++ b/vendor/github.com/gophercloud/gophercloud/provider_client.go
@@ -305,6 +305,9 @@ type RequestOpts struct {
// ErrorContext specifies the resource error type to return if an error is encountered.
// This lets resources override default error messages based on the response status code.
ErrorContext error
+ // KeepResponseBody specifies whether to keep the HTTP response body. Usually used, when the HTTP
+ // response body is considered for further use. Valid when JSONResponse is nil.
+ KeepResponseBody bool
}
// requestState contains temporary state for a single ProviderClient.Request() call.
@@ -346,6 +349,11 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts
contentType = &applicationJSON
}
+ // Return an error, when "KeepResponseBody" is true and "JSONResponse" is not nil
+ if options.KeepResponseBody && options.JSONResponse != nil {
+ return nil, errors.New("cannot use KeepResponseBody when JSONResponse is not nil")
+ }
+
if options.RawBody != nil {
body = options.RawBody
}
@@ -384,9 +392,6 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts
req.Header.Set(k, v)
}
- // Set connection parameter to close the connection immediately when we've got the response
- req.Close = true
-
prereqtok := req.Header.Get("X-Auth-Token")
// Issue the request.
@@ -414,11 +419,12 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts
body, _ := ioutil.ReadAll(resp.Body)
resp.Body.Close()
respErr := ErrUnexpectedResponseCode{
- URL: url,
- Method: method,
- Expected: options.OkCodes,
- Actual: resp.StatusCode,
- Body: body,
+ URL: url,
+ Method: method,
+ Expected: options.OkCodes,
+ Actual: resp.StatusCode,
+ Body: body,
+ ResponseHeader: resp.Header,
}
errType := options.ErrorContext
@@ -513,25 +519,40 @@ func (client *ProviderClient) doRequest(method, url string, options *RequestOpts
// Parse the response body as JSON, if requested to do so.
if options.JSONResponse != nil {
defer resp.Body.Close()
+ // Don't decode JSON when there is no content
+ if resp.StatusCode == http.StatusNoContent {
+ // read till EOF, otherwise the connection will be closed and cannot be reused
+ _, err = io.Copy(ioutil.Discard, resp.Body)
+ return resp, err
+ }
if err := json.NewDecoder(resp.Body).Decode(options.JSONResponse); err != nil {
return nil, err
}
}
+ // Close unused body to allow the HTTP connection to be reused
+ if !options.KeepResponseBody && options.JSONResponse == nil {
+ defer resp.Body.Close()
+ // read till EOF, otherwise the connection will be closed and cannot be reused
+ if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
+ return nil, err
+ }
+ }
+
return resp, nil
}
func defaultOkCodes(method string) []int {
- switch {
- case method == "GET":
+ switch method {
+ case "GET", "HEAD":
return []int{200}
- case method == "POST":
+ case "POST":
return []int{201, 202}
- case method == "PUT":
+ case "PUT":
return []int{201, 202}
- case method == "PATCH":
+ case "PATCH":
return []int{200, 202, 204}
- case method == "DELETE":
+ case "DELETE":
return []int{202, 204}
}
diff --git a/vendor/github.com/gophercloud/gophercloud/results.go b/vendor/github.com/gophercloud/gophercloud/results.go
index 94a16bff0..1b608103b 100644
--- a/vendor/github.com/gophercloud/gophercloud/results.go
+++ b/vendor/github.com/gophercloud/gophercloud/results.go
@@ -131,6 +131,18 @@ func (r Result) extractIntoPtr(to interface{}, label string) error {
// fields of the struct or composed extension struct
// at the end of this method.
toValue.Set(newSlice)
+
+ // jtopjian: This was put into place to resolve the issue
+ // described at
+ // https://github.com/gophercloud/gophercloud/issues/1963
+ //
+ // This probably isn't the best fix, but it appears to
+ // be resolving the issue, so I'm going to implement it
+ // for now.
+ //
+ // For future readers, this entire case statement could
+ // use a review.
+ return nil
}
}
case reflect.Struct:
diff --git a/vendor/github.com/gophercloud/gophercloud/service_client.go b/vendor/github.com/gophercloud/gophercloud/service_client.go
index f222f05a6..dd54abe30 100644
--- a/vendor/github.com/gophercloud/gophercloud/service_client.go
+++ b/vendor/github.com/gophercloud/gophercloud/service_client.go
@@ -152,3 +152,11 @@ func (client *ServiceClient) Request(method, url string, options *RequestOpts) (
}
return client.ProviderClient.Request(method, url, options)
}
+
+// ParseResponse is a helper function to parse http.Response to constituents.
+func ParseResponse(resp *http.Response, err error) (io.ReadCloser, http.Header, error) {
+ if resp != nil {
+ return resp.Body, resp.Header, err
+ }
+ return nil, nil, err
+}
diff --git a/vendor/github.com/hashicorp/golang-lru/lru.go b/vendor/github.com/hashicorp/golang-lru/lru.go
index 052a38b4c..4e5e9d8fd 100644
--- a/vendor/github.com/hashicorp/golang-lru/lru.go
+++ b/vendor/github.com/hashicorp/golang-lru/lru.go
@@ -37,7 +37,7 @@ func (c *Cache) Purge() {
c.lock.Unlock()
}
-// Add adds a value to the cache. Returns true if an eviction occurred.
+// Add adds a value to the cache. Returns true if an eviction occurred.
func (c *Cache) Add(key, value interface{}) (evicted bool) {
c.lock.Lock()
evicted = c.lru.Add(key, value)
@@ -71,8 +71,8 @@ func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) {
return value, ok
}
-// ContainsOrAdd checks if a key is in the cache without updating the
-// recent-ness or deleting it for being stale, and if not, adds the value.
+// ContainsOrAdd checks if a key is in the cache without updating the
+// recent-ness or deleting it for being stale, and if not, adds the value.
// Returns whether found and whether an eviction occurred.
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
c.lock.Lock()
@@ -85,6 +85,22 @@ func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
return false, evicted
}
+// PeekOrAdd checks if a key is in the cache without updating the
+// recent-ness or deleting it for being stale, and if not, adds the value.
+// Returns whether found and whether an eviction occurred.
+func (c *Cache) PeekOrAdd(key, value interface{}) (previous interface{}, ok, evicted bool) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ previous, ok = c.lru.Peek(key)
+ if ok {
+ return previous, true, false
+ }
+
+ evicted = c.lru.Add(key, value)
+ return nil, false, evicted
+}
+
// Remove removes the provided key from the cache.
func (c *Cache) Remove(key interface{}) (present bool) {
c.lock.Lock()
diff --git a/vendor/github.com/mailru/easyjson/jwriter/writer.go b/vendor/github.com/mailru/easyjson/jwriter/writer.go
index b9ed7ccaa..eb8547ccc 100644
--- a/vendor/github.com/mailru/easyjson/jwriter/writer.go
+++ b/vendor/github.com/mailru/easyjson/jwriter/writer.go
@@ -270,16 +270,25 @@ func (w *Writer) Bool(v bool) {
const chars = "0123456789abcdef"
-func isNotEscapedSingleChar(c byte, escapeHTML bool) bool {
- // Note: might make sense to use a table if there are more chars to escape. With 4 chars
- // it benchmarks the same.
- if escapeHTML {
- return c != '<' && c != '>' && c != '&' && c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf
- } else {
- return c != '\\' && c != '"' && c >= 0x20 && c < utf8.RuneSelf
+func getTable(falseValues ...int) [128]bool {
+ table := [128]bool{}
+
+ for i := 0; i < 128; i++ {
+ table[i] = true
+ }
+
+ for _, v := range falseValues {
+ table[v] = false
}
+
+ return table
}
+var (
+ htmlEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '&', '<', '>', '\\')
+ htmlNoEscapeTable = getTable(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, '"', '\\')
+)
+
func (w *Writer) String(s string) {
w.Buffer.AppendByte('"')
@@ -288,15 +297,23 @@ func (w *Writer) String(s string) {
p := 0 // last non-escape symbol
+ var escapeTable [128]bool
+ if w.NoEscapeHTML {
+ escapeTable = htmlNoEscapeTable
+ } else {
+ escapeTable = htmlEscapeTable
+ }
+
for i := 0; i < len(s); {
c := s[i]
- if isNotEscapedSingleChar(c, !w.NoEscapeHTML) {
- // single-width character, no escaping is required
- i++
- continue
- } else if c < utf8.RuneSelf {
- // single-with character, need to escape
+ if c < utf8.RuneSelf {
+ if escapeTable[c] {
+ // single-width character, no escaping is required
+ i++
+ continue
+ }
+
w.Buffer.AppendString(s[p:i])
switch c {
case '\t':
diff --git a/vendor/github.com/mitchellh/mapstructure/.travis.yml b/vendor/github.com/mitchellh/mapstructure/.travis.yml
index 1689c7d73..b122a8e3d 100644
--- a/vendor/github.com/mitchellh/mapstructure/.travis.yml
+++ b/vendor/github.com/mitchellh/mapstructure/.travis.yml
@@ -6,3 +6,4 @@ go:
script:
- go test
+ - go test -bench . -benchmem
diff --git a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md
index 3b3cb723f..0a21e2cd1 100644
--- a/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md
+++ b/vendor/github.com/mitchellh/mapstructure/CHANGELOG.md
@@ -1,3 +1,22 @@
+## 1.2.2
+
+* Do not add unsettable (unexported) values to the unused metadata key
+ or "remain" value. [GH-150]
+
+## 1.2.1
+
+* Go modules checksum mismatch fix
+
+## 1.2.0
+
+* Added support to capture unused values in a field using the `",remain"` value
+ in the mapstructure tag. There is an example to showcase usage.
+* Added `DecoderConfig` option to always squash embedded structs
+* `json.Number` can decode into `uint` types
+* Empty slices are preserved and not replaced with nil slices
+* Fix panic that can occur in when decoding a map into a nil slice of structs
+* Improved package documentation for godoc
+
## 1.1.2
* Fix error when decode hook decodes interface implementation into interface
diff --git a/vendor/github.com/mitchellh/mapstructure/go.mod b/vendor/github.com/mitchellh/mapstructure/go.mod
index d2a712562..a03ae9730 100644
--- a/vendor/github.com/mitchellh/mapstructure/go.mod
+++ b/vendor/github.com/mitchellh/mapstructure/go.mod
@@ -1 +1,3 @@
module github.com/mitchellh/mapstructure
+
+go 1.14
diff --git a/vendor/github.com/mitchellh/mapstructure/mapstructure.go b/vendor/github.com/mitchellh/mapstructure/mapstructure.go
index 256ee63fb..daea3318e 100644
--- a/vendor/github.com/mitchellh/mapstructure/mapstructure.go
+++ b/vendor/github.com/mitchellh/mapstructure/mapstructure.go
@@ -1,10 +1,109 @@
-// Package mapstructure exposes functionality to convert an arbitrary
-// map[string]interface{} into a native Go structure.
+// Package mapstructure exposes functionality to convert one arbitrary
+// Go type into another, typically to convert a map[string]interface{}
+// into a native Go structure.
//
// The Go structure can be arbitrarily complex, containing slices,
// other structs, etc. and the decoder will properly decode nested
// maps and so on into the proper structures in the native Go struct.
// See the examples to see what the decoder is capable of.
+//
+// The simplest function to start with is Decode.
+//
+// Field Tags
+//
+// When decoding to a struct, mapstructure will use the field name by
+// default to perform the mapping. For example, if a struct has a field
+// "Username" then mapstructure will look for a key in the source value
+// of "username" (case insensitive).
+//
+// type User struct {
+// Username string
+// }
+//
+// You can change the behavior of mapstructure by using struct tags.
+// The default struct tag that mapstructure looks for is "mapstructure"
+// but you can customize it using DecoderConfig.
+//
+// Renaming Fields
+//
+// To rename the key that mapstructure looks for, use the "mapstructure"
+// tag and set a value directly. For example, to change the "username" example
+// above to "user":
+//
+// type User struct {
+// Username string `mapstructure:"user"`
+// }
+//
+// Embedded Structs and Squashing
+//
+// Embedded structs are treated as if they're another field with that name.
+// By default, the two structs below are equivalent when decoding with
+// mapstructure:
+//
+// type Person struct {
+// Name string
+// }
+//
+// type Friend struct {
+// Person
+// }
+//
+// type Friend struct {
+// Person Person
+// }
+//
+// This would require an input that looks like below:
+//
+// map[string]interface{}{
+// "person": map[string]interface{}{"name": "alice"},
+// }
+//
+// If your "person" value is NOT nested, then you can append ",squash" to
+// your tag value and mapstructure will treat it as if the embedded struct
+// were part of the struct directly. Example:
+//
+// type Friend struct {
+// Person `mapstructure:",squash"`
+// }
+//
+// Now the following input would be accepted:
+//
+// map[string]interface{}{
+// "name": "alice",
+// }
+//
+// DecoderConfig has a field that changes the behavior of mapstructure
+// to always squash embedded structs.
+//
+// Remainder Values
+//
+// If there are any unmapped keys in the source value, mapstructure by
+// default will silently ignore them. You can error by setting ErrorUnused
+// in DecoderConfig. If you're using Metadata you can also maintain a slice
+// of the unused keys.
+//
+// You can also use the ",remain" suffix on your tag to collect all unused
+// values in a map. The field with this tag MUST be a map type and should
+// probably be a "map[string]interface{}" or "map[interface{}]interface{}".
+// See example below:
+//
+// type Friend struct {
+// Name string
+// Other map[string]interface{} `mapstructure:",remain"`
+// }
+//
+// Given the input below, Other would be populated with the other
+// values that weren't used (everything but "name"):
+//
+// map[string]interface{}{
+// "name": "bob",
+// "address": "123 Maple St.",
+// }
+//
+// Other Configuration
+//
+// mapstructure is highly configurable. See the DecoderConfig struct
+// for other features and options that are supported.
package mapstructure
import (
@@ -80,6 +179,14 @@ type DecoderConfig struct {
//
WeaklyTypedInput bool
+ // Squash will squash embedded structs. A squash tag may also be
+ // added to an individual struct field using a tag. For example:
+ //
+ // type Parent struct {
+ // Child `mapstructure:",squash"`
+ // }
+ Squash bool
+
// Metadata is the struct that will contain extra metadata about
// the decoding. If this is nil, then no metadata will be tracked.
Metadata *Metadata
@@ -438,6 +545,7 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er
func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error {
dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
+ dataType := dataVal.Type()
switch {
case dataKind == reflect.Int:
@@ -469,6 +577,18 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
} else {
return fmt.Errorf("cannot parse '%s' as uint: %s", name, err)
}
+ case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
+ jn := data.(json.Number)
+ i, err := jn.Int64()
+ if err != nil {
+ return fmt.Errorf(
+ "error decoding json.Number into %s: %s", name, err)
+ }
+ if i < 0 && !d.config.WeaklyTypedInput {
+ return fmt.Errorf("cannot parse '%s', %d overflows uint",
+ name, i)
+ }
+ val.SetUint(uint64(i))
default:
return fmt.Errorf(
"'%s' expected type '%s', got unconvertible type '%s'",
@@ -689,16 +809,19 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
keyName = tagParts[0]
}
+ // If Squash is set in the config, we squash the field down.
+ squash := d.config.Squash && v.Kind() == reflect.Struct
// If "squash" is specified in the tag, we squash the field down.
- squash := false
- for _, tag := range tagParts[1:] {
- if tag == "squash" {
- squash = true
- break
+ if !squash {
+ for _, tag := range tagParts[1:] {
+ if tag == "squash" {
+ squash = true
+ break
+ }
+ }
+ if squash && v.Kind() != reflect.Struct {
+ return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
}
- }
- if squash && v.Kind() != reflect.Struct {
- return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
}
switch v.Kind() {
@@ -805,8 +928,8 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
valElemType := valType.Elem()
sliceType := reflect.SliceOf(valElemType)
- valSlice := val
- if valSlice.IsNil() || d.config.ZeroFields {
+ // If we have a non array/slice type then we first attempt to convert.
+ if dataValKind != reflect.Array && dataValKind != reflect.Slice {
if d.config.WeaklyTypedInput {
switch {
// Slice and array we use the normal logic
@@ -833,18 +956,17 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
}
}
- // Check input type
- if dataValKind != reflect.Array && dataValKind != reflect.Slice {
- return fmt.Errorf(
- "'%s': source data must be an array or slice, got %s", name, dataValKind)
-
- }
+ return fmt.Errorf(
+ "'%s': source data must be an array or slice, got %s", name, dataValKind)
+ }
- // If the input value is empty, then don't allocate since non-nil != nil
- if dataVal.Len() == 0 {
- return nil
- }
+ // If the input value is nil, then don't allocate since empty != nil
+ if dataVal.IsNil() {
+ return nil
+ }
+ valSlice := val
+ if valSlice.IsNil() || d.config.ZeroFields {
// Make a new slice to hold our result, same size as the original data.
valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len())
}
@@ -1005,6 +1127,11 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
field reflect.StructField
val reflect.Value
}
+
+ // remainField is set to a valid field set with the "remain" tag if
+ // we are keeping track of remaining values.
+ var remainField *field
+
fields := []field{}
for len(structs) > 0 {
structVal := structs[0]
@@ -1017,13 +1144,21 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
fieldKind := fieldType.Type.Kind()
// If "squash" is specified in the tag, we squash the field down.
- squash := false
+ squash := d.config.Squash && fieldKind == reflect.Struct
+ remain := false
+
+ // We always parse the tags cause we're looking for other tags too
tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
for _, tag := range tagParts[1:] {
if tag == "squash" {
squash = true
break
}
+
+ if tag == "remain" {
+ remain = true
+ break
+ }
}
if squash {
@@ -1036,8 +1171,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
continue
}
- // Normal struct field, store it away
- fields = append(fields, field{fieldType, structVal.Field(i)})
+ // Build our field
+ fieldCurrent := field{fieldType, structVal.Field(i)}
+ if remain {
+ remainField = &fieldCurrent
+ } else {
+ // Normal struct field, store it away
+ fields = append(fields, field{fieldType, structVal.Field(i)})
+ }
}
}
@@ -1078,9 +1219,6 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
}
}
- // Delete the key we're using from the unused map so we stop tracking
- delete(dataValKeysUnused, rawMapKey.Interface())
-
if !fieldValue.IsValid() {
// This should never happen
panic("field is not valid")
@@ -1092,6 +1230,9 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
continue
}
+ // Delete the key we're using from the unused map so we stop tracking
+ delete(dataValKeysUnused, rawMapKey.Interface())
+
// If the name is empty string, then we're at the root, and we
// don't dot-join the fields.
if name != "" {
@@ -1103,6 +1244,25 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
}
}
+ // If we have a "remain"-tagged field and we have unused keys then
+ // we put the unused keys directly into the remain field.
+ if remainField != nil && len(dataValKeysUnused) > 0 {
+ // Build a map of only the unused values
+ remain := map[interface{}]interface{}{}
+ for key := range dataValKeysUnused {
+ remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface()
+ }
+
+ // Decode it as-if we were just decoding this map onto our map.
+ if err := d.decodeMap(name, remain, remainField.val); err != nil {
+ errors = appendErrors(errors, err)
+ }
+
+ // Set the map to nil so we have none so that the next check will
+ // not error (ErrorUnused)
+ dataValKeysUnused = nil
+ }
+
if d.config.ErrorUnused && len(dataValKeysUnused) > 0 {
keys := make([]string, 0, len(dataValKeysUnused))
for rawKey := range dataValKeysUnused {
diff --git a/vendor/go.uber.org/atomic/CHANGELOG.md b/vendor/go.uber.org/atomic/CHANGELOG.md
index a88b023e4..aef8b6ebc 100644
--- a/vendor/go.uber.org/atomic/CHANGELOG.md
+++ b/vendor/go.uber.org/atomic/CHANGELOG.md
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.6.0] - 2020-02-24
+### Changed
+- Drop library dependency on `golang.org/x/{lint, tools}`.
+
+## [1.5.1] - 2019-11-19
+- Fix bug where `Bool.CAS` and `Bool.Toggle` do work correctly together
+ causing `CAS` to fail even though the old value matches.
+
## [1.5.0] - 2019-10-29
### Changed
- With Go modules, only the `go.uber.org/atomic` import path is supported now.
@@ -44,7 +52,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Initial release.
-[1.4.0]: https://github.com/uber-go/atomic/compare/v1.4.0...v1.5.0
+[1.6.0]: https://github.com/uber-go/atomic/compare/v1.5.1...v1.6.0
+[1.5.1]: https://github.com/uber-go/atomic/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/uber-go/atomic/compare/v1.4.0...v1.5.0
[1.4.0]: https://github.com/uber-go/atomic/compare/v1.3.2...v1.4.0
[1.3.2]: https://github.com/uber-go/atomic/compare/v1.3.1...v1.3.2
[1.3.1]: https://github.com/uber-go/atomic/compare/v1.3.0...v1.3.1
diff --git a/vendor/go.uber.org/atomic/README.md b/vendor/go.uber.org/atomic/README.md
index 3cc368ba3..ade0c20f1 100644
--- a/vendor/go.uber.org/atomic/README.md
+++ b/vendor/go.uber.org/atomic/README.md
@@ -8,13 +8,25 @@ Simple wrappers for primitive types to enforce atomic access.
$ go get -u go.uber.org/atomic@v1
```
-Note: If you are using Go modules, this package will fail to compile with the
-import path `github.com/uber-go/atomic`. To continue using that import path,
-you will have to add a `replace` directive to your `go.mod`, replacing
-`github.com/uber-go/atomic` with `go.uber.org/atomic`.
+### Legacy Import Path
+
+As of v1.5.0, the import path `go.uber.org/atomic` is the only supported way
+of using this package. If you are using Go modules, this package will fail to
+compile with the legacy import path path `github.com/uber-go/atomic`.
+
+We recommend migrating your code to the new import path but if you're unable
+to do so, or if your dependencies are still using the old import path, you
+will have to add a `replace` directive to your `go.mod` file downgrading the
+legacy import path to an older version.
+
+```
+replace github.com/uber-go/atomic => github.com/uber-go/atomic v1.4.0
+```
+
+You can do so automatically by running the following command.
```shell
-$ go mod edit -replace github.com/uber-go/atomic=go.uber.org/atomic@v1
+$ go mod edit -replace github.com/uber-go/atomic=github.com/uber-go/atomic@v1.4.0
```
## Usage
diff --git a/vendor/go.uber.org/atomic/atomic.go b/vendor/go.uber.org/atomic/atomic.go
index 1db6849fc..ad5fa0980 100644
--- a/vendor/go.uber.org/atomic/atomic.go
+++ b/vendor/go.uber.org/atomic/atomic.go
@@ -250,11 +250,16 @@ func (b *Bool) Swap(new bool) bool {
// Toggle atomically negates the Boolean and returns the previous value.
func (b *Bool) Toggle() bool {
- return truthy(atomic.AddUint32(&b.v, 1) - 1)
+ for {
+ old := b.Load()
+ if b.CAS(old, !old) {
+ return old
+ }
+ }
}
func truthy(n uint32) bool {
- return n&1 == 1
+ return n == 1
}
func boolToInt(b bool) uint32 {
diff --git a/vendor/go.uber.org/multierr/.gitignore b/vendor/go.uber.org/multierr/.gitignore
index 61ead8666..b9a05e3da 100644
--- a/vendor/go.uber.org/multierr/.gitignore
+++ b/vendor/go.uber.org/multierr/.gitignore
@@ -1 +1,4 @@
/vendor
+cover.html
+cover.out
+/bin
diff --git a/vendor/go.uber.org/multierr/.travis.yml b/vendor/go.uber.org/multierr/.travis.yml
index 5ffa8fed4..786c917a3 100644
--- a/vendor/go.uber.org/multierr/.travis.yml
+++ b/vendor/go.uber.org/multierr/.travis.yml
@@ -5,11 +5,12 @@ go_import_path: go.uber.org/multierr
env:
global:
- GO15VENDOREXPERIMENT=1
+ - GO111MODULE=on
go:
- - 1.7
- - 1.8
- - tip
+ - 1.11.x
+ - 1.12.x
+ - 1.13.x
cache:
directories:
@@ -18,16 +19,11 @@ cache:
before_install:
- go version
-install:
-- |
- set -e
- make install_ci
-
script:
- |
set -e
make lint
- make test_ci
+ make cover
after_success:
- bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/go.uber.org/multierr/CHANGELOG.md b/vendor/go.uber.org/multierr/CHANGELOG.md
index 898445d06..f0055f895 100644
--- a/vendor/go.uber.org/multierr/CHANGELOG.md
+++ b/vendor/go.uber.org/multierr/CHANGELOG.md
@@ -1,6 +1,19 @@
Releases
========
+v1.3.0 (2019-10-29)
+===================
+
+- Switch to Go modules.
+
+
+v1.2.0 (2019-09-26)
+===================
+
+- Support extracting and matching against wrapped errors with `errors.As`
+ and `errors.Is`.
+
+
v1.1.0 (2017-06-30)
===================
diff --git a/vendor/go.uber.org/multierr/Makefile b/vendor/go.uber.org/multierr/Makefile
index a7437d061..416018237 100644
--- a/vendor/go.uber.org/multierr/Makefile
+++ b/vendor/go.uber.org/multierr/Makefile
@@ -1,23 +1,17 @@
-export GO15VENDOREXPERIMENT=1
-
-PACKAGES := $(shell glide nv)
+# Directory to put `go install`ed binaries in.
+export GOBIN ?= $(shell pwd)/bin
GO_FILES := $(shell \
find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
-o -name '*.go' -print | cut -b3-)
-.PHONY: install
-install:
- glide --version || go get github.com/Masterminds/glide
- glide install
-
.PHONY: build
build:
- go build -i $(PACKAGES)
+ go build ./...
.PHONY: test
test:
- go test -cover -race $(PACKAGES)
+ go test -race ./...
.PHONY: gofmt
gofmt:
@@ -25,50 +19,24 @@ gofmt:
@gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
@[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" | cat - $(FMT_LOG) && false)
-.PHONY: govet
-govet:
- $(eval VET_LOG := $(shell mktemp -t govet.XXXXX))
- @go vet $(PACKAGES) 2>&1 \
- | grep -v '^exit status' > $(VET_LOG) || true
- @[ ! -s "$(VET_LOG)" ] || (echo "govet failed:" | cat - $(VET_LOG) && false)
-
.PHONY: golint
golint:
- @go get github.com/golang/lint/golint
- $(eval LINT_LOG := $(shell mktemp -t golint.XXXXX))
- @cat /dev/null > $(LINT_LOG)
- @$(foreach pkg, $(PACKAGES), golint $(pkg) >> $(LINT_LOG) || true;)
- @[ ! -s "$(LINT_LOG)" ] || (echo "golint failed:" | cat - $(LINT_LOG) && false)
+ @go install golang.org/x/lint/golint
+ @$(GOBIN)/golint ./...
.PHONY: staticcheck
staticcheck:
- @go get honnef.co/go/tools/cmd/staticcheck
- $(eval STATICCHECK_LOG := $(shell mktemp -t staticcheck.XXXXX))
- @staticcheck $(PACKAGES) 2>&1 > $(STATICCHECK_LOG) || true
- @[ ! -s "$(STATICCHECK_LOG)" ] || (echo "staticcheck failed:" | cat - $(STATICCHECK_LOG) && false)
+ @go install honnef.co/go/tools/cmd/staticcheck
+ @$(GOBIN)/staticcheck ./...
.PHONY: lint
-lint: gofmt govet golint staticcheck
+lint: gofmt golint staticcheck
.PHONY: cover
cover:
- ./scripts/cover.sh $(shell go list $(PACKAGES))
+ go test -coverprofile=cover.out -coverpkg=./... -v ./...
go tool cover -html=cover.out -o cover.html
update-license:
- @go get go.uber.org/tools/update-license
- @update-license \
- $(shell go list -json $(PACKAGES) | \
- jq -r '.Dir + "/" + (.GoFiles | .[])')
-
-##############################################################################
-
-.PHONY: install_ci
-install_ci: install
- go get github.com/wadey/gocovmerge
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
-
-.PHONY: test_ci
-test_ci: install_ci
- ./scripts/cover.sh $(shell go list $(PACKAGES))
+ @go install go.uber.org/tools/update-license
+ @$(GOBIN)/update-license $(GO_FILES)
diff --git a/vendor/go.uber.org/multierr/README.md b/vendor/go.uber.org/multierr/README.md
index 065088f64..751bd65e5 100644
--- a/vendor/go.uber.org/multierr/README.md
+++ b/vendor/go.uber.org/multierr/README.md
@@ -17,7 +17,7 @@ Released under the [MIT License].
[MIT License]: LICENSE.txt
[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg
[doc]: https://godoc.org/go.uber.org/multierr
-[ci-img]: https://travis-ci.org/uber-go/multierr.svg?branch=master
+[ci-img]: https://travis-ci.com/uber-go/multierr.svg?branch=master
[cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg
-[ci]: https://travis-ci.org/uber-go/multierr
+[ci]: https://travis-ci.com/uber-go/multierr
[cov]: https://codecov.io/gh/uber-go/multierr
diff --git a/vendor/go.uber.org/multierr/error.go b/vendor/go.uber.org/multierr/error.go
index de6ce4736..d4be18344 100644
--- a/vendor/go.uber.org/multierr/error.go
+++ b/vendor/go.uber.org/multierr/error.go
@@ -33,7 +33,7 @@
// If only two errors are being combined, the Append function may be used
// instead.
//
-// err = multierr.Combine(reader.Close(), writer.Close())
+// err = multierr.Append(reader.Close(), writer.Close())
//
// This makes it possible to record resource cleanup failures from deferred
// blocks with the help of named return values.
@@ -99,8 +99,6 @@ var (
// Separator for single-line error messages.
_singlelineSeparator = []byte("; ")
- _newline = []byte("\n")
-
// Prefix for multi-line messages
_multilinePrefix = []byte("the following errors occurred:")
diff --git a/vendor/go.uber.org/multierr/glide.lock b/vendor/go.uber.org/multierr/glide.lock
deleted file mode 100644
index f9ea94c33..000000000
--- a/vendor/go.uber.org/multierr/glide.lock
+++ /dev/null
@@ -1,19 +0,0 @@
-hash: b53b5e9a84b9cb3cc4b2d0499e23da2feca1eec318ce9bb717ecf35bf24bf221
-updated: 2017-04-10T13:34:45.671678062-07:00
-imports:
-- name: go.uber.org/atomic
- version: 3b8db5e93c4c02efbc313e17b2e796b0914a01fb
-testImports:
-- name: github.com/davecgh/go-spew
- version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
- subpackages:
- - spew
-- name: github.com/pmezard/go-difflib
- version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
- subpackages:
- - difflib
-- name: github.com/stretchr/testify
- version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
- subpackages:
- - assert
- - require
diff --git a/vendor/go.uber.org/multierr/go.mod b/vendor/go.uber.org/multierr/go.mod
new file mode 100644
index 000000000..5463fac72
--- /dev/null
+++ b/vendor/go.uber.org/multierr/go.mod
@@ -0,0 +1,12 @@
+module go.uber.org/multierr
+
+go 1.12
+
+require (
+ github.com/stretchr/testify v1.3.0
+ go.uber.org/atomic v1.5.0
+ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee
+ golang.org/x/lint v0.0.0-20190930215403-16217165b5de
+ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 // indirect
+ honnef.co/go/tools v0.0.1-2019.2.3
+)
diff --git a/vendor/go.uber.org/multierr/go.sum b/vendor/go.uber.org/multierr/go.sum
new file mode 100644
index 000000000..b460913d2
--- /dev/null
+++ b/vendor/go.uber.org/multierr/go.sum
@@ -0,0 +1,45 @@
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+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/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+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/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/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+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-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c h1:IGkKhmfzcztjm6gYkykvu/NiS8kaqbCWAEWWAyf8J5U=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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=
+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=
diff --git a/vendor/go.uber.org/multierr/go113.go b/vendor/go.uber.org/multierr/go113.go
new file mode 100644
index 000000000..264b0eac0
--- /dev/null
+++ b/vendor/go.uber.org/multierr/go113.go
@@ -0,0 +1,52 @@
+// Copyright (c) 2019 Uber Technologies, Inc.
+//
+// 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.
+
+// +build go1.13
+
+package multierr
+
+import "errors"
+
+// As attempts to find the first error in the error list that matches the type
+// of the value that target points to.
+//
+// This function allows errors.As to traverse the values stored on the
+// multierr error.
+func (merr *multiError) As(target interface{}) bool {
+ for _, err := range merr.Errors() {
+ if errors.As(err, target) {
+ return true
+ }
+ }
+ return false
+}
+
+// Is attempts to match the provided error against errors in the error list.
+//
+// This function allows errors.Is to traverse the values stored on the
+// multierr error.
+func (merr *multiError) Is(target error) bool {
+ for _, err := range merr.Errors() {
+ if errors.Is(err, target) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/go.uber.org/multierr/tools.go b/vendor/go.uber.org/multierr/tools.go
new file mode 100644
index 000000000..df93f0723
--- /dev/null
+++ b/vendor/go.uber.org/multierr/tools.go
@@ -0,0 +1,30 @@
+// Copyright (c) 2019 Uber Technologies, Inc.
+//
+// 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.
+
+// +build tools
+
+package multierr
+
+import (
+ // Tools we use during development.
+ _ "go.uber.org/tools/update-license"
+ _ "golang.org/x/lint/golint"
+ _ "honnef.co/go/tools/cmd/staticcheck"
+)
diff --git a/vendor/go.uber.org/tools/LICENSE b/vendor/go.uber.org/tools/LICENSE
new file mode 100644
index 000000000..858e02475
--- /dev/null
+++ b/vendor/go.uber.org/tools/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2017 Uber Technologies, Inc.
+
+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.
diff --git a/vendor/go.uber.org/tools/update-license/.gitignore b/vendor/go.uber.org/tools/update-license/.gitignore
new file mode 100644
index 000000000..b167772c8
--- /dev/null
+++ b/vendor/go.uber.org/tools/update-license/.gitignore
@@ -0,0 +1 @@
+update-license
diff --git a/vendor/go.uber.org/tools/update-license/README.md b/vendor/go.uber.org/tools/update-license/README.md
new file mode 100644
index 000000000..5887df1dd
--- /dev/null
+++ b/vendor/go.uber.org/tools/update-license/README.md
@@ -0,0 +1,24 @@
+# update-license
+
+This is a small tool that updates the license header for Uber's open source Golang files.
+
+## Installation
+
+```
+go get go.uber.org/tools/update-license
+```
+
+## Usage
+
+```
+update-license go_files...
+```
+
+## Further Work
+
+* Support more licenses by name (MIT, Apache 2.0, etc), file path, url (http GET)
+* Support custom owner (not just "Uber Technologies, Inc.")
+* Support more languages than go (cover go, java, js, py to start, along with LICENSE, LICENSE.txt)
+* Talk about removing custom logic for header comments (ie `@generated`, `Code generated by`), it probably makes more sense just to put the license at the top
+* Better detection support for existing licenses so they can be removed
+* Verbose, dry run support
diff --git a/vendor/go.uber.org/tools/update-license/licenses.go b/vendor/go.uber.org/tools/update-license/licenses.go
new file mode 100644
index 000000000..76957c21e
--- /dev/null
+++ b/vendor/go.uber.org/tools/update-license/licenses.go
@@ -0,0 +1,56 @@
+// Copyright (c) 2019 Uber Technologies, Inc.
+//
+// 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.
+
+package main
+
+var licenseTemplates = map[string]string{
+ "Apache-2.0": `// Copyright {{.Year}} {{.Owner}}
+//
+// 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.`,
+ "MIT": `// Copyright (c) {{.Year}} {{.Owner}}
+//
+// 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.`,
+}
diff --git a/vendor/go.uber.org/tools/update-license/main.go b/vendor/go.uber.org/tools/update-license/main.go
new file mode 100644
index 000000000..269fd9b47
--- /dev/null
+++ b/vendor/go.uber.org/tools/update-license/main.go
@@ -0,0 +1,228 @@
+// Copyright (c) 2019 Uber Technologies, Inc.
+//
+// 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.
+
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "html/template"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "time"
+)
+
+const (
+ // how many lines to check for an existing copyright
+ // this logic is not great and we should probably do something else
+ // but this was copied from the python script
+ copyrightLineLimit = 5
+ headerPrefix = "// Copyright"
+)
+
+var (
+ flagDryRun = flag.Bool("dry", false, "Do not edit files and just print out what files would be edited")
+ flagOwner = flag.String("owner", "Uber Technologies, Inc.", "Copyright owner")
+ flagLicense = flag.String(
+ "license",
+ "MIT",
+ fmt.Sprintf(
+ "Type of license to use [%s]",
+ strings.Join(validLicenses(), ", "),
+ ),
+ )
+
+ lineSkipPrefixes = []string{
+ "// Code generated by",
+ "// @generated",
+ }
+)
+
+func main() {
+ log.SetFlags(0)
+ log.SetOutput(os.Stdout)
+ log.SetPrefix("")
+ if err := do(); err != nil {
+ log.Fatal(err)
+ }
+}
+
+func do() error {
+ flag.Parse()
+
+ if len(flag.Args()) < 1 {
+ return fmt.Errorf("usage: %s GO_FILES", os.Args[0])
+ }
+
+ return updateFiles(
+ flag.Args(),
+ time.Now().UTC().Year(),
+ *flagLicense,
+ *flagOwner,
+ *flagDryRun,
+ )
+}
+
+func fullLicense(ts string, year int, owner string) string {
+ var buf bytes.Buffer
+ t, err := template.New("").Parse(ts)
+ if err != nil {
+ log.Panic("failed to parse license template", err)
+ }
+
+ data := struct {
+ Year int
+ Owner string
+ }{year, owner}
+ if err := t.Execute(&buf, data); err != nil {
+ log.Panic("failed to execture license template", err)
+ }
+
+ return strings.TrimSpace(buf.String())
+}
+
+// validLicenses grabs all the license templates from the folder
+func validLicenses() []string {
+ res := make([]string, 0, len(licenseTemplates))
+
+ for k := range licenseTemplates {
+ res = append(res, k)
+ }
+
+ sort.Strings(res)
+ return res
+}
+
+func updateFiles(
+ filePaths []string,
+ year int,
+ license string,
+ owner string,
+ dryRun bool,
+) error {
+ if err := checkFilePaths(filePaths); err != nil {
+ return err
+ }
+ for _, filePath := range filePaths {
+ if err := updateFile(filePath, year, license, owner, dryRun); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func checkFilePaths(filePaths []string) error {
+ for _, filePath := range filePaths {
+ if filepath.Ext(filePath) != ".go" {
+ return fmt.Errorf("%s is not a go file", filePath)
+ }
+ }
+ return nil
+}
+
+func updateFile(
+ filePath string,
+ year int,
+ license string,
+ owner string,
+ dryRun bool,
+) error {
+ data, err := ioutil.ReadFile(filePath)
+ if err != nil {
+ return err
+ }
+ newData := updateData(data, year, license, owner)
+ if !bytes.Equal(data, newData) {
+ if dryRun {
+ log.Print(filePath)
+ return nil
+ }
+ // we could do something more complicated so that we do not
+ // need to pass 0644 as the file mode, but in this case it should
+ // never actually be used to create a file since we know the file
+ // already exists, and it's easier to use the ReadFile/WriteFile
+ // logic as it is right now, and since this is just a generation
+ // program, this should be acceptable
+ return ioutil.WriteFile(filePath, newData, 0644)
+ }
+ return nil
+}
+
+func updateData(
+ data []byte,
+ year int,
+ license string,
+ owner string,
+) []byte {
+ licenseText := fullLicense(string(licenseTemplates[license]), year, owner)
+
+ return []byte(
+ strings.Join(
+ updateLines(strings.Split(string(data), "\n"), licenseText),
+ "\n",
+ ),
+ )
+}
+
+// a value in the returned slice may contain newlines itself
+func updateLines(lines []string, license string) []string {
+ for i, line := range lines {
+ if i >= copyrightLineLimit {
+ break
+ }
+ if strings.HasPrefix(line, headerPrefix) {
+ // assume that the new license text always starts with the copyright
+ // string. Pretty safe to assume, right? RIGHT?
+ lines[i] = strings.Split(license, "\n")[0]
+ return lines
+ }
+ }
+ return addToLines(lines, license)
+}
+
+// a value in the returned slice may contain newlines itself
+func addToLines(lines []string, license string) []string {
+ i := 0
+ for len(lines) > i && lineContainsSkipPrefix(lines[i]) {
+ i++
+ // skip comments under the generated line too
+ for strings.HasPrefix(lines[i], "//") {
+ i++
+ }
+ }
+ if i == 0 {
+ return append([]string{license, ""}, lines...)
+ }
+ return append(lines[0:i], append([]string{"", license}, lines[i:]...)...)
+}
+
+func lineContainsSkipPrefix(line string) bool {
+ for _, skipPrefix := range lineSkipPrefixes {
+ if strings.HasPrefix(line, skipPrefix) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/go.uber.org/zap/.gitignore b/vendor/go.uber.org/zap/.gitignore
index 08fbde6ce..da9d9d00b 100644
--- a/vendor/go.uber.org/zap/.gitignore
+++ b/vendor/go.uber.org/zap/.gitignore
@@ -26,3 +26,7 @@ _testmain.go
*.pprof
*.out
*.log
+
+/bin
+cover.out
+cover.html
diff --git a/vendor/go.uber.org/zap/.readme.tmpl b/vendor/go.uber.org/zap/.readme.tmpl
index c6440db8e..3154a1e64 100644
--- a/vendor/go.uber.org/zap/.readme.tmpl
+++ b/vendor/go.uber.org/zap/.readme.tmpl
@@ -100,9 +100,10 @@ pinned in zap's [glide.lock][] file. [↩](#anchor-versions)
[doc-img]: https://godoc.org/go.uber.org/zap?status.svg
[doc]: https://godoc.org/go.uber.org/zap
-[ci-img]: https://travis-ci.org/uber-go/zap.svg?branch=master
-[ci]: https://travis-ci.org/uber-go/zap
+[ci-img]: https://travis-ci.com/uber-go/zap.svg?branch=master
+[ci]: https://travis-ci.com/uber-go/zap
[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg
[cov]: https://codecov.io/gh/uber-go/zap
[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
[glide.lock]: https://github.com/uber-go/zap/blob/master/glide.lock
+
diff --git a/vendor/go.uber.org/zap/.travis.yml b/vendor/go.uber.org/zap/.travis.yml
index ada5ebdcc..647b4ee43 100644
--- a/vendor/go.uber.org/zap/.travis.yml
+++ b/vendor/go.uber.org/zap/.travis.yml
@@ -1,21 +1,23 @@
language: go
sudo: false
-go:
- - 1.11.x
- - 1.12.x
+
go_import_path: go.uber.org/zap
env:
global:
- TEST_TIMEOUT_SCALE=10
-cache:
- directories:
- - vendor
-install:
- - make dependencies
+ - GO111MODULE=on
+
+matrix:
+ include:
+ - go: 1.12.x
+ - go: 1.13.x
+ env: LINT=1
+
script:
- - make lint
+ - test -z "$LINT" || make lint
- make test
- make bench
+
after_success:
- make cover
- bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/go.uber.org/zap/CHANGELOG.md b/vendor/go.uber.org/zap/CHANGELOG.md
index 28d10677e..bebdb748d 100644
--- a/vendor/go.uber.org/zap/CHANGELOG.md
+++ b/vendor/go.uber.org/zap/CHANGELOG.md
@@ -1,5 +1,26 @@
# Changelog
+## 1.13.0 (13 Nov 2019)
+
+Enhancements:
+* [#758][]: Add `Intp`, `Stringp`, and other similar `*p` field constructors
+ to log pointers to primitives with support for `nil` values.
+
+Thanks to @jbizzle for their contributions to this release.
+
+## 1.12.0 (29 Oct 2019)
+
+Enhancements:
+* [#751][]: Migrate to Go modules.
+
+## 1.11.0 (21 Oct 2019)
+
+Enhancements:
+* [#725][]: Add `zapcore.OmitKey` to omit keys in an `EncoderConfig`.
+* [#736][]: Add `RFC3339` and `RFC3339Nano` time encoders.
+
+Thanks to @juicemia, @uhthomas for their contributions to this release.
+
## 1.10.0 (29 Apr 2019)
Bugfixes:
@@ -325,3 +346,7 @@ upgrade to the upcoming stable release.
[#610]: https://github.com/uber-go/zap/pull/610
[#675]: https://github.com/uber-go/zap/pull/675
[#704]: https://github.com/uber-go/zap/pull/704
+[#725]: https://github.com/uber-go/zap/pull/725
+[#736]: https://github.com/uber-go/zap/pull/736
+[#751]: https://github.com/uber-go/zap/pull/751
+[#758]: https://github.com/uber-go/zap/pull/758
diff --git a/vendor/go.uber.org/zap/Makefile b/vendor/go.uber.org/zap/Makefile
index 073e9aa91..21e436c45 100644
--- a/vendor/go.uber.org/zap/Makefile
+++ b/vendor/go.uber.org/zap/Makefile
@@ -1,74 +1,55 @@
-export GO15VENDOREXPERIMENT=1
+export GOBIN ?= $(shell pwd)/bin
+GOLINT = $(GOBIN)/golint
BENCH_FLAGS ?= -cpuprofile=cpu.pprof -memprofile=mem.pprof -benchmem
-PKGS ?= $(shell glide novendor)
-# Many Go tools take file globs or directories as arguments instead of packages.
-PKG_FILES ?= *.go zapcore benchmarks buffer zapgrpc zaptest zaptest/observer internal/bufferpool internal/exit internal/color internal/ztest
-# The linting tools evolve with each Go version, so run them only on the latest
-# stable release.
-GO_VERSION := $(shell go version | cut -d " " -f 3)
-GO_MINOR_VERSION := $(word 2,$(subst ., ,$(GO_VERSION)))
-LINTABLE_MINOR_VERSIONS := 12
-ifneq ($(filter $(LINTABLE_MINOR_VERSIONS),$(GO_MINOR_VERSION)),)
-SHOULD_LINT := true
-endif
+# Directories containing independent Go modules.
+#
+# We track coverage only for the main module.
+MODULE_DIRS = . ./benchmarks
+# Many Go tools take file globs or directories as arguments instead of packages.
+GO_FILES := $(shell \
+ find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
+ -o -name '*.go' -print | cut -b3-)
.PHONY: all
all: lint test
-.PHONY: dependencies
-dependencies:
- @echo "Installing Glide and locked dependencies..."
- glide --version || go get -u -f github.com/Masterminds/glide
- glide install
- @echo "Installing test dependencies..."
- go install ./vendor/github.com/axw/gocov/gocov
- go install ./vendor/github.com/mattn/goveralls
-ifdef SHOULD_LINT
- @echo "Installing golint..."
- go install ./vendor/github.com/golang/lint/golint
-else
- @echo "Not installing golint, since we don't expect to lint on" $(GO_VERSION)
-endif
-
-# Disable printf-like invocation checking due to testify.assert.Error()
-VET_RULES := -printf=false
-
.PHONY: lint
-lint:
-ifdef SHOULD_LINT
+lint: $(GOLINT)
@rm -rf lint.log
@echo "Checking formatting..."
- @gofmt -d -s $(PKG_FILES) 2>&1 | tee lint.log
- @echo "Installing test dependencies for vet..."
- @go test -i $(PKGS)
+ @gofmt -d -s $(GO_FILES) 2>&1 | tee lint.log
@echo "Checking vet..."
- @go vet $(VET_RULES) $(PKGS) 2>&1 | tee -a lint.log
+ @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go vet ./... 2>&1) &&) true | tee -a lint.log
@echo "Checking lint..."
- @$(foreach dir,$(PKGS),golint $(dir) 2>&1 | tee -a lint.log;)
+ @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && $(GOLINT) ./... 2>&1) &&) true | tee -a lint.log
@echo "Checking for unresolved FIXMEs..."
- @git grep -i fixme | grep -v -e vendor -e Makefile | tee -a lint.log
+ @git grep -i fixme | grep -v -e Makefile | tee -a lint.log
@echo "Checking for license headers..."
- @./check_license.sh | tee -a lint.log
+ @./checklicense.sh | tee -a lint.log
@[ ! -s lint.log ]
-else
- @echo "Skipping linters on" $(GO_VERSION)
-endif
+
+$(GOLINT):
+ go install golang.org/x/lint/golint
.PHONY: test
test:
- go test -race $(PKGS)
+ @$(foreach dir,$(MODULE_DIRS),(cd $(dir) && go test -race ./...) &&) true
.PHONY: cover
cover:
- ./scripts/cover.sh $(PKGS)
+ go test -race -coverprofile=cover.out -coverpkg=./... ./...
+ go tool cover -html=cover.out -o cover.html
.PHONY: bench
BENCH ?= .
bench:
- @$(foreach pkg,$(PKGS),go test -bench=$(BENCH) -run="^$$" $(BENCH_FLAGS) $(pkg);)
+ @$(foreach dir,$(MODULE_DIRS), ( \
+ cd $(dir) && \
+ go list ./... | xargs -n1 go test -bench=$(BENCH) -run="^$$" $(BENCH_FLAGS) \
+ ) &&) true
.PHONY: updatereadme
updatereadme:
diff --git a/vendor/go.uber.org/zap/README.md b/vendor/go.uber.org/zap/README.md
index f4fd1cb44..bcea28a19 100644
--- a/vendor/go.uber.org/zap/README.md
+++ b/vendor/go.uber.org/zap/README.md
@@ -64,43 +64,40 @@ id="anchor-versions">[1](#footnote-versions)
Log a message and 10 fields:
-| Package | Time | Objects Allocated |
-| :--- | :---: | :---: |
-| :zap: zap | 3131 ns/op | 5 allocs/op |
-| :zap: zap (sugared) | 4173 ns/op | 21 allocs/op |
-| zerolog | 16154 ns/op | 90 allocs/op |
-| lion | 16341 ns/op | 111 allocs/op |
-| go-kit | 17049 ns/op | 126 allocs/op |
-| logrus | 23662 ns/op | 142 allocs/op |
-| log15 | 36351 ns/op | 149 allocs/op |
-| apex/log | 42530 ns/op | 126 allocs/op |
+| Package | Time | Time % to zap | Objects Allocated |
+| :------ | :--: | :-----------: | :---------------: |
+| :zap: zap | 862 ns/op | +0% | 5 allocs/op
+| :zap: zap (sugared) | 1250 ns/op | +45% | 11 allocs/op
+| zerolog | 4021 ns/op | +366% | 76 allocs/op
+| go-kit | 4542 ns/op | +427% | 105 allocs/op
+| apex/log | 26785 ns/op | +3007% | 115 allocs/op
+| logrus | 29501 ns/op | +3322% | 125 allocs/op
+| log15 | 29906 ns/op | +3369% | 122 allocs/op
Log a message with a logger that already has 10 fields of context:
-| Package | Time | Objects Allocated |
-| :--- | :---: | :---: |
-| :zap: zap | 380 ns/op | 0 allocs/op |
-| :zap: zap (sugared) | 564 ns/op | 2 allocs/op |
-| zerolog | 321 ns/op | 0 allocs/op |
-| lion | 7092 ns/op | 39 allocs/op |
-| go-kit | 20226 ns/op | 115 allocs/op |
-| logrus | 22312 ns/op | 130 allocs/op |
-| log15 | 28788 ns/op | 79 allocs/op |
-| apex/log | 42063 ns/op | 115 allocs/op |
+| Package | Time | Time % to zap | Objects Allocated |
+| :------ | :--: | :-----------: | :---------------: |
+| :zap: zap | 126 ns/op | +0% | 0 allocs/op
+| :zap: zap (sugared) | 187 ns/op | +48% | 2 allocs/op
+| zerolog | 88 ns/op | -30% | 0 allocs/op
+| go-kit | 5087 ns/op | +3937% | 103 allocs/op
+| log15 | 18548 ns/op | +14621% | 73 allocs/op
+| apex/log | 26012 ns/op | +20544% | 104 allocs/op
+| logrus | 27236 ns/op | +21516% | 113 allocs/op
Log a static string, without any context or `printf`-style templating:
-| Package | Time | Objects Allocated |
-| :--- | :---: | :---: |
-| :zap: zap | 361 ns/op | 0 allocs/op |
-| :zap: zap (sugared) | 534 ns/op | 2 allocs/op |
-| zerolog | 323 ns/op | 0 allocs/op |
-| standard library | 575 ns/op | 2 allocs/op |
-| go-kit | 922 ns/op | 13 allocs/op |
-| lion | 1413 ns/op | 10 allocs/op |
-| logrus | 2291 ns/op | 27 allocs/op |
-| apex/log | 3690 ns/op | 11 allocs/op |
-| log15 | 5954 ns/op | 26 allocs/op |
+| Package | Time | Time % to zap | Objects Allocated |
+| :------ | :--: | :-----------: | :---------------: |
+| :zap: zap | 118 ns/op | +0% | 0 allocs/op
+| :zap: zap (sugared) | 191 ns/op | +62% | 2 allocs/op
+| zerolog | 93 ns/op | -21% | 0 allocs/op
+| go-kit | 280 ns/op | +137% | 11 allocs/op
+| standard library | 499 ns/op | +323% | 2 allocs/op
+| apex/log | 1990 ns/op | +1586% | 10 allocs/op
+| logrus | 3129 ns/op | +2552% | 24 allocs/op
+| log15 | 3887 ns/op | +3194% | 23 allocs/op
## Development Status: Stable
@@ -124,13 +121,14 @@ Released under the [MIT License](LICENSE.txt).
In particular, keep in mind that we may be
benchmarking against slightly older versions of other packages. Versions are
-pinned in zap's [glide.lock][] file. [↩](#anchor-versions)
+pinned in the [benchmarks/go.mod][] file. [↩](#anchor-versions)
[doc-img]: https://godoc.org/go.uber.org/zap?status.svg
[doc]: https://godoc.org/go.uber.org/zap
-[ci-img]: https://travis-ci.org/uber-go/zap.svg?branch=master
-[ci]: https://travis-ci.org/uber-go/zap
+[ci-img]: https://travis-ci.com/uber-go/zap.svg?branch=master
+[ci]: https://travis-ci.com/uber-go/zap
[cov-img]: https://codecov.io/gh/uber-go/zap/branch/master/graph/badge.svg
[cov]: https://codecov.io/gh/uber-go/zap
[benchmarking suite]: https://github.com/uber-go/zap/tree/master/benchmarks
-[glide.lock]: https://github.com/uber-go/zap/blob/master/glide.lock
+[benchmarks/go.mod]: https://github.com/uber-go/zap/blob/master/benchmarks/go.mod
+
diff --git a/vendor/go.uber.org/zap/check_license.sh b/vendor/go.uber.org/zap/checklicense.sh
similarity index 100%
rename from vendor/go.uber.org/zap/check_license.sh
rename to vendor/go.uber.org/zap/checklicense.sh
diff --git a/vendor/go.uber.org/zap/field.go b/vendor/go.uber.org/zap/field.go
index 5130e1347..83c1ea245 100644
--- a/vendor/go.uber.org/zap/field.go
+++ b/vendor/go.uber.org/zap/field.go
@@ -38,6 +38,12 @@ func Skip() Field {
return Field{Type: zapcore.SkipType}
}
+// nilField returns a field which will marshal explicitly as nil. See motivation
+// in https://github.com/uber-go/zap/issues/753 . If we ever make breaking
+// changes and add zapcore.NilType and zapcore.ObjectEncoder.AddNil, the
+// implementation here should be changed to reflect that.
+func nilField(key string) Field { return Reflect(key, nil) }
+
// Binary constructs a field that carries an opaque binary blob.
//
// Binary data is serialized in an encoding-appropriate format. For example,
@@ -56,6 +62,15 @@ func Bool(key string, val bool) Field {
return Field{Key: key, Type: zapcore.BoolType, Integer: ival}
}
+// Boolp constructs a field that carries a *bool. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Boolp(key string, val *bool) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Bool(key, *val)
+}
+
// ByteString constructs a field that carries UTF-8 encoded text as a []byte.
// To log opaque binary blobs (which aren't necessarily valid UTF-8), use
// Binary.
@@ -70,6 +85,15 @@ func Complex128(key string, val complex128) Field {
return Field{Key: key, Type: zapcore.Complex128Type, Interface: val}
}
+// Complex128p constructs a field that carries a *complex128. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Complex128p(key string, val *complex128) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Complex128(key, *val)
+}
+
// Complex64 constructs a field that carries a complex number. Unlike most
// numeric fields, this costs an allocation (to convert the complex64 to
// interface{}).
@@ -77,6 +101,15 @@ func Complex64(key string, val complex64) Field {
return Field{Key: key, Type: zapcore.Complex64Type, Interface: val}
}
+// Complex64p constructs a field that carries a *complex64. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Complex64p(key string, val *complex64) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Complex64(key, *val)
+}
+
// Float64 constructs a field that carries a float64. The way the
// floating-point value is represented is encoder-dependent, so marshaling is
// necessarily lazy.
@@ -84,6 +117,15 @@ func Float64(key string, val float64) Field {
return Field{Key: key, Type: zapcore.Float64Type, Integer: int64(math.Float64bits(val))}
}
+// Float64p constructs a field that carries a *float64. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Float64p(key string, val *float64) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Float64(key, *val)
+}
+
// Float32 constructs a field that carries a float32. The way the
// floating-point value is represented is encoder-dependent, so marshaling is
// necessarily lazy.
@@ -91,66 +133,183 @@ func Float32(key string, val float32) Field {
return Field{Key: key, Type: zapcore.Float32Type, Integer: int64(math.Float32bits(val))}
}
+// Float32p constructs a field that carries a *float32. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Float32p(key string, val *float32) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Float32(key, *val)
+}
+
// Int constructs a field with the given key and value.
func Int(key string, val int) Field {
return Int64(key, int64(val))
}
+// Intp constructs a field that carries a *int. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Intp(key string, val *int) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Int(key, *val)
+}
+
// Int64 constructs a field with the given key and value.
func Int64(key string, val int64) Field {
return Field{Key: key, Type: zapcore.Int64Type, Integer: val}
}
+// Int64p constructs a field that carries a *int64. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Int64p(key string, val *int64) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Int64(key, *val)
+}
+
// Int32 constructs a field with the given key and value.
func Int32(key string, val int32) Field {
return Field{Key: key, Type: zapcore.Int32Type, Integer: int64(val)}
}
+// Int32p constructs a field that carries a *int32. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Int32p(key string, val *int32) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Int32(key, *val)
+}
+
// Int16 constructs a field with the given key and value.
func Int16(key string, val int16) Field {
return Field{Key: key, Type: zapcore.Int16Type, Integer: int64(val)}
}
+// Int16p constructs a field that carries a *int16. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Int16p(key string, val *int16) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Int16(key, *val)
+}
+
// Int8 constructs a field with the given key and value.
func Int8(key string, val int8) Field {
return Field{Key: key, Type: zapcore.Int8Type, Integer: int64(val)}
}
+// Int8p constructs a field that carries a *int8. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Int8p(key string, val *int8) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Int8(key, *val)
+}
+
// String constructs a field with the given key and value.
func String(key string, val string) Field {
return Field{Key: key, Type: zapcore.StringType, String: val}
}
+// Stringp constructs a field that carries a *string. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Stringp(key string, val *string) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return String(key, *val)
+}
+
// Uint constructs a field with the given key and value.
func Uint(key string, val uint) Field {
return Uint64(key, uint64(val))
}
+// Uintp constructs a field that carries a *uint. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Uintp(key string, val *uint) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Uint(key, *val)
+}
+
// Uint64 constructs a field with the given key and value.
func Uint64(key string, val uint64) Field {
return Field{Key: key, Type: zapcore.Uint64Type, Integer: int64(val)}
}
+// Uint64p constructs a field that carries a *uint64. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Uint64p(key string, val *uint64) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Uint64(key, *val)
+}
+
// Uint32 constructs a field with the given key and value.
func Uint32(key string, val uint32) Field {
return Field{Key: key, Type: zapcore.Uint32Type, Integer: int64(val)}
}
+// Uint32p constructs a field that carries a *uint32. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Uint32p(key string, val *uint32) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Uint32(key, *val)
+}
+
// Uint16 constructs a field with the given key and value.
func Uint16(key string, val uint16) Field {
return Field{Key: key, Type: zapcore.Uint16Type, Integer: int64(val)}
}
+// Uint16p constructs a field that carries a *uint16. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Uint16p(key string, val *uint16) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Uint16(key, *val)
+}
+
// Uint8 constructs a field with the given key and value.
func Uint8(key string, val uint8) Field {
return Field{Key: key, Type: zapcore.Uint8Type, Integer: int64(val)}
}
+// Uint8p constructs a field that carries a *uint8. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Uint8p(key string, val *uint8) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Uint8(key, *val)
+}
+
// Uintptr constructs a field with the given key and value.
func Uintptr(key string, val uintptr) Field {
return Field{Key: key, Type: zapcore.UintptrType, Integer: int64(val)}
}
+// Uintptrp constructs a field that carries a *uintptr. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Uintptrp(key string, val *uintptr) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Uintptr(key, *val)
+}
+
// Reflect constructs a field with the given key and an arbitrary object. It uses
// an encoding-appropriate, reflection-based function to lazily serialize nearly
// any object into the logging context, but it's relatively slow and
@@ -183,6 +342,15 @@ func Time(key string, val time.Time) Field {
return Field{Key: key, Type: zapcore.TimeType, Integer: val.UnixNano(), Interface: val.Location()}
}
+// Timep constructs a field that carries a *time.Time. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Timep(key string, val *time.Time) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Time(key, *val)
+}
+
// Stack constructs a field that stores a stacktrace of the current goroutine
// under provided key. Keep in mind that taking a stacktrace is eager and
// expensive (relatively speaking); this function both makes an allocation and
@@ -201,6 +369,15 @@ func Duration(key string, val time.Duration) Field {
return Field{Key: key, Type: zapcore.DurationType, Integer: int64(val)}
}
+// Durationp constructs a field that carries a *time.Duration. The returned Field will safely
+// and explicitly represent `nil` when appropriate.
+func Durationp(key string, val *time.Duration) Field {
+ if val == nil {
+ return nilField(key)
+ }
+ return Duration(key, *val)
+}
+
// Object constructs a field with the given key and ObjectMarshaler. It
// provides a flexible, but still type-safe and efficient, way to add map- or
// struct-like user-defined types to the logging context. The struct's
@@ -224,78 +401,116 @@ func Any(key string, value interface{}) Field {
return Array(key, val)
case bool:
return Bool(key, val)
+ case *bool:
+ return Boolp(key, val)
case []bool:
return Bools(key, val)
case complex128:
return Complex128(key, val)
+ case *complex128:
+ return Complex128p(key, val)
case []complex128:
return Complex128s(key, val)
case complex64:
return Complex64(key, val)
+ case *complex64:
+ return Complex64p(key, val)
case []complex64:
return Complex64s(key, val)
case float64:
return Float64(key, val)
+ case *float64:
+ return Float64p(key, val)
case []float64:
return Float64s(key, val)
case float32:
return Float32(key, val)
+ case *float32:
+ return Float32p(key, val)
case []float32:
return Float32s(key, val)
case int:
return Int(key, val)
+ case *int:
+ return Intp(key, val)
case []int:
return Ints(key, val)
case int64:
return Int64(key, val)
+ case *int64:
+ return Int64p(key, val)
case []int64:
return Int64s(key, val)
case int32:
return Int32(key, val)
+ case *int32:
+ return Int32p(key, val)
case []int32:
return Int32s(key, val)
case int16:
return Int16(key, val)
+ case *int16:
+ return Int16p(key, val)
case []int16:
return Int16s(key, val)
case int8:
return Int8(key, val)
+ case *int8:
+ return Int8p(key, val)
case []int8:
return Int8s(key, val)
case string:
return String(key, val)
+ case *string:
+ return Stringp(key, val)
case []string:
return Strings(key, val)
case uint:
return Uint(key, val)
+ case *uint:
+ return Uintp(key, val)
case []uint:
return Uints(key, val)
case uint64:
return Uint64(key, val)
+ case *uint64:
+ return Uint64p(key, val)
case []uint64:
return Uint64s(key, val)
case uint32:
return Uint32(key, val)
+ case *uint32:
+ return Uint32p(key, val)
case []uint32:
return Uint32s(key, val)
case uint16:
return Uint16(key, val)
+ case *uint16:
+ return Uint16p(key, val)
case []uint16:
return Uint16s(key, val)
case uint8:
return Uint8(key, val)
+ case *uint8:
+ return Uint8p(key, val)
case []byte:
return Binary(key, val)
case uintptr:
return Uintptr(key, val)
+ case *uintptr:
+ return Uintptrp(key, val)
case []uintptr:
return Uintptrs(key, val)
case time.Time:
return Time(key, val)
+ case *time.Time:
+ return Timep(key, val)
case []time.Time:
return Times(key, val)
case time.Duration:
return Duration(key, val)
+ case *time.Duration:
+ return Durationp(key, val)
case []time.Duration:
return Durations(key, val)
case error:
diff --git a/vendor/go.uber.org/zap/glide.lock b/vendor/go.uber.org/zap/glide.lock
deleted file mode 100644
index 881b462c0..000000000
--- a/vendor/go.uber.org/zap/glide.lock
+++ /dev/null
@@ -1,76 +0,0 @@
-hash: f073ba522c06c88ea3075bde32a8aaf0969a840a66cab6318a0897d141ffee92
-updated: 2017-07-22T18:06:49.598185334-07:00
-imports:
-- name: go.uber.org/atomic
- version: 4e336646b2ef9fc6e47be8e21594178f98e5ebcf
-- name: go.uber.org/multierr
- version: 3c4937480c32f4c13a875a1829af76c98ca3d40a
-testImports:
-- name: github.com/apex/log
- version: d9b960447bfa720077b2da653cc79e533455b499
- subpackages:
- - handlers/json
-- name: github.com/axw/gocov
- version: 3a69a0d2a4ef1f263e2d92b041a69593d6964fe8
- subpackages:
- - gocov
-- name: github.com/davecgh/go-spew
- version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9
- subpackages:
- - spew
-- name: github.com/fatih/color
- version: 62e9147c64a1ed519147b62a56a14e83e2be02c1
-- name: github.com/go-kit/kit
- version: e10f5bf035be9af21fd5b2fb4469d5716c6ab07d
- subpackages:
- - log
-- name: github.com/go-logfmt/logfmt
- version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5
-- name: github.com/go-stack/stack
- version: 54be5f394ed2c3e19dac9134a40a95ba5a017f7b
-- name: github.com/golang/lint
- version: c5fb716d6688a859aae56d26d3e6070808df29f7
- subpackages:
- - golint
-- name: github.com/kr/logfmt
- version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
-- name: github.com/mattn/go-colorable
- version: 3fa8c76f9daed4067e4a806fb7e4dc86455c6d6a
-- name: github.com/mattn/go-isatty
- version: fc9e8d8ef48496124e79ae0df75490096eccf6fe
-- name: github.com/mattn/goveralls
- version: 6efce81852ad1b7567c17ad71b03aeccc9dd9ae0
-- name: github.com/pborman/uuid
- version: e790cca94e6cc75c7064b1332e63811d4aae1a53
-- name: github.com/pkg/errors
- version: 645ef00459ed84a119197bfb8d8205042c6df63d
-- name: github.com/pmezard/go-difflib
- version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
- subpackages:
- - difflib
-- name: github.com/rs/zerolog
- version: eed4c2b94d945e0b2456ad6aa518a443986b5f22
-- name: github.com/satori/go.uuid
- version: 5bf94b69c6b68ee1b541973bb8e1144db23a194b
-- name: github.com/sirupsen/logrus
- version: 7dd06bf38e1e13df288d471a57d5adbac106be9e
-- name: github.com/stretchr/testify
- version: f6abca593680b2315d2075e0f5e2a9751e3f431a
- subpackages:
- - assert
- - require
-- name: go.pedge.io/lion
- version: 87958e8713f1fa138d993087133b97e976642159
-- name: golang.org/x/sys
- version: c4489faa6e5ab84c0ef40d6ee878f7a030281f0f
- subpackages:
- - unix
-- name: golang.org/x/tools
- version: 496819729719f9d07692195e0a94d6edd2251389
- subpackages:
- - cover
-- name: gopkg.in/inconshreveable/log15.v2
- version: b105bd37f74e5d9dc7b6ad7806715c7a2b83fd3f
- subpackages:
- - stack
- - term
diff --git a/vendor/go.uber.org/zap/glide.yaml b/vendor/go.uber.org/zap/glide.yaml
index 94412594c..8e1d05e9a 100644
--- a/vendor/go.uber.org/zap/glide.yaml
+++ b/vendor/go.uber.org/zap/glide.yaml
@@ -22,12 +22,11 @@ testImport:
- package: github.com/mattn/goveralls
- package: github.com/pborman/uuid
- package: github.com/pkg/errors
-- package: go.pedge.io/lion
- package: github.com/rs/zerolog
- package: golang.org/x/tools
subpackages:
- cover
-- package: github.com/golang/lint
+- package: golang.org/x/lint
subpackages:
- golint
- package: github.com/axw/gocov
diff --git a/vendor/go.uber.org/zap/go.mod b/vendor/go.uber.org/zap/go.mod
new file mode 100644
index 000000000..1fb6bba0b
--- /dev/null
+++ b/vendor/go.uber.org/zap/go.mod
@@ -0,0 +1,11 @@
+module go.uber.org/zap
+
+go 1.13
+
+require (
+ github.com/pkg/errors v0.8.1
+ github.com/stretchr/testify v1.4.0
+ go.uber.org/atomic v1.5.0
+ go.uber.org/multierr v1.3.0
+ golang.org/x/lint v0.0.0-20190930215403-16217165b5de
+)
diff --git a/vendor/go.uber.org/zap/go.sum b/vendor/go.uber.org/zap/go.sum
new file mode 100644
index 000000000..9ff6735d8
--- /dev/null
+++ b/vendor/go.uber.org/zap/go.sum
@@ -0,0 +1,56 @@
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+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/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/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+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/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/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c h1:IGkKhmfzcztjm6gYkykvu/NiS8kaqbCWAEWWAyf8J5U=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+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=
diff --git a/vendor/go.uber.org/atomic/tools.go b/vendor/go.uber.org/zap/tools.go
similarity index 95%
rename from vendor/go.uber.org/atomic/tools.go
rename to vendor/go.uber.org/zap/tools.go
index 654f5b2fe..2b6366bea 100644
--- a/vendor/go.uber.org/atomic/tools.go
+++ b/vendor/go.uber.org/zap/tools.go
@@ -20,9 +20,9 @@
// +build tools
-package atomic
+package zap
import (
- // Tools used during development.
+ // Tools we use during development.
_ "golang.org/x/lint/golint"
)
diff --git a/vendor/go.uber.org/zap/zapcore/encoder.go b/vendor/go.uber.org/zap/zapcore/encoder.go
index f0509522b..5e0a69be5 100644
--- a/vendor/go.uber.org/zap/zapcore/encoder.go
+++ b/vendor/go.uber.org/zap/zapcore/encoder.go
@@ -31,6 +31,9 @@ import (
// behavior.
const DefaultLineEnding = "\n"
+// OmitKey defines the key to use when callers want to remove a key from log output.
+const OmitKey = ""
+
// A LevelEncoder serializes a Level to a primitive type.
type LevelEncoder func(Level, PrimitiveArrayEncoder)
@@ -115,11 +118,30 @@ func ISO8601TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02T15:04:05.000Z0700"))
}
-// UnmarshalText unmarshals text to a TimeEncoder. "iso8601" and "ISO8601" are
-// unmarshaled to ISO8601TimeEncoder, "millis" is unmarshaled to
-// EpochMillisTimeEncoder, and anything else is unmarshaled to EpochTimeEncoder.
+// RFC3339TimeEncoder serializes a time.Time to an RFC3339-formatted string.
+func RFC3339TimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ enc.AppendString(t.Format(time.RFC3339))
+}
+
+// RFC3339NanoTimeEncoder serializes a time.Time to an RFC3339-formatted string
+// with nanosecond precision.
+func RFC3339NanoTimeEncoder(t time.Time, enc PrimitiveArrayEncoder) {
+ enc.AppendString(t.Format(time.RFC3339Nano))
+}
+
+// UnmarshalText unmarshals text to a TimeEncoder.
+// "rfc3339nano" and "RFC3339Nano" are unmarshaled to RFC3339NanoTimeEncoder.
+// "rfc3339" and "RFC3339" are unmarshaled to RFC3339TimeEncoder.
+// "iso8601" and "ISO8601" are unmarshaled to ISO8601TimeEncoder.
+// "millis" is unmarshaled to EpochMillisTimeEncoder.
+// "nanos" is unmarshaled to EpochNanosEncoder.
+// Anything else is unmarshaled to EpochTimeEncoder.
func (e *TimeEncoder) UnmarshalText(text []byte) error {
switch string(text) {
+ case "rfc3339nano", "RFC3339Nano":
+ *e = RFC3339NanoTimeEncoder
+ case "rfc3339", "RFC3339":
+ *e = RFC3339TimeEncoder
case "iso8601", "ISO8601":
*e = ISO8601TimeEncoder
case "millis":
@@ -272,8 +294,8 @@ type ObjectEncoder interface {
AddUint8(key string, value uint8)
AddUintptr(key string, value uintptr)
- // AddReflected uses reflection to serialize arbitrary objects, so it's slow
- // and allocation-heavy.
+ // AddReflected uses reflection to serialize arbitrary objects, so it can be
+ // slow and allocation-heavy.
AddReflected(key string, value interface{}) error
// OpenNamespace opens an isolated namespace where all subsequent fields will
// be added. Applications can use namespaces to prevent key collisions when
@@ -343,6 +365,7 @@ type Encoder interface {
Clone() Encoder
// EncodeEntry encodes an entry and fields, along with any accumulated
- // context, into a byte buffer and returns it.
+ // context, into a byte buffer and returns it. Any fields that are empty,
+ // including fields on the `Entry` type, should be omitted.
EncodeEntry(Entry, []Field) (*buffer.Buffer, error)
}
diff --git a/vendor/go.uber.org/zap/zapcore/entry.go b/vendor/go.uber.org/zap/zapcore/entry.go
index 7d9893f33..8273abdf0 100644
--- a/vendor/go.uber.org/zap/zapcore/entry.go
+++ b/vendor/go.uber.org/zap/zapcore/entry.go
@@ -136,7 +136,8 @@ func (ec EntryCaller) TrimmedPath() string {
// An Entry represents a complete log message. The entry's structured context
// is already serialized, but the log level, time, message, and call site
-// information are available for inspection and modification.
+// information are available for inspection and modification. Any fields left
+// empty will be omitted when encoding.
//
// Entries are pooled, so any functions that accept them MUST be careful not to
// retain references to them.
diff --git a/vendor/go.uber.org/zap/zapcore/json_encoder.go b/vendor/go.uber.org/zap/zapcore/json_encoder.go
index 9aec4eada..56256be8d 100644
--- a/vendor/go.uber.org/zap/zapcore/json_encoder.go
+++ b/vendor/go.uber.org/zap/zapcore/json_encoder.go
@@ -145,15 +145,29 @@ func (enc *jsonEncoder) resetReflectBuf() {
}
}
-func (enc *jsonEncoder) AddReflected(key string, obj interface{}) error {
+var nullLiteralBytes = []byte("null")
+
+// Only invoke the standard JSON encoder if there is actually something to
+// encode; otherwise write JSON null literal directly.
+func (enc *jsonEncoder) encodeReflected(obj interface{}) ([]byte, error) {
+ if obj == nil {
+ return nullLiteralBytes, nil
+ }
enc.resetReflectBuf()
- err := enc.reflectEnc.Encode(obj)
+ if err := enc.reflectEnc.Encode(obj); err != nil {
+ return nil, err
+ }
+ enc.reflectBuf.TrimNewline()
+ return enc.reflectBuf.Bytes(), nil
+}
+
+func (enc *jsonEncoder) AddReflected(key string, obj interface{}) error {
+ valueBytes, err := enc.encodeReflected(obj)
if err != nil {
return err
}
- enc.reflectBuf.TrimNewline()
enc.addKey(key)
- _, err = enc.buf.Write(enc.reflectBuf.Bytes())
+ _, err = enc.buf.Write(valueBytes)
return err
}
@@ -236,14 +250,12 @@ func (enc *jsonEncoder) AppendInt64(val int64) {
}
func (enc *jsonEncoder) AppendReflected(val interface{}) error {
- enc.resetReflectBuf()
- err := enc.reflectEnc.Encode(val)
+ valueBytes, err := enc.encodeReflected(val)
if err != nil {
return err
}
- enc.reflectBuf.TrimNewline()
enc.addElementSeparator()
- _, err = enc.buf.Write(enc.reflectBuf.Bytes())
+ _, err = enc.buf.Write(valueBytes)
return err
}
diff --git a/vendor/golang.org/x/oauth2/README.md b/vendor/golang.org/x/oauth2/README.md
index 0f443e693..8cfd6063e 100644
--- a/vendor/golang.org/x/oauth2/README.md
+++ b/vendor/golang.org/x/oauth2/README.md
@@ -16,15 +16,16 @@ Or you can manually git clone the repository to
See godoc for further documentation and examples.
-* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2)
-* [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google)
+* [godoc.org/golang.org/x/oauth2](https://godoc.org/golang.org/x/oauth2)
+* [godoc.org/golang.org/x/oauth2/google](https://godoc.org/golang.org/x/oauth2/google)
## Policy for new packages
-We no longer accept new provider-specific packages in this repo. For
-defining provider endpoints and provider-specific OAuth2 behavior, we
-encourage you to create packages elsewhere. We'll keep the existing
-packages for compatibility.
+We no longer accept new provider-specific packages in this repo if all
+they do is add a single endpoint variable. If you just want to add a
+single endpoint, add it to the
+[godoc.org/golang.org/x/oauth2/endpoints](https://godoc.org/golang.org/x/oauth2/endpoints)
+package.
## Report Issues / Send Patches
diff --git a/vendor/golang.org/x/oauth2/transport.go b/vendor/golang.org/x/oauth2/transport.go
index aa0d34f1e..90657915f 100644
--- a/vendor/golang.org/x/oauth2/transport.go
+++ b/vendor/golang.org/x/oauth2/transport.go
@@ -6,7 +6,7 @@ package oauth2
import (
"errors"
- "io"
+ "log"
"net/http"
"sync"
)
@@ -25,9 +25,6 @@ type Transport struct {
// Base is the base RoundTripper used to make HTTP requests.
// If nil, http.DefaultTransport is used.
Base http.RoundTripper
-
- mu sync.Mutex // guards modReq
- modReq map[*http.Request]*http.Request // original -> modified
}
// RoundTrip authorizes and authenticates the request with an
@@ -52,35 +49,22 @@ func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
req2 := cloneRequest(req) // per RoundTripper contract
token.SetAuthHeader(req2)
- t.setModReq(req, req2)
- res, err := t.base().RoundTrip(req2)
- // req.Body is assumed to have been closed by the base RoundTripper.
+ // req.Body is assumed to be closed by the base RoundTripper.
reqBodyClosed = true
-
- if err != nil {
- t.setModReq(req, nil)
- return nil, err
- }
- res.Body = &onEOFReader{
- rc: res.Body,
- fn: func() { t.setModReq(req, nil) },
- }
- return res, nil
+ return t.base().RoundTrip(req2)
}
-// CancelRequest cancels an in-flight request by closing its connection.
+var cancelOnce sync.Once
+
+// CancelRequest does nothing. It used to be a legacy cancellation mechanism
+// but now only it only logs on first use to warn that it's deprecated.
+//
+// Deprecated: use contexts for cancellation instead.
func (t *Transport) CancelRequest(req *http.Request) {
- type canceler interface {
- CancelRequest(*http.Request)
- }
- if cr, ok := t.base().(canceler); ok {
- t.mu.Lock()
- modReq := t.modReq[req]
- delete(t.modReq, req)
- t.mu.Unlock()
- cr.CancelRequest(modReq)
- }
+ cancelOnce.Do(func() {
+ log.Printf("deprecated: golang.org/x/oauth2: Transport.CancelRequest no longer does anything; use contexts")
+ })
}
func (t *Transport) base() http.RoundTripper {
@@ -90,19 +74,6 @@ func (t *Transport) base() http.RoundTripper {
return http.DefaultTransport
}
-func (t *Transport) setModReq(orig, mod *http.Request) {
- t.mu.Lock()
- defer t.mu.Unlock()
- if t.modReq == nil {
- t.modReq = make(map[*http.Request]*http.Request)
- }
- if mod == nil {
- delete(t.modReq, orig)
- } else {
- t.modReq[orig] = mod
- }
-}
-
// cloneRequest returns a clone of the provided *http.Request.
// The clone is a shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
@@ -116,29 +87,3 @@ func cloneRequest(r *http.Request) *http.Request {
}
return r2
}
-
-type onEOFReader struct {
- rc io.ReadCloser
- fn func()
-}
-
-func (r *onEOFReader) Read(p []byte) (n int, err error) {
- n, err = r.rc.Read(p)
- if err == io.EOF {
- r.runFunc()
- }
- return
-}
-
-func (r *onEOFReader) Close() error {
- err := r.rc.Close()
- r.runFunc()
- return err
-}
-
-func (r *onEOFReader) runFunc() {
- if fn := r.fn; fn != nil {
- fn()
- r.fn = nil
- }
-}
diff --git a/vendor/golang.org/x/time/rate/rate.go b/vendor/golang.org/x/time/rate/rate.go
index 563f70429..a114b1aa5 100644
--- a/vendor/golang.org/x/time/rate/rate.go
+++ b/vendor/golang.org/x/time/rate/rate.go
@@ -196,7 +196,7 @@ func (lim *Limiter) Reserve() *Reservation {
// ReserveN returns a Reservation that indicates how long the caller must wait before n events happen.
// The Limiter takes this Reservation into account when allowing future events.
-// ReserveN returns false if n exceeds the Limiter's burst size.
+// The returned Reservation’s OK() method returns false if n exceeds the Limiter's burst size.
// Usage example:
// r := lim.ReserveN(time.Now(), 1)
// if !r.OK() {
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 000000000..8c9977355
--- /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 000000000..57eaf6faa
--- /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 000000000..ea56b724e
--- /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 000000000..2856df137
--- /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 000000000..be9814346
--- /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/ast/inspector/inspector.go b/vendor/golang.org/x/tools/go/ast/inspector/inspector.go
new file mode 100644
index 000000000..af5e17fee
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/ast/inspector/inspector.go
@@ -0,0 +1,186 @@
+// 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 inspector provides helper functions for traversal over the
+// syntax trees of a package, including node filtering by type, and
+// materialization of the traversal stack.
+//
+// During construction, the inspector does a complete traversal and
+// builds a list of push/pop events and their node type. Subsequent
+// method calls that request a traversal scan this list, rather than walk
+// the AST, and perform type filtering using efficient bit sets.
+//
+// Experiments suggest the inspector's traversals are about 2.5x faster
+// than ast.Inspect, but it may take around 5 traversals for this
+// benefit to amortize the inspector's construction cost.
+// If efficiency is the primary concern, do not use Inspector for
+// one-off traversals.
+package inspector
+
+// There are four orthogonal features in a traversal:
+// 1 type filtering
+// 2 pruning
+// 3 postorder calls to f
+// 4 stack
+// Rather than offer all of them in the API,
+// only a few combinations are exposed:
+// - Preorder is the fastest and has fewest features,
+// but is the most commonly needed traversal.
+// - Nodes and WithStack both provide pruning and postorder calls,
+// even though few clients need it, because supporting two versions
+// is not justified.
+// More combinations could be supported by expressing them as
+// wrappers around a more generic traversal, but this was measured
+// and found to degrade performance significantly (30%).
+
+import (
+ "go/ast"
+)
+
+// An Inspector provides methods for inspecting
+// (traversing) the syntax trees of a package.
+type Inspector struct {
+ events []event
+}
+
+// New returns an Inspector for the specified syntax trees.
+func New(files []*ast.File) *Inspector {
+ return &Inspector{traverse(files)}
+}
+
+// An event represents a push or a pop
+// of an ast.Node during a traversal.
+type event struct {
+ node ast.Node
+ typ uint64 // typeOf(node)
+ index int // 1 + index of corresponding pop event, or 0 if this is a pop
+}
+
+// Preorder visits all the nodes of the files supplied to New in
+// depth-first order. It calls f(n) for each node n before it visits
+// n's children.
+//
+// The types argument, if non-empty, enables type-based filtering of
+// events. The function f if is called only for nodes whose type
+// matches an element of the types slice.
+func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) {
+ // Because it avoids postorder calls to f, and the pruning
+ // check, Preorder is almost twice as fast as Nodes. The two
+ // features seem to contribute similar slowdowns (~1.4x each).
+
+ mask := maskOf(types)
+ for i := 0; i < len(in.events); {
+ ev := in.events[i]
+ if ev.typ&mask != 0 {
+ if ev.index > 0 {
+ f(ev.node)
+ }
+ }
+ i++
+ }
+}
+
+// Nodes visits the nodes of the files supplied to New in depth-first
+// order. It calls f(n, true) for each node n before it visits n's
+// children. If f returns true, Nodes invokes f recursively for each
+// of the non-nil children of the node, followed by a call of
+// f(n, false).
+//
+// The types argument, if non-empty, enables type-based filtering of
+// events. The function f if is called only for nodes whose type
+// matches an element of the types slice.
+func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proceed bool)) {
+ mask := maskOf(types)
+ for i := 0; i < len(in.events); {
+ ev := in.events[i]
+ if ev.typ&mask != 0 {
+ if ev.index > 0 {
+ // push
+ if !f(ev.node, true) {
+ i = ev.index // jump to corresponding pop + 1
+ continue
+ }
+ } else {
+ // pop
+ f(ev.node, false)
+ }
+ }
+ i++
+ }
+}
+
+// WithStack visits nodes in a similar manner to Nodes, but it
+// supplies each call to f an additional argument, the current
+// traversal stack. The stack's first element is the outermost node,
+// an *ast.File; its last is the innermost, n.
+func (in *Inspector) WithStack(types []ast.Node, f func(n ast.Node, push bool, stack []ast.Node) (proceed bool)) {
+ mask := maskOf(types)
+ var stack []ast.Node
+ for i := 0; i < len(in.events); {
+ ev := in.events[i]
+ if ev.index > 0 {
+ // push
+ stack = append(stack, ev.node)
+ if ev.typ&mask != 0 {
+ if !f(ev.node, true, stack) {
+ i = ev.index
+ stack = stack[:len(stack)-1]
+ continue
+ }
+ }
+ } else {
+ // pop
+ if ev.typ&mask != 0 {
+ f(ev.node, false, stack)
+ }
+ stack = stack[:len(stack)-1]
+ }
+ i++
+ }
+}
+
+// traverse builds the table of events representing a traversal.
+func traverse(files []*ast.File) []event {
+ // Preallocate approximate number of events
+ // based on source file extent.
+ // This makes traverse faster by 4x (!).
+ var extent int
+ for _, f := range files {
+ extent += int(f.End() - f.Pos())
+ }
+ // This estimate is based on the net/http package.
+ capacity := extent * 33 / 100
+ if capacity > 1e6 {
+ capacity = 1e6 // impose some reasonable maximum
+ }
+ events := make([]event, 0, capacity)
+
+ var stack []event
+ for _, f := range files {
+ ast.Inspect(f, func(n ast.Node) bool {
+ if n != nil {
+ // push
+ ev := event{
+ node: n,
+ typ: typeOf(n),
+ index: len(events), // push event temporarily holds own index
+ }
+ stack = append(stack, ev)
+ events = append(events, ev)
+ } else {
+ // pop
+ ev := stack[len(stack)-1]
+ stack = stack[:len(stack)-1]
+
+ events[ev.index].index = len(events) + 1 // make push refer to pop
+
+ ev.index = 0 // turn ev into a pop event
+ events = append(events, ev)
+ }
+ return true
+ })
+ }
+
+ return events
+}
diff --git a/vendor/golang.org/x/tools/go/ast/inspector/typeof.go b/vendor/golang.org/x/tools/go/ast/inspector/typeof.go
new file mode 100644
index 000000000..d61301b13
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/ast/inspector/typeof.go
@@ -0,0 +1,216 @@
+package inspector
+
+// This file defines func typeOf(ast.Node) uint64.
+//
+// The initial map-based implementation was too slow;
+// see https://go-review.googlesource.com/c/tools/+/135655/1/go/ast/inspector/inspector.go#196
+
+import "go/ast"
+
+const (
+ nArrayType = iota
+ nAssignStmt
+ nBadDecl
+ nBadExpr
+ nBadStmt
+ nBasicLit
+ nBinaryExpr
+ nBlockStmt
+ nBranchStmt
+ nCallExpr
+ nCaseClause
+ nChanType
+ nCommClause
+ nComment
+ nCommentGroup
+ nCompositeLit
+ nDeclStmt
+ nDeferStmt
+ nEllipsis
+ nEmptyStmt
+ nExprStmt
+ nField
+ nFieldList
+ nFile
+ nForStmt
+ nFuncDecl
+ nFuncLit
+ nFuncType
+ nGenDecl
+ nGoStmt
+ nIdent
+ nIfStmt
+ nImportSpec
+ nIncDecStmt
+ nIndexExpr
+ nInterfaceType
+ nKeyValueExpr
+ nLabeledStmt
+ nMapType
+ nPackage
+ nParenExpr
+ nRangeStmt
+ nReturnStmt
+ nSelectStmt
+ nSelectorExpr
+ nSendStmt
+ nSliceExpr
+ nStarExpr
+ nStructType
+ nSwitchStmt
+ nTypeAssertExpr
+ nTypeSpec
+ nTypeSwitchStmt
+ nUnaryExpr
+ nValueSpec
+)
+
+// typeOf returns a distinct single-bit value that represents the type of n.
+//
+// Various implementations were benchmarked with BenchmarkNewInspector:
+// GOGC=off
+// - type switch 4.9-5.5ms 2.1ms
+// - binary search over a sorted list of types 5.5-5.9ms 2.5ms
+// - linear scan, frequency-ordered list 5.9-6.1ms 2.7ms
+// - linear scan, unordered list 6.4ms 2.7ms
+// - hash table 6.5ms 3.1ms
+// A perfect hash seemed like overkill.
+//
+// The compiler's switch statement is the clear winner
+// as it produces a binary tree in code,
+// with constant conditions and good branch prediction.
+// (Sadly it is the most verbose in source code.)
+// Binary search suffered from poor branch prediction.
+//
+func typeOf(n ast.Node) uint64 {
+ // Fast path: nearly half of all nodes are identifiers.
+ if _, ok := n.(*ast.Ident); ok {
+ return 1 << nIdent
+ }
+
+ // These cases include all nodes encountered by ast.Inspect.
+ switch n.(type) {
+ case *ast.ArrayType:
+ return 1 << nArrayType
+ case *ast.AssignStmt:
+ return 1 << nAssignStmt
+ case *ast.BadDecl:
+ return 1 << nBadDecl
+ case *ast.BadExpr:
+ return 1 << nBadExpr
+ case *ast.BadStmt:
+ return 1 << nBadStmt
+ case *ast.BasicLit:
+ return 1 << nBasicLit
+ case *ast.BinaryExpr:
+ return 1 << nBinaryExpr
+ case *ast.BlockStmt:
+ return 1 << nBlockStmt
+ case *ast.BranchStmt:
+ return 1 << nBranchStmt
+ case *ast.CallExpr:
+ return 1 << nCallExpr
+ case *ast.CaseClause:
+ return 1 << nCaseClause
+ case *ast.ChanType:
+ return 1 << nChanType
+ case *ast.CommClause:
+ return 1 << nCommClause
+ case *ast.Comment:
+ return 1 << nComment
+ case *ast.CommentGroup:
+ return 1 << nCommentGroup
+ case *ast.CompositeLit:
+ return 1 << nCompositeLit
+ case *ast.DeclStmt:
+ return 1 << nDeclStmt
+ case *ast.DeferStmt:
+ return 1 << nDeferStmt
+ case *ast.Ellipsis:
+ return 1 << nEllipsis
+ case *ast.EmptyStmt:
+ return 1 << nEmptyStmt
+ case *ast.ExprStmt:
+ return 1 << nExprStmt
+ case *ast.Field:
+ return 1 << nField
+ case *ast.FieldList:
+ return 1 << nFieldList
+ case *ast.File:
+ return 1 << nFile
+ case *ast.ForStmt:
+ return 1 << nForStmt
+ case *ast.FuncDecl:
+ return 1 << nFuncDecl
+ case *ast.FuncLit:
+ return 1 << nFuncLit
+ case *ast.FuncType:
+ return 1 << nFuncType
+ case *ast.GenDecl:
+ return 1 << nGenDecl
+ case *ast.GoStmt:
+ return 1 << nGoStmt
+ case *ast.Ident:
+ return 1 << nIdent
+ case *ast.IfStmt:
+ return 1 << nIfStmt
+ case *ast.ImportSpec:
+ return 1 << nImportSpec
+ case *ast.IncDecStmt:
+ return 1 << nIncDecStmt
+ case *ast.IndexExpr:
+ return 1 << nIndexExpr
+ case *ast.InterfaceType:
+ return 1 << nInterfaceType
+ case *ast.KeyValueExpr:
+ return 1 << nKeyValueExpr
+ case *ast.LabeledStmt:
+ return 1 << nLabeledStmt
+ case *ast.MapType:
+ return 1 << nMapType
+ case *ast.Package:
+ return 1 << nPackage
+ case *ast.ParenExpr:
+ return 1 << nParenExpr
+ case *ast.RangeStmt:
+ return 1 << nRangeStmt
+ case *ast.ReturnStmt:
+ return 1 << nReturnStmt
+ case *ast.SelectStmt:
+ return 1 << nSelectStmt
+ case *ast.SelectorExpr:
+ return 1 << nSelectorExpr
+ case *ast.SendStmt:
+ return 1 << nSendStmt
+ case *ast.SliceExpr:
+ return 1 << nSliceExpr
+ case *ast.StarExpr:
+ return 1 << nStarExpr
+ case *ast.StructType:
+ return 1 << nStructType
+ case *ast.SwitchStmt:
+ return 1 << nSwitchStmt
+ case *ast.TypeAssertExpr:
+ return 1 << nTypeAssertExpr
+ case *ast.TypeSpec:
+ return 1 << nTypeSpec
+ case *ast.TypeSwitchStmt:
+ return 1 << nTypeSwitchStmt
+ case *ast.UnaryExpr:
+ return 1 << nUnaryExpr
+ case *ast.ValueSpec:
+ return 1 << nValueSpec
+ }
+ return 0
+}
+
+func maskOf(nodes []ast.Node) uint64 {
+ if nodes == nil {
+ return 1<<64 - 1 // match all node types
+ }
+ var mask uint64
+ for _, n := range nodes {
+ mask |= typeOf(n)
+ }
+ return mask
+}
diff --git a/vendor/golang.org/x/tools/go/buildutil/allpackages.go b/vendor/golang.org/x/tools/go/buildutil/allpackages.go
new file mode 100644
index 000000000..c0cb03e7b
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/buildutil/allpackages.go
@@ -0,0 +1,198 @@
+// Copyright 2014 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 buildutil provides utilities related to the go/build
+// package in the standard library.
+//
+// All I/O is done via the build.Context file system interface, which must
+// be concurrency-safe.
+package buildutil // import "golang.org/x/tools/go/buildutil"
+
+import (
+ "go/build"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+)
+
+// AllPackages returns the package path of each Go package in any source
+// directory of the specified build context (e.g. $GOROOT or an element
+// of $GOPATH). Errors are ignored. The results are sorted.
+// All package paths are canonical, and thus may contain "/vendor/".
+//
+// The result may include import paths for directories that contain no
+// *.go files, such as "archive" (in $GOROOT/src).
+//
+// All I/O is done via the build.Context file system interface,
+// which must be concurrency-safe.
+//
+func AllPackages(ctxt *build.Context) []string {
+ var list []string
+ ForEachPackage(ctxt, func(pkg string, _ error) {
+ list = append(list, pkg)
+ })
+ sort.Strings(list)
+ return list
+}
+
+// ForEachPackage calls the found function with the package path of
+// each Go package it finds in any source directory of the specified
+// build context (e.g. $GOROOT or an element of $GOPATH).
+// All package paths are canonical, and thus may contain "/vendor/".
+//
+// If the package directory exists but could not be read, the second
+// argument to the found function provides the error.
+//
+// All I/O is done via the build.Context file system interface,
+// which must be concurrency-safe.
+//
+func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) {
+ ch := make(chan item)
+
+ var wg sync.WaitGroup
+ for _, root := range ctxt.SrcDirs() {
+ root := root
+ wg.Add(1)
+ go func() {
+ allPackages(ctxt, root, ch)
+ wg.Done()
+ }()
+ }
+ go func() {
+ wg.Wait()
+ close(ch)
+ }()
+
+ // All calls to found occur in the caller's goroutine.
+ for i := range ch {
+ found(i.importPath, i.err)
+ }
+}
+
+type item struct {
+ importPath string
+ err error // (optional)
+}
+
+// We use a process-wide counting semaphore to limit
+// the number of parallel calls to ReadDir.
+var ioLimit = make(chan bool, 20)
+
+func allPackages(ctxt *build.Context, root string, ch chan<- item) {
+ root = filepath.Clean(root) + string(os.PathSeparator)
+
+ var wg sync.WaitGroup
+
+ var walkDir func(dir string)
+ walkDir = func(dir string) {
+ // Avoid .foo, _foo, and testdata directory trees.
+ base := filepath.Base(dir)
+ if base == "" || base[0] == '.' || base[0] == '_' || base == "testdata" {
+ return
+ }
+
+ pkg := filepath.ToSlash(strings.TrimPrefix(dir, root))
+
+ // Prune search if we encounter any of these import paths.
+ switch pkg {
+ case "builtin":
+ return
+ }
+
+ ioLimit <- true
+ files, err := ReadDir(ctxt, dir)
+ <-ioLimit
+ if pkg != "" || err != nil {
+ ch <- item{pkg, err}
+ }
+ for _, fi := range files {
+ fi := fi
+ if fi.IsDir() {
+ wg.Add(1)
+ go func() {
+ walkDir(filepath.Join(dir, fi.Name()))
+ wg.Done()
+ }()
+ }
+ }
+ }
+
+ walkDir(root)
+ wg.Wait()
+}
+
+// ExpandPatterns returns the set of packages matched by patterns,
+// which may have the following forms:
+//
+// golang.org/x/tools/cmd/guru # a single package
+// golang.org/x/tools/... # all packages beneath dir
+// ... # the entire workspace.
+//
+// Order is significant: a pattern preceded by '-' removes matching
+// packages from the set. For example, these patterns match all encoding
+// packages except encoding/xml:
+//
+// encoding/... -encoding/xml
+//
+// A trailing slash in a pattern is ignored. (Path components of Go
+// package names are separated by slash, not the platform's path separator.)
+//
+func ExpandPatterns(ctxt *build.Context, patterns []string) map[string]bool {
+ // TODO(adonovan): support other features of 'go list':
+ // - "std"/"cmd"/"all" meta-packages
+ // - "..." not at the end of a pattern
+ // - relative patterns using "./" or "../" prefix
+
+ pkgs := make(map[string]bool)
+ doPkg := func(pkg string, neg bool) {
+ if neg {
+ delete(pkgs, pkg)
+ } else {
+ pkgs[pkg] = true
+ }
+ }
+
+ // Scan entire workspace if wildcards are present.
+ // TODO(adonovan): opt: scan only the necessary subtrees of the workspace.
+ var all []string
+ for _, arg := range patterns {
+ if strings.HasSuffix(arg, "...") {
+ all = AllPackages(ctxt)
+ break
+ }
+ }
+
+ for _, arg := range patterns {
+ if arg == "" {
+ continue
+ }
+
+ neg := arg[0] == '-'
+ if neg {
+ arg = arg[1:]
+ }
+
+ if arg == "..." {
+ // ... matches all packages
+ for _, pkg := range all {
+ doPkg(pkg, neg)
+ }
+ } else if dir := strings.TrimSuffix(arg, "/..."); dir != arg {
+ // dir/... matches all packages beneath dir
+ for _, pkg := range all {
+ if strings.HasPrefix(pkg, dir) &&
+ (len(pkg) == len(dir) || pkg[len(dir)] == '/') {
+ doPkg(pkg, neg)
+ }
+ }
+ } else {
+ // single package
+ doPkg(strings.TrimSuffix(arg, "/"), neg)
+ }
+ }
+
+ return pkgs
+}
diff --git a/vendor/golang.org/x/tools/go/buildutil/fakecontext.go b/vendor/golang.org/x/tools/go/buildutil/fakecontext.go
new file mode 100644
index 000000000..8b7f06673
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/buildutil/fakecontext.go
@@ -0,0 +1,109 @@
+package buildutil
+
+import (
+ "fmt"
+ "go/build"
+ "io"
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
+ "sort"
+ "strings"
+ "time"
+)
+
+// FakeContext returns a build.Context for the fake file tree specified
+// by pkgs, which maps package import paths to a mapping from file base
+// names to contents.
+//
+// The fake Context has a GOROOT of "/go" and no GOPATH, and overrides
+// the necessary file access methods to read from memory instead of the
+// real file system.
+//
+// Unlike a real file tree, the fake one has only two levels---packages
+// and files---so ReadDir("/go/src/") returns all packages under
+// /go/src/ including, for instance, "math" and "math/big".
+// ReadDir("/go/src/math/big") would return all the files in the
+// "math/big" package.
+//
+func FakeContext(pkgs map[string]map[string]string) *build.Context {
+ clean := func(filename string) string {
+ f := path.Clean(filepath.ToSlash(filename))
+ // Removing "/go/src" while respecting segment
+ // boundaries has this unfortunate corner case:
+ if f == "/go/src" {
+ return ""
+ }
+ return strings.TrimPrefix(f, "/go/src/")
+ }
+
+ ctxt := build.Default // copy
+ ctxt.GOROOT = "/go"
+ ctxt.GOPATH = ""
+ ctxt.Compiler = "gc"
+ ctxt.IsDir = func(dir string) bool {
+ dir = clean(dir)
+ if dir == "" {
+ return true // needed by (*build.Context).SrcDirs
+ }
+ return pkgs[dir] != nil
+ }
+ ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) {
+ dir = clean(dir)
+ var fis []os.FileInfo
+ if dir == "" {
+ // enumerate packages
+ for importPath := range pkgs {
+ fis = append(fis, fakeDirInfo(importPath))
+ }
+ } else {
+ // enumerate files of package
+ for basename := range pkgs[dir] {
+ fis = append(fis, fakeFileInfo(basename))
+ }
+ }
+ sort.Sort(byName(fis))
+ return fis, nil
+ }
+ ctxt.OpenFile = func(filename string) (io.ReadCloser, error) {
+ filename = clean(filename)
+ dir, base := path.Split(filename)
+ content, ok := pkgs[path.Clean(dir)][base]
+ if !ok {
+ return nil, fmt.Errorf("file not found: %s", filename)
+ }
+ return ioutil.NopCloser(strings.NewReader(content)), nil
+ }
+ ctxt.IsAbsPath = func(path string) bool {
+ path = filepath.ToSlash(path)
+ // Don't rely on the default (filepath.Path) since on
+ // Windows, it reports virtual paths as non-absolute.
+ return strings.HasPrefix(path, "/")
+ }
+ return &ctxt
+}
+
+type byName []os.FileInfo
+
+func (s byName) Len() int { return len(s) }
+func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
+
+type fakeFileInfo string
+
+func (fi fakeFileInfo) Name() string { return string(fi) }
+func (fakeFileInfo) Sys() interface{} { return nil }
+func (fakeFileInfo) ModTime() time.Time { return time.Time{} }
+func (fakeFileInfo) IsDir() bool { return false }
+func (fakeFileInfo) Size() int64 { return 0 }
+func (fakeFileInfo) Mode() os.FileMode { return 0644 }
+
+type fakeDirInfo string
+
+func (fd fakeDirInfo) Name() string { return string(fd) }
+func (fakeDirInfo) Sys() interface{} { return nil }
+func (fakeDirInfo) ModTime() time.Time { return time.Time{} }
+func (fakeDirInfo) IsDir() bool { return true }
+func (fakeDirInfo) Size() int64 { return 0 }
+func (fakeDirInfo) Mode() os.FileMode { return 0755 }
diff --git a/vendor/golang.org/x/tools/go/buildutil/overlay.go b/vendor/golang.org/x/tools/go/buildutil/overlay.go
new file mode 100644
index 000000000..8e239086b
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/buildutil/overlay.go
@@ -0,0 +1,103 @@
+// Copyright 2016 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 buildutil
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "go/build"
+ "io"
+ "io/ioutil"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+// OverlayContext overlays a build.Context with additional files from
+// a map. Files in the map take precedence over other files.
+//
+// In addition to plain string comparison, two file names are
+// considered equal if their base names match and their directory
+// components point at the same directory on the file system. That is,
+// symbolic links are followed for directories, but not files.
+//
+// A common use case for OverlayContext is to allow editors to pass in
+// a set of unsaved, modified files.
+//
+// Currently, only the Context.OpenFile function will respect the
+// overlay. This may change in the future.
+func OverlayContext(orig *build.Context, overlay map[string][]byte) *build.Context {
+ // TODO(dominikh): Implement IsDir, HasSubdir and ReadDir
+
+ rc := func(data []byte) (io.ReadCloser, error) {
+ return ioutil.NopCloser(bytes.NewBuffer(data)), nil
+ }
+
+ copy := *orig // make a copy
+ ctxt := ©
+ ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
+ // Fast path: names match exactly.
+ if content, ok := overlay[path]; ok {
+ return rc(content)
+ }
+
+ // Slow path: check for same file under a different
+ // alias, perhaps due to a symbolic link.
+ for filename, content := range overlay {
+ if sameFile(path, filename) {
+ return rc(content)
+ }
+ }
+
+ return OpenFile(orig, path)
+ }
+ return ctxt
+}
+
+// ParseOverlayArchive parses an archive containing Go files and their
+// contents. The result is intended to be used with OverlayContext.
+//
+//
+// Archive format
+//
+// The archive consists of a series of files. Each file consists of a
+// name, a decimal file size and the file contents, separated by
+// newlines. No newline follows after the file contents.
+func ParseOverlayArchive(archive io.Reader) (map[string][]byte, error) {
+ overlay := make(map[string][]byte)
+ r := bufio.NewReader(archive)
+ for {
+ // Read file name.
+ filename, err := r.ReadString('\n')
+ if err != nil {
+ if err == io.EOF {
+ break // OK
+ }
+ return nil, fmt.Errorf("reading archive file name: %v", err)
+ }
+ filename = filepath.Clean(strings.TrimSpace(filename))
+
+ // Read file size.
+ sz, err := r.ReadString('\n')
+ if err != nil {
+ return nil, fmt.Errorf("reading size of archive file %s: %v", filename, err)
+ }
+ sz = strings.TrimSpace(sz)
+ size, err := strconv.ParseUint(sz, 10, 32)
+ if err != nil {
+ return nil, fmt.Errorf("parsing size of archive file %s: %v", filename, err)
+ }
+
+ // Read file content.
+ content := make([]byte, size)
+ if _, err := io.ReadFull(r, content); err != nil {
+ return nil, fmt.Errorf("reading archive file %s: %v", filename, err)
+ }
+ overlay[filename] = content
+ }
+
+ return overlay, nil
+}
diff --git a/vendor/golang.org/x/tools/go/buildutil/tags.go b/vendor/golang.org/x/tools/go/buildutil/tags.go
new file mode 100644
index 000000000..486606f37
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/buildutil/tags.go
@@ -0,0 +1,75 @@
+package buildutil
+
+// This logic was copied from stringsFlag from $GOROOT/src/cmd/go/build.go.
+
+import "fmt"
+
+const TagsFlagDoc = "a list of `build tags` to consider satisfied during the build. " +
+ "For more information about build tags, see the description of " +
+ "build constraints in the documentation for the go/build package"
+
+// TagsFlag is an implementation of the flag.Value and flag.Getter interfaces that parses
+// a flag value in the same manner as go build's -tags flag and
+// populates a []string slice.
+//
+// See $GOROOT/src/go/build/doc.go for description of build tags.
+// See $GOROOT/src/cmd/go/doc.go for description of 'go build -tags' flag.
+//
+// Example:
+// flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
+type TagsFlag []string
+
+func (v *TagsFlag) Set(s string) error {
+ var err error
+ *v, err = splitQuotedFields(s)
+ if *v == nil {
+ *v = []string{}
+ }
+ return err
+}
+
+func (v *TagsFlag) Get() interface{} { return *v }
+
+func splitQuotedFields(s string) ([]string, error) {
+ // Split fields allowing '' or "" around elements.
+ // Quotes further inside the string do not count.
+ var f []string
+ for len(s) > 0 {
+ for len(s) > 0 && isSpaceByte(s[0]) {
+ s = s[1:]
+ }
+ if len(s) == 0 {
+ break
+ }
+ // Accepted quoted string. No unescaping inside.
+ if s[0] == '"' || s[0] == '\'' {
+ quote := s[0]
+ s = s[1:]
+ i := 0
+ for i < len(s) && s[i] != quote {
+ i++
+ }
+ if i >= len(s) {
+ return nil, fmt.Errorf("unterminated %c string", quote)
+ }
+ f = append(f, s[:i])
+ s = s[i+1:]
+ continue
+ }
+ i := 0
+ for i < len(s) && !isSpaceByte(s[i]) {
+ i++
+ }
+ f = append(f, s[:i])
+ s = s[i:]
+ }
+ return f, nil
+}
+
+func (v *TagsFlag) String() string {
+ return ""
+}
+
+func isSpaceByte(c byte) bool {
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r'
+}
diff --git a/vendor/golang.org/x/tools/go/buildutil/util.go b/vendor/golang.org/x/tools/go/buildutil/util.go
new file mode 100644
index 000000000..fc923d7a7
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/buildutil/util.go
@@ -0,0 +1,212 @@
+// Copyright 2014 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 buildutil
+
+import (
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/parser"
+ "go/token"
+ "io"
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+)
+
+// ParseFile behaves like parser.ParseFile,
+// but uses the build context's file system interface, if any.
+//
+// If file is not absolute (as defined by IsAbsPath), the (dir, file)
+// components are joined using JoinPath; dir must be absolute.
+//
+// The displayPath function, if provided, is used to transform the
+// filename that will be attached to the ASTs.
+//
+// TODO(adonovan): call this from go/loader.parseFiles when the tree thaws.
+//
+func ParseFile(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, file string, mode parser.Mode) (*ast.File, error) {
+ if !IsAbsPath(ctxt, file) {
+ file = JoinPath(ctxt, dir, file)
+ }
+ rd, err := OpenFile(ctxt, file)
+ if err != nil {
+ return nil, err
+ }
+ defer rd.Close() // ignore error
+ if displayPath != nil {
+ file = displayPath(file)
+ }
+ return parser.ParseFile(fset, file, rd, mode)
+}
+
+// ContainingPackage returns the package containing filename.
+//
+// If filename is not absolute, it is interpreted relative to working directory dir.
+// All I/O is via the build context's file system interface, if any.
+//
+// The '...Files []string' fields of the resulting build.Package are not
+// populated (build.FindOnly mode).
+//
+func ContainingPackage(ctxt *build.Context, dir, filename string) (*build.Package, error) {
+ if !IsAbsPath(ctxt, filename) {
+ filename = JoinPath(ctxt, dir, filename)
+ }
+
+ // We must not assume the file tree uses
+ // "/" always,
+ // `\` always,
+ // or os.PathSeparator (which varies by platform),
+ // but to make any progress, we are forced to assume that
+ // paths will not use `\` unless the PathSeparator
+ // is also `\`, thus we can rely on filepath.ToSlash for some sanity.
+
+ dirSlash := path.Dir(filepath.ToSlash(filename)) + "/"
+
+ // We assume that no source root (GOPATH[i] or GOROOT) contains any other.
+ for _, srcdir := range ctxt.SrcDirs() {
+ srcdirSlash := filepath.ToSlash(srcdir) + "/"
+ if importPath, ok := HasSubdir(ctxt, srcdirSlash, dirSlash); ok {
+ return ctxt.Import(importPath, dir, build.FindOnly)
+ }
+ }
+
+ return nil, fmt.Errorf("can't find package containing %s", filename)
+}
+
+// -- Effective methods of file system interface -------------------------
+
+// (go/build.Context defines these as methods, but does not export them.)
+
+// hasSubdir calls ctxt.HasSubdir (if not nil) or else uses
+// the local file system to answer the question.
+func HasSubdir(ctxt *build.Context, root, dir string) (rel string, ok bool) {
+ if f := ctxt.HasSubdir; f != nil {
+ return f(root, dir)
+ }
+
+ // Try using paths we received.
+ if rel, ok = hasSubdir(root, dir); ok {
+ return
+ }
+
+ // Try expanding symlinks and comparing
+ // expanded against unexpanded and
+ // expanded against expanded.
+ rootSym, _ := filepath.EvalSymlinks(root)
+ dirSym, _ := filepath.EvalSymlinks(dir)
+
+ if rel, ok = hasSubdir(rootSym, dir); ok {
+ return
+ }
+ if rel, ok = hasSubdir(root, dirSym); ok {
+ return
+ }
+ return hasSubdir(rootSym, dirSym)
+}
+
+func hasSubdir(root, dir string) (rel string, ok bool) {
+ const sep = string(filepath.Separator)
+ root = filepath.Clean(root)
+ if !strings.HasSuffix(root, sep) {
+ root += sep
+ }
+
+ dir = filepath.Clean(dir)
+ if !strings.HasPrefix(dir, root) {
+ return "", false
+ }
+
+ return filepath.ToSlash(dir[len(root):]), true
+}
+
+// FileExists returns true if the specified file exists,
+// using the build context's file system interface.
+func FileExists(ctxt *build.Context, path string) bool {
+ if ctxt.OpenFile != nil {
+ r, err := ctxt.OpenFile(path)
+ if err != nil {
+ return false
+ }
+ r.Close() // ignore error
+ return true
+ }
+ _, err := os.Stat(path)
+ return err == nil
+}
+
+// OpenFile behaves like os.Open,
+// but uses the build context's file system interface, if any.
+func OpenFile(ctxt *build.Context, path string) (io.ReadCloser, error) {
+ if ctxt.OpenFile != nil {
+ return ctxt.OpenFile(path)
+ }
+ return os.Open(path)
+}
+
+// IsAbsPath behaves like filepath.IsAbs,
+// but uses the build context's file system interface, if any.
+func IsAbsPath(ctxt *build.Context, path string) bool {
+ if ctxt.IsAbsPath != nil {
+ return ctxt.IsAbsPath(path)
+ }
+ return filepath.IsAbs(path)
+}
+
+// JoinPath behaves like filepath.Join,
+// but uses the build context's file system interface, if any.
+func JoinPath(ctxt *build.Context, path ...string) string {
+ if ctxt.JoinPath != nil {
+ return ctxt.JoinPath(path...)
+ }
+ return filepath.Join(path...)
+}
+
+// IsDir behaves like os.Stat plus IsDir,
+// but uses the build context's file system interface, if any.
+func IsDir(ctxt *build.Context, path string) bool {
+ if ctxt.IsDir != nil {
+ return ctxt.IsDir(path)
+ }
+ fi, err := os.Stat(path)
+ return err == nil && fi.IsDir()
+}
+
+// ReadDir behaves like ioutil.ReadDir,
+// but uses the build context's file system interface, if any.
+func ReadDir(ctxt *build.Context, path string) ([]os.FileInfo, error) {
+ if ctxt.ReadDir != nil {
+ return ctxt.ReadDir(path)
+ }
+ return ioutil.ReadDir(path)
+}
+
+// SplitPathList behaves like filepath.SplitList,
+// but uses the build context's file system interface, if any.
+func SplitPathList(ctxt *build.Context, s string) []string {
+ if ctxt.SplitPathList != nil {
+ return ctxt.SplitPathList(s)
+ }
+ return filepath.SplitList(s)
+}
+
+// sameFile returns true if x and y have the same basename and denote
+// the same file.
+//
+func sameFile(x, y string) bool {
+ if path.Clean(x) == path.Clean(y) {
+ return true
+ }
+ if filepath.Base(x) == filepath.Base(y) { // (optimisation)
+ if xi, err := os.Stat(x); err == nil {
+ if yi, err := os.Stat(y); err == nil {
+ return os.SameFile(xi, yi)
+ }
+ }
+ }
+ return false
+}
diff --git a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go
new file mode 100644
index 000000000..dc6177c12
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go
@@ -0,0 +1,117 @@
+// 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 packagesdriver fetches type sizes for go/packages and go/analysis.
+package packagesdriver
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "go/types"
+ "os/exec"
+ "strings"
+
+ "golang.org/x/tools/internal/gocommand"
+)
+
+var debug = false
+
+func GetSizes(ctx context.Context, buildFlags, env []string, gocmdRunner *gocommand.Runner, dir string) (types.Sizes, error) {
+ // TODO(matloob): Clean this up. This code is mostly a copy of packages.findExternalDriver.
+ const toolPrefix = "GOPACKAGESDRIVER="
+ tool := ""
+ for _, env := range env {
+ if val := strings.TrimPrefix(env, toolPrefix); val != env {
+ tool = val
+ }
+ }
+
+ if tool == "" {
+ var err error
+ tool, err = exec.LookPath("gopackagesdriver")
+ if err != nil {
+ // We did not find the driver, so use "go list".
+ tool = "off"
+ }
+ }
+
+ if tool == "off" {
+ return GetSizesGolist(ctx, buildFlags, env, gocmdRunner, dir)
+ }
+
+ req, err := json.Marshal(struct {
+ Command string `json:"command"`
+ Env []string `json:"env"`
+ BuildFlags []string `json:"build_flags"`
+ }{
+ Command: "sizes",
+ Env: env,
+ BuildFlags: buildFlags,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("failed to encode message to driver tool: %v", err)
+ }
+
+ buf := new(bytes.Buffer)
+ cmd := exec.CommandContext(ctx, tool)
+ cmd.Dir = dir
+ cmd.Env = env
+ cmd.Stdin = bytes.NewReader(req)
+ cmd.Stdout = buf
+ cmd.Stderr = new(bytes.Buffer)
+ if err := cmd.Run(); err != nil {
+ return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr)
+ }
+ var response struct {
+ // Sizes, if not nil, is the types.Sizes to use when type checking.
+ Sizes *types.StdSizes
+ }
+ if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
+ return nil, err
+ }
+ return response.Sizes, nil
+}
+
+func GetSizesGolist(ctx context.Context, buildFlags, env []string, gocmdRunner *gocommand.Runner, dir string) (types.Sizes, error) {
+ inv := gocommand.Invocation{
+ Verb: "list",
+ Args: []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"},
+ Env: env,
+ BuildFlags: buildFlags,
+ WorkingDir: dir,
+ }
+ stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv)
+ var goarch, compiler string
+ if rawErr != nil {
+ if strings.Contains(rawErr.Error(), "cannot find main module") {
+ // User's running outside of a module. All bets are off. Get GOARCH and guess compiler is gc.
+ // TODO(matloob): Is this a problem in practice?
+ inv := gocommand.Invocation{
+ Verb: "env",
+ Args: []string{"GOARCH"},
+ Env: env,
+ WorkingDir: dir,
+ }
+ envout, enverr := gocmdRunner.Run(ctx, inv)
+ if enverr != nil {
+ return nil, enverr
+ }
+ goarch = strings.TrimSpace(envout.String())
+ compiler = "gc"
+ } else {
+ return nil, friendlyErr
+ }
+ } else {
+ fields := strings.Fields(stdout.String())
+ if len(fields) < 2 {
+ return nil, fmt.Errorf("could not parse GOARCH and Go compiler in format \" \":\nstdout: <<%s>>\nstderr: <<%s>>",
+ stdout.String(), stderr.String())
+ }
+ goarch = fields[0]
+ compiler = fields[1]
+ }
+ return types.SizesFor(compiler, goarch), nil
+}
diff --git a/vendor/golang.org/x/tools/go/packages/doc.go b/vendor/golang.org/x/tools/go/packages/doc.go
new file mode 100644
index 000000000..4bfe28a51
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/packages/doc.go
@@ -0,0 +1,221 @@
+// 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 packages loads Go packages for inspection and analysis.
+
+The Load function takes as input a list of patterns and return a list of Package
+structs describing individual packages matched by those patterns.
+The LoadMode controls the amount of detail in the loaded packages.
+
+Load passes most patterns directly to the underlying build tool,
+but all patterns with the prefix "query=", where query is a
+non-empty string of letters from [a-z], are reserved and may be
+interpreted as query operators.
+
+Two query operators are currently supported: "file" and "pattern".
+
+The query "file=path/to/file.go" matches the package or packages enclosing
+the Go source file path/to/file.go. For example "file=~/go/src/fmt/print.go"
+might return the packages "fmt" and "fmt [fmt.test]".
+
+The query "pattern=string" causes "string" to be passed directly to
+the underlying build tool. In most cases this is unnecessary,
+but an application can use Load("pattern=" + x) as an escaping mechanism
+to ensure that x is not interpreted as a query operator if it contains '='.
+
+All other query operators are reserved for future use and currently
+cause Load to report an error.
+
+The Package struct provides basic information about the package, including
+
+ - ID, a unique identifier for the package in the returned set;
+ - GoFiles, the names of the package's Go source files;
+ - Imports, a map from source import strings to the Packages they name;
+ - Types, the type information for the package's exported symbols;
+ - Syntax, the parsed syntax trees for the package's source code; and
+ - TypeInfo, the result of a complete type-check of the package syntax trees.
+
+(See the documentation for type Package for the complete list of fields
+and more detailed descriptions.)
+
+For example,
+
+ Load(nil, "bytes", "unicode...")
+
+returns four Package structs describing the standard library packages
+bytes, unicode, unicode/utf16, and unicode/utf8. Note that one pattern
+can match multiple packages and that a package might be matched by
+multiple patterns: in general it is not possible to determine which
+packages correspond to which patterns.
+
+Note that the list returned by Load contains only the packages matched
+by the patterns. Their dependencies can be found by walking the import
+graph using the Imports fields.
+
+The Load function can be configured by passing a pointer to a Config as
+the first argument. A nil Config is equivalent to the zero Config, which
+causes Load to run in LoadFiles mode, collecting minimal information.
+See the documentation for type Config for details.
+
+As noted earlier, the Config.Mode controls the amount of detail
+reported about the loaded packages. See the documentation for type LoadMode
+for details.
+
+Most tools should pass their command-line arguments (after any flags)
+uninterpreted to the loader, so that the loader can interpret them
+according to the conventions of the underlying build system.
+See the Example function for typical usage.
+
+*/
+package packages // import "golang.org/x/tools/go/packages"
+
+/*
+
+Motivation and design considerations
+
+The new package's design solves problems addressed by two existing
+packages: go/build, which locates and describes packages, and
+golang.org/x/tools/go/loader, which loads, parses and type-checks them.
+The go/build.Package structure encodes too much of the 'go build' way
+of organizing projects, leaving us in need of a data type that describes a
+package of Go source code independent of the underlying build system.
+We wanted something that works equally well with go build and vgo, and
+also other build systems such as Bazel and Blaze, making it possible to
+construct analysis tools that work in all these environments.
+Tools such as errcheck and staticcheck were essentially unavailable to
+the Go community at Google, and some of Google's internal tools for Go
+are unavailable externally.
+This new package provides a uniform way to obtain package metadata by
+querying each of these build systems, optionally supporting their
+preferred command-line notations for packages, so that tools integrate
+neatly with users' build environments. The Metadata query function
+executes an external query tool appropriate to the current workspace.
+
+Loading packages always returns the complete import graph "all the way down",
+even if all you want is information about a single package, because the query
+mechanisms of all the build systems we currently support ({go,vgo} list, and
+blaze/bazel aspect-based query) cannot provide detailed information
+about one package without visiting all its dependencies too, so there is
+no additional asymptotic cost to providing transitive information.
+(This property might not be true of a hypothetical 5th build system.)
+
+In calls to TypeCheck, all initial packages, and any package that
+transitively depends on one of them, must be loaded from source.
+Consider A->B->C->D->E: if A,C are initial, A,B,C must be loaded from
+source; D may be loaded from export data, and E may not be loaded at all
+(though it's possible that D's export data mentions it, so a
+types.Package may be created for it and exposed.)
+
+The old loader had a feature to suppress type-checking of function
+bodies on a per-package basis, primarily intended to reduce the work of
+obtaining type information for imported packages. Now that imports are
+satisfied by export data, the optimization no longer seems necessary.
+
+Despite some early attempts, the old loader did not exploit export data,
+instead always using the equivalent of WholeProgram mode. This was due
+to the complexity of mixing source and export data packages (now
+resolved by the upward traversal mentioned above), and because export data
+files were nearly always missing or stale. Now that 'go build' supports
+caching, all the underlying build systems can guarantee to produce
+export data in a reasonable (amortized) time.
+
+Test "main" packages synthesized by the build system are now reported as
+first-class packages, avoiding the need for clients (such as go/ssa) to
+reinvent this generation logic.
+
+One way in which go/packages is simpler than the old loader is in its
+treatment of in-package tests. In-package tests are packages that
+consist of all the files of the library under test, plus the test files.
+The old loader constructed in-package tests by a two-phase process of
+mutation called "augmentation": first it would construct and type check
+all the ordinary library packages and type-check the packages that
+depend on them; then it would add more (test) files to the package and
+type-check again. This two-phase approach had four major problems:
+1) in processing the tests, the loader modified the library package,
+ leaving no way for a client application to see both the test
+ package and the library package; one would mutate into the other.
+2) because test files can declare additional methods on types defined in
+ the library portion of the package, the dispatch of method calls in
+ the library portion was affected by the presence of the test files.
+ This should have been a clue that the packages were logically
+ different.
+3) this model of "augmentation" assumed at most one in-package test
+ per library package, which is true of projects using 'go build',
+ but not other build systems.
+4) because of the two-phase nature of test processing, all packages that
+ import the library package had to be processed before augmentation,
+ forcing a "one-shot" API and preventing the client from calling Load
+ in several times in sequence as is now possible in WholeProgram mode.
+ (TypeCheck mode has a similar one-shot restriction for a different reason.)
+
+Early drafts of this package supported "multi-shot" operation.
+Although it allowed clients to make a sequence of calls (or concurrent
+calls) to Load, building up the graph of Packages incrementally,
+it was of marginal value: it complicated the API
+(since it allowed some options to vary across calls but not others),
+it complicated the implementation,
+it cannot be made to work in Types mode, as explained above,
+and it was less efficient than making one combined call (when this is possible).
+Among the clients we have inspected, none made multiple calls to load
+but could not be easily and satisfactorily modified to make only a single call.
+However, applications changes may be required.
+For example, the ssadump command loads the user-specified packages
+and in addition the runtime package. It is tempting to simply append
+"runtime" to the user-provided list, but that does not work if the user
+specified an ad-hoc package such as [a.go b.go].
+Instead, ssadump no longer requests the runtime package,
+but seeks it among the dependencies of the user-specified packages,
+and emits an error if it is not found.
+
+Overlays: The Overlay field in the Config allows providing alternate contents
+for Go source files, by providing a mapping from file path to contents.
+go/packages will pull in new imports added in overlay files when go/packages
+is run in LoadImports mode or greater.
+Overlay support for the go list driver isn't complete yet: if the file doesn't
+exist on disk, it will only be recognized in an overlay if it is a non-test file
+and the package would be reported even without the overlay.
+
+Questions & Tasks
+
+- Add GOARCH/GOOS?
+ They are not portable concepts, but could be made portable.
+ Our goal has been to allow users to express themselves using the conventions
+ of the underlying build system: if the build system honors GOARCH
+ during a build and during a metadata query, then so should
+ applications built atop that query mechanism.
+ Conversely, if the target architecture of the build is determined by
+ command-line flags, the application can pass the relevant
+ flags through to the build system using a command such as:
+ myapp -query_flag="--cpu=amd64" -query_flag="--os=darwin"
+ However, this approach is low-level, unwieldy, and non-portable.
+ GOOS and GOARCH seem important enough to warrant a dedicated option.
+
+- How should we handle partial failures such as a mixture of good and
+ malformed patterns, existing and non-existent packages, successful and
+ failed builds, import failures, import cycles, and so on, in a call to
+ Load?
+
+- Support bazel, blaze, and go1.10 list, not just go1.11 list.
+
+- Handle (and test) various partial success cases, e.g.
+ a mixture of good packages and:
+ invalid patterns
+ nonexistent packages
+ empty packages
+ packages with malformed package or import declarations
+ unreadable files
+ import cycles
+ other parse errors
+ type errors
+ Make sure we record errors at the correct place in the graph.
+
+- Missing packages among initial arguments are not reported.
+ Return bogus packages for them, like golist does.
+
+- "undeclared name" errors (for example) are reported out of source file
+ order. I suspect this is due to the breadth-first resolution now used
+ by go/types. Is that a bug? Discuss with gri.
+
+*/
diff --git a/vendor/golang.org/x/tools/go/packages/external.go b/vendor/golang.org/x/tools/go/packages/external.go
new file mode 100644
index 000000000..8c8473fd0
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/packages/external.go
@@ -0,0 +1,101 @@
+// 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.
+
+// This file enables an external tool to intercept package requests.
+// If the tool is present then its results are used in preference to
+// the go list command.
+
+package packages
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "os"
+ "os/exec"
+ "strings"
+)
+
+// The Driver Protocol
+//
+// The driver, given the inputs to a call to Load, returns metadata about the packages specified.
+// This allows for different build systems to support go/packages by telling go/packages how the
+// packages' source is organized.
+// The driver is a binary, either specified by the GOPACKAGESDRIVER environment variable or in
+// the path as gopackagesdriver. It's given the inputs to load in its argv. See the package
+// documentation in doc.go for the full description of the patterns that need to be supported.
+// A driver receives as a JSON-serialized driverRequest struct in standard input and will
+// produce a JSON-serialized driverResponse (see definition in packages.go) in its standard output.
+
+// driverRequest is used to provide the portion of Load's Config that is needed by a driver.
+type driverRequest struct {
+ Mode LoadMode `json:"mode"`
+ // Env specifies the environment the underlying build system should be run in.
+ Env []string `json:"env"`
+ // BuildFlags are flags that should be passed to the underlying build system.
+ BuildFlags []string `json:"build_flags"`
+ // Tests specifies whether the patterns should also return test packages.
+ Tests bool `json:"tests"`
+ // Overlay maps file paths (relative to the driver's working directory) to the byte contents
+ // of overlay files.
+ Overlay map[string][]byte `json:"overlay"`
+}
+
+// findExternalDriver returns the file path of a tool that supplies
+// the build system package structure, or "" if not found."
+// If GOPACKAGESDRIVER is set in the environment findExternalTool returns its
+// value, otherwise it searches for a binary named gopackagesdriver on the PATH.
+func findExternalDriver(cfg *Config) driver {
+ const toolPrefix = "GOPACKAGESDRIVER="
+ tool := ""
+ for _, env := range cfg.Env {
+ if val := strings.TrimPrefix(env, toolPrefix); val != env {
+ tool = val
+ }
+ }
+ if tool != "" && tool == "off" {
+ return nil
+ }
+ if tool == "" {
+ var err error
+ tool, err = exec.LookPath("gopackagesdriver")
+ if err != nil {
+ return nil
+ }
+ }
+ return func(cfg *Config, words ...string) (*driverResponse, error) {
+ req, err := json.Marshal(driverRequest{
+ Mode: cfg.Mode,
+ Env: cfg.Env,
+ BuildFlags: cfg.BuildFlags,
+ Tests: cfg.Tests,
+ Overlay: cfg.Overlay,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("failed to encode message to driver tool: %v", err)
+ }
+
+ buf := new(bytes.Buffer)
+ stderr := new(bytes.Buffer)
+ cmd := exec.CommandContext(cfg.Context, tool, words...)
+ cmd.Dir = cfg.Dir
+ cmd.Env = cfg.Env
+ cmd.Stdin = bytes.NewReader(req)
+ cmd.Stdout = buf
+ cmd.Stderr = stderr
+
+ if err := cmd.Run(); err != nil {
+ return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr)
+ }
+ if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTDRIVERERRORS") != "" {
+ fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd, words...), stderr)
+ }
+
+ var response driverResponse
+ if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
+ return nil, err
+ }
+ return &response, nil
+ }
+}
diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go
new file mode 100644
index 000000000..cb6b14c1b
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/packages/golist.go
@@ -0,0 +1,907 @@
+// 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 packages
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "go/types"
+ "log"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "reflect"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "unicode"
+
+ "golang.org/x/tools/go/internal/packagesdriver"
+ "golang.org/x/tools/internal/gocommand"
+ "golang.org/x/xerrors"
+)
+
+// debug controls verbose logging.
+var debug, _ = strconv.ParseBool(os.Getenv("GOPACKAGESDEBUG"))
+
+// A goTooOldError reports that the go command
+// found by exec.LookPath is too old to use the new go list behavior.
+type goTooOldError struct {
+ error
+}
+
+// responseDeduper wraps a driverResponse, deduplicating its contents.
+type responseDeduper struct {
+ seenRoots map[string]bool
+ seenPackages map[string]*Package
+ dr *driverResponse
+}
+
+func newDeduper() *responseDeduper {
+ return &responseDeduper{
+ dr: &driverResponse{},
+ seenRoots: map[string]bool{},
+ seenPackages: map[string]*Package{},
+ }
+}
+
+// addAll fills in r with a driverResponse.
+func (r *responseDeduper) addAll(dr *driverResponse) {
+ for _, pkg := range dr.Packages {
+ r.addPackage(pkg)
+ }
+ for _, root := range dr.Roots {
+ r.addRoot(root)
+ }
+}
+
+func (r *responseDeduper) addPackage(p *Package) {
+ if r.seenPackages[p.ID] != nil {
+ return
+ }
+ r.seenPackages[p.ID] = p
+ r.dr.Packages = append(r.dr.Packages, p)
+}
+
+func (r *responseDeduper) addRoot(id string) {
+ if r.seenRoots[id] {
+ return
+ }
+ r.seenRoots[id] = true
+ r.dr.Roots = append(r.dr.Roots, id)
+}
+
+type golistState struct {
+ cfg *Config
+ ctx context.Context
+
+ envOnce sync.Once
+ goEnvError error
+ goEnv map[string]string
+
+ rootsOnce sync.Once
+ rootDirsError error
+ rootDirs map[string]string
+
+ // vendorDirs caches the (non)existence of vendor directories.
+ vendorDirs map[string]bool
+}
+
+// getEnv returns Go environment variables. Only specific variables are
+// populated -- computing all of them is slow.
+func (state *golistState) getEnv() (map[string]string, error) {
+ state.envOnce.Do(func() {
+ var b *bytes.Buffer
+ b, state.goEnvError = state.invokeGo("env", "-json", "GOMOD", "GOPATH")
+ if state.goEnvError != nil {
+ return
+ }
+
+ state.goEnv = make(map[string]string)
+ decoder := json.NewDecoder(b)
+ if state.goEnvError = decoder.Decode(&state.goEnv); state.goEnvError != nil {
+ return
+ }
+ })
+ return state.goEnv, state.goEnvError
+}
+
+// mustGetEnv is a convenience function that can be used if getEnv has already succeeded.
+func (state *golistState) mustGetEnv() map[string]string {
+ env, err := state.getEnv()
+ if err != nil {
+ panic(fmt.Sprintf("mustGetEnv: %v", err))
+ }
+ return env
+}
+
+// goListDriver uses the go list command to interpret the patterns and produce
+// the build system package structure.
+// See driver for more details.
+func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
+ // Make sure that any asynchronous go commands are killed when we return.
+ parentCtx := cfg.Context
+ if parentCtx == nil {
+ parentCtx = context.Background()
+ }
+ ctx, cancel := context.WithCancel(parentCtx)
+ defer cancel()
+
+ response := newDeduper()
+
+ // Fill in response.Sizes asynchronously if necessary.
+ var sizeserr error
+ var sizeswg sync.WaitGroup
+ if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
+ sizeswg.Add(1)
+ go func() {
+ var sizes types.Sizes
+ sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, cfg.BuildFlags, cfg.Env, cfg.gocmdRunner, cfg.Dir)
+ // types.SizesFor always returns nil or a *types.StdSizes.
+ response.dr.Sizes, _ = sizes.(*types.StdSizes)
+ sizeswg.Done()
+ }()
+ }
+
+ state := &golistState{
+ cfg: cfg,
+ ctx: ctx,
+ vendorDirs: map[string]bool{},
+ }
+
+ // Determine files requested in contains patterns
+ var containFiles []string
+ restPatterns := make([]string, 0, len(patterns))
+ // Extract file= and other [querytype]= patterns. Report an error if querytype
+ // doesn't exist.
+extractQueries:
+ for _, pattern := range patterns {
+ eqidx := strings.Index(pattern, "=")
+ if eqidx < 0 {
+ restPatterns = append(restPatterns, pattern)
+ } else {
+ query, value := pattern[:eqidx], pattern[eqidx+len("="):]
+ switch query {
+ case "file":
+ containFiles = append(containFiles, value)
+ case "pattern":
+ restPatterns = append(restPatterns, value)
+ case "": // not a reserved query
+ restPatterns = append(restPatterns, pattern)
+ default:
+ for _, rune := range query {
+ if rune < 'a' || rune > 'z' { // not a reserved query
+ restPatterns = append(restPatterns, pattern)
+ continue extractQueries
+ }
+ }
+ // Reject all other patterns containing "="
+ return nil, fmt.Errorf("invalid query type %q in query pattern %q", query, pattern)
+ }
+ }
+ }
+
+ // See if we have any patterns to pass through to go list. Zero initial
+ // patterns also requires a go list call, since it's the equivalent of
+ // ".".
+ if len(restPatterns) > 0 || len(patterns) == 0 {
+ dr, err := state.createDriverResponse(restPatterns...)
+ if err != nil {
+ return nil, err
+ }
+ response.addAll(dr)
+ }
+
+ if len(containFiles) != 0 {
+ if err := state.runContainsQueries(response, containFiles); err != nil {
+ return nil, err
+ }
+ }
+
+ modifiedPkgs, needPkgs, err := state.processGolistOverlay(response)
+ if err != nil {
+ return nil, err
+ }
+
+ var containsCandidates []string
+ if len(containFiles) > 0 {
+ containsCandidates = append(containsCandidates, modifiedPkgs...)
+ containsCandidates = append(containsCandidates, needPkgs...)
+ }
+ if err := state.addNeededOverlayPackages(response, needPkgs); err != nil {
+ return nil, err
+ }
+ // Check candidate packages for containFiles.
+ if len(containFiles) > 0 {
+ for _, id := range containsCandidates {
+ pkg, ok := response.seenPackages[id]
+ if !ok {
+ response.addPackage(&Package{
+ ID: id,
+ Errors: []Error{
+ {
+ Kind: ListError,
+ Msg: fmt.Sprintf("package %s expected but not seen", id),
+ },
+ },
+ })
+ continue
+ }
+ for _, f := range containFiles {
+ for _, g := range pkg.GoFiles {
+ if sameFile(f, g) {
+ response.addRoot(id)
+ }
+ }
+ }
+ }
+ }
+
+ sizeswg.Wait()
+ if sizeserr != nil {
+ return nil, sizeserr
+ }
+ return response.dr, nil
+}
+
+func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error {
+ if len(pkgs) == 0 {
+ return nil
+ }
+ dr, err := state.createDriverResponse(pkgs...)
+ if err != nil {
+ return err
+ }
+ for _, pkg := range dr.Packages {
+ response.addPackage(pkg)
+ }
+ _, needPkgs, err := state.processGolistOverlay(response)
+ if err != nil {
+ return err
+ }
+ return state.addNeededOverlayPackages(response, needPkgs)
+}
+
+func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error {
+ for _, query := range queries {
+ // TODO(matloob): Do only one query per directory.
+ fdir := filepath.Dir(query)
+ // Pass absolute path of directory to go list so that it knows to treat it as a directory,
+ // not a package path.
+ pattern, err := filepath.Abs(fdir)
+ if err != nil {
+ return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err)
+ }
+ dirResponse, err := state.createDriverResponse(pattern)
+
+ // If there was an error loading the package, or the package is returned
+ // with errors, try to load the file as an ad-hoc package.
+ // Usually the error will appear in a returned package, but may not if we're
+ // in module mode and the ad-hoc is located outside a module.
+ if err != nil || len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].GoFiles) == 0 &&
+ len(dirResponse.Packages[0].Errors) == 1 {
+ var queryErr error
+ if dirResponse, queryErr = state.adhocPackage(pattern, query); queryErr != nil {
+ return err // return the original error
+ }
+ }
+ isRoot := make(map[string]bool, len(dirResponse.Roots))
+ for _, root := range dirResponse.Roots {
+ isRoot[root] = true
+ }
+ for _, pkg := range dirResponse.Packages {
+ // Add any new packages to the main set
+ // We don't bother to filter packages that will be dropped by the changes of roots,
+ // that will happen anyway during graph construction outside this function.
+ // Over-reporting packages is not a problem.
+ response.addPackage(pkg)
+ // if the package was not a root one, it cannot have the file
+ if !isRoot[pkg.ID] {
+ continue
+ }
+ for _, pkgFile := range pkg.GoFiles {
+ if filepath.Base(query) == filepath.Base(pkgFile) {
+ response.addRoot(pkg.ID)
+ break
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// adhocPackage attempts to load or construct an ad-hoc package for a given
+// query, if the original call to the driver produced inadequate results.
+func (state *golistState) adhocPackage(pattern, query string) (*driverResponse, error) {
+ response, err := state.createDriverResponse(query)
+ if err != nil {
+ return nil, err
+ }
+ // If we get nothing back from `go list`,
+ // try to make this file into its own ad-hoc package.
+ // TODO(rstambler): Should this check against the original response?
+ if len(response.Packages) == 0 {
+ response.Packages = append(response.Packages, &Package{
+ ID: "command-line-arguments",
+ PkgPath: query,
+ GoFiles: []string{query},
+ CompiledGoFiles: []string{query},
+ Imports: make(map[string]*Package),
+ })
+ response.Roots = append(response.Roots, "command-line-arguments")
+ }
+ // Handle special cases.
+ if len(response.Packages) == 1 {
+ // golang/go#33482: If this is a file= query for ad-hoc packages where
+ // the file only exists on an overlay, and exists outside of a module,
+ // add the file to the package and remove the errors.
+ if response.Packages[0].ID == "command-line-arguments" ||
+ filepath.ToSlash(response.Packages[0].PkgPath) == filepath.ToSlash(query) {
+ if len(response.Packages[0].GoFiles) == 0 {
+ filename := filepath.Join(pattern, filepath.Base(query)) // avoid recomputing abspath
+ // TODO(matloob): check if the file is outside of a root dir?
+ for path := range state.cfg.Overlay {
+ if path == filename {
+ response.Packages[0].Errors = nil
+ response.Packages[0].GoFiles = []string{path}
+ response.Packages[0].CompiledGoFiles = []string{path}
+ }
+ }
+ }
+ }
+ }
+ return response, nil
+}
+
+// Fields must match go list;
+// see $GOROOT/src/cmd/go/internal/load/pkg.go.
+type jsonPackage struct {
+ ImportPath string
+ Dir string
+ Name string
+ Export string
+ GoFiles []string
+ CompiledGoFiles []string
+ CFiles []string
+ CgoFiles []string
+ CXXFiles []string
+ MFiles []string
+ HFiles []string
+ FFiles []string
+ SFiles []string
+ SwigFiles []string
+ SwigCXXFiles []string
+ SysoFiles []string
+ Imports []string
+ ImportMap map[string]string
+ Deps []string
+ Module *Module
+ TestGoFiles []string
+ TestImports []string
+ XTestGoFiles []string
+ XTestImports []string
+ ForTest string // q in a "p [q.test]" package, else ""
+ DepOnly bool
+
+ Error *jsonPackageError
+}
+
+type jsonPackageError struct {
+ ImportStack []string
+ Pos string
+ Err string
+}
+
+func otherFiles(p *jsonPackage) [][]string {
+ return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles}
+}
+
+// createDriverResponse uses the "go list" command to expand the pattern
+// words and return a response for the specified packages.
+func (state *golistState) createDriverResponse(words ...string) (*driverResponse, error) {
+ // go list uses the following identifiers in ImportPath and Imports:
+ //
+ // "p" -- importable package or main (command)
+ // "q.test" -- q's test executable
+ // "p [q.test]" -- variant of p as built for q's test executable
+ // "q_test [q.test]" -- q's external test package
+ //
+ // The packages p that are built differently for a test q.test
+ // are q itself, plus any helpers used by the external test q_test,
+ // typically including "testing" and all its dependencies.
+
+ // Run "go list" for complete
+ // information on the specified packages.
+ buf, err := state.invokeGo("list", golistargs(state.cfg, words)...)
+ if err != nil {
+ return nil, err
+ }
+ seen := make(map[string]*jsonPackage)
+ pkgs := make(map[string]*Package)
+ additionalErrors := make(map[string][]Error)
+ // Decode the JSON and convert it to Package form.
+ var response driverResponse
+ for dec := json.NewDecoder(buf); dec.More(); {
+ p := new(jsonPackage)
+ if err := dec.Decode(p); err != nil {
+ return nil, fmt.Errorf("JSON decoding failed: %v", err)
+ }
+
+ if p.ImportPath == "" {
+ // The documentation for go list says that “[e]rroneous packages will have
+ // a non-empty ImportPath”. If for some reason it comes back empty, we
+ // prefer to error out rather than silently discarding data or handing
+ // back a package without any way to refer to it.
+ if p.Error != nil {
+ return nil, Error{
+ Pos: p.Error.Pos,
+ Msg: p.Error.Err,
+ }
+ }
+ return nil, fmt.Errorf("package missing import path: %+v", p)
+ }
+
+ // Work around https://golang.org/issue/33157:
+ // go list -e, when given an absolute path, will find the package contained at
+ // that directory. But when no package exists there, it will return a fake package
+ // with an error and the ImportPath set to the absolute path provided to go list.
+ // Try to convert that absolute path to what its package path would be if it's
+ // contained in a known module or GOPATH entry. This will allow the package to be
+ // properly "reclaimed" when overlays are processed.
+ if filepath.IsAbs(p.ImportPath) && p.Error != nil {
+ pkgPath, ok, err := state.getPkgPath(p.ImportPath)
+ if err != nil {
+ return nil, err
+ }
+ if ok {
+ p.ImportPath = pkgPath
+ }
+ }
+
+ if old, found := seen[p.ImportPath]; found {
+ // If one version of the package has an error, and the other doesn't, assume
+ // that this is a case where go list is reporting a fake dependency variant
+ // of the imported package: When a package tries to invalidly import another
+ // package, go list emits a variant of the imported package (with the same
+ // import path, but with an error on it, and the package will have a
+ // DepError set on it). An example of when this can happen is for imports of
+ // main packages: main packages can not be imported, but they may be
+ // separately matched and listed by another pattern.
+ // See golang.org/issue/36188 for more details.
+
+ // The plan is that eventually, hopefully in Go 1.15, the error will be
+ // reported on the importing package rather than the duplicate "fake"
+ // version of the imported package. Once all supported versions of Go
+ // have the new behavior this logic can be deleted.
+ // TODO(matloob): delete the workaround logic once all supported versions of
+ // Go return the errors on the proper package.
+
+ // There should be exactly one version of a package that doesn't have an
+ // error.
+ if old.Error == nil && p.Error == nil {
+ if !reflect.DeepEqual(p, old) {
+ return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath)
+ }
+ continue
+ }
+
+ // Determine if this package's error needs to be bubbled up.
+ // This is a hack, and we expect for go list to eventually set the error
+ // on the package.
+ if old.Error != nil {
+ var errkind string
+ if strings.Contains(old.Error.Err, "not an importable package") {
+ errkind = "not an importable package"
+ } else if strings.Contains(old.Error.Err, "use of internal package") && strings.Contains(old.Error.Err, "not allowed") {
+ errkind = "use of internal package not allowed"
+ }
+ if errkind != "" {
+ if len(old.Error.ImportStack) < 1 {
+ return nil, fmt.Errorf(`internal error: go list gave a %q error with empty import stack`, errkind)
+ }
+ importingPkg := old.Error.ImportStack[len(old.Error.ImportStack)-1]
+ if importingPkg == old.ImportPath {
+ // Using an older version of Go which put this package itself on top of import
+ // stack, instead of the importer. Look for importer in second from top
+ // position.
+ if len(old.Error.ImportStack) < 2 {
+ return nil, fmt.Errorf(`internal error: go list gave a %q error with an import stack without importing package`, errkind)
+ }
+ importingPkg = old.Error.ImportStack[len(old.Error.ImportStack)-2]
+ }
+ additionalErrors[importingPkg] = append(additionalErrors[importingPkg], Error{
+ Pos: old.Error.Pos,
+ Msg: old.Error.Err,
+ Kind: ListError,
+ })
+ }
+ }
+
+ // Make sure that if there's a version of the package without an error,
+ // that's the one reported to the user.
+ if old.Error == nil {
+ continue
+ }
+
+ // This package will replace the old one at the end of the loop.
+ }
+ seen[p.ImportPath] = p
+
+ pkg := &Package{
+ Name: p.Name,
+ ID: p.ImportPath,
+ GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles),
+ CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
+ OtherFiles: absJoin(p.Dir, otherFiles(p)...),
+ forTest: p.ForTest,
+ Module: p.Module,
+ }
+
+ if (state.cfg.Mode&TypecheckCgo) != 0 && len(p.CgoFiles) != 0 {
+ if len(p.CompiledGoFiles) > len(p.GoFiles) {
+ // We need the cgo definitions, which are in the first
+ // CompiledGoFile after the non-cgo ones. This is a hack but there
+ // isn't currently a better way to find it. We also need the pure
+ // Go files and unprocessed cgo files, all of which are already
+ // in pkg.GoFiles.
+ cgoTypes := p.CompiledGoFiles[len(p.GoFiles)]
+ pkg.CompiledGoFiles = append([]string{cgoTypes}, pkg.GoFiles...)
+ } else {
+ // golang/go#38990: go list silently fails to do cgo processing
+ pkg.CompiledGoFiles = nil
+ pkg.Errors = append(pkg.Errors, Error{
+ Msg: "go list failed to return CompiledGoFiles; https://golang.org/issue/38990?",
+ Kind: ListError,
+ })
+ }
+ }
+
+ // Work around https://golang.org/issue/28749:
+ // cmd/go puts assembly, C, and C++ files in CompiledGoFiles.
+ // Filter out any elements of CompiledGoFiles that are also in OtherFiles.
+ // We have to keep this workaround in place until go1.12 is a distant memory.
+ if len(pkg.OtherFiles) > 0 {
+ other := make(map[string]bool, len(pkg.OtherFiles))
+ for _, f := range pkg.OtherFiles {
+ other[f] = true
+ }
+
+ out := pkg.CompiledGoFiles[:0]
+ for _, f := range pkg.CompiledGoFiles {
+ if other[f] {
+ continue
+ }
+ out = append(out, f)
+ }
+ pkg.CompiledGoFiles = out
+ }
+
+ // Extract the PkgPath from the package's ID.
+ if i := strings.IndexByte(pkg.ID, ' '); i >= 0 {
+ pkg.PkgPath = pkg.ID[:i]
+ } else {
+ pkg.PkgPath = pkg.ID
+ }
+
+ if pkg.PkgPath == "unsafe" {
+ pkg.GoFiles = nil // ignore fake unsafe.go file
+ }
+
+ // Assume go list emits only absolute paths for Dir.
+ if p.Dir != "" && !filepath.IsAbs(p.Dir) {
+ log.Fatalf("internal error: go list returned non-absolute Package.Dir: %s", p.Dir)
+ }
+
+ if p.Export != "" && !filepath.IsAbs(p.Export) {
+ pkg.ExportFile = filepath.Join(p.Dir, p.Export)
+ } else {
+ pkg.ExportFile = p.Export
+ }
+
+ // imports
+ //
+ // Imports contains the IDs of all imported packages.
+ // ImportsMap records (path, ID) only where they differ.
+ ids := make(map[string]bool)
+ for _, id := range p.Imports {
+ ids[id] = true
+ }
+ pkg.Imports = make(map[string]*Package)
+ for path, id := range p.ImportMap {
+ pkg.Imports[path] = &Package{ID: id} // non-identity import
+ delete(ids, id)
+ }
+ for id := range ids {
+ if id == "C" {
+ continue
+ }
+
+ pkg.Imports[id] = &Package{ID: id} // identity import
+ }
+ if !p.DepOnly {
+ response.Roots = append(response.Roots, pkg.ID)
+ }
+
+ // Work around for pre-go.1.11 versions of go list.
+ // TODO(matloob): they should be handled by the fallback.
+ // Can we delete this?
+ if len(pkg.CompiledGoFiles) == 0 {
+ pkg.CompiledGoFiles = pkg.GoFiles
+ }
+
+ if p.Error != nil {
+ msg := strings.TrimSpace(p.Error.Err) // Trim to work around golang.org/issue/32363.
+ // Address golang.org/issue/35964 by appending import stack to error message.
+ if msg == "import cycle not allowed" && len(p.Error.ImportStack) != 0 {
+ msg += fmt.Sprintf(": import stack: %v", p.Error.ImportStack)
+ }
+ pkg.Errors = append(pkg.Errors, Error{
+ Pos: p.Error.Pos,
+ Msg: msg,
+ Kind: ListError,
+ })
+ }
+
+ pkgs[pkg.ID] = pkg
+ }
+
+ for id, errs := range additionalErrors {
+ if p, ok := pkgs[id]; ok {
+ p.Errors = append(p.Errors, errs...)
+ }
+ }
+ for _, pkg := range pkgs {
+ response.Packages = append(response.Packages, pkg)
+ }
+ sort.Slice(response.Packages, func(i, j int) bool { return response.Packages[i].ID < response.Packages[j].ID })
+
+ return &response, nil
+}
+
+// getPkgPath finds the package path of a directory if it's relative to a root directory.
+func (state *golistState) getPkgPath(dir string) (string, bool, error) {
+ absDir, err := filepath.Abs(dir)
+ if err != nil {
+ return "", false, err
+ }
+ roots, err := state.determineRootDirs()
+ if err != nil {
+ return "", false, err
+ }
+
+ for rdir, rpath := range roots {
+ // Make sure that the directory is in the module,
+ // to avoid creating a path relative to another module.
+ if !strings.HasPrefix(absDir, rdir) {
+ continue
+ }
+ // TODO(matloob): This doesn't properly handle symlinks.
+ r, err := filepath.Rel(rdir, dir)
+ if err != nil {
+ continue
+ }
+ if rpath != "" {
+ // We choose only one root even though the directory even it can belong in multiple modules
+ // or GOPATH entries. This is okay because we only need to work with absolute dirs when a
+ // file is missing from disk, for instance when gopls calls go/packages in an overlay.
+ // Once the file is saved, gopls, or the next invocation of the tool will get the correct
+ // result straight from golist.
+ // TODO(matloob): Implement module tiebreaking?
+ return path.Join(rpath, filepath.ToSlash(r)), true, nil
+ }
+ return filepath.ToSlash(r), true, nil
+ }
+ return "", false, nil
+}
+
+// absJoin absolutizes and flattens the lists of files.
+func absJoin(dir string, fileses ...[]string) (res []string) {
+ for _, files := range fileses {
+ for _, file := range files {
+ if !filepath.IsAbs(file) {
+ file = filepath.Join(dir, file)
+ }
+ res = append(res, file)
+ }
+ }
+ return res
+}
+
+func golistargs(cfg *Config, words []string) []string {
+ const findFlags = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo
+ fullargs := []string{
+ "-e", "-json",
+ fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypes|NeedTypesInfo|NeedTypesSizes) != 0),
+ fmt.Sprintf("-test=%t", cfg.Tests),
+ fmt.Sprintf("-export=%t", usesExportData(cfg)),
+ fmt.Sprintf("-deps=%t", cfg.Mode&NeedImports != 0),
+ // go list doesn't let you pass -test and -find together,
+ // probably because you'd just get the TestMain.
+ fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0),
+ }
+ fullargs = append(fullargs, cfg.BuildFlags...)
+ fullargs = append(fullargs, "--")
+ fullargs = append(fullargs, words...)
+ return fullargs
+}
+
+// invokeGo returns the stdout of a go command invocation.
+func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, error) {
+ cfg := state.cfg
+
+ inv := gocommand.Invocation{
+ Verb: verb,
+ Args: args,
+ BuildFlags: cfg.BuildFlags,
+ Env: cfg.Env,
+ Logf: cfg.Logf,
+ WorkingDir: cfg.Dir,
+ }
+ gocmdRunner := cfg.gocmdRunner
+ if gocmdRunner == nil {
+ gocmdRunner = &gocommand.Runner{}
+ }
+ stdout, stderr, _, err := gocmdRunner.RunRaw(cfg.Context, inv)
+ if err != nil {
+ // Check for 'go' executable not being found.
+ if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
+ return nil, fmt.Errorf("'go list' driver requires 'go', but %s", exec.ErrNotFound)
+ }
+
+ exitErr, ok := err.(*exec.ExitError)
+ if !ok {
+ // Catastrophic error:
+ // - context cancellation
+ return nil, xerrors.Errorf("couldn't run 'go': %w", err)
+ }
+
+ // Old go version?
+ if strings.Contains(stderr.String(), "flag provided but not defined") {
+ return nil, goTooOldError{fmt.Errorf("unsupported version of go: %s: %s", exitErr, stderr)}
+ }
+
+ // Related to #24854
+ if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "unexpected directory layout") {
+ return nil, fmt.Errorf("%s", stderr.String())
+ }
+
+ // Is there an error running the C compiler in cgo? This will be reported in the "Error" field
+ // and should be suppressed by go list -e.
+ //
+ // This condition is not perfect yet because the error message can include other error messages than runtime/cgo.
+ isPkgPathRune := func(r rune) bool {
+ // From https://golang.org/ref/spec#Import_declarations:
+ // Implementation restriction: A compiler may restrict ImportPaths to non-empty strings
+ // using only characters belonging to Unicode's L, M, N, P, and S general categories
+ // (the Graphic characters without spaces) and may also exclude the
+ // characters !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character U+FFFD.
+ return unicode.IsOneOf([]*unicode.RangeTable{unicode.L, unicode.M, unicode.N, unicode.P, unicode.S}, r) &&
+ !strings.ContainsRune("!\"#$%&'()*,:;<=>?[\\]^`{|}\uFFFD", r)
+ }
+ if len(stderr.String()) > 0 && strings.HasPrefix(stderr.String(), "# ") {
+ msg := stderr.String()[len("# "):]
+ if strings.HasPrefix(strings.TrimLeftFunc(msg, isPkgPathRune), "\n") {
+ return stdout, nil
+ }
+ // Treat pkg-config errors as a special case (golang.org/issue/36770).
+ if strings.HasPrefix(msg, "pkg-config") {
+ return stdout, nil
+ }
+ }
+
+ // This error only appears in stderr. See golang.org/cl/166398 for a fix in go list to show
+ // the error in the Err section of stdout in case -e option is provided.
+ // This fix is provided for backwards compatibility.
+ if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must be .go files") {
+ output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
+ strings.Trim(stderr.String(), "\n"))
+ return bytes.NewBufferString(output), nil
+ }
+
+ // Similar to the previous error, but currently lacks a fix in Go.
+ if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "named files must all be in one directory") {
+ output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
+ strings.Trim(stderr.String(), "\n"))
+ return bytes.NewBufferString(output), nil
+ }
+
+ // Backwards compatibility for Go 1.11 because 1.12 and 1.13 put the directory in the ImportPath.
+ // If the package doesn't exist, put the absolute path of the directory into the error message,
+ // as Go 1.13 list does.
+ const noSuchDirectory = "no such directory"
+ if len(stderr.String()) > 0 && strings.Contains(stderr.String(), noSuchDirectory) {
+ errstr := stderr.String()
+ abspath := strings.TrimSpace(errstr[strings.Index(errstr, noSuchDirectory)+len(noSuchDirectory):])
+ output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
+ abspath, strings.Trim(stderr.String(), "\n"))
+ return bytes.NewBufferString(output), nil
+ }
+
+ // Workaround for #29280: go list -e has incorrect behavior when an ad-hoc package doesn't exist.
+ // Note that the error message we look for in this case is different that the one looked for above.
+ if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no such file or directory") {
+ output := fmt.Sprintf(`{"ImportPath": "command-line-arguments","Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
+ strings.Trim(stderr.String(), "\n"))
+ return bytes.NewBufferString(output), nil
+ }
+
+ // Workaround for #34273. go list -e with GO111MODULE=on has incorrect behavior when listing a
+ // directory outside any module.
+ if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside available modules") {
+ output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
+ // TODO(matloob): command-line-arguments isn't correct here.
+ "command-line-arguments", strings.Trim(stderr.String(), "\n"))
+ return bytes.NewBufferString(output), nil
+ }
+
+ // Another variation of the previous error
+ if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "outside module root") {
+ output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
+ // TODO(matloob): command-line-arguments isn't correct here.
+ "command-line-arguments", strings.Trim(stderr.String(), "\n"))
+ return bytes.NewBufferString(output), nil
+ }
+
+ // Workaround for an instance of golang.org/issue/26755: go list -e will return a non-zero exit
+ // status if there's a dependency on a package that doesn't exist. But it should return
+ // a zero exit status and set an error on that package.
+ if len(stderr.String()) > 0 && strings.Contains(stderr.String(), "no Go files in") {
+ // Don't clobber stdout if `go list` actually returned something.
+ if len(stdout.String()) > 0 {
+ return stdout, nil
+ }
+ // try to extract package name from string
+ stderrStr := stderr.String()
+ var importPath string
+ colon := strings.Index(stderrStr, ":")
+ if colon > 0 && strings.HasPrefix(stderrStr, "go build ") {
+ importPath = stderrStr[len("go build "):colon]
+ }
+ output := fmt.Sprintf(`{"ImportPath": %q,"Incomplete": true,"Error": {"Pos": "","Err": %q}}`,
+ importPath, strings.Trim(stderrStr, "\n"))
+ return bytes.NewBufferString(output), nil
+ }
+
+ // Export mode entails a build.
+ // If that build fails, errors appear on stderr
+ // (despite the -e flag) and the Export field is blank.
+ // Do not fail in that case.
+ // The same is true if an ad-hoc package given to go list doesn't exist.
+ // TODO(matloob): Remove these once we can depend on go list to exit with a zero status with -e even when
+ // packages don't exist or a build fails.
+ if !usesExportData(cfg) && !containsGoFile(args) {
+ return nil, fmt.Errorf("go %v: %s: %s", args, exitErr, stderr)
+ }
+ }
+ return stdout, nil
+}
+
+func containsGoFile(s []string) bool {
+ for _, f := range s {
+ if strings.HasSuffix(f, ".go") {
+ return true
+ }
+ }
+ return false
+}
+
+func cmdDebugStr(cmd *exec.Cmd, args ...string) string {
+ env := make(map[string]string)
+ for _, kv := range cmd.Env {
+ split := strings.Split(kv, "=")
+ k, v := split[0], split[1]
+ env[k] = v
+ }
+ var quotedArgs []string
+ for _, arg := range args {
+ quotedArgs = append(quotedArgs, strconv.Quote(arg))
+ }
+
+ return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v PWD=%v go %s", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["PWD"], strings.Join(quotedArgs, " "))
+}
diff --git a/vendor/golang.org/x/tools/go/packages/golist_overlay.go b/vendor/golang.org/x/tools/go/packages/golist_overlay.go
new file mode 100644
index 000000000..b82c90d7c
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/packages/golist_overlay.go
@@ -0,0 +1,457 @@
+package packages
+
+import (
+ "encoding/json"
+ "fmt"
+ "go/parser"
+ "go/token"
+ "log"
+ "os"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+// processGolistOverlay provides rudimentary support for adding
+// files that don't exist on disk to an overlay. The results can be
+// sometimes incorrect.
+// TODO(matloob): Handle unsupported cases, including the following:
+// - determining the correct package to add given a new import path
+func (state *golistState) processGolistOverlay(response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) {
+ havePkgs := make(map[string]string) // importPath -> non-test package ID
+ needPkgsSet := make(map[string]bool)
+ modifiedPkgsSet := make(map[string]bool)
+
+ pkgOfDir := make(map[string][]*Package)
+ for _, pkg := range response.dr.Packages {
+ // This is an approximation of import path to id. This can be
+ // wrong for tests, vendored packages, and a number of other cases.
+ havePkgs[pkg.PkgPath] = pkg.ID
+ x := commonDir(pkg.GoFiles)
+ if x != "" {
+ pkgOfDir[x] = append(pkgOfDir[x], pkg)
+ }
+ }
+
+ // If no new imports are added, it is safe to avoid loading any needPkgs.
+ // Otherwise, it's hard to tell which package is actually being loaded
+ // (due to vendoring) and whether any modified package will show up
+ // in the transitive set of dependencies (because new imports are added,
+ // potentially modifying the transitive set of dependencies).
+ var overlayAddsImports bool
+
+ // If both a package and its test package are created by the overlay, we
+ // need the real package first. Process all non-test files before test
+ // files, and make the whole process deterministic while we're at it.
+ var overlayFiles []string
+ for opath := range state.cfg.Overlay {
+ overlayFiles = append(overlayFiles, opath)
+ }
+ sort.Slice(overlayFiles, func(i, j int) bool {
+ iTest := strings.HasSuffix(overlayFiles[i], "_test.go")
+ jTest := strings.HasSuffix(overlayFiles[j], "_test.go")
+ if iTest != jTest {
+ return !iTest // non-tests are before tests.
+ }
+ return overlayFiles[i] < overlayFiles[j]
+ })
+ for _, opath := range overlayFiles {
+ contents := state.cfg.Overlay[opath]
+ base := filepath.Base(opath)
+ dir := filepath.Dir(opath)
+ var pkg *Package // if opath belongs to both a package and its test variant, this will be the test variant
+ var testVariantOf *Package // if opath is a test file, this is the package it is testing
+ var fileExists bool
+ isTestFile := strings.HasSuffix(opath, "_test.go")
+ pkgName, ok := extractPackageName(opath, contents)
+ if !ok {
+ // Don't bother adding a file that doesn't even have a parsable package statement
+ // to the overlay.
+ continue
+ }
+ // If all the overlay files belong to a different package, change the
+ // package name to that package.
+ maybeFixPackageName(pkgName, isTestFile, pkgOfDir[dir])
+ nextPackage:
+ for _, p := range response.dr.Packages {
+ if pkgName != p.Name && p.ID != "command-line-arguments" {
+ continue
+ }
+ for _, f := range p.GoFiles {
+ if !sameFile(filepath.Dir(f), dir) {
+ continue
+ }
+ // Make sure to capture information on the package's test variant, if needed.
+ if isTestFile && !hasTestFiles(p) {
+ // TODO(matloob): Are there packages other than the 'production' variant
+ // of a package that this can match? This shouldn't match the test main package
+ // because the file is generated in another directory.
+ testVariantOf = p
+ continue nextPackage
+ }
+ // We must have already seen the package of which this is a test variant.
+ if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath {
+ if hasTestFiles(p) {
+ testVariantOf = pkg
+ }
+ }
+ pkg = p
+ if filepath.Base(f) == base {
+ fileExists = true
+ }
+ }
+ }
+ // The overlay could have included an entirely new package.
+ if pkg == nil {
+ // Try to find the module or gopath dir the file is contained in.
+ // Then for modules, add the module opath to the beginning.
+ pkgPath, ok, err := state.getPkgPath(dir)
+ if err != nil {
+ return nil, nil, err
+ }
+ if !ok {
+ break
+ }
+ isXTest := strings.HasSuffix(pkgName, "_test")
+ if isXTest {
+ pkgPath += "_test"
+ }
+ id := pkgPath
+ if isTestFile && !isXTest {
+ id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath)
+ }
+ // Try to reclaim a package with the same ID, if it exists in the response.
+ for _, p := range response.dr.Packages {
+ if reclaimPackage(p, id, opath, contents) {
+ pkg = p
+ break
+ }
+ }
+ // Otherwise, create a new package.
+ if pkg == nil {
+ pkg = &Package{
+ PkgPath: pkgPath,
+ ID: id,
+ Name: pkgName,
+ Imports: make(map[string]*Package),
+ }
+ response.addPackage(pkg)
+ havePkgs[pkg.PkgPath] = id
+ // Add the production package's sources for a test variant.
+ if isTestFile && !isXTest && testVariantOf != nil {
+ pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...)
+ pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...)
+ // Add the package under test and its imports to the test variant.
+ pkg.forTest = testVariantOf.PkgPath
+ for k, v := range testVariantOf.Imports {
+ pkg.Imports[k] = &Package{ID: v.ID}
+ }
+ }
+ // TODO(rstambler): Handle forTest for x_tests.
+ }
+ }
+ if !fileExists {
+ pkg.GoFiles = append(pkg.GoFiles, opath)
+ // TODO(matloob): Adding the file to CompiledGoFiles can exhibit the wrong behavior
+ // if the file will be ignored due to its build tags.
+ pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, opath)
+ modifiedPkgsSet[pkg.ID] = true
+ }
+ imports, err := extractImports(opath, contents)
+ if err != nil {
+ // Let the parser or type checker report errors later.
+ continue
+ }
+ for _, imp := range imports {
+ // TODO(rstambler): If the package is an x test and the import has
+ // a test variant, make sure to replace it.
+ if _, found := pkg.Imports[imp]; found {
+ continue
+ }
+ overlayAddsImports = true
+ id, ok := havePkgs[imp]
+ if !ok {
+ var err error
+ id, err = state.resolveImport(dir, imp)
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+ pkg.Imports[imp] = &Package{ID: id}
+ // Add dependencies to the non-test variant version of this package as well.
+ if testVariantOf != nil {
+ testVariantOf.Imports[imp] = &Package{ID: id}
+ }
+ }
+ }
+
+ // toPkgPath guesses the package path given the id.
+ toPkgPath := func(sourceDir, id string) (string, error) {
+ if i := strings.IndexByte(id, ' '); i >= 0 {
+ return state.resolveImport(sourceDir, id[:i])
+ }
+ return state.resolveImport(sourceDir, id)
+ }
+
+ // Now that new packages have been created, do another pass to determine
+ // the new set of missing packages.
+ for _, pkg := range response.dr.Packages {
+ for _, imp := range pkg.Imports {
+ if len(pkg.GoFiles) == 0 {
+ return nil, nil, fmt.Errorf("cannot resolve imports for package %q with no Go files", pkg.PkgPath)
+ }
+ pkgPath, err := toPkgPath(filepath.Dir(pkg.GoFiles[0]), imp.ID)
+ if err != nil {
+ return nil, nil, err
+ }
+ if _, ok := havePkgs[pkgPath]; !ok {
+ needPkgsSet[pkgPath] = true
+ }
+ }
+ }
+
+ if overlayAddsImports {
+ needPkgs = make([]string, 0, len(needPkgsSet))
+ for pkg := range needPkgsSet {
+ needPkgs = append(needPkgs, pkg)
+ }
+ }
+ modifiedPkgs = make([]string, 0, len(modifiedPkgsSet))
+ for pkg := range modifiedPkgsSet {
+ modifiedPkgs = append(modifiedPkgs, pkg)
+ }
+ return modifiedPkgs, needPkgs, err
+}
+
+// resolveImport finds the the ID of a package given its import path.
+// In particular, it will find the right vendored copy when in GOPATH mode.
+func (state *golistState) resolveImport(sourceDir, importPath string) (string, error) {
+ env, err := state.getEnv()
+ if err != nil {
+ return "", err
+ }
+ if env["GOMOD"] != "" {
+ return importPath, nil
+ }
+
+ searchDir := sourceDir
+ for {
+ vendorDir := filepath.Join(searchDir, "vendor")
+ exists, ok := state.vendorDirs[vendorDir]
+ if !ok {
+ info, err := os.Stat(vendorDir)
+ exists = err == nil && info.IsDir()
+ state.vendorDirs[vendorDir] = exists
+ }
+
+ if exists {
+ vendoredPath := filepath.Join(vendorDir, importPath)
+ if info, err := os.Stat(vendoredPath); err == nil && info.IsDir() {
+ // We should probably check for .go files here, but shame on anyone who fools us.
+ path, ok, err := state.getPkgPath(vendoredPath)
+ if err != nil {
+ return "", err
+ }
+ if ok {
+ return path, nil
+ }
+ }
+ }
+
+ // We know we've hit the top of the filesystem when we Dir / and get /,
+ // or C:\ and get C:\, etc.
+ next := filepath.Dir(searchDir)
+ if next == searchDir {
+ break
+ }
+ searchDir = next
+ }
+ return importPath, nil
+}
+
+func hasTestFiles(p *Package) bool {
+ for _, f := range p.GoFiles {
+ if strings.HasSuffix(f, "_test.go") {
+ return true
+ }
+ }
+ return false
+}
+
+// determineRootDirs returns a mapping from absolute directories that could
+// contain code to their corresponding import path prefixes.
+func (state *golistState) determineRootDirs() (map[string]string, error) {
+ env, err := state.getEnv()
+ if err != nil {
+ return nil, err
+ }
+ if env["GOMOD"] != "" {
+ state.rootsOnce.Do(func() {
+ state.rootDirs, state.rootDirsError = state.determineRootDirsModules()
+ })
+ } else {
+ state.rootsOnce.Do(func() {
+ state.rootDirs, state.rootDirsError = state.determineRootDirsGOPATH()
+ })
+ }
+ return state.rootDirs, state.rootDirsError
+}
+
+func (state *golistState) determineRootDirsModules() (map[string]string, error) {
+ // This will only return the root directory for the main module.
+ // For now we only support overlays in main modules.
+ // Editing files in the module cache isn't a great idea, so we don't
+ // plan to ever support that, but editing files in replaced modules
+ // is something we may want to support. To do that, we'll want to
+ // do a go list -m to determine the replaced module's module path and
+ // directory, and then a go list -m {{with .Replace}}{{.Dir}}{{end}}
+ // from the main module to determine if that module is actually a replacement.
+ // See bcmills's comment here: https://github.com/golang/go/issues/37629#issuecomment-594179751
+ // for more information.
+ out, err := state.invokeGo("list", "-m", "-json")
+ if err != nil {
+ return nil, err
+ }
+ m := map[string]string{}
+ type jsonMod struct{ Path, Dir string }
+ for dec := json.NewDecoder(out); dec.More(); {
+ mod := new(jsonMod)
+ if err := dec.Decode(mod); err != nil {
+ return nil, err
+ }
+ if mod.Dir != "" && mod.Path != "" {
+ // This is a valid module; add it to the map.
+ absDir, err := filepath.Abs(mod.Dir)
+ if err != nil {
+ return nil, err
+ }
+ m[absDir] = mod.Path
+ }
+ }
+ return m, nil
+}
+
+func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) {
+ m := map[string]string{}
+ for _, dir := range filepath.SplitList(state.mustGetEnv()["GOPATH"]) {
+ absDir, err := filepath.Abs(dir)
+ if err != nil {
+ return nil, err
+ }
+ m[filepath.Join(absDir, "src")] = ""
+ }
+ return m, nil
+}
+
+func extractImports(filename string, contents []byte) ([]string, error) {
+ f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.ImportsOnly) // TODO(matloob): reuse fileset?
+ if err != nil {
+ return nil, err
+ }
+ var res []string
+ for _, imp := range f.Imports {
+ quotedPath := imp.Path.Value
+ path, err := strconv.Unquote(quotedPath)
+ if err != nil {
+ return nil, err
+ }
+ res = append(res, path)
+ }
+ return res, nil
+}
+
+// reclaimPackage attempts to reuse a package that failed to load in an overlay.
+//
+// If the package has errors and has no Name, GoFiles, or Imports,
+// then it's possible that it doesn't yet exist on disk.
+func reclaimPackage(pkg *Package, id string, filename string, contents []byte) bool {
+ // TODO(rstambler): Check the message of the actual error?
+ // It differs between $GOPATH and module mode.
+ if pkg.ID != id {
+ return false
+ }
+ if len(pkg.Errors) != 1 {
+ return false
+ }
+ if pkg.Name != "" || pkg.ExportFile != "" {
+ return false
+ }
+ if len(pkg.GoFiles) > 0 || len(pkg.CompiledGoFiles) > 0 || len(pkg.OtherFiles) > 0 {
+ return false
+ }
+ if len(pkg.Imports) > 0 {
+ return false
+ }
+ pkgName, ok := extractPackageName(filename, contents)
+ if !ok {
+ return false
+ }
+ pkg.Name = pkgName
+ pkg.Errors = nil
+ return true
+}
+
+func extractPackageName(filename string, contents []byte) (string, bool) {
+ // TODO(rstambler): Check the message of the actual error?
+ // It differs between $GOPATH and module mode.
+ f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.PackageClauseOnly) // TODO(matloob): reuse fileset?
+ if err != nil {
+ return "", false
+ }
+ return f.Name.Name, true
+}
+
+func commonDir(a []string) string {
+ seen := make(map[string]bool)
+ x := append([]string{}, a...)
+ for _, f := range x {
+ seen[filepath.Dir(f)] = true
+ }
+ if len(seen) > 1 {
+ log.Fatalf("commonDir saw %v for %v", seen, x)
+ }
+ for k := range seen {
+ // len(seen) == 1
+ return k
+ }
+ return "" // no files
+}
+
+// It is possible that the files in the disk directory dir have a different package
+// name from newName, which is deduced from the overlays. If they all have a different
+// package name, and they all have the same package name, then that name becomes
+// the package name.
+// It returns true if it changes the package name, false otherwise.
+func maybeFixPackageName(newName string, isTestFile bool, pkgsOfDir []*Package) {
+ names := make(map[string]int)
+ for _, p := range pkgsOfDir {
+ names[p.Name]++
+ }
+ if len(names) != 1 {
+ // some files are in different packages
+ return
+ }
+ var oldName string
+ for k := range names {
+ oldName = k
+ }
+ if newName == oldName {
+ return
+ }
+ // We might have a case where all of the package names in the directory are
+ // the same, but the overlay file is for an x test, which belongs to its
+ // own package. If the x test does not yet exist on disk, we may not yet
+ // have its package name on disk, but we should not rename the packages.
+ //
+ // We use a heuristic to determine if this file belongs to an x test:
+ // The test file should have a package name whose package name has a _test
+ // suffix or looks like "newName_test".
+ maybeXTest := strings.HasPrefix(oldName+"_test", newName) || strings.HasSuffix(newName, "_test")
+ if isTestFile && maybeXTest {
+ return
+ }
+ for _, p := range pkgsOfDir {
+ p.Name = newName
+ }
+}
diff --git a/vendor/golang.org/x/tools/go/packages/loadmode_string.go b/vendor/golang.org/x/tools/go/packages/loadmode_string.go
new file mode 100644
index 000000000..7ea37e7ee
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/packages/loadmode_string.go
@@ -0,0 +1,57 @@
+// Copyright 2019 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 packages
+
+import (
+ "fmt"
+ "strings"
+)
+
+var allModes = []LoadMode{
+ NeedName,
+ NeedFiles,
+ NeedCompiledGoFiles,
+ NeedImports,
+ NeedDeps,
+ NeedExportsFile,
+ NeedTypes,
+ NeedSyntax,
+ NeedTypesInfo,
+ NeedTypesSizes,
+}
+
+var modeStrings = []string{
+ "NeedName",
+ "NeedFiles",
+ "NeedCompiledGoFiles",
+ "NeedImports",
+ "NeedDeps",
+ "NeedExportsFile",
+ "NeedTypes",
+ "NeedSyntax",
+ "NeedTypesInfo",
+ "NeedTypesSizes",
+}
+
+func (mod LoadMode) String() string {
+ m := mod
+ if m == 0 {
+ return "LoadMode(0)"
+ }
+ var out []string
+ for i, x := range allModes {
+ if x > m {
+ break
+ }
+ if (m & x) != 0 {
+ out = append(out, modeStrings[i])
+ m = m ^ x
+ }
+ }
+ if m != 0 {
+ out = append(out, "Unknown")
+ }
+ return fmt.Sprintf("LoadMode(%s)", strings.Join(out, "|"))
+}
diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go
new file mode 100644
index 000000000..1e6f9a467
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/packages/packages.go
@@ -0,0 +1,1200 @@
+// 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 packages
+
+// See doc.go for package documentation and implementation notes.
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/scanner"
+ "go/token"
+ "go/types"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "reflect"
+ "strings"
+ "sync"
+ "time"
+
+ "golang.org/x/tools/go/gcexportdata"
+ "golang.org/x/tools/internal/gocommand"
+ "golang.org/x/tools/internal/packagesinternal"
+)
+
+// A LoadMode controls the amount of detail to return when loading.
+// The bits below can be combined to specify which fields should be
+// filled in the result packages.
+// The zero value is a special case, equivalent to combining
+// the NeedName, NeedFiles, and NeedCompiledGoFiles bits.
+// ID and Errors (if present) will always be filled.
+// Load may return more information than requested.
+type LoadMode int
+
+// TODO(matloob): When a V2 of go/packages is released, rename NeedExportsFile to
+// NeedExportFile to make it consistent with the Package field it's adding.
+
+const (
+ // NeedName adds Name and PkgPath.
+ NeedName LoadMode = 1 << iota
+
+ // NeedFiles adds GoFiles and OtherFiles.
+ NeedFiles
+
+ // NeedCompiledGoFiles adds CompiledGoFiles.
+ NeedCompiledGoFiles
+
+ // NeedImports adds Imports. If NeedDeps is not set, the Imports field will contain
+ // "placeholder" Packages with only the ID set.
+ NeedImports
+
+ // NeedDeps adds the fields requested by the LoadMode in the packages in Imports.
+ NeedDeps
+
+ // NeedExportsFile adds ExportFile.
+ NeedExportsFile
+
+ // NeedTypes adds Types, Fset, and IllTyped.
+ NeedTypes
+
+ // NeedSyntax adds Syntax.
+ NeedSyntax
+
+ // NeedTypesInfo adds TypesInfo.
+ NeedTypesInfo
+
+ // NeedTypesSizes adds TypesSizes.
+ NeedTypesSizes
+
+ // TypecheckCgo enables full support for type checking cgo. Requires Go 1.15+.
+ // Modifies CompiledGoFiles and Types, and has no effect on its own.
+ TypecheckCgo
+
+ // NeedModule adds Module.
+ NeedModule
+)
+
+const (
+ // Deprecated: LoadFiles exists for historical compatibility
+ // and should not be used. Please directly specify the needed fields using the Need values.
+ LoadFiles = NeedName | NeedFiles | NeedCompiledGoFiles
+
+ // Deprecated: LoadImports exists for historical compatibility
+ // and should not be used. Please directly specify the needed fields using the Need values.
+ LoadImports = LoadFiles | NeedImports
+
+ // Deprecated: LoadTypes exists for historical compatibility
+ // and should not be used. Please directly specify the needed fields using the Need values.
+ LoadTypes = LoadImports | NeedTypes | NeedTypesSizes
+
+ // Deprecated: LoadSyntax exists for historical compatibility
+ // and should not be used. Please directly specify the needed fields using the Need values.
+ LoadSyntax = LoadTypes | NeedSyntax | NeedTypesInfo
+
+ // Deprecated: LoadAllSyntax exists for historical compatibility
+ // and should not be used. Please directly specify the needed fields using the Need values.
+ LoadAllSyntax = LoadSyntax | NeedDeps
+)
+
+// A Config specifies details about how packages should be loaded.
+// The zero value is a valid configuration.
+// Calls to Load do not modify this struct.
+type Config struct {
+ // Mode controls the level of information returned for each package.
+ Mode LoadMode
+
+ // Context specifies the context for the load operation.
+ // If the context is cancelled, the loader may stop early
+ // and return an ErrCancelled error.
+ // If Context is nil, the load cannot be cancelled.
+ Context context.Context
+
+ // Logf is the logger for the config.
+ // If the user provides a logger, debug logging is enabled.
+ // If the GOPACKAGESDEBUG environment variable is set to true,
+ // but the logger is nil, default to log.Printf.
+ Logf func(format string, args ...interface{})
+
+ // Dir is the directory in which to run the build system's query tool
+ // that provides information about the packages.
+ // If Dir is empty, the tool is run in the current directory.
+ Dir string
+
+ // Env is the environment to use when invoking the build system's query tool.
+ // If Env is nil, the current environment is used.
+ // As in os/exec's Cmd, only the last value in the slice for
+ // each environment key is used. To specify the setting of only
+ // a few variables, append to the current environment, as in:
+ //
+ // opt.Env = append(os.Environ(), "GOOS=plan9", "GOARCH=386")
+ //
+ Env []string
+
+ // gocmdRunner guards go command calls from concurrency errors.
+ gocmdRunner *gocommand.Runner
+
+ // BuildFlags is a list of command-line flags to be passed through to
+ // the build system's query tool.
+ BuildFlags []string
+
+ // Fset provides source position information for syntax trees and types.
+ // If Fset is nil, Load will use a new fileset, but preserve Fset's value.
+ Fset *token.FileSet
+
+ // ParseFile is called to read and parse each file
+ // when preparing a package's type-checked syntax tree.
+ // It must be safe to call ParseFile simultaneously from multiple goroutines.
+ // If ParseFile is nil, the loader will uses parser.ParseFile.
+ //
+ // ParseFile should parse the source from src and use filename only for
+ // recording position information.
+ //
+ // An application may supply a custom implementation of ParseFile
+ // to change the effective file contents or the behavior of the parser,
+ // or to modify the syntax tree. For example, selectively eliminating
+ // unwanted function bodies can significantly accelerate type checking.
+ ParseFile func(fset *token.FileSet, filename string, src []byte) (*ast.File, error)
+
+ // If Tests is set, the loader includes not just the packages
+ // matching a particular pattern but also any related test packages,
+ // including test-only variants of the package and the test executable.
+ //
+ // For example, when using the go command, loading "fmt" with Tests=true
+ // returns four packages, with IDs "fmt" (the standard package),
+ // "fmt [fmt.test]" (the package as compiled for the test),
+ // "fmt_test" (the test functions from source files in package fmt_test),
+ // and "fmt.test" (the test binary).
+ //
+ // In build systems with explicit names for tests,
+ // setting Tests may have no effect.
+ Tests bool
+
+ // Overlay provides a mapping of absolute file paths to file contents.
+ // If the file with the given path already exists, the parser will use the
+ // alternative file contents provided by the map.
+ //
+ // Overlays provide incomplete support for when a given file doesn't
+ // already exist on disk. See the package doc above for more details.
+ Overlay map[string][]byte
+}
+
+// driver is the type for functions that query the build system for the
+// packages named by the patterns.
+type driver func(cfg *Config, patterns ...string) (*driverResponse, error)
+
+// driverResponse contains the results for a driver query.
+type driverResponse struct {
+ // Sizes, if not nil, is the types.Sizes to use when type checking.
+ Sizes *types.StdSizes
+
+ // Roots is the set of package IDs that make up the root packages.
+ // We have to encode this separately because when we encode a single package
+ // we cannot know if it is one of the roots as that requires knowledge of the
+ // graph it is part of.
+ Roots []string `json:",omitempty"`
+
+ // Packages is the full set of packages in the graph.
+ // The packages are not connected into a graph.
+ // The Imports if populated will be stubs that only have their ID set.
+ // Imports will be connected and then type and syntax information added in a
+ // later pass (see refine).
+ Packages []*Package
+}
+
+// Load loads and returns the Go packages named by the given patterns.
+//
+// Config specifies loading options;
+// nil behaves the same as an empty Config.
+//
+// Load returns an error if any of the patterns was invalid
+// as defined by the underlying build system.
+// It may return an empty list of packages without an error,
+// for instance for an empty expansion of a valid wildcard.
+// Errors associated with a particular package are recorded in the
+// corresponding Package's Errors list, and do not cause Load to
+// return an error. Clients may need to handle such errors before
+// proceeding with further analysis. The PrintErrors function is
+// provided for convenient display of all errors.
+func Load(cfg *Config, patterns ...string) ([]*Package, error) {
+ l := newLoader(cfg)
+ response, err := defaultDriver(&l.Config, patterns...)
+ if err != nil {
+ return nil, err
+ }
+ l.sizes = response.Sizes
+ return l.refine(response.Roots, response.Packages...)
+}
+
+// defaultDriver is a driver that looks for an external driver binary, and if
+// it does not find it falls back to the built in go list driver.
+func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
+ driver := findExternalDriver(cfg)
+ if driver == nil {
+ driver = goListDriver
+ }
+ return driver(cfg, patterns...)
+}
+
+// A Package describes a loaded Go package.
+type Package struct {
+ // ID is a unique identifier for a package,
+ // in a syntax provided by the underlying build system.
+ //
+ // Because the syntax varies based on the build system,
+ // clients should treat IDs as opaque and not attempt to
+ // interpret them.
+ ID string
+
+ // Name is the package name as it appears in the package source code.
+ Name string
+
+ // PkgPath is the package path as used by the go/types package.
+ PkgPath string
+
+ // Errors contains any errors encountered querying the metadata
+ // of the package, or while parsing or type-checking its files.
+ Errors []Error
+
+ // GoFiles lists the absolute file paths of the package's Go source files.
+ GoFiles []string
+
+ // CompiledGoFiles lists the absolute file paths of the package's source
+ // files that are suitable for type checking.
+ // This may differ from GoFiles if files are processed before compilation.
+ CompiledGoFiles []string
+
+ // OtherFiles lists the absolute file paths of the package's non-Go source files,
+ // including assembly, C, C++, Fortran, Objective-C, SWIG, and so on.
+ OtherFiles []string
+
+ // ExportFile is the absolute path to a file containing type
+ // information for the package as provided by the build system.
+ ExportFile string
+
+ // Imports maps import paths appearing in the package's Go source files
+ // to corresponding loaded Packages.
+ Imports map[string]*Package
+
+ // Types provides type information for the package.
+ // The NeedTypes LoadMode bit sets this field for packages matching the
+ // patterns; type information for dependencies may be missing or incomplete,
+ // unless NeedDeps and NeedImports are also set.
+ Types *types.Package
+
+ // Fset provides position information for Types, TypesInfo, and Syntax.
+ // It is set only when Types is set.
+ Fset *token.FileSet
+
+ // IllTyped indicates whether the package or any dependency contains errors.
+ // It is set only when Types is set.
+ IllTyped bool
+
+ // Syntax is the package's syntax trees, for the files listed in CompiledGoFiles.
+ //
+ // The NeedSyntax LoadMode bit populates this field for packages matching the patterns.
+ // If NeedDeps and NeedImports are also set, this field will also be populated
+ // for dependencies.
+ Syntax []*ast.File
+
+ // TypesInfo provides type information about the package's syntax trees.
+ // It is set only when Syntax is set.
+ TypesInfo *types.Info
+
+ // TypesSizes provides the effective size function for types in TypesInfo.
+ TypesSizes types.Sizes
+
+ // forTest is the package under test, if any.
+ forTest string
+
+ // module is the module information for the package if it exists.
+ Module *Module
+}
+
+// Module provides module information for a package.
+type Module struct {
+ Path string // module path
+ Version string // module version
+ Replace *Module // replaced by this module
+ Time *time.Time // time version was created
+ Main bool // is this the main module?
+ Indirect bool // is this module only an indirect dependency of main module?
+ Dir string // directory holding files for this module, if any
+ GoMod string // path to go.mod file used when loading this module, if any
+ GoVersion string // go version used in module
+ Error *ModuleError // error loading module
+}
+
+// ModuleError holds errors loading a module.
+type ModuleError struct {
+ Err string // the error itself
+}
+
+func init() {
+ packagesinternal.GetForTest = func(p interface{}) string {
+ return p.(*Package).forTest
+ }
+ packagesinternal.GetGoCmdRunner = func(config interface{}) *gocommand.Runner {
+ return config.(*Config).gocmdRunner
+ }
+ packagesinternal.SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {
+ config.(*Config).gocmdRunner = runner
+ }
+}
+
+// An Error describes a problem with a package's metadata, syntax, or types.
+type Error struct {
+ Pos string // "file:line:col" or "file:line" or "" or "-"
+ Msg string
+ Kind ErrorKind
+}
+
+// ErrorKind describes the source of the error, allowing the user to
+// differentiate between errors generated by the driver, the parser, or the
+// type-checker.
+type ErrorKind int
+
+const (
+ UnknownError ErrorKind = iota
+ ListError
+ ParseError
+ TypeError
+)
+
+func (err Error) Error() string {
+ pos := err.Pos
+ if pos == "" {
+ pos = "-" // like token.Position{}.String()
+ }
+ return pos + ": " + err.Msg
+}
+
+// flatPackage is the JSON form of Package
+// It drops all the type and syntax fields, and transforms the Imports
+//
+// TODO(adonovan): identify this struct with Package, effectively
+// publishing the JSON protocol.
+type flatPackage struct {
+ ID string
+ Name string `json:",omitempty"`
+ PkgPath string `json:",omitempty"`
+ Errors []Error `json:",omitempty"`
+ GoFiles []string `json:",omitempty"`
+ CompiledGoFiles []string `json:",omitempty"`
+ OtherFiles []string `json:",omitempty"`
+ ExportFile string `json:",omitempty"`
+ Imports map[string]string `json:",omitempty"`
+}
+
+// MarshalJSON returns the Package in its JSON form.
+// For the most part, the structure fields are written out unmodified, and
+// the type and syntax fields are skipped.
+// The imports are written out as just a map of path to package id.
+// The errors are written using a custom type that tries to preserve the
+// structure of error types we know about.
+//
+// This method exists to enable support for additional build systems. It is
+// not intended for use by clients of the API and we may change the format.
+func (p *Package) MarshalJSON() ([]byte, error) {
+ flat := &flatPackage{
+ ID: p.ID,
+ Name: p.Name,
+ PkgPath: p.PkgPath,
+ Errors: p.Errors,
+ GoFiles: p.GoFiles,
+ CompiledGoFiles: p.CompiledGoFiles,
+ OtherFiles: p.OtherFiles,
+ ExportFile: p.ExportFile,
+ }
+ if len(p.Imports) > 0 {
+ flat.Imports = make(map[string]string, len(p.Imports))
+ for path, ipkg := range p.Imports {
+ flat.Imports[path] = ipkg.ID
+ }
+ }
+ return json.Marshal(flat)
+}
+
+// UnmarshalJSON reads in a Package from its JSON format.
+// See MarshalJSON for details about the format accepted.
+func (p *Package) UnmarshalJSON(b []byte) error {
+ flat := &flatPackage{}
+ if err := json.Unmarshal(b, &flat); err != nil {
+ return err
+ }
+ *p = Package{
+ ID: flat.ID,
+ Name: flat.Name,
+ PkgPath: flat.PkgPath,
+ Errors: flat.Errors,
+ GoFiles: flat.GoFiles,
+ CompiledGoFiles: flat.CompiledGoFiles,
+ OtherFiles: flat.OtherFiles,
+ ExportFile: flat.ExportFile,
+ }
+ if len(flat.Imports) > 0 {
+ p.Imports = make(map[string]*Package, len(flat.Imports))
+ for path, id := range flat.Imports {
+ p.Imports[path] = &Package{ID: id}
+ }
+ }
+ return nil
+}
+
+func (p *Package) String() string { return p.ID }
+
+// loaderPackage augments Package with state used during the loading phase
+type loaderPackage struct {
+ *Package
+ importErrors map[string]error // maps each bad import to its error
+ loadOnce sync.Once
+ color uint8 // for cycle detection
+ needsrc bool // load from source (Mode >= LoadTypes)
+ needtypes bool // type information is either requested or depended on
+ initial bool // package was matched by a pattern
+}
+
+// loader holds the working state of a single call to load.
+type loader struct {
+ pkgs map[string]*loaderPackage
+ Config
+ sizes types.Sizes
+ parseCache map[string]*parseValue
+ parseCacheMu sync.Mutex
+ exportMu sync.Mutex // enforces mutual exclusion of exportdata operations
+
+ // Config.Mode contains the implied mode (see impliedLoadMode).
+ // Implied mode contains all the fields we need the data for.
+ // In requestedMode there are the actually requested fields.
+ // We'll zero them out before returning packages to the user.
+ // This makes it easier for us to get the conditions where
+ // we need certain modes right.
+ requestedMode LoadMode
+}
+
+type parseValue struct {
+ f *ast.File
+ err error
+ ready chan struct{}
+}
+
+func newLoader(cfg *Config) *loader {
+ ld := &loader{
+ parseCache: map[string]*parseValue{},
+ }
+ if cfg != nil {
+ ld.Config = *cfg
+ // If the user has provided a logger, use it.
+ ld.Config.Logf = cfg.Logf
+ }
+ if ld.Config.Logf == nil {
+ // If the GOPACKAGESDEBUG environment variable is set to true,
+ // but the user has not provided a logger, default to log.Printf.
+ if debug {
+ ld.Config.Logf = log.Printf
+ } else {
+ ld.Config.Logf = func(format string, args ...interface{}) {}
+ }
+ }
+ if ld.Config.Mode == 0 {
+ ld.Config.Mode = NeedName | NeedFiles | NeedCompiledGoFiles // Preserve zero behavior of Mode for backwards compatibility.
+ }
+ if ld.Config.Env == nil {
+ ld.Config.Env = os.Environ()
+ }
+ if ld.Config.gocmdRunner == nil {
+ ld.Config.gocmdRunner = &gocommand.Runner{}
+ }
+ if ld.Context == nil {
+ ld.Context = context.Background()
+ }
+ if ld.Dir == "" {
+ if dir, err := os.Getwd(); err == nil {
+ ld.Dir = dir
+ }
+ }
+
+ // Save the actually requested fields. We'll zero them out before returning packages to the user.
+ ld.requestedMode = ld.Mode
+ ld.Mode = impliedLoadMode(ld.Mode)
+
+ if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 {
+ if ld.Fset == nil {
+ ld.Fset = token.NewFileSet()
+ }
+
+ // ParseFile is required even in LoadTypes mode
+ // because we load source if export data is missing.
+ if ld.ParseFile == nil {
+ ld.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) {
+ const mode = parser.AllErrors | parser.ParseComments
+ return parser.ParseFile(fset, filename, src, mode)
+ }
+ }
+ }
+
+ return ld
+}
+
+// refine connects the supplied packages into a graph and then adds type and
+// and syntax information as requested by the LoadMode.
+func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
+ rootMap := make(map[string]int, len(roots))
+ for i, root := range roots {
+ rootMap[root] = i
+ }
+ ld.pkgs = make(map[string]*loaderPackage)
+ // first pass, fixup and build the map and roots
+ var initial = make([]*loaderPackage, len(roots))
+ for _, pkg := range list {
+ rootIndex := -1
+ if i, found := rootMap[pkg.ID]; found {
+ rootIndex = i
+ }
+
+ // Overlays can invalidate export data.
+ // TODO(matloob): make this check fine-grained based on dependencies on overlaid files
+ exportDataInvalid := len(ld.Overlay) > 0 || pkg.ExportFile == "" && pkg.PkgPath != "unsafe"
+ // This package needs type information if the caller requested types and the package is
+ // either a root, or it's a non-root and the user requested dependencies ...
+ needtypes := (ld.Mode&NeedTypes|NeedTypesInfo != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0))
+ // This package needs source if the call requested source (or types info, which implies source)
+ // and the package is either a root, or itas a non- root and the user requested dependencies...
+ needsrc := ((ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0)) ||
+ // ... or if we need types and the exportData is invalid. We fall back to (incompletely)
+ // typechecking packages from source if they fail to compile.
+ (ld.Mode&NeedTypes|NeedTypesInfo != 0 && exportDataInvalid)) && pkg.PkgPath != "unsafe"
+ lpkg := &loaderPackage{
+ Package: pkg,
+ needtypes: needtypes,
+ needsrc: needsrc,
+ }
+ ld.pkgs[lpkg.ID] = lpkg
+ if rootIndex >= 0 {
+ initial[rootIndex] = lpkg
+ lpkg.initial = true
+ }
+ }
+ for i, root := range roots {
+ if initial[i] == nil {
+ return nil, fmt.Errorf("root package %v is missing", root)
+ }
+ }
+
+ // Materialize the import graph.
+
+ const (
+ white = 0 // new
+ grey = 1 // in progress
+ black = 2 // complete
+ )
+
+ // visit traverses the import graph, depth-first,
+ // and materializes the graph as Packages.Imports.
+ //
+ // Valid imports are saved in the Packages.Import map.
+ // Invalid imports (cycles and missing nodes) are saved in the importErrors map.
+ // Thus, even in the presence of both kinds of errors, the Import graph remains a DAG.
+ //
+ // visit returns whether the package needs src or has a transitive
+ // dependency on a package that does. These are the only packages
+ // for which we load source code.
+ var stack []*loaderPackage
+ var visit func(lpkg *loaderPackage) bool
+ var srcPkgs []*loaderPackage
+ visit = func(lpkg *loaderPackage) bool {
+ switch lpkg.color {
+ case black:
+ return lpkg.needsrc
+ case grey:
+ panic("internal error: grey node")
+ }
+ lpkg.color = grey
+ stack = append(stack, lpkg) // push
+ stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports
+ // If NeedImports isn't set, the imports fields will all be zeroed out.
+ if ld.Mode&NeedImports != 0 {
+ lpkg.Imports = make(map[string]*Package, len(stubs))
+ for importPath, ipkg := range stubs {
+ var importErr error
+ imp := ld.pkgs[ipkg.ID]
+ if imp == nil {
+ // (includes package "C" when DisableCgo)
+ importErr = fmt.Errorf("missing package: %q", ipkg.ID)
+ } else if imp.color == grey {
+ importErr = fmt.Errorf("import cycle: %s", stack)
+ }
+ if importErr != nil {
+ if lpkg.importErrors == nil {
+ lpkg.importErrors = make(map[string]error)
+ }
+ lpkg.importErrors[importPath] = importErr
+ continue
+ }
+
+ if visit(imp) {
+ lpkg.needsrc = true
+ }
+ lpkg.Imports[importPath] = imp.Package
+ }
+ }
+ if lpkg.needsrc {
+ srcPkgs = append(srcPkgs, lpkg)
+ }
+ if ld.Mode&NeedTypesSizes != 0 {
+ lpkg.TypesSizes = ld.sizes
+ }
+ stack = stack[:len(stack)-1] // pop
+ lpkg.color = black
+
+ return lpkg.needsrc
+ }
+
+ if ld.Mode&NeedImports == 0 {
+ // We do this to drop the stub import packages that we are not even going to try to resolve.
+ for _, lpkg := range initial {
+ lpkg.Imports = nil
+ }
+ } else {
+ // For each initial package, create its import DAG.
+ for _, lpkg := range initial {
+ visit(lpkg)
+ }
+ }
+ if ld.Mode&NeedImports != 0 && ld.Mode&NeedTypes != 0 {
+ for _, lpkg := range srcPkgs {
+ // Complete type information is required for the
+ // immediate dependencies of each source package.
+ for _, ipkg := range lpkg.Imports {
+ imp := ld.pkgs[ipkg.ID]
+ imp.needtypes = true
+ }
+ }
+ }
+ // Load type data and syntax if needed, starting at
+ // the initial packages (roots of the import DAG).
+ if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 {
+ var wg sync.WaitGroup
+ for _, lpkg := range initial {
+ wg.Add(1)
+ go func(lpkg *loaderPackage) {
+ ld.loadRecursive(lpkg)
+ wg.Done()
+ }(lpkg)
+ }
+ wg.Wait()
+ }
+
+ result := make([]*Package, len(initial))
+ for i, lpkg := range initial {
+ result[i] = lpkg.Package
+ }
+ for i := range ld.pkgs {
+ // Clear all unrequested fields, for extra de-Hyrum-ization.
+ if ld.requestedMode&NeedName == 0 {
+ ld.pkgs[i].Name = ""
+ ld.pkgs[i].PkgPath = ""
+ }
+ if ld.requestedMode&NeedFiles == 0 {
+ ld.pkgs[i].GoFiles = nil
+ ld.pkgs[i].OtherFiles = nil
+ }
+ if ld.requestedMode&NeedCompiledGoFiles == 0 {
+ ld.pkgs[i].CompiledGoFiles = nil
+ }
+ if ld.requestedMode&NeedImports == 0 {
+ ld.pkgs[i].Imports = nil
+ }
+ if ld.requestedMode&NeedExportsFile == 0 {
+ ld.pkgs[i].ExportFile = ""
+ }
+ if ld.requestedMode&NeedTypes == 0 {
+ ld.pkgs[i].Types = nil
+ ld.pkgs[i].Fset = nil
+ ld.pkgs[i].IllTyped = false
+ }
+ if ld.requestedMode&NeedSyntax == 0 {
+ ld.pkgs[i].Syntax = nil
+ }
+ if ld.requestedMode&NeedTypesInfo == 0 {
+ ld.pkgs[i].TypesInfo = nil
+ }
+ if ld.requestedMode&NeedTypesSizes == 0 {
+ ld.pkgs[i].TypesSizes = nil
+ }
+ if ld.requestedMode&NeedModule == 0 {
+ ld.pkgs[i].Module = nil
+ }
+ }
+
+ return result, nil
+}
+
+// loadRecursive loads the specified package and its dependencies,
+// recursively, in parallel, in topological order.
+// It is atomic and idempotent.
+// Precondition: ld.Mode&NeedTypes.
+func (ld *loader) loadRecursive(lpkg *loaderPackage) {
+ lpkg.loadOnce.Do(func() {
+ // Load the direct dependencies, in parallel.
+ var wg sync.WaitGroup
+ for _, ipkg := range lpkg.Imports {
+ imp := ld.pkgs[ipkg.ID]
+ wg.Add(1)
+ go func(imp *loaderPackage) {
+ ld.loadRecursive(imp)
+ wg.Done()
+ }(imp)
+ }
+ wg.Wait()
+ ld.loadPackage(lpkg)
+ })
+}
+
+// loadPackage loads the specified package.
+// It must be called only once per Package,
+// after immediate dependencies are loaded.
+// Precondition: ld.Mode & NeedTypes.
+func (ld *loader) loadPackage(lpkg *loaderPackage) {
+ if lpkg.PkgPath == "unsafe" {
+ // Fill in the blanks to avoid surprises.
+ lpkg.Types = types.Unsafe
+ lpkg.Fset = ld.Fset
+ lpkg.Syntax = []*ast.File{}
+ lpkg.TypesInfo = new(types.Info)
+ lpkg.TypesSizes = ld.sizes
+ return
+ }
+
+ // Call NewPackage directly with explicit name.
+ // This avoids skew between golist and go/types when the files'
+ // package declarations are inconsistent.
+ lpkg.Types = types.NewPackage(lpkg.PkgPath, lpkg.Name)
+ lpkg.Fset = ld.Fset
+
+ // Subtle: we populate all Types fields with an empty Package
+ // before loading export data so that export data processing
+ // never has to create a types.Package for an indirect dependency,
+ // which would then require that such created packages be explicitly
+ // inserted back into the Import graph as a final step after export data loading.
+ // The Diamond test exercises this case.
+ if !lpkg.needtypes && !lpkg.needsrc {
+ return
+ }
+ if !lpkg.needsrc {
+ ld.loadFromExportData(lpkg)
+ return // not a source package, don't get syntax trees
+ }
+
+ appendError := func(err error) {
+ // Convert various error types into the one true Error.
+ var errs []Error
+ switch err := err.(type) {
+ case Error:
+ // from driver
+ errs = append(errs, err)
+
+ case *os.PathError:
+ // from parser
+ errs = append(errs, Error{
+ Pos: err.Path + ":1",
+ Msg: err.Err.Error(),
+ Kind: ParseError,
+ })
+
+ case scanner.ErrorList:
+ // from parser
+ for _, err := range err {
+ errs = append(errs, Error{
+ Pos: err.Pos.String(),
+ Msg: err.Msg,
+ Kind: ParseError,
+ })
+ }
+
+ case types.Error:
+ // from type checker
+ errs = append(errs, Error{
+ Pos: err.Fset.Position(err.Pos).String(),
+ Msg: err.Msg,
+ Kind: TypeError,
+ })
+
+ default:
+ // unexpected impoverished error from parser?
+ errs = append(errs, Error{
+ Pos: "-",
+ Msg: err.Error(),
+ Kind: UnknownError,
+ })
+
+ // If you see this error message, please file a bug.
+ log.Printf("internal error: error %q (%T) without position", err, err)
+ }
+
+ lpkg.Errors = append(lpkg.Errors, errs...)
+ }
+
+ if ld.Config.Mode&NeedTypes != 0 && len(lpkg.CompiledGoFiles) == 0 && lpkg.ExportFile != "" {
+ // The config requested loading sources and types, but sources are missing.
+ // Add an error to the package and fall back to loading from export data.
+ appendError(Error{"-", fmt.Sprintf("sources missing for package %s", lpkg.ID), ParseError})
+ ld.loadFromExportData(lpkg)
+ return // can't get syntax trees for this package
+ }
+
+ files, errs := ld.parseFiles(lpkg.CompiledGoFiles)
+ for _, err := range errs {
+ appendError(err)
+ }
+
+ lpkg.Syntax = files
+ if ld.Config.Mode&NeedTypes == 0 {
+ return
+ }
+
+ lpkg.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),
+ }
+ lpkg.TypesSizes = ld.sizes
+
+ importer := importerFunc(func(path string) (*types.Package, error) {
+ if path == "unsafe" {
+ return types.Unsafe, nil
+ }
+
+ // The imports map is keyed by import path.
+ ipkg := lpkg.Imports[path]
+ if ipkg == nil {
+ if err := lpkg.importErrors[path]; err != nil {
+ return nil, err
+ }
+ // There was skew between the metadata and the
+ // import declarations, likely due to an edit
+ // race, or because the ParseFile feature was
+ // used to supply alternative file contents.
+ return nil, fmt.Errorf("no metadata for %s", path)
+ }
+
+ if ipkg.Types != nil && ipkg.Types.Complete() {
+ return ipkg.Types, nil
+ }
+ log.Fatalf("internal error: package %q without types was imported from %q", path, lpkg)
+ panic("unreachable")
+ })
+
+ // type-check
+ tc := &types.Config{
+ Importer: importer,
+
+ // Type-check bodies of functions only in non-initial packages.
+ // Example: for import graph A->B->C and initial packages {A,C},
+ // we can ignore function bodies in B.
+ IgnoreFuncBodies: ld.Mode&NeedDeps == 0 && !lpkg.initial,
+
+ Error: appendError,
+ Sizes: ld.sizes,
+ }
+ if (ld.Mode & TypecheckCgo) != 0 {
+ // TODO: remove this when we stop supporting 1.14.
+ rtc := reflect.ValueOf(tc).Elem()
+ usesCgo := rtc.FieldByName("UsesCgo")
+ if !usesCgo.IsValid() {
+ appendError(Error{
+ Msg: "TypecheckCgo requires Go 1.15+",
+ Kind: ListError,
+ })
+ return
+ }
+ usesCgo.SetBool(true)
+ }
+ types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
+
+ lpkg.importErrors = nil // no longer needed
+
+ // If !Cgo, the type-checker uses FakeImportC mode, so
+ // it doesn't invoke the importer for import "C",
+ // nor report an error for the import,
+ // or for any undefined C.f reference.
+ // We must detect this explicitly and correctly
+ // mark the package as IllTyped (by reporting an error).
+ // TODO(adonovan): if these errors are annoying,
+ // we could just set IllTyped quietly.
+ if tc.FakeImportC {
+ outer:
+ for _, f := range lpkg.Syntax {
+ for _, imp := range f.Imports {
+ if imp.Path.Value == `"C"` {
+ err := types.Error{Fset: ld.Fset, Pos: imp.Pos(), Msg: `import "C" ignored`}
+ appendError(err)
+ break outer
+ }
+ }
+ }
+ }
+
+ // Record accumulated errors.
+ illTyped := len(lpkg.Errors) > 0
+ if !illTyped {
+ for _, imp := range lpkg.Imports {
+ if imp.IllTyped {
+ illTyped = true
+ break
+ }
+ }
+ }
+ lpkg.IllTyped = illTyped
+}
+
+// An importFunc is an implementation of the single-method
+// types.Importer interface based on a function value.
+type importerFunc func(path string) (*types.Package, error)
+
+func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
+
+// We use a counting semaphore to limit
+// the number of parallel I/O calls per process.
+var ioLimit = make(chan bool, 20)
+
+func (ld *loader) parseFile(filename string) (*ast.File, error) {
+ ld.parseCacheMu.Lock()
+ v, ok := ld.parseCache[filename]
+ if ok {
+ // cache hit
+ ld.parseCacheMu.Unlock()
+ <-v.ready
+ } else {
+ // cache miss
+ v = &parseValue{ready: make(chan struct{})}
+ ld.parseCache[filename] = v
+ ld.parseCacheMu.Unlock()
+
+ var src []byte
+ for f, contents := range ld.Config.Overlay {
+ if sameFile(f, filename) {
+ src = contents
+ }
+ }
+ var err error
+ if src == nil {
+ ioLimit <- true // wait
+ src, err = ioutil.ReadFile(filename)
+ <-ioLimit // signal
+ }
+ if err != nil {
+ v.err = err
+ } else {
+ v.f, v.err = ld.ParseFile(ld.Fset, filename, src)
+ }
+
+ close(v.ready)
+ }
+ return v.f, v.err
+}
+
+// parseFiles reads and parses the Go source files and returns the ASTs
+// of the ones that could be at least partially parsed, along with a
+// list of I/O and parse errors encountered.
+//
+// Because files are scanned in parallel, the token.Pos
+// positions of the resulting ast.Files are not ordered.
+//
+func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) {
+ var wg sync.WaitGroup
+ n := len(filenames)
+ parsed := make([]*ast.File, n)
+ errors := make([]error, n)
+ for i, file := range filenames {
+ if ld.Config.Context.Err() != nil {
+ parsed[i] = nil
+ errors[i] = ld.Config.Context.Err()
+ continue
+ }
+ wg.Add(1)
+ go func(i int, filename string) {
+ parsed[i], errors[i] = ld.parseFile(filename)
+ wg.Done()
+ }(i, file)
+ }
+ wg.Wait()
+
+ // Eliminate nils, preserving order.
+ var o int
+ for _, f := range parsed {
+ if f != nil {
+ parsed[o] = f
+ o++
+ }
+ }
+ parsed = parsed[:o]
+
+ o = 0
+ for _, err := range errors {
+ if err != nil {
+ errors[o] = err
+ o++
+ }
+ }
+ errors = errors[:o]
+
+ return parsed, errors
+}
+
+// sameFile returns true if x and y have the same basename and denote
+// the same file.
+//
+func sameFile(x, y string) bool {
+ if x == y {
+ // It could be the case that y doesn't exist.
+ // For instance, it may be an overlay file that
+ // hasn't been written to disk. To handle that case
+ // let x == y through. (We added the exact absolute path
+ // string to the CompiledGoFiles list, so the unwritten
+ // overlay case implies x==y.)
+ return true
+ }
+ if strings.EqualFold(filepath.Base(x), filepath.Base(y)) { // (optimisation)
+ if xi, err := os.Stat(x); err == nil {
+ if yi, err := os.Stat(y); err == nil {
+ return os.SameFile(xi, yi)
+ }
+ }
+ }
+ return false
+}
+
+// loadFromExportData returns type information for the specified
+// package, loading it from an export data file on the first request.
+func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error) {
+ if lpkg.PkgPath == "" {
+ log.Fatalf("internal error: Package %s has no PkgPath", lpkg)
+ }
+
+ // Because gcexportdata.Read has the potential to create or
+ // modify the types.Package for each node in the transitive
+ // closure of dependencies of lpkg, all exportdata operations
+ // must be sequential. (Finer-grained locking would require
+ // changes to the gcexportdata API.)
+ //
+ // The exportMu lock guards the Package.Pkg field and the
+ // types.Package it points to, for each Package in the graph.
+ //
+ // Not all accesses to Package.Pkg need to be protected by exportMu:
+ // graph ordering ensures that direct dependencies of source
+ // packages are fully loaded before the importer reads their Pkg field.
+ ld.exportMu.Lock()
+ defer ld.exportMu.Unlock()
+
+ if tpkg := lpkg.Types; tpkg != nil && tpkg.Complete() {
+ return tpkg, nil // cache hit
+ }
+
+ lpkg.IllTyped = true // fail safe
+
+ if lpkg.ExportFile == "" {
+ // Errors while building export data will have been printed to stderr.
+ return nil, fmt.Errorf("no export data file")
+ }
+ f, err := os.Open(lpkg.ExportFile)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ // Read gc export data.
+ //
+ // We don't currently support gccgo export data because all
+ // underlying workspaces use the gc toolchain. (Even build
+ // systems that support gccgo don't use it for workspace
+ // queries.)
+ r, err := gcexportdata.NewReader(f)
+ if err != nil {
+ return nil, fmt.Errorf("reading %s: %v", lpkg.ExportFile, err)
+ }
+
+ // Build the view.
+ //
+ // The gcexportdata machinery has no concept of package ID.
+ // It identifies packages by their PkgPath, which although not
+ // globally unique is unique within the scope of one invocation
+ // of the linker, type-checker, or gcexportdata.
+ //
+ // So, we must build a PkgPath-keyed view of the global
+ // (conceptually ID-keyed) cache of packages and pass it to
+ // gcexportdata. The view must contain every existing
+ // package that might possibly be mentioned by the
+ // current package---its transitive closure.
+ //
+ // In loadPackage, we unconditionally create a types.Package for
+ // each dependency so that export data loading does not
+ // create new ones.
+ //
+ // TODO(adonovan): it would be simpler and more efficient
+ // if the export data machinery invoked a callback to
+ // get-or-create a package instead of a map.
+ //
+ view := make(map[string]*types.Package) // view seen by gcexportdata
+ seen := make(map[*loaderPackage]bool) // all visited packages
+ var visit func(pkgs map[string]*Package)
+ visit = func(pkgs map[string]*Package) {
+ for _, p := range pkgs {
+ lpkg := ld.pkgs[p.ID]
+ if !seen[lpkg] {
+ seen[lpkg] = true
+ view[lpkg.PkgPath] = lpkg.Types
+ visit(lpkg.Imports)
+ }
+ }
+ }
+ visit(lpkg.Imports)
+
+ viewLen := len(view) + 1 // adding the self package
+ // Parse the export data.
+ // (May modify incomplete packages in view but not create new ones.)
+ tpkg, err := gcexportdata.Read(r, ld.Fset, view, lpkg.PkgPath)
+ if err != nil {
+ return nil, fmt.Errorf("reading %s: %v", lpkg.ExportFile, err)
+ }
+ if viewLen != len(view) {
+ log.Fatalf("Unexpected package creation during export data loading")
+ }
+
+ lpkg.Types = tpkg
+ lpkg.IllTyped = false
+
+ return tpkg, nil
+}
+
+// impliedLoadMode returns loadMode with its dependencies.
+func impliedLoadMode(loadMode LoadMode) LoadMode {
+ if loadMode&NeedTypesInfo != 0 && loadMode&NeedImports == 0 {
+ // If NeedTypesInfo, go/packages needs to do typechecking itself so it can
+ // associate type info with the AST. To do so, we need the export data
+ // for dependencies, which means we need to ask for the direct dependencies.
+ // NeedImports is used to ask for the direct dependencies.
+ loadMode |= NeedImports
+ }
+
+ if loadMode&NeedDeps != 0 && loadMode&NeedImports == 0 {
+ // With NeedDeps we need to load at least direct dependencies.
+ // NeedImports is used to ask for the direct dependencies.
+ loadMode |= NeedImports
+ }
+
+ return loadMode
+}
+
+func usesExportData(cfg *Config) bool {
+ return cfg.Mode&NeedExportsFile != 0 || cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedDeps == 0
+}
diff --git a/vendor/golang.org/x/tools/go/packages/visit.go b/vendor/golang.org/x/tools/go/packages/visit.go
new file mode 100644
index 000000000..b13cb081f
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/packages/visit.go
@@ -0,0 +1,55 @@
+package packages
+
+import (
+ "fmt"
+ "os"
+ "sort"
+)
+
+// Visit visits all the packages in the import graph whose roots are
+// pkgs, calling the optional pre function the first time each package
+// is encountered (preorder), and the optional post function after a
+// package's dependencies have been visited (postorder).
+// The boolean result of pre(pkg) determines whether
+// the imports of package pkg are visited.
+func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) {
+ seen := make(map[*Package]bool)
+ var visit func(*Package)
+ visit = func(pkg *Package) {
+ if !seen[pkg] {
+ seen[pkg] = true
+
+ if pre == nil || pre(pkg) {
+ paths := make([]string, 0, len(pkg.Imports))
+ for path := range pkg.Imports {
+ paths = append(paths, path)
+ }
+ sort.Strings(paths) // Imports is a map, this makes visit stable
+ for _, path := range paths {
+ visit(pkg.Imports[path])
+ }
+ }
+
+ if post != nil {
+ post(pkg)
+ }
+ }
+ }
+ for _, pkg := range pkgs {
+ visit(pkg)
+ }
+}
+
+// PrintErrors prints to os.Stderr the accumulated errors of all
+// packages in the import graph rooted at pkgs, dependencies first.
+// PrintErrors returns the number of errors printed.
+func PrintErrors(pkgs []*Package) int {
+ var n int
+ Visit(pkgs, nil, func(pkg *Package) {
+ for _, err := range pkg.Errors {
+ fmt.Fprintln(os.Stderr, err)
+ n++
+ }
+ })
+ return n
+}
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 000000000..cffd7acbe
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go
@@ -0,0 +1,524 @@
+// 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
+ names := scope.Names()
+ for _, name := range 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 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/go/types/typeutil/callee.go b/vendor/golang.org/x/tools/go/types/typeutil/callee.go
new file mode 100644
index 000000000..38f596daf
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/types/typeutil/callee.go
@@ -0,0 +1,46 @@
+// 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 typeutil
+
+import (
+ "go/ast"
+ "go/types"
+
+ "golang.org/x/tools/go/ast/astutil"
+)
+
+// Callee returns the named target of a function call, if any:
+// a function, method, builtin, or variable.
+func Callee(info *types.Info, call *ast.CallExpr) types.Object {
+ var obj types.Object
+ switch fun := astutil.Unparen(call.Fun).(type) {
+ case *ast.Ident:
+ obj = info.Uses[fun] // type, var, builtin, or declared func
+ case *ast.SelectorExpr:
+ if sel, ok := info.Selections[fun]; ok {
+ obj = sel.Obj() // method or field
+ } else {
+ obj = info.Uses[fun.Sel] // qualified identifier?
+ }
+ }
+ if _, ok := obj.(*types.TypeName); ok {
+ return nil // T(x) is a conversion, not a call
+ }
+ return obj
+}
+
+// StaticCallee returns the target (function or method) of a static
+// function call, if any. It returns nil for calls to builtins.
+func StaticCallee(info *types.Info, call *ast.CallExpr) *types.Func {
+ if f, ok := Callee(info, call).(*types.Func); ok && !interfaceMethod(f) {
+ return f
+ }
+ return nil
+}
+
+func interfaceMethod(f *types.Func) bool {
+ recv := f.Type().(*types.Signature).Recv()
+ return recv != nil && types.IsInterface(recv.Type())
+}
diff --git a/vendor/golang.org/x/tools/go/types/typeutil/imports.go b/vendor/golang.org/x/tools/go/types/typeutil/imports.go
new file mode 100644
index 000000000..9c441dba9
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/types/typeutil/imports.go
@@ -0,0 +1,31 @@
+// Copyright 2014 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 typeutil
+
+import "go/types"
+
+// Dependencies returns all dependencies of the specified packages.
+//
+// Dependent packages appear in topological order: if package P imports
+// package Q, Q appears earlier than P in the result.
+// The algorithm follows import statements in the order they
+// appear in the source code, so the result is a total order.
+//
+func Dependencies(pkgs ...*types.Package) []*types.Package {
+ var result []*types.Package
+ seen := make(map[*types.Package]bool)
+ var visit func(pkgs []*types.Package)
+ visit = func(pkgs []*types.Package) {
+ for _, p := range pkgs {
+ if !seen[p] {
+ seen[p] = true
+ visit(p.Imports())
+ result = append(result, p)
+ }
+ }
+ }
+ visit(pkgs)
+ return result
+}
diff --git a/vendor/golang.org/x/tools/go/types/typeutil/map.go b/vendor/golang.org/x/tools/go/types/typeutil/map.go
new file mode 100644
index 000000000..c7f754500
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/types/typeutil/map.go
@@ -0,0 +1,313 @@
+// Copyright 2014 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 typeutil defines various utilities for types, such as Map,
+// a mapping from types.Type to interface{} values.
+package typeutil // import "golang.org/x/tools/go/types/typeutil"
+
+import (
+ "bytes"
+ "fmt"
+ "go/types"
+ "reflect"
+)
+
+// Map is a hash-table-based mapping from types (types.Type) to
+// arbitrary interface{} values. The concrete types that implement
+// the Type interface are pointers. Since they are not canonicalized,
+// == cannot be used to check for equivalence, and thus we cannot
+// simply use a Go map.
+//
+// Just as with map[K]V, a nil *Map is a valid empty map.
+//
+// Not thread-safe.
+//
+type Map struct {
+ hasher Hasher // shared by many Maps
+ table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
+ length int // number of map entries
+}
+
+// entry is an entry (key/value association) in a hash bucket.
+type entry struct {
+ key types.Type
+ value interface{}
+}
+
+// SetHasher sets the hasher used by Map.
+//
+// All Hashers are functionally equivalent but contain internal state
+// used to cache the results of hashing previously seen types.
+//
+// A single Hasher created by MakeHasher() may be shared among many
+// Maps. This is recommended if the instances have many keys in
+// common, as it will amortize the cost of hash computation.
+//
+// A Hasher may grow without bound as new types are seen. Even when a
+// type is deleted from the map, the Hasher never shrinks, since other
+// types in the map may reference the deleted type indirectly.
+//
+// Hashers are not thread-safe, and read-only operations such as
+// Map.Lookup require updates to the hasher, so a full Mutex lock (not a
+// read-lock) is require around all Map operations if a shared
+// hasher is accessed from multiple threads.
+//
+// If SetHasher is not called, the Map will create a private hasher at
+// the first call to Insert.
+//
+func (m *Map) SetHasher(hasher Hasher) {
+ m.hasher = hasher
+}
+
+// Delete removes the entry with the given key, if any.
+// It returns true if the entry was found.
+//
+func (m *Map) Delete(key types.Type) bool {
+ if m != nil && m.table != nil {
+ hash := m.hasher.Hash(key)
+ bucket := m.table[hash]
+ for i, e := range bucket {
+ if e.key != nil && types.Identical(key, e.key) {
+ // We can't compact the bucket as it
+ // would disturb iterators.
+ bucket[i] = entry{}
+ m.length--
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// At returns the map entry for the given key.
+// The result is nil if the entry is not present.
+//
+func (m *Map) At(key types.Type) interface{} {
+ if m != nil && m.table != nil {
+ for _, e := range m.table[m.hasher.Hash(key)] {
+ if e.key != nil && types.Identical(key, e.key) {
+ return e.value
+ }
+ }
+ }
+ return nil
+}
+
+// Set sets the map entry for key to val,
+// and returns the previous entry, if any.
+func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) {
+ if m.table != nil {
+ hash := m.hasher.Hash(key)
+ bucket := m.table[hash]
+ var hole *entry
+ for i, e := range bucket {
+ if e.key == nil {
+ hole = &bucket[i]
+ } else if types.Identical(key, e.key) {
+ prev = e.value
+ bucket[i].value = value
+ return
+ }
+ }
+
+ if hole != nil {
+ *hole = entry{key, value} // overwrite deleted entry
+ } else {
+ m.table[hash] = append(bucket, entry{key, value})
+ }
+ } else {
+ if m.hasher.memo == nil {
+ m.hasher = MakeHasher()
+ }
+ hash := m.hasher.Hash(key)
+ m.table = map[uint32][]entry{hash: {entry{key, value}}}
+ }
+
+ m.length++
+ return
+}
+
+// Len returns the number of map entries.
+func (m *Map) Len() int {
+ if m != nil {
+ return m.length
+ }
+ return 0
+}
+
+// Iterate calls function f on each entry in the map in unspecified order.
+//
+// If f should mutate the map, Iterate provides the same guarantees as
+// Go maps: if f deletes a map entry that Iterate has not yet reached,
+// f will not be invoked for it, but if f inserts a map entry that
+// Iterate has not yet reached, whether or not f will be invoked for
+// it is unspecified.
+//
+func (m *Map) Iterate(f func(key types.Type, value interface{})) {
+ if m != nil {
+ for _, bucket := range m.table {
+ for _, e := range bucket {
+ if e.key != nil {
+ f(e.key, e.value)
+ }
+ }
+ }
+ }
+}
+
+// Keys returns a new slice containing the set of map keys.
+// The order is unspecified.
+func (m *Map) Keys() []types.Type {
+ keys := make([]types.Type, 0, m.Len())
+ m.Iterate(func(key types.Type, _ interface{}) {
+ keys = append(keys, key)
+ })
+ return keys
+}
+
+func (m *Map) toString(values bool) string {
+ if m == nil {
+ return "{}"
+ }
+ var buf bytes.Buffer
+ fmt.Fprint(&buf, "{")
+ sep := ""
+ m.Iterate(func(key types.Type, value interface{}) {
+ fmt.Fprint(&buf, sep)
+ sep = ", "
+ fmt.Fprint(&buf, key)
+ if values {
+ fmt.Fprintf(&buf, ": %q", value)
+ }
+ })
+ fmt.Fprint(&buf, "}")
+ return buf.String()
+}
+
+// String returns a string representation of the map's entries.
+// Values are printed using fmt.Sprintf("%v", v).
+// Order is unspecified.
+//
+func (m *Map) String() string {
+ return m.toString(true)
+}
+
+// KeysString returns a string representation of the map's key set.
+// Order is unspecified.
+//
+func (m *Map) KeysString() string {
+ return m.toString(false)
+}
+
+////////////////////////////////////////////////////////////////////////
+// Hasher
+
+// A Hasher maps each type to its hash value.
+// For efficiency, a hasher uses memoization; thus its memory
+// footprint grows monotonically over time.
+// Hashers are not thread-safe.
+// Hashers have reference semantics.
+// Call MakeHasher to create a Hasher.
+type Hasher struct {
+ memo map[types.Type]uint32
+}
+
+// MakeHasher returns a new Hasher instance.
+func MakeHasher() Hasher {
+ return Hasher{make(map[types.Type]uint32)}
+}
+
+// Hash computes a hash value for the given type t such that
+// Identical(t, t') => Hash(t) == Hash(t').
+func (h Hasher) Hash(t types.Type) uint32 {
+ hash, ok := h.memo[t]
+ if !ok {
+ hash = h.hashFor(t)
+ h.memo[t] = hash
+ }
+ return hash
+}
+
+// hashString computes the Fowler–Noll–Vo hash of s.
+func hashString(s string) uint32 {
+ var h uint32
+ for i := 0; i < len(s); i++ {
+ h ^= uint32(s[i])
+ h *= 16777619
+ }
+ return h
+}
+
+// hashFor computes the hash of t.
+func (h Hasher) hashFor(t types.Type) uint32 {
+ // See Identical for rationale.
+ switch t := t.(type) {
+ case *types.Basic:
+ return uint32(t.Kind())
+
+ case *types.Array:
+ return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem())
+
+ case *types.Slice:
+ return 9049 + 2*h.Hash(t.Elem())
+
+ case *types.Struct:
+ var hash uint32 = 9059
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ f := t.Field(i)
+ if f.Anonymous() {
+ hash += 8861
+ }
+ hash += hashString(t.Tag(i))
+ hash += hashString(f.Name()) // (ignore f.Pkg)
+ hash += h.Hash(f.Type())
+ }
+ return hash
+
+ case *types.Pointer:
+ return 9067 + 2*h.Hash(t.Elem())
+
+ case *types.Signature:
+ var hash uint32 = 9091
+ if t.Variadic() {
+ hash *= 8863
+ }
+ return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
+
+ case *types.Interface:
+ var hash uint32 = 9103
+ for i, n := 0, t.NumMethods(); i < n; i++ {
+ // See go/types.identicalMethods for rationale.
+ // Method order is not significant.
+ // Ignore m.Pkg().
+ m := t.Method(i)
+ hash += 3*hashString(m.Name()) + 5*h.Hash(m.Type())
+ }
+ return hash
+
+ case *types.Map:
+ return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem())
+
+ case *types.Chan:
+ return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
+
+ case *types.Named:
+ // Not safe with a copying GC; objects may move.
+ return uint32(reflect.ValueOf(t.Obj()).Pointer())
+
+ case *types.Tuple:
+ return h.hashTuple(t)
+ }
+ panic(t)
+}
+
+func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
+ // See go/types.identicalTypes for rationale.
+ n := tuple.Len()
+ var hash uint32 = 9137 + 2*uint32(n)
+ for i := 0; i < n; i++ {
+ hash += 3 * h.Hash(tuple.At(i).Type())
+ }
+ return hash
+}
diff --git a/vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go b/vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go
new file mode 100644
index 000000000..32084610f
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go
@@ -0,0 +1,72 @@
+// Copyright 2014 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.
+
+// This file implements a cache of method sets.
+
+package typeutil
+
+import (
+ "go/types"
+ "sync"
+)
+
+// A MethodSetCache records the method set of each type T for which
+// MethodSet(T) is called so that repeat queries are fast.
+// The zero value is a ready-to-use cache instance.
+type MethodSetCache struct {
+ mu sync.Mutex
+ named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N
+ others map[types.Type]*types.MethodSet // all other types
+}
+
+// MethodSet returns the method set of type T. It is thread-safe.
+//
+// If cache is nil, this function is equivalent to types.NewMethodSet(T).
+// Utility functions can thus expose an optional *MethodSetCache
+// parameter to clients that care about performance.
+//
+func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet {
+ if cache == nil {
+ return types.NewMethodSet(T)
+ }
+ cache.mu.Lock()
+ defer cache.mu.Unlock()
+
+ switch T := T.(type) {
+ case *types.Named:
+ return cache.lookupNamed(T).value
+
+ case *types.Pointer:
+ if N, ok := T.Elem().(*types.Named); ok {
+ return cache.lookupNamed(N).pointer
+ }
+ }
+
+ // all other types
+ // (The map uses pointer equivalence, not type identity.)
+ mset := cache.others[T]
+ if mset == nil {
+ mset = types.NewMethodSet(T)
+ if cache.others == nil {
+ cache.others = make(map[types.Type]*types.MethodSet)
+ }
+ cache.others[T] = mset
+ }
+ return mset
+}
+
+func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } {
+ if cache.named == nil {
+ cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet })
+ }
+ // Avoid recomputing mset(*T) for each distinct Pointer
+ // instance whose underlying type is a named type.
+ msets, ok := cache.named[named]
+ if !ok {
+ msets.value = types.NewMethodSet(named)
+ msets.pointer = types.NewMethodSet(types.NewPointer(named))
+ cache.named[named] = msets
+ }
+ return msets
+}
diff --git a/vendor/golang.org/x/tools/go/types/typeutil/ui.go b/vendor/golang.org/x/tools/go/types/typeutil/ui.go
new file mode 100644
index 000000000..9849c24ce
--- /dev/null
+++ b/vendor/golang.org/x/tools/go/types/typeutil/ui.go
@@ -0,0 +1,52 @@
+// Copyright 2014 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 typeutil
+
+// This file defines utilities for user interfaces that display types.
+
+import "go/types"
+
+// IntuitiveMethodSet returns the intuitive method set of a type T,
+// which is the set of methods you can call on an addressable value of
+// that type.
+//
+// The result always contains MethodSet(T), and is exactly MethodSet(T)
+// for interface types and for pointer-to-concrete types.
+// For all other concrete types T, the result additionally
+// contains each method belonging to *T if there is no identically
+// named method on T itself.
+//
+// This corresponds to user intuition about method sets;
+// this function is intended only for user interfaces.
+//
+// The order of the result is as for types.MethodSet(T).
+//
+func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {
+ isPointerToConcrete := func(T types.Type) bool {
+ ptr, ok := T.(*types.Pointer)
+ return ok && !types.IsInterface(ptr.Elem())
+ }
+
+ var result []*types.Selection
+ mset := msets.MethodSet(T)
+ if types.IsInterface(T) || isPointerToConcrete(T) {
+ for i, n := 0, mset.Len(); i < n; i++ {
+ result = append(result, mset.At(i))
+ }
+ } else {
+ // T is some other concrete type.
+ // Report methods of T and *T, preferring those of T.
+ pmset := msets.MethodSet(types.NewPointer(T))
+ for i, n := 0, pmset.Len(); i < n; i++ {
+ meth := pmset.At(i)
+ if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
+ meth = m
+ }
+ result = append(result, meth)
+ }
+
+ }
+ return result
+}
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 000000000..26586810c
--- /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/golang.org/x/tools/internal/event/core/event.go b/vendor/golang.org/x/tools/internal/event/core/event.go
new file mode 100644
index 000000000..e37b49491
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/event/core/event.go
@@ -0,0 +1,85 @@
+// Copyright 2019 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 core provides support for event based telemetry.
+package core
+
+import (
+ "fmt"
+ "time"
+
+ "golang.org/x/tools/internal/event/label"
+)
+
+// Event holds the information about an event of note that ocurred.
+type Event struct {
+ at time.Time
+
+ // As events are often on the stack, storing the first few labels directly
+ // in the event can avoid an allocation at all for the very common cases of
+ // simple events.
+ // The length needs to be large enough to cope with the majority of events
+ // but no so large as to cause undue stack pressure.
+ // A log message with two values will use 3 labels (one for each value and
+ // one for the message itself).
+
+ static [3]label.Label // inline storage for the first few labels
+ dynamic []label.Label // dynamically sized storage for remaining labels
+}
+
+// eventLabelMap implements label.Map for a the labels of an Event.
+type eventLabelMap struct {
+ event Event
+}
+
+func (ev Event) At() time.Time { return ev.at }
+
+func (ev Event) Format(f fmt.State, r rune) {
+ if !ev.at.IsZero() {
+ fmt.Fprint(f, ev.at.Format("2006/01/02 15:04:05 "))
+ }
+ for index := 0; ev.Valid(index); index++ {
+ if l := ev.Label(index); l.Valid() {
+ fmt.Fprintf(f, "\n\t%v", l)
+ }
+ }
+}
+
+func (ev Event) Valid(index int) bool {
+ return index >= 0 && index < len(ev.static)+len(ev.dynamic)
+}
+
+func (ev Event) Label(index int) label.Label {
+ if index < len(ev.static) {
+ return ev.static[index]
+ }
+ return ev.dynamic[index-len(ev.static)]
+}
+
+func (ev Event) Find(key label.Key) label.Label {
+ for _, l := range ev.static {
+ if l.Key() == key {
+ return l
+ }
+ }
+ for _, l := range ev.dynamic {
+ if l.Key() == key {
+ return l
+ }
+ }
+ return label.Label{}
+}
+
+func MakeEvent(static [3]label.Label, labels []label.Label) Event {
+ return Event{
+ static: static,
+ dynamic: labels,
+ }
+}
+
+// CloneEvent event returns a copy of the event with the time adjusted to at.
+func CloneEvent(ev Event, at time.Time) Event {
+ ev.at = at
+ return ev
+}
diff --git a/vendor/golang.org/x/tools/internal/event/core/export.go b/vendor/golang.org/x/tools/internal/event/core/export.go
new file mode 100644
index 000000000..05f3a9a57
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/event/core/export.go
@@ -0,0 +1,70 @@
+// Copyright 2019 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 core
+
+import (
+ "context"
+ "sync/atomic"
+ "time"
+ "unsafe"
+
+ "golang.org/x/tools/internal/event/label"
+)
+
+// Exporter is a function that handles events.
+// It may return a modified context and event.
+type Exporter func(context.Context, Event, label.Map) context.Context
+
+var (
+ exporter unsafe.Pointer
+)
+
+// SetExporter sets the global exporter function that handles all events.
+// The exporter is called synchronously from the event call site, so it should
+// return quickly so as not to hold up user code.
+func SetExporter(e Exporter) {
+ p := unsafe.Pointer(&e)
+ if e == nil {
+ // &e is always valid, and so p is always valid, but for the early abort
+ // of ProcessEvent to be efficient it needs to make the nil check on the
+ // pointer without having to dereference it, so we make the nil function
+ // also a nil pointer
+ p = nil
+ }
+ atomic.StorePointer(&exporter, p)
+}
+
+// deliver is called to deliver an event to the supplied exporter.
+// it will fill in the time.
+func deliver(ctx context.Context, exporter Exporter, ev Event) context.Context {
+ // add the current time to the event
+ ev.at = time.Now()
+ // hand the event off to the current exporter
+ return exporter(ctx, ev, ev)
+}
+
+// Export is called to deliver an event to the global exporter if set.
+func Export(ctx context.Context, ev Event) context.Context {
+ // get the global exporter and abort early if there is not one
+ exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
+ if exporterPtr == nil {
+ return ctx
+ }
+ return deliver(ctx, *exporterPtr, ev)
+}
+
+// ExportPair is called to deliver a start event to the supplied exporter.
+// It also returns a function that will deliver the end event to the same
+// exporter.
+// It will fill in the time.
+func ExportPair(ctx context.Context, begin, end Event) (context.Context, func()) {
+ // get the global exporter and abort early if there is not one
+ exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter))
+ if exporterPtr == nil {
+ return ctx, func() {}
+ }
+ ctx = deliver(ctx, *exporterPtr, begin)
+ return ctx, func() { deliver(ctx, *exporterPtr, end) }
+}
diff --git a/vendor/golang.org/x/tools/internal/event/core/fast.go b/vendor/golang.org/x/tools/internal/event/core/fast.go
new file mode 100644
index 000000000..06c1d4615
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/event/core/fast.go
@@ -0,0 +1,77 @@
+// Copyright 2019 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 core
+
+import (
+ "context"
+
+ "golang.org/x/tools/internal/event/keys"
+ "golang.org/x/tools/internal/event/label"
+)
+
+// Log1 takes a message and one label delivers a log event to the exporter.
+// It is a customized version of Print that is faster and does no allocation.
+func Log1(ctx context.Context, message string, t1 label.Label) {
+ Export(ctx, MakeEvent([3]label.Label{
+ keys.Msg.Of(message),
+ t1,
+ }, nil))
+}
+
+// Log2 takes a message and two labels and delivers a log event to the exporter.
+// It is a customized version of Print that is faster and does no allocation.
+func Log2(ctx context.Context, message string, t1 label.Label, t2 label.Label) {
+ Export(ctx, MakeEvent([3]label.Label{
+ keys.Msg.Of(message),
+ t1,
+ t2,
+ }, nil))
+}
+
+// Metric1 sends a label event to the exporter with the supplied labels.
+func Metric1(ctx context.Context, t1 label.Label) context.Context {
+ return Export(ctx, MakeEvent([3]label.Label{
+ keys.Metric.New(),
+ t1,
+ }, nil))
+}
+
+// Metric2 sends a label event to the exporter with the supplied labels.
+func Metric2(ctx context.Context, t1, t2 label.Label) context.Context {
+ return Export(ctx, MakeEvent([3]label.Label{
+ keys.Metric.New(),
+ t1,
+ t2,
+ }, nil))
+}
+
+// Start1 sends a span start event with the supplied label list to the exporter.
+// It also returns a function that will end the span, which should normally be
+// deferred.
+func Start1(ctx context.Context, name string, t1 label.Label) (context.Context, func()) {
+ return ExportPair(ctx,
+ MakeEvent([3]label.Label{
+ keys.Start.Of(name),
+ t1,
+ }, nil),
+ MakeEvent([3]label.Label{
+ keys.End.New(),
+ }, nil))
+}
+
+// Start2 sends a span start event with the supplied label list to the exporter.
+// It also returns a function that will end the span, which should normally be
+// deferred.
+func Start2(ctx context.Context, name string, t1, t2 label.Label) (context.Context, func()) {
+ return ExportPair(ctx,
+ MakeEvent([3]label.Label{
+ keys.Start.Of(name),
+ t1,
+ t2,
+ }, nil),
+ MakeEvent([3]label.Label{
+ keys.End.New(),
+ }, nil))
+}
diff --git a/vendor/golang.org/x/tools/internal/event/doc.go b/vendor/golang.org/x/tools/internal/event/doc.go
new file mode 100644
index 000000000..5dc6e6bab
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/event/doc.go
@@ -0,0 +1,7 @@
+// Copyright 2019 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 event provides a set of packages that cover the main
+// concepts of telemetry in an implementation agnostic way.
+package event
diff --git a/vendor/golang.org/x/tools/internal/event/event.go b/vendor/golang.org/x/tools/internal/event/event.go
new file mode 100644
index 000000000..4d55e577d
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/event/event.go
@@ -0,0 +1,127 @@
+// Copyright 2019 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 event
+
+import (
+ "context"
+
+ "golang.org/x/tools/internal/event/core"
+ "golang.org/x/tools/internal/event/keys"
+ "golang.org/x/tools/internal/event/label"
+)
+
+// Exporter is a function that handles events.
+// It may return a modified context and event.
+type Exporter func(context.Context, core.Event, label.Map) context.Context
+
+// SetExporter sets the global exporter function that handles all events.
+// The exporter is called synchronously from the event call site, so it should
+// return quickly so as not to hold up user code.
+func SetExporter(e Exporter) {
+ core.SetExporter(core.Exporter(e))
+}
+
+// Log takes a message and a label list and combines them into a single event
+// before delivering them to the exporter.
+func Log(ctx context.Context, message string, labels ...label.Label) {
+ core.Export(ctx, core.MakeEvent([3]label.Label{
+ keys.Msg.Of(message),
+ }, labels))
+}
+
+// IsLog returns true if the event was built by the Log function.
+// It is intended to be used in exporters to identify the semantics of the
+// event when deciding what to do with it.
+func IsLog(ev core.Event) bool {
+ return ev.Label(0).Key() == keys.Msg
+}
+
+// Error takes a message and a label list and combines them into a single event
+// before delivering them to the exporter. It captures the error in the
+// delivered event.
+func Error(ctx context.Context, message string, err error, labels ...label.Label) {
+ core.Export(ctx, core.MakeEvent([3]label.Label{
+ keys.Msg.Of(message),
+ keys.Err.Of(err),
+ }, labels))
+}
+
+// IsError returns true if the event was built by the Error function.
+// It is intended to be used in exporters to identify the semantics of the
+// event when deciding what to do with it.
+func IsError(ev core.Event) bool {
+ return ev.Label(0).Key() == keys.Msg &&
+ ev.Label(1).Key() == keys.Err
+}
+
+// Metric sends a label event to the exporter with the supplied labels.
+func Metric(ctx context.Context, labels ...label.Label) {
+ core.Export(ctx, core.MakeEvent([3]label.Label{
+ keys.Metric.New(),
+ }, labels))
+}
+
+// IsMetric returns true if the event was built by the Metric function.
+// It is intended to be used in exporters to identify the semantics of the
+// event when deciding what to do with it.
+func IsMetric(ev core.Event) bool {
+ return ev.Label(0).Key() == keys.Metric
+}
+
+// Label sends a label event to the exporter with the supplied labels.
+func Label(ctx context.Context, labels ...label.Label) context.Context {
+ return core.Export(ctx, core.MakeEvent([3]label.Label{
+ keys.Label.New(),
+ }, labels))
+}
+
+// IsLabel returns true if the event was built by the Label function.
+// It is intended to be used in exporters to identify the semantics of the
+// event when deciding what to do with it.
+func IsLabel(ev core.Event) bool {
+ return ev.Label(0).Key() == keys.Label
+}
+
+// Start sends a span start event with the supplied label list to the exporter.
+// It also returns a function that will end the span, which should normally be
+// deferred.
+func Start(ctx context.Context, name string, labels ...label.Label) (context.Context, func()) {
+ return core.ExportPair(ctx,
+ core.MakeEvent([3]label.Label{
+ keys.Start.Of(name),
+ }, labels),
+ core.MakeEvent([3]label.Label{
+ keys.End.New(),
+ }, nil))
+}
+
+// IsStart returns true if the event was built by the Start function.
+// It is intended to be used in exporters to identify the semantics of the
+// event when deciding what to do with it.
+func IsStart(ev core.Event) bool {
+ return ev.Label(0).Key() == keys.Start
+}
+
+// IsEnd returns true if the event was built by the End function.
+// It is intended to be used in exporters to identify the semantics of the
+// event when deciding what to do with it.
+func IsEnd(ev core.Event) bool {
+ return ev.Label(0).Key() == keys.End
+}
+
+// Detach returns a context without an associated span.
+// This allows the creation of spans that are not children of the current span.
+func Detach(ctx context.Context) context.Context {
+ return core.Export(ctx, core.MakeEvent([3]label.Label{
+ keys.Detach.New(),
+ }, nil))
+}
+
+// IsDetach returns true if the event was built by the Detach function.
+// It is intended to be used in exporters to identify the semantics of the
+// event when deciding what to do with it.
+func IsDetach(ev core.Event) bool {
+ return ev.Label(0).Key() == keys.Detach
+}
diff --git a/vendor/golang.org/x/tools/internal/event/keys/keys.go b/vendor/golang.org/x/tools/internal/event/keys/keys.go
new file mode 100644
index 000000000..a02206e30
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/event/keys/keys.go
@@ -0,0 +1,564 @@
+// Copyright 2019 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 keys
+
+import (
+ "fmt"
+ "io"
+ "math"
+ "strconv"
+
+ "golang.org/x/tools/internal/event/label"
+)
+
+// Value represents a key for untyped values.
+type Value struct {
+ name string
+ description string
+}
+
+// New creates a new Key for untyped values.
+func New(name, description string) *Value {
+ return &Value{name: name, description: description}
+}
+
+func (k *Value) Name() string { return k.name }
+func (k *Value) Description() string { return k.description }
+
+func (k *Value) Format(w io.Writer, buf []byte, l label.Label) {
+ fmt.Fprint(w, k.From(l))
+}
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *Value) Get(lm label.Map) interface{} {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return nil
+}
+
+// From can be used to get a value from a Label.
+func (k *Value) From(t label.Label) interface{} { return t.UnpackValue() }
+
+// Of creates a new Label with this key and the supplied value.
+func (k *Value) Of(value interface{}) label.Label { return label.OfValue(k, value) }
+
+// Tag represents a key for tagging labels that have no value.
+// These are used when the existence of the label is the entire information it
+// carries, such as marking events to be of a specific kind, or from a specific
+// package.
+type Tag struct {
+ name string
+ description string
+}
+
+// NewTag creates a new Key for tagging labels.
+func NewTag(name, description string) *Tag {
+ return &Tag{name: name, description: description}
+}
+
+func (k *Tag) Name() string { return k.name }
+func (k *Tag) Description() string { return k.description }
+
+func (k *Tag) Format(w io.Writer, buf []byte, l label.Label) {}
+
+// New creates a new Label with this key.
+func (k *Tag) New() label.Label { return label.OfValue(k, nil) }
+
+// Int represents a key
+type Int struct {
+ name string
+ description string
+}
+
+// NewInt creates a new Key for int values.
+func NewInt(name, description string) *Int {
+ return &Int{name: name, description: description}
+}
+
+func (k *Int) Name() string { return k.name }
+func (k *Int) Description() string { return k.description }
+
+func (k *Int) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *Int) Of(v int) label.Label { return label.Of64(k, uint64(v)) }
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *Int) Get(lm label.Map) int {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return 0
+}
+
+// From can be used to get a value from a Label.
+func (k *Int) From(t label.Label) int { return int(t.Unpack64()) }
+
+// Int8 represents a key
+type Int8 struct {
+ name string
+ description string
+}
+
+// NewInt8 creates a new Key for int8 values.
+func NewInt8(name, description string) *Int8 {
+ return &Int8{name: name, description: description}
+}
+
+func (k *Int8) Name() string { return k.name }
+func (k *Int8) Description() string { return k.description }
+
+func (k *Int8) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *Int8) Of(v int8) label.Label { return label.Of64(k, uint64(v)) }
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *Int8) Get(lm label.Map) int8 {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return 0
+}
+
+// From can be used to get a value from a Label.
+func (k *Int8) From(t label.Label) int8 { return int8(t.Unpack64()) }
+
+// Int16 represents a key
+type Int16 struct {
+ name string
+ description string
+}
+
+// NewInt16 creates a new Key for int16 values.
+func NewInt16(name, description string) *Int16 {
+ return &Int16{name: name, description: description}
+}
+
+func (k *Int16) Name() string { return k.name }
+func (k *Int16) Description() string { return k.description }
+
+func (k *Int16) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *Int16) Of(v int16) label.Label { return label.Of64(k, uint64(v)) }
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *Int16) Get(lm label.Map) int16 {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return 0
+}
+
+// From can be used to get a value from a Label.
+func (k *Int16) From(t label.Label) int16 { return int16(t.Unpack64()) }
+
+// Int32 represents a key
+type Int32 struct {
+ name string
+ description string
+}
+
+// NewInt32 creates a new Key for int32 values.
+func NewInt32(name, description string) *Int32 {
+ return &Int32{name: name, description: description}
+}
+
+func (k *Int32) Name() string { return k.name }
+func (k *Int32) Description() string { return k.description }
+
+func (k *Int32) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *Int32) Of(v int32) label.Label { return label.Of64(k, uint64(v)) }
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *Int32) Get(lm label.Map) int32 {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return 0
+}
+
+// From can be used to get a value from a Label.
+func (k *Int32) From(t label.Label) int32 { return int32(t.Unpack64()) }
+
+// Int64 represents a key
+type Int64 struct {
+ name string
+ description string
+}
+
+// NewInt64 creates a new Key for int64 values.
+func NewInt64(name, description string) *Int64 {
+ return &Int64{name: name, description: description}
+}
+
+func (k *Int64) Name() string { return k.name }
+func (k *Int64) Description() string { return k.description }
+
+func (k *Int64) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendInt(buf, k.From(l), 10))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *Int64) Of(v int64) label.Label { return label.Of64(k, uint64(v)) }
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *Int64) Get(lm label.Map) int64 {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return 0
+}
+
+// From can be used to get a value from a Label.
+func (k *Int64) From(t label.Label) int64 { return int64(t.Unpack64()) }
+
+// UInt represents a key
+type UInt struct {
+ name string
+ description string
+}
+
+// NewUInt creates a new Key for uint values.
+func NewUInt(name, description string) *UInt {
+ return &UInt{name: name, description: description}
+}
+
+func (k *UInt) Name() string { return k.name }
+func (k *UInt) Description() string { return k.description }
+
+func (k *UInt) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *UInt) Of(v uint) label.Label { return label.Of64(k, uint64(v)) }
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *UInt) Get(lm label.Map) uint {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return 0
+}
+
+// From can be used to get a value from a Label.
+func (k *UInt) From(t label.Label) uint { return uint(t.Unpack64()) }
+
+// UInt8 represents a key
+type UInt8 struct {
+ name string
+ description string
+}
+
+// NewUInt8 creates a new Key for uint8 values.
+func NewUInt8(name, description string) *UInt8 {
+ return &UInt8{name: name, description: description}
+}
+
+func (k *UInt8) Name() string { return k.name }
+func (k *UInt8) Description() string { return k.description }
+
+func (k *UInt8) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *UInt8) Of(v uint8) label.Label { return label.Of64(k, uint64(v)) }
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *UInt8) Get(lm label.Map) uint8 {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return 0
+}
+
+// From can be used to get a value from a Label.
+func (k *UInt8) From(t label.Label) uint8 { return uint8(t.Unpack64()) }
+
+// UInt16 represents a key
+type UInt16 struct {
+ name string
+ description string
+}
+
+// NewUInt16 creates a new Key for uint16 values.
+func NewUInt16(name, description string) *UInt16 {
+ return &UInt16{name: name, description: description}
+}
+
+func (k *UInt16) Name() string { return k.name }
+func (k *UInt16) Description() string { return k.description }
+
+func (k *UInt16) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *UInt16) Of(v uint16) label.Label { return label.Of64(k, uint64(v)) }
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *UInt16) Get(lm label.Map) uint16 {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return 0
+}
+
+// From can be used to get a value from a Label.
+func (k *UInt16) From(t label.Label) uint16 { return uint16(t.Unpack64()) }
+
+// UInt32 represents a key
+type UInt32 struct {
+ name string
+ description string
+}
+
+// NewUInt32 creates a new Key for uint32 values.
+func NewUInt32(name, description string) *UInt32 {
+ return &UInt32{name: name, description: description}
+}
+
+func (k *UInt32) Name() string { return k.name }
+func (k *UInt32) Description() string { return k.description }
+
+func (k *UInt32) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *UInt32) Of(v uint32) label.Label { return label.Of64(k, uint64(v)) }
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *UInt32) Get(lm label.Map) uint32 {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return 0
+}
+
+// From can be used to get a value from a Label.
+func (k *UInt32) From(t label.Label) uint32 { return uint32(t.Unpack64()) }
+
+// UInt64 represents a key
+type UInt64 struct {
+ name string
+ description string
+}
+
+// NewUInt64 creates a new Key for uint64 values.
+func NewUInt64(name, description string) *UInt64 {
+ return &UInt64{name: name, description: description}
+}
+
+func (k *UInt64) Name() string { return k.name }
+func (k *UInt64) Description() string { return k.description }
+
+func (k *UInt64) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendUint(buf, k.From(l), 10))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *UInt64) Of(v uint64) label.Label { return label.Of64(k, v) }
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *UInt64) Get(lm label.Map) uint64 {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return 0
+}
+
+// From can be used to get a value from a Label.
+func (k *UInt64) From(t label.Label) uint64 { return t.Unpack64() }
+
+// Float32 represents a key
+type Float32 struct {
+ name string
+ description string
+}
+
+// NewFloat32 creates a new Key for float32 values.
+func NewFloat32(name, description string) *Float32 {
+ return &Float32{name: name, description: description}
+}
+
+func (k *Float32) Name() string { return k.name }
+func (k *Float32) Description() string { return k.description }
+
+func (k *Float32) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendFloat(buf, float64(k.From(l)), 'E', -1, 32))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *Float32) Of(v float32) label.Label {
+ return label.Of64(k, uint64(math.Float32bits(v)))
+}
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *Float32) Get(lm label.Map) float32 {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return 0
+}
+
+// From can be used to get a value from a Label.
+func (k *Float32) From(t label.Label) float32 {
+ return math.Float32frombits(uint32(t.Unpack64()))
+}
+
+// Float64 represents a key
+type Float64 struct {
+ name string
+ description string
+}
+
+// NewFloat64 creates a new Key for int64 values.
+func NewFloat64(name, description string) *Float64 {
+ return &Float64{name: name, description: description}
+}
+
+func (k *Float64) Name() string { return k.name }
+func (k *Float64) Description() string { return k.description }
+
+func (k *Float64) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendFloat(buf, k.From(l), 'E', -1, 64))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *Float64) Of(v float64) label.Label {
+ return label.Of64(k, math.Float64bits(v))
+}
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *Float64) Get(lm label.Map) float64 {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return 0
+}
+
+// From can be used to get a value from a Label.
+func (k *Float64) From(t label.Label) float64 {
+ return math.Float64frombits(t.Unpack64())
+}
+
+// String represents a key
+type String struct {
+ name string
+ description string
+}
+
+// NewString creates a new Key for int64 values.
+func NewString(name, description string) *String {
+ return &String{name: name, description: description}
+}
+
+func (k *String) Name() string { return k.name }
+func (k *String) Description() string { return k.description }
+
+func (k *String) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendQuote(buf, k.From(l)))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *String) Of(v string) label.Label { return label.OfString(k, v) }
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *String) Get(lm label.Map) string {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return ""
+}
+
+// From can be used to get a value from a Label.
+func (k *String) From(t label.Label) string { return t.UnpackString() }
+
+// Boolean represents a key
+type Boolean struct {
+ name string
+ description string
+}
+
+// NewBoolean creates a new Key for bool values.
+func NewBoolean(name, description string) *Boolean {
+ return &Boolean{name: name, description: description}
+}
+
+func (k *Boolean) Name() string { return k.name }
+func (k *Boolean) Description() string { return k.description }
+
+func (k *Boolean) Format(w io.Writer, buf []byte, l label.Label) {
+ w.Write(strconv.AppendBool(buf, k.From(l)))
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *Boolean) Of(v bool) label.Label {
+ if v {
+ return label.Of64(k, 1)
+ }
+ return label.Of64(k, 0)
+}
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *Boolean) Get(lm label.Map) bool {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return false
+}
+
+// From can be used to get a value from a Label.
+func (k *Boolean) From(t label.Label) bool { return t.Unpack64() > 0 }
+
+// Error represents a key
+type Error struct {
+ name string
+ description string
+}
+
+// NewError creates a new Key for int64 values.
+func NewError(name, description string) *Error {
+ return &Error{name: name, description: description}
+}
+
+func (k *Error) Name() string { return k.name }
+func (k *Error) Description() string { return k.description }
+
+func (k *Error) Format(w io.Writer, buf []byte, l label.Label) {
+ io.WriteString(w, k.From(l).Error())
+}
+
+// Of creates a new Label with this key and the supplied value.
+func (k *Error) Of(v error) label.Label { return label.OfValue(k, v) }
+
+// Get can be used to get a label for the key from a label.Map.
+func (k *Error) Get(lm label.Map) error {
+ if t := lm.Find(k); t.Valid() {
+ return k.From(t)
+ }
+ return nil
+}
+
+// From can be used to get a value from a Label.
+func (k *Error) From(t label.Label) error {
+ err, _ := t.UnpackValue().(error)
+ return err
+}
diff --git a/vendor/golang.org/x/tools/internal/event/keys/standard.go b/vendor/golang.org/x/tools/internal/event/keys/standard.go
new file mode 100644
index 000000000..7e9586659
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/event/keys/standard.go
@@ -0,0 +1,22 @@
+// 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 keys
+
+var (
+ // Msg is a key used to add message strings to label lists.
+ Msg = NewString("message", "a readable message")
+ // Label is a key used to indicate an event adds labels to the context.
+ Label = NewTag("label", "a label context marker")
+ // Start is used for things like traces that have a name.
+ Start = NewString("start", "span start")
+ // Metric is a key used to indicate an event records metrics.
+ End = NewTag("end", "a span end marker")
+ // Metric is a key used to indicate an event records metrics.
+ Detach = NewTag("detach", "a span detach marker")
+ // Err is a key used to add error values to label lists.
+ Err = NewError("error", "an error that occurred")
+ // Metric is a key used to indicate an event records metrics.
+ Metric = NewTag("metric", "a metric event marker")
+)
diff --git a/vendor/golang.org/x/tools/internal/event/label/label.go b/vendor/golang.org/x/tools/internal/event/label/label.go
new file mode 100644
index 000000000..b55c12eb2
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/event/label/label.go
@@ -0,0 +1,213 @@
+// Copyright 2019 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 label
+
+import (
+ "fmt"
+ "io"
+ "reflect"
+ "unsafe"
+)
+
+// Key is used as the identity of a Label.
+// Keys are intended to be compared by pointer only, the name should be unique
+// for communicating with external systems, but it is not required or enforced.
+type Key interface {
+ // Name returns the key name.
+ Name() string
+ // Description returns a string that can be used to describe the value.
+ Description() string
+
+ // Format is used in formatting to append the value of the label to the
+ // supplied buffer.
+ // The formatter may use the supplied buf as a scratch area to avoid
+ // allocations.
+ Format(w io.Writer, buf []byte, l Label)
+}
+
+// Label holds a key and value pair.
+// It is normally used when passing around lists of labels.
+type Label struct {
+ key Key
+ packed uint64
+ untyped interface{}
+}
+
+// Map is the interface to a collection of Labels indexed by key.
+type Map interface {
+ // Find returns the label that matches the supplied key.
+ Find(key Key) Label
+}
+
+// List is the interface to something that provides an iterable
+// list of labels.
+// Iteration should start from 0 and continue until Valid returns false.
+type List interface {
+ // Valid returns true if the index is within range for the list.
+ // It does not imply the label at that index will itself be valid.
+ Valid(index int) bool
+ // Label returns the label at the given index.
+ Label(index int) Label
+}
+
+// list implements LabelList for a list of Labels.
+type list struct {
+ labels []Label
+}
+
+// filter wraps a LabelList filtering out specific labels.
+type filter struct {
+ keys []Key
+ underlying List
+}
+
+// listMap implements LabelMap for a simple list of labels.
+type listMap struct {
+ labels []Label
+}
+
+// mapChain implements LabelMap for a list of underlying LabelMap.
+type mapChain struct {
+ maps []Map
+}
+
+// OfValue creates a new label from the key and value.
+// This method is for implementing new key types, label creation should
+// normally be done with the Of method of the key.
+func OfValue(k Key, value interface{}) Label { return Label{key: k, untyped: value} }
+
+// UnpackValue assumes the label was built using LabelOfValue and returns the value
+// that was passed to that constructor.
+// This method is for implementing new key types, for type safety normal
+// access should be done with the From method of the key.
+func (t Label) UnpackValue() interface{} { return t.untyped }
+
+// Of64 creates a new label from a key and a uint64. This is often
+// used for non uint64 values that can be packed into a uint64.
+// This method is for implementing new key types, label creation should
+// normally be done with the Of method of the key.
+func Of64(k Key, v uint64) Label { return Label{key: k, packed: v} }
+
+// Unpack64 assumes the label was built using LabelOf64 and returns the value that
+// was passed to that constructor.
+// This method is for implementing new key types, for type safety normal
+// access should be done with the From method of the key.
+func (t Label) Unpack64() uint64 { return t.packed }
+
+// OfString creates a new label from a key and a string.
+// This method is for implementing new key types, label creation should
+// normally be done with the Of method of the key.
+func OfString(k Key, v string) Label {
+ hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
+ return Label{
+ key: k,
+ packed: uint64(hdr.Len),
+ untyped: unsafe.Pointer(hdr.Data),
+ }
+}
+
+// UnpackString assumes the label was built using LabelOfString and returns the
+// value that was passed to that constructor.
+// This method is for implementing new key types, for type safety normal
+// access should be done with the From method of the key.
+func (t Label) UnpackString() string {
+ var v string
+ hdr := (*reflect.StringHeader)(unsafe.Pointer(&v))
+ hdr.Data = uintptr(t.untyped.(unsafe.Pointer))
+ hdr.Len = int(t.packed)
+ return *(*string)(unsafe.Pointer(hdr))
+}
+
+// Valid returns true if the Label is a valid one (it has a key).
+func (t Label) Valid() bool { return t.key != nil }
+
+// Key returns the key of this Label.
+func (t Label) Key() Key { return t.key }
+
+// Format is used for debug printing of labels.
+func (t Label) Format(f fmt.State, r rune) {
+ if !t.Valid() {
+ io.WriteString(f, `nil`)
+ return
+ }
+ io.WriteString(f, t.Key().Name())
+ io.WriteString(f, "=")
+ var buf [128]byte
+ t.Key().Format(f, buf[:0], t)
+}
+
+func (l *list) Valid(index int) bool {
+ return index >= 0 && index < len(l.labels)
+}
+
+func (l *list) Label(index int) Label {
+ return l.labels[index]
+}
+
+func (f *filter) Valid(index int) bool {
+ return f.underlying.Valid(index)
+}
+
+func (f *filter) Label(index int) Label {
+ l := f.underlying.Label(index)
+ for _, f := range f.keys {
+ if l.Key() == f {
+ return Label{}
+ }
+ }
+ return l
+}
+
+func (lm listMap) Find(key Key) Label {
+ for _, l := range lm.labels {
+ if l.Key() == key {
+ return l
+ }
+ }
+ return Label{}
+}
+
+func (c mapChain) Find(key Key) Label {
+ for _, src := range c.maps {
+ l := src.Find(key)
+ if l.Valid() {
+ return l
+ }
+ }
+ return Label{}
+}
+
+var emptyList = &list{}
+
+func NewList(labels ...Label) List {
+ if len(labels) == 0 {
+ return emptyList
+ }
+ return &list{labels: labels}
+}
+
+func Filter(l List, keys ...Key) List {
+ if len(keys) == 0 {
+ return l
+ }
+ return &filter{keys: keys, underlying: l}
+}
+
+func NewMap(labels ...Label) Map {
+ return listMap{labels: labels}
+}
+
+func MergeMaps(srcs ...Map) Map {
+ var nonNil []Map
+ for _, src := range srcs {
+ if src != nil {
+ nonNil = append(nonNil, src)
+ }
+ }
+ if len(nonNil) == 1 {
+ return nonNil[0]
+ }
+ return mapChain{maps: nonNil}
+}
diff --git a/vendor/golang.org/x/tools/internal/gocommand/invoke.go b/vendor/golang.org/x/tools/internal/gocommand/invoke.go
new file mode 100644
index 000000000..9aa798456
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/gocommand/invoke.go
@@ -0,0 +1,187 @@
+// 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 gocommand is a helper for calling the go command.
+package gocommand
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "regexp"
+ "strings"
+ "sync"
+ "time"
+
+ "golang.org/x/tools/internal/event"
+)
+
+// An Runner will run go command invocations and serialize
+// them if it sees a concurrency error.
+type Runner struct {
+ // LoadMu guards packages.Load calls and associated state.
+ loadMu sync.Mutex
+ serializeLoads int
+}
+
+// 1.13: go: updates to go.mod needed, but contents have changed
+// 1.14: go: updating go.mod: existing contents have changed since last read
+var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`)
+
+// Run calls Runner.RunRaw, serializing requests if they fight over
+// go.mod changes.
+func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) {
+ stdout, _, friendly, _ := runner.RunRaw(ctx, inv)
+ return stdout, friendly
+}
+
+// RunRaw calls Invocation.runRaw, serializing requests if they fight over
+// go.mod changes.
+func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) {
+ // We want to run invocations concurrently as much as possible. However,
+ // if go.mod updates are needed, only one can make them and the others will
+ // fail. We need to retry in those cases, but we don't want to thrash so
+ // badly we never recover. To avoid that, once we've seen one concurrency
+ // error, start serializing everything until the backlog has cleared out.
+ runner.loadMu.Lock()
+ var locked bool // If true, we hold the mutex and have incremented.
+ if runner.serializeLoads == 0 {
+ runner.loadMu.Unlock()
+ } else {
+ locked = true
+ runner.serializeLoads++
+ }
+ defer func() {
+ if locked {
+ runner.serializeLoads--
+ runner.loadMu.Unlock()
+ }
+ }()
+
+ for {
+ stdout, stderr, friendlyErr, err := inv.runRaw(ctx)
+ if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) {
+ return stdout, stderr, friendlyErr, err
+ }
+ event.Error(ctx, "Load concurrency error, will retry serially", err)
+ if !locked {
+ runner.loadMu.Lock()
+ runner.serializeLoads++
+ locked = true
+ }
+ }
+}
+
+// An Invocation represents a call to the go command.
+type Invocation struct {
+ Verb string
+ Args []string
+ BuildFlags []string
+ Env []string
+ WorkingDir string
+ Logf func(format string, args ...interface{})
+}
+
+// RunRaw is like RunPiped, but also returns the raw stderr and error for callers
+// that want to do low-level error handling/recovery.
+func (i *Invocation) runRaw(ctx context.Context) (stdout *bytes.Buffer, stderr *bytes.Buffer, friendlyError error, rawError error) {
+ stdout = &bytes.Buffer{}
+ stderr = &bytes.Buffer{}
+ rawError = i.RunPiped(ctx, stdout, stderr)
+ if rawError != nil {
+ friendlyError = rawError
+ // Check for 'go' executable not being found.
+ if ee, ok := rawError.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
+ friendlyError = fmt.Errorf("go command required, not found: %v", ee)
+ }
+ if ctx.Err() != nil {
+ friendlyError = ctx.Err()
+ }
+ friendlyError = fmt.Errorf("err: %v: stderr: %s", friendlyError, stderr)
+ }
+ return
+}
+
+// RunPiped is like Run, but relies on the given stdout/stderr
+func (i *Invocation) RunPiped(ctx context.Context, stdout, stderr io.Writer) error {
+ log := i.Logf
+ if log == nil {
+ log = func(string, ...interface{}) {}
+ }
+
+ goArgs := []string{i.Verb}
+ switch i.Verb {
+ case "mod":
+ // mod needs the sub-verb before build flags.
+ goArgs = append(goArgs, i.Args[0])
+ goArgs = append(goArgs, i.BuildFlags...)
+ goArgs = append(goArgs, i.Args[1:]...)
+ case "env":
+ // env doesn't take build flags.
+ goArgs = append(goArgs, i.Args...)
+ default:
+ goArgs = append(goArgs, i.BuildFlags...)
+ goArgs = append(goArgs, i.Args...)
+ }
+ cmd := exec.Command("go", goArgs...)
+ cmd.Stdout = stdout
+ cmd.Stderr = stderr
+ // On darwin the cwd gets resolved to the real path, which breaks anything that
+ // expects the working directory to keep the original path, including the
+ // go command when dealing with modules.
+ // The Go stdlib has a special feature where if the cwd and the PWD are the
+ // same node then it trusts the PWD, so by setting it in the env for the child
+ // process we fix up all the paths returned by the go command.
+ cmd.Env = append(os.Environ(), i.Env...)
+ if i.WorkingDir != "" {
+ cmd.Env = append(cmd.Env, "PWD="+i.WorkingDir)
+ cmd.Dir = i.WorkingDir
+ }
+
+ defer func(start time.Time) { log("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
+
+ return runCmdContext(ctx, cmd)
+}
+
+// runCmdContext is like exec.CommandContext except it sends os.Interrupt
+// before os.Kill.
+func runCmdContext(ctx context.Context, cmd *exec.Cmd) error {
+ if err := cmd.Start(); err != nil {
+ return err
+ }
+ resChan := make(chan error, 1)
+ go func() {
+ resChan <- cmd.Wait()
+ }()
+
+ select {
+ case err := <-resChan:
+ return err
+ case <-ctx.Done():
+ }
+ // Cancelled. Interrupt and see if it ends voluntarily.
+ cmd.Process.Signal(os.Interrupt)
+ select {
+ case err := <-resChan:
+ return err
+ case <-time.After(time.Second):
+ }
+ // Didn't shut down in response to interrupt. Kill it hard.
+ cmd.Process.Kill()
+ return <-resChan
+}
+
+func cmdDebugStr(cmd *exec.Cmd) string {
+ env := make(map[string]string)
+ for _, kv := range cmd.Env {
+ split := strings.Split(kv, "=")
+ k, v := split[0], split[1]
+ env[k] = v
+ }
+
+ return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v GOPROXY=%v PWD=%v go %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["GOPROXY"], env["PWD"], cmd.Args)
+}
diff --git a/vendor/golang.org/x/tools/internal/packagesinternal/packages.go b/vendor/golang.org/x/tools/internal/packagesinternal/packages.go
new file mode 100644
index 000000000..ff5a6b17d
--- /dev/null
+++ b/vendor/golang.org/x/tools/internal/packagesinternal/packages.go
@@ -0,0 +1,12 @@
+// Package packagesinternal exposes internal-only fields from go/packages.
+package packagesinternal
+
+import (
+ "golang.org/x/tools/internal/gocommand"
+)
+
+var GetForTest = func(p interface{}) string { return "" }
+
+var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil }
+
+var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {}
diff --git a/vendor/golang.org/x/xerrors/LICENSE b/vendor/golang.org/x/xerrors/LICENSE
new file mode 100644
index 000000000..e4a47e17f
--- /dev/null
+++ b/vendor/golang.org/x/xerrors/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2019 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/golang.org/x/xerrors/PATENTS b/vendor/golang.org/x/xerrors/PATENTS
new file mode 100644
index 000000000..733099041
--- /dev/null
+++ b/vendor/golang.org/x/xerrors/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the Go project.
+
+Google hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section)
+patent license to make, have made, use, offer to sell, sell, import,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of Go, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of Go. This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation. If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of Go or any code incorporated within this
+implementation of Go constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of Go
+shall terminate as of the date such litigation is filed.
diff --git a/vendor/golang.org/x/xerrors/README b/vendor/golang.org/x/xerrors/README
new file mode 100644
index 000000000..aac7867a5
--- /dev/null
+++ b/vendor/golang.org/x/xerrors/README
@@ -0,0 +1,2 @@
+This repository holds the transition packages for the new Go 1.13 error values.
+See golang.org/design/29934-error-values.
diff --git a/vendor/golang.org/x/xerrors/adaptor.go b/vendor/golang.org/x/xerrors/adaptor.go
new file mode 100644
index 000000000..4317f2483
--- /dev/null
+++ b/vendor/golang.org/x/xerrors/adaptor.go
@@ -0,0 +1,193 @@
+// 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 xerrors
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "strconv"
+)
+
+// FormatError calls the FormatError method of f with an errors.Printer
+// configured according to s and verb, and writes the result to s.
+func FormatError(f Formatter, s fmt.State, verb rune) {
+ // Assuming this function is only called from the Format method, and given
+ // that FormatError takes precedence over Format, it cannot be called from
+ // any package that supports errors.Formatter. It is therefore safe to
+ // disregard that State may be a specific printer implementation and use one
+ // of our choice instead.
+
+ // limitations: does not support printing error as Go struct.
+
+ var (
+ sep = " " // separator before next error
+ p = &state{State: s}
+ direct = true
+ )
+
+ var err error = f
+
+ switch verb {
+ // Note that this switch must match the preference order
+ // for ordinary string printing (%#v before %+v, and so on).
+
+ case 'v':
+ if s.Flag('#') {
+ if stringer, ok := err.(fmt.GoStringer); ok {
+ io.WriteString(&p.buf, stringer.GoString())
+ goto exit
+ }
+ // proceed as if it were %v
+ } else if s.Flag('+') {
+ p.printDetail = true
+ sep = "\n - "
+ }
+ case 's':
+ case 'q', 'x', 'X':
+ // Use an intermediate buffer in the rare cases that precision,
+ // truncation, or one of the alternative verbs (q, x, and X) are
+ // specified.
+ direct = false
+
+ default:
+ p.buf.WriteString("%!")
+ p.buf.WriteRune(verb)
+ p.buf.WriteByte('(')
+ switch {
+ case err != nil:
+ p.buf.WriteString(reflect.TypeOf(f).String())
+ default:
+ p.buf.WriteString("")
+ }
+ p.buf.WriteByte(')')
+ io.Copy(s, &p.buf)
+ return
+ }
+
+loop:
+ for {
+ switch v := err.(type) {
+ case Formatter:
+ err = v.FormatError((*printer)(p))
+ case fmt.Formatter:
+ v.Format(p, 'v')
+ break loop
+ default:
+ io.WriteString(&p.buf, v.Error())
+ break loop
+ }
+ if err == nil {
+ break
+ }
+ if p.needColon || !p.printDetail {
+ p.buf.WriteByte(':')
+ p.needColon = false
+ }
+ p.buf.WriteString(sep)
+ p.inDetail = false
+ p.needNewline = false
+ }
+
+exit:
+ width, okW := s.Width()
+ prec, okP := s.Precision()
+
+ if !direct || (okW && width > 0) || okP {
+ // Construct format string from State s.
+ format := []byte{'%'}
+ if s.Flag('-') {
+ format = append(format, '-')
+ }
+ if s.Flag('+') {
+ format = append(format, '+')
+ }
+ if s.Flag(' ') {
+ format = append(format, ' ')
+ }
+ if okW {
+ format = strconv.AppendInt(format, int64(width), 10)
+ }
+ if okP {
+ format = append(format, '.')
+ format = strconv.AppendInt(format, int64(prec), 10)
+ }
+ format = append(format, string(verb)...)
+ fmt.Fprintf(s, string(format), p.buf.String())
+ } else {
+ io.Copy(s, &p.buf)
+ }
+}
+
+var detailSep = []byte("\n ")
+
+// state tracks error printing state. It implements fmt.State.
+type state struct {
+ fmt.State
+ buf bytes.Buffer
+
+ printDetail bool
+ inDetail bool
+ needColon bool
+ needNewline bool
+}
+
+func (s *state) Write(b []byte) (n int, err error) {
+ if s.printDetail {
+ if len(b) == 0 {
+ return 0, nil
+ }
+ if s.inDetail && s.needColon {
+ s.needNewline = true
+ if b[0] == '\n' {
+ b = b[1:]
+ }
+ }
+ k := 0
+ for i, c := range b {
+ if s.needNewline {
+ if s.inDetail && s.needColon {
+ s.buf.WriteByte(':')
+ s.needColon = false
+ }
+ s.buf.Write(detailSep)
+ s.needNewline = false
+ }
+ if c == '\n' {
+ s.buf.Write(b[k:i])
+ k = i + 1
+ s.needNewline = true
+ }
+ }
+ s.buf.Write(b[k:])
+ if !s.inDetail {
+ s.needColon = true
+ }
+ } else if !s.inDetail {
+ s.buf.Write(b)
+ }
+ return len(b), nil
+}
+
+// printer wraps a state to implement an xerrors.Printer.
+type printer state
+
+func (s *printer) Print(args ...interface{}) {
+ if !s.inDetail || s.printDetail {
+ fmt.Fprint((*state)(s), args...)
+ }
+}
+
+func (s *printer) Printf(format string, args ...interface{}) {
+ if !s.inDetail || s.printDetail {
+ fmt.Fprintf((*state)(s), format, args...)
+ }
+}
+
+func (s *printer) Detail() bool {
+ s.inDetail = true
+ return s.printDetail
+}
diff --git a/vendor/golang.org/x/xerrors/codereview.cfg b/vendor/golang.org/x/xerrors/codereview.cfg
new file mode 100644
index 000000000..3f8b14b64
--- /dev/null
+++ b/vendor/golang.org/x/xerrors/codereview.cfg
@@ -0,0 +1 @@
+issuerepo: golang/go
diff --git a/vendor/golang.org/x/xerrors/doc.go b/vendor/golang.org/x/xerrors/doc.go
new file mode 100644
index 000000000..eef99d9d5
--- /dev/null
+++ b/vendor/golang.org/x/xerrors/doc.go
@@ -0,0 +1,22 @@
+// Copyright 2019 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 xerrors implements functions to manipulate errors.
+//
+// This package is based on the Go 2 proposal for error values:
+// https://golang.org/design/29934-error-values
+//
+// These functions were incorporated into the standard library's errors package
+// in Go 1.13:
+// - Is
+// - As
+// - Unwrap
+//
+// Also, Errorf's %w verb was incorporated into fmt.Errorf.
+//
+// Use this package to get equivalent behavior in all supported Go versions.
+//
+// No other features of this package were included in Go 1.13, and at present
+// there are no plans to include any of them.
+package xerrors // import "golang.org/x/xerrors"
diff --git a/vendor/golang.org/x/xerrors/errors.go b/vendor/golang.org/x/xerrors/errors.go
new file mode 100644
index 000000000..e88d3772d
--- /dev/null
+++ b/vendor/golang.org/x/xerrors/errors.go
@@ -0,0 +1,33 @@
+// Copyright 2011 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 xerrors
+
+import "fmt"
+
+// errorString is a trivial implementation of error.
+type errorString struct {
+ s string
+ frame Frame
+}
+
+// New returns an error that formats as the given text.
+//
+// The returned error contains a Frame set to the caller's location and
+// implements Formatter to show this information when printed with details.
+func New(text string) error {
+ return &errorString{text, Caller(1)}
+}
+
+func (e *errorString) Error() string {
+ return e.s
+}
+
+func (e *errorString) Format(s fmt.State, v rune) { FormatError(e, s, v) }
+
+func (e *errorString) FormatError(p Printer) (next error) {
+ p.Print(e.s)
+ e.frame.Format(p)
+ return nil
+}
diff --git a/vendor/golang.org/x/xerrors/fmt.go b/vendor/golang.org/x/xerrors/fmt.go
new file mode 100644
index 000000000..829862ddf
--- /dev/null
+++ b/vendor/golang.org/x/xerrors/fmt.go
@@ -0,0 +1,187 @@
+// 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 xerrors
+
+import (
+ "fmt"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "golang.org/x/xerrors/internal"
+)
+
+const percentBangString = "%!"
+
+// Errorf formats according to a format specifier and returns the string as a
+// value that satisfies error.
+//
+// The returned error includes the file and line number of the caller when
+// formatted with additional detail enabled. If the last argument is an error
+// the returned error's Format method will return it if the format string ends
+// with ": %s", ": %v", or ": %w". If the last argument is an error and the
+// format string ends with ": %w", the returned error implements an Unwrap
+// method returning it.
+//
+// If the format specifier includes a %w verb with an error operand in a
+// position other than at the end, the returned error will still implement an
+// Unwrap method returning the operand, but the error's Format method will not
+// return the wrapped error.
+//
+// It is invalid to include more than one %w verb or to supply it with an
+// operand that does not implement the error interface. The %w verb is otherwise
+// a synonym for %v.
+func Errorf(format string, a ...interface{}) error {
+ format = formatPlusW(format)
+ // Support a ": %[wsv]" suffix, which works well with xerrors.Formatter.
+ wrap := strings.HasSuffix(format, ": %w")
+ idx, format2, ok := parsePercentW(format)
+ percentWElsewhere := !wrap && idx >= 0
+ if !percentWElsewhere && (wrap || strings.HasSuffix(format, ": %s") || strings.HasSuffix(format, ": %v")) {
+ err := errorAt(a, len(a)-1)
+ if err == nil {
+ return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)}
+ }
+ // TODO: this is not entirely correct. The error value could be
+ // printed elsewhere in format if it mixes numbered with unnumbered
+ // substitutions. With relatively small changes to doPrintf we can
+ // have it optionally ignore extra arguments and pass the argument
+ // list in its entirety.
+ msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...)
+ frame := Frame{}
+ if internal.EnableTrace {
+ frame = Caller(1)
+ }
+ if wrap {
+ return &wrapError{msg, err, frame}
+ }
+ return &noWrapError{msg, err, frame}
+ }
+ // Support %w anywhere.
+ // TODO: don't repeat the wrapped error's message when %w occurs in the middle.
+ msg := fmt.Sprintf(format2, a...)
+ if idx < 0 {
+ return &noWrapError{msg, nil, Caller(1)}
+ }
+ err := errorAt(a, idx)
+ if !ok || err == nil {
+ // Too many %ws or argument of %w is not an error. Approximate the Go
+ // 1.13 fmt.Errorf message.
+ return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, msg), nil, Caller(1)}
+ }
+ frame := Frame{}
+ if internal.EnableTrace {
+ frame = Caller(1)
+ }
+ return &wrapError{msg, err, frame}
+}
+
+func errorAt(args []interface{}, i int) error {
+ if i < 0 || i >= len(args) {
+ return nil
+ }
+ err, ok := args[i].(error)
+ if !ok {
+ return nil
+ }
+ return err
+}
+
+// formatPlusW is used to avoid the vet check that will barf at %w.
+func formatPlusW(s string) string {
+ return s
+}
+
+// Return the index of the only %w in format, or -1 if none.
+// Also return a rewritten format string with %w replaced by %v, and
+// false if there is more than one %w.
+// TODO: handle "%[N]w".
+func parsePercentW(format string) (idx int, newFormat string, ok bool) {
+ // Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go.
+ idx = -1
+ ok = true
+ n := 0
+ sz := 0
+ var isW bool
+ for i := 0; i < len(format); i += sz {
+ if format[i] != '%' {
+ sz = 1
+ continue
+ }
+ // "%%" is not a format directive.
+ if i+1 < len(format) && format[i+1] == '%' {
+ sz = 2
+ continue
+ }
+ sz, isW = parsePrintfVerb(format[i:])
+ if isW {
+ if idx >= 0 {
+ ok = false
+ } else {
+ idx = n
+ }
+ // "Replace" the last character, the 'w', with a 'v'.
+ p := i + sz - 1
+ format = format[:p] + "v" + format[p+1:]
+ }
+ n++
+ }
+ return idx, format, ok
+}
+
+// Parse the printf verb starting with a % at s[0].
+// Return how many bytes it occupies and whether the verb is 'w'.
+func parsePrintfVerb(s string) (int, bool) {
+ // Assume only that the directive is a sequence of non-letters followed by a single letter.
+ sz := 0
+ var r rune
+ for i := 1; i < len(s); i += sz {
+ r, sz = utf8.DecodeRuneInString(s[i:])
+ if unicode.IsLetter(r) {
+ return i + sz, r == 'w'
+ }
+ }
+ return len(s), false
+}
+
+type noWrapError struct {
+ msg string
+ err error
+ frame Frame
+}
+
+func (e *noWrapError) Error() string {
+ return fmt.Sprint(e)
+}
+
+func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
+
+func (e *noWrapError) FormatError(p Printer) (next error) {
+ p.Print(e.msg)
+ e.frame.Format(p)
+ return e.err
+}
+
+type wrapError struct {
+ msg string
+ err error
+ frame Frame
+}
+
+func (e *wrapError) Error() string {
+ return fmt.Sprint(e)
+}
+
+func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
+
+func (e *wrapError) FormatError(p Printer) (next error) {
+ p.Print(e.msg)
+ e.frame.Format(p)
+ return e.err
+}
+
+func (e *wrapError) Unwrap() error {
+ return e.err
+}
diff --git a/vendor/golang.org/x/xerrors/format.go b/vendor/golang.org/x/xerrors/format.go
new file mode 100644
index 000000000..1bc9c26b9
--- /dev/null
+++ b/vendor/golang.org/x/xerrors/format.go
@@ -0,0 +1,34 @@
+// 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 xerrors
+
+// A Formatter formats error messages.
+type Formatter interface {
+ error
+
+ // FormatError prints the receiver's first error and returns the next error in
+ // the error chain, if any.
+ FormatError(p Printer) (next error)
+}
+
+// A Printer formats error messages.
+//
+// The most common implementation of Printer is the one provided by package fmt
+// during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message
+// typically provide their own implementations.
+type Printer interface {
+ // Print appends args to the message output.
+ Print(args ...interface{})
+
+ // Printf writes a formatted string.
+ Printf(format string, args ...interface{})
+
+ // Detail reports whether error detail is requested.
+ // After the first call to Detail, all text written to the Printer
+ // is formatted as additional detail, or ignored when
+ // detail has not been requested.
+ // If Detail returns false, the caller can avoid printing the detail at all.
+ Detail() bool
+}
diff --git a/vendor/golang.org/x/xerrors/frame.go b/vendor/golang.org/x/xerrors/frame.go
new file mode 100644
index 000000000..0de628ec5
--- /dev/null
+++ b/vendor/golang.org/x/xerrors/frame.go
@@ -0,0 +1,56 @@
+// 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 xerrors
+
+import (
+ "runtime"
+)
+
+// A Frame contains part of a call stack.
+type Frame struct {
+ // Make room for three PCs: the one we were asked for, what it called,
+ // and possibly a PC for skipPleaseUseCallersFrames. See:
+ // https://go.googlesource.com/go/+/032678e0fb/src/runtime/extern.go#169
+ frames [3]uintptr
+}
+
+// Caller returns a Frame that describes a frame on the caller's stack.
+// The argument skip is the number of frames to skip over.
+// Caller(0) returns the frame for the caller of Caller.
+func Caller(skip int) Frame {
+ var s Frame
+ runtime.Callers(skip+1, s.frames[:])
+ return s
+}
+
+// location reports the file, line, and function of a frame.
+//
+// The returned function may be "" even if file and line are not.
+func (f Frame) location() (function, file string, line int) {
+ frames := runtime.CallersFrames(f.frames[:])
+ if _, ok := frames.Next(); !ok {
+ return "", "", 0
+ }
+ fr, ok := frames.Next()
+ if !ok {
+ return "", "", 0
+ }
+ return fr.Function, fr.File, fr.Line
+}
+
+// Format prints the stack as error detail.
+// It should be called from an error's Format implementation
+// after printing any other error detail.
+func (f Frame) Format(p Printer) {
+ if p.Detail() {
+ function, file, line := f.location()
+ if function != "" {
+ p.Printf("%s\n ", function)
+ }
+ if file != "" {
+ p.Printf("%s:%d\n", file, line)
+ }
+ }
+}
diff --git a/vendor/golang.org/x/xerrors/go.mod b/vendor/golang.org/x/xerrors/go.mod
new file mode 100644
index 000000000..870d4f612
--- /dev/null
+++ b/vendor/golang.org/x/xerrors/go.mod
@@ -0,0 +1,3 @@
+module golang.org/x/xerrors
+
+go 1.11
diff --git a/vendor/golang.org/x/xerrors/internal/internal.go b/vendor/golang.org/x/xerrors/internal/internal.go
new file mode 100644
index 000000000..89f4eca5d
--- /dev/null
+++ b/vendor/golang.org/x/xerrors/internal/internal.go
@@ -0,0 +1,8 @@
+// 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 internal
+
+// EnableTrace indicates whether stack information should be recorded in errors.
+var EnableTrace = true
diff --git a/vendor/golang.org/x/xerrors/wrap.go b/vendor/golang.org/x/xerrors/wrap.go
new file mode 100644
index 000000000..9a3b51037
--- /dev/null
+++ b/vendor/golang.org/x/xerrors/wrap.go
@@ -0,0 +1,106 @@
+// 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 xerrors
+
+import (
+ "reflect"
+)
+
+// A Wrapper provides context around another error.
+type Wrapper interface {
+ // Unwrap returns the next error in the error chain.
+ // If there is no next error, Unwrap returns nil.
+ Unwrap() error
+}
+
+// Opaque returns an error with the same error formatting as err
+// but that does not match err and cannot be unwrapped.
+func Opaque(err error) error {
+ return noWrapper{err}
+}
+
+type noWrapper struct {
+ error
+}
+
+func (e noWrapper) FormatError(p Printer) (next error) {
+ if f, ok := e.error.(Formatter); ok {
+ return f.FormatError(p)
+ }
+ p.Print(e.error)
+ return nil
+}
+
+// Unwrap returns the result of calling the Unwrap method on err, if err implements
+// Unwrap. Otherwise, Unwrap returns nil.
+func Unwrap(err error) error {
+ u, ok := err.(Wrapper)
+ if !ok {
+ return nil
+ }
+ return u.Unwrap()
+}
+
+// Is reports whether any error in err's chain matches target.
+//
+// An error is considered to match a target if it is equal to that target or if
+// it implements a method Is(error) bool such that Is(target) returns true.
+func Is(err, target error) bool {
+ if target == nil {
+ return err == target
+ }
+
+ isComparable := reflect.TypeOf(target).Comparable()
+ for {
+ if isComparable && err == target {
+ return true
+ }
+ if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
+ return true
+ }
+ // TODO: consider supporing target.Is(err). This would allow
+ // user-definable predicates, but also may allow for coping with sloppy
+ // APIs, thereby making it easier to get away with them.
+ if err = Unwrap(err); err == nil {
+ return false
+ }
+ }
+}
+
+// As finds the first error in err's chain that matches the type to which target
+// points, and if so, sets the target to its value and returns true. An error
+// matches a type if it is assignable to the target type, or if it has a method
+// As(interface{}) bool such that As(target) returns true. As will panic if target
+// is not a non-nil pointer to a type which implements error or is of interface type.
+//
+// The As method should set the target to its value and return true if err
+// matches the type to which target points.
+func As(err error, target interface{}) bool {
+ if target == nil {
+ panic("errors: target cannot be nil")
+ }
+ val := reflect.ValueOf(target)
+ typ := val.Type()
+ if typ.Kind() != reflect.Ptr || val.IsNil() {
+ panic("errors: target must be a non-nil pointer")
+ }
+ if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) {
+ panic("errors: *target must be interface or implement error")
+ }
+ targetType := typ.Elem()
+ for err != nil {
+ if reflect.TypeOf(err).AssignableTo(targetType) {
+ val.Elem().Set(reflect.ValueOf(err))
+ return true
+ }
+ if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
+ return true
+ }
+ err = Unwrap(err)
+ }
+ return false
+}
+
+var errorType = reflect.TypeOf((*error)(nil)).Elem()
diff --git a/vendor/google.golang.org/appengine/.travis.yml b/vendor/google.golang.org/appengine/.travis.yml
index 70ffe89d5..6d03f4d36 100644
--- a/vendor/google.golang.org/appengine/.travis.yml
+++ b/vendor/google.golang.org/appengine/.travis.yml
@@ -10,8 +10,6 @@ script:
matrix:
include:
- - go: 1.8.x
- env: GOAPP=true
- go: 1.9.x
env: GOAPP=true
- go: 1.10.x
diff --git a/vendor/google.golang.org/appengine/internal/api.go b/vendor/google.golang.org/appengine/internal/api.go
index a6ec19e14..721053c20 100644
--- a/vendor/google.golang.org/appengine/internal/api.go
+++ b/vendor/google.golang.org/appengine/internal/api.go
@@ -58,8 +58,11 @@ var (
apiHTTPClient = &http.Client{
Transport: &http.Transport{
- Proxy: http.ProxyFromEnvironment,
- Dial: limitDial,
+ Proxy: http.ProxyFromEnvironment,
+ Dial: limitDial,
+ MaxIdleConns: 1000,
+ MaxIdleConnsPerHost: 10000,
+ IdleConnTimeout: 90 * time.Second,
},
}
diff --git a/vendor/gopkg.in/yaml.v2/apic.go b/vendor/gopkg.in/yaml.v2/apic.go
index 1f7e87e67..d2c2308f1 100644
--- a/vendor/gopkg.in/yaml.v2/apic.go
+++ b/vendor/gopkg.in/yaml.v2/apic.go
@@ -86,6 +86,7 @@ func yaml_emitter_initialize(emitter *yaml_emitter_t) {
raw_buffer: make([]byte, 0, output_raw_buffer_size),
states: make([]yaml_emitter_state_t, 0, initial_stack_size),
events: make([]yaml_event_t, 0, initial_queue_size),
+ best_width: -1,
}
}
diff --git a/vendor/honnef.co/go/tools/LICENSE b/vendor/honnef.co/go/tools/LICENSE
new file mode 100644
index 000000000..dfd031454
--- /dev/null
+++ b/vendor/honnef.co/go/tools/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2016 Dominik Honnef
+
+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.
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 000000000..7c241b71a
--- /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/arg/arg.go b/vendor/honnef.co/go/tools/arg/arg.go
new file mode 100644
index 000000000..1e7f30db4
--- /dev/null
+++ b/vendor/honnef.co/go/tools/arg/arg.go
@@ -0,0 +1,48 @@
+package arg
+
+var args = map[string]int{
+ "(*encoding/json.Decoder).Decode.v": 0,
+ "(*encoding/json.Encoder).Encode.v": 0,
+ "(*encoding/xml.Decoder).Decode.v": 0,
+ "(*encoding/xml.Encoder).Encode.v": 0,
+ "(*sync.Pool).Put.x": 0,
+ "(*text/template.Template).Parse.text": 0,
+ "(io.Seeker).Seek.offset": 0,
+ "(time.Time).Sub.u": 0,
+ "append.elems": 1,
+ "append.slice": 0,
+ "bytes.Equal.a": 0,
+ "bytes.Equal.b": 1,
+ "encoding/binary.Write.data": 2,
+ "errors.New.text": 0,
+ "fmt.Fprintf.format": 1,
+ "fmt.Printf.format": 0,
+ "fmt.Sprintf.a[0]": 1,
+ "fmt.Sprintf.format": 0,
+ "json.Marshal.v": 0,
+ "json.Unmarshal.v": 1,
+ "len.v": 0,
+ "make.size[0]": 1,
+ "make.size[1]": 2,
+ "make.t": 0,
+ "net/url.Parse.rawurl": 0,
+ "os.OpenFile.flag": 1,
+ "os/exec.Command.name": 0,
+ "os/signal.Notify.c": 0,
+ "regexp.Compile.expr": 0,
+ "runtime.SetFinalizer.finalizer": 1,
+ "runtime.SetFinalizer.obj": 0,
+ "sort.Sort.data": 0,
+ "time.Parse.layout": 0,
+ "time.Sleep.d": 0,
+ "xml.Marshal.v": 0,
+ "xml.Unmarshal.v": 1,
+}
+
+func Arg(name string) int {
+ n, ok := args[name]
+ if !ok {
+ panic("unknown argument " + name)
+ }
+ return n
+}
diff --git a/vendor/honnef.co/go/tools/cmd/staticcheck/README.md b/vendor/honnef.co/go/tools/cmd/staticcheck/README.md
new file mode 100644
index 000000000..4d14577fd
--- /dev/null
+++ b/vendor/honnef.co/go/tools/cmd/staticcheck/README.md
@@ -0,0 +1,15 @@
+# staticcheck
+
+_staticcheck_ offers extensive analysis of Go code, covering a myriad
+of categories. It will detect bugs, suggest code simplifications,
+point out dead code, and more.
+
+## Installation
+
+See [the main README](https://github.com/dominikh/go-tools#installation) for installation instructions.
+
+## Documentation
+
+Detailed documentation can be found on
+[staticcheck.io](https://staticcheck.io/docs/).
+
diff --git a/vendor/honnef.co/go/tools/cmd/staticcheck/staticcheck.go b/vendor/honnef.co/go/tools/cmd/staticcheck/staticcheck.go
new file mode 100644
index 000000000..4f504dc39
--- /dev/null
+++ b/vendor/honnef.co/go/tools/cmd/staticcheck/staticcheck.go
@@ -0,0 +1,44 @@
+// staticcheck analyses Go code and makes it better.
+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"
+ "honnef.co/go/tools/staticcheck"
+ "honnef.co/go/tools/stylecheck"
+ "honnef.co/go/tools/unused"
+)
+
+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:])
+
+ 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)
+ }
+
+ 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
new file mode 100644
index 000000000..c22093a6d
--- /dev/null
+++ b/vendor/honnef.co/go/tools/config/config.go
@@ -0,0 +1,224 @@
+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 {
+ if el == "inherit" {
+ out = append(out, a...)
+ } else {
+ out = append(out, el)
+ }
+ }
+
+ return out
+}
+
+func normalizeList(list []string) []string {
+ if len(list) > 1 {
+ nlist := make([]string, 0, len(list))
+ nlist = append(nlist, list[0])
+ for i, el := range list[1:] {
+ if el != list[i] {
+ nlist = append(nlist, el)
+ }
+ }
+ list = nlist
+ }
+
+ for _, el := range list {
+ if el == "inherit" {
+ // This should never happen, because the default config
+ // should not use "inherit"
+ panic(`unresolved "inherit"`)
+ }
+ }
+
+ return list
+}
+
+func (cfg Config) Merge(ocfg Config) Config {
+ if ocfg.Checks != nil {
+ cfg.Checks = mergeLists(cfg.Checks, ocfg.Checks)
+ }
+ if ocfg.Initialisms != nil {
+ cfg.Initialisms = mergeLists(cfg.Initialisms, ocfg.Initialisms)
+ }
+ if ocfg.DotImportWhitelist != nil {
+ cfg.DotImportWhitelist = mergeLists(cfg.DotImportWhitelist, ocfg.DotImportWhitelist)
+ }
+ if ocfg.HTTPStatusCodeWhitelist != nil {
+ cfg.HTTPStatusCodeWhitelist = mergeLists(cfg.HTTPStatusCodeWhitelist, ocfg.HTTPStatusCodeWhitelist)
+ }
+ return cfg
+}
+
+type Config struct {
+ // TODO(dh): this implementation makes it impossible for external
+ // clients to add their own checkers with configuration. At the
+ // moment, we don't really care about that; we don't encourage
+ // that people use this package. In the future, we may. The
+ // obvious solution would be using map[string]interface{}, but
+ // that's obviously subpar.
+
+ Checks []string `toml:"checks"`
+ Initialisms []string `toml:"initialisms"`
+ DotImportWhitelist []string `toml:"dot_import_whitelist"`
+ HTTPStatusCodeWhitelist []string `toml:"http_status_code_whitelist"`
+}
+
+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",
+ "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID",
+ "IP", "JSON", "QPS", "RAM", "RPC", "SLA",
+ "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL",
+ "UDP", "UI", "GID", "UID", "UUID", "URI",
+ "URL", "UTF8", "VM", "XML", "XMPP", "XSRF",
+ "XSS", "SIP", "RTP",
+ },
+ DotImportWhitelist: []string{},
+ HTTPStatusCodeWhitelist: []string{"200", "400", "404", "500"},
+}
+
+const configName = "staticcheck.conf"
+
+func parseConfigs(dir string) ([]Config, error) {
+ var out []Config
+
+ // TODO(dh): consider stopping at the GOPATH/module boundary
+ for dir != "" {
+ f, err := os.Open(filepath.Join(dir, configName))
+ if os.IsNotExist(err) {
+ ndir := filepath.Dir(dir)
+ if ndir == dir {
+ break
+ }
+ dir = ndir
+ continue
+ }
+ if err != nil {
+ return nil, err
+ }
+ var cfg Config
+ _, err = toml.DecodeReader(f, &cfg)
+ f.Close()
+ if err != nil {
+ return nil, err
+ }
+ out = append(out, cfg)
+ ndir := filepath.Dir(dir)
+ if ndir == dir {
+ break
+ }
+ dir = ndir
+ }
+ out = append(out, DefaultConfig)
+ if len(out) < 2 {
+ return out, nil
+ }
+ for i := 0; i < len(out)/2; i++ {
+ out[i], out[len(out)-1-i] = out[len(out)-1-i], out[i]
+ }
+ return out, nil
+}
+
+func mergeConfigs(confs []Config) Config {
+ if len(confs) == 0 {
+ // This shouldn't happen because we always have at least a
+ // default config.
+ panic("trying to merge zero configs")
+ }
+ if len(confs) == 1 {
+ return confs[0]
+ }
+ conf := confs[0]
+ for _, oconf := range confs[1:] {
+ conf = conf.Merge(oconf)
+ }
+ return conf
+}
+
+func Load(dir string) (Config, error) {
+ confs, err := parseConfigs(dir)
+ if err != nil {
+ return Config{}, err
+ }
+ conf := mergeConfigs(confs)
+
+ conf.Checks = normalizeList(conf.Checks)
+ conf.Initialisms = normalizeList(conf.Initialisms)
+ conf.DotImportWhitelist = normalizeList(conf.DotImportWhitelist)
+ conf.HTTPStatusCodeWhitelist = normalizeList(conf.HTTPStatusCodeWhitelist)
+
+ return conf, nil
+}
diff --git a/vendor/honnef.co/go/tools/config/example.conf b/vendor/honnef.co/go/tools/config/example.conf
new file mode 100644
index 000000000..a715a24d4
--- /dev/null
+++ b/vendor/honnef.co/go/tools/config/example.conf
@@ -0,0 +1,10 @@
+checks = ["all", "-ST1003", "-ST1014"]
+initialisms = ["ACL", "API", "ASCII", "CPU", "CSS", "DNS",
+ "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID",
+ "IP", "JSON", "QPS", "RAM", "RPC", "SLA",
+ "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL",
+ "UDP", "UI", "GID", "UID", "UUID", "URI",
+ "URL", "UTF8", "VM", "XML", "XMPP", "XSRF",
+ "XSS", "SIP", "RTP"]
+dot_import_whitelist = []
+http_status_code_whitelist = ["200", "400", "404", "500"]
diff --git a/vendor/honnef.co/go/tools/deprecated/stdlib.go b/vendor/honnef.co/go/tools/deprecated/stdlib.go
new file mode 100644
index 000000000..5d8ce186b
--- /dev/null
+++ b/vendor/honnef.co/go/tools/deprecated/stdlib.go
@@ -0,0 +1,112 @@
+package deprecated
+
+type Deprecation struct {
+ DeprecatedSince int
+ AlternativeAvailableSince int
+}
+
+var Stdlib = map[string]Deprecation{
+ "image/jpeg.Reader": {4, 0},
+ // FIXME(dh): AllowBinary isn't being detected as deprecated
+ // because the comment has a newline right after "Deprecated:"
+ "go/build.AllowBinary": {7, 7},
+ "(archive/zip.FileHeader).CompressedSize": {1, 1},
+ "(archive/zip.FileHeader).UncompressedSize": {1, 1},
+ "(archive/zip.FileHeader).ModifiedTime": {10, 10},
+ "(archive/zip.FileHeader).ModifiedDate": {10, 10},
+ "(*archive/zip.FileHeader).ModTime": {10, 10},
+ "(*archive/zip.FileHeader).SetModTime": {10, 10},
+ "(go/doc.Package).Bugs": {1, 1},
+ "os.SEEK_SET": {7, 7},
+ "os.SEEK_CUR": {7, 7},
+ "os.SEEK_END": {7, 7},
+ "(net.Dialer).Cancel": {7, 7},
+ "runtime.CPUProfile": {9, 0},
+ "compress/flate.ReadError": {6, 6},
+ "compress/flate.WriteError": {6, 6},
+ "path/filepath.HasPrefix": {0, 0},
+ "(net/http.Transport).Dial": {7, 7},
+ "(*net/http.Transport).CancelRequest": {6, 5},
+ "net/http.ErrWriteAfterFlush": {7, 0},
+ "net/http.ErrHeaderTooLong": {8, 0},
+ "net/http.ErrShortBody": {8, 0},
+ "net/http.ErrMissingContentLength": {8, 0},
+ "net/http/httputil.ErrPersistEOF": {0, 0},
+ "net/http/httputil.ErrClosed": {0, 0},
+ "net/http/httputil.ErrPipeline": {0, 0},
+ "net/http/httputil.ServerConn": {0, 0},
+ "net/http/httputil.NewServerConn": {0, 0},
+ "net/http/httputil.ClientConn": {0, 0},
+ "net/http/httputil.NewClientConn": {0, 0},
+ "net/http/httputil.NewProxyClientConn": {0, 0},
+ "(net/http.Request).Cancel": {7, 7},
+ "(text/template/parse.PipeNode).Line": {1, 1},
+ "(text/template/parse.ActionNode).Line": {1, 1},
+ "(text/template/parse.BranchNode).Line": {1, 1},
+ "(text/template/parse.TemplateNode).Line": {1, 1},
+ "database/sql/driver.ColumnConverter": {9, 9},
+ "database/sql/driver.Execer": {8, 8},
+ "database/sql/driver.Queryer": {8, 8},
+ "(database/sql/driver.Conn).Begin": {8, 8},
+ "(database/sql/driver.Stmt).Exec": {8, 8},
+ "(database/sql/driver.Stmt).Query": {8, 8},
+ "syscall.StringByteSlice": {1, 1},
+ "syscall.StringBytePtr": {1, 1},
+ "syscall.StringSlicePtr": {1, 1},
+ "syscall.StringToUTF16": {1, 1},
+ "syscall.StringToUTF16Ptr": {1, 1},
+ "(*regexp.Regexp).Copy": {12, 12},
+ "(archive/tar.Header).Xattrs": {10, 10},
+ "archive/tar.TypeRegA": {11, 1},
+ "go/types.NewInterface": {11, 11},
+ "(*go/types.Interface).Embedded": {11, 11},
+ "go/importer.For": {12, 12},
+ "encoding/json.InvalidUTF8Error": {2, 2},
+ "encoding/json.UnmarshalFieldError": {2, 2},
+ "encoding/csv.ErrTrailingComma": {2, 2},
+ "(encoding/csv.Reader).TrailingComma": {2, 2},
+ "(net.Dialer).DualStack": {12, 12},
+ "net/http.ErrUnexpectedTrailer": {12, 12},
+ "net/http.CloseNotifier": {11, 7},
+ "net/http.ProtocolError": {8, 8},
+ "(crypto/x509.CertificateRequest).Attributes": {5, 3},
+ // This function has no alternative, but also no purpose.
+ "(*crypto/rc4.Cipher).Reset": {12, 0},
+ "(net/http/httptest.ResponseRecorder).HeaderMap": {11, 7},
+
+ // All of these have been deprecated in favour of external libraries
+ "syscall.AttachLsf": {7, 0},
+ "syscall.DetachLsf": {7, 0},
+ "syscall.LsfSocket": {7, 0},
+ "syscall.SetLsfPromisc": {7, 0},
+ "syscall.LsfJump": {7, 0},
+ "syscall.LsfStmt": {7, 0},
+ "syscall.BpfStmt": {7, 0},
+ "syscall.BpfJump": {7, 0},
+ "syscall.BpfBuflen": {7, 0},
+ "syscall.SetBpfBuflen": {7, 0},
+ "syscall.BpfDatalink": {7, 0},
+ "syscall.SetBpfDatalink": {7, 0},
+ "syscall.SetBpfPromisc": {7, 0},
+ "syscall.FlushBpf": {7, 0},
+ "syscall.BpfInterface": {7, 0},
+ "syscall.SetBpfInterface": {7, 0},
+ "syscall.BpfTimeout": {7, 0},
+ "syscall.SetBpfTimeout": {7, 0},
+ "syscall.BpfStats": {7, 0},
+ "syscall.SetBpfImmediate": {7, 0},
+ "syscall.SetBpf": {7, 0},
+ "syscall.CheckBpfVersion": {7, 0},
+ "syscall.BpfHeadercmpl": {7, 0},
+ "syscall.SetBpfHeadercmpl": {7, 0},
+ "syscall.RouteRIB": {8, 0},
+ "syscall.RoutingMessage": {8, 0},
+ "syscall.RouteMessage": {8, 0},
+ "syscall.InterfaceMessage": {8, 0},
+ "syscall.InterfaceAddrMessage": {8, 0},
+ "syscall.ParseRoutingMessage": {8, 0},
+ "syscall.ParseRoutingSockaddr": {8, 0},
+ "InterfaceAnnounceMessage": {7, 0},
+ "InterfaceMulticastAddrMessage": {7, 0},
+ "syscall.FormatMessage": {5, 0},
+}
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 000000000..8587b0e0e
--- /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 000000000..1ed9563a3
--- /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 000000000..861ca4110
--- /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 000000000..26e76ff73
--- /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/loops.go b/vendor/honnef.co/go/tools/functions/loops.go
new file mode 100644
index 000000000..15877a2f9
--- /dev/null
+++ b/vendor/honnef.co/go/tools/functions/loops.go
@@ -0,0 +1,54 @@
+package functions
+
+import "honnef.co/go/tools/ssa"
+
+type Loop struct{ ssa.BlockSet }
+
+func FindLoops(fn *ssa.Function) []Loop {
+ if fn.Blocks == nil {
+ return nil
+ }
+ tree := fn.DomPreorder()
+ var sets []Loop
+ for _, h := range tree {
+ for _, n := range h.Preds {
+ if !h.Dominates(n) {
+ continue
+ }
+ // n is a back-edge to h
+ // h is the loop header
+ if n == h {
+ set := Loop{}
+ set.Add(n)
+ sets = append(sets, set)
+ continue
+ }
+ set := Loop{}
+ set.Add(h)
+ set.Add(n)
+ for _, b := range allPredsBut(n, h, nil) {
+ set.Add(b)
+ }
+ sets = append(sets, set)
+ }
+ }
+ return sets
+}
+
+func allPredsBut(b, but *ssa.BasicBlock, list []*ssa.BasicBlock) []*ssa.BasicBlock {
+outer:
+ for _, pred := range b.Preds {
+ if pred == but {
+ continue
+ }
+ for _, p := range list {
+ // TODO improve big-o complexity of this function
+ if pred == p {
+ continue outer
+ }
+ }
+ list = append(list, pred)
+ list = allPredsBut(pred, but, list)
+ }
+ return list
+}
diff --git a/vendor/honnef.co/go/tools/functions/pure.go b/vendor/honnef.co/go/tools/functions/pure.go
new file mode 100644
index 000000000..8bc558771
--- /dev/null
+++ b/vendor/honnef.co/go/tools/functions/pure.go
@@ -0,0 +1,46 @@
+package functions
+
+import (
+ "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 IsStub(fn *ssa.Function) bool {
+ if len(fn.Blocks) == 0 {
+ return true
+ }
+ if len(fn.Blocks) > 1 {
+ return false
+ }
+ instrs := filterDebug(fn.Blocks[0].Instrs)
+ if len(instrs) != 1 {
+ return false
+ }
+
+ switch instrs[0].(type) {
+ case *ssa.Return:
+ // Since this is the only instruction, the return value must
+ // be a constant. We consider all constants as stubs, not just
+ // the zero value. This does not, unfortunately, cover zero
+ // initialised structs, as these cause additional
+ // instructions.
+ return true
+ case *ssa.Panic:
+ return true
+ default:
+ return false
+ }
+}
diff --git a/vendor/honnef.co/go/tools/functions/terminates.go b/vendor/honnef.co/go/tools/functions/terminates.go
new file mode 100644
index 000000000..3e9c3a23f
--- /dev/null
+++ b/vendor/honnef.co/go/tools/functions/terminates.go
@@ -0,0 +1,24 @@
+package functions
+
+import "honnef.co/go/tools/ssa"
+
+// 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 {
+ if fn.Blocks == nil {
+ // assuming that a function terminates is the conservative
+ // choice
+ return true
+ }
+
+ for _, block := range fn.Blocks {
+ if len(block.Instrs) == 0 {
+ continue
+ }
+ if _, ok := block.Instrs[len(block.Instrs)-1].(*ssa.Return); ok {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/honnef.co/go/tools/go/types/typeutil/callee.go b/vendor/honnef.co/go/tools/go/types/typeutil/callee.go
new file mode 100644
index 000000000..38f596daf
--- /dev/null
+++ b/vendor/honnef.co/go/tools/go/types/typeutil/callee.go
@@ -0,0 +1,46 @@
+// 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 typeutil
+
+import (
+ "go/ast"
+ "go/types"
+
+ "golang.org/x/tools/go/ast/astutil"
+)
+
+// Callee returns the named target of a function call, if any:
+// a function, method, builtin, or variable.
+func Callee(info *types.Info, call *ast.CallExpr) types.Object {
+ var obj types.Object
+ switch fun := astutil.Unparen(call.Fun).(type) {
+ case *ast.Ident:
+ obj = info.Uses[fun] // type, var, builtin, or declared func
+ case *ast.SelectorExpr:
+ if sel, ok := info.Selections[fun]; ok {
+ obj = sel.Obj() // method or field
+ } else {
+ obj = info.Uses[fun.Sel] // qualified identifier?
+ }
+ }
+ if _, ok := obj.(*types.TypeName); ok {
+ return nil // T(x) is a conversion, not a call
+ }
+ return obj
+}
+
+// StaticCallee returns the target (function or method) of a static
+// function call, if any. It returns nil for calls to builtins.
+func StaticCallee(info *types.Info, call *ast.CallExpr) *types.Func {
+ if f, ok := Callee(info, call).(*types.Func); ok && !interfaceMethod(f) {
+ return f
+ }
+ return nil
+}
+
+func interfaceMethod(f *types.Func) bool {
+ recv := f.Type().(*types.Signature).Recv()
+ return recv != nil && types.IsInterface(recv.Type())
+}
diff --git a/vendor/honnef.co/go/tools/go/types/typeutil/identical.go b/vendor/honnef.co/go/tools/go/types/typeutil/identical.go
new file mode 100644
index 000000000..c0ca441c3
--- /dev/null
+++ b/vendor/honnef.co/go/tools/go/types/typeutil/identical.go
@@ -0,0 +1,75 @@
+package typeutil
+
+import (
+ "go/types"
+)
+
+// 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
+ }
+
+ 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
+ }
+}
diff --git a/vendor/honnef.co/go/tools/go/types/typeutil/imports.go b/vendor/honnef.co/go/tools/go/types/typeutil/imports.go
new file mode 100644
index 000000000..9c441dba9
--- /dev/null
+++ b/vendor/honnef.co/go/tools/go/types/typeutil/imports.go
@@ -0,0 +1,31 @@
+// Copyright 2014 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 typeutil
+
+import "go/types"
+
+// Dependencies returns all dependencies of the specified packages.
+//
+// Dependent packages appear in topological order: if package P imports
+// package Q, Q appears earlier than P in the result.
+// The algorithm follows import statements in the order they
+// appear in the source code, so the result is a total order.
+//
+func Dependencies(pkgs ...*types.Package) []*types.Package {
+ var result []*types.Package
+ seen := make(map[*types.Package]bool)
+ var visit func(pkgs []*types.Package)
+ visit = func(pkgs []*types.Package) {
+ for _, p := range pkgs {
+ if !seen[p] {
+ seen[p] = true
+ visit(p.Imports())
+ result = append(result, p)
+ }
+ }
+ }
+ visit(pkgs)
+ return result
+}
diff --git a/vendor/honnef.co/go/tools/go/types/typeutil/map.go b/vendor/honnef.co/go/tools/go/types/typeutil/map.go
new file mode 100644
index 000000000..f929353cc
--- /dev/null
+++ b/vendor/honnef.co/go/tools/go/types/typeutil/map.go
@@ -0,0 +1,319 @@
+// Copyright 2014 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 typeutil defines various utilities for types, such as Map,
+// a mapping from types.Type to interface{} values.
+package typeutil
+
+import (
+ "bytes"
+ "fmt"
+ "go/types"
+ "reflect"
+)
+
+// Map is a hash-table-based mapping from types (types.Type) to
+// arbitrary interface{} values. The concrete types that implement
+// the Type interface are pointers. Since they are not canonicalized,
+// == cannot be used to check for equivalence, and thus we cannot
+// simply use a Go map.
+//
+// Just as with map[K]V, a nil *Map is a valid empty map.
+//
+// Not thread-safe.
+//
+// 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
+ table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
+ length int // number of map entries
+}
+
+// entry is an entry (key/value association) in a hash bucket.
+type entry struct {
+ key types.Type
+ value interface{}
+}
+
+// SetHasher sets the hasher used by Map.
+//
+// All Hashers are functionally equivalent but contain internal state
+// used to cache the results of hashing previously seen types.
+//
+// A single Hasher created by MakeHasher() may be shared among many
+// Maps. This is recommended if the instances have many keys in
+// common, as it will amortize the cost of hash computation.
+//
+// A Hasher may grow without bound as new types are seen. Even when a
+// type is deleted from the map, the Hasher never shrinks, since other
+// types in the map may reference the deleted type indirectly.
+//
+// Hashers are not thread-safe, and read-only operations such as
+// Map.Lookup require updates to the hasher, so a full Mutex lock (not a
+// read-lock) is require around all Map operations if a shared
+// hasher is accessed from multiple threads.
+//
+// If SetHasher is not called, the Map will create a private hasher at
+// the first call to Insert.
+//
+func (m *Map) SetHasher(hasher Hasher) {
+ m.hasher = hasher
+}
+
+// Delete removes the entry with the given key, if any.
+// It returns true if the entry was found.
+//
+func (m *Map) Delete(key types.Type) bool {
+ if m != nil && m.table != nil {
+ hash := m.hasher.Hash(key)
+ bucket := m.table[hash]
+ for i, e := range bucket {
+ if e.key != nil && Identical(key, e.key) {
+ // We can't compact the bucket as it
+ // would disturb iterators.
+ bucket[i] = entry{}
+ m.length--
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// At returns the map entry for the given key.
+// The result is nil if the entry is not present.
+//
+func (m *Map) At(key types.Type) interface{} {
+ if m != nil && m.table != nil {
+ for _, e := range m.table[m.hasher.Hash(key)] {
+ if e.key != nil && Identical(key, e.key) {
+ return e.value
+ }
+ }
+ }
+ return nil
+}
+
+// Set sets the map entry for key to val,
+// and returns the previous entry, if any.
+func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) {
+ if m.table != nil {
+ hash := m.hasher.Hash(key)
+ bucket := m.table[hash]
+ var hole *entry
+ for i, e := range bucket {
+ if e.key == nil {
+ hole = &bucket[i]
+ } else if Identical(key, e.key) {
+ prev = e.value
+ bucket[i].value = value
+ return
+ }
+ }
+
+ if hole != nil {
+ *hole = entry{key, value} // overwrite deleted entry
+ } else {
+ m.table[hash] = append(bucket, entry{key, value})
+ }
+ } else {
+ if m.hasher.memo == nil {
+ m.hasher = MakeHasher()
+ }
+ hash := m.hasher.Hash(key)
+ m.table = map[uint32][]entry{hash: {entry{key, value}}}
+ }
+
+ m.length++
+ return
+}
+
+// Len returns the number of map entries.
+func (m *Map) Len() int {
+ if m != nil {
+ return m.length
+ }
+ return 0
+}
+
+// Iterate calls function f on each entry in the map in unspecified order.
+//
+// If f should mutate the map, Iterate provides the same guarantees as
+// Go maps: if f deletes a map entry that Iterate has not yet reached,
+// f will not be invoked for it, but if f inserts a map entry that
+// Iterate has not yet reached, whether or not f will be invoked for
+// it is unspecified.
+//
+func (m *Map) Iterate(f func(key types.Type, value interface{})) {
+ if m != nil {
+ for _, bucket := range m.table {
+ for _, e := range bucket {
+ if e.key != nil {
+ f(e.key, e.value)
+ }
+ }
+ }
+ }
+}
+
+// Keys returns a new slice containing the set of map keys.
+// The order is unspecified.
+func (m *Map) Keys() []types.Type {
+ keys := make([]types.Type, 0, m.Len())
+ m.Iterate(func(key types.Type, _ interface{}) {
+ keys = append(keys, key)
+ })
+ return keys
+}
+
+func (m *Map) toString(values bool) string {
+ if m == nil {
+ return "{}"
+ }
+ var buf bytes.Buffer
+ fmt.Fprint(&buf, "{")
+ sep := ""
+ m.Iterate(func(key types.Type, value interface{}) {
+ fmt.Fprint(&buf, sep)
+ sep = ", "
+ fmt.Fprint(&buf, key)
+ if values {
+ fmt.Fprintf(&buf, ": %q", value)
+ }
+ })
+ fmt.Fprint(&buf, "}")
+ return buf.String()
+}
+
+// String returns a string representation of the map's entries.
+// Values are printed using fmt.Sprintf("%v", v).
+// Order is unspecified.
+//
+func (m *Map) String() string {
+ return m.toString(true)
+}
+
+// KeysString returns a string representation of the map's key set.
+// Order is unspecified.
+//
+func (m *Map) KeysString() string {
+ return m.toString(false)
+}
+
+////////////////////////////////////////////////////////////////////////
+// Hasher
+
+// A Hasher maps each type to its hash value.
+// For efficiency, a hasher uses memoization; thus its memory
+// footprint grows monotonically over time.
+// Hashers are not thread-safe.
+// Hashers have reference semantics.
+// Call MakeHasher to create a Hasher.
+type Hasher struct {
+ memo map[types.Type]uint32
+}
+
+// MakeHasher returns a new Hasher instance.
+func MakeHasher() Hasher {
+ return Hasher{make(map[types.Type]uint32)}
+}
+
+// Hash computes a hash value for the given type t such that
+// Identical(t, t') => Hash(t) == Hash(t').
+func (h Hasher) Hash(t types.Type) uint32 {
+ hash, ok := h.memo[t]
+ if !ok {
+ hash = h.hashFor(t)
+ h.memo[t] = hash
+ }
+ return hash
+}
+
+// hashString computes the Fowler–Noll–Vo hash of s.
+func hashString(s string) uint32 {
+ var h uint32
+ for i := 0; i < len(s); i++ {
+ h ^= uint32(s[i])
+ h *= 16777619
+ }
+ return h
+}
+
+// hashFor computes the hash of t.
+func (h Hasher) hashFor(t types.Type) uint32 {
+ // See Identical for rationale.
+ switch t := t.(type) {
+ case *types.Basic:
+ return uint32(t.Kind())
+
+ case *types.Array:
+ return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem())
+
+ case *types.Slice:
+ return 9049 + 2*h.Hash(t.Elem())
+
+ case *types.Struct:
+ var hash uint32 = 9059
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ f := t.Field(i)
+ if f.Anonymous() {
+ hash += 8861
+ }
+ hash += hashString(t.Tag(i))
+ hash += hashString(f.Name()) // (ignore f.Pkg)
+ hash += h.Hash(f.Type())
+ }
+ return hash
+
+ case *types.Pointer:
+ return 9067 + 2*h.Hash(t.Elem())
+
+ case *types.Signature:
+ var hash uint32 = 9091
+ if t.Variadic() {
+ hash *= 8863
+ }
+ return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
+
+ case *types.Interface:
+ var hash uint32 = 9103
+ for i, n := 0, t.NumMethods(); i < n; i++ {
+ // See go/types.identicalMethods for rationale.
+ // Method order is not significant.
+ // Ignore m.Pkg().
+ m := t.Method(i)
+ hash += 3*hashString(m.Name()) + 5*h.Hash(m.Type())
+ }
+ return hash
+
+ case *types.Map:
+ return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem())
+
+ case *types.Chan:
+ return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
+
+ case *types.Named:
+ // Not safe with a copying GC; objects may move.
+ return uint32(reflect.ValueOf(t.Obj()).Pointer())
+
+ case *types.Tuple:
+ return h.hashTuple(t)
+ }
+ panic(t)
+}
+
+func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
+ // See go/types.identicalTypes for rationale.
+ n := tuple.Len()
+ var hash uint32 = 9137 + 2*uint32(n)
+ for i := 0; i < n; i++ {
+ hash += 3 * h.Hash(tuple.At(i).Type())
+ }
+ return hash
+}
diff --git a/vendor/honnef.co/go/tools/go/types/typeutil/methodsetcache.go b/vendor/honnef.co/go/tools/go/types/typeutil/methodsetcache.go
new file mode 100644
index 000000000..32084610f
--- /dev/null
+++ b/vendor/honnef.co/go/tools/go/types/typeutil/methodsetcache.go
@@ -0,0 +1,72 @@
+// Copyright 2014 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.
+
+// This file implements a cache of method sets.
+
+package typeutil
+
+import (
+ "go/types"
+ "sync"
+)
+
+// A MethodSetCache records the method set of each type T for which
+// MethodSet(T) is called so that repeat queries are fast.
+// The zero value is a ready-to-use cache instance.
+type MethodSetCache struct {
+ mu sync.Mutex
+ named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N
+ others map[types.Type]*types.MethodSet // all other types
+}
+
+// MethodSet returns the method set of type T. It is thread-safe.
+//
+// If cache is nil, this function is equivalent to types.NewMethodSet(T).
+// Utility functions can thus expose an optional *MethodSetCache
+// parameter to clients that care about performance.
+//
+func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet {
+ if cache == nil {
+ return types.NewMethodSet(T)
+ }
+ cache.mu.Lock()
+ defer cache.mu.Unlock()
+
+ switch T := T.(type) {
+ case *types.Named:
+ return cache.lookupNamed(T).value
+
+ case *types.Pointer:
+ if N, ok := T.Elem().(*types.Named); ok {
+ return cache.lookupNamed(N).pointer
+ }
+ }
+
+ // all other types
+ // (The map uses pointer equivalence, not type identity.)
+ mset := cache.others[T]
+ if mset == nil {
+ mset = types.NewMethodSet(T)
+ if cache.others == nil {
+ cache.others = make(map[types.Type]*types.MethodSet)
+ }
+ cache.others[T] = mset
+ }
+ return mset
+}
+
+func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } {
+ if cache.named == nil {
+ cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet })
+ }
+ // Avoid recomputing mset(*T) for each distinct Pointer
+ // instance whose underlying type is a named type.
+ msets, ok := cache.named[named]
+ if !ok {
+ msets.value = types.NewMethodSet(named)
+ msets.pointer = types.NewMethodSet(types.NewPointer(named))
+ cache.named[named] = msets
+ }
+ return msets
+}
diff --git a/vendor/honnef.co/go/tools/go/types/typeutil/ui.go b/vendor/honnef.co/go/tools/go/types/typeutil/ui.go
new file mode 100644
index 000000000..9849c24ce
--- /dev/null
+++ b/vendor/honnef.co/go/tools/go/types/typeutil/ui.go
@@ -0,0 +1,52 @@
+// Copyright 2014 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 typeutil
+
+// This file defines utilities for user interfaces that display types.
+
+import "go/types"
+
+// IntuitiveMethodSet returns the intuitive method set of a type T,
+// which is the set of methods you can call on an addressable value of
+// that type.
+//
+// The result always contains MethodSet(T), and is exactly MethodSet(T)
+// for interface types and for pointer-to-concrete types.
+// For all other concrete types T, the result additionally
+// contains each method belonging to *T if there is no identically
+// named method on T itself.
+//
+// This corresponds to user intuition about method sets;
+// this function is intended only for user interfaces.
+//
+// The order of the result is as for types.MethodSet(T).
+//
+func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {
+ isPointerToConcrete := func(T types.Type) bool {
+ ptr, ok := T.(*types.Pointer)
+ return ok && !types.IsInterface(ptr.Elem())
+ }
+
+ var result []*types.Selection
+ mset := msets.MethodSet(T)
+ if types.IsInterface(T) || isPointerToConcrete(T) {
+ for i, n := 0, mset.Len(); i < n; i++ {
+ result = append(result, mset.At(i))
+ }
+ } else {
+ // T is some other concrete type.
+ // Report methods of T and *T, preferring those of T.
+ pmset := msets.MethodSet(types.NewPointer(T))
+ for i, n := 0, pmset.Len(); i < n; i++ {
+ meth := pmset.At(i)
+ if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
+ meth = m
+ }
+ result = append(result, meth)
+ }
+
+ }
+ return result
+}
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 000000000..2b33ca106
--- /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 000000000..3034f76a5
--- /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 000000000..a53543ec5
--- /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 000000000..fde918d12
--- /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 000000000..3f3f1708f
--- /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
new file mode 100644
index 000000000..affee6607
--- /dev/null
+++ b/vendor/honnef.co/go/tools/internal/sharedcheck/lint.go
@@ -0,0 +1,70 @@
+package sharedcheck
+
+import (
+ "go/ast"
+ "go/types"
+
+ "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(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) {
+ return true
+ }
+
+ v, _ := ssafn.ValueForExpr(rng.X)
+
+ // Check that we're converting from string to []rune
+ val, _ := v.(*ssa.Convert)
+ if val == nil {
+ return true
+ }
+ Tsrc, ok := val.X.Type().(*types.Basic)
+ if !ok || Tsrc.Kind() != types.String {
+ return true
+ }
+ Tdst, ok := val.Type().(*types.Slice)
+ if !ok {
+ return true
+ }
+ TdstElem, ok := Tdst.Elem().(*types.Basic)
+ if !ok || TdstElem.Kind() != types.Int32 {
+ return true
+ }
+
+ // Check that the result of the conversion is only used to
+ // range over
+ refs := val.Referrers()
+ if refs == nil {
+ return true
+ }
+
+ // Expect two refs: one for obtaining the length of the slice,
+ // one for accessing the elements
+ if len(FilterDebug(*refs)) != 2 {
+ // TODO(dh): right now, we check that only one place
+ // refers to our slice. This will miss cases such as
+ // ranging over the slice twice. Ideally, we'd ensure that
+ // the slice is only used for ranging over (without
+ // accessing the key), but that is harder to do because in
+ // SSA form, ranging over a slice looks like an ordinary
+ // loop with index increments and slice accesses. We'd
+ // have to look at the associated AST node to check that
+ // it's a range statement.
+ return true
+ }
+
+ 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/LICENSE b/vendor/honnef.co/go/tools/lint/LICENSE
new file mode 100644
index 000000000..796130a12
--- /dev/null
+++ b/vendor/honnef.co/go/tools/lint/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2013 The Go Authors. All rights reserved.
+Copyright (c) 2016 Dominik Honnef. 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/lint/lint.go b/vendor/honnef.co/go/tools/lint/lint.go
new file mode 100644
index 000000000..de5a8f128
--- /dev/null
+++ b/vendor/honnef.co/go/tools/lint/lint.go
@@ -0,0 +1,491 @@
+// Package lint provides the foundation for tools like staticcheck
+package lint // import "honnef.co/go/tools/lint"
+
+import (
+ "bytes"
+ "fmt"
+ "go/scanner"
+ "go/token"
+ "go/types"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "unicode"
+
+ "golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/go/packages"
+ "honnef.co/go/tools/config"
+)
+
+type Documentation struct {
+ Title string
+ Text string
+ Since string
+ NonDefault bool
+ Options []string
+}
+
+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 {
+ Match(p Problem) bool
+}
+
+type LineIgnore struct {
+ File string
+ Line int
+ Checks []string
+ Matched bool
+ Pos token.Pos
+}
+
+func (li *LineIgnore) Match(p Problem) bool {
+ 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
+ return true
+ }
+ }
+ return false
+}
+
+func (li *LineIgnore) String() string {
+ matched := "not matched"
+ if li.Matched {
+ matched = "matched"
+ }
+ return fmt.Sprintf("%s:%d %s (%s)", li.File, li.Line, strings.Join(li.Checks, ", "), matched)
+}
+
+type FileIgnore struct {
+ File string
+ Checks []string
+}
+
+func (fi *FileIgnore) Match(p Problem) bool {
+ if p.Pos.Filename != fi.File {
+ return false
+ }
+ for _, c := range fi.Checks {
+ if m, _ := filepath.Match(c, p.Check); m {
+ return true
+ }
+ }
+ return false
+}
+
+type Severity uint8
+
+const (
+ Error Severity = iota
+ Warning
+ Ignored
+)
+
+// Problem represents a problem in some source code.
+type Problem struct {
+ Pos token.Position
+ End token.Position
+ Message string
+ Check string
+ Severity Severity
+}
+
+func (p *Problem) String() string {
+ return fmt.Sprintf("%s (%s)", p.Message, p.Check)
+}
+
+// A Linter lints Go source code.
+type Linter struct {
+ Checkers []*analysis.Analyzer
+ CumulativeCheckers []CumulativeChecker
+ GoVersion int
+ Config config.Config
+ Stats Stats
+}
+
+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())
+ }
+
+ // 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"
+ }
+ }
+
+ allowed := FilterChecks(allAnalyzers, checks)
+ var allowedAnalyzers []*analysis.Analyzer
+ for _, c := range l.Checkers {
+ if allowed[c.Name] {
+ allowedAnalyzers = append(allowedAnalyzers, c)
+ }
+ }
+ hasCumulative := false
+ for _, cum := range l.CumulativeCheckers {
+ a := cum.Analyzer()
+ if allowed[a.Name] {
+ hasCumulative = true
+ allowedAnalyzers = append(allowedAnalyzers, a)
+ }
+ }
+
+ 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))
+ }
+ }
+ 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)
+ }
+ case error:
+ p := Problem{
+ Pos: token.Position{},
+ Message: e.Error(),
+ Severity: Error,
+ Check: "compile",
+ }
+ pkg.problems = append(pkg.problems, p)
+ }
+ }
+ }
+
+ 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
+ }
+ 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
+ }
+ }
+ }
+
+ 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)
+ }
+ }
+ }
+
+ 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)
+ }
+ }
+
+ 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
+ }
+ if pi.Line != pj.Line {
+ return pi.Line < pj.Line
+ }
+ if pi.Column != pj.Column {
+ return pi.Column < pj.Column
+ }
+
+ return problems[i].Message < problems[j].Message
+ })
+
+ 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)
+ }
+ }
+ return out, nil
+}
+
+func FilterChecks(allChecks []*analysis.Analyzer, checks []string) map[string]bool {
+ // OPT(dh): this entire computation could be cached per package
+ allowedChecks := map[string]bool{}
+
+ for _, check := range checks {
+ b := true
+ if len(check) > 1 && check[0] == '-' {
+ b = false
+ check = check[1:]
+ }
+ if check == "*" || check == "all" {
+ // Match all
+ for _, c := range allChecks {
+ allowedChecks[c.Name] = b
+ }
+ } else if strings.HasSuffix(check, "*") {
+ // Glob
+ prefix := check[:len(check)-1]
+ isCat := strings.IndexFunc(prefix, func(r rune) bool { return unicode.IsNumber(r) }) == -1
+
+ for _, c := range allChecks {
+ 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.Name[:idx]
+ if prefix == cat {
+ allowedChecks[c.Name] = b
+ }
+ } else {
+ // Glob is S1*
+ if strings.HasPrefix(c.Name, prefix) {
+ allowedChecks[c.Name] = b
+ }
+ }
+ }
+ } else {
+ // Literal check name
+ allowedChecks[check] = b
+ }
+ }
+ return allowedChecks
+}
+
+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)
+
+ if filepath.Ext(adjPos.Filename) == ".go" {
+ return adjPos
+ }
+ return pos
+}
+
+var bufferPool = &sync.Pool{
+ New: func() interface{} {
+ buf := bytes.NewBuffer(nil)
+ buf.Grow(64)
+ return buf
+ },
+}
+
+func FuncName(f *types.Func) string {
+ buf := bufferPool.Get().(*bytes.Buffer)
+ buf.Reset()
+ if f.Type() != nil {
+ sig := f.Type().(*types.Signature)
+ if recv := sig.Recv(); recv != nil {
+ buf.WriteByte('(')
+ if _, ok := recv.Type().(*types.Interface); ok {
+ // gcimporter creates abstract methods of
+ // named interfaces using the interface type
+ // (not the named type) as the receiver.
+ // Don't print it in full.
+ buf.WriteString("interface")
+ } else {
+ types.WriteType(buf, recv.Type(), nil)
+ }
+ buf.WriteByte(')')
+ buf.WriteByte('.')
+ } else if f.Pkg() != nil {
+ writePackage(buf, f.Pkg())
+ }
+ }
+ buf.WriteString(f.Name())
+ s := buf.String()
+ bufferPool.Put(buf)
+ return s
+}
+
+func writePackage(buf *bytes.Buffer, pkg *types.Package) {
+ if pkg == nil {
+ return
+ }
+ 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
new file mode 100644
index 000000000..3b939e95f
--- /dev/null
+++ b/vendor/honnef.co/go/tools/lint/lintdsl/lintdsl.go
@@ -0,0 +1,400 @@
+// Package lintdsl provides helpers for implementing static analysis
+// checks. Dot-importing this package is encouraged.
+package lintdsl
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/printer"
+ "go/token"
+ "go/types"
+ "strings"
+
+ "golang.org/x/tools/go/analysis"
+ "honnef.co/go/tools/facts"
+ "honnef.co/go/tools/lint"
+ "honnef.co/go/tools/ssa"
+)
+
+type packager interface {
+ Package() *ssa.Package
+}
+
+func CallName(call *ssa.CallCommon) string {
+ if call.IsInvoke() {
+ return ""
+ }
+ switch v := call.Value.(type) {
+ case *ssa.Function:
+ fn, ok := v.Object().(*types.Func)
+ if !ok {
+ return ""
+ }
+ return lint.FuncName(fn)
+ case *ssa.Builtin:
+ return v.Name()
+ }
+ return ""
+}
+
+func IsCallTo(call *ssa.CallCommon, name string) bool { return CallName(call) == name }
+func IsType(T types.Type, name string) bool { return types.TypeString(T, nil) == name }
+
+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
+}
+
+func IsExample(fn *ssa.Function) bool {
+ if !strings.HasPrefix(fn.Name(), "Example") {
+ return false
+ }
+ f := fn.Prog.Fset.File(fn.Pos())
+ if f == nil {
+ return false
+ }
+ return strings.HasSuffix(f.Name(), "_test.go")
+}
+
+func IsPointerLike(T types.Type) bool {
+ switch T := T.Underlying().(type) {
+ case *types.Interface, *types.Chan, *types.Map, *types.Signature, *types.Pointer:
+ return true
+ case *types.Basic:
+ return T.Kind() == types.UnsafePointer
+ }
+ return false
+}
+
+func IsIdent(expr ast.Expr, ident string) bool {
+ id, ok := expr.(*ast.Ident)
+ return ok && id.Name == ident
+}
+
+// isBlank returns whether id is the blank identifier "_".
+// If id == nil, the answer is false.
+func IsBlank(id ast.Expr) bool {
+ ident, _ := id.(*ast.Ident)
+ return ident != nil && ident.Name == "_"
+}
+
+func IsIntLiteral(expr ast.Expr, literal string) bool {
+ lit, ok := expr.(*ast.BasicLit)
+ return ok && lit.Kind == token.INT && lit.Value == literal
+}
+
+// Deprecated: use IsIntLiteral instead
+func IsZero(expr ast.Expr) bool {
+ return IsIntLiteral(expr, "0")
+}
+
+func IsOfType(pass *analysis.Pass, expr ast.Expr, name string) bool {
+ return IsType(pass.TypesInfo.TypeOf(expr), name)
+}
+
+func IsInTest(pass *analysis.Pass, node lint.Positioner) bool {
+ // FIXME(dh): this doesn't work for global variables with
+ // initializers
+ f := pass.Fset.File(node.Pos())
+ return f != nil && strings.HasSuffix(f.Name(), "_test.go")
+}
+
+func IsInMain(pass *analysis.Pass, node lint.Positioner) bool {
+ if node, ok := node.(packager); ok {
+ return node.Package().Pkg.Name() == "main"
+ }
+ return pass.Pkg.Name() == "main"
+}
+
+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 {
+ pkg, ok := info.ObjectOf(x).(*types.PkgName)
+ if !ok {
+ // This shouldn't happen
+ return fmt.Sprintf("%s.%s", x.Name, expr.Sel.Name)
+ }
+ return fmt.Sprintf("%s.%s", pkg.Imported().Path(), expr.Sel.Name)
+ }
+ panic(fmt.Sprintf("unsupported selector: %v", expr))
+ }
+ return fmt.Sprintf("(%s).%s", sel.Recv(), sel.Obj().Name())
+}
+
+func IsNil(pass *analysis.Pass, expr ast.Expr) bool {
+ return pass.TypesInfo.Types[expr].IsNil()
+}
+
+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(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.
+
+ ident, ok := expr.(*ast.Ident)
+ if !ok {
+ return false
+ }
+ obj := pass.TypesInfo.ObjectOf(ident)
+ c, ok := obj.(*types.Const)
+ if !ok {
+ return false
+ }
+ basic, ok := c.Type().(*types.Basic)
+ if !ok {
+ return false
+ }
+ if basic.Kind() != types.UntypedBool && basic.Kind() != types.Bool {
+ return false
+ }
+ return true
+}
+
+func ExprToInt(pass *analysis.Pass, expr ast.Expr) (int64, bool) {
+ tv := pass.TypesInfo.Types[expr]
+ if tv.Value == nil {
+ return 0, false
+ }
+ if tv.Value.Kind() != constant.Int {
+ return 0, false
+ }
+ return constant.Int64Val(tv.Value)
+}
+
+func ExprToString(pass *analysis.Pass, expr ast.Expr) (string, bool) {
+ val := pass.TypesInfo.Types[expr].Value
+ if val == nil {
+ return "", false
+ }
+ if val.Kind() != constant.String {
+ return "", false
+ }
+ return constant.StringVal(val), true
+}
+
+// Dereference returns a pointer's element type; otherwise it returns
+// T.
+func Dereference(T types.Type) types.Type {
+ if p, ok := T.Underlying().(*types.Pointer); ok {
+ return p.Elem()
+ }
+ return T
+}
+
+// DereferenceR returns a pointer's element type; otherwise it returns
+// T. If the element type is itself a pointer, DereferenceR will be
+// applied recursively.
+func DereferenceR(T types.Type) types.Type {
+ if p, ok := T.Underlying().(*types.Pointer); ok {
+ return DereferenceR(p.Elem())
+ }
+ return T
+}
+
+func IsGoVersion(pass *analysis.Pass, minor int) bool {
+ version := pass.Analyzer.Flags.Lookup("go").Value.(flag.Getter).Get().(int)
+ return version >= minor
+}
+
+func CallNameAST(pass *analysis.Pass, call *ast.CallExpr) string {
+ switch fun := call.Fun.(type) {
+ case *ast.SelectorExpr:
+ fn, ok := pass.TypesInfo.ObjectOf(fun.Sel).(*types.Func)
+ if !ok {
+ return ""
+ }
+ return lint.FuncName(fn)
+ case *ast.Ident:
+ obj := pass.TypesInfo.ObjectOf(fun)
+ switch obj := obj.(type) {
+ case *types.Func:
+ return lint.FuncName(obj)
+ case *types.Builtin:
+ return obj.Name()
+ default:
+ return ""
+ }
+ default:
+ return ""
+ }
+}
+
+func IsCallToAST(pass *analysis.Pass, node ast.Node, name string) bool {
+ call, ok := node.(*ast.CallExpr)
+ if !ok {
+ return false
+ }
+ return CallNameAST(pass, call) == name
+}
+
+func IsCallToAnyAST(pass *analysis.Pass, node ast.Node, names ...string) bool {
+ for _, name := range names {
+ if IsCallToAST(pass, node, name) {
+ return true
+ }
+ }
+ return false
+}
+
+func Render(pass *analysis.Pass, x interface{}) string {
+ var buf bytes.Buffer
+ if err := printer.Fprint(&buf, pass.Fset, x); err != nil {
+ panic(err)
+ }
+ return buf.String()
+}
+
+func RenderArgs(pass *analysis.Pass, args []ast.Expr) string {
+ var ss []string
+ for _, arg := range args {
+ ss = append(ss, Render(pass, arg))
+ }
+ return strings.Join(ss, ", ")
+}
+
+func Preamble(f *ast.File) string {
+ cutoff := f.Package
+ if f.Doc != nil {
+ cutoff = f.Doc.Pos()
+ }
+ var out []string
+ for _, cmt := range f.Comments {
+ if cmt.Pos() >= cutoff {
+ break
+ }
+ out = append(out, cmt.Text())
+ }
+ return strings.Join(out, "\n")
+}
+
+func Inspect(node ast.Node, fn func(node ast.Node) bool) {
+ if node == nil {
+ return
+ }
+ ast.Inspect(node, fn)
+}
+
+func GroupSpecs(fset *token.FileSet, specs []ast.Spec) [][]ast.Spec {
+ if len(specs) == 0 {
+ return nil
+ }
+ groups := make([][]ast.Spec, 1)
+ groups[0] = append(groups[0], specs[0])
+
+ for _, spec := range specs[1:] {
+ g := groups[len(groups)-1]
+ if fset.PositionFor(spec.Pos(), false).Line-1 !=
+ fset.PositionFor(g[len(g)-1].End(), false).Line {
+
+ groups = append(groups, nil)
+ }
+
+ groups[len(groups)-1] = append(groups[len(groups)-1], spec)
+ }
+
+ return groups
+}
+
+func IsObject(obj types.Object, name string) bool {
+ var path string
+ if pkg := obj.Pkg(); pkg != nil {
+ path = pkg.Path() + "."
+ }
+ return path+obj.Name() == name
+}
+
+type Field struct {
+ Var *types.Var
+ Tag string
+ Path []int
+}
+
+// FlattenFields recursively flattens T and embedded structs,
+// returning a list of fields. If multiple fields with the same name
+// exist, all will be returned.
+func FlattenFields(T *types.Struct) []Field {
+ return flattenFields(T, nil, nil)
+}
+
+func flattenFields(T *types.Struct, path []int, seen map[types.Type]bool) []Field {
+ if seen == nil {
+ seen = map[types.Type]bool{}
+ }
+ if seen[T] {
+ return nil
+ }
+ seen[T] = true
+ var out []Field
+ for i := 0; i < T.NumFields(); i++ {
+ field := T.Field(i)
+ tag := T.Tag(i)
+ np := append(path[:len(path):len(path)], i)
+ if field.Anonymous() {
+ if s, ok := Dereference(field.Type()).Underlying().(*types.Struct); ok {
+ out = append(out, flattenFields(s, np, seen)...)
+ }
+ } else {
+ out = append(out, Field{field, tag, np})
+ }
+ }
+ 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
new file mode 100644
index 000000000..9385431f8
--- /dev/null
+++ b/vendor/honnef.co/go/tools/lint/lintutil/format/format.go
@@ -0,0 +1,135 @@
+// Package format provides formatters for linter problems.
+package format
+
+import (
+ "encoding/json"
+ "fmt"
+ "go/token"
+ "io"
+ "os"
+ "path/filepath"
+ "text/tabwriter"
+
+ "honnef.co/go/tools/lint"
+)
+
+func shortPath(path string) string {
+ cwd, err := os.Getwd()
+ if err != nil {
+ return path
+ }
+ if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) {
+ return rel
+ }
+ return path
+}
+
+func relativePositionString(pos token.Position) string {
+ s := shortPath(pos.Filename)
+ if pos.IsValid() {
+ if s != "" {
+ s += ":"
+ }
+ s += fmt.Sprintf("%d:%d", pos.Line, pos.Column)
+ }
+ if s == "" {
+ s = "-"
+ }
+ return s
+}
+
+type Statter interface {
+ Stats(total, errors, warnings int)
+}
+
+type Formatter interface {
+ Format(p lint.Problem)
+}
+
+type Text struct {
+ W io.Writer
+}
+
+func (o Text) Format(p lint.Problem) {
+ fmt.Fprintf(o.W, "%v: %s\n", relativePositionString(p.Pos), p.String())
+}
+
+type JSON struct {
+ W io.Writer
+}
+
+func severity(s lint.Severity) string {
+ switch s {
+ case lint.Error:
+ return "error"
+ case lint.Warning:
+ return "warning"
+ case lint.Ignored:
+ return "ignored"
+ }
+ return ""
+}
+
+func (o JSON) Format(p lint.Problem) {
+ type location struct {
+ File string `json:"file"`
+ Line int `json:"line"`
+ Column int `json:"column"`
+ }
+ jp := struct {
+ 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.Pos.Filename,
+ Line: p.Pos.Line,
+ Column: p.Pos.Column,
+ },
+ End: location{
+ File: p.End.Filename,
+ Line: p.End.Line,
+ Column: p.End.Column,
+ },
+ Message: p.Message,
+ }
+ _ = json.NewEncoder(o.W).Encode(jp)
+}
+
+type Stylish struct {
+ W io.Writer
+
+ prevFile string
+ tw *tabwriter.Writer
+}
+
+func (o *Stylish) Format(p lint.Problem) {
+ pos := p.Pos
+ if pos.Filename == "" {
+ pos.Filename = "-"
+ }
+
+ if pos.Filename != o.prevFile {
+ if o.prevFile != "" {
+ o.tw.Flush()
+ fmt.Fprintln(o.W)
+ }
+ 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", pos.Line, pos.Column, p.Check, p.Message)
+}
+
+func (o *Stylish) Stats(total, errors, warnings int) {
+ if o.tw != nil {
+ o.tw.Flush()
+ fmt.Fprintln(o.W)
+ }
+ fmt.Fprintf(o.W, " ✖ %d problems (%d errors, %d warnings)\n",
+ total, errors, warnings)
+}
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 000000000..ba8caf0af
--- /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 000000000..3a62ede03
--- /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 000000000..53f21c666
--- /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
new file mode 100644
index 000000000..fe0279f92
--- /dev/null
+++ b/vendor/honnef.co/go/tools/lint/lintutil/util.go
@@ -0,0 +1,392 @@
+// Copyright (c) 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 or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+// Package lintutil provides helpers for writing linter command lines.
+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/pprof"
+ "strconv"
+ "strings"
+ "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)
+ fmt.Fprintf(os.Stderr, "\t%s [flags] # runs on package in current directory\n", name)
+ fmt.Fprintf(os.Stderr, "\t%s [flags] packages\n", name)
+ fmt.Fprintf(os.Stderr, "\t%s [flags] directory\n", name)
+ fmt.Fprintf(os.Stderr, "\t%s [flags] files... # must be a single package\n", name)
+ fmt.Fprintf(os.Stderr, "Flags:\n")
+ flags.PrintDefaults()
+ }
+}
+
+type list []string
+
+func (list *list) String() string {
+ return `"` + strings.Join(*list, ",") + `"`
+}
+
+func (list *list) Set(s string) error {
+ if s == "" {
+ *list = nil
+ return nil
+ }
+
+ *list = strings.Split(s, ",")
+ return nil
+}
+
+func FlagSet(name string) *flag.FlagSet {
+ flags := flag.NewFlagSet("", flag.ExitOnError)
+ flags.Usage = usage(name, flags)
+ flags.String("tags", "", "List of `build tags`")
+ 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.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"}
+ flags.Var(&checks, "checks", "Comma-separated list of `checks` to enable.")
+ flags.Var(&fail, "fail", "Comma-separated list of `checks` that can cause a non-zero exit status.")
+
+ 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))
+ }
+
+ flags.Var(version, "go", "Target Go `version` in the format '1.x'")
+ return flags
+}
+
+func findCheck(cs []*analysis.Analyzer, check string) (*analysis.Analyzer, bool) {
+ for _, c := range cs {
+ if c.Name == check {
+ return c, true
+ }
+ }
+ return nil, false
+}
+
+func ProcessFlagSet(cs []*analysis.Analyzer, cums []lint.CumulativeChecker, fs *flag.FlagSet) {
+ tags := fs.Lookup("tags").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)
+ printVersion := fs.Lookup("version").Value.(flag.Getter).Get().(bool)
+ showIgnored := fs.Lookup("show-ignored").Value.(flag.Getter).Get().(bool)
+ explain := fs.Lookup("explain").Value.(flag.Getter).Get().(string)
+
+ 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)
+
+ exit := func(code int) {
+ if cpuProfile != "" {
+ pprof.StopCPUProfile()
+ }
+ if memProfile != "" {
+ f, err := os.Create(memProfile)
+ if err != nil {
+ panic(err)
+ }
+ runtime.GC()
+ pprof.WriteHeapProfile(f)
+ }
+ os.Exit(code)
+ }
+ if cpuProfile != "" {
+ f, err := os.Create(cpuProfile)
+ if err != nil {
+ log.Fatal(err)
+ }
+ 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 != "" {
+ 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)
+ }
+ if check.Doc == "" {
+ fmt.Fprintln(os.Stderr, explain, "has no documentation")
+ exit(1)
+ }
+ fmt.Println(check.Doc)
+ exit(0)
+ }
+
+ ps, err := Lint(cs, cums, fs.Args(), &Options{
+ Tags: tags,
+ LintTests: tests,
+ GoVersion: goVersion,
+ Config: cfg,
+ })
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ exit(1)
+ }
+
+ var f format.Formatter
+ switch formatter {
+ case "text":
+ f = format.Text{W: os.Stdout}
+ case "stylish":
+ f = &format.Stylish{W: os.Stdout}
+ case "json":
+ f = format.JSON{W: os.Stdout}
+ default:
+ fmt.Fprintf(os.Stderr, "unsupported output format %q\n", formatter)
+ exit(2)
+ }
+
+ var (
+ total int
+ errors int
+ warnings int
+ )
+
+ fail := *fs.Lookup("fail").Value.(*list)
+ 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(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 {
+ p.Severity = lint.Warning
+ warnings++
+ }
+ f.Format(p)
+ }
+ if f, ok := f.(format.Statter); ok {
+ f.Stats(total, errors, warnings)
+ }
+ if errors > 0 {
+ exit(1)
+ }
+ exit(0)
+}
+
+type Options struct {
+ Config config.Config
+
+ Tags string
+ LintTests bool
+ GoVersion int
+}
+
+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{}
+ }
+
+ l := &lint.Linter{
+ 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)
+ }
+
+ 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+)?)?$`)
+
+func parsePos(pos string) token.Position {
+ if pos == "-" || pos == "" {
+ return token.Position{}
+ }
+ parts := posRe.FindStringSubmatch(pos)
+ if parts == nil {
+ panic(fmt.Sprintf("internal error: 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,
+ }
+}
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 000000000..3b22a63fa
--- /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 000000000..2f6508559
--- /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 000000000..9c6885d48
--- /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/printf/fuzz.go b/vendor/honnef.co/go/tools/printf/fuzz.go
new file mode 100644
index 000000000..8ebf357fb
--- /dev/null
+++ b/vendor/honnef.co/go/tools/printf/fuzz.go
@@ -0,0 +1,11 @@
+// +build gofuzz
+
+package printf
+
+func Fuzz(data []byte) int {
+ _, err := Parse(string(data))
+ if err == nil {
+ return 1
+ }
+ return 0
+}
diff --git a/vendor/honnef.co/go/tools/printf/printf.go b/vendor/honnef.co/go/tools/printf/printf.go
new file mode 100644
index 000000000..754db9b16
--- /dev/null
+++ b/vendor/honnef.co/go/tools/printf/printf.go
@@ -0,0 +1,197 @@
+// Package printf implements a parser for fmt.Printf-style format
+// strings.
+//
+// It parses verbs according to the following syntax:
+// Numeric -> '0'-'9'
+// Letter -> 'a'-'z' | 'A'-'Z'
+// Index -> '[' Numeric+ ']'
+// Star -> '*'
+// Star -> Index '*'
+//
+// Precision -> Numeric+ | Star
+// Width -> Numeric+ | Star
+//
+// WidthAndPrecision -> Width '.' Precision
+// WidthAndPrecision -> Width '.'
+// WidthAndPrecision -> Width
+// WidthAndPrecision -> '.' Precision
+// WidthAndPrecision -> '.'
+//
+// Flag -> '+' | '-' | '#' | ' ' | '0'
+// Verb -> Letter | '%'
+//
+// Input -> '%' [ Flag+ ] [ WidthAndPrecision ] [ Index ] Verb
+package printf
+
+import (
+ "errors"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// ErrInvalid is returned for invalid format strings or verbs.
+var ErrInvalid = errors.New("invalid format string")
+
+type Verb struct {
+ Letter rune
+ Flags string
+
+ Width Argument
+ Precision Argument
+ // Which value in the argument list the verb uses.
+ // -1 denotes the next argument,
+ // values > 0 denote explicit arguments.
+ // The value 0 denotes that no argument is consumed. This is the case for %%.
+ Value int
+
+ Raw string
+}
+
+// Argument is an implicit or explicit width or precision.
+type Argument interface {
+ isArgument()
+}
+
+// The Default value, when no width or precision is provided.
+type Default struct{}
+
+// Zero is the implicit zero value.
+// This value may only appear for precisions in format strings like %6.f
+type Zero struct{}
+
+// Star is a * value, which may either refer to the next argument (Index == -1) or an explicit argument.
+type Star struct{ Index int }
+
+// A Literal value, such as 6 in %6d.
+type Literal int
+
+func (Default) isArgument() {}
+func (Zero) isArgument() {}
+func (Star) isArgument() {}
+func (Literal) isArgument() {}
+
+// Parse parses f and returns a list of actions.
+// An action may either be a literal string, or a Verb.
+func Parse(f string) ([]interface{}, error) {
+ var out []interface{}
+ for len(f) > 0 {
+ if f[0] == '%' {
+ v, n, err := ParseVerb(f)
+ if err != nil {
+ return nil, err
+ }
+ f = f[n:]
+ out = append(out, v)
+ } else {
+ n := strings.IndexByte(f, '%')
+ if n > -1 {
+ out = append(out, f[:n])
+ f = f[n:]
+ } else {
+ out = append(out, f)
+ f = ""
+ }
+ }
+ }
+
+ return out, nil
+}
+
+func atoi(s string) int {
+ n, _ := strconv.Atoi(s)
+ return n
+}
+
+// ParseVerb parses the verb at the beginning of f.
+// It returns the verb, how much of the input was consumed, and an error, if any.
+func ParseVerb(f string) (Verb, int, error) {
+ if len(f) < 2 {
+ return Verb{}, 0, ErrInvalid
+ }
+ const (
+ flags = 1
+
+ width = 2
+ widthStar = 3
+ widthIndex = 5
+
+ dot = 6
+ prec = 7
+ precStar = 8
+ precIndex = 10
+
+ verbIndex = 11
+ verb = 12
+ )
+
+ m := re.FindStringSubmatch(f)
+ if m == nil {
+ return Verb{}, 0, ErrInvalid
+ }
+
+ v := Verb{
+ Letter: []rune(m[verb])[0],
+ Flags: m[flags],
+ Raw: m[0],
+ }
+
+ if m[width] != "" {
+ // Literal width
+ v.Width = Literal(atoi(m[width]))
+ } else if m[widthStar] != "" {
+ // Star width
+ if m[widthIndex] != "" {
+ v.Width = Star{atoi(m[widthIndex])}
+ } else {
+ v.Width = Star{-1}
+ }
+ } else {
+ // Default width
+ v.Width = Default{}
+ }
+
+ if m[dot] == "" {
+ // default precision
+ v.Precision = Default{}
+ } else {
+ if m[prec] != "" {
+ // Literal precision
+ v.Precision = Literal(atoi(m[prec]))
+ } else if m[precStar] != "" {
+ // Star precision
+ if m[precIndex] != "" {
+ v.Precision = Star{atoi(m[precIndex])}
+ } else {
+ v.Precision = Star{-1}
+ }
+ } else {
+ // Zero precision
+ v.Precision = Zero{}
+ }
+ }
+
+ if m[verb] == "%" {
+ v.Value = 0
+ } else if m[verbIndex] != "" {
+ v.Value = atoi(m[verbIndex])
+ } else {
+ v.Value = -1
+ }
+
+ return v, len(m[0]), nil
+}
+
+const (
+ flags = `([+#0 -]*)`
+ verb = `([a-zA-Z%])`
+ index = `(?:\[([0-9]+)\])`
+ star = `((` + index + `)?\*)`
+ width1 = `([0-9]+)`
+ width2 = star
+ width = `(?:` + width1 + `|` + width2 + `)`
+ precision = width
+ widthAndPrecision = `(?:(?:` + width + `)?(?:(\.)(?:` + precision + `)?)?)`
+)
+
+var re = regexp.MustCompile(`^%` + flags + widthAndPrecision + `?` + index + `?` + verb)
diff --git a/vendor/honnef.co/go/tools/simple/CONTRIBUTING.md b/vendor/honnef.co/go/tools/simple/CONTRIBUTING.md
new file mode 100644
index 000000000..c54c6c50a
--- /dev/null
+++ b/vendor/honnef.co/go/tools/simple/CONTRIBUTING.md
@@ -0,0 +1,15 @@
+# Contributing to gosimple
+
+## Before filing an issue:
+
+### Are you having trouble building gosimple?
+
+Check you have the latest version of its dependencies. Run
+```
+go get -u honnef.co/go/tools/simple
+```
+If you still have problems, consider searching for existing issues before filing a new issue.
+
+## Before sending a pull request:
+
+Have you understood the purpose of gosimple? Make sure to carefully read `README`.
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 000000000..abb1648fa
--- /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
new file mode 100644
index 000000000..eb0072de5
--- /dev/null
+++ b/vendor/honnef.co/go/tools/simple/doc.go
@@ -0,0 +1,425 @@
+package simple
+
+import "honnef.co/go/tools/lint"
+
+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)
+ }
+
+After:
+
+ x := <-ch
+ fmt.Println(x)`,
+ Since: "2017.1",
+ },
+
+ "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
+ }
+
+After:
+
+ copy(dst, src)`,
+ Since: "2017.1",
+ },
+
+ "S1002": &lint.Documentation{
+ Title: `Omit comparison with boolean constant`,
+ Text: `Before:
+
+ 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:
+
+ for _ = range s {}
+ x, _ = someMap[key]
+ _ = <-ch
+
+After:
+
+ for range s{}
+ x = someMap[key]
+ <-ch`,
+ 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",
+ },
+
+ "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:
+
+ regexp.Compile("\\A(\\w+) profile: total \\d+\\n\\z")
+
+After:
+
+ regexp.Compile(` + "`" + `\A(\w+) profile: total \d+\n\z` + "`" + `)`,
+ Since: "2017.1",
+ },
+
+ "S1008": &lint.Documentation{
+ Title: `Simplify returning boolean expression`,
+ Text: `Before:
+
+ 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 x != nil && len(x) != 0 {}
+
+After:
+
+ if len(x) != 0 {}`,
+ 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",
+ },
+
+ "S1011": &lint.Documentation{
+ Title: `Use a single append to concatenate two slices`,
+ Text: `Before:
+
+ 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:
+
+ time.Now().Sub(x)
+
+After:
+
+ time.Since(x)`,
+ Since: "2017.1",
+ },
+
+ "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:
+
+ var x T1
+ y := T2{
+ Field1: x.Field1,
+ Field2: x.Field2,
+ }
+
+After:
+
+ var x T1
+ y := T2(x)`,
+ Since: "2017.1",
+ },
+
+ "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 strings.HasPrefix(str, prefix) {
+ str = str[len(prefix):]
+ }
+
+After:
+
+ str = strings.TrimPrefix(str, prefix)`,
+ Since: "2017.1",
+ },
+
+ "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:
+
+ for i := 0; i < n; i++ {
+ bs[i] = bs[offset+i]
+ }
+
+After:
+
+ copy(bs[:n], bs[offset:])`,
+ 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",
+ },
+
+ "S1020": &lint.Documentation{
+ Title: `Omit redundant nil check in type assertion`,
+ Text: `Before:
+
+ if _, ok := i.(T); ok && i != nil {}
+
+After:
+
+ 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:
+
+ x.Sub(time.Now())
+
+After:
+
+ time.Until(x)`,
+ Since: "2017.1",
+ },
+
+ "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
+
+ func (T2) String() string { return "Hello, world" }
+
+ 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)
+
+to
+
+ x
+ string(y)
+ z.String()`,
+ Since: "2017.1",
+ },
+
+ "S1028": &lint.Documentation{
+ Title: `Simplify error construction with fmt.Errorf`,
+ Text: `Before:
+
+ errors.New(fmt.Sprintf(...))
+
+After:
+
+ fmt.Errorf(...)`,
+ Since: "2017.1",
+ },
+
+ "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) {}
+
+After:
+
+ for _, r := range s {}`,
+ 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",
+ },
+
+ "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 {
+ ...
+ }
+ }
+
+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",
+ },
+}
diff --git a/vendor/honnef.co/go/tools/simple/lint.go b/vendor/honnef.co/go/tools/simple/lint.go
new file mode 100644
index 000000000..c78a7bb7a
--- /dev/null
+++ b/vendor/honnef.co/go/tools/simple/lint.go
@@ -0,0 +1,1816 @@
+// Package simple contains a linter for Go source code.
+package simple // import "honnef.co/go/tools/simple"
+
+import (
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "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"
+)
+
+func LintSingleCaseSelect(pass *analysis.Pass) (interface{}, error) {
+ isSingleSelect := func(node ast.Node) bool {
+ v, ok := node.(*ast.SelectStmt)
+ if !ok {
+ return false
+ }
+ return len(v.Body.List) == 1
+ }
+
+ seen := map[ast.Node]struct{}{}
+ fn := func(node ast.Node) {
+ switch v := node.(type) {
+ case *ast.ForStmt:
+ if len(v.Body.List) != 1 {
+ return
+ }
+ if !isSingleSelect(v.Body.List[0]) {
+ return
+ }
+ if _, ok := v.Body.List[0].(*ast.SelectStmt).Body.List[0].(*ast.CommClause).Comm.(*ast.SendStmt); ok {
+ // Don't suggest using range for channel sends
+ return
+ }
+ seen[v.Body.List[0]] = struct{}{}
+ ReportNodefFG(pass, node, "should use for range instead of for { select {} }")
+ case *ast.SelectStmt:
+ if _, ok := seen[v]; ok {
+ return
+ }
+ if !isSingleSelect(v) {
+ return
+ }
+ ReportNodefFG(pass, node, "should use a simple channel send/receive instead of select with a single case")
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil), (*ast.SelectStmt)(nil)}, fn)
+ return nil, nil
+}
+
+func LintLoopCopy(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ loop := node.(*ast.RangeStmt)
+
+ if loop.Key == nil {
+ return
+ }
+ if len(loop.Body.List) != 1 {
+ return
+ }
+ stmt, ok := loop.Body.List[0].(*ast.AssignStmt)
+ if !ok {
+ return
+ }
+ if stmt.Tok != token.ASSIGN || len(stmt.Lhs) != 1 || len(stmt.Rhs) != 1 {
+ return
+ }
+ lhs, ok := stmt.Lhs[0].(*ast.IndexExpr)
+ if !ok {
+ return
+ }
+
+ if _, ok := pass.TypesInfo.TypeOf(lhs.X).(*types.Slice); !ok {
+ return
+ }
+ lidx, ok := lhs.Index.(*ast.Ident)
+ if !ok {
+ return
+ }
+ key, ok := loop.Key.(*ast.Ident)
+ if !ok {
+ return
+ }
+ if pass.TypesInfo.TypeOf(lhs) == nil || pass.TypesInfo.TypeOf(stmt.Rhs[0]) == nil {
+ return
+ }
+ if pass.TypesInfo.ObjectOf(lidx) != pass.TypesInfo.ObjectOf(key) {
+ return
+ }
+ if !types.Identical(pass.TypesInfo.TypeOf(lhs), pass.TypesInfo.TypeOf(stmt.Rhs[0])) {
+ return
+ }
+ if _, ok := pass.TypesInfo.TypeOf(loop.X).(*types.Slice); !ok {
+ return
+ }
+
+ if rhs, ok := stmt.Rhs[0].(*ast.IndexExpr); ok {
+ rx, ok := rhs.X.(*ast.Ident)
+ _ = rx
+ if !ok {
+ return
+ }
+ ridx, ok := rhs.Index.(*ast.Ident)
+ if !ok {
+ return
+ }
+ if pass.TypesInfo.ObjectOf(ridx) != pass.TypesInfo.ObjectOf(key) {
+ return
+ }
+ } else if rhs, ok := stmt.Rhs[0].(*ast.Ident); ok {
+ value, ok := loop.Value.(*ast.Ident)
+ if !ok {
+ return
+ }
+ if pass.TypesInfo.ObjectOf(rhs) != pass.TypesInfo.ObjectOf(value) {
+ return
+ }
+ } else {
+ return
+ }
+ ReportNodefFG(pass, loop, "should use copy() instead of a loop")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.RangeStmt)(nil)}, fn)
+ return nil, nil
+}
+
+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(pass, expr.X)
+ y := IsBoolConst(pass, expr.Y)
+ if !x && !y {
+ return
+ }
+ var other ast.Expr
+ var val bool
+ if x {
+ val = BoolConst(pass, expr.X)
+ other = expr.Y
+ } else {
+ val = BoolConst(pass, expr.Y)
+ other = expr.X
+ }
+ basic, ok := pass.TypesInfo.TypeOf(other).Underlying().(*types.Basic)
+ if !ok || basic.Kind() != types.Bool {
+ return
+ }
+ op := ""
+ if (expr.Op == token.EQL && !val) || (expr.Op == token.NEQ && val) {
+ op = "!"
+ }
+ r := op + Render(pass, other)
+ l1 := len(r)
+ r = strings.TrimLeft(r, "!")
+ if (l1-len(r))%2 == 1 {
+ r = "!" + r
+ }
+ if IsInTest(pass, node) {
+ return
+ }
+ ReportNodefFG(pass, expr, "should omit comparison to bool constant, can be simplified to %s", r)
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func LintBytesBufferConversions(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ call := node.(*ast.CallExpr)
+ if len(call.Args) != 1 {
+ return
+ }
+
+ argCall, ok := call.Args[0].(*ast.CallExpr)
+ if !ok {
+ return
+ }
+ sel, ok := argCall.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+
+ 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))
+ }
+
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+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},
+ 0: {token.GEQ: true, token.LSS: false},
+ }
+ fn := func(node ast.Node) {
+ expr := node.(*ast.BinaryExpr)
+ switch expr.Op {
+ case token.GEQ, token.GTR, token.NEQ, token.LSS, token.EQL:
+ default:
+ return
+ }
+
+ value, ok := ExprToInt(pass, expr.Y)
+ if !ok {
+ return
+ }
+
+ allowedOps, ok := allowed[value]
+ if !ok {
+ return
+ }
+ b, ok := allowedOps[expr.Op]
+ if !ok {
+ return
+ }
+
+ call, ok := expr.X.(*ast.CallExpr)
+ if !ok {
+ return
+ }
+ sel, ok := call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+ pkgIdent, ok := sel.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+ funIdent := sel.Sel
+ if pkgIdent.Name != "strings" && pkgIdent.Name != "bytes" {
+ return
+ }
+ newFunc := ""
+ switch funIdent.Name {
+ case "IndexRune":
+ newFunc = "ContainsRune"
+ case "IndexAny":
+ newFunc = "ContainsAny"
+ case "Index":
+ newFunc = "Contains"
+ default:
+ return
+ }
+
+ prefix := ""
+ if !b {
+ prefix = "!"
+ }
+ ReportNodefFG(pass, node, "should use %s%s.%s(%s) instead", prefix, pkgIdent.Name, newFunc, RenderArgs(pass, call.Args))
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn)
+ return nil, nil
+}
+
+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 {
+ return
+ }
+ call, ok := expr.X.(*ast.CallExpr)
+ if !ok {
+ return
+ }
+ if !IsCallToAST(pass, call, "bytes.Compare") {
+ return
+ }
+ value, ok := ExprToInt(pass, expr.Y)
+ if !ok || value != 0 {
+ return
+ }
+ args := RenderArgs(pass, call.Args)
+ prefix := ""
+ if expr.Op == token.NEQ {
+ prefix = "!"
+ }
+ ReportNodefFG(pass, node, "should use %sbytes.Equal(%s) instead", prefix, args)
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn)
+ return nil, nil
+}
+
+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(pass, loop.Cond) || !BoolConst(pass, loop.Cond) {
+ return
+ }
+ ReportNodefFG(pass, loop, "should use for {} instead of for true {}")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn)
+ return nil, nil
+}
+
+func LintRegexpRaw(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ call := node.(*ast.CallExpr)
+ if !IsCallToAST(pass, call, "regexp.MustCompile") &&
+ !IsCallToAST(pass, call, "regexp.Compile") {
+ return
+ }
+ sel, ok := call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+ if len(call.Args) != 1 {
+ // invalid function call
+ return
+ }
+ lit, ok := call.Args[Arg("regexp.Compile.expr")].(*ast.BasicLit)
+ if !ok {
+ // TODO(dominikh): support string concat, maybe support constants
+ return
+ }
+ if lit.Kind != token.STRING {
+ // invalid function call
+ return
+ }
+ if lit.Value[0] != '"' {
+ // already a raw string
+ return
+ }
+ val := lit.Value
+ if !strings.Contains(val, `\\`) {
+ return
+ }
+ if strings.Contains(val, "`") {
+ return
+ }
+
+ bs := false
+ for _, c := range val {
+ if !bs && c == '\\' {
+ bs = true
+ continue
+ }
+ if bs && c == '\\' {
+ bs = false
+ continue
+ }
+ if bs {
+ // backslash followed by non-backslash -> escape sequence
+ return
+ }
+ }
+
+ ReportNodefFG(pass, call, "should use raw string (`...`) with regexp.%s to avoid having to escape twice", sel.Sel.Name)
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func LintIfReturn(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ block := node.(*ast.BlockStmt)
+ l := len(block.List)
+ if l < 2 {
+ return
+ }
+ n1, n2 := block.List[l-2], block.List[l-1]
+
+ if len(block.List) >= 3 {
+ if _, ok := block.List[l-3].(*ast.IfStmt); ok {
+ // Do not flag a series of if statements
+ return
+ }
+ }
+ // if statement with no init, no else, a single condition
+ // checking an identifier or function call and just a return
+ // statement in the body, that returns a boolean constant
+ ifs, ok := n1.(*ast.IfStmt)
+ if !ok {
+ return
+ }
+ if ifs.Else != nil || ifs.Init != nil {
+ return
+ }
+ if len(ifs.Body.List) != 1 {
+ return
+ }
+ if op, ok := ifs.Cond.(*ast.BinaryExpr); ok {
+ switch op.Op {
+ case token.EQL, token.LSS, token.GTR, token.NEQ, token.LEQ, token.GEQ:
+ default:
+ return
+ }
+ }
+ ret1, ok := ifs.Body.List[0].(*ast.ReturnStmt)
+ if !ok {
+ return
+ }
+ if len(ret1.Results) != 1 {
+ return
+ }
+ if !IsBoolConst(pass, ret1.Results[0]) {
+ return
+ }
+
+ ret2, ok := n2.(*ast.ReturnStmt)
+ if !ok {
+ return
+ }
+ if len(ret2.Results) != 1 {
+ return
+ }
+ if !IsBoolConst(pass, ret2.Results[0]) {
+ 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 '")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BlockStmt)(nil)}, fn)
+ return nil, nil
+}
+
+// LintRedundantNilCheckWithLen checks for the following reduntant nil-checks:
+//
+// if x == nil || len(x) == 0 {}
+// if x != nil && len(x) != 0 {}
+// if x != nil && len(x) == N {} (where N != 0)
+// if x != nil && len(x) > N {}
+// if x != nil && len(x) >= N {} (where N != 0)
+//
+func LintRedundantNilCheckWithLen(pass *analysis.Pass) (interface{}, error) {
+ isConstZero := func(expr ast.Expr) (isConst bool, isZero bool) {
+ _, ok := expr.(*ast.BasicLit)
+ if ok {
+ return true, IsZero(expr)
+ }
+ id, ok := expr.(*ast.Ident)
+ if !ok {
+ return false, false
+ }
+ c, ok := pass.TypesInfo.ObjectOf(id).(*types.Const)
+ if !ok {
+ return false, false
+ }
+ return true, c.Val().Kind() == constant.Int && c.Val().String() == "0"
+ }
+
+ fn := func(node ast.Node) {
+ // check that expr is "x || y" or "x && y"
+ expr := node.(*ast.BinaryExpr)
+ if expr.Op != token.LOR && expr.Op != token.LAND {
+ return
+ }
+ eqNil := expr.Op == token.LOR
+
+ // check that x is "xx == nil" or "xx != nil"
+ x, ok := expr.X.(*ast.BinaryExpr)
+ if !ok {
+ return
+ }
+ if eqNil && x.Op != token.EQL {
+ return
+ }
+ if !eqNil && x.Op != token.NEQ {
+ return
+ }
+ xx, ok := x.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+ if !IsNil(pass, x.Y) {
+ return
+ }
+
+ // check that y is "len(xx) == 0" or "len(xx) ... "
+ y, ok := expr.Y.(*ast.BinaryExpr)
+ if !ok {
+ return
+ }
+ if eqNil && y.Op != token.EQL { // must be len(xx) *==* 0
+ return
+ }
+ yx, ok := y.X.(*ast.CallExpr)
+ if !ok {
+ return
+ }
+ yxFun, ok := yx.Fun.(*ast.Ident)
+ if !ok || yxFun.Name != "len" || len(yx.Args) != 1 {
+ return
+ }
+ yxArg, ok := yx.Args[Arg("len.v")].(*ast.Ident)
+ if !ok {
+ return
+ }
+ if yxArg.Name != xx.Name {
+ return
+ }
+
+ if eqNil && !IsZero(y.Y) { // must be len(x) == *0*
+ return
+ }
+
+ if !eqNil {
+ isConst, isZero := isConstZero(y.Y)
+ if !isConst {
+ return
+ }
+ switch y.Op {
+ case token.EQL:
+ // avoid false positive for "xx != nil && len(xx) == 0"
+ if isZero {
+ return
+ }
+ case token.GEQ:
+ // avoid false positive for "xx != nil && len(xx) >= 0"
+ if isZero {
+ return
+ }
+ case token.NEQ:
+ // avoid false positive for "xx != nil && len(xx) != "
+ if !isZero {
+ return
+ }
+ case token.GTR:
+ // ok
+ default:
+ return
+ }
+ }
+
+ // 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 pass.TypesInfo.TypeOf(xx).(type) {
+ case *types.Slice:
+ nilType = "nil slices"
+ case *types.Map:
+ nilType = "nil maps"
+ case *types.Chan:
+ nilType = "nil channels"
+ default:
+ return
+ }
+ ReportNodefFG(pass, expr, "should omit nil check; len() for %s is defined as zero", nilType)
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func LintSlicing(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ n := node.(*ast.SliceExpr)
+ if n.Max != nil {
+ return
+ }
+ s, ok := n.X.(*ast.Ident)
+ if !ok || s.Obj == nil {
+ return
+ }
+ call, ok := n.High.(*ast.CallExpr)
+ if !ok || len(call.Args) != 1 || call.Ellipsis.IsValid() {
+ return
+ }
+ fun, ok := call.Fun.(*ast.Ident)
+ if !ok || fun.Name != "len" {
+ return
+ }
+ 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
+ }
+ ReportNodefFG(pass, n, "should omit second index in slice, s[a:len(s)] is identical to s[a:]")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.SliceExpr)(nil)}, fn)
+ return nil, nil
+}
+
+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 pass.TypesInfo.ObjectOf(ident) == pass.TypesInfo.ObjectOf(ident2) {
+ found = true
+ return false
+ }
+ return true
+ }
+ ast.Inspect(expr, fn)
+ return found
+}
+
+func LintLoopAppend(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ loop := node.(*ast.RangeStmt)
+ if !IsBlank(loop.Key) {
+ return
+ }
+ val, ok := loop.Value.(*ast.Ident)
+ if !ok {
+ return
+ }
+ if len(loop.Body.List) != 1 {
+ return
+ }
+ stmt, ok := loop.Body.List[0].(*ast.AssignStmt)
+ if !ok {
+ return
+ }
+ if stmt.Tok != token.ASSIGN || len(stmt.Lhs) != 1 || len(stmt.Rhs) != 1 {
+ return
+ }
+ if refersTo(pass, stmt.Lhs[0], val) {
+ return
+ }
+ call, ok := stmt.Rhs[0].(*ast.CallExpr)
+ if !ok {
+ return
+ }
+ if len(call.Args) != 2 || call.Ellipsis.IsValid() {
+ return
+ }
+ fun, ok := call.Fun.(*ast.Ident)
+ if !ok {
+ return
+ }
+ obj := pass.TypesInfo.ObjectOf(fun)
+ fn, ok := obj.(*types.Builtin)
+ if !ok || fn.Name() != "append" {
+ return
+ }
+
+ 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 {
+ return
+ }
+ if !types.Identical(src, dst) {
+ return
+ }
+
+ if Render(pass, stmt.Lhs[0]) != Render(pass, call.Args[Arg("append.slice")]) {
+ return
+ }
+
+ el, ok := call.Args[Arg("append.elems")].(*ast.Ident)
+ if !ok {
+ return
+ }
+ if pass.TypesInfo.ObjectOf(val) != pass.TypesInfo.ObjectOf(el) {
+ return
+ }
+ 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))
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.RangeStmt)(nil)}, fn)
+ return nil, nil
+}
+
+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(pass, sel.X, "time.Now") {
+ return
+ }
+ if sel.Sel.Name != "Sub" {
+ return
+ }
+ ReportNodefFG(pass, call, "should use time.Since instead of time.Now().Sub")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+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(pass, call, "(time.Time).Sub") {
+ return
+ }
+ if !IsCallToAST(pass, call.Args[Arg("(time.Time).Sub.u")], "time.Now") {
+ return
+ }
+ ReportNodefFG(pass, call, "should use time.Until instead of t.Sub(time.Now())")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+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 {
+ return
+ }
+ if !IsBlank(assign.Lhs[1]) {
+ return
+ }
+ switch rhs := assign.Rhs[0].(type) {
+ case *ast.IndexExpr:
+ // The type-checker should make sure that it's a map, but
+ // let's be safe.
+ if _, ok := pass.TypesInfo.TypeOf(rhs.X).Underlying().(*types.Map); !ok {
+ return
+ }
+ case *ast.UnaryExpr:
+ if rhs.Op != token.ARROW {
+ return
+ }
+ default:
+ return
+ }
+ cp := *assign
+ cp.Lhs = cp.Lhs[0:1]
+ ReportNodefFG(pass, assign, "should write %s instead of %s", Render(pass, &cp), Render(pass, assign))
+ }
+
+ fn2 := func(node ast.Node) {
+ stmt := node.(*ast.AssignStmt)
+ if len(stmt.Lhs) != len(stmt.Rhs) {
+ return
+ }
+ for i, lh := range stmt.Lhs {
+ rh := stmt.Rhs[i]
+ if !IsBlank(lh) {
+ continue
+ }
+ expr, ok := rh.(*ast.UnaryExpr)
+ if !ok {
+ continue
+ }
+ if expr.Op != token.ARROW {
+ continue
+ }
+ ReportNodefFG(pass, lh, "'_ = <-ch' can be simplified to '<-ch'")
+ }
+ }
+
+ fn3 := func(node ast.Node) {
+ rs := node.(*ast.RangeStmt)
+
+ // for x, _
+ if !IsBlank(rs.Key) && IsBlank(rs.Value) {
+ 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) {
+ ReportNodefFG(pass, rs.Key, "should omit values from range; this loop is equivalent to `for range ...`")
+ }
+ }
+
+ 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 LintSimplerStructConversion(pass *analysis.Pass) (interface{}, error) {
+ var skip ast.Node
+ fn := func(node ast.Node) {
+ // Do not suggest type conversion between pointers
+ if unary, ok := node.(*ast.UnaryExpr); ok && unary.Op == token.AND {
+ if lit, ok := unary.X.(*ast.CompositeLit); ok {
+ skip = lit
+ }
+ return
+ }
+
+ if node == skip {
+ return
+ }
+
+ lit, ok := node.(*ast.CompositeLit)
+ if !ok {
+ return
+ }
+ typ1, _ := pass.TypesInfo.TypeOf(lit.Type).(*types.Named)
+ if typ1 == nil {
+ return
+ }
+ s1, ok := typ1.Underlying().(*types.Struct)
+ if !ok {
+ return
+ }
+
+ var typ2 *types.Named
+ var ident *ast.Ident
+ getSelType := func(expr ast.Expr) (types.Type, *ast.Ident, bool) {
+ sel, ok := expr.(*ast.SelectorExpr)
+ if !ok {
+ return nil, nil, false
+ }
+ ident, ok := sel.X.(*ast.Ident)
+ if !ok {
+ return nil, nil, false
+ }
+ typ := pass.TypesInfo.TypeOf(sel.X)
+ return typ, ident, typ != nil
+ }
+ if len(lit.Elts) == 0 {
+ return
+ }
+ if s1.NumFields() != len(lit.Elts) {
+ return
+ }
+ for i, elt := range lit.Elts {
+ var t types.Type
+ var id *ast.Ident
+ var ok bool
+ switch elt := elt.(type) {
+ case *ast.SelectorExpr:
+ t, id, ok = getSelType(elt)
+ if !ok {
+ return
+ }
+ if i >= s1.NumFields() || s1.Field(i).Name() != elt.Sel.Name {
+ return
+ }
+ case *ast.KeyValueExpr:
+ var sel *ast.SelectorExpr
+ sel, ok = elt.Value.(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+
+ if elt.Key.(*ast.Ident).Name != sel.Sel.Name {
+ return
+ }
+ t, id, ok = getSelType(elt.Value)
+ }
+ if !ok {
+ return
+ }
+ // All fields must be initialized from the same object
+ if ident != nil && ident.Obj != id.Obj {
+ return
+ }
+ typ2, _ = t.(*types.Named)
+ if typ2 == nil {
+ return
+ }
+ ident = id
+ }
+
+ if typ2 == nil {
+ return
+ }
+
+ if typ1.Obj().Pkg() != typ2.Obj().Pkg() {
+ // Do not suggest type conversions between different
+ // packages. Types in different packages might only match
+ // by coincidence. Furthermore, if the dependency ever
+ // adds more fields to its type, it could break the code
+ // that relies on the type conversion to work.
+ return
+ }
+
+ s2, ok := typ2.Underlying().(*types.Struct)
+ if !ok {
+ return
+ }
+ if typ1 == typ2 {
+ return
+ }
+ if IsGoVersion(pass, 8) {
+ if !types.IdenticalIgnoreTags(s1, s2) {
+ return
+ }
+ } else {
+ if !types.Identical(s1, s2) {
+ return
+ }
+ }
+ ReportNodefFG(pass, node, "should convert %s (type %s) to %s instead of using struct literal",
+ ident.Name, typ2.Obj().Name(), typ1.Obj().Name())
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.UnaryExpr)(nil), (*ast.CompositeLit)(nil)}, fn)
+ return nil, nil
+}
+
+func LintTrim(pass *analysis.Pass) (interface{}, error) {
+ sameNonDynamic := func(node1, node2 ast.Node) bool {
+ if reflect.TypeOf(node1) != reflect.TypeOf(node2) {
+ return false
+ }
+
+ switch node1 := node1.(type) {
+ case *ast.Ident:
+ return node1.Obj == node2.(*ast.Ident).Obj
+ case *ast.SelectorExpr:
+ return Render(pass, node1) == Render(pass, node2)
+ case *ast.IndexExpr:
+ return Render(pass, node1) == Render(pass, node2)
+ }
+ return false
+ }
+
+ isLenOnIdent := func(fn ast.Expr, ident ast.Expr) bool {
+ call, ok := fn.(*ast.CallExpr)
+ if !ok {
+ return false
+ }
+ if fn, ok := call.Fun.(*ast.Ident); !ok || fn.Name != "len" {
+ return false
+ }
+ if len(call.Args) != 1 {
+ return false
+ }
+ return sameNonDynamic(call.Args[Arg("len.v")], ident)
+ }
+
+ fn := func(node ast.Node) {
+ var pkg string
+ var fun string
+
+ ifstmt := node.(*ast.IfStmt)
+ if ifstmt.Init != nil {
+ return
+ }
+ if ifstmt.Else != nil {
+ return
+ }
+ if len(ifstmt.Body.List) != 1 {
+ return
+ }
+ condCall, ok := ifstmt.Cond.(*ast.CallExpr)
+ if !ok {
+ return
+ }
+ switch {
+ case IsCallToAST(pass, condCall, "strings.HasPrefix"):
+ pkg = "strings"
+ fun = "HasPrefix"
+ case IsCallToAST(pass, condCall, "strings.HasSuffix"):
+ pkg = "strings"
+ fun = "HasSuffix"
+ case IsCallToAST(pass, condCall, "strings.Contains"):
+ pkg = "strings"
+ fun = "Contains"
+ case IsCallToAST(pass, condCall, "bytes.HasPrefix"):
+ pkg = "bytes"
+ fun = "HasPrefix"
+ case IsCallToAST(pass, condCall, "bytes.HasSuffix"):
+ pkg = "bytes"
+ fun = "HasSuffix"
+ case IsCallToAST(pass, condCall, "bytes.Contains"):
+ pkg = "bytes"
+ fun = "Contains"
+ default:
+ return
+ }
+
+ assign, ok := ifstmt.Body.List[0].(*ast.AssignStmt)
+ if !ok {
+ return
+ }
+ if assign.Tok != token.ASSIGN {
+ return
+ }
+ if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {
+ return
+ }
+ if !sameNonDynamic(condCall.Args[0], assign.Lhs[0]) {
+ return
+ }
+
+ switch rhs := assign.Rhs[0].(type) {
+ case *ast.CallExpr:
+ if len(rhs.Args) < 2 || !sameNonDynamic(condCall.Args[0], rhs.Args[0]) || !sameNonDynamic(condCall.Args[1], rhs.Args[1]) {
+ return
+ }
+ 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:
+ slice := rhs
+ if !ok {
+ return
+ }
+ if slice.Slice3 {
+ return
+ }
+ if !sameNonDynamic(slice.X, condCall.Args[0]) {
+ return
+ }
+ var index ast.Expr
+ switch fun {
+ case "HasPrefix":
+ // TODO(dh) We could detect a High that is len(s), but another
+ // rule will already flag that, anyway.
+ if slice.High != nil {
+ return
+ }
+ index = slice.Low
+ case "HasSuffix":
+ if slice.Low != nil {
+ n, ok := ExprToInt(pass, slice.Low)
+ if !ok || n != 0 {
+ return
+ }
+ }
+ index = slice.High
+ }
+
+ switch index := index.(type) {
+ case *ast.CallExpr:
+ if fun != "HasPrefix" {
+ return
+ }
+ if fn, ok := index.Fun.(*ast.Ident); !ok || fn.Name != "len" {
+ return
+ }
+ if len(index.Args) != 1 {
+ return
+ }
+ id3 := index.Args[Arg("len.v")]
+ switch oid3 := condCall.Args[1].(type) {
+ case *ast.BasicLit:
+ if pkg != "strings" {
+ return
+ }
+ lit, ok := id3.(*ast.BasicLit)
+ if !ok {
+ return
+ }
+ s1, ok1 := ExprToString(pass, lit)
+ s2, ok2 := ExprToString(pass, condCall.Args[1])
+ if !ok1 || !ok2 || s1 != s2 {
+ return
+ }
+ default:
+ if !sameNonDynamic(id3, oid3) {
+ return
+ }
+ }
+ case *ast.BasicLit, *ast.Ident:
+ if fun != "HasPrefix" {
+ return
+ }
+ if pkg != "strings" {
+ return
+ }
+ string, ok1 := ExprToString(pass, condCall.Args[1])
+ int, ok2 := ExprToInt(pass, slice.Low)
+ if !ok1 || !ok2 || int != int64(len(string)) {
+ return
+ }
+ case *ast.BinaryExpr:
+ if fun != "HasSuffix" {
+ return
+ }
+ if index.Op != token.SUB {
+ return
+ }
+ if !isLenOnIdent(index.X, condCall.Args[0]) ||
+ !isLenOnIdent(index.Y, condCall.Args[1]) {
+ return
+ }
+ default:
+ return
+ }
+
+ var replacement string
+ switch fun {
+ case "HasPrefix":
+ replacement = "TrimPrefix"
+ case "HasSuffix":
+ replacement = "TrimSuffix"
+ }
+ ReportNodefFG(pass, ifstmt, "should replace this if statement with an unconditional %s.%s", pkg, replacement)
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn)
+ return nil, nil
+}
+
+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
+ // TODO(dh): support sliding to a different offset than the beginning of the slice
+
+ fn := func(node ast.Node) {
+ /*
+ for i := 0; i < n; i++ {
+ bs[i] = bs[offset+i]
+ }
+
+ ↓
+
+ copy(bs[:n], bs[offset:offset+n])
+ */
+
+ loop := node.(*ast.ForStmt)
+ if len(loop.Body.List) != 1 || loop.Init == nil || loop.Cond == nil || loop.Post == nil {
+ return
+ }
+ assign, ok := loop.Init.(*ast.AssignStmt)
+ if !ok || len(assign.Lhs) != 1 || len(assign.Rhs) != 1 || !IsZero(assign.Rhs[0]) {
+ return
+ }
+ initvar, ok := assign.Lhs[0].(*ast.Ident)
+ if !ok {
+ return
+ }
+ post, ok := loop.Post.(*ast.IncDecStmt)
+ if !ok || post.Tok != token.INC {
+ return
+ }
+ postvar, ok := post.X.(*ast.Ident)
+ if !ok || pass.TypesInfo.ObjectOf(postvar) != pass.TypesInfo.ObjectOf(initvar) {
+ return
+ }
+ bin, ok := loop.Cond.(*ast.BinaryExpr)
+ if !ok || bin.Op != token.LSS {
+ return
+ }
+ binx, ok := bin.X.(*ast.Ident)
+ if !ok || pass.TypesInfo.ObjectOf(binx) != pass.TypesInfo.ObjectOf(initvar) {
+ return
+ }
+ biny, ok := bin.Y.(*ast.Ident)
+ if !ok {
+ return
+ }
+
+ assign, ok = loop.Body.List[0].(*ast.AssignStmt)
+ if !ok || len(assign.Lhs) != 1 || len(assign.Rhs) != 1 || assign.Tok != token.ASSIGN {
+ return
+ }
+ lhs, ok := assign.Lhs[0].(*ast.IndexExpr)
+ if !ok {
+ return
+ }
+ rhs, ok := assign.Rhs[0].(*ast.IndexExpr)
+ if !ok {
+ return
+ }
+
+ bs1, ok := lhs.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+ bs2, ok := rhs.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+ obj1 := pass.TypesInfo.ObjectOf(bs1)
+ obj2 := pass.TypesInfo.ObjectOf(bs2)
+ if obj1 != obj2 {
+ return
+ }
+ if _, ok := obj1.Type().Underlying().(*types.Slice); !ok {
+ return
+ }
+
+ index1, ok := lhs.Index.(*ast.Ident)
+ if !ok || pass.TypesInfo.ObjectOf(index1) != pass.TypesInfo.ObjectOf(initvar) {
+ return
+ }
+ index2, ok := rhs.Index.(*ast.BinaryExpr)
+ if !ok || index2.Op != token.ADD {
+ return
+ }
+ add1, ok := index2.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+ add2, ok := index2.Y.(*ast.Ident)
+ if !ok || pass.TypesInfo.ObjectOf(add2) != pass.TypesInfo.ObjectOf(initvar) {
+ return
+ }
+
+ ReportNodefFG(pass, loop, "should use copy(%s[:%s], %s[%s:]) instead", Render(pass, bs1), Render(pass, biny), Render(pass, bs1), Render(pass, add1))
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn)
+ return nil, nil
+}
+
+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" {
+ // FIXME check whether make is indeed the built-in function
+ return
+ }
+ switch len(call.Args) {
+ case 2:
+ // make(T, len)
+ if _, ok := pass.TypesInfo.TypeOf(call.Args[Arg("make.t")]).Underlying().(*types.Slice); ok {
+ break
+ }
+ if IsZero(call.Args[Arg("make.size[0]")]) {
+ 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(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(pass, call.Args[Arg("make.t")]), Render(pass, call.Args[Arg("make.size[0]")]))
+ }
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+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 {
+ return false
+ }
+ xident, ok := xbinop.X.(*ast.Ident)
+ if !ok || xident.Obj != ident.Obj {
+ return false
+ }
+ if !IsNil(pass, xbinop.Y) {
+ return false
+ }
+ return true
+ }
+ isOKCheck := func(ident *ast.Ident, expr ast.Expr) bool {
+ yident, ok := expr.(*ast.Ident)
+ if !ok || yident.Obj != ident.Obj {
+ return false
+ }
+ return true
+ }
+ fn1 := func(node ast.Node) {
+ ifstmt := node.(*ast.IfStmt)
+ assign, ok := ifstmt.Init.(*ast.AssignStmt)
+ if !ok || len(assign.Lhs) != 2 || len(assign.Rhs) != 1 || !IsBlank(assign.Lhs[0]) {
+ return
+ }
+ assert, ok := assign.Rhs[0].(*ast.TypeAssertExpr)
+ if !ok {
+ return
+ }
+ binop, ok := ifstmt.Cond.(*ast.BinaryExpr)
+ if !ok || binop.Op != token.LAND {
+ return
+ }
+ assertIdent, ok := assert.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+ assignIdent, ok := assign.Lhs[1].(*ast.Ident)
+ if !ok {
+ return
+ }
+ if !(isNilCheck(assertIdent, binop.X) && isOKCheck(assignIdent, binop.Y)) &&
+ !(isNilCheck(assertIdent, binop.Y) && isOKCheck(assignIdent, binop.X)) {
+ return
+ }
+ 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 {}'
+ ifstmt := node.(*ast.IfStmt)
+ if ifstmt.Init != nil {
+ return
+ }
+ if ifstmt.Else != nil {
+ return
+ }
+ if len(ifstmt.Body.List) != 1 {
+ return
+ }
+ binop, ok := ifstmt.Cond.(*ast.BinaryExpr)
+ if !ok {
+ return
+ }
+ if binop.Op != token.NEQ {
+ return
+ }
+ lhs, ok := binop.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+ if !IsNil(pass, binop.Y) {
+ return
+ }
+
+ // Check that inner ifstmt is an `if _, ok := x.(T); ok {}`
+ ifstmt, ok = ifstmt.Body.List[0].(*ast.IfStmt)
+ if !ok {
+ return
+ }
+ assign, ok := ifstmt.Init.(*ast.AssignStmt)
+ if !ok || len(assign.Lhs) != 2 || len(assign.Rhs) != 1 || !IsBlank(assign.Lhs[0]) {
+ return
+ }
+ assert, ok := assign.Rhs[0].(*ast.TypeAssertExpr)
+ if !ok {
+ return
+ }
+ assertIdent, ok := assert.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+ if lhs.Obj != assertIdent.Obj {
+ return
+ }
+ assignIdent, ok := assign.Lhs[1].(*ast.Ident)
+ if !ok {
+ return
+ }
+ if !isOKCheck(assignIdent, ifstmt.Cond) {
+ return
+ }
+ ReportNodefFG(pass, ifstmt, "when %s is true, %s can't be nil", Render(pass, assignIdent), Render(pass, assertIdent))
+ }
+ 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 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 {
+ if num >= 2 {
+ return false
+ }
+ assign, ok := node.(*ast.AssignStmt)
+ if !ok {
+ return true
+ }
+ for _, lhs := range assign.Lhs {
+ if oident, ok := lhs.(*ast.Ident); ok {
+ if oident.Obj == ident.Obj {
+ num++
+ }
+ }
+ }
+
+ return true
+ })
+ return num >= 2
+ }
+ fn := func(node ast.Node) {
+ block := node.(*ast.BlockStmt)
+ if len(block.List) < 2 {
+ return
+ }
+ for i, stmt := range block.List[:len(block.List)-1] {
+ _ = i
+ decl, ok := stmt.(*ast.DeclStmt)
+ if !ok {
+ continue
+ }
+ gdecl, ok := decl.Decl.(*ast.GenDecl)
+ if !ok || gdecl.Tok != token.VAR || len(gdecl.Specs) != 1 {
+ continue
+ }
+ vspec, ok := gdecl.Specs[0].(*ast.ValueSpec)
+ if !ok || len(vspec.Names) != 1 || len(vspec.Values) != 0 {
+ continue
+ }
+
+ assign, ok := block.List[i+1].(*ast.AssignStmt)
+ if !ok || assign.Tok != token.ASSIGN {
+ continue
+ }
+ if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {
+ continue
+ }
+ ident, ok := assign.Lhs[0].(*ast.Ident)
+ if !ok {
+ continue
+ }
+ if vspec.Names[0].Obj != ident.Obj {
+ continue
+ }
+
+ if refersTo(pass, assign.Rhs[0], ident) {
+ continue
+ }
+ if hasMultipleAssignments(block, ident) {
+ continue
+ }
+
+ ReportNodefFG(pass, decl, "should merge variable declaration with assignment on next line")
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BlockStmt)(nil)}, fn)
+ return nil, nil
+}
+
+func LintRedundantBreak(pass *analysis.Pass) (interface{}, error) {
+ fn1 := func(node ast.Node) {
+ clause := node.(*ast.CaseClause)
+ if len(clause.Body) < 2 {
+ return
+ }
+ branch, ok := clause.Body[len(clause.Body)-1].(*ast.BranchStmt)
+ if !ok || branch.Tok != token.BREAK || branch.Label != nil {
+ return
+ }
+ ReportNodefFG(pass, branch, "redundant break statement")
+ }
+ fn2 := func(node ast.Node) {
+ var ret *ast.FieldList
+ var body *ast.BlockStmt
+ switch x := node.(type) {
+ case *ast.FuncDecl:
+ ret = x.Type.Results
+ body = x.Body
+ case *ast.FuncLit:
+ ret = x.Type.Results
+ body = x.Body
+ default:
+ panic(fmt.Sprintf("unreachable: %T", node))
+ }
+ // if the func has results, a return can't be redundant.
+ // similarly, if there are no statements, there can be
+ // no return.
+ if ret != nil || body == nil || len(body.List) < 1 {
+ return
+ }
+ rst, ok := body.List[len(body.List)-1].(*ast.ReturnStmt)
+ if !ok {
+ return
+ }
+ // we don't need to check rst.Results as we already
+ // checked x.Type.Results to be nil.
+ ReportNodefFG(pass, rst, "redundant return statement")
+ }
+ 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, msCache *typeutil.MethodSetCache) bool {
+ ms := msCache.MethodSet(T)
+ sel := ms.Lookup(nil, "String")
+ if sel == nil {
+ return false
+ }
+ fn, ok := sel.Obj().(*types.Func)
+ if !ok {
+ // should be unreachable
+ return false
+ }
+ sig := fn.Type().(*types.Signature)
+ if sig.Params().Len() != 0 {
+ return false
+ }
+ if sig.Results().Len() != 1 {
+ return false
+ }
+ if !IsType(sig.Results().At(0).Type(), "string") {
+ return false
+ }
+ return true
+}
+
+func LintRedundantSprintf(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ call := node.(*ast.CallExpr)
+ if !IsCallToAST(pass, call, "fmt.Sprintf") {
+ return
+ }
+ if len(call.Args) != 2 {
+ return
+ }
+ if s, ok := ExprToString(pass, call.Args[Arg("fmt.Sprintf.format")]); !ok || s != "%s" {
+ return
+ }
+ arg := call.Args[Arg("fmt.Sprintf.a[0]")]
+ typ := pass.TypesInfo.TypeOf(arg)
+
+ 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() {
+ ReportNodefFG(pass, call, "the argument is already a string, there's no need to use fmt.Sprintf")
+ } else {
+ ReportNodefFG(pass, call, "the argument's underlying type is a string, should use a simple conversion instead of fmt.Sprintf")
+ }
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func LintErrorsNewSprintf(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ if !IsCallToAST(pass, node, "errors.New") {
+ return
+ }
+ call := node.(*ast.CallExpr)
+ if !IsCallToAST(pass, call.Args[Arg("errors.New.text")], "fmt.Sprintf") {
+ return
+ }
+ ReportNodefFG(pass, node, "should use fmt.Errorf(...) instead of errors.New(fmt.Sprintf(...))")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func LintRangeStringRunes(pass *analysis.Pass) (interface{}, error) {
+ return sharedcheck.CheckRangeStringRunes(pass)
+}
+
+func LintNilCheckAroundRange(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ ifstmt := node.(*ast.IfStmt)
+ cond, ok := ifstmt.Cond.(*ast.BinaryExpr)
+ if !ok {
+ return
+ }
+
+ if cond.Op != token.NEQ || !IsNil(pass, cond.Y) || len(ifstmt.Body.List) != 1 {
+ return
+ }
+
+ loop, ok := ifstmt.Body.List[0].(*ast.RangeStmt)
+ if !ok {
+ return
+ }
+ ifXIdent, ok := cond.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+ rangeXIdent, ok := loop.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+ if ifXIdent.Obj != rangeXIdent.Obj {
+ return
+ }
+ switch pass.TypesInfo.TypeOf(rangeXIdent).(type) {
+ case *types.Slice, *types.Map:
+ ReportNodefFG(pass, node, "unnecessary nil check around range")
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn)
+ return nil, nil
+}
+
+func isPermissibleSort(pass *analysis.Pass, node ast.Node) bool {
+ call := node.(*ast.CallExpr)
+ typeconv, ok := call.Args[0].(*ast.CallExpr)
+ if !ok {
+ return true
+ }
+
+ sel, ok := typeconv.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return true
+ }
+ name := SelectorName(pass, sel)
+ switch name {
+ case "sort.IntSlice", "sort.Float64Slice", "sort.StringSlice":
+ default:
+ return true
+ }
+
+ return false
+}
+
+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) {
+ case *ast.FuncLit:
+ body = node.Body
+ case *ast.FuncDecl:
+ body = node.Body
+ default:
+ panic(fmt.Sprintf("unreachable: %T", node))
+ }
+ if body == nil {
+ return
+ }
+
+ var errors []Error
+ permissible := false
+ fnSorts := func(node ast.Node) bool {
+ if permissible {
+ return false
+ }
+ if !IsCallToAST(pass, node, "sort.Sort") {
+ return true
+ }
+ 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(pass, sel)
+
+ switch name {
+ case "sort.IntSlice":
+ errors = append(errors, Error{node, "should use sort.Ints(...) instead of sort.Sort(sort.IntSlice(...))"})
+ case "sort.Float64Slice":
+ errors = append(errors, Error{node, "should use sort.Float64s(...) instead of sort.Sort(sort.Float64Slice(...))"})
+ case "sort.StringSlice":
+ errors = append(errors, Error{node, "should use sort.Strings(...) instead of sort.Sort(sort.StringSlice(...))"})
+ }
+ return true
+ }
+ ast.Inspect(body, fnSorts)
+
+ if permissible {
+ return
+ }
+ allErrors = append(allErrors, errors...)
+ }
+ 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 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 `_, = []
+
+ assign, ok := stmt.(*ast.AssignStmt)
+ if !ok {
+ return nil, nil, nil, false
+ }
+ if len(assign.Lhs) != 2 || len(assign.Rhs) != 1 {
+ return nil, nil, nil, false
+ }
+ if !IsBlank(assign.Lhs[0]) {
+ return nil, nil, nil, false
+ }
+ ident, ok := assign.Lhs[1].(*ast.Ident)
+ if !ok {
+ return nil, nil, nil, false
+ }
+ index, ok := assign.Rhs[0].(*ast.IndexExpr)
+ if !ok {
+ return nil, nil, nil, false
+ }
+ if _, ok := pass.TypesInfo.TypeOf(index.X).(*types.Map); !ok {
+ return nil, nil, nil, false
+ }
+ key = index.Index
+ return ident, index.X, key, true
+ }
+ fn := func(node ast.Node) {
+ stmt := node.(*ast.IfStmt)
+ if len(stmt.Body.List) != 1 {
+ return
+ }
+ if stmt.Else != nil {
+ return
+ }
+ expr, ok := stmt.Body.List[0].(*ast.ExprStmt)
+ if !ok {
+ return
+ }
+ call, ok := expr.X.(*ast.CallExpr)
+ if !ok {
+ return
+ }
+ if !IsCallToAST(pass, call, "delete") {
+ return
+ }
+ b, m, key, ok := isCommaOkMapIndex(stmt.Init)
+ if !ok {
+ return
+ }
+ if cond, ok := stmt.Cond.(*ast.Ident); !ok || pass.TypesInfo.ObjectOf(cond) != pass.TypesInfo.ObjectOf(b) {
+ return
+ }
+ if Render(pass, call.Args[0]) != Render(pass, m) || Render(pass, call.Args[1]) != Render(pass, key) {
+ return
+ }
+ ReportNodefFG(pass, stmt, "unnecessary guard around call to delete")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn)
+ return nil, nil
+}
+
+func LintSimplifyTypeSwitch(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ stmt := node.(*ast.TypeSwitchStmt)
+ if stmt.Init != nil {
+ // bailing out for now, can't anticipate how type switches with initializers are being used
+ return
+ }
+ expr, ok := stmt.Assign.(*ast.ExprStmt)
+ if !ok {
+ // the user is in fact assigning the result
+ return
+ }
+ assert := expr.X.(*ast.TypeAssertExpr)
+ ident, ok := assert.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+ x := pass.TypesInfo.ObjectOf(ident)
+ var allOffenders []ast.Node
+ for _, clause := range stmt.Body.List {
+ clause := clause.(*ast.CaseClause)
+ if len(clause.List) != 1 {
+ continue
+ }
+ hasUnrelatedAssertion := false
+ var offenders []ast.Node
+ ast.Inspect(clause, func(node ast.Node) bool {
+ assert2, ok := node.(*ast.TypeAssertExpr)
+ if !ok {
+ return true
+ }
+ ident, ok := assert2.X.(*ast.Ident)
+ if !ok {
+ hasUnrelatedAssertion = true
+ return false
+ }
+ if pass.TypesInfo.ObjectOf(ident) != x {
+ hasUnrelatedAssertion = true
+ return false
+ }
+
+ if !types.Identical(pass.TypesInfo.TypeOf(clause.List[0]), pass.TypesInfo.TypeOf(assert2.Type)) {
+ hasUnrelatedAssertion = true
+ return false
+ }
+ offenders = append(offenders, assert2)
+ return true
+ })
+ if !hasUnrelatedAssertion {
+ // don't flag cases that have other type assertions
+ // unrelated to the one in the case clause. often
+ // times, this is done for symmetry, when two
+ // different values have to be asserted to the same
+ // type.
+ allOffenders = append(allOffenders, offenders...)
+ }
+ }
+ if len(allOffenders) != 0 {
+ at := ""
+ for _, offender := range allOffenders {
+ pos := lint.DisplayPosition(pass.Fset, offender.Pos())
+ at += "\n\t" + pos.String()
+ }
+ 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)
+ }
+ }
+ 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/LICENSE b/vendor/honnef.co/go/tools/ssa/LICENSE
new file mode 100644
index 000000000..aee48041e
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2009 The Go Authors. All rights reserved.
+Copyright (c) 2016 Dominik Honnef. 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/ssa/blockopt.go b/vendor/honnef.co/go/tools/ssa/blockopt.go
new file mode 100644
index 000000000..22c9a4c0d
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/blockopt.go
@@ -0,0 +1,195 @@
+// 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 ssa
+
+// Simple block optimizations to simplify the control flow graph.
+
+// TODO(adonovan): opt: instead of creating several "unreachable" blocks
+// per function in the Builder, reuse a single one (e.g. at Blocks[1])
+// to reduce garbage.
+
+import (
+ "fmt"
+ "os"
+)
+
+// If true, perform sanity checking and show progress at each
+// successive iteration of optimizeBlocks. Very verbose.
+const debugBlockOpt = false
+
+// markReachable sets Index=-1 for all blocks reachable from b.
+func markReachable(b *BasicBlock) {
+ b.Index = -1
+ for _, succ := range b.Succs {
+ if succ.Index == 0 {
+ markReachable(succ)
+ }
+ }
+}
+
+func DeleteUnreachableBlocks(f *Function) {
+ deleteUnreachableBlocks(f)
+}
+
+// deleteUnreachableBlocks marks all reachable blocks of f and
+// eliminates (nils) all others, including possibly cyclic subgraphs.
+//
+func deleteUnreachableBlocks(f *Function) {
+ const white, black = 0, -1
+ // We borrow b.Index temporarily as the mark bit.
+ for _, b := range f.Blocks {
+ b.Index = white
+ }
+ markReachable(f.Blocks[0])
+ if f.Recover != nil {
+ markReachable(f.Recover)
+ }
+ for i, b := range f.Blocks {
+ if b.Index == white {
+ for _, c := range b.Succs {
+ if c.Index == black {
+ c.removePred(b) // delete white->black edge
+ }
+ }
+ if debugBlockOpt {
+ fmt.Fprintln(os.Stderr, "unreachable", b)
+ }
+ f.Blocks[i] = nil // delete b
+ }
+ }
+ f.removeNilBlocks()
+}
+
+// jumpThreading attempts to apply simple jump-threading to block b,
+// in which a->b->c become a->c if b is just a Jump.
+// The result is true if the optimization was applied.
+//
+func jumpThreading(f *Function, b *BasicBlock) bool {
+ if b.Index == 0 {
+ return false // don't apply to entry block
+ }
+ if b.Instrs == nil {
+ return false
+ }
+ if _, ok := b.Instrs[0].(*Jump); !ok {
+ return false // not just a jump
+ }
+ c := b.Succs[0]
+ if c == b {
+ return false // don't apply to degenerate jump-to-self.
+ }
+ if c.hasPhi() {
+ return false // not sound without more effort
+ }
+ for j, a := range b.Preds {
+ a.replaceSucc(b, c)
+
+ // If a now has two edges to c, replace its degenerate If by Jump.
+ if len(a.Succs) == 2 && a.Succs[0] == c && a.Succs[1] == c {
+ jump := new(Jump)
+ jump.setBlock(a)
+ a.Instrs[len(a.Instrs)-1] = jump
+ a.Succs = a.Succs[:1]
+ c.removePred(b)
+ } else {
+ if j == 0 {
+ c.replacePred(b, a)
+ } else {
+ c.Preds = append(c.Preds, a)
+ }
+ }
+
+ if debugBlockOpt {
+ fmt.Fprintln(os.Stderr, "jumpThreading", a, b, c)
+ }
+ }
+ f.Blocks[b.Index] = nil // delete b
+ return true
+}
+
+// fuseBlocks attempts to apply the block fusion optimization to block
+// a, in which a->b becomes ab if len(a.Succs)==len(b.Preds)==1.
+// The result is true if the optimization was applied.
+//
+func fuseBlocks(f *Function, a *BasicBlock) bool {
+ if len(a.Succs) != 1 {
+ return false
+ }
+ b := a.Succs[0]
+ if len(b.Preds) != 1 {
+ return false
+ }
+
+ // Degenerate &&/|| ops may result in a straight-line CFG
+ // containing φ-nodes. (Ideally we'd replace such them with
+ // their sole operand but that requires Referrers, built later.)
+ if b.hasPhi() {
+ return false // not sound without further effort
+ }
+
+ // Eliminate jump at end of A, then copy all of B across.
+ a.Instrs = append(a.Instrs[:len(a.Instrs)-1], b.Instrs...)
+ for _, instr := range b.Instrs {
+ instr.setBlock(a)
+ }
+
+ // A inherits B's successors
+ a.Succs = append(a.succs2[:0], b.Succs...)
+
+ // Fix up Preds links of all successors of B.
+ for _, c := range b.Succs {
+ c.replacePred(b, a)
+ }
+
+ if debugBlockOpt {
+ fmt.Fprintln(os.Stderr, "fuseBlocks", a, b)
+ }
+
+ f.Blocks[b.Index] = nil // delete b
+ return true
+}
+
+func OptimizeBlocks(f *Function) {
+ optimizeBlocks(f)
+}
+
+// optimizeBlocks() performs some simple block optimizations on a
+// completed function: dead block elimination, block fusion, jump
+// threading.
+//
+func optimizeBlocks(f *Function) {
+ deleteUnreachableBlocks(f)
+
+ // Loop until no further progress.
+ changed := true
+ for changed {
+ changed = false
+
+ if debugBlockOpt {
+ f.WriteTo(os.Stderr)
+ mustSanityCheck(f, nil)
+ }
+
+ for _, b := range f.Blocks {
+ // f.Blocks will temporarily contain nils to indicate
+ // deleted blocks; we remove them at the end.
+ if b == nil {
+ continue
+ }
+
+ // Fuse blocks. b->c becomes bc.
+ if fuseBlocks(f, b) {
+ changed = true
+ }
+
+ // a->b->c becomes a->c if b contains only a Jump.
+ if jumpThreading(f, b) {
+ changed = true
+ continue // (b was disconnected)
+ }
+ }
+ }
+ f.removeNilBlocks()
+}
diff --git a/vendor/honnef.co/go/tools/ssa/builder.go b/vendor/honnef.co/go/tools/ssa/builder.go
new file mode 100644
index 000000000..317ac0611
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/builder.go
@@ -0,0 +1,2379 @@
+// 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 ssa
+
+// This file implements the BUILD phase of SSA construction.
+//
+// SSA construction has two phases, CREATE and BUILD. In the CREATE phase
+// (create.go), all packages are constructed and type-checked and
+// definitions of all package members are created, method-sets are
+// computed, and wrapper methods are synthesized.
+// ssa.Packages are created in arbitrary order.
+//
+// In the BUILD phase (builder.go), the builder traverses the AST of
+// each Go source function and generates SSA instructions for the
+// function body. Initializer expressions for package-level variables
+// are emitted to the package's init() function in the order specified
+// by go/types.Info.InitOrder, then code for each function in the
+// package is generated in lexical order.
+// The BUILD phases for distinct packages are independent and are
+// executed in parallel.
+//
+// TODO(adonovan): indeed, building functions is now embarrassingly parallel.
+// Audit for concurrency then benchmark using more goroutines.
+//
+// The builder's and Program's indices (maps) are populated and
+// mutated during the CREATE phase, but during the BUILD phase they
+// remain constant. The sole exception is Prog.methodSets and its
+// related maps, which are protected by a dedicated mutex.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/token"
+ "go/types"
+ "os"
+ "sync"
+)
+
+type opaqueType struct {
+ types.Type
+ name string
+}
+
+func (t *opaqueType) String() string { return t.name }
+
+var (
+ varOk = newVar("ok", tBool)
+ varIndex = newVar("index", tInt)
+
+ // Type constants.
+ tBool = types.Typ[types.Bool]
+ tByte = types.Typ[types.Byte]
+ tInt = types.Typ[types.Int]
+ tInvalid = types.Typ[types.Invalid]
+ tString = types.Typ[types.String]
+ tUntypedNil = types.Typ[types.UntypedNil]
+ tRangeIter = &opaqueType{nil, "iter"} // the type of all "range" iterators
+ tEface = types.NewInterfaceType(nil, nil).Complete()
+
+ // SSA Value constants.
+ vZero = intConst(0)
+ vOne = intConst(1)
+ vTrue = NewConst(constant.MakeBool(true), tBool)
+)
+
+// builder holds state associated with the package currently being built.
+// Its methods contain all the logic for AST-to-SSA conversion.
+type builder struct{}
+
+// cond emits to fn code to evaluate boolean condition e and jump
+// to t or f depending on its value, performing various simplifications.
+//
+// Postcondition: fn.currentBlock is nil.
+//
+func (b *builder) cond(fn *Function, e ast.Expr, t, f *BasicBlock) {
+ switch e := e.(type) {
+ case *ast.ParenExpr:
+ b.cond(fn, e.X, t, f)
+ return
+
+ case *ast.BinaryExpr:
+ switch e.Op {
+ case token.LAND:
+ ltrue := fn.newBasicBlock("cond.true")
+ b.cond(fn, e.X, ltrue, f)
+ fn.currentBlock = ltrue
+ b.cond(fn, e.Y, t, f)
+ return
+
+ case token.LOR:
+ lfalse := fn.newBasicBlock("cond.false")
+ b.cond(fn, e.X, t, lfalse)
+ fn.currentBlock = lfalse
+ b.cond(fn, e.Y, t, f)
+ return
+ }
+
+ case *ast.UnaryExpr:
+ if e.Op == token.NOT {
+ b.cond(fn, e.X, f, t)
+ return
+ }
+ }
+
+ // A traditional compiler would simplify "if false" (etc) here
+ // but we do not, for better fidelity to the source code.
+ //
+ // The value of a constant condition may be platform-specific,
+ // and may cause blocks that are reachable in some configuration
+ // to be hidden from subsequent analyses such as bug-finding tools.
+ emitIf(fn, b.expr(fn, e), t, f)
+}
+
+// logicalBinop emits code to fn to evaluate e, a &&- or
+// ||-expression whose reified boolean value is wanted.
+// The value is returned.
+//
+func (b *builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value {
+ rhs := fn.newBasicBlock("binop.rhs")
+ done := fn.newBasicBlock("binop.done")
+
+ // T(e) = T(e.X) = T(e.Y) after untyped constants have been
+ // eliminated.
+ // TODO(adonovan): not true; MyBool==MyBool yields UntypedBool.
+ t := fn.Pkg.typeOf(e)
+
+ var short Value // value of the short-circuit path
+ switch e.Op {
+ case token.LAND:
+ b.cond(fn, e.X, rhs, done)
+ short = NewConst(constant.MakeBool(false), t)
+
+ case token.LOR:
+ b.cond(fn, e.X, done, rhs)
+ short = NewConst(constant.MakeBool(true), t)
+ }
+
+ // Is rhs unreachable?
+ if rhs.Preds == nil {
+ // Simplify false&&y to false, true||y to true.
+ fn.currentBlock = done
+ return short
+ }
+
+ // Is done unreachable?
+ if done.Preds == nil {
+ // Simplify true&&y (or false||y) to y.
+ fn.currentBlock = rhs
+ return b.expr(fn, e.Y)
+ }
+
+ // All edges from e.X to done carry the short-circuit value.
+ var edges []Value
+ for range done.Preds {
+ edges = append(edges, short)
+ }
+
+ // The edge from e.Y to done carries the value of e.Y.
+ fn.currentBlock = rhs
+ edges = append(edges, b.expr(fn, e.Y))
+ emitJump(fn, done)
+ fn.currentBlock = done
+
+ phi := &Phi{Edges: edges, Comment: e.Op.String()}
+ phi.pos = e.OpPos
+ phi.typ = t
+ return done.emit(phi)
+}
+
+// exprN lowers a multi-result expression e to SSA form, emitting code
+// to fn and returning a single Value whose type is a *types.Tuple.
+// The caller must access the components via Extract.
+//
+// Multi-result expressions include CallExprs in a multi-value
+// assignment or return statement, and "value,ok" uses of
+// TypeAssertExpr, IndexExpr (when X is a map), and UnaryExpr (when Op
+// is token.ARROW).
+//
+func (b *builder) exprN(fn *Function, e ast.Expr) Value {
+ typ := fn.Pkg.typeOf(e).(*types.Tuple)
+ switch e := e.(type) {
+ case *ast.ParenExpr:
+ return b.exprN(fn, e.X)
+
+ case *ast.CallExpr:
+ // Currently, no built-in function nor type conversion
+ // has multiple results, so we can avoid some of the
+ // cases for single-valued CallExpr.
+ var c Call
+ b.setCall(fn, e, &c.Call)
+ c.typ = typ
+ return fn.emit(&c)
+
+ case *ast.IndexExpr:
+ mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map)
+ lookup := &Lookup{
+ X: b.expr(fn, e.X),
+ Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()),
+ CommaOk: true,
+ }
+ lookup.setType(typ)
+ lookup.setPos(e.Lbrack)
+ return fn.emit(lookup)
+
+ case *ast.TypeAssertExpr:
+ return emitTypeTest(fn, b.expr(fn, e.X), typ.At(0).Type(), e.Lparen)
+
+ case *ast.UnaryExpr: // must be receive <-
+ unop := &UnOp{
+ Op: token.ARROW,
+ X: b.expr(fn, e.X),
+ CommaOk: true,
+ }
+ unop.setType(typ)
+ unop.setPos(e.OpPos)
+ return fn.emit(unop)
+ }
+ panic(fmt.Sprintf("exprN(%T) in %s", e, fn))
+}
+
+// builtin emits to fn SSA instructions to implement a call to the
+// built-in function obj with the specified arguments
+// and return type. It returns the value defined by the result.
+//
+// The result is nil if no special handling was required; in this case
+// the caller should treat this like an ordinary library function
+// call.
+//
+func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ types.Type, pos token.Pos) Value {
+ switch obj.Name() {
+ case "make":
+ switch typ.Underlying().(type) {
+ case *types.Slice:
+ n := b.expr(fn, args[1])
+ m := n
+ if len(args) == 3 {
+ m = b.expr(fn, args[2])
+ }
+ if m, ok := m.(*Const); ok {
+ // treat make([]T, n, m) as new([m]T)[:n]
+ cap := m.Int64()
+ at := types.NewArray(typ.Underlying().(*types.Slice).Elem(), cap)
+ alloc := emitNew(fn, at, pos)
+ alloc.Comment = "makeslice"
+ v := &Slice{
+ X: alloc,
+ High: n,
+ }
+ v.setPos(pos)
+ v.setType(typ)
+ return fn.emit(v)
+ }
+ v := &MakeSlice{
+ Len: n,
+ Cap: m,
+ }
+ v.setPos(pos)
+ v.setType(typ)
+ return fn.emit(v)
+
+ case *types.Map:
+ var res Value
+ if len(args) == 2 {
+ res = b.expr(fn, args[1])
+ }
+ v := &MakeMap{Reserve: res}
+ v.setPos(pos)
+ v.setType(typ)
+ return fn.emit(v)
+
+ case *types.Chan:
+ var sz Value = vZero
+ if len(args) == 2 {
+ sz = b.expr(fn, args[1])
+ }
+ v := &MakeChan{Size: sz}
+ v.setPos(pos)
+ v.setType(typ)
+ return fn.emit(v)
+ }
+
+ case "new":
+ alloc := emitNew(fn, deref(typ), pos)
+ alloc.Comment = "new"
+ return alloc
+
+ case "len", "cap":
+ // Special case: len or cap of an array or *array is
+ // based on the type, not the value which may be nil.
+ // We must still evaluate the value, though. (If it
+ // was side-effect free, the whole call would have
+ // been constant-folded.)
+ t := deref(fn.Pkg.typeOf(args[0])).Underlying()
+ if at, ok := t.(*types.Array); ok {
+ b.expr(fn, args[0]) // for effects only
+ return intConst(at.Len())
+ }
+ // Otherwise treat as normal.
+
+ case "panic":
+ fn.emit(&Panic{
+ X: emitConv(fn, b.expr(fn, args[0]), tEface),
+ pos: pos,
+ })
+ fn.currentBlock = fn.newBasicBlock("unreachable")
+ return vTrue // any non-nil Value will do
+ }
+ return nil // treat all others as a regular function call
+}
+
+// addr lowers a single-result addressable expression e to SSA form,
+// emitting code to fn and returning the location (an lvalue) defined
+// by the expression.
+//
+// If escaping is true, addr marks the base variable of the
+// addressable expression e as being a potentially escaping pointer
+// value. For example, in this code:
+//
+// a := A{
+// b: [1]B{B{c: 1}}
+// }
+// return &a.b[0].c
+//
+// the application of & causes a.b[0].c to have its address taken,
+// which means that ultimately the local variable a must be
+// heap-allocated. This is a simple but very conservative escape
+// analysis.
+//
+// Operations forming potentially escaping pointers include:
+// - &x, including when implicit in method call or composite literals.
+// - a[:] iff a is an array (not *array)
+// - references to variables in lexically enclosing functions.
+//
+func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
+ switch e := e.(type) {
+ case *ast.Ident:
+ if isBlankIdent(e) {
+ return blank{}
+ }
+ obj := fn.Pkg.objectOf(e)
+ v := fn.Prog.packageLevelValue(obj) // var (address)
+ if v == nil {
+ v = fn.lookup(obj, escaping)
+ }
+ return &address{addr: v, pos: e.Pos(), expr: e}
+
+ case *ast.CompositeLit:
+ t := deref(fn.Pkg.typeOf(e))
+ var v *Alloc
+ if escaping {
+ v = emitNew(fn, t, e.Lbrace)
+ } else {
+ v = fn.addLocal(t, e.Lbrace)
+ }
+ v.Comment = "complit"
+ var sb storebuf
+ b.compLit(fn, v, e, true, &sb)
+ sb.emit(fn)
+ return &address{addr: v, pos: e.Lbrace, expr: e}
+
+ case *ast.ParenExpr:
+ return b.addr(fn, e.X, escaping)
+
+ case *ast.SelectorExpr:
+ sel, ok := fn.Pkg.info.Selections[e]
+ if !ok {
+ // qualified identifier
+ return b.addr(fn, e.Sel, escaping)
+ }
+ if sel.Kind() != types.FieldVal {
+ panic(sel)
+ }
+ wantAddr := true
+ v := b.receiver(fn, e.X, wantAddr, escaping, sel)
+ last := len(sel.Index()) - 1
+ return &address{
+ addr: emitFieldSelection(fn, v, sel.Index()[last], true, e.Sel),
+ pos: e.Sel.Pos(),
+ expr: e.Sel,
+ }
+
+ case *ast.IndexExpr:
+ var x Value
+ var et types.Type
+ switch t := fn.Pkg.typeOf(e.X).Underlying().(type) {
+ case *types.Array:
+ x = b.addr(fn, e.X, escaping).address(fn)
+ et = types.NewPointer(t.Elem())
+ case *types.Pointer: // *array
+ x = b.expr(fn, e.X)
+ et = types.NewPointer(t.Elem().Underlying().(*types.Array).Elem())
+ case *types.Slice:
+ x = b.expr(fn, e.X)
+ et = types.NewPointer(t.Elem())
+ case *types.Map:
+ return &element{
+ m: b.expr(fn, e.X),
+ k: emitConv(fn, b.expr(fn, e.Index), t.Key()),
+ t: t.Elem(),
+ pos: e.Lbrack,
+ }
+ default:
+ panic("unexpected container type in IndexExpr: " + t.String())
+ }
+ v := &IndexAddr{
+ X: x,
+ Index: emitConv(fn, b.expr(fn, e.Index), tInt),
+ }
+ v.setPos(e.Lbrack)
+ v.setType(et)
+ return &address{addr: fn.emit(v), pos: e.Lbrack, expr: e}
+
+ case *ast.StarExpr:
+ return &address{addr: b.expr(fn, e.X), pos: e.Star, expr: e}
+ }
+
+ panic(fmt.Sprintf("unexpected address expression: %T", e))
+}
+
+type store struct {
+ lhs lvalue
+ rhs Value
+}
+
+type storebuf struct{ stores []store }
+
+func (sb *storebuf) store(lhs lvalue, rhs Value) {
+ sb.stores = append(sb.stores, store{lhs, rhs})
+}
+
+func (sb *storebuf) emit(fn *Function) {
+ for _, s := range sb.stores {
+ s.lhs.store(fn, s.rhs)
+ }
+}
+
+// assign emits to fn code to initialize the lvalue loc with the value
+// of expression e. If isZero is true, assign assumes that loc holds
+// the zero value for its type.
+//
+// This is equivalent to loc.store(fn, b.expr(fn, e)), but may generate
+// better code in some cases, e.g., for composite literals in an
+// addressable location.
+//
+// If sb is not nil, assign generates code to evaluate expression e, but
+// not to update loc. Instead, the necessary stores are appended to the
+// storebuf sb so that they can be executed later. This allows correct
+// in-place update of existing variables when the RHS is a composite
+// literal that may reference parts of the LHS.
+//
+func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *storebuf) {
+ // Can we initialize it in place?
+ if e, ok := unparen(e).(*ast.CompositeLit); ok {
+ // A CompositeLit never evaluates to a pointer,
+ // so if the type of the location is a pointer,
+ // an &-operation is implied.
+ if _, ok := loc.(blank); !ok { // avoid calling blank.typ()
+ if isPointer(loc.typ()) {
+ ptr := b.addr(fn, e, true).address(fn)
+ // copy address
+ if sb != nil {
+ sb.store(loc, ptr)
+ } else {
+ loc.store(fn, ptr)
+ }
+ return
+ }
+ }
+
+ if _, ok := loc.(*address); ok {
+ if isInterface(loc.typ()) {
+ // e.g. var x interface{} = T{...}
+ // Can't in-place initialize an interface value.
+ // Fall back to copying.
+ } else {
+ // x = T{...} or x := T{...}
+ addr := loc.address(fn)
+ if sb != nil {
+ b.compLit(fn, addr, e, isZero, sb)
+ } else {
+ var sb storebuf
+ b.compLit(fn, addr, e, isZero, &sb)
+ sb.emit(fn)
+ }
+
+ // Subtle: emit debug ref for aggregate types only;
+ // slice and map are handled by store ops in compLit.
+ switch loc.typ().Underlying().(type) {
+ case *types.Struct, *types.Array:
+ emitDebugRef(fn, e, addr, true)
+ }
+
+ return
+ }
+ }
+ }
+
+ // simple case: just copy
+ rhs := b.expr(fn, e)
+ if sb != nil {
+ sb.store(loc, rhs)
+ } else {
+ loc.store(fn, rhs)
+ }
+}
+
+// expr lowers a single-result expression e to SSA form, emitting code
+// to fn and returning the Value defined by the expression.
+//
+func (b *builder) expr(fn *Function, e ast.Expr) Value {
+ e = unparen(e)
+
+ tv := fn.Pkg.info.Types[e]
+
+ // Is expression a constant?
+ if tv.Value != nil {
+ return NewConst(tv.Value, tv.Type)
+ }
+
+ var v Value
+ if tv.Addressable() {
+ // Prefer pointer arithmetic ({Index,Field}Addr) followed
+ // by Load over subelement extraction (e.g. Index, Field),
+ // to avoid large copies.
+ v = b.addr(fn, e, false).load(fn)
+ } else {
+ v = b.expr0(fn, e, tv)
+ }
+ if fn.debugInfo() {
+ emitDebugRef(fn, e, v, false)
+ }
+ return v
+}
+
+func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
+ switch e := e.(type) {
+ case *ast.BasicLit:
+ panic("non-constant BasicLit") // unreachable
+
+ case *ast.FuncLit:
+ fn2 := &Function{
+ name: fmt.Sprintf("%s$%d", fn.Name(), 1+len(fn.AnonFuncs)),
+ Signature: fn.Pkg.typeOf(e.Type).Underlying().(*types.Signature),
+ pos: e.Type.Func,
+ parent: fn,
+ Pkg: fn.Pkg,
+ Prog: fn.Prog,
+ syntax: e,
+ }
+ fn.AnonFuncs = append(fn.AnonFuncs, fn2)
+ b.buildFunction(fn2)
+ if fn2.FreeVars == nil {
+ return fn2
+ }
+ v := &MakeClosure{Fn: fn2}
+ v.setType(tv.Type)
+ for _, fv := range fn2.FreeVars {
+ v.Bindings = append(v.Bindings, fv.outer)
+ fv.outer = nil
+ }
+ return fn.emit(v)
+
+ case *ast.TypeAssertExpr: // single-result form only
+ return emitTypeAssert(fn, b.expr(fn, e.X), tv.Type, e.Lparen)
+
+ case *ast.CallExpr:
+ if fn.Pkg.info.Types[e.Fun].IsType() {
+ // Explicit type conversion, e.g. string(x) or big.Int(x)
+ x := b.expr(fn, e.Args[0])
+ y := emitConv(fn, x, tv.Type)
+ if y != x {
+ switch y := y.(type) {
+ case *Convert:
+ y.pos = e.Lparen
+ case *ChangeType:
+ y.pos = e.Lparen
+ case *MakeInterface:
+ y.pos = e.Lparen
+ }
+ }
+ return y
+ }
+ // Call to "intrinsic" built-ins, e.g. new, make, panic.
+ if id, ok := unparen(e.Fun).(*ast.Ident); ok {
+ if obj, ok := fn.Pkg.info.Uses[id].(*types.Builtin); ok {
+ if v := b.builtin(fn, obj, e.Args, tv.Type, e.Lparen); v != nil {
+ return v
+ }
+ }
+ }
+ // Regular function call.
+ var v Call
+ b.setCall(fn, e, &v.Call)
+ v.setType(tv.Type)
+ return fn.emit(&v)
+
+ case *ast.UnaryExpr:
+ switch e.Op {
+ case token.AND: // &X --- potentially escaping.
+ addr := b.addr(fn, e.X, true)
+ if _, ok := unparen(e.X).(*ast.StarExpr); ok {
+ // &*p must panic if p is nil (http://golang.org/s/go12nil).
+ // For simplicity, we'll just (suboptimally) rely
+ // on the side effects of a load.
+ // TODO(adonovan): emit dedicated nilcheck.
+ addr.load(fn)
+ }
+ return addr.address(fn)
+ case token.ADD:
+ return b.expr(fn, e.X)
+ case token.NOT, token.ARROW, token.SUB, token.XOR: // ! <- - ^
+ v := &UnOp{
+ Op: e.Op,
+ X: b.expr(fn, e.X),
+ }
+ v.setPos(e.OpPos)
+ v.setType(tv.Type)
+ return fn.emit(v)
+ default:
+ panic(e.Op)
+ }
+
+ case *ast.BinaryExpr:
+ switch e.Op {
+ case token.LAND, token.LOR:
+ return b.logicalBinop(fn, e)
+ case token.SHL, token.SHR:
+ fallthrough
+ case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
+ return emitArith(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), tv.Type, e.OpPos)
+
+ case token.EQL, token.NEQ, token.GTR, token.LSS, token.LEQ, token.GEQ:
+ cmp := emitCompare(fn, e.Op, b.expr(fn, e.X), b.expr(fn, e.Y), e.OpPos)
+ // The type of x==y may be UntypedBool.
+ return emitConv(fn, cmp, DefaultType(tv.Type))
+ default:
+ panic("illegal op in BinaryExpr: " + e.Op.String())
+ }
+
+ case *ast.SliceExpr:
+ var low, high, max Value
+ var x Value
+ switch fn.Pkg.typeOf(e.X).Underlying().(type) {
+ case *types.Array:
+ // Potentially escaping.
+ x = b.addr(fn, e.X, true).address(fn)
+ case *types.Basic, *types.Slice, *types.Pointer: // *array
+ x = b.expr(fn, e.X)
+ default:
+ panic("unreachable")
+ }
+ if e.High != nil {
+ high = b.expr(fn, e.High)
+ }
+ if e.Low != nil {
+ low = b.expr(fn, e.Low)
+ }
+ if e.Slice3 {
+ max = b.expr(fn, e.Max)
+ }
+ v := &Slice{
+ X: x,
+ Low: low,
+ High: high,
+ Max: max,
+ }
+ v.setPos(e.Lbrack)
+ v.setType(tv.Type)
+ return fn.emit(v)
+
+ case *ast.Ident:
+ obj := fn.Pkg.info.Uses[e]
+ // Universal built-in or nil?
+ switch obj := obj.(type) {
+ case *types.Builtin:
+ return &Builtin{name: obj.Name(), sig: tv.Type.(*types.Signature)}
+ case *types.Nil:
+ return nilConst(tv.Type)
+ }
+ // Package-level func or var?
+ if v := fn.Prog.packageLevelValue(obj); v != nil {
+ if _, ok := obj.(*types.Var); ok {
+ return emitLoad(fn, v) // var (address)
+ }
+ return v // (func)
+ }
+ // Local var.
+ return emitLoad(fn, fn.lookup(obj, false)) // var (address)
+
+ case *ast.SelectorExpr:
+ sel, ok := fn.Pkg.info.Selections[e]
+ if !ok {
+ // qualified identifier
+ return b.expr(fn, e.Sel)
+ }
+ switch sel.Kind() {
+ case types.MethodExpr:
+ // (*T).f or T.f, the method f from the method-set of type T.
+ // The result is a "thunk".
+ return emitConv(fn, makeThunk(fn.Prog, sel), tv.Type)
+
+ case types.MethodVal:
+ // e.f where e is an expression and f is a method.
+ // The result is a "bound".
+ obj := sel.Obj().(*types.Func)
+ rt := recvType(obj)
+ wantAddr := isPointer(rt)
+ escaping := true
+ v := b.receiver(fn, e.X, wantAddr, escaping, sel)
+ if isInterface(rt) {
+ // If v has interface type I,
+ // we must emit a check that v is non-nil.
+ // We use: typeassert v.(I).
+ emitTypeAssert(fn, v, rt, token.NoPos)
+ }
+ c := &MakeClosure{
+ Fn: makeBound(fn.Prog, obj),
+ Bindings: []Value{v},
+ }
+ c.setPos(e.Sel.Pos())
+ c.setType(tv.Type)
+ return fn.emit(c)
+
+ case types.FieldVal:
+ indices := sel.Index()
+ last := len(indices) - 1
+ v := b.expr(fn, e.X)
+ v = emitImplicitSelections(fn, v, indices[:last])
+ v = emitFieldSelection(fn, v, indices[last], false, e.Sel)
+ return v
+ }
+
+ panic("unexpected expression-relative selector")
+
+ case *ast.IndexExpr:
+ switch t := fn.Pkg.typeOf(e.X).Underlying().(type) {
+ case *types.Array:
+ // Non-addressable array (in a register).
+ v := &Index{
+ X: b.expr(fn, e.X),
+ Index: emitConv(fn, b.expr(fn, e.Index), tInt),
+ }
+ v.setPos(e.Lbrack)
+ v.setType(t.Elem())
+ return fn.emit(v)
+
+ case *types.Map:
+ // Maps are not addressable.
+ mapt := fn.Pkg.typeOf(e.X).Underlying().(*types.Map)
+ v := &Lookup{
+ X: b.expr(fn, e.X),
+ Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()),
+ }
+ v.setPos(e.Lbrack)
+ v.setType(mapt.Elem())
+ return fn.emit(v)
+
+ case *types.Basic: // => string
+ // Strings are not addressable.
+ v := &Lookup{
+ X: b.expr(fn, e.X),
+ Index: b.expr(fn, e.Index),
+ }
+ v.setPos(e.Lbrack)
+ v.setType(tByte)
+ return fn.emit(v)
+
+ case *types.Slice, *types.Pointer: // *array
+ // Addressable slice/array; use IndexAddr and Load.
+ return b.addr(fn, e, false).load(fn)
+
+ default:
+ panic("unexpected container type in IndexExpr: " + t.String())
+ }
+
+ case *ast.CompositeLit, *ast.StarExpr:
+ // Addressable types (lvalues)
+ return b.addr(fn, e, false).load(fn)
+ }
+
+ panic(fmt.Sprintf("unexpected expr: %T", e))
+}
+
+// stmtList emits to fn code for all statements in list.
+func (b *builder) stmtList(fn *Function, list []ast.Stmt) {
+ for _, s := range list {
+ b.stmt(fn, s)
+ }
+}
+
+// receiver emits to fn code for expression e in the "receiver"
+// position of selection e.f (where f may be a field or a method) and
+// returns the effective receiver after applying the implicit field
+// selections of sel.
+//
+// wantAddr requests that the result is an an address. If
+// !sel.Indirect(), this may require that e be built in addr() mode; it
+// must thus be addressable.
+//
+// escaping is defined as per builder.addr().
+//
+func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, sel *types.Selection) Value {
+ var v Value
+ if wantAddr && !sel.Indirect() && !isPointer(fn.Pkg.typeOf(e)) {
+ v = b.addr(fn, e, escaping).address(fn)
+ } else {
+ v = b.expr(fn, e)
+ }
+
+ last := len(sel.Index()) - 1
+ v = emitImplicitSelections(fn, v, sel.Index()[:last])
+ if !wantAddr && isPointer(v.Type()) {
+ v = emitLoad(fn, v)
+ }
+ return v
+}
+
+// setCallFunc populates the function parts of a CallCommon structure
+// (Func, Method, Recv, Args[0]) based on the kind of invocation
+// occurring in e.
+//
+func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
+ c.pos = e.Lparen
+
+ // Is this a method call?
+ if selector, ok := unparen(e.Fun).(*ast.SelectorExpr); ok {
+ sel, ok := fn.Pkg.info.Selections[selector]
+ if ok && sel.Kind() == types.MethodVal {
+ obj := sel.Obj().(*types.Func)
+ recv := recvType(obj)
+ wantAddr := isPointer(recv)
+ escaping := true
+ v := b.receiver(fn, selector.X, wantAddr, escaping, sel)
+ if isInterface(recv) {
+ // Invoke-mode call.
+ c.Value = v
+ c.Method = obj
+ } else {
+ // "Call"-mode call.
+ c.Value = fn.Prog.declaredFunc(obj)
+ c.Args = append(c.Args, v)
+ }
+ return
+ }
+
+ // sel.Kind()==MethodExpr indicates T.f() or (*T).f():
+ // a statically dispatched call to the method f in the
+ // method-set of T or *T. T may be an interface.
+ //
+ // e.Fun would evaluate to a concrete method, interface
+ // wrapper function, or promotion wrapper.
+ //
+ // For now, we evaluate it in the usual way.
+ //
+ // TODO(adonovan): opt: inline expr() here, to make the
+ // call static and to avoid generation of wrappers.
+ // It's somewhat tricky as it may consume the first
+ // actual parameter if the call is "invoke" mode.
+ //
+ // Examples:
+ // type T struct{}; func (T) f() {} // "call" mode
+ // type T interface { f() } // "invoke" mode
+ //
+ // type S struct{ T }
+ //
+ // var s S
+ // S.f(s)
+ // (*S).f(&s)
+ //
+ // Suggested approach:
+ // - consume the first actual parameter expression
+ // and build it with b.expr().
+ // - apply implicit field selections.
+ // - use MethodVal logic to populate fields of c.
+ }
+
+ // Evaluate the function operand in the usual way.
+ c.Value = b.expr(fn, e.Fun)
+}
+
+// emitCallArgs emits to f code for the actual parameters of call e to
+// a (possibly built-in) function of effective type sig.
+// The argument values are appended to args, which is then returned.
+//
+func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallExpr, args []Value) []Value {
+ // f(x, y, z...): pass slice z straight through.
+ if e.Ellipsis != 0 {
+ for i, arg := range e.Args {
+ v := emitConv(fn, b.expr(fn, arg), sig.Params().At(i).Type())
+ args = append(args, v)
+ }
+ return args
+ }
+
+ offset := len(args) // 1 if call has receiver, 0 otherwise
+
+ // Evaluate actual parameter expressions.
+ //
+ // If this is a chained call of the form f(g()) where g has
+ // multiple return values (MRV), they are flattened out into
+ // args; a suffix of them may end up in a varargs slice.
+ for _, arg := range e.Args {
+ v := b.expr(fn, arg)
+ if ttuple, ok := v.Type().(*types.Tuple); ok { // MRV chain
+ for i, n := 0, ttuple.Len(); i < n; i++ {
+ args = append(args, emitExtract(fn, v, i))
+ }
+ } else {
+ args = append(args, v)
+ }
+ }
+
+ // Actual->formal assignability conversions for normal parameters.
+ np := sig.Params().Len() // number of normal parameters
+ if sig.Variadic() {
+ np--
+ }
+ for i := 0; i < np; i++ {
+ args[offset+i] = emitConv(fn, args[offset+i], sig.Params().At(i).Type())
+ }
+
+ // Actual->formal assignability conversions for variadic parameter,
+ // and construction of slice.
+ if sig.Variadic() {
+ varargs := args[offset+np:]
+ st := sig.Params().At(np).Type().(*types.Slice)
+ vt := st.Elem()
+ if len(varargs) == 0 {
+ args = append(args, nilConst(st))
+ } else {
+ // Replace a suffix of args with a slice containing it.
+ at := types.NewArray(vt, int64(len(varargs)))
+ a := emitNew(fn, at, token.NoPos)
+ a.setPos(e.Rparen)
+ a.Comment = "varargs"
+ for i, arg := range varargs {
+ iaddr := &IndexAddr{
+ X: a,
+ Index: intConst(int64(i)),
+ }
+ iaddr.setType(types.NewPointer(vt))
+ fn.emit(iaddr)
+ emitStore(fn, iaddr, arg, arg.Pos())
+ }
+ s := &Slice{X: a}
+ s.setType(st)
+ args[offset+np] = fn.emit(s)
+ args = args[:offset+np+1]
+ }
+ }
+ return args
+}
+
+// setCall emits to fn code to evaluate all the parameters of a function
+// call e, and populates *c with those values.
+//
+func (b *builder) setCall(fn *Function, e *ast.CallExpr, c *CallCommon) {
+ // First deal with the f(...) part and optional receiver.
+ b.setCallFunc(fn, e, c)
+
+ // Then append the other actual parameters.
+ sig, _ := fn.Pkg.typeOf(e.Fun).Underlying().(*types.Signature)
+ if sig == nil {
+ panic(fmt.Sprintf("no signature for call of %s", e.Fun))
+ }
+ c.Args = b.emitCallArgs(fn, sig, e, c.Args)
+}
+
+// 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, val, oldv.Type()), loc.typ(), pos))
+}
+
+// localValueSpec emits to fn code to define all of the vars in the
+// function-local ValueSpec, spec.
+//
+func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) {
+ switch {
+ case len(spec.Values) == len(spec.Names):
+ // e.g. var x, y = 0, 1
+ // 1:1 assignment
+ for i, id := range spec.Names {
+ if !isBlankIdent(id) {
+ fn.addLocalForIdent(id)
+ }
+ lval := b.addr(fn, id, false) // non-escaping
+ b.assign(fn, lval, spec.Values[i], true, nil)
+ }
+
+ case len(spec.Values) == 0:
+ // e.g. var x, y int
+ // Locals are implicitly zero-initialized.
+ for _, id := range spec.Names {
+ if !isBlankIdent(id) {
+ lhs := fn.addLocalForIdent(id)
+ if fn.debugInfo() {
+ emitDebugRef(fn, id, lhs, true)
+ }
+ }
+ }
+
+ default:
+ // e.g. var x, y = pos()
+ tuple := b.exprN(fn, spec.Values[0])
+ for i, id := range spec.Names {
+ if !isBlankIdent(id) {
+ fn.addLocalForIdent(id)
+ lhs := b.addr(fn, id, false) // non-escaping
+ lhs.store(fn, emitExtract(fn, tuple, i))
+ }
+ }
+ }
+}
+
+// assignStmt emits code to fn for a parallel assignment of rhss to lhss.
+// isDef is true if this is a short variable declaration (:=).
+//
+// Note the similarity with localValueSpec.
+//
+func (b *builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool) {
+ // Side effects of all LHSs and RHSs must occur in left-to-right order.
+ lvals := make([]lvalue, len(lhss))
+ isZero := make([]bool, len(lhss))
+ for i, lhs := range lhss {
+ var lval lvalue = blank{}
+ if !isBlankIdent(lhs) {
+ if isDef {
+ if obj := fn.Pkg.info.Defs[lhs.(*ast.Ident)]; obj != nil {
+ fn.addNamedLocal(obj)
+ isZero[i] = true
+ }
+ }
+ lval = b.addr(fn, lhs, false) // non-escaping
+ }
+ lvals[i] = lval
+ }
+ if len(lhss) == len(rhss) {
+ // Simple assignment: x = f() (!isDef)
+ // Parallel assignment: x, y = f(), g() (!isDef)
+ // or short var decl: x, y := f(), g() (isDef)
+ //
+ // In all cases, the RHSs may refer to the LHSs,
+ // so we need a storebuf.
+ var sb storebuf
+ for i := range rhss {
+ b.assign(fn, lvals[i], rhss[i], isZero[i], &sb)
+ }
+ sb.emit(fn)
+ } else {
+ // e.g. x, y = pos()
+ tuple := b.exprN(fn, rhss[0])
+ emitDebugRef(fn, rhss[0], tuple, false)
+ for i, lval := range lvals {
+ lval.store(fn, emitExtract(fn, tuple, i))
+ }
+ }
+}
+
+// arrayLen returns the length of the array whose composite literal elements are elts.
+func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 {
+ var max int64 = -1
+ var i int64 = -1
+ for _, e := range elts {
+ if kv, ok := e.(*ast.KeyValueExpr); ok {
+ i = b.expr(fn, kv.Key).(*Const).Int64()
+ } else {
+ i++
+ }
+ if i > max {
+ max = i
+ }
+ }
+ return max + 1
+}
+
+// compLit emits to fn code to initialize a composite literal e at
+// address addr with type typ.
+//
+// Nested composite literals are recursively initialized in place
+// where possible. If isZero is true, compLit assumes that addr
+// holds the zero value for typ.
+//
+// Because the elements of a composite literal may refer to the
+// variables being updated, as in the second line below,
+// x := T{a: 1}
+// x = T{a: x.a}
+// all the reads must occur before all the writes. Thus all stores to
+// loc are emitted to the storebuf sb for later execution.
+//
+// A CompositeLit may have pointer type only in the recursive (nested)
+// case when the type name is implicit. e.g. in []*T{{}}, the inner
+// literal has type *T behaves like &T{}.
+// In that case, addr must hold a T, not a *T.
+//
+func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero bool, sb *storebuf) {
+ typ := deref(fn.Pkg.typeOf(e))
+ switch t := typ.Underlying().(type) {
+ case *types.Struct:
+ if !isZero && len(e.Elts) != t.NumFields() {
+ // memclear
+ sb.store(&address{addr, e.Lbrace, nil},
+ zeroValue(fn, deref(addr.Type())))
+ isZero = true
+ }
+ for i, e := range e.Elts {
+ fieldIndex := i
+ pos := e.Pos()
+ if kv, ok := e.(*ast.KeyValueExpr); ok {
+ fname := kv.Key.(*ast.Ident).Name
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ sf := t.Field(i)
+ if sf.Name() == fname {
+ fieldIndex = i
+ pos = kv.Colon
+ e = kv.Value
+ break
+ }
+ }
+ }
+ sf := t.Field(fieldIndex)
+ faddr := &FieldAddr{
+ X: addr,
+ Field: fieldIndex,
+ }
+ faddr.setType(types.NewPointer(sf.Type()))
+ fn.emit(faddr)
+ b.assign(fn, &address{addr: faddr, pos: pos, expr: e}, e, isZero, sb)
+ }
+
+ case *types.Array, *types.Slice:
+ var at *types.Array
+ var array Value
+ switch t := t.(type) {
+ case *types.Slice:
+ at = types.NewArray(t.Elem(), b.arrayLen(fn, e.Elts))
+ alloc := emitNew(fn, at, e.Lbrace)
+ alloc.Comment = "slicelit"
+ array = alloc
+ case *types.Array:
+ at = t
+ array = addr
+
+ if !isZero && int64(len(e.Elts)) != at.Len() {
+ // memclear
+ sb.store(&address{array, e.Lbrace, nil},
+ zeroValue(fn, deref(array.Type())))
+ }
+ }
+
+ var idx *Const
+ for _, e := range e.Elts {
+ pos := e.Pos()
+ if kv, ok := e.(*ast.KeyValueExpr); ok {
+ idx = b.expr(fn, kv.Key).(*Const)
+ pos = kv.Colon
+ e = kv.Value
+ } else {
+ var idxval int64
+ if idx != nil {
+ idxval = idx.Int64() + 1
+ }
+ idx = intConst(idxval)
+ }
+ iaddr := &IndexAddr{
+ X: array,
+ Index: idx,
+ }
+ iaddr.setType(types.NewPointer(at.Elem()))
+ fn.emit(iaddr)
+ if t != at { // slice
+ // backing array is unaliased => storebuf not needed.
+ b.assign(fn, &address{addr: iaddr, pos: pos, expr: e}, e, true, nil)
+ } else {
+ b.assign(fn, &address{addr: iaddr, pos: pos, expr: e}, e, true, sb)
+ }
+ }
+
+ if t != at { // slice
+ s := &Slice{X: array}
+ s.setPos(e.Lbrace)
+ s.setType(typ)
+ sb.store(&address{addr: addr, pos: e.Lbrace, expr: e}, fn.emit(s))
+ }
+
+ case *types.Map:
+ m := &MakeMap{Reserve: intConst(int64(len(e.Elts)))}
+ m.setPos(e.Lbrace)
+ m.setType(typ)
+ fn.emit(m)
+ for _, e := range e.Elts {
+ e := e.(*ast.KeyValueExpr)
+
+ // If a key expression in a map literal is itself a
+ // composite literal, the type may be omitted.
+ // For example:
+ // map[*struct{}]bool{{}: true}
+ // An &-operation may be implied:
+ // map[*struct{}]bool{&struct{}{}: true}
+ var key Value
+ if _, ok := unparen(e.Key).(*ast.CompositeLit); ok && isPointer(t.Key()) {
+ // A CompositeLit never evaluates to a pointer,
+ // so if the type of the location is a pointer,
+ // an &-operation is implied.
+ key = b.addr(fn, e.Key, true).address(fn)
+ } else {
+ key = b.expr(fn, e.Key)
+ }
+
+ loc := element{
+ m: m,
+ k: emitConv(fn, key, t.Key()),
+ t: t.Elem(),
+ pos: e.Colon,
+ }
+
+ // We call assign() only because it takes care
+ // of any &-operation required in the recursive
+ // case, e.g.,
+ // map[int]*struct{}{0: {}} implies &struct{}{}.
+ // In-place update is of course impossible,
+ // and no storebuf is needed.
+ b.assign(fn, &loc, e.Value, true, nil)
+ }
+ sb.store(&address{addr: addr, pos: e.Lbrace, expr: e}, m)
+
+ default:
+ panic("unexpected CompositeLit type: " + t.String())
+ }
+}
+
+// switchStmt emits to fn code for the switch statement s, optionally
+// labelled by label.
+//
+func (b *builder) switchStmt(fn *Function, s *ast.SwitchStmt, label *lblock) {
+ // We treat SwitchStmt like a sequential if-else chain.
+ // Multiway dispatch can be recovered later by ssautil.Switches()
+ // to those cases that are free of side effects.
+ if s.Init != nil {
+ b.stmt(fn, s.Init)
+ }
+ var tag Value = vTrue
+ if s.Tag != nil {
+ tag = b.expr(fn, s.Tag)
+ }
+ done := fn.newBasicBlock("switch.done")
+ if label != nil {
+ label._break = done
+ }
+ // We pull the default case (if present) down to the end.
+ // But each fallthrough label must point to the next
+ // body block in source order, so we preallocate a
+ // body block (fallthru) for the next case.
+ // Unfortunately this makes for a confusing block order.
+ var dfltBody *[]ast.Stmt
+ var dfltFallthrough *BasicBlock
+ var fallthru, dfltBlock *BasicBlock
+ ncases := len(s.Body.List)
+ for i, clause := range s.Body.List {
+ body := fallthru
+ if body == nil {
+ body = fn.newBasicBlock("switch.body") // first case only
+ }
+
+ // Preallocate body block for the next case.
+ fallthru = done
+ if i+1 < ncases {
+ fallthru = fn.newBasicBlock("switch.body")
+ }
+
+ cc := clause.(*ast.CaseClause)
+ if cc.List == nil {
+ // Default case.
+ dfltBody = &cc.Body
+ dfltFallthrough = fallthru
+ dfltBlock = body
+ continue
+ }
+
+ var nextCond *BasicBlock
+ for _, cond := range cc.List {
+ nextCond = fn.newBasicBlock("switch.next")
+ // TODO(adonovan): opt: when tag==vTrue, we'd
+ // get better code if we use b.cond(cond)
+ // instead of BinOp(EQL, tag, b.expr(cond))
+ // followed by If. Don't forget conversions
+ // though.
+ cond := emitCompare(fn, token.EQL, tag, b.expr(fn, cond), cond.Pos())
+ emitIf(fn, cond, body, nextCond)
+ fn.currentBlock = nextCond
+ }
+ fn.currentBlock = body
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ _fallthrough: fallthru,
+ }
+ b.stmtList(fn, cc.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, done)
+ fn.currentBlock = nextCond
+ }
+ if dfltBlock != nil {
+ emitJump(fn, dfltBlock)
+ fn.currentBlock = dfltBlock
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ _fallthrough: dfltFallthrough,
+ }
+ b.stmtList(fn, *dfltBody)
+ fn.targets = fn.targets.tail
+ }
+ emitJump(fn, done)
+ fn.currentBlock = done
+}
+
+// typeSwitchStmt emits to fn code for the type switch statement s, optionally
+// labelled by label.
+//
+func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lblock) {
+ // We treat TypeSwitchStmt like a sequential if-else chain.
+ // Multiway dispatch can be recovered later by ssautil.Switches().
+
+ // Typeswitch lowering:
+ //
+ // var x X
+ // switch y := x.(type) {
+ // case T1, T2: S1 // >1 (y := x)
+ // case nil: SN // nil (y := x)
+ // default: SD // 0 types (y := x)
+ // case T3: S3 // 1 type (y := x.(T3))
+ // }
+ //
+ // ...s.Init...
+ // x := eval x
+ // .caseT1:
+ // t1, ok1 := typeswitch,ok x
+ // if ok1 then goto S1 else goto .caseT2
+ // .caseT2:
+ // t2, ok2 := typeswitch,ok x
+ // if ok2 then goto S1 else goto .caseNil
+ // .S1:
+ // y := x
+ // ...S1...
+ // goto done
+ // .caseNil:
+ // if t2, ok2 := typeswitch,ok x
+ // if x == nil then goto SN else goto .caseT3
+ // .SN:
+ // y := x
+ // ...SN...
+ // goto done
+ // .caseT3:
+ // t3, ok3 := typeswitch,ok x
+ // if ok3 then goto S3 else goto default
+ // .S3:
+ // y := t3
+ // ...S3...
+ // goto done
+ // .default:
+ // y := x
+ // ...SD...
+ // goto done
+ // .done:
+
+ if s.Init != nil {
+ b.stmt(fn, s.Init)
+ }
+
+ var x Value
+ switch ass := s.Assign.(type) {
+ case *ast.ExprStmt: // x.(type)
+ x = b.expr(fn, unparen(ass.X).(*ast.TypeAssertExpr).X)
+ case *ast.AssignStmt: // y := x.(type)
+ x = b.expr(fn, unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
+ }
+
+ done := fn.newBasicBlock("typeswitch.done")
+ if label != nil {
+ label._break = done
+ }
+ var default_ *ast.CaseClause
+ for _, clause := range s.Body.List {
+ cc := clause.(*ast.CaseClause)
+ if cc.List == nil {
+ default_ = cc
+ continue
+ }
+ body := fn.newBasicBlock("typeswitch.body")
+ var next *BasicBlock
+ var casetype types.Type
+ var ti Value // ti, ok := typeassert,ok x
+ for _, cond := range cc.List {
+ next = fn.newBasicBlock("typeswitch.next")
+ casetype = fn.Pkg.typeOf(cond)
+ var condv Value
+ if casetype == tUntypedNil {
+ condv = emitCompare(fn, token.EQL, x, nilConst(x.Type()), token.NoPos)
+ ti = x
+ } else {
+ yok := emitTypeTest(fn, x, casetype, cc.Case)
+ ti = emitExtract(fn, yok, 0)
+ condv = emitExtract(fn, yok, 1)
+ }
+ emitIf(fn, condv, body, next)
+ fn.currentBlock = next
+ }
+ if len(cc.List) != 1 {
+ ti = x
+ }
+ fn.currentBlock = body
+ b.typeCaseBody(fn, cc, ti, done)
+ fn.currentBlock = next
+ }
+ if default_ != nil {
+ b.typeCaseBody(fn, default_, x, done)
+ } else {
+ emitJump(fn, done)
+ }
+ fn.currentBlock = done
+}
+
+func (b *builder) typeCaseBody(fn *Function, cc *ast.CaseClause, x Value, done *BasicBlock) {
+ if obj := fn.Pkg.info.Implicits[cc]; obj != nil {
+ // In a switch y := x.(type), each case clause
+ // implicitly declares a distinct object y.
+ // In a single-type case, y has that type.
+ // In multi-type cases, 'case nil' and default,
+ // y has the same type as the interface operand.
+ emitStore(fn, fn.addNamedLocal(obj), x, obj.Pos())
+ }
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ }
+ b.stmtList(fn, cc.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, done)
+}
+
+// selectStmt emits to fn code for the select statement s, optionally
+// labelled by label.
+//
+func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) {
+ // A blocking select of a single case degenerates to a
+ // simple send or receive.
+ // TODO(adonovan): opt: is this optimization worth its weight?
+ if len(s.Body.List) == 1 {
+ clause := s.Body.List[0].(*ast.CommClause)
+ if clause.Comm != nil {
+ b.stmt(fn, clause.Comm)
+ done := fn.newBasicBlock("select.done")
+ if label != nil {
+ label._break = done
+ }
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ }
+ b.stmtList(fn, clause.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, done)
+ fn.currentBlock = done
+ return
+ }
+ }
+
+ // First evaluate all channels in all cases, and find
+ // the directions of each state.
+ var states []*SelectState
+ blocking := true
+ debugInfo := fn.debugInfo()
+ for _, clause := range s.Body.List {
+ var st *SelectState
+ switch comm := clause.(*ast.CommClause).Comm.(type) {
+ case nil: // default case
+ blocking = false
+ continue
+
+ case *ast.SendStmt: // ch<- i
+ ch := b.expr(fn, comm.Chan)
+ st = &SelectState{
+ Dir: types.SendOnly,
+ Chan: ch,
+ Send: emitConv(fn, b.expr(fn, comm.Value),
+ ch.Type().Underlying().(*types.Chan).Elem()),
+ Pos: comm.Arrow,
+ }
+ if debugInfo {
+ st.DebugNode = comm
+ }
+
+ case *ast.AssignStmt: // x := <-ch
+ recv := unparen(comm.Rhs[0]).(*ast.UnaryExpr)
+ st = &SelectState{
+ Dir: types.RecvOnly,
+ Chan: b.expr(fn, recv.X),
+ Pos: recv.OpPos,
+ }
+ if debugInfo {
+ st.DebugNode = recv
+ }
+
+ case *ast.ExprStmt: // <-ch
+ recv := unparen(comm.X).(*ast.UnaryExpr)
+ st = &SelectState{
+ Dir: types.RecvOnly,
+ Chan: b.expr(fn, recv.X),
+ Pos: recv.OpPos,
+ }
+ if debugInfo {
+ st.DebugNode = recv
+ }
+ }
+ states = append(states, st)
+ }
+
+ // We dispatch on the (fair) result of Select using a
+ // sequential if-else chain, in effect:
+ //
+ // idx, recvOk, r0...r_n-1 := select(...)
+ // if idx == 0 { // receive on channel 0 (first receive => r0)
+ // x, ok := r0, recvOk
+ // ...state0...
+ // } else if v == 1 { // send on channel 1
+ // ...state1...
+ // } else {
+ // ...default...
+ // }
+ sel := &Select{
+ States: states,
+ Blocking: blocking,
+ }
+ sel.setPos(s.Select)
+ var vars []*types.Var
+ vars = append(vars, varIndex, varOk)
+ for _, st := range states {
+ if st.Dir == types.RecvOnly {
+ tElem := st.Chan.Type().Underlying().(*types.Chan).Elem()
+ vars = append(vars, anonVar(tElem))
+ }
+ }
+ sel.setType(types.NewTuple(vars...))
+
+ fn.emit(sel)
+ idx := emitExtract(fn, sel, 0)
+
+ done := fn.newBasicBlock("select.done")
+ if label != nil {
+ label._break = done
+ }
+
+ var defaultBody *[]ast.Stmt
+ state := 0
+ r := 2 // index in 'sel' tuple of value; increments if st.Dir==RECV
+ for _, cc := range s.Body.List {
+ clause := cc.(*ast.CommClause)
+ if clause.Comm == nil {
+ defaultBody = &clause.Body
+ continue
+ }
+ body := fn.newBasicBlock("select.body")
+ next := fn.newBasicBlock("select.next")
+ emitIf(fn, emitCompare(fn, token.EQL, idx, intConst(int64(state)), token.NoPos), body, next)
+ fn.currentBlock = body
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ }
+ switch comm := clause.Comm.(type) {
+ case *ast.ExprStmt: // <-ch
+ if debugInfo {
+ v := emitExtract(fn, sel, r)
+ emitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false)
+ }
+ r++
+
+ case *ast.AssignStmt: // x := <-states[state].Chan
+ if comm.Tok == token.DEFINE {
+ fn.addLocalForIdent(comm.Lhs[0].(*ast.Ident))
+ }
+ x := b.addr(fn, comm.Lhs[0], false) // non-escaping
+ v := emitExtract(fn, sel, r)
+ if debugInfo {
+ emitDebugRef(fn, states[state].DebugNode.(ast.Expr), v, false)
+ }
+ x.store(fn, v)
+
+ if len(comm.Lhs) == 2 { // x, ok := ...
+ if comm.Tok == token.DEFINE {
+ fn.addLocalForIdent(comm.Lhs[1].(*ast.Ident))
+ }
+ ok := b.addr(fn, comm.Lhs[1], false) // non-escaping
+ ok.store(fn, emitExtract(fn, sel, 1))
+ }
+ r++
+ }
+ b.stmtList(fn, clause.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, done)
+ fn.currentBlock = next
+ state++
+ }
+ if defaultBody != nil {
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ }
+ b.stmtList(fn, *defaultBody)
+ fn.targets = fn.targets.tail
+ } else {
+ // A blocking select must match some case.
+ // (This should really be a runtime.errorString, not a string.)
+ fn.emit(&Panic{
+ X: emitConv(fn, stringConst("blocking select matched no case"), tEface),
+ })
+ fn.currentBlock = fn.newBasicBlock("unreachable")
+ }
+ emitJump(fn, done)
+ fn.currentBlock = done
+}
+
+// forStmt emits to fn code for the for statement s, optionally
+// labelled by label.
+//
+func (b *builder) forStmt(fn *Function, s *ast.ForStmt, label *lblock) {
+ // ...init...
+ // jump loop
+ // loop:
+ // if cond goto body else done
+ // body:
+ // ...body...
+ // jump post
+ // post: (target of continue)
+ // ...post...
+ // jump loop
+ // done: (target of break)
+ if s.Init != nil {
+ b.stmt(fn, s.Init)
+ }
+ body := fn.newBasicBlock("for.body")
+ done := fn.newBasicBlock("for.done") // target of 'break'
+ loop := body // target of back-edge
+ if s.Cond != nil {
+ loop = fn.newBasicBlock("for.loop")
+ }
+ cont := loop // target of 'continue'
+ if s.Post != nil {
+ cont = fn.newBasicBlock("for.post")
+ }
+ if label != nil {
+ label._break = done
+ label._continue = cont
+ }
+ emitJump(fn, loop)
+ fn.currentBlock = loop
+ if loop != body {
+ b.cond(fn, s.Cond, body, done)
+ fn.currentBlock = body
+ }
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ _continue: cont,
+ }
+ b.stmt(fn, s.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, cont)
+
+ if s.Post != nil {
+ fn.currentBlock = cont
+ b.stmt(fn, s.Post)
+ emitJump(fn, loop) // back-edge
+ }
+ fn.currentBlock = done
+}
+
+// rangeIndexed emits to fn the header for an integer-indexed loop
+// over array, *array or slice value x.
+// The v result is defined only if tv is non-nil.
+// forPos is the position of the "for" token.
+//
+func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, pos token.Pos) (k, v Value, loop, done *BasicBlock) {
+ //
+ // length = len(x)
+ // index = -1
+ // loop: (target of continue)
+ // index++
+ // if index < length goto body else done
+ // body:
+ // k = index
+ // v = x[index]
+ // ...body...
+ // jump loop
+ // done: (target of break)
+
+ // Determine number of iterations.
+ var length Value
+ if arr, ok := deref(x.Type()).Underlying().(*types.Array); ok {
+ // For array or *array, the number of iterations is
+ // known statically thanks to the type. We avoid a
+ // data dependence upon x, permitting later dead-code
+ // elimination if x is pure, static unrolling, etc.
+ // Ranging over a nil *array may have >0 iterations.
+ // We still generate code for x, in case it has effects.
+ length = intConst(arr.Len())
+ } else {
+ // length = len(x).
+ var c Call
+ c.Call.Value = makeLen(x.Type())
+ c.Call.Args = []Value{x}
+ c.setType(tInt)
+ length = fn.emit(&c)
+ }
+
+ index := fn.addLocal(tInt, token.NoPos)
+ emitStore(fn, index, intConst(-1), pos)
+
+ loop = fn.newBasicBlock("rangeindex.loop")
+ emitJump(fn, loop)
+ fn.currentBlock = loop
+
+ incr := &BinOp{
+ Op: token.ADD,
+ X: emitLoad(fn, index),
+ Y: vOne,
+ }
+ incr.setType(tInt)
+ emitStore(fn, index, fn.emit(incr), pos)
+
+ body := fn.newBasicBlock("rangeindex.body")
+ done = fn.newBasicBlock("rangeindex.done")
+ emitIf(fn, emitCompare(fn, token.LSS, incr, length, token.NoPos), body, done)
+ fn.currentBlock = body
+
+ k = emitLoad(fn, index)
+ if tv != nil {
+ switch t := x.Type().Underlying().(type) {
+ case *types.Array:
+ instr := &Index{
+ X: x,
+ Index: k,
+ }
+ instr.setType(t.Elem())
+ v = fn.emit(instr)
+
+ case *types.Pointer: // *array
+ instr := &IndexAddr{
+ X: x,
+ Index: k,
+ }
+ instr.setType(types.NewPointer(t.Elem().Underlying().(*types.Array).Elem()))
+ v = emitLoad(fn, fn.emit(instr))
+
+ case *types.Slice:
+ instr := &IndexAddr{
+ X: x,
+ Index: k,
+ }
+ instr.setType(types.NewPointer(t.Elem()))
+ v = emitLoad(fn, fn.emit(instr))
+
+ default:
+ panic("rangeIndexed x:" + t.String())
+ }
+ }
+ return
+}
+
+// rangeIter emits to fn the header for a loop using
+// Range/Next/Extract to iterate over map or string value x.
+// tk and tv are the types of the key/value results k and v, or nil
+// if the respective component is not wanted.
+//
+func (b *builder) rangeIter(fn *Function, x Value, tk, tv types.Type, pos token.Pos) (k, v Value, loop, done *BasicBlock) {
+ //
+ // it = range x
+ // loop: (target of continue)
+ // okv = next it (ok, key, value)
+ // ok = extract okv #0
+ // if ok goto body else done
+ // body:
+ // k = extract okv #1
+ // v = extract okv #2
+ // ...body...
+ // jump loop
+ // done: (target of break)
+ //
+
+ if tk == nil {
+ tk = tInvalid
+ }
+ if tv == nil {
+ tv = tInvalid
+ }
+
+ rng := &Range{X: x}
+ rng.setPos(pos)
+ rng.setType(tRangeIter)
+ it := fn.emit(rng)
+
+ loop = fn.newBasicBlock("rangeiter.loop")
+ emitJump(fn, loop)
+ fn.currentBlock = loop
+
+ _, isString := x.Type().Underlying().(*types.Basic)
+
+ okv := &Next{
+ Iter: it,
+ IsString: isString,
+ }
+ okv.setType(types.NewTuple(
+ varOk,
+ newVar("k", tk),
+ newVar("v", tv),
+ ))
+ fn.emit(okv)
+
+ body := fn.newBasicBlock("rangeiter.body")
+ done = fn.newBasicBlock("rangeiter.done")
+ emitIf(fn, emitExtract(fn, okv, 0), body, done)
+ fn.currentBlock = body
+
+ if tk != tInvalid {
+ k = emitExtract(fn, okv, 1)
+ }
+ if tv != tInvalid {
+ v = emitExtract(fn, okv, 2)
+ }
+ return
+}
+
+// rangeChan emits to fn the header for a loop that receives from
+// channel x until it fails.
+// tk is the channel's element type, or nil if the k result is
+// not wanted
+// pos is the position of the '=' or ':=' token.
+//
+func (b *builder) rangeChan(fn *Function, x Value, tk types.Type, pos token.Pos) (k Value, loop, done *BasicBlock) {
+ //
+ // loop: (target of continue)
+ // ko = <-x (key, ok)
+ // ok = extract ko #1
+ // if ok goto body else done
+ // body:
+ // k = extract ko #0
+ // ...
+ // goto loop
+ // done: (target of break)
+
+ loop = fn.newBasicBlock("rangechan.loop")
+ emitJump(fn, loop)
+ fn.currentBlock = loop
+ recv := &UnOp{
+ Op: token.ARROW,
+ X: x,
+ CommaOk: true,
+ }
+ recv.setPos(pos)
+ recv.setType(types.NewTuple(
+ newVar("k", x.Type().Underlying().(*types.Chan).Elem()),
+ varOk,
+ ))
+ ko := fn.emit(recv)
+ body := fn.newBasicBlock("rangechan.body")
+ done = fn.newBasicBlock("rangechan.done")
+ emitIf(fn, emitExtract(fn, ko, 1), body, done)
+ fn.currentBlock = body
+ if tk != nil {
+ k = emitExtract(fn, ko, 0)
+ }
+ return
+}
+
+// rangeStmt emits to fn code for the range statement s, optionally
+// labelled by label.
+//
+func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock) {
+ var tk, tv types.Type
+ if s.Key != nil && !isBlankIdent(s.Key) {
+ tk = fn.Pkg.typeOf(s.Key)
+ }
+ if s.Value != nil && !isBlankIdent(s.Value) {
+ tv = fn.Pkg.typeOf(s.Value)
+ }
+
+ // If iteration variables are defined (:=), this
+ // occurs once outside the loop.
+ //
+ // Unlike a short variable declaration, a RangeStmt
+ // using := never redeclares an existing variable; it
+ // always creates a new one.
+ if s.Tok == token.DEFINE {
+ if tk != nil {
+ fn.addLocalForIdent(s.Key.(*ast.Ident))
+ }
+ if tv != nil {
+ fn.addLocalForIdent(s.Value.(*ast.Ident))
+ }
+ }
+
+ x := b.expr(fn, s.X)
+
+ var k, v Value
+ var loop, done *BasicBlock
+ switch rt := x.Type().Underlying().(type) {
+ case *types.Slice, *types.Array, *types.Pointer: // *array
+ k, v, loop, done = b.rangeIndexed(fn, x, tv, s.For)
+
+ case *types.Chan:
+ k, loop, done = b.rangeChan(fn, x, tk, s.For)
+
+ case *types.Map, *types.Basic: // string
+ k, v, loop, done = b.rangeIter(fn, x, tk, tv, s.For)
+
+ default:
+ panic("Cannot range over: " + rt.String())
+ }
+
+ // Evaluate both LHS expressions before we update either.
+ var kl, vl lvalue
+ if tk != nil {
+ kl = b.addr(fn, s.Key, false) // non-escaping
+ }
+ if tv != nil {
+ vl = b.addr(fn, s.Value, false) // non-escaping
+ }
+ if tk != nil {
+ kl.store(fn, k)
+ }
+ if tv != nil {
+ vl.store(fn, v)
+ }
+
+ if label != nil {
+ label._break = done
+ label._continue = loop
+ }
+
+ fn.targets = &targets{
+ tail: fn.targets,
+ _break: done,
+ _continue: loop,
+ }
+ b.stmt(fn, s.Body)
+ fn.targets = fn.targets.tail
+ emitJump(fn, loop) // back-edge
+ fn.currentBlock = done
+}
+
+// stmt lowers statement s to SSA form, emitting code to fn.
+func (b *builder) stmt(fn *Function, _s ast.Stmt) {
+ // The label of the current statement. If non-nil, its _goto
+ // target is always set; its _break and _continue are set only
+ // within the body of switch/typeswitch/select/for/range.
+ // It is effectively an additional default-nil parameter of stmt().
+ var label *lblock
+start:
+ switch s := _s.(type) {
+ case *ast.EmptyStmt:
+ // ignore. (Usually removed by gofmt.)
+
+ case *ast.DeclStmt: // Con, Var or Typ
+ d := s.Decl.(*ast.GenDecl)
+ if d.Tok == token.VAR {
+ for _, spec := range d.Specs {
+ if vs, ok := spec.(*ast.ValueSpec); ok {
+ b.localValueSpec(fn, vs)
+ }
+ }
+ }
+
+ case *ast.LabeledStmt:
+ label = fn.labelledBlock(s.Label)
+ emitJump(fn, label._goto)
+ fn.currentBlock = label._goto
+ _s = s.Stmt
+ goto start // effectively: tailcall stmt(fn, s.Stmt, label)
+
+ case *ast.ExprStmt:
+ b.expr(fn, s.X)
+
+ case *ast.SendStmt:
+ fn.emit(&Send{
+ Chan: b.expr(fn, s.Chan),
+ X: emitConv(fn, b.expr(fn, s.Value),
+ fn.Pkg.typeOf(s.Chan).Underlying().(*types.Chan).Elem()),
+ pos: s.Arrow,
+ })
+
+ case *ast.IncDecStmt:
+ op := token.ADD
+ if s.Tok == token.DEC {
+ op = token.SUB
+ }
+ loc := b.addr(fn, s.X, false)
+ b.assignOp(fn, loc, NewConst(constant.MakeInt64(1), loc.typ()), op, s.Pos())
+
+ case *ast.AssignStmt:
+ switch s.Tok {
+ case token.ASSIGN, token.DEFINE:
+ b.assignStmt(fn, s.Lhs, s.Rhs, s.Tok == token.DEFINE)
+
+ default: // +=, etc.
+ op := s.Tok + token.ADD - token.ADD_ASSIGN
+ b.assignOp(fn, b.addr(fn, s.Lhs[0], false), b.expr(fn, s.Rhs[0]), op, s.Pos())
+ }
+
+ case *ast.GoStmt:
+ // The "intrinsics" new/make/len/cap are forbidden here.
+ // panic is treated like an ordinary function call.
+ v := Go{pos: s.Go}
+ b.setCall(fn, s.Call, &v.Call)
+ fn.emit(&v)
+
+ case *ast.DeferStmt:
+ // The "intrinsics" new/make/len/cap are forbidden here.
+ // panic is treated like an ordinary function call.
+ v := Defer{pos: s.Defer}
+ b.setCall(fn, s.Call, &v.Call)
+ fn.emit(&v)
+
+ // A deferred call can cause recovery from panic,
+ // and control resumes at the Recover block.
+ createRecoverBlock(fn)
+
+ case *ast.ReturnStmt:
+ var results []Value
+ if len(s.Results) == 1 && fn.Signature.Results().Len() > 1 {
+ // Return of one expression in a multi-valued function.
+ tuple := b.exprN(fn, s.Results[0])
+ ttuple := tuple.Type().(*types.Tuple)
+ for i, n := 0, ttuple.Len(); i < n; i++ {
+ results = append(results,
+ emitConv(fn, emitExtract(fn, tuple, i),
+ fn.Signature.Results().At(i).Type()))
+ }
+ } else {
+ // 1:1 return, or no-arg return in non-void function.
+ for i, r := range s.Results {
+ v := emitConv(fn, b.expr(fn, r), fn.Signature.Results().At(i).Type())
+ results = append(results, v)
+ }
+ }
+ if fn.namedResults != nil {
+ // Function has named result parameters (NRPs).
+ // Perform parallel assignment of return operands to NRPs.
+ for i, r := range results {
+ emitStore(fn, fn.namedResults[i], r, s.Return)
+ }
+ }
+ // Run function calls deferred in this
+ // function when explicitly returning from it.
+ fn.emit(new(RunDefers))
+ if fn.namedResults != nil {
+ // Reload NRPs to form the result tuple.
+ results = results[:0]
+ for _, r := range fn.namedResults {
+ results = append(results, emitLoad(fn, r))
+ }
+ }
+ fn.emit(&Return{Results: results, pos: s.Return})
+ fn.currentBlock = fn.newBasicBlock("unreachable")
+
+ case *ast.BranchStmt:
+ var block *BasicBlock
+ switch s.Tok {
+ case token.BREAK:
+ if s.Label != nil {
+ block = fn.labelledBlock(s.Label)._break
+ } else {
+ for t := fn.targets; t != nil && block == nil; t = t.tail {
+ block = t._break
+ }
+ }
+
+ case token.CONTINUE:
+ if s.Label != nil {
+ block = fn.labelledBlock(s.Label)._continue
+ } else {
+ for t := fn.targets; t != nil && block == nil; t = t.tail {
+ block = t._continue
+ }
+ }
+
+ case token.FALLTHROUGH:
+ for t := fn.targets; t != nil && block == nil; t = t.tail {
+ block = t._fallthrough
+ }
+
+ case token.GOTO:
+ block = fn.labelledBlock(s.Label)._goto
+ }
+ emitJump(fn, block)
+ fn.currentBlock = fn.newBasicBlock("unreachable")
+
+ case *ast.BlockStmt:
+ b.stmtList(fn, s.List)
+
+ case *ast.IfStmt:
+ if s.Init != nil {
+ b.stmt(fn, s.Init)
+ }
+ then := fn.newBasicBlock("if.then")
+ done := fn.newBasicBlock("if.done")
+ els := done
+ if s.Else != nil {
+ els = fn.newBasicBlock("if.else")
+ }
+ b.cond(fn, s.Cond, then, els)
+ fn.currentBlock = then
+ b.stmt(fn, s.Body)
+ emitJump(fn, done)
+
+ if s.Else != nil {
+ fn.currentBlock = els
+ b.stmt(fn, s.Else)
+ emitJump(fn, done)
+ }
+
+ fn.currentBlock = done
+
+ case *ast.SwitchStmt:
+ b.switchStmt(fn, s, label)
+
+ case *ast.TypeSwitchStmt:
+ b.typeSwitchStmt(fn, s, label)
+
+ case *ast.SelectStmt:
+ b.selectStmt(fn, s, label)
+
+ case *ast.ForStmt:
+ b.forStmt(fn, s, label)
+
+ case *ast.RangeStmt:
+ b.rangeStmt(fn, s, label)
+
+ default:
+ panic(fmt.Sprintf("unexpected statement kind: %T", s))
+ }
+}
+
+// buildFunction builds SSA code for the body of function fn. Idempotent.
+func (b *builder) buildFunction(fn *Function) {
+ if fn.Blocks != nil {
+ return // building already started
+ }
+
+ var recvField *ast.FieldList
+ var body *ast.BlockStmt
+ var functype *ast.FuncType
+ switch n := fn.syntax.(type) {
+ case nil:
+ return // not a Go source function. (Synthetic, or from object file.)
+ case *ast.FuncDecl:
+ functype = n.Type
+ recvField = n.Recv
+ body = n.Body
+ case *ast.FuncLit:
+ functype = n.Type
+ body = n.Body
+ default:
+ panic(n)
+ }
+
+ if body == nil {
+ // External function.
+ if fn.Params == nil {
+ // This condition ensures we add a non-empty
+ // params list once only, but we may attempt
+ // the degenerate empty case repeatedly.
+ // TODO(adonovan): opt: don't do that.
+
+ // We set Function.Params even though there is no body
+ // code to reference them. This simplifies clients.
+ if recv := fn.Signature.Recv(); recv != nil {
+ fn.addParamObj(recv)
+ }
+ params := fn.Signature.Params()
+ for i, n := 0, params.Len(); i < n; i++ {
+ fn.addParamObj(params.At(i))
+ }
+ }
+ return
+ }
+ if fn.Prog.mode&LogSource != 0 {
+ defer logStack("build function %s @ %s", fn, fn.Prog.Fset.Position(fn.pos))()
+ }
+ fn.startBody()
+ fn.createSyntacticParams(recvField, functype)
+ b.stmt(fn, body)
+ if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb == fn.Recover || cb.Preds != nil) {
+ // Control fell off the end of the function's body block.
+ //
+ // Block optimizations eliminate the current block, if
+ // unreachable. It is a builder invariant that
+ // if this no-arg return is ill-typed for
+ // fn.Signature.Results, this block must be
+ // unreachable. The sanity checker checks this.
+ fn.emit(new(RunDefers))
+ fn.emit(new(Return))
+ }
+ fn.finishBody()
+}
+
+// buildFuncDecl builds SSA code for the function or method declared
+// by decl in package pkg.
+//
+func (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) {
+ id := decl.Name
+ if isBlankIdent(id) {
+ return // discard
+ }
+ fn := pkg.values[pkg.info.Defs[id]].(*Function)
+ if decl.Recv == nil && id.Name == "init" {
+ var v Call
+ v.Call.Value = fn
+ v.setType(types.NewTuple())
+ pkg.init.emit(&v)
+ }
+ b.buildFunction(fn)
+}
+
+// Build calls Package.Build for each package in prog.
+// Building occurs in parallel unless the BuildSerially mode flag was set.
+//
+// Build is intended for whole-program analysis; a typical compiler
+// need only build a single package.
+//
+// Build is idempotent and thread-safe.
+//
+func (prog *Program) Build() {
+ var wg sync.WaitGroup
+ for _, p := range prog.packages {
+ if prog.mode&BuildSerially != 0 {
+ p.Build()
+ } else {
+ wg.Add(1)
+ go func(p *Package) {
+ p.Build()
+ wg.Done()
+ }(p)
+ }
+ }
+ wg.Wait()
+}
+
+// Build builds SSA code for all functions and vars in package p.
+//
+// Precondition: CreatePackage must have been called for all of p's
+// direct imports (and hence its direct imports must have been
+// error-free).
+//
+// Build is idempotent and thread-safe.
+//
+func (p *Package) Build() { p.buildOnce.Do(p.build) }
+
+func (p *Package) build() {
+ if p.info == nil {
+ return // synthetic package, e.g. "testmain"
+ }
+
+ // Ensure we have runtime type info for all exported members.
+ // TODO(adonovan): ideally belongs in memberFromObject, but
+ // that would require package creation in topological order.
+ for name, mem := range p.Members {
+ if ast.IsExported(name) {
+ p.Prog.needMethodsOf(mem.Type())
+ }
+ }
+ if p.Prog.mode&LogSource != 0 {
+ defer logStack("build %s", p)()
+ }
+ init := p.init
+ init.startBody()
+
+ var done *BasicBlock
+
+ if p.Prog.mode&BareInits == 0 {
+ // Make init() skip if package is already initialized.
+ initguard := p.Var("init$guard")
+ doinit := init.newBasicBlock("init.start")
+ done = init.newBasicBlock("init.done")
+ emitIf(init, emitLoad(init, initguard), done, doinit)
+ init.currentBlock = doinit
+ emitStore(init, initguard, vTrue, token.NoPos)
+
+ // Call the init() function of each package we import.
+ for _, pkg := range p.Pkg.Imports() {
+ prereq := p.Prog.packages[pkg]
+ if prereq == nil {
+ panic(fmt.Sprintf("Package(%q).Build(): unsatisfied import: Program.CreatePackage(%q) was not called", p.Pkg.Path(), pkg.Path()))
+ }
+ var v Call
+ v.Call.Value = prereq.init
+ v.Call.pos = init.pos
+ v.setType(types.NewTuple())
+ init.emit(&v)
+ }
+ }
+
+ var b builder
+
+ // Initialize package-level vars in correct order.
+ for _, varinit := range p.info.InitOrder {
+ if init.Prog.mode&LogSource != 0 {
+ fmt.Fprintf(os.Stderr, "build global initializer %v @ %s\n",
+ varinit.Lhs, p.Prog.Fset.Position(varinit.Rhs.Pos()))
+ }
+ if len(varinit.Lhs) == 1 {
+ // 1:1 initialization: var x, y = a(), b()
+ var lval lvalue
+ if v := varinit.Lhs[0]; v.Name() != "_" {
+ lval = &address{addr: p.values[v].(*Global), pos: v.Pos()}
+ } else {
+ lval = blank{}
+ }
+ b.assign(init, lval, varinit.Rhs, true, nil)
+ } else {
+ // n:1 initialization: var x, y := f()
+ tuple := b.exprN(init, varinit.Rhs)
+ for i, v := range varinit.Lhs {
+ if v.Name() == "_" {
+ continue
+ }
+ emitStore(init, p.values[v].(*Global), emitExtract(init, tuple, i), v.Pos())
+ }
+ }
+ }
+
+ // Build all package-level functions, init functions
+ // and methods, including unreachable/blank ones.
+ // We build them in source order, but it's not significant.
+ for _, file := range p.files {
+ for _, decl := range file.Decls {
+ if decl, ok := decl.(*ast.FuncDecl); ok {
+ b.buildFuncDecl(p, decl)
+ }
+ }
+ }
+
+ // Finish up init().
+ if p.Prog.mode&BareInits == 0 {
+ emitJump(init, done)
+ init.currentBlock = done
+ }
+ init.emit(new(Return))
+ init.finishBody()
+
+ p.info = nil // We no longer need ASTs or go/types deductions.
+
+ if p.Prog.mode&SanityCheckFunctions != 0 {
+ sanityCheckPackage(p)
+ }
+}
+
+// Like ObjectOf, but panics instead of returning nil.
+// Only valid during p's create and build phases.
+func (p *Package) objectOf(id *ast.Ident) types.Object {
+ if o := p.info.ObjectOf(id); o != nil {
+ return o
+ }
+ panic(fmt.Sprintf("no types.Object for ast.Ident %s @ %s",
+ id.Name, p.Prog.Fset.Position(id.Pos())))
+}
+
+// Like TypeOf, but panics instead of returning nil.
+// Only valid during p's create and build phases.
+func (p *Package) typeOf(e ast.Expr) types.Type {
+ if T := p.info.TypeOf(e); T != nil {
+ return T
+ }
+ panic(fmt.Sprintf("no type for %T @ %s",
+ e, p.Prog.Fset.Position(e.Pos())))
+}
diff --git a/vendor/honnef.co/go/tools/ssa/const.go b/vendor/honnef.co/go/tools/ssa/const.go
new file mode 100644
index 000000000..f95d9e114
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/const.go
@@ -0,0 +1,169 @@
+// 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 ssa
+
+// This file defines the Const SSA value type.
+
+import (
+ "fmt"
+ "go/constant"
+ "go/token"
+ "go/types"
+ "strconv"
+)
+
+// 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 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(constant.MakeInt64(i), tInt)
+}
+
+// nilConst returns a nil constant of the specified type, which may
+// be any reference type, including interfaces.
+//
+func nilConst(typ types.Type) *Const {
+ return NewConst(nil, typ)
+}
+
+// stringConst returns a 'string' constant that evaluates to s.
+func stringConst(s string) *Const {
+ return NewConst(constant.MakeString(s), tString)
+}
+
+// zeroConst returns a new "zero" constant of the specified type,
+// which must not be an array or struct type: the zero values of
+// aggregates are well-defined but cannot be represented by Const.
+//
+func zeroConst(t types.Type) *Const {
+ switch t := t.(type) {
+ case *types.Basic:
+ switch {
+ case t.Info()&types.IsBoolean != 0:
+ return NewConst(constant.MakeBool(false), t)
+ case t.Info()&types.IsNumeric != 0:
+ return NewConst(constant.MakeInt64(0), t)
+ case t.Info()&types.IsString != 0:
+ return NewConst(constant.MakeString(""), t)
+ case t.Kind() == types.UnsafePointer:
+ fallthrough
+ case t.Kind() == types.UntypedNil:
+ return nilConst(t)
+ default:
+ panic(fmt.Sprint("zeroConst for unexpected type:", t))
+ }
+ case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature:
+ return nilConst(t)
+ case *types.Named:
+ return NewConst(zeroConst(t.Underlying()).Value, t)
+ case *types.Array, *types.Struct, *types.Tuple:
+ panic(fmt.Sprint("zeroConst applied to aggregate:", t))
+ }
+ panic(fmt.Sprint("zeroConst: unexpected ", t))
+}
+
+func (c *Const) RelString(from *types.Package) string {
+ var s string
+ if c.Value == nil {
+ s = "nil"
+ } 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 {
+ s = s[:max-3] + "..." // abbreviate
+ }
+ s = strconv.Quote(s)
+ } else {
+ s = c.Value.String()
+ }
+ return s + ":" + relType(c.Type(), from)
+}
+
+func (c *Const) Name() string {
+ return c.RelString(nil)
+}
+
+func (c *Const) String() string {
+ return c.Name()
+}
+
+func (c *Const) Type() types.Type {
+ return c.typ
+}
+
+func (c *Const) Referrers() *[]Instruction {
+ return nil
+}
+
+func (c *Const) Parent() *Function { return nil }
+
+func (c *Const) Pos() token.Pos {
+ return token.NoPos
+}
+
+// IsNil returns true if this constant represents a typed or untyped nil value.
+func (c *Const) IsNil() bool {
+ return c.Value == nil
+}
+
+// TODO(adonovan): move everything below into honnef.co/go/tools/ssa/interp.
+
+// Int64 returns the numeric value of this constant truncated to fit
+// a signed 64-bit integer.
+//
+func (c *Const) Int64() int64 {
+ switch x := constant.ToInt(c.Value); x.Kind() {
+ case constant.Int:
+ if i, ok := constant.Int64Val(x); ok {
+ return i
+ }
+ return 0
+ case constant.Float:
+ f, _ := constant.Float64Val(x)
+ return int64(f)
+ }
+ panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
+}
+
+// Uint64 returns the numeric value of this constant truncated to fit
+// an unsigned 64-bit integer.
+//
+func (c *Const) Uint64() uint64 {
+ switch x := constant.ToInt(c.Value); x.Kind() {
+ case constant.Int:
+ if u, ok := constant.Uint64Val(x); ok {
+ return u
+ }
+ return 0
+ case constant.Float:
+ f, _ := constant.Float64Val(x)
+ return uint64(f)
+ }
+ panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
+}
+
+// Float64 returns the numeric value of this constant truncated to fit
+// a float64.
+//
+func (c *Const) Float64() float64 {
+ f, _ := constant.Float64Val(c.Value)
+ return f
+}
+
+// Complex128 returns the complex value of this constant truncated to
+// fit a complex128.
+//
+func (c *Const) Complex128() complex128 {
+ 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
new file mode 100644
index 000000000..85163a0c5
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/create.go
@@ -0,0 +1,270 @@
+// 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 ssa
+
+// This file implements the CREATE phase of SSA construction.
+// See builder.go for explanation.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+ "os"
+ "sync"
+
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// NewProgram returns a new SSA Program.
+//
+// mode controls diagnostics and checking during SSA construction.
+//
+func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
+ prog := &Program{
+ Fset: fset,
+ imported: make(map[string]*Package),
+ packages: make(map[*types.Package]*Package),
+ thunks: make(map[selectionKey]*Function),
+ bounds: make(map[*types.Func]*Function),
+ mode: mode,
+ }
+
+ h := typeutil.MakeHasher() // protected by methodsMu, in effect
+ prog.methodSets.SetHasher(h)
+ prog.canon.SetHasher(h)
+
+ return prog
+}
+
+// memberFromObject populates package pkg with a member for the
+// typechecker object obj.
+//
+// For objects from Go source code, syntax is the associated syntax
+// tree (for funcs and vars only); it will be used during the build
+// phase.
+//
+func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
+ name := obj.Name()
+ switch obj := obj.(type) {
+ case *types.Builtin:
+ if pkg.Pkg != types.Unsafe {
+ panic("unexpected builtin object: " + obj.String())
+ }
+
+ case *types.TypeName:
+ pkg.Members[name] = &Type{
+ object: obj,
+ pkg: pkg,
+ }
+
+ case *types.Const:
+ c := &NamedConst{
+ object: obj,
+ Value: NewConst(obj.Val(), obj.Type()),
+ pkg: pkg,
+ }
+ pkg.values[obj] = c.Value
+ pkg.Members[name] = c
+
+ case *types.Var:
+ g := &Global{
+ Pkg: pkg,
+ name: name,
+ object: obj,
+ typ: types.NewPointer(obj.Type()), // address
+ pos: obj.Pos(),
+ }
+ pkg.values[obj] = g
+ pkg.Members[name] = g
+
+ case *types.Func:
+ sig := obj.Type().(*types.Signature)
+ if sig.Recv() == nil && name == "init" {
+ pkg.ninit++
+ name = fmt.Sprintf("init#%d", pkg.ninit)
+ }
+ fn := &Function{
+ name: name,
+ object: obj,
+ Signature: sig,
+ syntax: syntax,
+ pos: obj.Pos(),
+ Pkg: pkg,
+ Prog: pkg.Prog,
+ }
+ if syntax == nil {
+ fn.Synthetic = "loaded from gc object file"
+ }
+
+ pkg.values[obj] = fn
+ if sig.Recv() == nil {
+ pkg.Members[name] = fn // package-level function
+ }
+
+ default: // (incl. *types.Package)
+ panic("unexpected Object type: " + obj.String())
+ }
+}
+
+// membersFromDecl populates package pkg with members for each
+// typechecker object (var, func, const or type) associated with the
+// specified decl.
+//
+func membersFromDecl(pkg *Package, decl ast.Decl) {
+ switch decl := decl.(type) {
+ case *ast.GenDecl: // import, const, type or var
+ switch decl.Tok {
+ case token.CONST:
+ for _, spec := range decl.Specs {
+ for _, id := range spec.(*ast.ValueSpec).Names {
+ if !isBlankIdent(id) {
+ memberFromObject(pkg, pkg.info.Defs[id], nil)
+ }
+ }
+ }
+
+ case token.VAR:
+ for _, spec := range decl.Specs {
+ for _, id := range spec.(*ast.ValueSpec).Names {
+ if !isBlankIdent(id) {
+ memberFromObject(pkg, pkg.info.Defs[id], spec)
+ }
+ }
+ }
+
+ case token.TYPE:
+ for _, spec := range decl.Specs {
+ id := spec.(*ast.TypeSpec).Name
+ if !isBlankIdent(id) {
+ memberFromObject(pkg, pkg.info.Defs[id], nil)
+ }
+ }
+ }
+
+ case *ast.FuncDecl:
+ id := decl.Name
+ if !isBlankIdent(id) {
+ memberFromObject(pkg, pkg.info.Defs[id], decl)
+ }
+ }
+}
+
+// CreatePackage constructs and returns an SSA Package from the
+// specified type-checked, error-free file ASTs, and populates its
+// Members mapping.
+//
+// importable determines whether this package should be returned by a
+// subsequent call to ImportedPackage(pkg.Path()).
+//
+// The real work of building SSA form for each function is not done
+// until a subsequent call to Package.Build().
+//
+func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {
+ p := &Package{
+ Prog: prog,
+ Members: make(map[string]Member),
+ values: make(map[types.Object]Value),
+ Pkg: pkg,
+ info: info, // transient (CREATE and BUILD phases)
+ files: files, // transient (CREATE and BUILD phases)
+ }
+
+ // Add init() function.
+ p.init = &Function{
+ name: "init",
+ Signature: new(types.Signature),
+ Synthetic: "package initializer",
+ Pkg: p,
+ Prog: prog,
+ }
+ p.Members[p.init.name] = p.init
+
+ // CREATE phase.
+ // Allocate all package members: vars, funcs, consts and types.
+ if len(files) > 0 {
+ // Go source package.
+ for _, file := range files {
+ for _, decl := range file.Decls {
+ membersFromDecl(p, decl)
+ }
+ }
+ } else {
+ // GC-compiled binary package (or "unsafe")
+ // No code.
+ // No position information.
+ scope := p.Pkg.Scope()
+ for _, name := range scope.Names() {
+ obj := scope.Lookup(name)
+ memberFromObject(p, obj, nil)
+ if obj, ok := obj.(*types.TypeName); ok {
+ if named, ok := obj.Type().(*types.Named); ok {
+ for i, n := 0, named.NumMethods(); i < n; i++ {
+ memberFromObject(p, named.Method(i), nil)
+ }
+ }
+ }
+ }
+ }
+
+ if prog.mode&BareInits == 0 {
+ // Add initializer guard variable.
+ initguard := &Global{
+ Pkg: p,
+ name: "init$guard",
+ typ: types.NewPointer(tBool),
+ }
+ p.Members[initguard.Name()] = initguard
+ }
+
+ if prog.mode&GlobalDebug != 0 {
+ p.SetDebugMode(true)
+ }
+
+ if prog.mode&PrintPackages != 0 {
+ printMu.Lock()
+ p.WriteTo(os.Stdout)
+ printMu.Unlock()
+ }
+
+ if importable {
+ prog.imported[p.Pkg.Path()] = p
+ }
+ prog.packages[p.Pkg] = p
+
+ return p
+}
+
+// printMu serializes printing of Packages/Functions to stdout.
+var printMu sync.Mutex
+
+// AllPackages returns a new slice containing all packages in the
+// program prog in unspecified order.
+//
+func (prog *Program) AllPackages() []*Package {
+ pkgs := make([]*Package, 0, len(prog.packages))
+ for _, pkg := range prog.packages {
+ pkgs = append(pkgs, pkg)
+ }
+ return pkgs
+}
+
+// ImportedPackage returns the importable Package whose PkgPath
+// is path, or nil if no such Package has been created.
+//
+// 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
new file mode 100644
index 000000000..0f71fda00
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/doc.go
@@ -0,0 +1,125 @@
+// 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 ssa defines a representation of the elements of Go programs
+// (packages, types, functions, variables and constants) using a
+// static single-assignment (SSA) form intermediate representation
+// (IR) for the bodies of functions.
+//
+// THIS INTERFACE IS EXPERIMENTAL AND IS LIKELY TO CHANGE.
+//
+// For an introduction to SSA form, see
+// http://en.wikipedia.org/wiki/Static_single_assignment_form.
+// This page provides a broader reading list:
+// http://www.dcs.gla.ac.uk/~jsinger/ssa.html.
+//
+// The level of abstraction of the SSA form is intentionally close to
+// the source language to facilitate construction of source analysis
+// tools. It is not intended for machine code generation.
+//
+// All looping, branching and switching constructs are replaced with
+// unstructured control flow. Higher-level control flow constructs
+// such as multi-way branch can be reconstructed as needed; see
+// ssautil.Switches() for an example.
+//
+// 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
+// stores. Registerisation of eligible locals and φ-node insertion
+// using dominance and dataflow are then performed as a second pass
+// called "lifting" to improve the accuracy and performance of
+// subsequent analyses; this pass can be skipped by setting the
+// NaiveForm builder flag.
+//
+// The primary interfaces of this package are:
+//
+// - Member: a named member of a Go package.
+// - Value: an expression that yields a value.
+// - Instruction: a statement that consumes values and performs computation.
+// - Node: a Value or Instruction (emphasizing its membership in the SSA value graph)
+//
+// A computation that yields a result implements both the Value and
+// Instruction interfaces. The following table shows for each
+// concrete type which of these interfaces it implements.
+//
+// Value? Instruction? Member?
+// *Alloc ✔ ✔
+// *BinOp ✔ ✔
+// *Builtin ✔
+// *Call ✔ ✔
+// *ChangeInterface ✔ ✔
+// *ChangeType ✔ ✔
+// *Const ✔
+// *Convert ✔ ✔
+// *DebugRef ✔
+// *Defer ✔
+// *Extract ✔ ✔
+// *Field ✔ ✔
+// *FieldAddr ✔ ✔
+// *FreeVar ✔
+// *Function ✔ ✔ (func)
+// *Global ✔ ✔ (var)
+// *Go ✔
+// *If ✔
+// *Index ✔ ✔
+// *IndexAddr ✔ ✔
+// *Jump ✔
+// *Lookup ✔ ✔
+// *MakeChan ✔ ✔
+// *MakeClosure ✔ ✔
+// *MakeInterface ✔ ✔
+// *MakeMap ✔ ✔
+// *MakeSlice ✔ ✔
+// *MapUpdate ✔
+// *NamedConst ✔ (const)
+// *Next ✔ ✔
+// *Panic ✔
+// *Parameter ✔
+// *Phi ✔ ✔
+// *Range ✔ ✔
+// *Return ✔
+// *RunDefers ✔
+// *Select ✔ ✔
+// *Send ✔
+// *Slice ✔ ✔
+// *Store ✔
+// *Type ✔ (type)
+// *TypeAssert ✔ ✔
+// *UnOp ✔ ✔
+//
+// Other key types in this package include: Program, Package, Function
+// and BasicBlock.
+//
+// The program representation constructed by this package is fully
+// resolved internally, i.e. it does not rely on the names of Values,
+// Packages, Functions, Types or BasicBlocks for the correct
+// interpretation of the program. Only the identities of objects and
+// the topology of the SSA and type graphs are semantically
+// significant. (There is one exception: Ids, used to identify field
+// and method names, contain strings.) Avoidance of name-based
+// operations simplifies the implementation of subsequent passes and
+// can make them very efficient. Many objects are nonetheless named
+// to aid in debugging, but it is not essential that the names be
+// either accurate or unambiguous. The public API exposes a number of
+// name-based maps for client convenience.
+//
+// The ssa/ssautil package provides various utilities that depend only
+// on the public API of this package.
+//
+// TODO(adonovan): Consider the exceptional control-flow implications
+// of defer and recover().
+//
+// TODO(adonovan): write a how-to document for all the various cases
+// of trying to determine corresponding elements across the four
+// domains of source locations, ast.Nodes, types.Objects,
+// ssa.Values/Instructions.
+//
+package ssa // import "honnef.co/go/tools/ssa"
diff --git a/vendor/honnef.co/go/tools/ssa/dom.go b/vendor/honnef.co/go/tools/ssa/dom.go
new file mode 100644
index 000000000..a036be87c
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/dom.go
@@ -0,0 +1,343 @@
+// 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 ssa
+
+// This file defines algorithms related to dominance.
+
+// Dominator tree construction ----------------------------------------
+//
+// We use the algorithm described in Lengauer & Tarjan. 1979. A fast
+// algorithm for finding dominators in a flowgraph.
+// http://doi.acm.org/10.1145/357062.357071
+//
+// We also apply the optimizations to SLT described in Georgiadis et
+// al, Finding Dominators in Practice, JGAA 2006,
+// http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
+// to avoid the need for buckets of size > 1.
+
+import (
+ "bytes"
+ "fmt"
+ "math/big"
+ "os"
+ "sort"
+)
+
+// Idom returns the block that immediately dominates b:
+// its parent in the dominator tree, if any.
+// Neither the entry node (b.Index==0) nor recover node
+// (b==b.Parent().Recover()) have a parent.
+//
+func (b *BasicBlock) Idom() *BasicBlock { return b.dom.idom }
+
+// Dominees returns the list of blocks that b immediately dominates:
+// its children in the dominator tree.
+//
+func (b *BasicBlock) Dominees() []*BasicBlock { return b.dom.children }
+
+// Dominates reports whether b dominates c.
+func (b *BasicBlock) Dominates(c *BasicBlock) bool {
+ return b.dom.pre <= c.dom.pre && c.dom.post <= b.dom.post
+}
+
+type byDomPreorder []*BasicBlock
+
+func (a byDomPreorder) Len() int { return len(a) }
+func (a byDomPreorder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a byDomPreorder) Less(i, j int) bool { return a[i].dom.pre < a[j].dom.pre }
+
+// DomPreorder returns a new slice containing the blocks of f in
+// dominator tree preorder.
+//
+func (f *Function) DomPreorder() []*BasicBlock {
+ n := len(f.Blocks)
+ order := make(byDomPreorder, n)
+ copy(order, f.Blocks)
+ sort.Sort(order)
+ return order
+}
+
+// domInfo contains a BasicBlock's dominance information.
+type domInfo struct {
+ idom *BasicBlock // immediate dominator (parent in domtree)
+ children []*BasicBlock // nodes immediately dominated by this one
+ pre, post int32 // pre- and post-order numbering within domtree
+}
+
+// ltState holds the working state for Lengauer-Tarjan algorithm
+// (during which domInfo.pre is repurposed for CFG DFS preorder number).
+type ltState struct {
+ // Each slice is indexed by b.Index.
+ sdom []*BasicBlock // b's semidominator
+ parent []*BasicBlock // b's parent in DFS traversal of CFG
+ ancestor []*BasicBlock // b's ancestor with least sdom
+}
+
+// dfs implements the depth-first search part of the LT algorithm.
+func (lt *ltState) dfs(v *BasicBlock, i int32, preorder []*BasicBlock) int32 {
+ preorder[i] = v
+ v.dom.pre = i // For now: DFS preorder of spanning tree of CFG
+ i++
+ lt.sdom[v.Index] = v
+ lt.link(nil, v)
+ for _, w := range v.Succs {
+ if lt.sdom[w.Index] == nil {
+ lt.parent[w.Index] = v
+ i = lt.dfs(w, i, preorder)
+ }
+ }
+ return i
+}
+
+// eval implements the EVAL part of the LT algorithm.
+func (lt *ltState) eval(v *BasicBlock) *BasicBlock {
+ // TODO(adonovan): opt: do path compression per simple LT.
+ u := v
+ for ; lt.ancestor[v.Index] != nil; v = lt.ancestor[v.Index] {
+ if lt.sdom[v.Index].dom.pre < lt.sdom[u.Index].dom.pre {
+ u = v
+ }
+ }
+ return u
+}
+
+// link implements the LINK part of the LT algorithm.
+func (lt *ltState) link(v, w *BasicBlock) {
+ lt.ancestor[w.Index] = v
+}
+
+// buildDomTree computes the dominator tree of f using the LT algorithm.
+// Precondition: all blocks are reachable (e.g. optimizeBlocks has been run).
+//
+func buildDomTree(f *Function) {
+ // The step numbers refer to the original LT paper; the
+ // reordering is due to Georgiadis.
+
+ // Clear any previous domInfo.
+ for _, b := range f.Blocks {
+ b.dom = domInfo{}
+ }
+
+ n := len(f.Blocks)
+ // Allocate space for 5 contiguous [n]*BasicBlock arrays:
+ // sdom, parent, ancestor, preorder, buckets.
+ space := make([]*BasicBlock, 5*n)
+ lt := ltState{
+ sdom: space[0:n],
+ parent: space[n : 2*n],
+ ancestor: space[2*n : 3*n],
+ }
+
+ // Step 1. Number vertices by depth-first preorder.
+ preorder := space[3*n : 4*n]
+ root := f.Blocks[0]
+ prenum := lt.dfs(root, 0, preorder)
+ recover := f.Recover
+ if recover != nil {
+ lt.dfs(recover, prenum, preorder)
+ }
+
+ buckets := space[4*n : 5*n]
+ copy(buckets, preorder)
+
+ // In reverse preorder...
+ for i := int32(n) - 1; i > 0; i-- {
+ w := preorder[i]
+
+ // Step 3. Implicitly define the immediate dominator of each node.
+ for v := buckets[i]; v != w; v = buckets[v.dom.pre] {
+ u := lt.eval(v)
+ if lt.sdom[u.Index].dom.pre < i {
+ v.dom.idom = u
+ } else {
+ v.dom.idom = w
+ }
+ }
+
+ // Step 2. Compute the semidominators of all nodes.
+ lt.sdom[w.Index] = lt.parent[w.Index]
+ for _, v := range w.Preds {
+ u := lt.eval(v)
+ if lt.sdom[u.Index].dom.pre < lt.sdom[w.Index].dom.pre {
+ lt.sdom[w.Index] = lt.sdom[u.Index]
+ }
+ }
+
+ lt.link(lt.parent[w.Index], w)
+
+ if lt.parent[w.Index] == lt.sdom[w.Index] {
+ w.dom.idom = lt.parent[w.Index]
+ } else {
+ buckets[i] = buckets[lt.sdom[w.Index].dom.pre]
+ buckets[lt.sdom[w.Index].dom.pre] = w
+ }
+ }
+
+ // The final 'Step 3' is now outside the loop.
+ for v := buckets[0]; v != root; v = buckets[v.dom.pre] {
+ v.dom.idom = root
+ }
+
+ // Step 4. Explicitly define the immediate dominator of each
+ // node, in preorder.
+ for _, w := range preorder[1:] {
+ if w == root || w == recover {
+ w.dom.idom = nil
+ } else {
+ if w.dom.idom != lt.sdom[w.Index] {
+ w.dom.idom = w.dom.idom.dom.idom
+ }
+ // Calculate Children relation as inverse of Idom.
+ w.dom.idom.dom.children = append(w.dom.idom.dom.children, w)
+ }
+ }
+
+ pre, post := numberDomTree(root, 0, 0)
+ if recover != nil {
+ numberDomTree(recover, pre, post)
+ }
+
+ // printDomTreeDot(os.Stderr, f) // debugging
+ // printDomTreeText(os.Stderr, root, 0) // debugging
+
+ if f.Prog.mode&SanityCheckFunctions != 0 {
+ sanityCheckDomTree(f)
+ }
+}
+
+// numberDomTree sets the pre- and post-order numbers of a depth-first
+// traversal of the dominator tree rooted at v. These are used to
+// answer dominance queries in constant time.
+//
+func numberDomTree(v *BasicBlock, pre, post int32) (int32, int32) {
+ v.dom.pre = pre
+ pre++
+ for _, child := range v.dom.children {
+ pre, post = numberDomTree(child, pre, post)
+ }
+ v.dom.post = post
+ post++
+ return pre, post
+}
+
+// Testing utilities ----------------------------------------
+
+// sanityCheckDomTree checks the correctness of the dominator tree
+// computed by the LT algorithm by comparing against the dominance
+// relation computed by a naive Kildall-style forward dataflow
+// analysis (Algorithm 10.16 from the "Dragon" book).
+//
+func sanityCheckDomTree(f *Function) {
+ n := len(f.Blocks)
+
+ // D[i] is the set of blocks that dominate f.Blocks[i],
+ // represented as a bit-set of block indices.
+ D := make([]big.Int, n)
+
+ one := big.NewInt(1)
+
+ // all is the set of all blocks; constant.
+ var all big.Int
+ all.Set(one).Lsh(&all, uint(n)).Sub(&all, one)
+
+ // Initialization.
+ for i, b := range f.Blocks {
+ if i == 0 || b == f.Recover {
+ // A root is dominated only by itself.
+ D[i].SetBit(&D[0], 0, 1)
+ } else {
+ // All other blocks are (initially) dominated
+ // by every block.
+ D[i].Set(&all)
+ }
+ }
+
+ // Iteration until fixed point.
+ for changed := true; changed; {
+ changed = false
+ for i, b := range f.Blocks {
+ if i == 0 || b == f.Recover {
+ continue
+ }
+ // Compute intersection across predecessors.
+ var x big.Int
+ x.Set(&all)
+ for _, pred := range b.Preds {
+ x.And(&x, &D[pred.Index])
+ }
+ x.SetBit(&x, i, 1) // a block always dominates itself.
+ if D[i].Cmp(&x) != 0 {
+ D[i].Set(&x)
+ changed = true
+ }
+ }
+ }
+
+ // Check the entire relation. O(n^2).
+ // The Recover block (if any) must be treated specially so we skip it.
+ ok := true
+ for i := 0; i < n; i++ {
+ for j := 0; j < n; j++ {
+ b, c := f.Blocks[i], f.Blocks[j]
+ if c == f.Recover {
+ continue
+ }
+ actual := b.Dominates(c)
+ expected := D[j].Bit(i) == 1
+ if actual != expected {
+ fmt.Fprintf(os.Stderr, "dominates(%s, %s)==%t, want %t\n", b, c, actual, expected)
+ ok = false
+ }
+ }
+ }
+
+ preorder := f.DomPreorder()
+ for _, b := range f.Blocks {
+ if got := preorder[b.dom.pre]; got != b {
+ fmt.Fprintf(os.Stderr, "preorder[%d]==%s, want %s\n", b.dom.pre, got, b)
+ ok = false
+ }
+ }
+
+ if !ok {
+ panic("sanityCheckDomTree failed for " + f.String())
+ }
+
+}
+
+// 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 {
+ printDomTreeText(buf, child, indent+1)
+ }
+}
+
+// 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 {")
+ for i, b := range f.Blocks {
+ v := b.dom
+ fmt.Fprintf(buf, "\tn%d [label=\"%s (%d, %d)\",shape=\"rectangle\"];\n", v.pre, b, v.pre, v.post)
+ // TODO(adonovan): improve appearance of edges
+ // belonging to both dominator tree and CFG.
+
+ // Dominator tree edge.
+ if i != 0 {
+ fmt.Fprintf(buf, "\tn%d -> n%d [style=\"solid\",weight=100];\n", v.idom.dom.pre, v.pre)
+ }
+ // CFG edges.
+ for _, pred := range b.Preds {
+ fmt.Fprintf(buf, "\tn%d -> n%d [style=\"dotted\",weight=0];\n", pred.dom.pre, v.pre)
+ }
+ }
+ fmt.Fprintln(buf, "}")
+}
diff --git a/vendor/honnef.co/go/tools/ssa/emit.go b/vendor/honnef.co/go/tools/ssa/emit.go
new file mode 100644
index 000000000..6bf9ec32d
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/emit.go
@@ -0,0 +1,469 @@
+// 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 ssa
+
+// Helpers for emitting SSA instructions.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+)
+
+// emitNew emits to f a new (heap Alloc) instruction allocating an
+// object of type typ. pos is the optional source location.
+//
+func emitNew(f *Function, typ types.Type, pos token.Pos) *Alloc {
+ v := &Alloc{Heap: true}
+ v.setType(types.NewPointer(typ))
+ v.setPos(pos)
+ f.emit(v)
+ return v
+}
+
+// emitLoad emits to f an instruction to load the address addr into a
+// new temporary, and returns the value so defined.
+//
+func emitLoad(f *Function, addr Value) *UnOp {
+ v := &UnOp{Op: token.MUL, X: addr}
+ v.setType(deref(addr.Type()))
+ f.emit(v)
+ return v
+}
+
+// emitDebugRef emits to f a DebugRef pseudo-instruction associating
+// expression e with value v.
+//
+func emitDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) {
+ if !f.debugInfo() {
+ return // debugging not enabled
+ }
+ if v == nil || e == nil {
+ panic("nil")
+ }
+ var obj types.Object
+ e = unparen(e)
+ if id, ok := e.(*ast.Ident); ok {
+ if isBlankIdent(id) {
+ return
+ }
+ obj = f.Pkg.objectOf(id)
+ switch obj.(type) {
+ case *types.Nil, *types.Const, *types.Builtin:
+ return
+ }
+ }
+ f.emit(&DebugRef{
+ X: v,
+ Expr: e,
+ IsAddr: isAddr,
+ object: obj,
+ })
+}
+
+// emitArith emits to f code to compute the binary operation op(x, y)
+// where op is an eager shift, logical or arithmetic operation.
+// (Use emitCompare() for comparisons and Builder.logicalBinop() for
+// non-eager operations.)
+//
+func emitArith(f *Function, op token.Token, x, y Value, t types.Type, pos token.Pos) Value {
+ switch op {
+ case token.SHL, token.SHR:
+ x = emitConv(f, x, t)
+ // y may be signed or an 'untyped' constant.
+ // TODO(adonovan): whence signed values?
+ if b, ok := y.Type().Underlying().(*types.Basic); ok && b.Info()&types.IsUnsigned == 0 {
+ y = emitConv(f, y, types.Typ[types.Uint64])
+ }
+
+ case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
+ x = emitConv(f, x, t)
+ y = emitConv(f, y, t)
+
+ default:
+ panic("illegal op in emitArith: " + op.String())
+
+ }
+ v := &BinOp{
+ Op: op,
+ X: x,
+ Y: y,
+ }
+ v.setPos(pos)
+ v.setType(t)
+ return f.emit(v)
+}
+
+// emitCompare emits to f code compute the boolean result of
+// comparison comparison 'x op y'.
+//
+func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value {
+ xt := x.Type().Underlying()
+ yt := y.Type().Underlying()
+
+ // Special case to optimise a tagless SwitchStmt so that
+ // these are equivalent
+ // switch { case e: ...}
+ // switch true { case e: ... }
+ // if e==true { ... }
+ // even in the case when e's type is an interface.
+ // TODO(adonovan): opt: generalise to x==true, false!=y, etc.
+ if x == vTrue && op == token.EQL {
+ if yt, ok := yt.(*types.Basic); ok && yt.Info()&types.IsBoolean != 0 {
+ return y
+ }
+ }
+
+ if types.Identical(xt, yt) {
+ // no conversion necessary
+ } else if _, ok := xt.(*types.Interface); ok {
+ y = emitConv(f, y, x.Type())
+ } else if _, ok := yt.(*types.Interface); ok {
+ x = emitConv(f, x, y.Type())
+ } else if _, ok := x.(*Const); ok {
+ 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.
+ }
+
+ v := &BinOp{
+ Op: op,
+ X: x,
+ Y: y,
+ }
+ v.setPos(pos)
+ v.setType(tBool)
+ return f.emit(v)
+}
+
+// isValuePreserving returns true if a conversion from ut_src to
+// ut_dst is value-preserving, i.e. just a change of type.
+// Precondition: neither argument is a named type.
+//
+func isValuePreserving(ut_src, ut_dst types.Type) bool {
+ // Identical underlying types?
+ if structTypesIdentical(ut_dst, ut_src) {
+ return true
+ }
+
+ switch ut_dst.(type) {
+ case *types.Chan:
+ // Conversion between channel types?
+ _, ok := ut_src.(*types.Chan)
+ return ok
+
+ case *types.Pointer:
+ // Conversion between pointers with identical base types?
+ _, ok := ut_src.(*types.Pointer)
+ return ok
+ }
+ return false
+}
+
+// emitConv emits to f code to convert Value val to exactly type typ,
+// and returns the converted value. Implicit conversions are required
+// by language assignability rules in assignments, parameter passing,
+// etc. Conversions cannot fail dynamically.
+//
+func emitConv(f *Function, val Value, typ types.Type) Value {
+ t_src := val.Type()
+
+ // Identical types? Conversion is a no-op.
+ if types.Identical(t_src, typ) {
+ return val
+ }
+
+ ut_dst := typ.Underlying()
+ ut_src := t_src.Underlying()
+
+ // Just a change of type, but not value or representation?
+ if isValuePreserving(ut_src, ut_dst) {
+ c := &ChangeType{X: val}
+ c.setType(typ)
+ return f.emit(c)
+ }
+
+ // Conversion to, or construction of a value of, an interface type?
+ if _, ok := ut_dst.(*types.Interface); ok {
+ // Assignment from one interface type to another?
+ if _, ok := ut_src.(*types.Interface); ok {
+ c := &ChangeInterface{X: val}
+ c.setType(typ)
+ return f.emit(c)
+ }
+
+ // Untyped nil constant? Return interface-typed nil constant.
+ if ut_src == tUntypedNil {
+ return nilConst(typ)
+ }
+
+ // Convert (non-nil) "untyped" literals to their default type.
+ if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 {
+ val = emitConv(f, val, DefaultType(ut_src))
+ }
+
+ f.Pkg.Prog.needMethodsOf(val.Type())
+ mi := &MakeInterface{X: val}
+ mi.setType(typ)
+ return f.emit(mi)
+ }
+
+ // Conversion of a compile-time constant value?
+ if c, ok := val.(*Const); ok {
+ if _, ok := ut_dst.(*types.Basic); ok || c.IsNil() {
+ // Conversion of a compile-time constant to
+ // another constant type results in a new
+ // constant of the destination type and
+ // (initially) the same abstract value.
+ // We don't truncate the value yet.
+ return NewConst(c.Value, typ)
+ }
+
+ // We're converting from constant to non-constant type,
+ // e.g. string -> []byte/[]rune.
+ }
+
+ // A representation-changing conversion?
+ // At least one of {ut_src,ut_dst} must be *Basic.
+ // (The other may be []byte or []rune.)
+ _, ok1 := ut_src.(*types.Basic)
+ _, ok2 := ut_dst.(*types.Basic)
+ if ok1 || ok2 {
+ c := &Convert{X: val}
+ c.setType(typ)
+ return f.emit(c)
+ }
+
+ panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), typ))
+}
+
+// emitStore emits to f an instruction to store value val at location
+// addr, applying implicit conversions as required by assignability rules.
+//
+func emitStore(f *Function, addr, val Value, pos token.Pos) *Store {
+ s := &Store{
+ Addr: addr,
+ Val: emitConv(f, val, deref(addr.Type())),
+ pos: pos,
+ }
+ f.emit(s)
+ return s
+}
+
+// emitJump emits to f a jump to target, and updates the control-flow graph.
+// Postcondition: f.currentBlock is nil.
+//
+func emitJump(f *Function, target *BasicBlock) {
+ b := f.currentBlock
+ b.emit(new(Jump))
+ addEdge(b, target)
+ f.currentBlock = nil
+}
+
+// emitIf emits to f a conditional jump to tblock or fblock based on
+// cond, and updates the control-flow graph.
+// Postcondition: f.currentBlock is nil.
+//
+func emitIf(f *Function, cond Value, tblock, fblock *BasicBlock) {
+ b := f.currentBlock
+ b.emit(&If{Cond: cond})
+ addEdge(b, tblock)
+ addEdge(b, fblock)
+ f.currentBlock = nil
+}
+
+// emitExtract emits to f an instruction to extract the index'th
+// component of tuple. It returns the extracted value.
+//
+func emitExtract(f *Function, tuple Value, index int) Value {
+ e := &Extract{Tuple: tuple, Index: index}
+ e.setType(tuple.Type().(*types.Tuple).At(index).Type())
+ return f.emit(e)
+}
+
+// emitTypeAssert emits to f a type assertion value := x.(t) and
+// returns the value. x.Type() must be an interface.
+//
+func emitTypeAssert(f *Function, x Value, t types.Type, pos token.Pos) Value {
+ a := &TypeAssert{X: x, AssertedType: t}
+ a.setPos(pos)
+ a.setType(t)
+ return f.emit(a)
+}
+
+// emitTypeTest emits to f a type test value,ok := x.(t) and returns
+// a (value, ok) tuple. x.Type() must be an interface.
+//
+func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value {
+ a := &TypeAssert{
+ X: x,
+ AssertedType: t,
+ CommaOk: true,
+ }
+ a.setPos(pos)
+ a.setType(types.NewTuple(
+ newVar("value", t),
+ varOk,
+ ))
+ return f.emit(a)
+}
+
+// emitTailCall emits to f a function call in tail position. The
+// caller is responsible for all fields of 'call' except its type.
+// Intended for wrapper methods.
+// Precondition: f does/will not use deferred procedure calls.
+// Postcondition: f.currentBlock is nil.
+//
+func emitTailCall(f *Function, call *Call) {
+ tresults := f.Signature.Results()
+ nr := tresults.Len()
+ if nr == 1 {
+ call.typ = tresults.At(0).Type()
+ } else {
+ call.typ = tresults
+ }
+ tuple := f.emit(call)
+ var ret Return
+ switch nr {
+ case 0:
+ // no-op
+ case 1:
+ ret.Results = []Value{tuple}
+ default:
+ for i := 0; i < nr; i++ {
+ v := emitExtract(f, tuple, i)
+ // TODO(adonovan): in principle, this is required:
+ // v = emitConv(f, o.Type, f.Signature.Results[i].Type)
+ // but in practice emitTailCall is only used when
+ // the types exactly match.
+ ret.Results = append(ret.Results, v)
+ }
+ }
+ f.emit(&ret)
+ f.currentBlock = nil
+}
+
+// emitImplicitSelections emits to f code to apply the sequence of
+// implicit field selections specified by indices to base value v, and
+// returns the selected value.
+//
+// If v is the address of a struct, the result will be the address of
+// a field; if it is the value of a struct, the result will be the
+// value of a field.
+//
+func emitImplicitSelections(f *Function, v Value, indices []int) Value {
+ for _, index := range indices {
+ fld := deref(v.Type()).Underlying().(*types.Struct).Field(index)
+
+ if isPointer(v.Type()) {
+ instr := &FieldAddr{
+ X: v,
+ Field: index,
+ }
+ instr.setType(types.NewPointer(fld.Type()))
+ v = f.emit(instr)
+ // Load the field's value iff indirectly embedded.
+ if isPointer(fld.Type()) {
+ v = emitLoad(f, v)
+ }
+ } else {
+ instr := &Field{
+ X: v,
+ Field: index,
+ }
+ instr.setType(fld.Type())
+ v = f.emit(instr)
+ }
+ }
+ return v
+}
+
+// emitFieldSelection emits to f code to select the index'th field of v.
+//
+// If wantAddr, the input must be a pointer-to-struct and the result
+// will be the field's address; otherwise the result will be the
+// field's value.
+// Ident id is used for position and debug info.
+//
+func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value {
+ fld := deref(v.Type()).Underlying().(*types.Struct).Field(index)
+ if isPointer(v.Type()) {
+ instr := &FieldAddr{
+ X: v,
+ Field: index,
+ }
+ instr.setPos(id.Pos())
+ instr.setType(types.NewPointer(fld.Type()))
+ v = f.emit(instr)
+ // Load the field's value iff we don't want its address.
+ if !wantAddr {
+ v = emitLoad(f, v)
+ }
+ } else {
+ instr := &Field{
+ X: v,
+ Field: index,
+ }
+ instr.setPos(id.Pos())
+ instr.setType(fld.Type())
+ v = f.emit(instr)
+ }
+ emitDebugRef(f, id, v, wantAddr)
+ return v
+}
+
+// zeroValue emits to f code to produce a zero value of type t,
+// and returns it.
+//
+func zeroValue(f *Function, t types.Type) Value {
+ switch t.Underlying().(type) {
+ case *types.Struct, *types.Array:
+ return emitLoad(f, f.addLocal(t, token.NoPos))
+ default:
+ return zeroConst(t)
+ }
+}
+
+// createRecoverBlock emits to f a block of code to return after a
+// recovered panic, and sets f.Recover to it.
+//
+// If f's result parameters are named, the code loads and returns
+// their current values, otherwise it returns the zero values of their
+// type.
+//
+// Idempotent.
+//
+func createRecoverBlock(f *Function) {
+ if f.Recover != nil {
+ return // already created
+ }
+ saved := f.currentBlock
+
+ f.Recover = f.newBasicBlock("recover")
+ f.currentBlock = f.Recover
+
+ var results []Value
+ if f.namedResults != nil {
+ // Reload NRPs to form value tuple.
+ for _, r := range f.namedResults {
+ results = append(results, emitLoad(f, r))
+ }
+ } else {
+ R := f.Signature.Results()
+ for i, n := 0, R.Len(); i < n; i++ {
+ T := R.At(i).Type()
+
+ // Return zero value of each result type.
+ results = append(results, zeroValue(f, T))
+ }
+ }
+ f.emit(&Return{Results: results})
+
+ f.currentBlock = saved
+}
diff --git a/vendor/honnef.co/go/tools/ssa/func.go b/vendor/honnef.co/go/tools/ssa/func.go
new file mode 100644
index 000000000..222eea641
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/func.go
@@ -0,0 +1,765 @@
+// 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 ssa
+
+// This file implements the Function and BasicBlock types.
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+ "io"
+ "os"
+ "strings"
+)
+
+// addEdge adds a control-flow graph edge from from to to.
+func addEdge(from, to *BasicBlock) {
+ from.Succs = append(from.Succs, to)
+ to.Preds = append(to.Preds, from)
+}
+
+// Parent returns the function that contains block b.
+func (b *BasicBlock) Parent() *Function { return b.parent }
+
+// String returns a human-readable label of this block.
+// It is not guaranteed unique within the function.
+//
+func (b *BasicBlock) String() string {
+ return fmt.Sprintf("%d", b.Index)
+}
+
+// emit appends an instruction to the current basic block.
+// If the instruction defines a Value, it is returned.
+//
+func (b *BasicBlock) emit(i Instruction) Value {
+ i.setBlock(b)
+ b.Instrs = append(b.Instrs, i)
+ v, _ := i.(Value)
+ return v
+}
+
+// predIndex returns the i such that b.Preds[i] == c or panics if
+// there is none.
+func (b *BasicBlock) predIndex(c *BasicBlock) int {
+ for i, pred := range b.Preds {
+ if pred == c {
+ return i
+ }
+ }
+ panic(fmt.Sprintf("no edge %s -> %s", c, b))
+}
+
+// hasPhi returns true if b.Instrs contains φ-nodes.
+func (b *BasicBlock) hasPhi() bool {
+ _, ok := b.Instrs[0].(*Phi)
+ return ok
+}
+
+func (b *BasicBlock) Phis() []Instruction {
+ return b.phis()
+}
+
+// phis returns the prefix of b.Instrs containing all the block's φ-nodes.
+func (b *BasicBlock) phis() []Instruction {
+ for i, instr := range b.Instrs {
+ if _, ok := instr.(*Phi); !ok {
+ return b.Instrs[:i]
+ }
+ }
+ return nil // unreachable in well-formed blocks
+}
+
+// replacePred replaces all occurrences of p in b's predecessor list with q.
+// Ordinarily there should be at most one.
+//
+func (b *BasicBlock) replacePred(p, q *BasicBlock) {
+ for i, pred := range b.Preds {
+ if pred == p {
+ b.Preds[i] = q
+ }
+ }
+}
+
+// replaceSucc replaces all occurrences of p in b's successor list with q.
+// Ordinarily there should be at most one.
+//
+func (b *BasicBlock) replaceSucc(p, q *BasicBlock) {
+ for i, succ := range b.Succs {
+ if succ == p {
+ b.Succs[i] = q
+ }
+ }
+}
+
+func (b *BasicBlock) RemovePred(p *BasicBlock) {
+ b.removePred(p)
+}
+
+// removePred removes all occurrences of p in b's
+// predecessor list and φ-nodes.
+// Ordinarily there should be at most one.
+//
+func (b *BasicBlock) removePred(p *BasicBlock) {
+ phis := b.phis()
+
+ // We must preserve edge order for φ-nodes.
+ j := 0
+ for i, pred := range b.Preds {
+ if pred != p {
+ b.Preds[j] = b.Preds[i]
+ // Strike out φ-edge too.
+ for _, instr := range phis {
+ phi := instr.(*Phi)
+ phi.Edges[j] = phi.Edges[i]
+ }
+ j++
+ }
+ }
+ // Nil out b.Preds[j:] and φ-edges[j:] to aid GC.
+ for i := j; i < len(b.Preds); i++ {
+ b.Preds[i] = nil
+ for _, instr := range phis {
+ instr.(*Phi).Edges[i] = nil
+ }
+ }
+ b.Preds = b.Preds[:j]
+ for _, instr := range phis {
+ phi := instr.(*Phi)
+ phi.Edges = phi.Edges[:j]
+ }
+}
+
+// Destinations associated with unlabelled for/switch/select stmts.
+// We push/pop one of these as we enter/leave each construct and for
+// each BranchStmt we scan for the innermost target of the right type.
+//
+type targets struct {
+ tail *targets // rest of stack
+ _break *BasicBlock
+ _continue *BasicBlock
+ _fallthrough *BasicBlock
+}
+
+// Destinations associated with a labelled block.
+// We populate these as labels are encountered in forward gotos or
+// labelled statements.
+//
+type lblock struct {
+ _goto *BasicBlock
+ _break *BasicBlock
+ _continue *BasicBlock
+}
+
+// labelledBlock returns the branch target associated with the
+// specified label, creating it if needed.
+//
+func (f *Function) labelledBlock(label *ast.Ident) *lblock {
+ lb := f.lblocks[label.Obj]
+ if lb == nil {
+ lb = &lblock{_goto: f.newBasicBlock(label.Name)}
+ if f.lblocks == nil {
+ f.lblocks = make(map[*ast.Object]*lblock)
+ }
+ f.lblocks[label.Obj] = lb
+ }
+ return lb
+}
+
+// addParam adds a (non-escaping) parameter to f.Params of the
+// specified name, type and source position.
+//
+func (f *Function) addParam(name string, typ types.Type, pos token.Pos) *Parameter {
+ v := &Parameter{
+ name: name,
+ typ: typ,
+ pos: pos,
+ parent: f,
+ }
+ f.Params = append(f.Params, v)
+ return v
+}
+
+func (f *Function) addParamObj(obj types.Object) *Parameter {
+ name := obj.Name()
+ if name == "" {
+ name = fmt.Sprintf("arg%d", len(f.Params))
+ }
+ param := f.addParam(name, obj.Type(), obj.Pos())
+ param.object = obj
+ return param
+}
+
+// addSpilledParam declares a parameter that is pre-spilled to the
+// stack; the function body will load/store the spilled location.
+// Subsequent lifting will eliminate spills where possible.
+//
+func (f *Function) addSpilledParam(obj types.Object) {
+ param := f.addParamObj(obj)
+ spill := &Alloc{Comment: obj.Name()}
+ spill.setType(types.NewPointer(obj.Type()))
+ spill.setPos(obj.Pos())
+ f.objects[obj] = spill
+ f.Locals = append(f.Locals, spill)
+ f.emit(spill)
+ f.emit(&Store{Addr: spill, Val: param})
+}
+
+// startBody initializes the function prior to generating SSA code for its body.
+// Precondition: f.Type() already set.
+//
+func (f *Function) startBody() {
+ f.currentBlock = f.newBasicBlock("entry")
+ f.objects = make(map[types.Object]Value) // needed for some synthetics, e.g. init
+}
+
+// createSyntacticParams populates f.Params and generates code (spills
+// and named result locals) for all the parameters declared in the
+// syntax. In addition it populates the f.objects mapping.
+//
+// Preconditions:
+// f.startBody() was called.
+// Postcondition:
+// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)
+//
+func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.FuncType) {
+ // Receiver (at most one inner iteration).
+ if recv != nil {
+ for _, field := range recv.List {
+ for _, n := range field.Names {
+ f.addSpilledParam(f.Pkg.info.Defs[n])
+ }
+ // Anonymous receiver? No need to spill.
+ if field.Names == nil {
+ f.addParamObj(f.Signature.Recv())
+ }
+ }
+ }
+
+ // Parameters.
+ if functype.Params != nil {
+ n := len(f.Params) // 1 if has recv, 0 otherwise
+ for _, field := range functype.Params.List {
+ for _, n := range field.Names {
+ f.addSpilledParam(f.Pkg.info.Defs[n])
+ }
+ // Anonymous parameter? No need to spill.
+ if field.Names == nil {
+ f.addParamObj(f.Signature.Params().At(len(f.Params) - n))
+ }
+ }
+ }
+
+ // Named results.
+ if functype.Results != nil {
+ for _, field := range functype.Results.List {
+ // Implicit "var" decl of locals for named results.
+ for _, n := range field.Names {
+ f.namedResults = append(f.namedResults, f.addLocalForIdent(n))
+ }
+ }
+ }
+}
+
+// numberRegisters assigns numbers to all SSA registers
+// (value-defining Instructions) in f, to aid debugging.
+// (Non-Instruction Values are named at construction.)
+//
+func numberRegisters(f *Function) {
+ v := 0
+ for _, b := range f.Blocks {
+ for _, instr := range b.Instrs {
+ switch instr.(type) {
+ case Value:
+ instr.(interface {
+ setNum(int)
+ }).setNum(v)
+ v++
+ }
+ }
+ }
+}
+
+// buildReferrers populates the def/use information in all non-nil
+// Value.Referrers slice.
+// Precondition: all such slices are initially empty.
+func buildReferrers(f *Function) {
+ var rands []*Value
+ for _, b := range f.Blocks {
+ for _, instr := range b.Instrs {
+ rands = instr.Operands(rands[:0]) // recycle storage
+ for _, rand := range rands {
+ if r := *rand; r != nil {
+ if ref := r.Referrers(); ref != nil {
+ *ref = append(*ref, instr)
+ }
+ }
+ }
+ }
+ }
+}
+
+// finishBody() finalizes the function after SSA code generation of its body.
+func (f *Function) finishBody() {
+ f.objects = nil
+ f.currentBlock = nil
+ f.lblocks = nil
+
+ // Don't pin the AST in memory (except in debug mode).
+ if n := f.syntax; n != nil && !f.debugInfo() {
+ f.syntax = extentNode{n.Pos(), n.End()}
+ }
+
+ // Remove from f.Locals any Allocs that escape to the heap.
+ j := 0
+ for _, l := range f.Locals {
+ if !l.Heap {
+ f.Locals[j] = l
+ j++
+ }
+ }
+ // Nil out f.Locals[j:] to aid GC.
+ for i := j; i < len(f.Locals); i++ {
+ f.Locals[i] = nil
+ }
+ 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)
+
+ buildDomTree(f)
+
+ if f.Prog.mode&NaiveForm == 0 {
+ // For debugging pre-state of lifting pass:
+ // numberRegisters(f)
+ // f.WriteTo(os.Stderr)
+ lift(f)
+ }
+
+ f.namedResults = nil // (used by lifting)
+
+ numberRegisters(f)
+
+ if f.Prog.mode&PrintFunctions != 0 {
+ printMu.Lock()
+ f.WriteTo(os.Stdout)
+ printMu.Unlock()
+ }
+
+ if f.Prog.mode&SanityCheckFunctions != 0 {
+ mustSanityCheck(f, nil)
+ }
+}
+
+func (f *Function) RemoveNilBlocks() {
+ f.removeNilBlocks()
+}
+
+// removeNilBlocks eliminates nils from f.Blocks and updates each
+// BasicBlock.Index. Use this after any pass that may delete blocks.
+//
+func (f *Function) removeNilBlocks() {
+ j := 0
+ for _, b := range f.Blocks {
+ if b != nil {
+ b.Index = j
+ f.Blocks[j] = b
+ j++
+ }
+ }
+ // Nil out f.Blocks[j:] to aid GC.
+ for i := j; i < len(f.Blocks); i++ {
+ f.Blocks[i] = nil
+ }
+ f.Blocks = f.Blocks[:j]
+}
+
+// SetDebugMode sets the debug mode for package pkg. If true, all its
+// functions will include full debug info. This greatly increases the
+// size of the instruction stream, and causes Functions to depend upon
+// the ASTs, potentially keeping them live in memory for longer.
+//
+func (pkg *Package) SetDebugMode(debug bool) {
+ // TODO(adonovan): do we want ast.File granularity?
+ pkg.debug = debug
+}
+
+// debugInfo reports whether debug info is wanted for this function.
+func (f *Function) debugInfo() bool {
+ return f.Pkg != nil && f.Pkg.debug
+}
+
+// addNamedLocal creates a local variable, adds it to function f and
+// returns it. Its name and type are taken from obj. Subsequent
+// calls to f.lookup(obj) will return the same local.
+//
+func (f *Function) addNamedLocal(obj types.Object) *Alloc {
+ l := f.addLocal(obj.Type(), obj.Pos())
+ l.Comment = obj.Name()
+ f.objects[obj] = l
+ return l
+}
+
+func (f *Function) addLocalForIdent(id *ast.Ident) *Alloc {
+ return f.addNamedLocal(f.Pkg.info.Defs[id])
+}
+
+// addLocal creates an anonymous local variable of type typ, adds it
+// to function f and returns it. pos is the optional source location.
+//
+func (f *Function) addLocal(typ types.Type, pos token.Pos) *Alloc {
+ v := &Alloc{}
+ v.setType(types.NewPointer(typ))
+ v.setPos(pos)
+ f.Locals = append(f.Locals, v)
+ f.emit(v)
+ return v
+}
+
+// lookup returns the address of the named variable identified by obj
+// that is local to function f or one of its enclosing functions.
+// If escaping, the reference comes from a potentially escaping pointer
+// expression and the referent must be heap-allocated.
+//
+func (f *Function) lookup(obj types.Object, escaping bool) Value {
+ if v, ok := f.objects[obj]; ok {
+ if alloc, ok := v.(*Alloc); ok && escaping {
+ alloc.Heap = true
+ }
+ return v // function-local var (address)
+ }
+
+ // Definition must be in an enclosing function;
+ // plumb it through intervening closures.
+ if f.parent == nil {
+ panic("no ssa.Value for " + obj.String())
+ }
+ outer := f.parent.lookup(obj, true) // escaping
+ v := &FreeVar{
+ name: obj.Name(),
+ typ: outer.Type(),
+ pos: outer.Pos(),
+ outer: outer,
+ parent: f,
+ }
+ f.objects[obj] = v
+ f.FreeVars = append(f.FreeVars, v)
+ return v
+}
+
+// emit emits the specified instruction to function f.
+func (f *Function) emit(instr Instruction) Value {
+ return f.currentBlock.emit(instr)
+}
+
+// RelString returns the full name of this function, qualified by
+// package name, receiver type, etc.
+//
+// The specific formatting rules are not guaranteed and may change.
+//
+// Examples:
+// "math.IsNaN" // a package-level function
+// "(*bytes.Buffer).Bytes" // a declared method or a wrapper
+// "(*bytes.Buffer).Bytes$thunk" // thunk (func wrapping method; receiver is param 0)
+// "(*bytes.Buffer).Bytes$bound" // bound (func wrapping method; receiver supplied by closure)
+// "main.main$1" // an anonymous function in main
+// "main.init#1" // a declared init function
+// "main.init" // the synthesized package initializer
+//
+// When these functions are referred to from within the same package
+// (i.e. from == f.Pkg.Object), they are rendered without the package path.
+// For example: "IsNaN", "(*Buffer).Bytes", etc.
+//
+// All non-synthetic functions have distinct package-qualified names.
+// (But two methods may have the same name "(T).f" if one is a synthetic
+// wrapper promoting a non-exported method "f" from another package; in
+// that case, the strings are equal but the identifiers "f" are distinct.)
+//
+func (f *Function) RelString(from *types.Package) string {
+ // Anonymous?
+ if f.parent != nil {
+ // An anonymous function's Name() looks like "parentName$1",
+ // but its String() should include the type/package/etc.
+ parent := f.parent.RelString(from)
+ for i, anon := range f.parent.AnonFuncs {
+ if anon == f {
+ return fmt.Sprintf("%s$%d", parent, 1+i)
+ }
+ }
+
+ return f.name // should never happen
+ }
+
+ // Method (declared or wrapper)?
+ if recv := f.Signature.Recv(); recv != nil {
+ return f.relMethod(from, recv.Type())
+ }
+
+ // Thunk?
+ if f.method != nil {
+ return f.relMethod(from, f.method.Recv())
+ }
+
+ // Bound?
+ if len(f.FreeVars) == 1 && strings.HasSuffix(f.name, "$bound") {
+ return f.relMethod(from, f.FreeVars[0].Type())
+ }
+
+ // Package-level function?
+ // Prefix with package name for cross-package references only.
+ if p := f.pkg(); p != nil && p != from {
+ return fmt.Sprintf("%s.%s", p.Path(), f.name)
+ }
+
+ // Unknown.
+ return f.name
+}
+
+func (f *Function) relMethod(from *types.Package, recv types.Type) string {
+ return fmt.Sprintf("(%s).%s", relType(recv, from), f.name)
+}
+
+// writeSignature writes to buf the signature sig in declaration syntax.
+func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *types.Signature, params []*Parameter) {
+ buf.WriteString("func ")
+ if recv := sig.Recv(); recv != nil {
+ buf.WriteString("(")
+ if n := params[0].Name(); n != "" {
+ buf.WriteString(n)
+ buf.WriteString(" ")
+ }
+ types.WriteType(buf, params[0].Type(), types.RelativeTo(from))
+ buf.WriteString(") ")
+ }
+ buf.WriteString(name)
+ types.WriteSignature(buf, sig, types.RelativeTo(from))
+}
+
+func (f *Function) pkg() *types.Package {
+ if f.Pkg != nil {
+ return f.Pkg.Pkg
+ }
+ return nil
+}
+
+var _ io.WriterTo = (*Function)(nil) // *Function implements io.Writer
+
+func (f *Function) WriteTo(w io.Writer) (int64, error) {
+ var buf bytes.Buffer
+ WriteFunction(&buf, f)
+ n, err := w.Write(buf.Bytes())
+ return int64(n), err
+}
+
+// WriteFunction writes to buf a human-readable "disassembly" of f.
+func WriteFunction(buf *bytes.Buffer, f *Function) {
+ fmt.Fprintf(buf, "# Name: %s\n", f.String())
+ if f.Pkg != nil {
+ fmt.Fprintf(buf, "# Package: %s\n", f.Pkg.Pkg.Path())
+ }
+ if syn := f.Synthetic; syn != "" {
+ fmt.Fprintln(buf, "# Synthetic:", syn)
+ }
+ if pos := f.Pos(); pos.IsValid() {
+ fmt.Fprintf(buf, "# Location: %s\n", f.Prog.Fset.Position(pos))
+ }
+
+ if f.parent != nil {
+ fmt.Fprintf(buf, "# Parent: %s\n", f.parent.Name())
+ }
+
+ if f.Recover != nil {
+ fmt.Fprintf(buf, "# Recover: %s\n", f.Recover)
+ }
+
+ from := f.pkg()
+
+ if f.FreeVars != nil {
+ buf.WriteString("# Free variables:\n")
+ for i, fv := range f.FreeVars {
+ fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), from))
+ }
+ }
+
+ if len(f.Locals) > 0 {
+ buf.WriteString("# Locals:\n")
+ for i, l := range f.Locals {
+ fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), from))
+ }
+ }
+ writeSignature(buf, from, f.Name(), f.Signature, f.Params)
+ buf.WriteString(":\n")
+
+ if f.Blocks == nil {
+ buf.WriteString("\t(external)\n")
+ }
+
+ // NB. column calculations are confused by non-ASCII
+ // characters and assume 8-space tabs.
+ const punchcard = 80 // for old time's sake.
+ const tabwidth = 8
+ for _, b := range f.Blocks {
+ if b == nil {
+ // Corrupt CFG.
+ fmt.Fprintf(buf, ".nil:\n")
+ continue
+ }
+ n, _ := fmt.Fprintf(buf, "%d:", b.Index)
+ bmsg := fmt.Sprintf("%s P:%d S:%d", b.Comment, len(b.Preds), len(b.Succs))
+ fmt.Fprintf(buf, "%*s%s\n", punchcard-1-n-len(bmsg), "", bmsg)
+
+ if false { // CFG debugging
+ fmt.Fprintf(buf, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs)
+ }
+ for _, instr := range b.Instrs {
+ buf.WriteString("\t")
+ switch v := instr.(type) {
+ case Value:
+ l := punchcard - tabwidth
+ // Left-align the instruction.
+ if name := v.Name(); name != "" {
+ n, _ := fmt.Fprintf(buf, "%s = ", name)
+ l -= n
+ }
+ n, _ := buf.WriteString(instr.String())
+ l -= n
+ // Right-align the type if there's space.
+ if t := v.Type(); t != nil {
+ buf.WriteByte(' ')
+ ts := relType(t, from)
+ l -= len(ts) + len(" ") // (spaces before and after type)
+ if l > 0 {
+ fmt.Fprintf(buf, "%*s", l, "")
+ }
+ buf.WriteString(ts)
+ }
+ case nil:
+ // Be robust against bad transforms.
+ buf.WriteString("")
+ default:
+ buf.WriteString(instr.String())
+ }
+ buf.WriteString("\n")
+ }
+ }
+ fmt.Fprintf(buf, "\n")
+}
+
+// newBasicBlock adds to f a new basic block and returns it. It does
+// not automatically become the current block for subsequent calls to emit.
+// comment is an optional string for more readable debugging output.
+//
+func (f *Function) newBasicBlock(comment string) *BasicBlock {
+ b := &BasicBlock{
+ Index: len(f.Blocks),
+ Comment: comment,
+ parent: f,
+ }
+ b.Succs = b.succs2[:0]
+ f.Blocks = append(f.Blocks, b)
+ return b
+}
+
+// NewFunction returns a new synthetic Function instance belonging to
+// prog, with its name and signature fields set as specified.
+//
+// The caller is responsible for initializing the remaining fields of
+// the function object, e.g. Pkg, Params, Blocks.
+//
+// It is practically impossible for clients to construct well-formed
+// SSA functions/packages/programs directly, so we assume this is the
+// job of the Builder alone. NewFunction exists to provide clients a
+// little flexibility. For example, analysis tools may wish to
+// construct fake Functions for the root of the callgraph, a fake
+// "reflect" package, etc.
+//
+// TODO(adonovan): think harder about the API here.
+//
+func (prog *Program) NewFunction(name string, sig *types.Signature, provenance string) *Function {
+ return &Function{Prog: prog, name: name, Signature: sig, Synthetic: provenance}
+}
+
+type extentNode [2]token.Pos
+
+func (n extentNode) Pos() token.Pos { return n[0] }
+func (n extentNode) End() token.Pos { return n[1] }
+
+// Syntax returns an ast.Node whose Pos/End methods provide the
+// lexical extent of the function if it was defined by Go source code
+// (f.Synthetic==""), or nil otherwise.
+//
+// If f was built with debug information (see Package.SetDebugRef),
+// the result is the *ast.FuncDecl or *ast.FuncLit that declared the
+// function. Otherwise, it is an opaque Node providing only position
+// information; this avoids pinning the AST in memory.
+//
+func (f *Function) Syntax() ast.Node { return f.syntax }
diff --git a/vendor/honnef.co/go/tools/ssa/identical.go b/vendor/honnef.co/go/tools/ssa/identical.go
new file mode 100644
index 000000000..53cbee107
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/identical.go
@@ -0,0 +1,7 @@
+// +build go1.8
+
+package ssa
+
+import "go/types"
+
+var structTypesIdentical = types.IdenticalIgnoreTags
diff --git a/vendor/honnef.co/go/tools/ssa/identical_17.go b/vendor/honnef.co/go/tools/ssa/identical_17.go
new file mode 100644
index 000000000..da89d3339
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/identical_17.go
@@ -0,0 +1,7 @@
+// +build !go1.8
+
+package ssa
+
+import "go/types"
+
+var structTypesIdentical = types.Identical
diff --git a/vendor/honnef.co/go/tools/ssa/lift.go b/vendor/honnef.co/go/tools/ssa/lift.go
new file mode 100644
index 000000000..531358fa3
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/lift.go
@@ -0,0 +1,657 @@
+// 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 ssa
+
+// This file defines the lifting pass which tries to "lift" Alloc
+// cells (new/local variables) into SSA registers, replacing loads
+// with the dominating stored value, eliminating loads and stores, and
+// inserting φ-nodes as needed.
+
+// Cited papers and resources:
+//
+// Ron Cytron et al. 1991. Efficiently computing SSA form...
+// http://doi.acm.org/10.1145/115372.115320
+//
+// Cooper, Harvey, Kennedy. 2001. A Simple, Fast Dominance Algorithm.
+// Software Practice and Experience 2001, 4:1-10.
+// http://www.hipersoft.rice.edu/grads/publications/dom14.pdf
+//
+// Daniel Berlin, llvmdev mailing list, 2012.
+// http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-January/046638.html
+// (Be sure to expand the whole thread.)
+
+// TODO(adonovan): opt: there are many optimizations worth evaluating, and
+// the conventional wisdom for SSA construction is that a simple
+// algorithm well engineered often beats those of better asymptotic
+// complexity on all but the most egregious inputs.
+//
+// Danny Berlin suggests that the Cooper et al. algorithm for
+// computing the dominance frontier is superior to Cytron et al.
+// Furthermore he recommends that rather than computing the DF for the
+// whole function then renaming all alloc cells, it may be cheaper to
+// compute the DF for each alloc cell separately and throw it away.
+//
+// Consider exploiting liveness information to avoid creating dead
+// φ-nodes which we then immediately remove.
+//
+// Also see many other "TODO: opt" suggestions in the code.
+
+import (
+ "fmt"
+ "go/token"
+ "go/types"
+ "math/big"
+ "os"
+)
+
+// If true, show diagnostic information at each step of lifting.
+// Very verbose.
+const debugLifting = false
+
+// domFrontier maps each block to the set of blocks in its dominance
+// frontier. The outer slice is conceptually a map keyed by
+// Block.Index. The inner slice is conceptually a set, possibly
+// containing duplicates.
+//
+// TODO(adonovan): opt: measure impact of dups; consider a packed bit
+// representation, e.g. big.Int, and bitwise parallel operations for
+// the union step in the Children loop.
+//
+// domFrontier's methods mutate the slice's elements but not its
+// length, so their receivers needn't be pointers.
+//
+type domFrontier [][]*BasicBlock
+
+func (df domFrontier) add(u, v *BasicBlock) {
+ p := &df[u.Index]
+ *p = append(*p, v)
+}
+
+// build builds the dominance frontier df for the dominator (sub)tree
+// rooted at u, using the Cytron et al. algorithm.
+//
+// TODO(adonovan): opt: consider Berlin approach, computing pruned SSA
+// by pruning the entire IDF computation, rather than merely pruning
+// the DF -> IDF step.
+func (df domFrontier) build(u *BasicBlock) {
+ // Encounter each node u in postorder of dom tree.
+ for _, child := range u.dom.children {
+ df.build(child)
+ }
+ for _, vb := range u.Succs {
+ if v := vb.dom; v.idom != u {
+ df.add(u, vb)
+ }
+ }
+ for _, w := range u.dom.children {
+ for _, vb := range df[w.Index] {
+ // TODO(adonovan): opt: use word-parallel bitwise union.
+ if v := vb.dom; v.idom != u {
+ df.add(u, vb)
+ }
+ }
+ }
+}
+
+func buildDomFrontier(fn *Function) domFrontier {
+ df := make(domFrontier, len(fn.Blocks))
+ df.build(fn.Blocks[0])
+ if fn.Recover != nil {
+ df.build(fn.Recover)
+ }
+ return df
+}
+
+func removeInstr(refs []Instruction, instr Instruction) []Instruction {
+ i := 0
+ for _, ref := range refs {
+ if ref == instr {
+ continue
+ }
+ refs[i] = ref
+ i++
+ }
+ for j := i; j != len(refs); j++ {
+ refs[j] = nil // aid GC
+ }
+ return refs[:i]
+}
+
+// lift replaces local and new Allocs accessed only with
+// load/store by SSA registers, inserting φ-nodes where necessary.
+// The result is a program in classical pruned SSA form.
+//
+// Preconditions:
+// - fn has no dead blocks (blockopt has run).
+// - Def/use info (Operands and Referrers) is up-to-date.
+// - The dominator tree is up-to-date.
+//
+func lift(fn *Function) {
+ // TODO(adonovan): opt: lots of little optimizations may be
+ // worthwhile here, especially if they cause us to avoid
+ // buildDomFrontier. For example:
+ //
+ // - Alloc never loaded? Eliminate.
+ // - Alloc never stored? Replace all loads with a zero constant.
+ // - Alloc stored once? Replace loads with dominating store;
+ // don't forget that an Alloc is itself an effective store
+ // of zero.
+ // - Alloc used only within a single block?
+ // Use degenerate algorithm avoiding φ-nodes.
+ // - Consider synergy with scalar replacement of aggregates (SRA).
+ // e.g. *(&x.f) where x is an Alloc.
+ // Perhaps we'd get better results if we generated this as x.f
+ // i.e. Field(x, .f) instead of Load(FieldIndex(x, .f)).
+ // Unclear.
+ //
+ // But we will start with the simplest correct code.
+ df := buildDomFrontier(fn)
+
+ if debugLifting {
+ title := false
+ for i, blocks := range df {
+ if blocks != nil {
+ if !title {
+ fmt.Fprintf(os.Stderr, "Dominance frontier of %s:\n", fn)
+ title = true
+ }
+ fmt.Fprintf(os.Stderr, "\t%s: %s\n", fn.Blocks[i], blocks)
+ }
+ }
+ }
+
+ newPhis := make(newPhiMap)
+
+ // During this pass we will replace some BasicBlock.Instrs
+ // (allocs, loads and stores) with nil, keeping a count in
+ // BasicBlock.gaps. At the end we will reset Instrs to the
+ // concatenation of all non-dead newPhis and non-nil Instrs
+ // for the block, reusing the original array if space permits.
+
+ // While we're here, we also eliminate 'rundefers'
+ // instructions in functions that contain no 'defer'
+ // instructions.
+ usesDefer := false
+
+ // A counter used to generate ~unique ids for Phi nodes, as an
+ // aid to debugging. We use large numbers to make them highly
+ // visible. All nodes are renumbered later.
+ fresh := 1000
+
+ // Determine which allocs we can lift and number them densely.
+ // The renaming phase uses this numbering for compact maps.
+ numAllocs := 0
+ for _, b := range fn.Blocks {
+ b.gaps = 0
+ b.rundefers = 0
+ for _, instr := range b.Instrs {
+ switch instr := instr.(type) {
+ case *Alloc:
+ index := -1
+ if liftAlloc(df, instr, newPhis, &fresh) {
+ index = numAllocs
+ numAllocs++
+ }
+ instr.index = index
+ case *Defer:
+ usesDefer = true
+ case *RunDefers:
+ b.rundefers++
+ }
+ }
+ }
+
+ // renaming maps an alloc (keyed by index) to its replacement
+ // value. Initially the renaming contains nil, signifying the
+ // zero constant of the appropriate type; we construct the
+ // Const lazily at most once on each path through the domtree.
+ // TODO(adonovan): opt: cache per-function not per subtree.
+ renaming := make([]Value, numAllocs)
+
+ // Renaming.
+ rename(fn.Blocks[0], renaming, newPhis)
+
+ // Eliminate dead φ-nodes.
+ removeDeadPhis(fn.Blocks, newPhis)
+
+ // Prepend remaining live φ-nodes to each block.
+ for _, b := range fn.Blocks {
+ nps := newPhis[b]
+ j := len(nps)
+
+ rundefersToKill := b.rundefers
+ if usesDefer {
+ rundefersToKill = 0
+ }
+
+ if j+b.gaps+rundefersToKill == 0 {
+ continue // fast path: no new phis or gaps
+ }
+
+ // Compact nps + non-nil Instrs into a new slice.
+ // TODO(adonovan): opt: compact in situ (rightwards)
+ // if Instrs has sufficient space or slack.
+ dst := make([]Instruction, len(b.Instrs)+j-b.gaps-rundefersToKill)
+ for i, np := range nps {
+ dst[i] = np.phi
+ }
+ for _, instr := range b.Instrs {
+ if instr == nil {
+ continue
+ }
+ if !usesDefer {
+ if _, ok := instr.(*RunDefers); ok {
+ continue
+ }
+ }
+ dst[j] = instr
+ j++
+ }
+ b.Instrs = dst
+ }
+
+ // Remove any fn.Locals that were lifted.
+ j := 0
+ for _, l := range fn.Locals {
+ if l.index < 0 {
+ fn.Locals[j] = l
+ j++
+ }
+ }
+ // Nil out fn.Locals[j:] to aid GC.
+ for i := j; i < len(fn.Locals); i++ {
+ fn.Locals[i] = nil
+ }
+ fn.Locals = fn.Locals[:j]
+}
+
+// removeDeadPhis removes φ-nodes not transitively needed by a
+// non-Phi, non-DebugRef instruction.
+func removeDeadPhis(blocks []*BasicBlock, newPhis newPhiMap) {
+ // First pass: find the set of "live" φ-nodes: those reachable
+ // from some non-Phi instruction.
+ //
+ // We compute reachability in reverse, starting from each φ,
+ // rather than forwards, starting from each live non-Phi
+ // instruction, because this way visits much less of the
+ // Value graph.
+ livePhis := make(map[*Phi]bool)
+ for _, npList := range newPhis {
+ for _, np := range npList {
+ phi := np.phi
+ if !livePhis[phi] && phiHasDirectReferrer(phi) {
+ markLivePhi(livePhis, phi)
+ }
+ }
+ }
+
+ // Existing φ-nodes due to && and || operators
+ // are all considered live (see Go issue 19622).
+ for _, b := range blocks {
+ for _, phi := range b.phis() {
+ markLivePhi(livePhis, phi.(*Phi))
+ }
+ }
+
+ // Second pass: eliminate unused phis from newPhis.
+ for block, npList := range newPhis {
+ j := 0
+ for _, np := range npList {
+ if livePhis[np.phi] {
+ npList[j] = np
+ j++
+ } else {
+ // discard it, first removing it from referrers
+ for _, val := range np.phi.Edges {
+ if refs := val.Referrers(); refs != nil {
+ *refs = removeInstr(*refs, np.phi)
+ }
+ }
+ np.phi.block = nil
+ }
+ }
+ newPhis[block] = npList[:j]
+ }
+}
+
+// markLivePhi marks phi, and all φ-nodes transitively reachable via
+// its Operands, live.
+func markLivePhi(livePhis map[*Phi]bool, phi *Phi) {
+ livePhis[phi] = true
+ for _, rand := range phi.Operands(nil) {
+ if q, ok := (*rand).(*Phi); ok {
+ if !livePhis[q] {
+ markLivePhi(livePhis, q)
+ }
+ }
+ }
+}
+
+// phiHasDirectReferrer reports whether phi is directly referred to by
+// a non-Phi instruction. Such instructions are the
+// roots of the liveness traversal.
+func phiHasDirectReferrer(phi *Phi) bool {
+ for _, instr := range *phi.Referrers() {
+ if _, ok := instr.(*Phi); !ok {
+ return true
+ }
+ }
+ return false
+}
+
+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 {
+ i := b.Index
+ if s.Bit(i) != 0 {
+ return false
+ }
+ s.SetBit(&s.Int, i, 1)
+ 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 {
+ l := s.BitLen()
+ for i := 0; i < l; i++ {
+ if s.Bit(i) == 1 {
+ s.SetBit(&s.Int, i, 0)
+ return i
+ }
+ }
+ return -1
+}
+
+// newPhi is a pair of a newly introduced φ-node and the lifted Alloc
+// it replaces.
+type newPhi struct {
+ phi *Phi
+ alloc *Alloc
+}
+
+// newPhiMap records for each basic block, the set of newPhis that
+// must be prepended to the block.
+type newPhiMap map[*BasicBlock][]newPhi
+
+// liftAlloc determines whether alloc can be lifted into registers,
+// and if so, it populates newPhis with all the φ-nodes it may require
+// and returns true.
+//
+// fresh is a source of fresh ids for phi nodes.
+//
+func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) bool {
+ // Don't lift aggregates into registers, because we don't have
+ // a way to express their zero-constants.
+ switch deref(alloc.Type()).Underlying().(type) {
+ case *types.Array, *types.Struct:
+ return false
+ }
+
+ // Don't lift named return values in functions that defer
+ // calls that may recover from panic.
+ if fn := alloc.Parent(); fn.Recover != nil {
+ for _, nr := range fn.namedResults {
+ if nr == alloc {
+ return false
+ }
+ }
+ }
+
+ // Compute defblocks, the set of blocks containing a
+ // definition of the alloc cell.
+ 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
+ // loads/stores into the cell, and DebugRef.
+ switch instr := instr.(type) {
+ case *Store:
+ if instr.Val == alloc {
+ return false // address used as value
+ }
+ if instr.Addr != alloc {
+ panic("Alloc.Referrers is inconsistent")
+ }
+ defblocks.Add(instr.Block())
+ case *UnOp:
+ if instr.Op != token.MUL {
+ return false // not a load
+ }
+ if instr.X != alloc {
+ panic("Alloc.Referrers is inconsistent")
+ }
+ case *DebugRef:
+ // ok
+ default:
+ return false // some other instruction
+ }
+ }
+ // The Alloc itself counts as a (zero) definition of the cell.
+ defblocks.Add(alloc.Block())
+
+ if debugLifting {
+ fmt.Fprintln(os.Stderr, "\tlifting ", alloc, alloc.Name())
+ }
+
+ fn := alloc.Parent()
+
+ // Φ-insertion.
+ //
+ // What follows is the body of the main loop of the insert-φ
+ // function described by Cytron et al, but instead of using
+ // counter tricks, we just reset the 'hasAlready' and 'work'
+ // sets each iteration. These are bitmaps so it's pretty cheap.
+ //
+ // TODO(adonovan): opt: recycle slice storage for W,
+ // hasAlready, defBlocks across liftAlloc calls.
+ var hasAlready BlockSet
+
+ // Initialize W and work to defblocks.
+ 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() {
+ u := fn.Blocks[i]
+ for _, v := range df[u.Index] {
+ if hasAlready.Add(v) {
+ // Create φ-node.
+ // It will be prepended to v.Instrs later, if needed.
+ phi := &Phi{
+ Edges: make([]Value, len(v.Preds)),
+ Comment: alloc.Comment,
+ }
+ // This is merely a debugging aid:
+ phi.setNum(*fresh)
+ *fresh++
+
+ phi.pos = alloc.Pos()
+ phi.setType(deref(alloc.Type()))
+ phi.block = v
+ if debugLifting {
+ fmt.Fprintf(os.Stderr, "\tplace %s = %s at block %s\n", phi.Name(), phi, v)
+ }
+ newPhis[v] = append(newPhis[v], newPhi{phi, alloc})
+
+ if work.Add(v) {
+ W.Add(v)
+ }
+ }
+ }
+ }
+
+ return true
+}
+
+// replaceAll replaces all intraprocedural uses of x with y,
+// updating x.Referrers and y.Referrers.
+// Precondition: x.Referrers() != nil, i.e. x must be local to some function.
+//
+func replaceAll(x, y Value) {
+ var rands []*Value
+ pxrefs := x.Referrers()
+ pyrefs := y.Referrers()
+ for _, instr := range *pxrefs {
+ rands = instr.Operands(rands[:0]) // recycle storage
+ for _, rand := range rands {
+ if *rand != nil {
+ if *rand == x {
+ *rand = y
+ }
+ }
+ }
+ if pyrefs != nil {
+ *pyrefs = append(*pyrefs, instr) // dups ok
+ }
+ }
+ *pxrefs = nil // x is now unreferenced
+}
+
+// renamed returns the value to which alloc is being renamed,
+// constructing it lazily if it's the implicit zero initialization.
+//
+func renamed(renaming []Value, alloc *Alloc) Value {
+ v := renaming[alloc.index]
+ if v == nil {
+ v = zeroConst(deref(alloc.Type()))
+ renaming[alloc.index] = v
+ }
+ return v
+}
+
+// rename implements the (Cytron et al) SSA renaming algorithm, a
+// preorder traversal of the dominator tree replacing all loads of
+// Alloc cells with the value stored to that cell by the dominating
+// store instruction. For lifting, we need only consider loads,
+// stores and φ-nodes.
+//
+// renaming is a map from *Alloc (keyed by index number) to its
+// dominating stored value; newPhis[x] is the set of new φ-nodes to be
+// prepended to block x.
+//
+func rename(u *BasicBlock, renaming []Value, newPhis newPhiMap) {
+ // Each φ-node becomes the new name for its associated Alloc.
+ for _, np := range newPhis[u] {
+ phi := np.phi
+ alloc := np.alloc
+ renaming[alloc.index] = phi
+ }
+
+ // Rename loads and stores of allocs.
+ for i, instr := range u.Instrs {
+ switch instr := instr.(type) {
+ case *Alloc:
+ if instr.index >= 0 { // store of zero to Alloc cell
+ // Replace dominated loads by the zero value.
+ renaming[instr.index] = nil
+ if debugLifting {
+ fmt.Fprintf(os.Stderr, "\tkill alloc %s\n", instr)
+ }
+ // Delete the Alloc.
+ u.Instrs[i] = nil
+ u.gaps++
+ }
+
+ case *Store:
+ if alloc, ok := instr.Addr.(*Alloc); ok && alloc.index >= 0 { // store to Alloc cell
+ // Replace dominated loads by the stored value.
+ renaming[alloc.index] = instr.Val
+ if debugLifting {
+ fmt.Fprintf(os.Stderr, "\tkill store %s; new value: %s\n",
+ instr, instr.Val.Name())
+ }
+ // Remove the store from the referrer list of the stored value.
+ if refs := instr.Val.Referrers(); refs != nil {
+ *refs = removeInstr(*refs, instr)
+ }
+ // Delete the Store.
+ u.Instrs[i] = nil
+ u.gaps++
+ }
+
+ case *UnOp:
+ if instr.Op == token.MUL {
+ if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // load of Alloc cell
+ newval := renamed(renaming, alloc)
+ if debugLifting {
+ fmt.Fprintf(os.Stderr, "\tupdate load %s = %s with %s\n",
+ instr.Name(), instr, newval.Name())
+ }
+ // Replace all references to
+ // the loaded value by the
+ // dominating stored value.
+ replaceAll(instr, newval)
+ // Delete the Load.
+ u.Instrs[i] = nil
+ u.gaps++
+ }
+ }
+
+ case *DebugRef:
+ if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // ref of Alloc cell
+ if instr.IsAddr {
+ instr.X = renamed(renaming, alloc)
+ instr.IsAddr = false
+
+ // Add DebugRef to instr.X's referrers.
+ if refs := instr.X.Referrers(); refs != nil {
+ *refs = append(*refs, instr)
+ }
+ } else {
+ // A source expression denotes the address
+ // of an Alloc that was optimized away.
+ instr.X = nil
+
+ // Delete the DebugRef.
+ u.Instrs[i] = nil
+ u.gaps++
+ }
+ }
+ }
+ }
+
+ // For each φ-node in a CFG successor, rename the edge.
+ for _, v := range u.Succs {
+ phis := newPhis[v]
+ if len(phis) == 0 {
+ continue
+ }
+ i := v.predIndex(u)
+ for _, np := range phis {
+ phi := np.phi
+ alloc := np.alloc
+ newval := renamed(renaming, alloc)
+ if debugLifting {
+ fmt.Fprintf(os.Stderr, "\tsetphi %s edge %s -> %s (#%d) (alloc=%s) := %s\n",
+ phi.Name(), u, v, i, alloc.Name(), newval.Name())
+ }
+ phi.Edges[i] = newval
+ if prefs := newval.Referrers(); prefs != nil {
+ *prefs = append(*prefs, phi)
+ }
+ }
+ }
+
+ // Continue depth-first recursion over domtree, pushing a
+ // fresh copy of the renaming map for each subtree.
+ for i, v := range u.dom.children {
+ r := renaming
+ if i < len(u.dom.children)-1 {
+ // On all but the final iteration, we must make
+ // a copy to avoid destructive update.
+ r = make([]Value, len(renaming))
+ copy(r, renaming)
+ }
+ rename(v, r, newPhis)
+ }
+
+}
diff --git a/vendor/honnef.co/go/tools/ssa/lvalue.go b/vendor/honnef.co/go/tools/ssa/lvalue.go
new file mode 100644
index 000000000..eb5d71e18
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/lvalue.go
@@ -0,0 +1,123 @@
+// 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 ssa
+
+// lvalues are the union of addressable expressions and map-index
+// expressions.
+
+import (
+ "go/ast"
+ "go/token"
+ "go/types"
+)
+
+// An lvalue represents an assignable location that may appear on the
+// left-hand side of an assignment. This is a generalization of a
+// pointer to permit updates to elements of maps.
+//
+type lvalue interface {
+ store(fn *Function, v Value) // stores v into the location
+ load(fn *Function) Value // loads the contents of the location
+ address(fn *Function) Value // address of the location
+ typ() types.Type // returns the type of the location
+}
+
+// An address is an lvalue represented by a true pointer.
+type address struct {
+ addr Value
+ pos token.Pos // source position
+ expr ast.Expr // source syntax of the value (not address) [debug mode]
+}
+
+func (a *address) load(fn *Function) Value {
+ load := emitLoad(fn, a.addr)
+ load.pos = a.pos
+ return load
+}
+
+func (a *address) store(fn *Function, v Value) {
+ store := emitStore(fn, a.addr, v, a.pos)
+ if a.expr != nil {
+ // store.Val is v, converted for assignability.
+ emitDebugRef(fn, a.expr, store.Val, false)
+ }
+}
+
+func (a *address) address(fn *Function) Value {
+ if a.expr != nil {
+ emitDebugRef(fn, a.expr, a.addr, true)
+ }
+ return a.addr
+}
+
+func (a *address) typ() types.Type {
+ return deref(a.addr.Type())
+}
+
+// An element is an lvalue represented by m[k], the location of an
+// element of a map or string. These locations are not addressable
+// since pointers cannot be formed from them, but they do support
+// load(), and in the case of maps, store().
+//
+type element struct {
+ m, k Value // map or string
+ t types.Type // map element type or string byte type
+ pos token.Pos // source position of colon ({k:v}) or lbrack (m[k]=v)
+}
+
+func (e *element) load(fn *Function) Value {
+ l := &Lookup{
+ X: e.m,
+ Index: e.k,
+ }
+ l.setPos(e.pos)
+ l.setType(e.t)
+ return fn.emit(l)
+}
+
+func (e *element) store(fn *Function, v Value) {
+ up := &MapUpdate{
+ Map: e.m,
+ Key: e.k,
+ Value: emitConv(fn, v, e.t),
+ }
+ up.pos = e.pos
+ fn.emit(up)
+}
+
+func (e *element) address(fn *Function) Value {
+ panic("map/string elements are not addressable")
+}
+
+func (e *element) typ() types.Type {
+ return e.t
+}
+
+// A blank is a dummy variable whose name is "_".
+// It is not reified: loads are illegal and stores are ignored.
+//
+type blank struct{}
+
+func (bl blank) load(fn *Function) Value {
+ panic("blank.load is illegal")
+}
+
+func (bl blank) store(fn *Function, v Value) {
+ s := &BlankStore{
+ Val: v,
+ }
+ fn.emit(s)
+}
+
+func (bl blank) address(fn *Function) Value {
+ panic("blank var is not addressable")
+}
+
+func (bl blank) typ() types.Type {
+ // This should be the type of the blank Ident; the typechecker
+ // doesn't provide this yet, but fortunately, we don't need it
+ // yet either.
+ panic("blank.typ is unimplemented")
+}
diff --git a/vendor/honnef.co/go/tools/ssa/methods.go b/vendor/honnef.co/go/tools/ssa/methods.go
new file mode 100644
index 000000000..9cf383916
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/methods.go
@@ -0,0 +1,239 @@
+// 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 ssa
+
+// This file defines utilities for population of method sets.
+
+import (
+ "fmt"
+ "go/types"
+)
+
+// MethodValue returns the Function implementing method sel, building
+// wrapper methods on demand. It returns nil if sel denotes an
+// abstract (interface) method.
+//
+// Precondition: sel.Kind() == MethodVal.
+//
+// Thread-safe.
+//
+// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
+//
+func (prog *Program) MethodValue(sel *types.Selection) *Function {
+ if sel.Kind() != types.MethodVal {
+ 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("MethodValue %s %v", T, sel)()
+ }
+
+ prog.methodsMu.Lock()
+ defer prog.methodsMu.Unlock()
+
+ return prog.addMethod(prog.createMethodSet(T), sel)
+}
+
+// LookupMethod returns the implementation of the method of type T
+// identified by (pkg, name). It returns nil if the method exists but
+// is abstract, and panics if T has no such method.
+//
+func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
+ sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
+ if sel == nil {
+ panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
+ }
+ return prog.MethodValue(sel)
+}
+
+// methodSet contains the (concrete) methods of a non-interface type.
+type methodSet struct {
+ mapping map[string]*Function // populated lazily
+ complete bool // mapping contains all methods
+}
+
+// Precondition: !isInterface(T).
+// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
+func (prog *Program) createMethodSet(T types.Type) *methodSet {
+ mset, ok := prog.methodSets.At(T).(*methodSet)
+ if !ok {
+ mset = &methodSet{mapping: make(map[string]*Function)}
+ prog.methodSets.Set(T, mset)
+ }
+ return mset
+}
+
+// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
+func (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function {
+ if sel.Kind() == types.MethodExpr {
+ panic(sel)
+ }
+ id := sel.Obj().Id()
+ fn := mset.mapping[id]
+ if fn == nil {
+ obj := sel.Obj().(*types.Func)
+
+ needsPromotion := len(sel.Index()) > 1
+ needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.Recv())
+ if needsPromotion || needsIndirection {
+ fn = makeWrapper(prog, sel)
+ } else {
+ fn = prog.declaredFunc(obj)
+ }
+ if fn.Signature.Recv() == nil {
+ panic(fn) // missing receiver
+ }
+ mset.mapping[id] = fn
+ }
+ return fn
+}
+
+// RuntimeTypes returns a new unordered slice containing all
+// concrete types in the program for which a complete (non-empty)
+// method set is required at run-time.
+//
+// Thread-safe.
+//
+// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
+//
+func (prog *Program) RuntimeTypes() []types.Type {
+ prog.methodsMu.Lock()
+ defer prog.methodsMu.Unlock()
+
+ var res []types.Type
+ prog.methodSets.Iterate(func(T types.Type, v interface{}) {
+ if v.(*methodSet).complete {
+ res = append(res, T)
+ }
+ })
+ return res
+}
+
+// declaredFunc returns the concrete function/method denoted by obj.
+// Panic ensues if there is none.
+//
+func (prog *Program) declaredFunc(obj *types.Func) *Function {
+ if v := prog.packageLevelValue(obj); v != nil {
+ return v.(*Function)
+ }
+ panic("no concrete method: " + obj.String())
+}
+
+// needMethodsOf ensures that runtime type information (including the
+// complete method set) is available for the specified type T and all
+// its subcomponents.
+//
+// needMethodsOf must be called for at least every type that is an
+// operand of some MakeInterface instruction, and for the type of
+// every exported package member.
+//
+// Precondition: T is not a method signature (*Signature with Recv()!=nil).
+//
+// Thread-safe. (Called via emitConv from multiple builder goroutines.)
+//
+// TODO(adonovan): make this faster. It accounts for 20% of SSA build time.
+//
+// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
+//
+func (prog *Program) needMethodsOf(T types.Type) {
+ prog.methodsMu.Lock()
+ prog.needMethods(T, false)
+ prog.methodsMu.Unlock()
+}
+
+// Precondition: T is not a method signature (*Signature with Recv()!=nil).
+// Recursive case: skip => don't create methods for T.
+//
+// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
+//
+func (prog *Program) needMethods(T types.Type, skip bool) {
+ // Each package maintains its own set of types it has visited.
+ if prevSkip, ok := prog.runtimeTypes.At(T).(bool); ok {
+ // needMethods(T) was previously called
+ if !prevSkip || skip {
+ return // already seen, with same or false 'skip' value
+ }
+ }
+ prog.runtimeTypes.Set(T, skip)
+
+ tmset := prog.MethodSets.MethodSet(T)
+
+ if !skip && !isInterface(T) && tmset.Len() > 0 {
+ // Create methods of T.
+ mset := prog.createMethodSet(T)
+ if !mset.complete {
+ mset.complete = true
+ n := tmset.Len()
+ for i := 0; i < n; i++ {
+ prog.addMethod(mset, tmset.At(i))
+ }
+ }
+ }
+
+ // Recursion over signatures of each method.
+ for i := 0; i < tmset.Len(); i++ {
+ sig := tmset.At(i).Type().(*types.Signature)
+ prog.needMethods(sig.Params(), false)
+ prog.needMethods(sig.Results(), false)
+ }
+
+ switch t := T.(type) {
+ case *types.Basic:
+ // nop
+
+ case *types.Interface:
+ // nop---handled by recursion over method set.
+
+ case *types.Pointer:
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Slice:
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Chan:
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Map:
+ prog.needMethods(t.Key(), false)
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Signature:
+ if t.Recv() != nil {
+ panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
+ }
+ prog.needMethods(t.Params(), false)
+ prog.needMethods(t.Results(), false)
+
+ case *types.Named:
+ // A pointer-to-named type can be derived from a named
+ // type via reflection. It may have methods too.
+ prog.needMethods(types.NewPointer(T), false)
+
+ // Consider 'type T struct{S}' where S has methods.
+ // Reflection provides no way to get from T to struct{S},
+ // only to S, so the method set of struct{S} is unwanted,
+ // so set 'skip' flag during recursion.
+ prog.needMethods(t.Underlying(), true)
+
+ case *types.Array:
+ prog.needMethods(t.Elem(), false)
+
+ case *types.Struct:
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ prog.needMethods(t.Field(i).Type(), false)
+ }
+
+ case *types.Tuple:
+ for i, n := 0, t.Len(); i < n; i++ {
+ prog.needMethods(t.At(i).Type(), false)
+ }
+
+ default:
+ panic(T)
+ }
+}
diff --git a/vendor/honnef.co/go/tools/ssa/mode.go b/vendor/honnef.co/go/tools/ssa/mode.go
new file mode 100644
index 000000000..d2a269893
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/mode.go
@@ -0,0 +1,100 @@
+// 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 ssa
+
+// This file defines the BuilderMode type and its command-line flag.
+
+import (
+ "bytes"
+ "fmt"
+)
+
+// BuilderMode is a bitmask of options for diagnostics and checking.
+//
+// *BuilderMode satisfies the flag.Value interface. Example:
+//
+// var mode = ssa.BuilderMode(0)
+// func init() { flag.Var(&mode, "build", ssa.BuilderModeDoc) }
+//
+type BuilderMode uint
+
+const (
+ PrintPackages BuilderMode = 1 << iota // Print package inventory to stdout
+ PrintFunctions // Print function SSA code to stdout
+ LogSource // Log source locations as SSA builder progresses
+ SanityCheckFunctions // Perform sanity checking of function bodies
+ NaiveForm // Build naïve SSA form: don't replace local loads/stores with registers
+ BuildSerially // Build packages serially, not in parallel.
+ GlobalDebug // Enable debug info for all packages
+ BareInits // Build init functions without guards or calls to dependent inits
+)
+
+const BuilderModeDoc = `Options controlling the SSA builder.
+The value is a sequence of zero or more of these letters:
+C perform sanity [C]hecking of the SSA form.
+D include [D]ebug info for every function.
+P print [P]ackage inventory.
+F print [F]unction SSA code.
+S log [S]ource locations as SSA builder progresses.
+L build distinct packages seria[L]ly instead of in parallel.
+N build [N]aive SSA form: don't replace local loads/stores with registers.
+I build bare [I]nit functions: no init guards or calls to dependent inits.
+`
+
+func (m BuilderMode) String() string {
+ var buf bytes.Buffer
+ if m&GlobalDebug != 0 {
+ buf.WriteByte('D')
+ }
+ if m&PrintPackages != 0 {
+ buf.WriteByte('P')
+ }
+ if m&PrintFunctions != 0 {
+ buf.WriteByte('F')
+ }
+ if m&LogSource != 0 {
+ buf.WriteByte('S')
+ }
+ if m&SanityCheckFunctions != 0 {
+ buf.WriteByte('C')
+ }
+ if m&NaiveForm != 0 {
+ buf.WriteByte('N')
+ }
+ if m&BuildSerially != 0 {
+ buf.WriteByte('L')
+ }
+ return buf.String()
+}
+
+// Set parses the flag characters in s and updates *m.
+func (m *BuilderMode) Set(s string) error {
+ var mode BuilderMode
+ for _, c := range s {
+ switch c {
+ case 'D':
+ mode |= GlobalDebug
+ case 'P':
+ mode |= PrintPackages
+ case 'F':
+ mode |= PrintFunctions
+ case 'S':
+ mode |= LogSource | BuildSerially
+ case 'C':
+ mode |= SanityCheckFunctions
+ case 'N':
+ mode |= NaiveForm
+ case 'L':
+ mode |= BuildSerially
+ default:
+ return fmt.Errorf("unknown BuilderMode option: %q", c)
+ }
+ }
+ *m = mode
+ return nil
+}
+
+// Get returns m.
+func (m BuilderMode) Get() interface{} { return m }
diff --git a/vendor/honnef.co/go/tools/ssa/print.go b/vendor/honnef.co/go/tools/ssa/print.go
new file mode 100644
index 000000000..6fd277277
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/print.go
@@ -0,0 +1,435 @@
+// 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 ssa
+
+// This file implements the String() methods for all Value and
+// Instruction types.
+
+import (
+ "bytes"
+ "fmt"
+ "go/types"
+ "io"
+ "reflect"
+ "sort"
+
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// relName returns the name of v relative to i.
+// In most cases, this is identical to v.Name(), but references to
+// Functions (including methods) and Globals use RelString and
+// all types are displayed with relType, so that only cross-package
+// references are package-qualified.
+//
+func relName(v Value, i Instruction) string {
+ var from *types.Package
+ if i != nil {
+ from = i.Parent().pkg()
+ }
+ switch v := v.(type) {
+ case Member: // *Function or *Global
+ return v.RelString(from)
+ case *Const:
+ return v.RelString(from)
+ }
+ return v.Name()
+}
+
+func relType(t types.Type, from *types.Package) string {
+ return types.TypeString(t, types.RelativeTo(from))
+}
+
+func relString(m Member, from *types.Package) string {
+ // NB: not all globals have an Object (e.g. init$guard),
+ // so use Package().Object not Object.Package().
+ if pkg := m.Package().Pkg; pkg != nil && pkg != from {
+ return fmt.Sprintf("%s.%s", pkg.Path(), m.Name())
+ }
+ return m.Name()
+}
+
+// Value.String()
+//
+// This method is provided only for debugging.
+// It never appears in disassembly, which uses Value.Name().
+
+func (v *Parameter) String() string {
+ from := v.Parent().pkg()
+ return fmt.Sprintf("parameter %s : %s", v.Name(), relType(v.Type(), from))
+}
+
+func (v *FreeVar) String() string {
+ from := v.Parent().pkg()
+ return fmt.Sprintf("freevar %s : %s", v.Name(), relType(v.Type(), from))
+}
+
+func (v *Builtin) String() string {
+ return fmt.Sprintf("builtin %s", v.Name())
+}
+
+// Instruction.String()
+
+func (v *Alloc) String() string {
+ op := "local"
+ if v.Heap {
+ op = "new"
+ }
+ from := v.Parent().pkg()
+ return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), from), v.Comment)
+}
+
+func (v *Phi) String() string {
+ var b bytes.Buffer
+ b.WriteString("phi [")
+ for i, edge := range v.Edges {
+ if i > 0 {
+ b.WriteString(", ")
+ }
+ // Be robust against malformed CFG.
+ if v.block == nil {
+ b.WriteString("??")
+ continue
+ }
+ block := -1
+ if i < len(v.block.Preds) {
+ block = v.block.Preds[i].Index
+ }
+ fmt.Fprintf(&b, "%d: ", block)
+ edgeVal := "" // be robust
+ if edge != nil {
+ edgeVal = relName(edge, v)
+ }
+ b.WriteString(edgeVal)
+ }
+ b.WriteString("]")
+ if v.Comment != "" {
+ b.WriteString(" #")
+ b.WriteString(v.Comment)
+ }
+ return b.String()
+}
+
+func printCall(v *CallCommon, prefix string, instr Instruction) string {
+ var b bytes.Buffer
+ b.WriteString(prefix)
+ if !v.IsInvoke() {
+ b.WriteString(relName(v.Value, instr))
+ } else {
+ fmt.Fprintf(&b, "invoke %s.%s", relName(v.Value, instr), v.Method.Name())
+ }
+ b.WriteString("(")
+ for i, arg := range v.Args {
+ if i > 0 {
+ b.WriteString(", ")
+ }
+ b.WriteString(relName(arg, instr))
+ }
+ if v.Signature().Variadic() {
+ b.WriteString("...")
+ }
+ b.WriteString(")")
+ return b.String()
+}
+
+func (c *CallCommon) String() string {
+ return printCall(c, "", nil)
+}
+
+func (v *Call) String() string {
+ return printCall(&v.Call, "", v)
+}
+
+func (v *BinOp) String() string {
+ return fmt.Sprintf("%s %s %s", relName(v.X, v), v.Op.String(), relName(v.Y, v))
+}
+
+func (v *UnOp) String() string {
+ return fmt.Sprintf("%s%s%s", v.Op, relName(v.X, v), commaOk(v.CommaOk))
+}
+
+func printConv(prefix string, v, x Value) string {
+ from := v.Parent().pkg()
+ return fmt.Sprintf("%s %s <- %s (%s)",
+ prefix,
+ relType(v.Type(), from),
+ relType(x.Type(), from),
+ relName(x, v.(Instruction)))
+}
+
+func (v *ChangeType) String() string { return printConv("changetype", v, v.X) }
+func (v *Convert) String() string { return printConv("convert", v, v.X) }
+func (v *ChangeInterface) String() string { return printConv("change interface", v, v.X) }
+func (v *MakeInterface) String() string { return printConv("make", v, v.X) }
+
+func (v *MakeClosure) String() string {
+ var b bytes.Buffer
+ fmt.Fprintf(&b, "make closure %s", relName(v.Fn, v))
+ if v.Bindings != nil {
+ b.WriteString(" [")
+ for i, c := range v.Bindings {
+ if i > 0 {
+ b.WriteString(", ")
+ }
+ b.WriteString(relName(c, v))
+ }
+ b.WriteString("]")
+ }
+ return b.String()
+}
+
+func (v *MakeSlice) String() string {
+ from := v.Parent().pkg()
+ return fmt.Sprintf("make %s %s %s",
+ relType(v.Type(), from),
+ relName(v.Len, v),
+ relName(v.Cap, v))
+}
+
+func (v *Slice) String() string {
+ var b bytes.Buffer
+ b.WriteString("slice ")
+ b.WriteString(relName(v.X, v))
+ b.WriteString("[")
+ if v.Low != nil {
+ b.WriteString(relName(v.Low, v))
+ }
+ b.WriteString(":")
+ if v.High != nil {
+ b.WriteString(relName(v.High, v))
+ }
+ if v.Max != nil {
+ b.WriteString(":")
+ b.WriteString(relName(v.Max, v))
+ }
+ b.WriteString("]")
+ return b.String()
+}
+
+func (v *MakeMap) String() string {
+ res := ""
+ if v.Reserve != nil {
+ res = relName(v.Reserve, v)
+ }
+ from := v.Parent().pkg()
+ return fmt.Sprintf("make %s %s", relType(v.Type(), from), res)
+}
+
+func (v *MakeChan) String() string {
+ from := v.Parent().pkg()
+ return fmt.Sprintf("make %s %s", relType(v.Type(), from), relName(v.Size, v))
+}
+
+func (v *FieldAddr) String() string {
+ st := deref(v.X.Type()).Underlying().(*types.Struct)
+ // Be robust against a bad index.
+ name := "?"
+ if 0 <= v.Field && v.Field < st.NumFields() {
+ name = st.Field(v.Field).Name()
+ }
+ return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field)
+}
+
+func (v *Field) String() string {
+ st := v.X.Type().Underlying().(*types.Struct)
+ // Be robust against a bad index.
+ name := "?"
+ if 0 <= v.Field && v.Field < st.NumFields() {
+ name = st.Field(v.Field).Name()
+ }
+ return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field)
+}
+
+func (v *IndexAddr) String() string {
+ return fmt.Sprintf("&%s[%s]", relName(v.X, v), relName(v.Index, v))
+}
+
+func (v *Index) String() string {
+ return fmt.Sprintf("%s[%s]", relName(v.X, v), relName(v.Index, v))
+}
+
+func (v *Lookup) String() string {
+ return fmt.Sprintf("%s[%s]%s", relName(v.X, v), relName(v.Index, v), commaOk(v.CommaOk))
+}
+
+func (v *Range) String() string {
+ return "range " + relName(v.X, v)
+}
+
+func (v *Next) String() string {
+ return "next " + relName(v.Iter, v)
+}
+
+func (v *TypeAssert) String() string {
+ from := v.Parent().pkg()
+ return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, from))
+}
+
+func (v *Extract) String() string {
+ return fmt.Sprintf("extract %s #%d", relName(v.Tuple, v), v.Index)
+}
+
+func (s *Jump) String() string {
+ // Be robust against malformed CFG.
+ block := -1
+ if s.block != nil && len(s.block.Succs) == 1 {
+ block = s.block.Succs[0].Index
+ }
+ return fmt.Sprintf("jump %d", block)
+}
+
+func (s *If) String() string {
+ // Be robust against malformed CFG.
+ tblock, fblock := -1, -1
+ if s.block != nil && len(s.block.Succs) == 2 {
+ tblock = s.block.Succs[0].Index
+ fblock = s.block.Succs[1].Index
+ }
+ return fmt.Sprintf("if %s goto %d else %d", relName(s.Cond, s), tblock, fblock)
+}
+
+func (s *Go) String() string {
+ return printCall(&s.Call, "go ", s)
+}
+
+func (s *Panic) String() string {
+ return "panic " + relName(s.X, s)
+}
+
+func (s *Return) String() string {
+ var b bytes.Buffer
+ b.WriteString("return")
+ for i, r := range s.Results {
+ if i == 0 {
+ b.WriteString(" ")
+ } else {
+ b.WriteString(", ")
+ }
+ b.WriteString(relName(r, s))
+ }
+ return b.String()
+}
+
+func (*RunDefers) String() string {
+ return "rundefers"
+}
+
+func (s *Send) String() string {
+ return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s))
+}
+
+func (s *Defer) String() string {
+ return printCall(&s.Call, "defer ", s)
+}
+
+func (s *Select) String() string {
+ var b bytes.Buffer
+ for i, st := range s.States {
+ if i > 0 {
+ b.WriteString(", ")
+ }
+ if st.Dir == types.RecvOnly {
+ b.WriteString("<-")
+ b.WriteString(relName(st.Chan, s))
+ } else {
+ b.WriteString(relName(st.Chan, s))
+ b.WriteString("<-")
+ b.WriteString(relName(st.Send, s))
+ }
+ }
+ non := ""
+ if !s.Blocking {
+ non = "non"
+ }
+ return fmt.Sprintf("select %sblocking [%s]", non, b.String())
+}
+
+func (s *Store) String() string {
+ return fmt.Sprintf("*%s = %s", relName(s.Addr, s), relName(s.Val, s))
+}
+
+func (s *BlankStore) String() string {
+ return fmt.Sprintf("_ = %s", relName(s.Val, s))
+}
+
+func (s *MapUpdate) String() string {
+ return fmt.Sprintf("%s[%s] = %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s))
+}
+
+func (s *DebugRef) String() string {
+ p := s.Parent().Prog.Fset.Position(s.Pos())
+ var descr interface{}
+ if s.object != nil {
+ descr = s.object // e.g. "var x int"
+ } else {
+ descr = reflect.TypeOf(s.Expr) // e.g. "*ast.CallExpr"
+ }
+ var addr string
+ if s.IsAddr {
+ addr = "address of "
+ }
+ return fmt.Sprintf("; %s%s @ %d:%d is %s", addr, descr, p.Line, p.Column, s.X.Name())
+}
+
+func (p *Package) String() string {
+ return "package " + p.Pkg.Path()
+}
+
+var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer
+
+func (p *Package) WriteTo(w io.Writer) (int64, error) {
+ var buf bytes.Buffer
+ WritePackage(&buf, p)
+ n, err := w.Write(buf.Bytes())
+ return int64(n), err
+}
+
+// WritePackage writes to buf a human-readable summary of p.
+func WritePackage(buf *bytes.Buffer, p *Package) {
+ fmt.Fprintf(buf, "%s:\n", p)
+
+ var names []string
+ maxname := 0
+ for name := range p.Members {
+ if l := len(name); l > maxname {
+ maxname = l
+ }
+ names = append(names, name)
+ }
+
+ from := p.Pkg
+ sort.Strings(names)
+ for _, name := range names {
+ switch mem := p.Members[name].(type) {
+ case *NamedConst:
+ fmt.Fprintf(buf, " const %-*s %s = %s\n",
+ maxname, name, mem.Name(), mem.Value.RelString(from))
+
+ case *Function:
+ fmt.Fprintf(buf, " func %-*s %s\n",
+ maxname, name, relType(mem.Type(), from))
+
+ case *Type:
+ fmt.Fprintf(buf, " type %-*s %s\n",
+ maxname, name, relType(mem.Type().Underlying(), from))
+ for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
+ fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from)))
+ }
+
+ case *Global:
+ fmt.Fprintf(buf, " var %-*s %s\n",
+ maxname, name, relType(mem.Type().(*types.Pointer).Elem(), from))
+ }
+ }
+
+ fmt.Fprintf(buf, "\n")
+}
+
+func commaOk(x bool) string {
+ if x {
+ return ",ok"
+ }
+ return ""
+}
diff --git a/vendor/honnef.co/go/tools/ssa/sanity.go b/vendor/honnef.co/go/tools/ssa/sanity.go
new file mode 100644
index 000000000..1d29b66b0
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/sanity.go
@@ -0,0 +1,535 @@
+// 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 ssa
+
+// An optional pass for sanity-checking invariants of the SSA representation.
+// Currently it checks CFG invariants but little at the instruction level.
+
+import (
+ "fmt"
+ "go/types"
+ "io"
+ "os"
+ "strings"
+)
+
+type sanity struct {
+ reporter io.Writer
+ fn *Function
+ block *BasicBlock
+ instrs map[Instruction]struct{}
+ insane bool
+}
+
+// sanityCheck performs integrity checking of the SSA representation
+// of the function fn and returns true if it was valid. Diagnostics
+// are written to reporter if non-nil, os.Stderr otherwise. Some
+// diagnostics are only warnings and do not imply a negative result.
+//
+// Sanity-checking is intended to facilitate the debugging of code
+// transformation passes.
+//
+func sanityCheck(fn *Function, reporter io.Writer) bool {
+ if reporter == nil {
+ reporter = os.Stderr
+ }
+ return (&sanity{reporter: reporter}).checkFunction(fn)
+}
+
+// mustSanityCheck is like sanityCheck but panics instead of returning
+// a negative result.
+//
+func mustSanityCheck(fn *Function, reporter io.Writer) {
+ if !sanityCheck(fn, reporter) {
+ fn.WriteTo(os.Stderr)
+ panic("SanityCheck failed")
+ }
+}
+
+func (s *sanity) diagnostic(prefix, format string, args ...interface{}) {
+ fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn)
+ if s.block != nil {
+ fmt.Fprintf(s.reporter, ", block %s", s.block)
+ }
+ io.WriteString(s.reporter, ": ")
+ fmt.Fprintf(s.reporter, format, args...)
+ io.WriteString(s.reporter, "\n")
+}
+
+func (s *sanity) errorf(format string, args ...interface{}) {
+ s.insane = true
+ s.diagnostic("Error", format, args...)
+}
+
+func (s *sanity) warnf(format string, args ...interface{}) {
+ s.diagnostic("Warning", format, args...)
+}
+
+// findDuplicate returns an arbitrary basic block that appeared more
+// than once in blocks, or nil if all were unique.
+func findDuplicate(blocks []*BasicBlock) *BasicBlock {
+ if len(blocks) < 2 {
+ return nil
+ }
+ if blocks[0] == blocks[1] {
+ return blocks[0]
+ }
+ // Slow path:
+ m := make(map[*BasicBlock]bool)
+ for _, b := range blocks {
+ if m[b] {
+ return b
+ }
+ m[b] = true
+ }
+ return nil
+}
+
+func (s *sanity) checkInstr(idx int, instr Instruction) {
+ switch instr := instr.(type) {
+ case *If, *Jump, *Return, *Panic:
+ s.errorf("control flow instruction not at end of block")
+ case *Phi:
+ if idx == 0 {
+ // It suffices to apply this check to just the first phi node.
+ if dup := findDuplicate(s.block.Preds); dup != nil {
+ s.errorf("phi node in block with duplicate predecessor %s", dup)
+ }
+ } else {
+ prev := s.block.Instrs[idx-1]
+ if _, ok := prev.(*Phi); !ok {
+ s.errorf("Phi instruction follows a non-Phi: %T", prev)
+ }
+ }
+ if ne, np := len(instr.Edges), len(s.block.Preds); ne != np {
+ s.errorf("phi node has %d edges but %d predecessors", ne, np)
+
+ } else {
+ for i, e := range instr.Edges {
+ if e == nil {
+ s.errorf("phi node '%s' has no value for edge #%d from %s", instr.Comment, i, s.block.Preds[i])
+ }
+ }
+ }
+
+ case *Alloc:
+ if !instr.Heap {
+ found := false
+ for _, l := range s.fn.Locals {
+ if l == instr {
+ found = true
+ break
+ }
+ }
+ if !found {
+ s.errorf("local alloc %s = %s does not appear in Function.Locals", instr.Name(), instr)
+ }
+ }
+
+ case *BinOp:
+ case *Call:
+ case *ChangeInterface:
+ case *ChangeType:
+ case *Convert:
+ if _, ok := instr.X.Type().Underlying().(*types.Basic); !ok {
+ if _, ok := instr.Type().Underlying().(*types.Basic); !ok {
+ s.errorf("convert %s -> %s: at least one type must be basic", instr.X.Type(), instr.Type())
+ }
+ }
+
+ case *Defer:
+ case *Extract:
+ case *Field:
+ case *FieldAddr:
+ case *Go:
+ case *Index:
+ case *IndexAddr:
+ case *Lookup:
+ case *MakeChan:
+ case *MakeClosure:
+ numFree := len(instr.Fn.(*Function).FreeVars)
+ numBind := len(instr.Bindings)
+ if numFree != numBind {
+ s.errorf("MakeClosure has %d Bindings for function %s with %d free vars",
+ numBind, instr.Fn, numFree)
+
+ }
+ if recv := instr.Type().(*types.Signature).Recv(); recv != nil {
+ s.errorf("MakeClosure's type includes receiver %s", recv.Type())
+ }
+
+ case *MakeInterface:
+ case *MakeMap:
+ case *MakeSlice:
+ case *MapUpdate:
+ case *Next:
+ case *Range:
+ case *RunDefers:
+ case *Select:
+ case *Send:
+ case *Slice:
+ case *Store:
+ case *TypeAssert:
+ case *UnOp:
+ case *DebugRef:
+ case *BlankStore:
+ case *Sigma:
+ // TODO(adonovan): implement checks.
+ default:
+ panic(fmt.Sprintf("Unknown instruction type: %T", instr))
+ }
+
+ if call, ok := instr.(CallInstruction); ok {
+ if call.Common().Signature() == nil {
+ s.errorf("nil signature: %s", call)
+ }
+ }
+
+ // Check that value-defining instructions have valid types
+ // and a valid referrer list.
+ if v, ok := instr.(Value); ok {
+ t := v.Type()
+ if t == nil {
+ s.errorf("no type: %s = %s", v.Name(), v)
+ } else if t == tRangeIter {
+ // not a proper type; ignore.
+ } else if b, ok := t.Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
+ s.errorf("instruction has 'untyped' result: %s = %s : %s", v.Name(), v, t)
+ }
+ s.checkReferrerList(v)
+ }
+
+ // Untyped constants are legal as instruction Operands(),
+ // for example:
+ // _ = "foo"[0]
+ // or:
+ // if wordsize==64 {...}
+
+ // All other non-Instruction Values can be found via their
+ // enclosing Function or Package.
+}
+
+func (s *sanity) checkFinalInstr(instr Instruction) {
+ switch instr := instr.(type) {
+ case *If:
+ if nsuccs := len(s.block.Succs); nsuccs != 2 {
+ s.errorf("If-terminated block has %d successors; expected 2", nsuccs)
+ return
+ }
+ if s.block.Succs[0] == s.block.Succs[1] {
+ s.errorf("If-instruction has same True, False target blocks: %s", s.block.Succs[0])
+ return
+ }
+
+ case *Jump:
+ if nsuccs := len(s.block.Succs); nsuccs != 1 {
+ s.errorf("Jump-terminated block has %d successors; expected 1", nsuccs)
+ return
+ }
+
+ case *Return:
+ if nsuccs := len(s.block.Succs); nsuccs != 0 {
+ s.errorf("Return-terminated block has %d successors; expected none", nsuccs)
+ return
+ }
+ if na, nf := len(instr.Results), s.fn.Signature.Results().Len(); nf != na {
+ s.errorf("%d-ary return in %d-ary function", na, nf)
+ }
+
+ case *Panic:
+ if nsuccs := len(s.block.Succs); nsuccs != 0 {
+ s.errorf("Panic-terminated block has %d successors; expected none", nsuccs)
+ return
+ }
+
+ default:
+ s.errorf("non-control flow instruction at end of block")
+ }
+}
+
+func (s *sanity) checkBlock(b *BasicBlock, index int) {
+ s.block = b
+
+ if b.Index != index {
+ s.errorf("block has incorrect Index %d", b.Index)
+ }
+ if b.parent != s.fn {
+ s.errorf("block has incorrect parent %s", b.parent)
+ }
+
+ // Check all blocks are reachable.
+ // (The entry block is always implicitly reachable,
+ // as is the Recover block, if any.)
+ if (index > 0 && b != b.parent.Recover) && len(b.Preds) == 0 {
+ s.warnf("unreachable block")
+ if b.Instrs == nil {
+ // Since this block is about to be pruned,
+ // tolerating transient problems in it
+ // simplifies other optimizations.
+ return
+ }
+ }
+
+ // Check predecessor and successor relations are dual,
+ // and that all blocks in CFG belong to same function.
+ for _, a := range b.Preds {
+ found := false
+ for _, bb := range a.Succs {
+ if bb == b {
+ found = true
+ break
+ }
+ }
+ if !found {
+ s.errorf("expected successor edge in predecessor %s; found only: %s", a, a.Succs)
+ }
+ if a.parent != s.fn {
+ s.errorf("predecessor %s belongs to different function %s", a, a.parent)
+ }
+ }
+ for _, c := range b.Succs {
+ found := false
+ for _, bb := range c.Preds {
+ if bb == b {
+ found = true
+ break
+ }
+ }
+ if !found {
+ s.errorf("expected predecessor edge in successor %s; found only: %s", c, c.Preds)
+ }
+ if c.parent != s.fn {
+ s.errorf("successor %s belongs to different function %s", c, c.parent)
+ }
+ }
+
+ // Check each instruction is sane.
+ n := len(b.Instrs)
+ if n == 0 {
+ s.errorf("basic block contains no instructions")
+ }
+ var rands [10]*Value // reuse storage
+ for j, instr := range b.Instrs {
+ if instr == nil {
+ s.errorf("nil instruction at index %d", j)
+ continue
+ }
+ if b2 := instr.Block(); b2 == nil {
+ s.errorf("nil Block() for instruction at index %d", j)
+ continue
+ } else if b2 != b {
+ s.errorf("wrong Block() (%s) for instruction at index %d ", b2, j)
+ continue
+ }
+ if j < n-1 {
+ s.checkInstr(j, instr)
+ } else {
+ s.checkFinalInstr(instr)
+ }
+
+ // Check Instruction.Operands.
+ operands:
+ for i, op := range instr.Operands(rands[:0]) {
+ if op == nil {
+ s.errorf("nil operand pointer %d of %s", i, instr)
+ continue
+ }
+ val := *op
+ if val == nil {
+ continue // a nil operand is ok
+ }
+
+ // Check that "untyped" types only appear on constant operands.
+ if _, ok := (*op).(*Const); !ok {
+ if basic, ok := (*op).Type().(*types.Basic); ok {
+ if basic.Info()&types.IsUntyped != 0 {
+ s.errorf("operand #%d of %s is untyped: %s", i, instr, basic)
+ }
+ }
+ }
+
+ // Check that Operands that are also Instructions belong to same function.
+ // TODO(adonovan): also check their block dominates block b.
+ if val, ok := val.(Instruction); ok {
+ if val.Block() == nil {
+ s.errorf("operand %d of %s is an instruction (%s) that belongs to no block", i, instr, val)
+ } else if val.Parent() != s.fn {
+ s.errorf("operand %d of %s is an instruction (%s) from function %s", i, instr, val, val.Parent())
+ }
+ }
+
+ // Check that each function-local operand of
+ // instr refers back to instr. (NB: quadratic)
+ switch val := val.(type) {
+ case *Const, *Global, *Builtin:
+ continue // not local
+ case *Function:
+ if val.parent == nil {
+ continue // only anon functions are local
+ }
+ }
+
+ // TODO(adonovan): check val.Parent() != nil <=> val.Referrers() is defined.
+
+ if refs := val.Referrers(); refs != nil {
+ for _, ref := range *refs {
+ if ref == instr {
+ continue operands
+ }
+ }
+ s.errorf("operand %d of %s (%s) does not refer to us", i, instr, val)
+ } else {
+ s.errorf("operand %d of %s (%s) has no referrers", i, instr, val)
+ }
+ }
+ }
+}
+
+func (s *sanity) checkReferrerList(v Value) {
+ refs := v.Referrers()
+ if refs == nil {
+ s.errorf("%s has missing referrer list", v.Name())
+ return
+ }
+ for i, ref := range *refs {
+ if _, ok := s.instrs[ref]; !ok {
+ s.errorf("%s.Referrers()[%d] = %s is not an instruction belonging to this function", v.Name(), i, ref)
+ }
+ }
+}
+
+func (s *sanity) checkFunction(fn *Function) bool {
+ // TODO(adonovan): check Function invariants:
+ // - check params match signature
+ // - check transient fields are nil
+ // - warn if any fn.Locals do not appear among block instructions.
+ s.fn = fn
+ if fn.Prog == nil {
+ s.errorf("nil Prog")
+ }
+
+ _ = 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
+ // separate-compilation model), and error.Error.
+ if fn.Pkg == nil {
+ if strings.HasPrefix(fn.Synthetic, "wrapper ") ||
+ strings.HasPrefix(fn.Synthetic, "bound ") ||
+ strings.HasPrefix(fn.Synthetic, "thunk ") ||
+ strings.HasSuffix(fn.name, "Error") {
+ // ok
+ } else {
+ s.errorf("nil Pkg")
+ }
+ }
+ if src, syn := fn.Synthetic == "", fn.Syntax() != nil; src != syn {
+ s.errorf("got fromSource=%t, hasSyntax=%t; want same values", src, syn)
+ }
+ for i, l := range fn.Locals {
+ if l.Parent() != fn {
+ s.errorf("Local %s at index %d has wrong parent", l.Name(), i)
+ }
+ if l.Heap {
+ s.errorf("Local %s at index %d has Heap flag set", l.Name(), i)
+ }
+ }
+ // Build the set of valid referrers.
+ s.instrs = make(map[Instruction]struct{})
+ for _, b := range fn.Blocks {
+ for _, instr := range b.Instrs {
+ s.instrs[instr] = struct{}{}
+ }
+ }
+ for i, p := range fn.Params {
+ 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 {
+ if fv.Parent() != fn {
+ s.errorf("FreeVar %s at index %d has wrong parent", fv.Name(), i)
+ }
+ s.checkReferrerList(fv)
+ }
+
+ if fn.Blocks != nil && len(fn.Blocks) == 0 {
+ // Function _had_ blocks (so it's not external) but
+ // they were "optimized" away, even the entry block.
+ s.errorf("Blocks slice is non-nil but empty")
+ }
+ for i, b := range fn.Blocks {
+ if b == nil {
+ s.warnf("nil *BasicBlock at f.Blocks[%d]", i)
+ continue
+ }
+ s.checkBlock(b, i)
+ }
+ if fn.Recover != nil && fn.Blocks[fn.Recover.Index] != fn.Recover {
+ s.errorf("Recover block is not in Blocks slice")
+ }
+
+ s.block = nil
+ for i, anon := range fn.AnonFuncs {
+ if anon.Parent() != fn {
+ s.errorf("AnonFuncs[%d]=%s but %s.Parent()=%s", i, anon, anon, anon.Parent())
+ }
+ }
+ s.fn = nil
+ return !s.insane
+}
+
+// sanityCheckPackage checks invariants of packages upon creation.
+// It does not require that the package is built.
+// Unlike sanityCheck (for functions), it just panics at the first error.
+func sanityCheckPackage(pkg *Package) {
+ if pkg.Pkg == nil {
+ panic(fmt.Sprintf("Package %s has no Object", pkg))
+ }
+ _ = pkg.String() // must not crash
+
+ for name, mem := range pkg.Members {
+ if name != mem.Name() {
+ panic(fmt.Sprintf("%s: %T.Name() = %s, want %s",
+ pkg.Pkg.Path(), mem, mem.Name(), name))
+ }
+ obj := mem.Object()
+ if obj == nil {
+ // This check is sound because fields
+ // {Global,Function}.object have type
+ // types.Object. (If they were declared as
+ // *types.{Var,Func}, we'd have a non-empty
+ // interface containing a nil pointer.)
+
+ continue // not all members have typechecker objects
+ }
+ if obj.Name() != name {
+ if obj.Name() == "init" && strings.HasPrefix(mem.Name(), "init#") {
+ // Ok. The name of a declared init function varies between
+ // its types.Func ("init") and its ssa.Function ("init#%d").
+ } else {
+ panic(fmt.Sprintf("%s: %T.Object().Name() = %s, want %s",
+ pkg.Pkg.Path(), mem, obj.Name(), name))
+ }
+ }
+ if obj.Pos() != mem.Pos() {
+ panic(fmt.Sprintf("%s Pos=%d obj.Pos=%d", mem, mem.Pos(), obj.Pos()))
+ }
+ }
+}
diff --git a/vendor/honnef.co/go/tools/ssa/source.go b/vendor/honnef.co/go/tools/ssa/source.go
new file mode 100644
index 000000000..8d9cca170
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/source.go
@@ -0,0 +1,293 @@
+// 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 ssa
+
+// This file defines utilities for working with source positions
+// or source-level named entities ("objects").
+
+// TODO(adonovan): test that {Value,Instruction}.Pos() positions match
+// the originating syntax, as specified.
+
+import (
+ "go/ast"
+ "go/token"
+ "go/types"
+)
+
+// EnclosingFunction returns the function that contains the syntax
+// node denoted by path.
+//
+// Syntax associated with package-level variable specifications is
+// enclosed by the package's init() function.
+//
+// Returns nil if not found; reasons might include:
+// - the node is not enclosed by any function.
+// - the node is within an anonymous function (FuncLit) and
+// its SSA function has not been created yet
+// (pkg.Build() has not yet been called).
+//
+func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
+ // Start with package-level function...
+ fn := findEnclosingPackageLevelFunction(pkg, path)
+ if fn == nil {
+ return nil // not in any function
+ }
+
+ // ...then walk down the nested anonymous functions.
+ n := len(path)
+outer:
+ for i := range path {
+ if lit, ok := path[n-1-i].(*ast.FuncLit); ok {
+ for _, anon := range fn.AnonFuncs {
+ if anon.Pos() == lit.Type.Func {
+ fn = anon
+ continue outer
+ }
+ }
+ // SSA function not found:
+ // - package not yet built, or maybe
+ // - builder skipped FuncLit in dead block
+ // (in principle; but currently the Builder
+ // generates even dead FuncLits).
+ return nil
+ }
+ }
+ return fn
+}
+
+// HasEnclosingFunction returns true if the AST node denoted by path
+// is contained within the declaration of some function or
+// package-level variable.
+//
+// Unlike EnclosingFunction, the behaviour of this function does not
+// depend on whether SSA code for pkg has been built, so it can be
+// used to quickly reject check inputs that will cause
+// EnclosingFunction to fail, prior to SSA building.
+//
+func HasEnclosingFunction(pkg *Package, path []ast.Node) bool {
+ return findEnclosingPackageLevelFunction(pkg, path) != nil
+}
+
+// findEnclosingPackageLevelFunction returns the Function
+// corresponding to the package-level function enclosing path.
+//
+func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function {
+ if n := len(path); n >= 2 { // [... {Gen,Func}Decl File]
+ switch decl := path[n-2].(type) {
+ case *ast.GenDecl:
+ if decl.Tok == token.VAR && n >= 3 {
+ // Package-level 'var' initializer.
+ return pkg.init
+ }
+
+ case *ast.FuncDecl:
+ if decl.Recv == nil && decl.Name.Name == "init" {
+ // Explicit init() function.
+ for _, b := range pkg.init.Blocks {
+ for _, instr := range b.Instrs {
+ if instr, ok := instr.(*Call); ok {
+ if callee, ok := instr.Call.Value.(*Function); ok && callee.Pkg == pkg && callee.Pos() == decl.Name.NamePos {
+ return callee
+ }
+ }
+ }
+ }
+ // Hack: return non-nil when SSA is not yet
+ // built so that HasEnclosingFunction works.
+ return pkg.init
+ }
+ // Declared function/method.
+ return findNamedFunc(pkg, decl.Name.NamePos)
+ }
+ }
+ return nil // not in any function
+}
+
+// findNamedFunc returns the named function whose FuncDecl.Ident is at
+// position pos.
+//
+func findNamedFunc(pkg *Package, pos token.Pos) *Function {
+ // Look at all package members and method sets of named types.
+ // Not very efficient.
+ for _, mem := range pkg.Members {
+ switch mem := mem.(type) {
+ case *Function:
+ if mem.Pos() == pos {
+ return mem
+ }
+ case *Type:
+ mset := pkg.Prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
+ for i, n := 0, mset.Len(); i < n; i++ {
+ // Don't call Program.Method: avoid creating wrappers.
+ obj := mset.At(i).Obj().(*types.Func)
+ if obj.Pos() == pos {
+ return pkg.values[obj].(*Function)
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// ValueForExpr returns the SSA Value that corresponds to non-constant
+// expression e.
+//
+// It returns nil if no value was found, e.g.
+// - the expression is not lexically contained within f;
+// - f was not built with debug information; or
+// - e is a constant expression. (For efficiency, no debug
+// information is stored for constants. Use
+// go/types.Info.Types[e].Value instead.)
+// - e is a reference to nil or a built-in function.
+// - the value was optimised away.
+//
+// If e is an addressable expression used in an lvalue context,
+// value is the address denoted by e, and isAddr is true.
+//
+// The types of e (or &e, if isAddr) and the result are equal
+// (modulo "untyped" bools resulting from comparisons).
+//
+// (Tip: to find the ssa.Value given a source position, use
+// astutil.PathEnclosingInterval to locate the ast.Node, then
+// EnclosingFunction to locate the Function, then ValueForExpr to find
+// the ssa.Value.)
+//
+func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) {
+ if f.debugInfo() { // (opt)
+ e = unparen(e)
+ for _, b := range f.Blocks {
+ for _, instr := range b.Instrs {
+ if ref, ok := instr.(*DebugRef); ok {
+ if ref.Expr == e {
+ return ref.X, ref.IsAddr
+ }
+ }
+ }
+ }
+ }
+ return
+}
+
+// --- Lookup functions for source-level named entities (types.Objects) ---
+
+// Package returns the SSA Package corresponding to the specified
+// type-checker package object.
+// It returns nil if no such SSA package has been created.
+//
+func (prog *Program) Package(obj *types.Package) *Package {
+ return prog.packages[obj]
+}
+
+// packageLevelValue returns the package-level value corresponding to
+// the specified named object, which may be a package-level const
+// (*Const), var (*Global) or func (*Function) of some package in
+// prog. It returns nil if the object is not found.
+//
+func (prog *Program) packageLevelValue(obj types.Object) Value {
+ if pkg, ok := prog.packages[obj.Pkg()]; ok {
+ return pkg.values[obj]
+ }
+ return nil
+}
+
+// FuncValue returns the concrete Function denoted by the source-level
+// named function obj, or nil if obj denotes an interface method.
+//
+// TODO(adonovan): check the invariant that obj.Type() matches the
+// result's Signature, both in the params/results and in the receiver.
+//
+func (prog *Program) FuncValue(obj *types.Func) *Function {
+ fn, _ := prog.packageLevelValue(obj).(*Function)
+ return fn
+}
+
+// ConstValue returns the SSA Value denoted by the source-level named
+// constant obj.
+//
+func (prog *Program) ConstValue(obj *types.Const) *Const {
+ // TODO(adonovan): opt: share (don't reallocate)
+ // Consts for const objects and constant ast.Exprs.
+
+ // Universal constant? {true,false,nil}
+ if obj.Parent() == types.Universe {
+ return NewConst(obj.Val(), obj.Type())
+ }
+ // Package-level named constant?
+ if v := prog.packageLevelValue(obj); v != nil {
+ return v.(*Const)
+ }
+ return NewConst(obj.Val(), obj.Type())
+}
+
+// VarValue returns the SSA Value that corresponds to a specific
+// identifier denoting the source-level named variable obj.
+//
+// VarValue returns nil if a local variable was not found, perhaps
+// because its package was not built, the debug information was not
+// requested during SSA construction, or the value was optimized away.
+//
+// ref is the path to an ast.Ident (e.g. from PathEnclosingInterval),
+// and that ident must resolve to obj.
+//
+// pkg is the package enclosing the reference. (A reference to a var
+// always occurs within a function, so we need to know where to find it.)
+//
+// If the identifier is a field selector and its base expression is
+// non-addressable, then VarValue returns the value of that field.
+// For example:
+// func f() struct {x int}
+// f().x // VarValue(x) returns a *Field instruction of type int
+//
+// All other identifiers denote addressable locations (variables).
+// For them, VarValue may return either the variable's address or its
+// value, even when the expression is evaluated only for its value; the
+// situation is reported by isAddr, the second component of the result.
+//
+// If !isAddr, the returned value is the one associated with the
+// specific identifier. For example,
+// var x int // VarValue(x) returns Const 0 here
+// x = 1 // VarValue(x) returns Const 1 here
+//
+// It is not specified whether the value or the address is returned in
+// any particular case, as it may depend upon optimizations performed
+// during SSA code generation, such as registerization, constant
+// folding, avoidance of materialization of subexpressions, etc.
+//
+func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) {
+ // All references to a var are local to some function, possibly init.
+ fn := EnclosingFunction(pkg, ref)
+ if fn == nil {
+ return // e.g. def of struct field; SSA not built?
+ }
+
+ id := ref[0].(*ast.Ident)
+
+ // Defining ident of a parameter?
+ if id.Pos() == obj.Pos() {
+ for _, param := range fn.Params {
+ if param.Object() == obj {
+ return param, false
+ }
+ }
+ }
+
+ // Other ident?
+ for _, b := range fn.Blocks {
+ for _, instr := range b.Instrs {
+ if dr, ok := instr.(*DebugRef); ok {
+ if dr.Pos() == id.Pos() {
+ return dr.X, dr.IsAddr
+ }
+ }
+ }
+ }
+
+ // Defining ident of package-level var?
+ if v := prog.packageLevelValue(obj); v != nil {
+ return v.(*Global), true
+ }
+
+ return // e.g. debug info not requested, or var optimized away
+}
diff --git a/vendor/honnef.co/go/tools/ssa/ssa.go b/vendor/honnef.co/go/tools/ssa/ssa.go
new file mode 100644
index 000000000..aeddd65e5
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/ssa.go
@@ -0,0 +1,1745 @@
+// 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 ssa
+
+// This package defines a high-level intermediate representation for
+// Go programs using static single-assignment (SSA) form.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/token"
+ "go/types"
+ "sync"
+
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+// A Program is a partial or complete Go program converted to SSA form.
+type Program struct {
+ Fset *token.FileSet // position information for the files of this Program
+ imported map[string]*Package // all importable Packages, keyed by import path
+ packages map[*types.Package]*Package // all loaded Packages, keyed by object
+ mode BuilderMode // set of mode bits for SSA construction
+ MethodSets typeutil.MethodSetCache // cache of type-checker's method-sets
+
+ methodsMu sync.Mutex // guards the following maps:
+ methodSets typeutil.Map // maps type to its concrete methodSet
+ runtimeTypes typeutil.Map // types for which rtypes are needed
+ canon typeutil.Map // type canonicalization map
+ bounds map[*types.Func]*Function // bounds for curried x.Method closures
+ thunks map[selectionKey]*Function // thunks for T.Method expressions
+}
+
+// A Package is a single analyzed Go package containing Members for
+// all package-level functions, variables, constants and types it
+// declares. These may be accessed directly via Members, or via the
+// type-specific accessor methods Func, Type, Var and Const.
+//
+// Members also contains entries for "init" (the synthetic package
+// initializer) and "init#%d", the nth declared init function,
+// and unspecified other things too.
+//
+type Package struct {
+ Prog *Program // the owning program
+ Pkg *types.Package // the corresponding go/types.Package
+ Members map[string]Member // all package members keyed by name (incl. init and init#%d)
+ values map[types.Object]Value // package members (incl. types and methods), keyed by object
+ init *Function // Func("init"); the package's init function
+ debug bool // include full debug info in this package
+
+ // The following fields are set transiently, then cleared
+ // after building.
+ buildOnce sync.Once // ensures package building occurs once
+ ninit int32 // number of init functions
+ info *types.Info // package type information
+ files []*ast.File // package ASTs
+}
+
+// A Member is a member of a Go package, implemented by *NamedConst,
+// *Global, *Function, or *Type; they are created by package-level
+// const, var, func and type declarations respectively.
+//
+type Member interface {
+ Name() string // declared name of the package member
+ String() string // package-qualified name of the package member
+ RelString(*types.Package) string // like String, but relative refs are unqualified
+ Object() types.Object // typechecker's object for this member, if any
+ Pos() token.Pos // position of member's declaration, if known
+ Type() types.Type // type of the package member
+ Token() token.Token // token.{VAR,FUNC,CONST,TYPE}
+ Package() *Package // the containing package
+}
+
+// A Type is a Member of a Package representing a package-level named type.
+type Type struct {
+ object *types.TypeName
+ pkg *Package
+}
+
+// A NamedConst is a Member of a Package representing a package-level
+// named constant.
+//
+// Pos() returns the position of the declaring ast.ValueSpec.Names[*]
+// identifier.
+//
+// NB: a NamedConst is not a Value; it contains a constant Value, which
+// it augments with the name and position of its 'const' declaration.
+//
+type NamedConst struct {
+ object *types.Const
+ Value *Const
+ pkg *Package
+}
+
+// A Value is an SSA value that can be referenced by an instruction.
+type Value interface {
+ // Name returns the name of this value, and determines how
+ // this Value appears when used as an operand of an
+ // Instruction.
+ //
+ // This is the same as the source name for Parameters,
+ // Builtins, Functions, FreeVars, Globals.
+ // For constants, it is a representation of the constant's value
+ // and type. For all other Values this is the name of the
+ // virtual register defined by the instruction.
+ //
+ // The name of an SSA Value is not semantically significant,
+ // and may not even be unique within a function.
+ Name() string
+
+ // If this value is an Instruction, String returns its
+ // disassembled form; otherwise it returns unspecified
+ // human-readable information about the Value, such as its
+ // kind, name and type.
+ String() string
+
+ // Type returns the type of this value. Many instructions
+ // (e.g. IndexAddr) change their behaviour depending on the
+ // types of their operands.
+ Type() types.Type
+
+ // Parent returns the function to which this Value belongs.
+ // It returns nil for named Functions, Builtin, Const and Global.
+ Parent() *Function
+
+ // Referrers returns the list of instructions that have this
+ // value as one of their operands; it may contain duplicates
+ // if an instruction has a repeated operand.
+ //
+ // Referrers actually returns a pointer through which the
+ // caller may perform mutations to the object's state.
+ //
+ // Referrers is currently only defined if Parent()!=nil,
+ // i.e. for the function-local values FreeVar, Parameter,
+ // Functions (iff anonymous) and all value-defining instructions.
+ // It returns nil for named Functions, Builtin, Const and Global.
+ //
+ // Instruction.Operands contains the inverse of this relation.
+ Referrers() *[]Instruction
+
+ // Pos returns the location of the AST token most closely
+ // associated with the operation that gave rise to this value,
+ // or token.NoPos if it was not explicit in the source.
+ //
+ // For each ast.Node type, a particular token is designated as
+ // the closest location for the expression, e.g. the Lparen
+ // for an *ast.CallExpr. This permits a compact but
+ // approximate mapping from Values to source positions for use
+ // in diagnostic messages, for example.
+ //
+ // (Do not use this position to determine which Value
+ // corresponds to an ast.Expr; use Function.ValueForExpr
+ // instead. NB: it requires that the function was built with
+ // debug information.)
+ Pos() token.Pos
+}
+
+// An Instruction is an SSA instruction that computes a new Value or
+// has some effect.
+//
+// An Instruction that defines a value (e.g. BinOp) also implements
+// the Value interface; an Instruction that only has an effect (e.g. Store)
+// does not.
+//
+type Instruction interface {
+ // String returns the disassembled form of this value.
+ //
+ // Examples of Instructions that are Values:
+ // "x + y" (BinOp)
+ // "len([])" (Call)
+ // Note that the name of the Value is not printed.
+ //
+ // Examples of Instructions that are not Values:
+ // "return x" (Return)
+ // "*y = x" (Store)
+ //
+ // (The separation Value.Name() from Value.String() is useful
+ // for some analyses which distinguish the operation from the
+ // value it defines, e.g., 'y = local int' is both an allocation
+ // of memory 'local int' and a definition of a pointer y.)
+ String() string
+
+ // Parent returns the function to which this instruction
+ // belongs.
+ Parent() *Function
+
+ // Block returns the basic block to which this instruction
+ // belongs.
+ Block() *BasicBlock
+
+ // setBlock sets the basic block to which this instruction belongs.
+ setBlock(*BasicBlock)
+
+ // Operands returns the operands of this instruction: the
+ // set of Values it references.
+ //
+ // Specifically, it appends their addresses to rands, a
+ // user-provided slice, and returns the resulting slice,
+ // permitting avoidance of memory allocation.
+ //
+ // The operands are appended in undefined order, but the order
+ // is consistent for a given Instruction; the addresses are
+ // always non-nil but may point to a nil Value. Clients may
+ // store through the pointers, e.g. to effect a value
+ // renaming.
+ //
+ // Value.Referrers is a subset of the inverse of this
+ // relation. (Referrers are not tracked for all types of
+ // Values.)
+ Operands(rands []*Value) []*Value
+
+ // Pos returns the location of the AST token most closely
+ // associated with the operation that gave rise to this
+ // instruction, or token.NoPos if it was not explicit in the
+ // source.
+ //
+ // For each ast.Node type, a particular token is designated as
+ // the closest location for the expression, e.g. the Go token
+ // for an *ast.GoStmt. This permits a compact but approximate
+ // mapping from Instructions to source positions for use in
+ // diagnostic messages, for example.
+ //
+ // (Do not use this position to determine which Instruction
+ // corresponds to an ast.Expr; see the notes for Value.Pos.
+ // This position may be used to determine which non-Value
+ // Instruction corresponds to some ast.Stmts, but not all: If
+ // and Jump instructions have no Pos(), for example.)
+ Pos() token.Pos
+}
+
+// A Node is a node in the SSA value graph. Every concrete type that
+// implements Node is also either a Value, an Instruction, or both.
+//
+// Node contains the methods common to Value and Instruction, plus the
+// Operands and Referrers methods generalized to return nil for
+// non-Instructions and non-Values, respectively.
+//
+// Node is provided to simplify SSA graph algorithms. Clients should
+// use the more specific and informative Value or Instruction
+// interfaces where appropriate.
+//
+type Node interface {
+ // Common methods:
+ String() string
+ Pos() token.Pos
+ Parent() *Function
+
+ // Partial methods:
+ Operands(rands []*Value) []*Value // nil for non-Instructions
+ Referrers() *[]Instruction // nil for non-Values
+}
+
+// Function represents the parameters, results, and code of a function
+// or method.
+//
+// If Blocks is nil, this indicates an external function for which no
+// Go source code is available. In this case, FreeVars and Locals
+// are nil too. Clients performing whole-program analysis must
+// handle external functions specially.
+//
+// Blocks contains the function's control-flow graph (CFG).
+// Blocks[0] is the function entry point; block order is not otherwise
+// semantically significant, though it may affect the readability of
+// the disassembly.
+// To iterate over the blocks in dominance order, use DomPreorder().
+//
+// Recover is an optional second entry point to which control resumes
+// after a recovered panic. The Recover block may contain only a return
+// statement, preceded by a load of the function's named return
+// parameters, if any.
+//
+// A nested function (Parent()!=nil) that refers to one or more
+// lexically enclosing local variables ("free variables") has FreeVars.
+// Such functions cannot be called directly but require a
+// value created by MakeClosure which, via its Bindings, supplies
+// values for these parameters.
+//
+// If the function is a method (Signature.Recv() != nil) then the first
+// element of Params is the receiver parameter.
+//
+// A Go package may declare many functions called "init".
+// For each one, Object().Name() returns "init" but Name() returns
+// "init#1", etc, in declaration order.
+//
+// Pos() returns the declaring ast.FuncLit.Type.Func or the position
+// of the ast.FuncDecl.Name, if the function was explicit in the
+// source. Synthetic wrappers, for which Synthetic != "", may share
+// the same position as the function they wrap.
+// Syntax.Pos() always returns the position of the declaring "func" token.
+//
+// Type() returns the function's Signature.
+//
+type Function struct {
+ name string
+ object types.Object // a declared *types.Func or one of its wrappers
+ method *types.Selection // info about provenance of synthetic methods
+ Signature *types.Signature
+ pos token.Pos
+
+ Synthetic string // provenance of synthetic function; "" for true source functions
+ syntax ast.Node // *ast.Func{Decl,Lit}; replaced with simple ast.Node after build, unless debug mode
+ parent *Function // enclosing function if anon; nil if global
+ Pkg *Package // enclosing package; nil for shared funcs (wrappers and error.Error)
+ Prog *Program // enclosing program
+ Params []*Parameter // function parameters; for methods, includes receiver
+ FreeVars []*FreeVar // free variables whose values must be supplied by closure
+ Locals []*Alloc // local variables of this function
+ Blocks []*BasicBlock // basic blocks of the function; nil => external
+ Recover *BasicBlock // optional; control transfers here after recovered panic
+ AnonFuncs []*Function // anonymous functions directly beneath this one
+ referrers []Instruction // referring instructions (iff Parent() != nil)
+
+ // The following fields are set transiently during building,
+ // then cleared.
+ currentBlock *BasicBlock // where to emit code
+ objects map[types.Object]Value // addresses of local variables
+ namedResults []*Alloc // tuple of named results
+ targets *targets // linked stack of branch targets
+ lblocks map[*ast.Object]*lblock // labelled blocks
+}
+
+// BasicBlock represents an SSA basic block.
+//
+// The final element of Instrs is always an explicit transfer of
+// control (If, Jump, Return, or Panic).
+//
+// A block may contain no Instructions only if it is unreachable,
+// i.e., Preds is nil. Empty blocks are typically pruned.
+//
+// BasicBlocks and their Preds/Succs relation form a (possibly cyclic)
+// graph independent of the SSA Value graph: the control-flow graph or
+// CFG. It is illegal for multiple edges to exist between the same
+// pair of blocks.
+//
+// Each BasicBlock is also a node in the dominator tree of the CFG.
+// The tree may be navigated using Idom()/Dominees() and queried using
+// Dominates().
+//
+// The order of Preds and Succs is significant (to Phi and If
+// instructions, respectively).
+//
+type BasicBlock struct {
+ Index int // index of this block within Parent().Blocks
+ Comment string // optional label; no semantic significance
+ parent *Function // parent function
+ Instrs []Instruction // instructions in order
+ Preds, Succs []*BasicBlock // predecessors and successors
+ succs2 [2]*BasicBlock // initial space for Succs
+ dom domInfo // dominator tree info
+ gaps int // number of nil Instrs (transient)
+ rundefers int // number of rundefers (transient)
+}
+
+// Pure values ----------------------------------------
+
+// A FreeVar represents a free variable of the function to which it
+// belongs.
+//
+// FreeVars are used to implement anonymous functions, whose free
+// variables are lexically captured in a closure formed by
+// MakeClosure. The value of such a free var is an Alloc or another
+// FreeVar and is considered a potentially escaping heap address, with
+// pointer type.
+//
+// FreeVars are also used to implement bound method closures. Such a
+// free var represents the receiver value and may be of any type that
+// has concrete methods.
+//
+// Pos() returns the position of the value that was captured, which
+// belongs to an enclosing function.
+//
+type FreeVar struct {
+ name string
+ typ types.Type
+ pos token.Pos
+ parent *Function
+ referrers []Instruction
+
+ // Transiently needed during building.
+ outer Value // the Value captured from the enclosing context.
+}
+
+// A Parameter represents an input parameter of a function.
+//
+type Parameter struct {
+ name string
+ object types.Object // a *types.Var; nil for non-source locals
+ typ types.Type
+ pos token.Pos
+ parent *Function
+ referrers []Instruction
+}
+
+// A Const represents the value of a constant expression.
+//
+// The underlying type of a constant may be any boolean, numeric, or
+// string type. In addition, a Const may represent the nil value of
+// any reference type---interface, map, channel, pointer, slice, or
+// function---but not "untyped nil".
+//
+// All source-level constant expressions are represented by a Const
+// 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/constant uses for
+// constants, or nil for a typed nil value.
+//
+// Pos() returns token.NoPos.
+//
+// Example printed form:
+// 42:int
+// "hello":untyped string
+// 3+4i:MyComplex
+//
+type Const struct {
+ typ types.Type
+ Value constant.Value
+}
+
+// A Global is a named Value holding the address of a package-level
+// variable.
+//
+// Pos() returns the position of the ast.ValueSpec.Names[*]
+// identifier.
+//
+type Global struct {
+ name string
+ object types.Object // a *types.Var; may be nil for synthetics e.g. init$guard
+ typ types.Type
+ pos token.Pos
+
+ Pkg *Package
+}
+
+// A Builtin represents a specific use of a built-in function, e.g. len.
+//
+// Builtins are immutable values. Builtins do not have addresses.
+// Builtins can only appear in CallCommon.Func.
+//
+// Name() indicates the function: one of the built-in functions from the
+// Go spec (excluding "make" and "new") or one of these ssa-defined
+// intrinsics:
+//
+// // wrapnilchk returns ptr if non-nil, panics otherwise.
+// // (For use in indirection wrappers.)
+// func ssa:wrapnilchk(ptr *T, recvType, methodName string) *T
+//
+// Object() returns a *types.Builtin for built-ins defined by the spec,
+// nil for others.
+//
+// Type() returns a *types.Signature representing the effective
+// signature of the built-in for this call.
+//
+type Builtin struct {
+ name string
+ sig *types.Signature
+}
+
+// Value-defining instructions ----------------------------------------
+
+// The Alloc instruction reserves space for a variable of the given type,
+// zero-initializes it, and yields its address.
+//
+// Alloc values are always addresses, and have pointer types, so the
+// type of the allocated variable is actually
+// Type().Underlying().(*types.Pointer).Elem().
+//
+// If Heap is false, Alloc allocates space in the function's
+// activation record (frame); we refer to an Alloc(Heap=false) as a
+// "local" alloc. Each local Alloc returns the same address each time
+// it is executed within the same activation; the space is
+// re-initialized to zero.
+//
+// If Heap is true, Alloc allocates space in the heap; we
+// refer to an Alloc(Heap=true) as a "new" alloc. Each new Alloc
+// returns a different address each time it is executed.
+//
+// When Alloc is applied to a channel, map or slice type, it returns
+// the address of an uninitialized (nil) reference of that kind; store
+// the result of MakeSlice, MakeMap or MakeChan in that location to
+// instantiate these types.
+//
+// Pos() returns the ast.CompositeLit.Lbrace for a composite literal,
+// or the ast.CallExpr.Rparen for a call to new() or for a call that
+// allocates a varargs slice.
+//
+// Example printed form:
+// t0 = local int
+// t1 = new int
+//
+type Alloc struct {
+ register
+ Comment string
+ Heap bool
+ index int // dense numbering; for lifting
+}
+
+var _ Instruction = (*Sigma)(nil)
+var _ Value = (*Sigma)(nil)
+
+type Sigma struct {
+ register
+ X Value
+ Branch bool
+}
+
+func (p *Sigma) Value() Value {
+ v := p.X
+ for {
+ sigma, ok := v.(*Sigma)
+ if !ok {
+ break
+ }
+ v = sigma
+ }
+ return v
+}
+
+func (p *Sigma) String() string {
+ return fmt.Sprintf("σ [%s.%t]", relName(p.X, p), p.Branch)
+}
+
+// The Phi instruction represents an SSA φ-node, which combines values
+// that differ across incoming control-flow edges and yields a new
+// value. Within a block, all φ-nodes must appear before all non-φ
+// nodes.
+//
+// Pos() returns the position of the && or || for short-circuit
+// control-flow joins, or that of the *Alloc for φ-nodes inserted
+// during SSA renaming.
+//
+// Example printed form:
+// t2 = phi [0: t0, 1: t1]
+//
+type Phi struct {
+ register
+ Comment string // a hint as to its purpose
+ Edges []Value // Edges[i] is value for Block().Preds[i]
+}
+
+// The Call instruction represents a function or method call.
+//
+// The Call instruction yields the function result if there is exactly
+// one. Otherwise it returns a tuple, the components of which are
+// accessed via Extract.
+//
+// See CallCommon for generic function call documentation.
+//
+// Pos() returns the ast.CallExpr.Lparen, if explicit in the source.
+//
+// Example printed form:
+// t2 = println(t0, t1)
+// t4 = t3()
+// t7 = invoke t5.Println(...t6)
+//
+type Call struct {
+ register
+ Call CallCommon
+}
+
+// The BinOp instruction yields the result of binary operation X Op Y.
+//
+// Pos() returns the ast.BinaryExpr.OpPos, if explicit in the source.
+//
+// Example printed form:
+// t1 = t0 + 1:int
+//
+type BinOp struct {
+ register
+ // One of:
+ // ADD SUB MUL QUO REM + - * / %
+ // AND OR XOR SHL SHR AND_NOT & | ^ << >> &^
+ // EQL NEQ LSS LEQ GTR GEQ == != < <= < >=
+ Op token.Token
+ X, Y Value
+}
+
+// The UnOp instruction yields the result of Op X.
+// ARROW is channel receive.
+// MUL is pointer indirection (load).
+// XOR is bitwise complement.
+// SUB is negation.
+// NOT is logical negation.
+//
+// If CommaOk and Op=ARROW, the result is a 2-tuple of the value above
+// and a boolean indicating the success of the receive. The
+// components of the tuple are accessed using Extract.
+//
+// Pos() returns the ast.UnaryExpr.OpPos, if explicit in the source.
+// For receive operations (ARROW) implicit in ranging over a channel,
+// Pos() returns the ast.RangeStmt.For.
+// For implicit memory loads (STAR), Pos() returns the position of the
+// most closely associated source-level construct; the details are not
+// specified.
+//
+// Example printed form:
+// t0 = *x
+// t2 = <-t1,ok
+//
+type UnOp struct {
+ register
+ Op token.Token // One of: NOT SUB ARROW MUL XOR ! - <- * ^
+ X Value
+ CommaOk bool
+}
+
+// The ChangeType instruction applies to X a value-preserving type
+// change to Type().
+//
+// Type changes are permitted:
+// - between a named type and its underlying type.
+// - between two named types of the same underlying type.
+// - between (possibly named) pointers to identical base types.
+// - from a bidirectional channel to a read- or write-channel,
+// optionally adding/removing a name.
+//
+// This operation cannot fail dynamically.
+//
+// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
+// from an explicit conversion in the source.
+//
+// Example printed form:
+// t1 = changetype *int <- IntPtr (t0)
+//
+type ChangeType struct {
+ register
+ X Value
+}
+
+// The Convert instruction yields the conversion of value X to type
+// Type(). One or both of those types is basic (but possibly named).
+//
+// A conversion may change the value and representation of its operand.
+// Conversions are permitted:
+// - between real numeric types.
+// - between complex numeric types.
+// - between string and []byte or []rune.
+// - between pointers and unsafe.Pointer.
+// - between unsafe.Pointer and uintptr.
+// - from (Unicode) integer to (UTF-8) string.
+// A conversion may imply a type name change also.
+//
+// This operation cannot fail dynamically.
+//
+// Conversions of untyped string/number/bool constants to a specific
+// representation are eliminated during SSA construction.
+//
+// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
+// from an explicit conversion in the source.
+//
+// Example printed form:
+// t1 = convert []byte <- string (t0)
+//
+type Convert struct {
+ register
+ X Value
+}
+
+// ChangeInterface constructs a value of one interface type from a
+// value of another interface type known to be assignable to it.
+// This operation cannot fail.
+//
+// Pos() returns the ast.CallExpr.Lparen if the instruction arose from
+// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the
+// instruction arose from an explicit e.(T) operation; or token.NoPos
+// otherwise.
+//
+// Example printed form:
+// t1 = change interface interface{} <- I (t0)
+//
+type ChangeInterface struct {
+ register
+ X Value
+}
+
+// MakeInterface constructs an instance of an interface type from a
+// value of a concrete type.
+//
+// Use Program.MethodSets.MethodSet(X.Type()) to find the method-set
+// 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(constant.MakeNil(), T, pos)
+//
+// Pos() returns the ast.CallExpr.Lparen, if the instruction arose
+// from an explicit conversion in the source.
+//
+// Example printed form:
+// t1 = make interface{} <- int (42:int)
+// t2 = make Stringer <- t0
+//
+type MakeInterface struct {
+ register
+ X Value
+}
+
+// The MakeClosure instruction yields a closure value whose code is
+// Fn and whose free variables' values are supplied by Bindings.
+//
+// Type() returns a (possibly named) *types.Signature.
+//
+// Pos() returns the ast.FuncLit.Type.Func for a function literal
+// closure or the ast.SelectorExpr.Sel for a bound method closure.
+//
+// Example printed form:
+// t0 = make closure anon@1.2 [x y z]
+// t1 = make closure bound$(main.I).add [i]
+//
+type MakeClosure struct {
+ register
+ Fn Value // always a *Function
+ Bindings []Value // values for each free variable in Fn.FreeVars
+}
+
+// The MakeMap instruction creates a new hash-table-based map object
+// and yields a value of kind map.
+//
+// Type() returns a (possibly named) *types.Map.
+//
+// Pos() returns the ast.CallExpr.Lparen, if created by make(map), or
+// the ast.CompositeLit.Lbrack if created by a literal.
+//
+// Example printed form:
+// t1 = make map[string]int t0
+// t1 = make StringIntMap t0
+//
+type MakeMap struct {
+ register
+ Reserve Value // initial space reservation; nil => default
+}
+
+// The MakeChan instruction creates a new channel object and yields a
+// value of kind chan.
+//
+// Type() returns a (possibly named) *types.Chan.
+//
+// Pos() returns the ast.CallExpr.Lparen for the make(chan) that
+// created it.
+//
+// Example printed form:
+// t0 = make chan int 0
+// t0 = make IntChan 0
+//
+type MakeChan struct {
+ register
+ Size Value // int; size of buffer; zero => synchronous.
+}
+
+// The MakeSlice instruction yields a slice of length Len backed by a
+// newly allocated array of length Cap.
+//
+// Both Len and Cap must be non-nil Values of integer type.
+//
+// (Alloc(types.Array) followed by Slice will not suffice because
+// Alloc can only create arrays of constant length.)
+//
+// Type() returns a (possibly named) *types.Slice.
+//
+// Pos() returns the ast.CallExpr.Lparen for the make([]T) that
+// created it.
+//
+// Example printed form:
+// t1 = make []string 1:int t0
+// t1 = make StringSlice 1:int t0
+//
+type MakeSlice struct {
+ register
+ Len Value
+ Cap Value
+}
+
+// The Slice instruction yields a slice of an existing string, slice
+// or *array X between optional integer bounds Low and High.
+//
+// Dynamically, this instruction panics if X evaluates to a nil *array
+// pointer.
+//
+// Type() returns string if the type of X was string, otherwise a
+// *types.Slice with the same element type as X.
+//
+// Pos() returns the ast.SliceExpr.Lbrack if created by a x[:] slice
+// operation, the ast.CompositeLit.Lbrace if created by a literal, or
+// NoPos if not explicit in the source (e.g. a variadic argument slice).
+//
+// Example printed form:
+// t1 = slice t0[1:]
+//
+type Slice struct {
+ register
+ X Value // slice, string, or *array
+ Low, High, Max Value // each may be nil
+}
+
+// The FieldAddr instruction yields the address of Field of *struct X.
+//
+// The field is identified by its index within the field list of the
+// struct type of X.
+//
+// Dynamically, this instruction panics if X evaluates to a nil
+// pointer.
+//
+// Type() returns a (possibly named) *types.Pointer.
+//
+// Pos() returns the position of the ast.SelectorExpr.Sel for the
+// field, if explicit in the source.
+//
+// Example printed form:
+// t1 = &t0.name [#1]
+//
+type FieldAddr struct {
+ register
+ X Value // *struct
+ Field int // field is X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct).Field(Field)
+}
+
+// The Field instruction yields the Field of struct X.
+//
+// The field is identified by its index within the field list of the
+// struct type of X; by using numeric indices we avoid ambiguity of
+// package-local identifiers and permit compact representations.
+//
+// Pos() returns the position of the ast.SelectorExpr.Sel for the
+// field, if explicit in the source.
+//
+// Example printed form:
+// t1 = t0.name [#1]
+//
+type Field struct {
+ register
+ X Value // struct
+ Field int // index into X.Type().(*types.Struct).Fields
+}
+
+// The IndexAddr instruction yields the address of the element at
+// index Index of collection X. Index is an integer expression.
+//
+// The elements of maps and strings are not addressable; use Lookup or
+// MapUpdate instead.
+//
+// Dynamically, this instruction panics if X evaluates to a nil *array
+// pointer.
+//
+// Type() returns a (possibly named) *types.Pointer.
+//
+// Pos() returns the ast.IndexExpr.Lbrack for the index operation, if
+// explicit in the source.
+//
+// Example printed form:
+// t2 = &t0[t1]
+//
+type IndexAddr struct {
+ register
+ X Value // slice or *array,
+ Index Value // numeric index
+}
+
+// The Index instruction yields element Index of array X.
+//
+// Pos() returns the ast.IndexExpr.Lbrack for the index operation, if
+// explicit in the source.
+//
+// Example printed form:
+// t2 = t0[t1]
+//
+type Index struct {
+ register
+ X Value // array
+ Index Value // integer index
+}
+
+// The Lookup instruction yields element Index of collection X, a map
+// or string. Index is an integer expression if X is a string or the
+// appropriate key type if X is a map.
+//
+// If CommaOk, the result is a 2-tuple of the value above and a
+// boolean indicating the result of a map membership test for the key.
+// The components of the tuple are accessed using Extract.
+//
+// Pos() returns the ast.IndexExpr.Lbrack, if explicit in the source.
+//
+// Example printed form:
+// t2 = t0[t1]
+// t5 = t3[t4],ok
+//
+type Lookup struct {
+ register
+ X Value // string or map
+ Index Value // numeric or key-typed index
+ CommaOk bool // return a value,ok pair
+}
+
+// SelectState is a helper for Select.
+// It represents one goal state and its corresponding communication.
+//
+type SelectState struct {
+ Dir types.ChanDir // direction of case (SendOnly or RecvOnly)
+ Chan Value // channel to use (for send or receive)
+ Send Value // value to send (for send)
+ Pos token.Pos // position of token.ARROW
+ DebugNode ast.Node // ast.SendStmt or ast.UnaryExpr(<-) [debug mode]
+}
+
+// The Select instruction tests whether (or blocks until) one
+// of the specified sent or received states is entered.
+//
+// Let n be the number of States for which Dir==RECV and T_i (0<=i string iterator; false => map iterator.
+}
+
+// The TypeAssert instruction tests whether interface value X has type
+// AssertedType.
+//
+// If !CommaOk, on success it returns v, the result of the conversion
+// (defined below); on failure it panics.
+//
+// If CommaOk: on success it returns a pair (v, true) where v is the
+// result of the conversion; on failure it returns (z, false) where z
+// is AssertedType's zero value. The components of the pair must be
+// accessed using the Extract instruction.
+//
+// If AssertedType is a concrete type, TypeAssert checks whether the
+// dynamic type in interface X is equal to it, and if so, the result
+// of the conversion is a copy of the value in the interface.
+//
+// If AssertedType is an interface, TypeAssert checks whether the
+// dynamic type of the interface is assignable to it, and if so, the
+// result of the conversion is a copy of the interface value X.
+// If AssertedType is a superinterface of X.Type(), the operation will
+// fail iff the operand is nil. (Contrast with ChangeInterface, which
+// performs no nil-check.)
+//
+// Type() reflects the actual type of the result, possibly a
+// 2-types.Tuple; AssertedType is the asserted type.
+//
+// Pos() returns the ast.CallExpr.Lparen if the instruction arose from
+// an explicit T(e) conversion; the ast.TypeAssertExpr.Lparen if the
+// instruction arose from an explicit e.(T) operation; or the
+// ast.CaseClause.Case if the instruction arose from a case of a
+// type-switch statement.
+//
+// Example printed form:
+// t1 = typeassert t0.(int)
+// t3 = typeassert,ok t2.(T)
+//
+type TypeAssert struct {
+ register
+ X Value
+ AssertedType types.Type
+ CommaOk bool
+}
+
+// The Extract instruction yields component Index of Tuple.
+//
+// This is used to access the results of instructions with multiple
+// return values, such as Call, TypeAssert, Next, UnOp(ARROW) and
+// IndexExpr(Map).
+//
+// Example printed form:
+// t1 = extract t0 #1
+//
+type Extract struct {
+ register
+ Tuple Value
+ Index int
+}
+
+// Instructions executed for effect. They do not yield a value. --------------------
+
+// The Jump instruction transfers control to the sole successor of its
+// owning block.
+//
+// A Jump must be the last instruction of its containing BasicBlock.
+//
+// Pos() returns NoPos.
+//
+// Example printed form:
+// jump done
+//
+type Jump struct {
+ anInstruction
+}
+
+// The If instruction transfers control to one of the two successors
+// of its owning block, depending on the boolean Cond: the first if
+// true, the second if false.
+//
+// An If instruction must be the last instruction of its containing
+// BasicBlock.
+//
+// Pos() returns NoPos.
+//
+// Example printed form:
+// if t0 goto done else body
+//
+type If struct {
+ anInstruction
+ Cond Value
+}
+
+// The Return instruction returns values and control back to the calling
+// function.
+//
+// len(Results) is always equal to the number of results in the
+// function's signature.
+//
+// If len(Results) > 1, Return returns a tuple value with the specified
+// components which the caller must access using Extract instructions.
+//
+// There is no instruction to return a ready-made tuple like those
+// returned by a "value,ok"-mode TypeAssert, Lookup or UnOp(ARROW) or
+// a tail-call to a function with multiple result parameters.
+//
+// Return must be the last instruction of its containing BasicBlock.
+// Such a block has no successors.
+//
+// Pos() returns the ast.ReturnStmt.Return, if explicit in the source.
+//
+// Example printed form:
+// return
+// return nil:I, 2:int
+//
+type Return struct {
+ anInstruction
+ Results []Value
+ pos token.Pos
+}
+
+// The RunDefers instruction pops and invokes the entire stack of
+// procedure calls pushed by Defer instructions in this function.
+//
+// It is legal to encounter multiple 'rundefers' instructions in a
+// single control-flow path through a function; this is useful in
+// the combined init() function, for example.
+//
+// Pos() returns NoPos.
+//
+// Example printed form:
+// rundefers
+//
+type RunDefers struct {
+ anInstruction
+}
+
+// The Panic instruction initiates a panic with value X.
+//
+// A Panic instruction must be the last instruction of its containing
+// BasicBlock, which must have no successors.
+//
+// NB: 'go panic(x)' and 'defer panic(x)' do not use this instruction;
+// they are treated as calls to a built-in function.
+//
+// Pos() returns the ast.CallExpr.Lparen if this panic was explicit
+// in the source.
+//
+// Example printed form:
+// panic t0
+//
+type Panic struct {
+ anInstruction
+ X Value // an interface{}
+ pos token.Pos
+}
+
+// The Go instruction creates a new goroutine and calls the specified
+// function within it.
+//
+// See CallCommon for generic function call documentation.
+//
+// Pos() returns the ast.GoStmt.Go.
+//
+// Example printed form:
+// go println(t0, t1)
+// go t3()
+// go invoke t5.Println(...t6)
+//
+type Go struct {
+ anInstruction
+ Call CallCommon
+ pos token.Pos
+}
+
+// The Defer instruction pushes the specified call onto a stack of
+// functions to be called by a RunDefers instruction or by a panic.
+//
+// See CallCommon for generic function call documentation.
+//
+// Pos() returns the ast.DeferStmt.Defer.
+//
+// Example printed form:
+// defer println(t0, t1)
+// defer t3()
+// defer invoke t5.Println(...t6)
+//
+type Defer struct {
+ anInstruction
+ Call CallCommon
+ pos token.Pos
+}
+
+// The Send instruction sends X on channel Chan.
+//
+// Pos() returns the ast.SendStmt.Arrow, if explicit in the source.
+//
+// Example printed form:
+// send t0 <- t1
+//
+type Send struct {
+ anInstruction
+ Chan, X Value
+ pos token.Pos
+}
+
+// The Store instruction stores Val at address Addr.
+// Stores can be of arbitrary types.
+//
+// Pos() returns the position of the source-level construct most closely
+// associated with the memory store operation.
+// Since implicit memory stores are numerous and varied and depend upon
+// implementation choices, the details are not specified.
+//
+// Example printed form:
+// *x = y
+//
+type Store struct {
+ anInstruction
+ Addr Value
+ Val Value
+ pos token.Pos
+}
+
+// The BlankStore instruction is emitted for assignments to the blank
+// identifier.
+//
+// BlankStore is a pseudo-instruction: it has no dynamic effect.
+//
+// Pos() returns NoPos.
+//
+// Example printed form:
+// _ = t0
+//
+type BlankStore struct {
+ anInstruction
+ Val Value
+}
+
+// The MapUpdate instruction updates the association of Map[Key] to
+// Value.
+//
+// Pos() returns the ast.KeyValueExpr.Colon or ast.IndexExpr.Lbrack,
+// if explicit in the source.
+//
+// Example printed form:
+// t0[t1] = t2
+//
+type MapUpdate struct {
+ anInstruction
+ Map Value
+ Key Value
+ Value Value
+ pos token.Pos
+}
+
+// A DebugRef instruction maps a source-level expression Expr to the
+// SSA value X that represents the value (!IsAddr) or address (IsAddr)
+// of that expression.
+//
+// DebugRef is a pseudo-instruction: it has no dynamic effect.
+//
+// Pos() returns Expr.Pos(), the start position of the source-level
+// expression. This is not the same as the "designated" token as
+// documented at Value.Pos(). e.g. CallExpr.Pos() does not return the
+// position of the ("designated") Lparen token.
+//
+// If Expr is an *ast.Ident denoting a var or func, Object() returns
+// the object; though this information can be obtained from the type
+// checker, including it here greatly facilitates debugging.
+// For non-Ident expressions, Object() returns nil.
+//
+// DebugRefs are generated only for functions built with debugging
+// enabled; see Package.SetDebugMode() and the GlobalDebug builder
+// mode flag.
+//
+// DebugRefs are not emitted for ast.Idents referring to constants or
+// predeclared identifiers, since they are trivial and numerous.
+// Nor are they emitted for ast.ParenExprs.
+//
+// (By representing these as instructions, rather than out-of-band,
+// consistency is maintained during transformation passes by the
+// ordinary SSA renaming machinery.)
+//
+// Example printed form:
+// ; *ast.CallExpr @ 102:9 is t5
+// ; var x float64 @ 109:72 is x
+// ; address of *ast.CompositeLit @ 216:10 is t0
+//
+type DebugRef struct {
+ anInstruction
+ Expr ast.Expr // the referring expression (never *ast.ParenExpr)
+ object types.Object // the identity of the source var/func
+ IsAddr bool // Expr is addressable and X is the address it denotes
+ X Value // the value or address of Expr
+}
+
+// Embeddable mix-ins and helpers for common parts of other structs. -----------
+
+// register is a mix-in embedded by all SSA values that are also
+// instructions, i.e. virtual registers, and provides a uniform
+// implementation of most of the Value interface: Value.Name() is a
+// numbered register (e.g. "t0"); the other methods are field accessors.
+//
+// Temporary names are automatically assigned to each register on
+// completion of building a function in SSA form.
+//
+// Clients must not assume that the 'id' value (and the Name() derived
+// from it) is unique within a function. As always in this API,
+// semantics are determined only by identity; names exist only to
+// facilitate debugging.
+//
+type register struct {
+ anInstruction
+ num int // "name" of virtual register, e.g. "t0". Not guaranteed unique.
+ typ types.Type // type of virtual register
+ pos token.Pos // position of source expression, or NoPos
+ referrers []Instruction
+}
+
+// anInstruction is a mix-in embedded by all Instructions.
+// It provides the implementations of the Block and setBlock methods.
+type anInstruction struct {
+ block *BasicBlock // the basic block of this instruction
+}
+
+// CallCommon is contained by Go, Defer and Call to hold the
+// common parts of a function or method call.
+//
+// Each CallCommon exists in one of two modes, function call and
+// interface method invocation, or "call" and "invoke" for short.
+//
+// 1. "call" mode: when Method is nil (!IsInvoke), a CallCommon
+// represents an ordinary function call of the value in Value,
+// which may be a *Builtin, a *Function or any other value of kind
+// 'func'.
+//
+// Value may be one of:
+// (a) a *Function, indicating a statically dispatched call
+// to a package-level function, an anonymous function, or
+// a method of a named type.
+// (b) a *MakeClosure, indicating an immediately applied
+// function literal with free variables.
+// (c) a *Builtin, indicating a statically dispatched call
+// to a built-in function.
+// (d) any other value, indicating a dynamically dispatched
+// function call.
+// StaticCallee returns the identity of the callee in cases
+// (a) and (b), nil otherwise.
+//
+// Args contains the arguments to the call. If Value is a method,
+// Args[0] contains the receiver parameter.
+//
+// Example printed form:
+// t2 = println(t0, t1)
+// go t3()
+// defer t5(...t6)
+//
+// 2. "invoke" mode: when Method is non-nil (IsInvoke), a CallCommon
+// represents a dynamically dispatched call to an interface method.
+// In this mode, Value is the interface value and Method is the
+// interface's abstract method. Note: an abstract method may be
+// shared by multiple interfaces due to embedding; Value.Type()
+// provides the specific interface used for this call.
+//
+// Value is implicitly supplied to the concrete method implementation
+// as the receiver parameter; in other words, Args[0] holds not the
+// receiver but the first true argument.
+//
+// Example printed form:
+// t1 = invoke t0.String()
+// go invoke t3.Run(t2)
+// defer invoke t4.Handle(...t5)
+//
+// For all calls to variadic functions (Signature().Variadic()),
+// the last element of Args is a slice.
+//
+type CallCommon struct {
+ Value Value // receiver (invoke mode) or func value (call mode)
+ Method *types.Func // abstract method (invoke mode)
+ Args []Value // actual parameters (in static method call, includes receiver)
+ pos token.Pos // position of CallExpr.Lparen, iff explicit in source
+}
+
+// IsInvoke returns true if this call has "invoke" (not "call") mode.
+func (c *CallCommon) IsInvoke() bool {
+ return c.Method != nil
+}
+
+func (c *CallCommon) Pos() token.Pos { return c.pos }
+
+// Signature returns the signature of the called function.
+//
+// For an "invoke"-mode call, the signature of the interface method is
+// returned.
+//
+// In either "call" or "invoke" mode, if the callee is a method, its
+// receiver is represented by sig.Recv, not sig.Params().At(0).
+//
+func (c *CallCommon) Signature() *types.Signature {
+ if c.Method != nil {
+ return c.Method.Type().(*types.Signature)
+ }
+ return c.Value.Type().Underlying().(*types.Signature)
+}
+
+// StaticCallee returns the callee if this is a trivially static
+// "call"-mode call to a function.
+func (c *CallCommon) StaticCallee() *Function {
+ switch fn := c.Value.(type) {
+ case *Function:
+ return fn
+ case *MakeClosure:
+ return fn.Fn.(*Function)
+ }
+ return nil
+}
+
+// Description returns a description of the mode of this call suitable
+// for a user interface, e.g., "static method call".
+func (c *CallCommon) Description() string {
+ switch fn := c.Value.(type) {
+ case *Builtin:
+ return "built-in function call"
+ case *MakeClosure:
+ return "static function closure call"
+ case *Function:
+ if fn.Signature.Recv() != nil {
+ return "static method call"
+ }
+ return "static function call"
+ }
+ if c.IsInvoke() {
+ return "dynamic method call" // ("invoke" mode)
+ }
+ return "dynamic function call"
+}
+
+// The CallInstruction interface, implemented by *Go, *Defer and *Call,
+// exposes the common parts of function-calling instructions,
+// yet provides a way back to the Value defined by *Call alone.
+//
+type CallInstruction interface {
+ Instruction
+ Common() *CallCommon // returns the common parts of the call
+ Value() *Call // returns the result value of the call (*Call) or nil (*Go, *Defer)
+}
+
+func (s *Call) Common() *CallCommon { return &s.Call }
+func (s *Defer) Common() *CallCommon { return &s.Call }
+func (s *Go) Common() *CallCommon { return &s.Call }
+
+func (s *Call) Value() *Call { return s }
+func (s *Defer) Value() *Call { return nil }
+func (s *Go) Value() *Call { return nil }
+
+func (v *Builtin) Type() types.Type { return v.sig }
+func (v *Builtin) Name() string { return v.name }
+func (*Builtin) Referrers() *[]Instruction { return nil }
+func (v *Builtin) Pos() token.Pos { return token.NoPos }
+func (v *Builtin) Object() types.Object { return types.Universe.Lookup(v.name) }
+func (v *Builtin) Parent() *Function { return nil }
+
+func (v *FreeVar) Type() types.Type { return v.typ }
+func (v *FreeVar) Name() string { return v.name }
+func (v *FreeVar) Referrers() *[]Instruction { return &v.referrers }
+func (v *FreeVar) Pos() token.Pos { return v.pos }
+func (v *FreeVar) Parent() *Function { return v.parent }
+
+func (v *Global) Type() types.Type { return v.typ }
+func (v *Global) Name() string { return v.name }
+func (v *Global) Parent() *Function { return nil }
+func (v *Global) Pos() token.Pos { return v.pos }
+func (v *Global) Referrers() *[]Instruction { return nil }
+func (v *Global) Token() token.Token { return token.VAR }
+func (v *Global) Object() types.Object { return v.object }
+func (v *Global) String() string { return v.RelString(nil) }
+func (v *Global) Package() *Package { return v.Pkg }
+func (v *Global) RelString(from *types.Package) string { return relString(v, from) }
+
+func (v *Function) Name() string { return v.name }
+func (v *Function) Type() types.Type { return v.Signature }
+func (v *Function) Pos() token.Pos { return v.pos }
+func (v *Function) Token() token.Token { return token.FUNC }
+func (v *Function) Object() types.Object { return v.object }
+func (v *Function) String() string { return v.RelString(nil) }
+func (v *Function) Package() *Package { return v.Pkg }
+func (v *Function) Parent() *Function { return v.parent }
+func (v *Function) Referrers() *[]Instruction {
+ if v.parent != nil {
+ return &v.referrers
+ }
+ return nil
+}
+
+func (v *Parameter) Type() types.Type { return v.typ }
+func (v *Parameter) Name() string { return v.name }
+func (v *Parameter) Object() types.Object { return v.object }
+func (v *Parameter) Referrers() *[]Instruction { return &v.referrers }
+func (v *Parameter) Pos() token.Pos { return v.pos }
+func (v *Parameter) Parent() *Function { return v.parent }
+
+func (v *Alloc) Type() types.Type { return v.typ }
+func (v *Alloc) Referrers() *[]Instruction { return &v.referrers }
+func (v *Alloc) Pos() token.Pos { return v.pos }
+
+func (v *register) Type() types.Type { return v.typ }
+func (v *register) setType(typ types.Type) { v.typ = typ }
+func (v *register) Name() string { return fmt.Sprintf("t%d", v.num) }
+func (v *register) setNum(num int) { v.num = num }
+func (v *register) Referrers() *[]Instruction { return &v.referrers }
+func (v *register) Pos() token.Pos { return v.pos }
+func (v *register) setPos(pos token.Pos) { v.pos = pos }
+
+func (v *anInstruction) Parent() *Function { return v.block.parent }
+func (v *anInstruction) Block() *BasicBlock { return v.block }
+func (v *anInstruction) setBlock(block *BasicBlock) { v.block = block }
+func (v *anInstruction) Referrers() *[]Instruction { return nil }
+
+func (t *Type) Name() string { return t.object.Name() }
+func (t *Type) Pos() token.Pos { return t.object.Pos() }
+func (t *Type) Type() types.Type { return t.object.Type() }
+func (t *Type) Token() token.Token { return token.TYPE }
+func (t *Type) Object() types.Object { return t.object }
+func (t *Type) String() string { return t.RelString(nil) }
+func (t *Type) Package() *Package { return t.pkg }
+func (t *Type) RelString(from *types.Package) string { return relString(t, from) }
+
+func (c *NamedConst) Name() string { return c.object.Name() }
+func (c *NamedConst) Pos() token.Pos { return c.object.Pos() }
+func (c *NamedConst) String() string { return c.RelString(nil) }
+func (c *NamedConst) Type() types.Type { return c.object.Type() }
+func (c *NamedConst) Token() token.Token { return token.CONST }
+func (c *NamedConst) Object() types.Object { return c.object }
+func (c *NamedConst) Package() *Package { return c.pkg }
+func (c *NamedConst) RelString(from *types.Package) string { return relString(c, from) }
+
+// Func returns the package-level function of the specified name,
+// or nil if not found.
+//
+func (p *Package) Func(name string) (f *Function) {
+ f, _ = p.Members[name].(*Function)
+ return
+}
+
+// Var returns the package-level variable of the specified name,
+// or nil if not found.
+//
+func (p *Package) Var(name string) (g *Global) {
+ g, _ = p.Members[name].(*Global)
+ return
+}
+
+// Const returns the package-level constant of the specified name,
+// or nil if not found.
+//
+func (p *Package) Const(name string) (c *NamedConst) {
+ c, _ = p.Members[name].(*NamedConst)
+ return
+}
+
+// Type returns the package-level type of the specified name,
+// or nil if not found.
+//
+func (p *Package) Type(name string) (t *Type) {
+ t, _ = p.Members[name].(*Type)
+ return
+}
+
+func (v *Call) Pos() token.Pos { return v.Call.pos }
+func (s *Defer) Pos() token.Pos { return s.pos }
+func (s *Go) Pos() token.Pos { return s.pos }
+func (s *MapUpdate) Pos() token.Pos { return s.pos }
+func (s *Panic) Pos() token.Pos { return s.pos }
+func (s *Return) Pos() token.Pos { return s.pos }
+func (s *Send) Pos() token.Pos { return s.pos }
+func (s *Store) Pos() token.Pos { return s.pos }
+func (s *BlankStore) Pos() token.Pos { return token.NoPos }
+func (s *If) Pos() token.Pos { return token.NoPos }
+func (s *Jump) Pos() token.Pos { return token.NoPos }
+func (s *RunDefers) Pos() token.Pos { return token.NoPos }
+func (s *DebugRef) Pos() token.Pos { return s.Expr.Pos() }
+
+// Operands.
+
+func (v *Alloc) Operands(rands []*Value) []*Value {
+ return rands
+}
+
+func (v *BinOp) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X, &v.Y)
+}
+
+func (c *CallCommon) Operands(rands []*Value) []*Value {
+ rands = append(rands, &c.Value)
+ for i := range c.Args {
+ rands = append(rands, &c.Args[i])
+ }
+ return rands
+}
+
+func (s *Go) Operands(rands []*Value) []*Value {
+ return s.Call.Operands(rands)
+}
+
+func (s *Call) Operands(rands []*Value) []*Value {
+ return s.Call.Operands(rands)
+}
+
+func (s *Defer) Operands(rands []*Value) []*Value {
+ return s.Call.Operands(rands)
+}
+
+func (v *ChangeInterface) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (v *ChangeType) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (v *Convert) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (s *DebugRef) Operands(rands []*Value) []*Value {
+ return append(rands, &s.X)
+}
+
+func (v *Extract) Operands(rands []*Value) []*Value {
+ return append(rands, &v.Tuple)
+}
+
+func (v *Field) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (v *FieldAddr) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (s *If) Operands(rands []*Value) []*Value {
+ return append(rands, &s.Cond)
+}
+
+func (v *Index) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X, &v.Index)
+}
+
+func (v *IndexAddr) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X, &v.Index)
+}
+
+func (*Jump) Operands(rands []*Value) []*Value {
+ return rands
+}
+
+func (v *Lookup) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X, &v.Index)
+}
+
+func (v *MakeChan) Operands(rands []*Value) []*Value {
+ return append(rands, &v.Size)
+}
+
+func (v *MakeClosure) Operands(rands []*Value) []*Value {
+ rands = append(rands, &v.Fn)
+ for i := range v.Bindings {
+ rands = append(rands, &v.Bindings[i])
+ }
+ return rands
+}
+
+func (v *MakeInterface) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (v *MakeMap) Operands(rands []*Value) []*Value {
+ return append(rands, &v.Reserve)
+}
+
+func (v *MakeSlice) Operands(rands []*Value) []*Value {
+ return append(rands, &v.Len, &v.Cap)
+}
+
+func (v *MapUpdate) Operands(rands []*Value) []*Value {
+ return append(rands, &v.Map, &v.Key, &v.Value)
+}
+
+func (v *Next) Operands(rands []*Value) []*Value {
+ return append(rands, &v.Iter)
+}
+
+func (s *Panic) Operands(rands []*Value) []*Value {
+ return append(rands, &s.X)
+}
+
+func (v *Sigma) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (v *Phi) Operands(rands []*Value) []*Value {
+ for i := range v.Edges {
+ rands = append(rands, &v.Edges[i])
+ }
+ return rands
+}
+
+func (v *Range) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (s *Return) Operands(rands []*Value) []*Value {
+ for i := range s.Results {
+ rands = append(rands, &s.Results[i])
+ }
+ return rands
+}
+
+func (*RunDefers) Operands(rands []*Value) []*Value {
+ return rands
+}
+
+func (v *Select) Operands(rands []*Value) []*Value {
+ for i := range v.States {
+ rands = append(rands, &v.States[i].Chan, &v.States[i].Send)
+ }
+ return rands
+}
+
+func (s *Send) Operands(rands []*Value) []*Value {
+ return append(rands, &s.Chan, &s.X)
+}
+
+func (v *Slice) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X, &v.Low, &v.High, &v.Max)
+}
+
+func (s *Store) Operands(rands []*Value) []*Value {
+ return append(rands, &s.Addr, &s.Val)
+}
+
+func (s *BlankStore) Operands(rands []*Value) []*Value {
+ return append(rands, &s.Val)
+}
+
+func (v *TypeAssert) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+func (v *UnOp) Operands(rands []*Value) []*Value {
+ return append(rands, &v.X)
+}
+
+// Non-Instruction Values:
+func (v *Builtin) Operands(rands []*Value) []*Value { return rands }
+func (v *FreeVar) Operands(rands []*Value) []*Value { return rands }
+func (v *Const) Operands(rands []*Value) []*Value { return rands }
+func (v *Function) Operands(rands []*Value) []*Value { return rands }
+func (v *Global) Operands(rands []*Value) []*Value { return rands }
+func (v *Parameter) Operands(rands []*Value) []*Value { return rands }
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 000000000..d7b38bc35
--- /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
new file mode 100644
index 000000000..8ec15ba50
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/testmain.go
@@ -0,0 +1,271 @@
+// 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 ssa
+
+// CreateTestMainPackage synthesizes a main package that runs all the
+// tests of the supplied packages.
+// It is closely coupled to $GOROOT/src/cmd/go/test.go and $GOROOT/src/testing.
+//
+// TODO(adonovan): throws this all away now that x/tools/go/packages
+// provides access to the actual synthetic test main files.
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/types"
+ "log"
+ "os"
+ "strings"
+ "text/template"
+)
+
+// 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
+
+ // The first two of these may be nil: if the program doesn't import "testing",
+ // it can't contain any tests, but it may yet contain Examples.
+ var testSig *types.Signature // func(*testing.T)
+ var benchmarkSig *types.Signature // func(*testing.B)
+ var exampleSig = types.NewSignature(nil, nil, nil, false) // func()
+
+ // Obtain the types from the parameters of testing.MainStart.
+ if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil {
+ mainStart := testingPkg.Func("MainStart")
+ params := mainStart.Signature.Params()
+ testSig = funcField(params.At(1).Type())
+ benchmarkSig = funcField(params.At(2).Type())
+
+ // Does the package define this function?
+ // func TestMain(*testing.M)
+ if f := pkg.Func("TestMain"); f != nil {
+ sig := f.Type().(*types.Signature)
+ starM := mainStart.Signature.Results().At(0).Type() // *testing.M
+ if sig.Results().Len() == 0 &&
+ sig.Params().Len() == 1 &&
+ types.Identical(sig.Params().At(0).Type(), starM) {
+ main = f
+ }
+ }
+ }
+
+ // TODO(adonovan): use a stable order, e.g. lexical.
+ for _, mem := range pkg.Members {
+ if f, ok := mem.(*Function); ok &&
+ ast.IsExported(f.Name()) &&
+ strings.HasSuffix(prog.Fset.Position(f.Pos()).Filename, "_test.go") {
+
+ switch {
+ case testSig != nil && isTestSig(f, "Test", testSig):
+ tests = append(tests, f)
+ case benchmarkSig != nil && isTestSig(f, "Benchmark", benchmarkSig):
+ benchmarks = append(benchmarks, f)
+ case isTestSig(f, "Example", exampleSig):
+ examples = append(examples, f)
+ default:
+ continue
+ }
+ }
+ }
+ return
+}
+
+// Like isTest, but checks the signature too.
+func isTestSig(f *Function, prefix string, sig *types.Signature) bool {
+ return isTest(f.Name(), prefix) && types.Identical(f.Signature, sig)
+}
+
+// Given the type of one of the three slice parameters of testing.Main,
+// returns the function type.
+func funcField(slice types.Type) *types.Signature {
+ return slice.(*types.Slice).Elem().Underlying().(*types.Struct).Field(1).Type().(*types.Signature)
+}
+
+// isTest tells whether name looks like a test (or benchmark, according to prefix).
+// It is a Test (say) if there is a character after Test that is not a lower-case letter.
+// We don't want TesticularCancer.
+// Plundered from $GOROOT/src/cmd/go/test.go
+func isTest(name, prefix string) bool {
+ if !strings.HasPrefix(name, prefix) {
+ return false
+ }
+ if len(name) == len(prefix) { // "Test" is ok
+ return true
+ }
+ return ast.IsExported(name[len(prefix):])
+}
+
+// CreateTestMainPackage creates and returns a synthetic "testmain"
+// package for the specified package if it defines tests, benchmarks or
+// executable examples, or nil otherwise. The new package is named
+// "main" and provides a function named "main" that runs the tests,
+// similar to the one that would be created by the 'go test' tool.
+//
+// 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")
+ }
+
+ // Template data
+ var data struct {
+ Pkg *Package
+ Tests, Benchmarks, Examples []*Function
+ Main *Function
+ Go18 bool
+ }
+ data.Pkg = pkg
+
+ // Enumerate tests.
+ data.Tests, data.Benchmarks, data.Examples, data.Main = FindTests(pkg)
+ if data.Main == nil &&
+ data.Tests == nil && data.Benchmarks == nil && data.Examples == nil {
+ return nil
+ }
+
+ // Synthesize source for testmain package.
+ path := pkg.Pkg.Path() + "$testmain"
+ tmpl := testmainTmpl
+ if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil {
+ // In Go 1.8, testing.MainStart's first argument is an interface, not a func.
+ data.Go18 = types.IsInterface(testingPkg.Func("MainStart").Signature.Params().At(0).Type())
+ } else {
+ // The program does not import "testing", but FindTests
+ // returned non-nil, which must mean there were Examples
+ // but no Test, Benchmark, or TestMain functions.
+
+ // We'll simply call them from testmain.main; this will
+ // ensure they don't panic, but will not check any
+ // "Output:" comments.
+ // (We should not execute an Example that has no
+ // "Output:" comment, but it's impossible to tell here.)
+ tmpl = examplesOnlyTmpl
+ }
+ var buf bytes.Buffer
+ if err := tmpl.Execute(&buf, data); err != nil {
+ log.Fatalf("internal error expanding template for %s: %v", path, err)
+ }
+ if false { // debugging
+ fmt.Fprintln(os.Stderr, buf.String())
+ }
+
+ // Parse and type-check the testmain package.
+ f, err := parser.ParseFile(prog.Fset, path+".go", &buf, parser.Mode(0))
+ if err != nil {
+ log.Fatalf("internal error parsing %s: %v", path, err)
+ }
+ conf := types.Config{
+ DisableUnusedImportCheck: true,
+ Importer: importer{pkg},
+ }
+ files := []*ast.File{f}
+ 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),
+ }
+ testmainPkg, err := conf.Check(path, prog.Fset, files, info)
+ if err != nil {
+ log.Fatalf("internal error type-checking %s: %v", path, err)
+ }
+
+ // Create and build SSA code.
+ testmain := prog.CreatePackage(testmainPkg, files, info, false)
+ testmain.SetDebugMode(false)
+ testmain.Build()
+ testmain.Func("main").Synthetic = "test main function"
+ testmain.Func("init").Synthetic = "package initializer"
+ return testmain
+}
+
+// An implementation of types.Importer for an already loaded SSA program.
+type importer struct {
+ pkg *Package // package under test; may be non-importable
+}
+
+func (imp importer) Import(path string) (*types.Package, error) {
+ if p := imp.pkg.Prog.ImportedPackage(path); p != nil {
+ return p.Pkg, nil
+ }
+ if path == imp.pkg.Pkg.Path() {
+ return imp.pkg.Pkg, nil
+ }
+ return nil, fmt.Errorf("not found") // can't happen
+}
+
+var testmainTmpl = template.Must(template.New("testmain").Parse(`
+package main
+
+import "io"
+import "os"
+import "testing"
+import p {{printf "%q" .Pkg.Pkg.Path}}
+
+{{if .Go18}}
+type deps struct{}
+
+func (deps) ImportPath() string { return "" }
+func (deps) MatchString(pat, str string) (bool, error) { return true, nil }
+func (deps) StartCPUProfile(io.Writer) error { return nil }
+func (deps) StartTestLog(io.Writer) {}
+func (deps) StopCPUProfile() {}
+func (deps) StopTestLog() error { return nil }
+func (deps) WriteHeapProfile(io.Writer) error { return nil }
+func (deps) WriteProfileTo(string, io.Writer, int) error { return nil }
+
+var match deps
+{{else}}
+func match(_, _ string) (bool, error) { return true, nil }
+{{end}}
+
+func main() {
+ tests := []testing.InternalTest{
+{{range .Tests}}
+ { {{printf "%q" .Name}}, p.{{.Name}} },
+{{end}}
+ }
+ benchmarks := []testing.InternalBenchmark{
+{{range .Benchmarks}}
+ { {{printf "%q" .Name}}, p.{{.Name}} },
+{{end}}
+ }
+ examples := []testing.InternalExample{
+{{range .Examples}}
+ {Name: {{printf "%q" .Name}}, F: p.{{.Name}}},
+{{end}}
+ }
+ m := testing.MainStart(match, tests, benchmarks, examples)
+{{with .Main}}
+ p.{{.Name}}(m)
+{{else}}
+ os.Exit(m.Run())
+{{end}}
+}
+
+`))
+
+var examplesOnlyTmpl = template.Must(template.New("examples").Parse(`
+package main
+
+import p {{printf "%q" .Pkg.Pkg.Path}}
+
+func main() {
+{{range .Examples}}
+ p.{{.Name}}()
+{{end}}
+}
+`))
diff --git a/vendor/honnef.co/go/tools/ssa/util.go b/vendor/honnef.co/go/tools/ssa/util.go
new file mode 100644
index 000000000..ddb118460
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/util.go
@@ -0,0 +1,119 @@
+// 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 ssa
+
+// This file defines a number of miscellaneous utility functions.
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+ "io"
+ "os"
+
+ "golang.org/x/tools/go/ast/astutil"
+)
+
+//// AST utilities
+
+func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
+
+// isBlankIdent returns true iff e is an Ident with name "_".
+// They have no associated types.Object, and thus no type.
+//
+func isBlankIdent(e ast.Expr) bool {
+ id, ok := e.(*ast.Ident)
+ return ok && id.Name == "_"
+}
+
+//// Type utilities. Some of these belong in go/types.
+
+// isPointer returns true for types whose underlying type is a pointer.
+func isPointer(typ types.Type) bool {
+ _, ok := typ.Underlying().(*types.Pointer)
+ return ok
+}
+
+func isInterface(T types.Type) bool { return types.IsInterface(T) }
+
+// deref returns a pointer's element type; otherwise it returns typ.
+func deref(typ types.Type) types.Type {
+ if p, ok := typ.Underlying().(*types.Pointer); ok {
+ return p.Elem()
+ }
+ return typ
+}
+
+// recvType returns the receiver type of method obj.
+func recvType(obj *types.Func) types.Type {
+ return obj.Type().(*types.Signature).Recv().Type()
+}
+
+// DefaultType returns the default "typed" type for an "untyped" type;
+// it returns the incoming type for all other types. The default type
+// for untyped nil is untyped nil.
+//
+// Exported to ssa/interp.
+//
+// TODO(adonovan): use go/types.DefaultType after 1.8.
+//
+func DefaultType(typ types.Type) types.Type {
+ if t, ok := typ.(*types.Basic); ok {
+ k := t.Kind()
+ switch k {
+ case types.UntypedBool:
+ k = types.Bool
+ case types.UntypedInt:
+ k = types.Int
+ case types.UntypedRune:
+ k = types.Rune
+ case types.UntypedFloat:
+ k = types.Float64
+ case types.UntypedComplex:
+ k = types.Complex128
+ case types.UntypedString:
+ k = types.String
+ }
+ typ = types.Typ[k]
+ }
+ return typ
+}
+
+// logStack prints the formatted "start" message to stderr and
+// returns a closure that prints the corresponding "end" message.
+// Call using 'defer logStack(...)()' to show builder stack on panic.
+// Don't forget trailing parens!
+//
+func logStack(format string, args ...interface{}) func() {
+ msg := fmt.Sprintf(format, args...)
+ io.WriteString(os.Stderr, msg)
+ io.WriteString(os.Stderr, "\n")
+ return func() {
+ io.WriteString(os.Stderr, msg)
+ io.WriteString(os.Stderr, " end\n")
+ }
+}
+
+// newVar creates a 'var' for use in a types.Tuple.
+func newVar(name string, typ types.Type) *types.Var {
+ return types.NewParam(token.NoPos, nil, name, typ)
+}
+
+// anonVar creates an anonymous 'var' for use in a types.Tuple.
+func anonVar(typ types.Type) *types.Var {
+ return newVar("", typ)
+}
+
+var lenResults = types.NewTuple(anonVar(tInt))
+
+// makeLen returns the len builtin specialized to type func(T)int.
+func makeLen(T types.Type) *Builtin {
+ lenParams := types.NewTuple(anonVar(T))
+ return &Builtin{
+ name: "len",
+ sig: types.NewSignature(nil, lenParams, lenResults, false),
+ }
+}
diff --git a/vendor/honnef.co/go/tools/ssa/wrappers.go b/vendor/honnef.co/go/tools/ssa/wrappers.go
new file mode 100644
index 000000000..a4ae71d8c
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/wrappers.go
@@ -0,0 +1,290 @@
+// 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 ssa
+
+// This file defines synthesis of Functions that delegate to declared
+// methods; they come in three kinds:
+//
+// (1) wrappers: methods that wrap declared methods, performing
+// implicit pointer indirections and embedded field selections.
+//
+// (2) thunks: funcs that wrap declared methods. Like wrappers,
+// thunks perform indirections and field selections. The thunk's
+// first parameter is used as the receiver for the method call.
+//
+// (3) bounds: funcs that wrap declared methods. The bound's sole
+// free variable, supplied by a closure, is used as the receiver
+// for the method call. No indirections or field selections are
+// performed since they can be done before the call.
+
+import (
+ "fmt"
+
+ "go/types"
+)
+
+// -- wrappers -----------------------------------------------------------
+
+// makeWrapper returns a synthetic method that delegates to the
+// declared method denoted by meth.Obj(), first performing any
+// necessary pointer indirections or field selections implied by meth.
+//
+// The resulting method's receiver type is meth.Recv().
+//
+// This function is versatile but quite subtle! Consider the
+// following axes of variation when making changes:
+// - optional receiver indirection
+// - optional implicit field selections
+// - meth.Obj() may denote a concrete or an interface method
+// - the result may be a thunk or a wrapper.
+//
+// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
+//
+func makeWrapper(prog *Program, sel *types.Selection) *Function {
+ obj := sel.Obj().(*types.Func) // the declared function
+ sig := sel.Type().(*types.Signature) // type of this wrapper
+
+ var recv *types.Var // wrapper's receiver or thunk's params[0]
+ name := obj.Name()
+ var description string
+ var start int // first regular param
+ if sel.Kind() == types.MethodExpr {
+ name += "$thunk"
+ description = "thunk"
+ recv = sig.Params().At(0)
+ start = 1
+ } else {
+ description = "wrapper"
+ recv = sig.Recv()
+ }
+
+ description = fmt.Sprintf("%s for %s", description, sel.Obj())
+ if prog.mode&LogSource != 0 {
+ defer logStack("make %s to (%s)", description, recv.Type())()
+ }
+ fn := &Function{
+ name: name,
+ method: sel,
+ object: obj,
+ Signature: sig,
+ Synthetic: description,
+ Prog: prog,
+ pos: obj.Pos(),
+ }
+ fn.startBody()
+ fn.addSpilledParam(recv)
+ createParams(fn, start)
+
+ indices := sel.Index()
+
+ var v Value = fn.Locals[0] // spilled receiver
+ if isPointer(sel.Recv()) {
+ v = emitLoad(fn, v)
+
+ // For simple indirection wrappers, perform an informative nil-check:
+ // "value method (T).f called using nil *T pointer"
+ if len(indices) == 1 && !isPointer(recvType(obj)) {
+ var c Call
+ c.Call.Value = &Builtin{
+ name: "ssa:wrapnilchk",
+ sig: types.NewSignature(nil,
+ types.NewTuple(anonVar(sel.Recv()), anonVar(tString), anonVar(tString)),
+ types.NewTuple(anonVar(sel.Recv())), false),
+ }
+ c.Call.Args = []Value{
+ v,
+ stringConst(deref(sel.Recv()).String()),
+ stringConst(sel.Obj().Name()),
+ }
+ c.setType(v.Type())
+ v = fn.emit(&c)
+ }
+ }
+
+ // Invariant: v is a pointer, either
+ // value of *A receiver param, or
+ // address of A spilled receiver.
+
+ // We use pointer arithmetic (FieldAddr possibly followed by
+ // Load) in preference to value extraction (Field possibly
+ // preceded by Load).
+
+ v = emitImplicitSelections(fn, v, indices[:len(indices)-1])
+
+ // Invariant: v is a pointer, either
+ // value of implicit *C field, or
+ // address of implicit C field.
+
+ var c Call
+ if r := recvType(obj); !isInterface(r) { // concrete method
+ if !isPointer(r) {
+ v = emitLoad(fn, v)
+ }
+ c.Call.Value = prog.declaredFunc(obj)
+ c.Call.Args = append(c.Call.Args, v)
+ } else {
+ c.Call.Method = obj
+ c.Call.Value = emitLoad(fn, v)
+ }
+ for _, arg := range fn.Params[1:] {
+ c.Call.Args = append(c.Call.Args, arg)
+ }
+ emitTailCall(fn, &c)
+ fn.finishBody()
+ return fn
+}
+
+// createParams creates parameters for wrapper method fn based on its
+// Signature.Params, which do not include the receiver.
+// start is the index of the first regular parameter to use.
+//
+func createParams(fn *Function, start int) {
+ tparams := fn.Signature.Params()
+ for i, n := start, tparams.Len(); i < n; i++ {
+ fn.addParamObj(tparams.At(i))
+ }
+}
+
+// -- bounds -----------------------------------------------------------
+
+// makeBound returns a bound method wrapper (or "bound"), a synthetic
+// function that delegates to a concrete or interface method denoted
+// by obj. The resulting function has no receiver, but has one free
+// variable which will be used as the method's receiver in the
+// tail-call.
+//
+// Use MakeClosure with such a wrapper to construct a bound method
+// closure. e.g.:
+//
+// type T int or: type T interface { meth() }
+// func (t T) meth()
+// var t T
+// f := t.meth
+// f() // calls t.meth()
+//
+// f is a closure of a synthetic wrapper defined as if by:
+//
+// f := func() { return t.meth() }
+//
+// Unlike makeWrapper, makeBound need perform no indirection or field
+// selections because that can be done before the closure is
+// constructed.
+//
+// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
+//
+func makeBound(prog *Program, obj *types.Func) *Function {
+ prog.methodsMu.Lock()
+ defer prog.methodsMu.Unlock()
+ fn, ok := prog.bounds[obj]
+ if !ok {
+ description := fmt.Sprintf("bound method wrapper for %s", obj)
+ if prog.mode&LogSource != 0 {
+ defer logStack("%s", description)()
+ }
+ fn = &Function{
+ name: obj.Name() + "$bound",
+ object: obj,
+ Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver
+ Synthetic: description,
+ Prog: prog,
+ pos: obj.Pos(),
+ }
+
+ fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn}
+ fn.FreeVars = []*FreeVar{fv}
+ fn.startBody()
+ createParams(fn, 0)
+ var c Call
+
+ if !isInterface(recvType(obj)) { // concrete
+ c.Call.Value = prog.declaredFunc(obj)
+ c.Call.Args = []Value{fv}
+ } else {
+ c.Call.Value = fv
+ c.Call.Method = obj
+ }
+ for _, arg := range fn.Params {
+ c.Call.Args = append(c.Call.Args, arg)
+ }
+ emitTailCall(fn, &c)
+ fn.finishBody()
+
+ prog.bounds[obj] = fn
+ }
+ return fn
+}
+
+// -- thunks -----------------------------------------------------------
+
+// makeThunk returns a thunk, a synthetic function that delegates to a
+// concrete or interface method denoted by sel.Obj(). The resulting
+// function has no receiver, but has an additional (first) regular
+// parameter.
+//
+// Precondition: sel.Kind() == types.MethodExpr.
+//
+// type T int or: type T interface { meth() }
+// func (t T) meth()
+// f := T.meth
+// var t T
+// f(t) // calls t.meth()
+//
+// f is a synthetic wrapper defined as if by:
+//
+// f := func(t T) { return t.meth() }
+//
+// TODO(adonovan): opt: currently the stub is created even when used
+// directly in a function call: C.f(i, 0). This is less efficient
+// than inlining the stub.
+//
+// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
+//
+func makeThunk(prog *Program, sel *types.Selection) *Function {
+ if sel.Kind() != types.MethodExpr {
+ panic(sel)
+ }
+
+ key := selectionKey{
+ kind: sel.Kind(),
+ recv: sel.Recv(),
+ obj: sel.Obj(),
+ index: fmt.Sprint(sel.Index()),
+ indirect: sel.Indirect(),
+ }
+
+ prog.methodsMu.Lock()
+ defer prog.methodsMu.Unlock()
+
+ // Canonicalize key.recv to avoid constructing duplicate thunks.
+ canonRecv, ok := prog.canon.At(key.recv).(types.Type)
+ if !ok {
+ canonRecv = key.recv
+ prog.canon.Set(key.recv, canonRecv)
+ }
+ key.recv = canonRecv
+
+ fn, ok := prog.thunks[key]
+ if !ok {
+ fn = makeWrapper(prog, sel)
+ if fn.Signature.Recv() != nil {
+ panic(fn) // unexpected receiver
+ }
+ prog.thunks[key] = fn
+ }
+ return fn
+}
+
+func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
+ return types.NewSignature(recv, s.Params(), s.Results(), s.Variadic())
+}
+
+// selectionKey is like types.Selection but a usable map key.
+type selectionKey struct {
+ kind types.SelectionKind
+ recv types.Type // canonicalized via Program.canon
+ obj types.Object
+ index string
+ indirect bool
+}
diff --git a/vendor/honnef.co/go/tools/ssa/write.go b/vendor/honnef.co/go/tools/ssa/write.go
new file mode 100644
index 000000000..89761a18a
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/write.go
@@ -0,0 +1,5 @@
+package ssa
+
+func NewJump(parent *BasicBlock) *Jump {
+ return &Jump{anInstruction{parent}}
+}
diff --git a/vendor/honnef.co/go/tools/ssautil/ssautil.go b/vendor/honnef.co/go/tools/ssautil/ssautil.go
new file mode 100644
index 000000000..72c3c919d
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssautil/ssautil.go
@@ -0,0 +1,58 @@
+package ssautil
+
+import (
+ "honnef.co/go/tools/ssa"
+)
+
+func Reachable(from, to *ssa.BasicBlock) bool {
+ if from == to {
+ return true
+ }
+ if from.Dominates(to) {
+ return true
+ }
+
+ found := false
+ Walk(from, func(b *ssa.BasicBlock) bool {
+ if b == to {
+ found = true
+ return false
+ }
+ return true
+ })
+ return found
+}
+
+func Walk(b *ssa.BasicBlock, fn func(*ssa.BasicBlock) bool) {
+ seen := map[*ssa.BasicBlock]bool{}
+ wl := []*ssa.BasicBlock{b}
+ for len(wl) > 0 {
+ b := wl[len(wl)-1]
+ wl = wl[:len(wl)-1]
+ if seen[b] {
+ continue
+ }
+ seen[b] = true
+ if !fn(b) {
+ continue
+ }
+ wl = append(wl, b.Succs...)
+ }
+}
+
+func Vararg(x *ssa.Slice) ([]ssa.Value, bool) {
+ var out []ssa.Value
+ slice, ok := x.X.(*ssa.Alloc)
+ if !ok || slice.Comment != "varargs" {
+ return nil, false
+ }
+ for _, ref := range *slice.Referrers() {
+ idx, ok := ref.(*ssa.IndexAddr)
+ if !ok {
+ continue
+ }
+ v := (*idx.Referrers())[0].(*ssa.Store).Val
+ out = append(out, v)
+ }
+ return out, true
+}
diff --git a/vendor/honnef.co/go/tools/staticcheck/CONTRIBUTING.md b/vendor/honnef.co/go/tools/staticcheck/CONTRIBUTING.md
new file mode 100644
index 000000000..b12c7afc7
--- /dev/null
+++ b/vendor/honnef.co/go/tools/staticcheck/CONTRIBUTING.md
@@ -0,0 +1,15 @@
+# Contributing to staticcheck
+
+## Before filing an issue:
+
+### Are you having trouble building staticcheck?
+
+Check you have the latest version of its dependencies. Run
+```
+go get -u honnef.co/go/tools/staticcheck
+```
+If you still have problems, consider searching for existing issues before filing a new issue.
+
+## Before sending a pull request:
+
+Have you understood the purpose of staticcheck? Make sure to carefully read `README`.
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 000000000..442aebe5a
--- /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/buildtag.go b/vendor/honnef.co/go/tools/staticcheck/buildtag.go
new file mode 100644
index 000000000..888d3e9dc
--- /dev/null
+++ b/vendor/honnef.co/go/tools/staticcheck/buildtag.go
@@ -0,0 +1,21 @@
+package staticcheck
+
+import (
+ "go/ast"
+ "strings"
+
+ . "honnef.co/go/tools/lint/lintdsl"
+)
+
+func buildTags(f *ast.File) [][]string {
+ var out [][]string
+ for _, line := range strings.Split(Preamble(f), "\n") {
+ if !strings.HasPrefix(line, "+build ") {
+ continue
+ }
+ line = strings.TrimSpace(strings.TrimPrefix(line, "+build "))
+ fields := strings.Fields(line)
+ out = append(out, fields)
+ }
+ return out
+}
diff --git a/vendor/honnef.co/go/tools/staticcheck/doc.go b/vendor/honnef.co/go/tools/staticcheck/doc.go
new file mode 100644
index 000000000..4a87d4a24
--- /dev/null
+++ b/vendor/honnef.co/go/tools/staticcheck/doc.go
@@ -0,0 +1,764 @@
+package staticcheck
+
+import "honnef.co/go/tools/lint"
+
+var Docs = map[string]*lint.Documentation{
+ "SA1000": &lint.Documentation{
+ Title: `Invalid regular expression`,
+ Since: "2017.1",
+ },
+
+ "SA1001": &lint.Documentation{
+ Title: `Invalid template`,
+ Since: "2017.1",
+ },
+
+ "SA1002": &lint.Documentation{
+ Title: `Invalid format in time.Parse`,
+ Since: "2017.1",
+ },
+
+ "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.`,
+ Since: "2017.1",
+ },
+
+ "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.
+
+The time package provides constants such as time.Second to express
+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.`,
+ Since: "2017.1",
+ },
+
+ "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
+responsible for splitting the user's input into a program name and its
+arguments. For example, the equivalent to
+
+ ls / /tmp
+
+would be
+
+ exec.Command("ls", "/", "/tmp")
+
+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")`,
+ Since: "2017.1",
+ },
+
+ "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
+
+ Interest rate: 5%
+
+and you printed it with
+
+ fmt.Printf(s)
+
+it would lead to the following output:
+
+ 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.`,
+ Since: "2017.1",
+ },
+
+ "SA1007": &lint.Documentation{
+ Title: `Invalid URL in net/url.Parse`,
+ Since: "2017.1",
+ },
+
+ "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.
+
+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:
+
+ h := http.Header{}
+ h["etag"] = []string{"1234"}
+ h.Add("etag", "5678")
+ fmt.Println(h)
+
+ // Output:
+ // map[Etag:[5678] etag:[1234]]
+
+The easiest way of obtaining the canonical form of a key is to use
+http.CanonicalHeaderKey.`,
+ Since: "2017.1",
+ },
+
+ "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",
+ },
+
+ "SA1011": &lint.Documentation{
+ Title: `Various methods in the strings package expect valid UTF-8, but invalid input is provided`,
+ Since: "2017.1",
+ },
+
+ "SA1012": &lint.Documentation{
+ Title: `A nil context.Context is being passed to a function, consider using context.TODO instead`,
+ 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",
+ },
+
+ "SA1014": &lint.Documentation{
+ Title: `Non-pointer value passed to Unmarshal or Decode`,
+ 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",
+ },
+
+ "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.`,
+ Since: "2017.1",
+ },
+
+ "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.`,
+ 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",
+ },
+
+ "SA1019": &lint.Documentation{
+ Title: `Using a deprecated function, variable, constant or field`,
+ Since: "2017.1",
+ },
+
+ "SA1020": &lint.Documentation{
+ Title: `Using an invalid host:port pair with a net.Listen-related function`,
+ Since: "2017.1",
+ },
+
+ "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.`,
+ 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",
+ },
+
+ "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,
+
+ strings.TrimLeft("42133word", "1234"))
+
+will result in the string "word" – any characters that are 1, 2, 3 or
+4 are cut from the left of the string.
+
+In order to remove one string from another, use strings.TrimPrefix instead.`,
+ Since: "2017.1",
+ },
+
+ "SA1025": &lint.Documentation{
+ Title: `It is not possible to use (*time.Timer).Reset's return value correctly`,
+ Since: "2019.1",
+ },
+
+ "SA1026": &lint.Documentation{
+ Title: `Cannot marshal channels or functions`,
+ Since: "2019.2",
+ },
+
+ "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.`,
+ Since: "2019.2",
+ },
+
+ "SA2000": &lint.Documentation{
+ Title: `sync.WaitGroup.Add called inside the goroutine, leading to a race condition`,
+ Since: "2017.1",
+ },
+
+ "SA2001": &lint.Documentation{
+ Title: `Empty critical section, did you mean to defer the unlock?`,
+ Text: `Empty critical sections of the kind
+
+ mu.Lock()
+ mu.Unlock()
+
+are very often a typo, and the following was intended instead:
+
+ mu.Lock()
+ defer mu.Unlock()
+
+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",
+ },
+
+ "SA2002": &lint.Documentation{
+ Title: `Called testing.T.FailNow or SkipNow in a goroutine, which isn't allowed`,
+ Since: "2017.1",
+ },
+
+ "SA2003": &lint.Documentation{
+ Title: `Deferred Lock right after locking, likely meant to defer Unlock instead`,
+ Since: "2017.1",
+ },
+
+ "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()).`,
+ Since: "2017.1",
+ },
+
+ "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.`,
+ Since: "2017.1",
+ },
+
+ "SA4000": &lint.Documentation{
+ Title: `Boolean expression has identical expressions on both sides`,
+ Since: "2017.1",
+ },
+
+ "SA4001": &lint.Documentation{
+ Title: `&*x gets simplified to x, it does not copy x`,
+ Since: "2017.1",
+ },
+
+ "SA4002": &lint.Documentation{
+ Title: `Comparing strings with known different sizes has predictable results`,
+ Since: "2017.1",
+ },
+
+ "SA4003": &lint.Documentation{
+ Title: `Comparing unsigned values against negative values is pointless`,
+ Since: "2017.1",
+ },
+
+ "SA4004": &lint.Documentation{
+ Title: `The loop exits unconditionally after one iteration`,
+ Since: "2017.1",
+ },
+
+ "SA4005": &lint.Documentation{
+ Title: `Field assignment that will never be observed. Did you mean to use a pointer receiver?`,
+ 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",
+ },
+
+ "SA4008": &lint.Documentation{
+ Title: `The variable in the loop condition never changes, are you incrementing the wrong variable?`,
+ Since: "2017.1",
+ },
+
+ "SA4009": &lint.Documentation{
+ Title: `A function argument is overwritten before its first use`,
+ Since: "2017.1",
+ },
+
+ "SA4010": &lint.Documentation{
+ Title: `The result of append will never be observed anywhere`,
+ 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",
+ },
+
+ "SA4012": &lint.Documentation{
+ Title: `Comparing a value against NaN even though no value is equal to NaN`,
+ 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",
+ },
+
+ "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",
+ },
+
+ "SA4015": &lint.Documentation{
+ Title: `Calling functions like math.Ceil on floats converted from integers doesn't do anything useful`,
+ Since: "2017.1",
+ },
+
+ "SA4016": &lint.Documentation{
+ Title: `Certain bitwise operations, such as x ^ 0, do not do anything useful`,
+ Since: "2017.1",
+ },
+
+ "SA4017": &lint.Documentation{
+ Title: `A pure function's return value is discarded, making the call pointless`,
+ Since: "2017.1",
+ },
+
+ "SA4018": &lint.Documentation{
+ Title: `Self-assignment of variables`,
+ Since: "2017.1",
+ },
+
+ "SA4019": &lint.Documentation{
+ Title: `Multiple, identical build constraints in the same file`,
+ Since: "2017.1",
+ },
+
+ "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 }
+
+ var v interface{} = T{}
+
+ switch v.(type) {
+ case io.Reader:
+ // ...
+ case T:
+ // unreachable
+ }
+
+the second case clause can never be reached because T implements
+io.Reader and case clauses are evaluated in source order.
+
+Another example:
+
+ type T struct{}
+ func (T) Read(b []byte) (int, error) { return 0, nil }
+ func (T) Close() error { return nil }
+
+ var v interface{} = T{}
+
+ switch v.(type) {
+ case io.Reader:
+ // ...
+ case io.ReadCloser:
+ // unreachable
+ }
+
+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 matching the first case.
+
+
+Structurally equivalent interfaces
+
+A special case of the previous example are structurally identical
+interfaces. Given these declarations
+
+ type T error
+ type V error
+
+ func doSomething() error {
+ err, ok := doAnotherThing()
+ if ok {
+ return T(err)
+ }
+
+ return U(err)
+ }
+
+the following type switch will have an unreachable case clause:
+
+ switch doSomething().(type) {
+ case T:
+ // ...
+ case V:
+ // unreachable
+ }
+
+T will always match before V because they are structurally equivalent
+and therefore doSomething()'s return value implements both.`,
+ Since: "2019.2",
+ },
+
+ "SA4021": &lint.Documentation{
+ Title: `x = append(y) is equivalent to x = y`,
+ Since: "2019.2",
+ },
+
+ "SA5000": &lint.Documentation{
+ Title: `Assignment to nil map`,
+ Since: "2017.1",
+ },
+
+ "SA5001": &lint.Documentation{
+ Title: `Defering Close before checking for a possible error`,
+ Since: "2017.1",
+ },
+
+ "SA5002": &lint.Documentation{
+ Title: `The empty for loop (for {}) spins and can block the scheduler`,
+ Since: "2017.1",
+ },
+
+ "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.`,
+ Since: "2017.1",
+ },
+
+ "SA5004": &lint.Documentation{
+ Title: `for { select { ... with an empty default branch spins`,
+ Since: "2017.1",
+ },
+
+ "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.
+
+If the finalizer references the object, however, it will always remain
+as the final reference to that object, preventing the garbage
+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.`,
+ Since: "2017.1",
+ },
+
+ "SA5006": &lint.Documentation{
+ Title: `Slice index out of bounds`,
+ Since: "2017.1",
+ },
+
+ "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.
+
+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.`,
+ Since: "2017.1",
+ },
+
+ "SA5008": &lint.Documentation{
+ Title: `Invalid struct tag`,
+ Since: "2019.2",
+ },
+
+ "SA5009": &lint.Documentation{
+ Title: `Invalid Printf call`,
+ Since: "2019.2",
+ },
+
+ "SA6000": &lint.Documentation{
+ Title: `Using regexp.Match or related in a loop, should use regexp.Compile`,
+ Since: "2017.1",
+ },
+
+ "SA6001": &lint.Documentation{
+ Title: `Missing an optimization opportunity when indexing maps by 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.
+
+Normally, a conversion of a byte slice to a string needs to copy the data and
+causes allocations. The compiler, however, recognizes m[string(b)] and
+uses the data of b directly, without copying it, because it knows that
+the data can't change during the map lookup. This leads to the
+counter-intuitive situation that
+
+ k := string(b)
+ println(m[k])
+ println(m[k])
+
+will be less efficient than
+
+ println(m[string(b)])
+ println(m[string(b)])
+
+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.`,
+ Since: "2017.1",
+ },
+
+ "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
+an interface, the value needs to be placed on the heap, which means an
+additional allocation. Slices are a common thing to put in sync.Pools,
+and they're structs with 3 fields (length, capacity, and a pointer to
+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.`,
+ Since: "2017.1",
+ },
+
+ "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,
+
+ for _, r := range s {}
+
+and
+
+ for _, r := range []rune(s) {}
+
+will yield the same values. The first version, however, will be faster
+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.`,
+ Since: "2017.1",
+ },
+
+ "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) {
+ ...
+ }
+
+is significantly more expensive than comparing them with
+strings.EqualFold(s1, s2). This is due to memory usage as well as
+computational complexity.
+
+strings.ToLower will have to allocate memory for the new strings, as
+well as convert both strings fully, even if they differ on the very
+first byte. strings.EqualFold, on the other hand, compares the strings
+one character at a time. It doesn't need to create two intermediate
+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/`,
+ Since: "2019.2",
+ },
+
+ "SA9001": &lint.Documentation{
+ Title: `Defers in range loops may not run when you expect them to`,
+ Since: "2017.1",
+ },
+
+ "SA9002": &lint.Documentation{
+ Title: `Using a non-octal os.FileMode that looks like it was meant to be in octal.`,
+ Since: "2017.1",
+ },
+
+ "SA9003": &lint.Documentation{
+ Title: `Empty body in an if or else branch`,
+ Since: "2017.1",
+ },
+
+ "SA9004": &lint.Documentation{
+ Title: `Only the first constant has an explicit type`,
+
+ Text: `In a constant declaration such as the following:
+
+ const (
+ First byte = 1
+ Second = 2
+ )
+
+the constant Second does not have the same type as the constant First.
+This construct shouldn't be confused with
+
+ const (
+ First byte = iota
+ Second
+ )
+
+where First and Second do indeed have the same type. The type is only
+passed on when no explicit value is assigned to the constant.
+
+When declaring enumerations with explicit values it is therefore
+important not to write
+
+ const (
+ EnumFirst EnumType = 1
+ EnumSecond = 2
+ EnumThird = 3
+ )
+
+This discrepancy in types can cause various confusing behaviors and
+bugs.
+
+
+Wrong type in variable declarations
+
+The most obvious issue with such incorrect enumerations expresses
+itself as a compile error:
+
+ package pkg
+
+ const (
+ EnumFirst uint8 = 1
+ EnumSecond = 2
+ )
+
+ func fn(useFirst bool) {
+ x := EnumSecond
+ if useFirst {
+ x = EnumFirst
+ }
+ }
+
+fails to compile with
+
+ ./const.go:11:5: cannot use EnumFirst (type uint8) as type int in assignment
+
+
+Losing method sets
+
+A more subtle issue occurs with types that have methods and optional
+interfaces. Consider the following:
+
+ package main
+
+ import "fmt"
+
+ type Enum int
+
+ func (e Enum) String() string {
+ return "an enum"
+ }
+
+ const (
+ EnumFirst Enum = 1
+ EnumSecond = 2
+ )
+
+ func main() {
+ fmt.Println(EnumFirst)
+ fmt.Println(EnumSecond)
+ }
+
+This code will output
+
+ an enum
+ 2
+
+as EnumSecond has no explicit type, and thus defaults to int.`,
+ Since: "2019.1",
+ },
+
+ "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.`,
+ 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 000000000..4c12b866a
--- /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
new file mode 100644
index 000000000..1558cbf94
--- /dev/null
+++ b/vendor/honnef.co/go/tools/staticcheck/lint.go
@@ -0,0 +1,3360 @@
+// Package staticcheck contains a linter for Go source code.
+package staticcheck // import "honnef.co/go/tools/staticcheck"
+
+import (
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/token"
+ "go/types"
+ htmltemplate "html/template"
+ "net/http"
+ "reflect"
+ "regexp"
+ "regexp/syntax"
+ "sort"
+ "strconv"
+ "strings"
+ 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"
+ "honnef.co/go/tools/printf"
+ "honnef.co/go/tools/ssa"
+ "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/ast/inspector"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+func validRegexp(call *Call) {
+ arg := call.Args[0]
+ err := ValidateRegexp(arg.Value)
+ if err != nil {
+ arg.Invalid(err.Error())
+ }
+}
+
+type runeSlice []rune
+
+func (rs runeSlice) Len() int { return len(rs) }
+func (rs runeSlice) Less(i int, j int) bool { return rs[i] < rs[j] }
+func (rs runeSlice) Swap(i int, j int) { rs[i], rs[j] = rs[j], rs[i] }
+
+func utf8Cutset(call *Call) {
+ arg := call.Args[1]
+ if InvalidUTF8(arg.Value) {
+ arg.Invalid(MsgInvalidUTF8)
+ }
+}
+
+func uniqueCutset(call *Call) {
+ arg := call.Args[1]
+ if !UniqueStringCutset(arg.Value) {
+ arg.Invalid(MsgNonUniqueCutset)
+ }
+}
+
+func unmarshalPointer(name string, arg int) CallCheck {
+ return func(call *Call) {
+ if !Pointer(call.Args[arg].Value) {
+ call.Args[arg].Invalid(fmt.Sprintf("%s expects to unmarshal into a pointer, but the provided value is not a pointer", name))
+ }
+ }
+}
+
+func pointlessIntMath(call *Call) {
+ if ConvertedFromInt(call.Args[0].Value) {
+ call.Invalid(fmt.Sprintf("calling %s on a converted integer is pointless", CallName(call.Instr.Common())))
+ }
+}
+
+func checkValidHostPort(arg int) CallCheck {
+ return func(call *Call) {
+ if !ValidHostPort(call.Args[arg].Value) {
+ call.Args[arg].Invalid(MsgInvalidHostPort)
+ }
+ }
+}
+
+var (
+ checkRegexpRules = map[string]CallCheck{
+ "regexp.MustCompile": validRegexp,
+ "regexp.Compile": validRegexp,
+ "regexp.Match": validRegexp,
+ "regexp.MatchReader": validRegexp,
+ "regexp.MatchString": validRegexp,
+ }
+
+ checkTimeParseRules = map[string]CallCheck{
+ "time.Parse": func(call *Call) {
+ arg := call.Args[Arg("time.Parse.layout")]
+ err := ValidateTimeLayout(arg.Value)
+ if err != nil {
+ arg.Invalid(err.Error())
+ }
+ },
+ }
+
+ checkEncodingBinaryRules = map[string]CallCheck{
+ "encoding/binary.Write": func(call *Call) {
+ arg := call.Args[Arg("encoding/binary.Write.data")]
+ if !CanBinaryMarshal(call.Pass, arg.Value) {
+ arg.Invalid(fmt.Sprintf("value of type %s cannot be used with binary.Write", arg.Value.Value.Type()))
+ }
+ },
+ }
+
+ checkURLsRules = map[string]CallCheck{
+ "net/url.Parse": func(call *Call) {
+ arg := call.Args[Arg("net/url.Parse.rawurl")]
+ err := ValidateURL(arg.Value)
+ if err != nil {
+ arg.Invalid(err.Error())
+ }
+ },
+ }
+
+ checkSyncPoolValueRules = map[string]CallCheck{
+ "(*sync.Pool).Put": func(call *Call) {
+ arg := call.Args[Arg("(*sync.Pool).Put.x")]
+ typ := arg.Value.Value.Type()
+ if !IsPointerLike(typ) {
+ arg.Invalid("argument should be pointer-like to avoid allocations")
+ }
+ },
+ }
+
+ checkRegexpFindAllRules = map[string]CallCheck{
+ "(*regexp.Regexp).FindAll": RepeatZeroTimes("a FindAll method", 1),
+ "(*regexp.Regexp).FindAllIndex": RepeatZeroTimes("a FindAll method", 1),
+ "(*regexp.Regexp).FindAllString": RepeatZeroTimes("a FindAll method", 1),
+ "(*regexp.Regexp).FindAllStringIndex": RepeatZeroTimes("a FindAll method", 1),
+ "(*regexp.Regexp).FindAllStringSubmatch": RepeatZeroTimes("a FindAll method", 1),
+ "(*regexp.Regexp).FindAllStringSubmatchIndex": RepeatZeroTimes("a FindAll method", 1),
+ "(*regexp.Regexp).FindAllSubmatch": RepeatZeroTimes("a FindAll method", 1),
+ "(*regexp.Regexp).FindAllSubmatchIndex": RepeatZeroTimes("a FindAll method", 1),
+ }
+
+ checkUTF8CutsetRules = map[string]CallCheck{
+ "strings.IndexAny": utf8Cutset,
+ "strings.LastIndexAny": utf8Cutset,
+ "strings.ContainsAny": utf8Cutset,
+ "strings.Trim": utf8Cutset,
+ "strings.TrimLeft": utf8Cutset,
+ "strings.TrimRight": utf8Cutset,
+ }
+
+ checkUniqueCutsetRules = map[string]CallCheck{
+ "strings.Trim": uniqueCutset,
+ "strings.TrimLeft": uniqueCutset,
+ "strings.TrimRight": uniqueCutset,
+ }
+
+ checkUnmarshalPointerRules = map[string]CallCheck{
+ "encoding/xml.Unmarshal": unmarshalPointer("xml.Unmarshal", 1),
+ "(*encoding/xml.Decoder).Decode": unmarshalPointer("Decode", 0),
+ "(*encoding/xml.Decoder).DecodeElement": unmarshalPointer("DecodeElement", 0),
+ "encoding/json.Unmarshal": unmarshalPointer("json.Unmarshal", 1),
+ "(*encoding/json.Decoder).Decode": unmarshalPointer("Decode", 0),
+ }
+
+ checkUnbufferedSignalChanRules = map[string]CallCheck{
+ "os/signal.Notify": func(call *Call) {
+ arg := call.Args[Arg("os/signal.Notify.c")]
+ if UnbufferedChannel(arg.Value) {
+ arg.Invalid("the channel used with signal.Notify should be buffered")
+ }
+ },
+ }
+
+ checkMathIntRules = map[string]CallCheck{
+ "math.Ceil": pointlessIntMath,
+ "math.Floor": pointlessIntMath,
+ "math.IsNaN": pointlessIntMath,
+ "math.Trunc": pointlessIntMath,
+ "math.IsInf": pointlessIntMath,
+ }
+
+ checkStringsReplaceZeroRules = map[string]CallCheck{
+ "strings.Replace": RepeatZeroTimes("strings.Replace", 3),
+ "bytes.Replace": RepeatZeroTimes("bytes.Replace", 3),
+ }
+
+ checkListenAddressRules = map[string]CallCheck{
+ "net/http.ListenAndServe": checkValidHostPort(0),
+ "net/http.ListenAndServeTLS": checkValidHostPort(0),
+ }
+
+ checkBytesEqualIPRules = map[string]CallCheck{
+ "bytes.Equal": func(call *Call) {
+ if ConvertedFrom(call.Args[Arg("bytes.Equal.a")].Value, "net.IP") &&
+ ConvertedFrom(call.Args[Arg("bytes.Equal.b")].Value, "net.IP") {
+ call.Invalid("use net.IP.Equal to compare net.IPs, not bytes.Equal")
+ }
+ },
+ }
+
+ checkRegexpMatchLoopRules = map[string]CallCheck{
+ "regexp.Match": loopedRegexp("regexp.Match"),
+ "regexp.MatchReader": loopedRegexp("regexp.MatchReader"),
+ "regexp.MatchString": loopedRegexp("regexp.MatchString"),
+ }
+
+ checkNoopMarshal = map[string]CallCheck{
+ // TODO(dh): should we really flag XML? Even an empty struct
+ // produces a non-zero amount of data, namely its type name.
+ // Let's see if we encounter any false positives.
+ //
+ // Also, should we flag gob?
+ "encoding/json.Marshal": checkNoopMarshalImpl(Arg("json.Marshal.v"), "MarshalJSON", "MarshalText"),
+ "encoding/xml.Marshal": checkNoopMarshalImpl(Arg("xml.Marshal.v"), "MarshalXML", "MarshalText"),
+ "(*encoding/json.Encoder).Encode": checkNoopMarshalImpl(Arg("(*encoding/json.Encoder).Encode.v"), "MarshalJSON", "MarshalText"),
+ "(*encoding/xml.Encoder).Encode": checkNoopMarshalImpl(Arg("(*encoding/xml.Encoder).Encode.v"), "MarshalXML", "MarshalText"),
+
+ "encoding/json.Unmarshal": checkNoopMarshalImpl(Arg("json.Unmarshal.v"), "UnmarshalJSON", "UnmarshalText"),
+ "encoding/xml.Unmarshal": checkNoopMarshalImpl(Arg("xml.Unmarshal.v"), "UnmarshalXML", "UnmarshalText"),
+ "(*encoding/json.Decoder).Decode": checkNoopMarshalImpl(Arg("(*encoding/json.Decoder).Decode.v"), "UnmarshalJSON", "UnmarshalText"),
+ "(*encoding/xml.Decoder).Decode": checkNoopMarshalImpl(Arg("(*encoding/xml.Decoder).Decode.v"), "UnmarshalXML", "UnmarshalText"),
+ }
+
+ checkUnsupportedMarshal = map[string]CallCheck{
+ "encoding/json.Marshal": checkUnsupportedMarshalImpl(Arg("json.Marshal.v"), "json", "MarshalJSON", "MarshalText"),
+ "encoding/xml.Marshal": checkUnsupportedMarshalImpl(Arg("xml.Marshal.v"), "xml", "MarshalXML", "MarshalText"),
+ "(*encoding/json.Encoder).Encode": checkUnsupportedMarshalImpl(Arg("(*encoding/json.Encoder).Encode.v"), "json", "MarshalJSON", "MarshalText"),
+ "(*encoding/xml.Encoder).Encode": checkUnsupportedMarshalImpl(Arg("(*encoding/xml.Encoder).Encode.v"), "xml", "MarshalXML", "MarshalText"),
+ }
+
+ checkAtomicAlignment = map[string]CallCheck{
+ "sync/atomic.AddInt64": checkAtomicAlignmentImpl,
+ "sync/atomic.AddUint64": checkAtomicAlignmentImpl,
+ "sync/atomic.CompareAndSwapInt64": checkAtomicAlignmentImpl,
+ "sync/atomic.CompareAndSwapUint64": checkAtomicAlignmentImpl,
+ "sync/atomic.LoadInt64": checkAtomicAlignmentImpl,
+ "sync/atomic.LoadUint64": checkAtomicAlignmentImpl,
+ "sync/atomic.StoreInt64": checkAtomicAlignmentImpl,
+ "sync/atomic.StoreUint64": checkAtomicAlignmentImpl,
+ "sync/atomic.SwapInt64": checkAtomicAlignmentImpl,
+ "sync/atomic.SwapUint64": checkAtomicAlignmentImpl,
+ }
+
+ // TODO(dh): detect printf wrappers
+ checkPrintfRules = map[string]CallCheck{
+ "fmt.Errorf": func(call *Call) { checkPrintfCall(call, 0, 1) },
+ "fmt.Printf": func(call *Call) { checkPrintfCall(call, 0, 1) },
+ "fmt.Sprintf": func(call *Call) { checkPrintfCall(call, 0, 1) },
+ "fmt.Fprintf": func(call *Call) { checkPrintfCall(call, 1, 2) },
+ }
+)
+
+func checkPrintfCall(call *Call, fIdx, vIdx int) {
+ f := call.Args[fIdx]
+ var args []ssa.Value
+ switch v := call.Args[vIdx].Value.Value.(type) {
+ case *ssa.Slice:
+ var ok bool
+ args, ok = ssautil.Vararg(v)
+ if !ok {
+ // We don't know what the actual arguments to the function are
+ return
+ }
+ case *ssa.Const:
+ // nil, i.e. no arguments
+ default:
+ // We don't know what the actual arguments to the function are
+ return
+ }
+ checkPrintfCallImpl(call, f.Value.Value, args)
+}
+
+type verbFlag int
+
+const (
+ isInt verbFlag = 1 << iota
+ isBool
+ isFP
+ isString
+ isPointer
+ isPseudoPointer
+ isSlice
+ isAny
+ noRecurse
+)
+
+var verbs = [...]verbFlag{
+ 'b': isPseudoPointer | isInt | isFP,
+ 'c': isInt,
+ 'd': isPseudoPointer | isInt,
+ 'e': isFP,
+ 'E': isFP,
+ 'f': isFP,
+ 'F': isFP,
+ 'g': isFP,
+ 'G': isFP,
+ 'o': isPseudoPointer | isInt,
+ 'p': isSlice | isPointer | noRecurse,
+ 'q': isInt | isString,
+ 's': isString,
+ 't': isBool,
+ 'T': isAny,
+ 'U': isInt,
+ 'v': isAny,
+ 'X': isPseudoPointer | isInt | isString,
+ 'x': isPseudoPointer | isInt | isString,
+}
+
+func checkPrintfCallImpl(call *Call, f ssa.Value, args []ssa.Value) {
+ 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
+ }
+ switch T := T.(type) {
+ case *types.Slice:
+ if verbs[verb]&isSlice != 0 {
+ return []types.Type{T}, false
+ }
+ if verbs[verb]&isString != 0 && IsType(T.Elem().Underlying(), "byte") {
+ return []types.Type{T}, false
+ }
+ return []types.Type{T.Elem()}, true
+ case *types.Map:
+ key := T.Key()
+ val := T.Elem()
+ return []types.Type{key, val}, true
+ case *types.Struct:
+ out := make([]types.Type, 0, T.NumFields())
+ for i := 0; i < T.NumFields(); i++ {
+ out = append(out, T.Field(i).Type())
+ }
+ return out, true
+ case *types.Array:
+ return []types.Type{T.Elem()}, true
+ default:
+ return []types.Type{T}, false
+ }
+ }
+ isInfo := func(T types.Type, info types.BasicInfo) bool {
+ basic, ok := T.Underlying().(*types.Basic)
+ return ok && basic.Info()&info != 0
+ }
+
+ isStringer := func(T types.Type, ms *types.MethodSet) bool {
+ sel := ms.Lookup(nil, "String")
+ if sel == nil {
+ return false
+ }
+ fn, ok := sel.Obj().(*types.Func)
+ if !ok {
+ // should be unreachable
+ return false
+ }
+ sig := fn.Type().(*types.Signature)
+ if sig.Params().Len() != 0 {
+ return false
+ }
+ if sig.Results().Len() != 1 {
+ return false
+ }
+ if !IsType(sig.Results().At(0).Type(), "string") {
+ return false
+ }
+ return true
+ }
+ isError := func(T types.Type, ms *types.MethodSet) bool {
+ sel := ms.Lookup(nil, "Error")
+ if sel == nil {
+ return false
+ }
+ fn, ok := sel.Obj().(*types.Func)
+ if !ok {
+ // should be unreachable
+ return false
+ }
+ sig := fn.Type().(*types.Signature)
+ if sig.Params().Len() != 0 {
+ return false
+ }
+ if sig.Results().Len() != 1 {
+ return false
+ }
+ if !IsType(sig.Results().At(0).Type(), "string") {
+ return false
+ }
+ return true
+ }
+
+ isFormatter := func(T types.Type, ms *types.MethodSet) bool {
+ sel := ms.Lookup(nil, "Format")
+ if sel == nil {
+ return false
+ }
+ fn, ok := sel.Obj().(*types.Func)
+ if !ok {
+ // should be unreachable
+ return false
+ }
+ sig := fn.Type().(*types.Signature)
+ if sig.Params().Len() != 2 {
+ return false
+ }
+ // TODO(dh): check the types of the arguments for more
+ // precision
+ if sig.Results().Len() != 0 {
+ return false
+ }
+ return true
+ }
+
+ seen := map[types.Type]bool{}
+ var checkType func(verb rune, T types.Type, top bool) bool
+ checkType = func(verb rune, T types.Type, top bool) bool {
+ if top {
+ for k := range seen {
+ delete(seen, k)
+ }
+ }
+ if seen[T] {
+ return true
+ }
+ seen[T] = true
+ if int(verb) >= len(verbs) {
+ // Unknown verb
+ return true
+ }
+
+ flags := verbs[verb]
+ if flags == 0 {
+ // Unknown verb
+ return true
+ }
+
+ ms := msCache.MethodSet(T)
+ if isFormatter(T, ms) {
+ // the value is responsible for formatting itself
+ return true
+ }
+
+ if flags&isString != 0 && (isStringer(T, ms) || isError(T, ms)) {
+ // Check for stringer early because we're about to dereference
+ return true
+ }
+
+ T = T.Underlying()
+ if flags&(isPointer|isPseudoPointer) == 0 && top {
+ T = Dereference(T)
+ }
+ if flags&isPseudoPointer != 0 && top {
+ t := Dereference(T)
+ if _, ok := t.Underlying().(*types.Struct); ok {
+ T = t
+ }
+ }
+
+ if _, ok := T.(*types.Interface); ok {
+ // We don't know what's in the interface
+ return true
+ }
+
+ var info types.BasicInfo
+ if flags&isInt != 0 {
+ info |= types.IsInteger
+ }
+ if flags&isBool != 0 {
+ info |= types.IsBoolean
+ }
+ if flags&isFP != 0 {
+ info |= types.IsFloat | types.IsComplex
+ }
+ if flags&isString != 0 {
+ info |= types.IsString
+ }
+
+ if info != 0 && isInfo(T, info) {
+ return true
+ }
+
+ if flags&isString != 0 && (IsType(T, "[]byte") || isStringer(T, ms) || isError(T, ms)) {
+ return true
+ }
+
+ if flags&isPointer != 0 && IsPointerLike(T) {
+ return true
+ }
+ if flags&isPseudoPointer != 0 {
+ switch U := T.Underlying().(type) {
+ case *types.Pointer:
+ if !top {
+ return true
+ }
+
+ if _, ok := U.Elem().Underlying().(*types.Struct); !ok {
+ return true
+ }
+ case *types.Chan, *types.Signature:
+ return true
+ }
+ }
+
+ if flags&isSlice != 0 {
+ if _, ok := T.(*types.Slice); ok {
+ return true
+ }
+ }
+
+ if flags&isAny != 0 {
+ return true
+ }
+
+ elems, ok := elem(T.Underlying(), verb)
+ if !ok {
+ return false
+ }
+ for _, elem := range elems {
+ if !checkType(verb, elem, false) {
+ return false
+ }
+ }
+
+ return true
+ }
+
+ k, ok := f.(*ssa.Const)
+ if !ok {
+ return
+ }
+ actions, err := printf.Parse(constant.StringVal(k.Value))
+ if err != nil {
+ call.Invalid("couldn't parse format string")
+ return
+ }
+
+ ptr := 1
+ hasExplicit := false
+
+ checkStar := func(verb printf.Verb, star printf.Argument) bool {
+ if star, ok := star.(printf.Star); ok {
+ idx := 0
+ if star.Index == -1 {
+ idx = ptr
+ ptr++
+ } else {
+ hasExplicit = true
+ idx = star.Index
+ ptr = star.Index + 1
+ }
+ if idx == 0 {
+ call.Invalid(fmt.Sprintf("Printf format %s reads invalid arg 0; indices are 1-based", verb.Raw))
+ return false
+ }
+ if idx > len(args) {
+ call.Invalid(
+ fmt.Sprintf("Printf format %s reads arg #%d, but call has only %d args",
+ verb.Raw, idx, len(args)))
+ return false
+ }
+ if arg, ok := args[idx-1].(*ssa.MakeInterface); ok {
+ if !isInfo(arg.X.Type(), types.IsInteger) {
+ call.Invalid(fmt.Sprintf("Printf format %s reads non-int arg #%d as argument of *", verb.Raw, idx))
+ }
+ }
+ }
+ return true
+ }
+
+ // We only report one problem per format string. Making a
+ // mistake with an index tends to invalidate all future
+ // implicit indices.
+ for _, action := range actions {
+ verb, ok := action.(printf.Verb)
+ if !ok {
+ continue
+ }
+
+ if !checkStar(verb, verb.Width) || !checkStar(verb, verb.Precision) {
+ return
+ }
+
+ off := ptr
+ if verb.Value != -1 {
+ hasExplicit = true
+ off = verb.Value
+ }
+ if off > len(args) {
+ call.Invalid(
+ fmt.Sprintf("Printf format %s reads arg #%d, but call has only %d args",
+ verb.Raw, off, len(args)))
+ return
+ } else if verb.Value == 0 && verb.Letter != '%' {
+ call.Invalid(fmt.Sprintf("Printf format %s reads invalid arg 0; indices are 1-based", verb.Raw))
+ return
+ } else if off != 0 {
+ arg, ok := args[off-1].(*ssa.MakeInterface)
+ if ok {
+ if !checkType(verb.Letter, arg.X.Type(), true) {
+ call.Invalid(fmt.Sprintf("Printf format %s has arg #%d of wrong type %s",
+ verb.Raw, ptr, args[ptr-1].(*ssa.MakeInterface).X.Type()))
+ return
+ }
+ }
+ }
+
+ switch verb.Value {
+ case -1:
+ // Consume next argument
+ ptr++
+ case 0:
+ // Don't consume any arguments
+ default:
+ ptr = verb.Value + 1
+ }
+ }
+
+ if !hasExplicit && ptr <= len(args) {
+ call.Invalid(fmt.Sprintf("Printf call needs %d args but has %d args", ptr-1, len(args)))
+ }
+}
+
+func checkAtomicAlignmentImpl(call *Call) {
+ sizes := call.Pass.TypesSizes
+ if sizes.Sizeof(types.Typ[types.Uintptr]) != 4 {
+ // Not running on a 32-bit platform
+ return
+ }
+ v, ok := call.Args[0].Value.Value.(*ssa.FieldAddr)
+ if !ok {
+ // TODO(dh): also check indexing into arrays and slices
+ return
+ }
+ T := v.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct)
+ fields := make([]*types.Var, 0, T.NumFields())
+ for i := 0; i < T.NumFields() && i <= v.Field; i++ {
+ fields = append(fields, T.Field(i))
+ }
+
+ off := sizes.Offsetsof(fields)[v.Field]
+ if off%8 != 0 {
+ msg := fmt.Sprintf("address of non 64-bit aligned field %s passed to %s",
+ T.Field(v.Field).Name(),
+ CallName(call.Instr.Common()))
+ call.Invalid(msg)
+ }
+}
+
+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)
+ if !ok {
+ return
+ }
+ if Ts.NumFields() == 0 {
+ return
+ }
+ fields := FlattenFields(Ts)
+ for _, field := range fields {
+ if field.Var.Exported() {
+ return
+ }
+ }
+ // OPT(dh): we could use a method set cache here
+ 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 {
+ if ms.Lookup(nil, meth) != nil {
+ return
+ }
+ }
+ arg.Invalid("struct doesn't have any exported fields, nor custom marshaling")
+ }
+}
+
+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
+ }
+ 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 {
+ if ms.Lookup(nil, meth) != nil {
+ return
+ }
+ }
+ fields := FlattenFields(Ts)
+ for _, field := range fields {
+ if !(field.Var.Exported()) {
+ continue
+ }
+ if reflect.StructTag(field.Tag).Get(tag) == "-" {
+ continue
+ }
+ 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 {
+ if ms.Lookup(nil, meth) != nil {
+ return
+ }
+ }
+ switch field.Var.Type().Underlying().(type) {
+ case *types.Chan, *types.Signature:
+ arg.Invalid(fmt.Sprintf("trying to marshal chan or func value, field %s", fieldPath(T, field.Path)))
+ }
+ }
+ }
+}
+
+func fieldPath(start types.Type, indices []int) string {
+ p := start.String()
+ for _, idx := range indices {
+ field := Dereference(start).Underlying().(*types.Struct).Field(idx)
+ start = field.Type()
+ p += "." + field.Name()
+ }
+ return p
+}
+
+func isInLoop(b *ssa.BasicBlock) bool {
+ sets := functions.FindLoops(b.Parent())
+ for _, set := range sets {
+ if set.Has(b) {
+ return true
+ }
+ }
+ return false
+}
+
+func CheckUntrappableSignal(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ call := node.(*ast.CallExpr)
+ 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(pass, conv.Fun, "os.Signal") {
+ arg = conv.Args[0]
+ }
+
+ 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(pass, arg, "syscall.SIGSTOP") {
+ ReportNodef(pass, arg, "%s signal cannot be trapped", Render(pass, arg))
+ }
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckTemplate(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ call := node.(*ast.CallExpr)
+ var kind string
+ if IsCallToAST(pass, call, "(*text/template.Template).Parse") {
+ kind = "text"
+ } else if IsCallToAST(pass, call, "(*html/template.Template).Parse") {
+ kind = "html"
+ } else {
+ return
+ }
+ sel := call.Fun.(*ast.SelectorExpr)
+ 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(pass, call.Args[Arg("(*text/template.Template).Parse.text")])
+ if !ok {
+ return
+ }
+ var err error
+ switch kind {
+ case "text":
+ _, err = texttemplate.New("").Parse(s)
+ case "html":
+ _, err = htmltemplate.New("").Parse(s)
+ }
+ if err != nil {
+ // TODO(dominikh): whitelist other parse errors, if any
+ if strings.Contains(err.Error(), "unexpected") {
+ ReportNodef(pass, call.Args[Arg("(*text/template.Template).Parse.text")], "%s", err)
+ }
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckTimeSleepConstant(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ call := node.(*ast.CallExpr)
+ if !IsCallToAST(pass, call, "time.Sleep") {
+ return
+ }
+ lit, ok := call.Args[Arg("time.Sleep.d")].(*ast.BasicLit)
+ if !ok {
+ return
+ }
+ n, err := strconv.Atoi(lit.Value)
+ if err != nil {
+ return
+ }
+ if n == 0 || n > 120 {
+ // time.Sleep(0) is a seldom used pattern in concurrency
+ // tests. >120 might be intentional. 120 was chosen
+ // because the user could've meant 2 minutes.
+ return
+ }
+ recommendation := "time.Sleep(time.Nanosecond)"
+ if n != 1 {
+ recommendation = fmt.Sprintf("time.Sleep(%d * time.Nanosecond)", n)
+ }
+ 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)
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckWaitgroupAdd(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ g := node.(*ast.GoStmt)
+ fun, ok := g.Call.Fun.(*ast.FuncLit)
+ if !ok {
+ return
+ }
+ if len(fun.Body.List) == 0 {
+ return
+ }
+ stmt, ok := fun.Body.List[0].(*ast.ExprStmt)
+ if !ok {
+ return
+ }
+ 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))
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.GoStmt)(nil)}, fn)
+ return nil, nil
+}
+
+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 {
+ return
+ }
+
+ if loop.Init != nil {
+ // TODO(dh): this isn't strictly necessary, it just makes
+ // the check easier.
+ return
+ }
+ // An empty loop is bad news in two cases: 1) The loop has no
+ // condition. In that case, it's just a loop that spins
+ // forever and as fast as it can, keeping a core busy. 2) The
+ // loop condition only consists of variable or field reads and
+ // operators on those. The only way those could change their
+ // value is with unsynchronised access, which constitutes a
+ // data race.
+ //
+ // If the condition contains any function calls, its behaviour
+ // is dynamic and the loop might terminate. Similarly for
+ // channel receives.
+
+ if loop.Cond != nil {
+ if hasSideEffects(loop.Cond) {
+ return
+ }
+ if ident, ok := loop.Cond.(*ast.Ident); 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
+ }
+ }
+ }
+ ReportNodef(pass, loop, "loop condition never changes or has a race condition")
+ }
+ ReportNodef(pass, loop, "this loop will spin, using 100%% CPU")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckDeferInInfiniteLoop(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ mightExit := false
+ var defers []ast.Stmt
+ loop := node.(*ast.ForStmt)
+ if loop.Cond != nil {
+ return
+ }
+ fn2 := func(node ast.Node) bool {
+ 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
+ // just the select/switch. This causes some false
+ // negatives.
+ if stmt.Tok == token.BREAK {
+ mightExit = true
+ return false
+ }
+ case *ast.DeferStmt:
+ defers = append(defers, stmt)
+ case *ast.FuncLit:
+ // Don't look into function bodies
+ return false
+ }
+ return true
+ }
+ ast.Inspect(loop.Body, fn2)
+ if mightExit {
+ return
+ }
+ for _, stmt := range defers {
+ ReportNodef(pass, stmt, "defers in this infinite loop will never run")
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckDubiousDeferInChannelRangeLoop(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ loop := node.(*ast.RangeStmt)
+ typ := pass.TypesInfo.TypeOf(loop.X)
+ _, ok := typ.Underlying().(*types.Chan)
+ if !ok {
+ return
+ }
+ fn2 := func(node ast.Node) bool {
+ switch stmt := node.(type) {
+ case *ast.DeferStmt:
+ 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
+ }
+ return true
+ }
+ ast.Inspect(loop.Body, fn2)
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.RangeStmt)(nil)}, fn)
+ return nil, nil
+}
+
+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
+ }
+
+ switch node := node.(type) {
+ case *ast.FuncDecl:
+ if fnmain != nil {
+ return true
+ }
+ 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
+ }
+ ident, ok := sel.X.(*ast.Ident)
+ if !ok {
+ return true
+ }
+ if arg != pass.TypesInfo.ObjectOf(ident) {
+ return true
+ }
+ if sel.Sel.Name == "Run" {
+ callsRun = true
+ return false
+ }
+ return true
+ default:
+ // unreachable
+ return true
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.FuncDecl)(nil), (*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func isTestMain(pass *analysis.Pass, decl *ast.FuncDecl) bool {
+ if decl.Name.Name != "TestMain" {
+ return false
+ }
+ if len(decl.Type.Params.List) != 1 {
+ return false
+ }
+ arg := decl.Type.Params.List[0]
+ if len(arg.Names) != 1 {
+ return false
+ }
+ return IsOfType(pass, arg.Type, "*testing.M")
+}
+
+func CheckExec(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ call := node.(*ast.CallExpr)
+ if !IsCallToAST(pass, call, "os/exec.Command") {
+ return
+ }
+ 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
+ }
+ 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")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+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 {
+ return
+ }
+ sel, ok := loop.Body.List[0].(*ast.SelectStmt)
+ if !ok {
+ return
+ }
+ for _, c := range sel.Body.List {
+ if comm, ok := c.(*ast.CommClause); ok && comm.Comm == nil && len(comm.Body) == 0 {
+ ReportNodef(pass, comm, "should not have an empty default case in a for+select loop. The loop will spin.")
+ }
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil)}, fn)
+ return nil, nil
+}
+
+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 := 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
+ }
+ }
+ case token.SUB, token.QUO, token.AND, token.REM, token.OR, token.XOR, token.AND_NOT,
+ token.LAND, token.LOR, token.LSS, token.GTR, token.LEQ, token.GEQ:
+ default:
+ // For some ops, such as + and *, it can make sense to
+ // have identical operands
+ return
+ }
+
+ 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(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
+ // identifier. Ideally we'd restrict this exception to
+ // calls of _cgoCheckPointer, but it's not worth the
+ // hassle of keeping track of the stack.
+ // are very rare to begin with, and we're mostly checking
+ // for them to catch typos such as 1 == 1 where the user
+ // meant to type i == 1. The odds of a false negative for
+ // 0 == 0 are slim.
+ return
+ }
+ ReportNodef(pass, op, "identical expressions on the left and right side of the '%s' operator", op.Op)
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckScopedBreak(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ var body *ast.BlockStmt
+ switch node := node.(type) {
+ case *ast.ForStmt:
+ body = node.Body
+ case *ast.RangeStmt:
+ body = node.Body
+ default:
+ panic(fmt.Sprintf("unreachable: %T", node))
+ }
+ for _, stmt := range body.List {
+ var blocks [][]ast.Stmt
+ switch stmt := stmt.(type) {
+ case *ast.SwitchStmt:
+ for _, c := range stmt.Body.List {
+ blocks = append(blocks, c.(*ast.CaseClause).Body)
+ }
+ case *ast.SelectStmt:
+ for _, c := range stmt.Body.List {
+ blocks = append(blocks, c.(*ast.CommClause).Body)
+ }
+ default:
+ continue
+ }
+
+ for _, body := range blocks {
+ if len(body) == 0 {
+ continue
+ }
+ lasts := []ast.Stmt{body[len(body)-1]}
+ // TODO(dh): unfold all levels of nested block
+ // statements, not just a single level if statement
+ if ifs, ok := lasts[0].(*ast.IfStmt); ok {
+ if len(ifs.Body.List) == 0 {
+ continue
+ }
+ lasts[0] = ifs.Body.List[len(ifs.Body.List)-1]
+
+ if block, ok := ifs.Else.(*ast.BlockStmt); ok {
+ if len(block.List) != 0 {
+ lasts = append(lasts, block.List[len(block.List)-1])
+ }
+ }
+ }
+ for _, last := range lasts {
+ branch, ok := last.(*ast.BranchStmt)
+ if !ok || branch.Tok != token.BREAK || branch.Label != nil {
+ continue
+ }
+ ReportNodef(pass, branch, "ineffective break statement. Did you mean to break out of the outer loop?")
+ }
+ }
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.ForStmt)(nil), (*ast.RangeStmt)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckUnsafePrintf(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ call := node.(*ast.CallExpr)
+ var arg int
+ if IsCallToAnyAST(pass, call, "fmt.Printf", "fmt.Sprintf", "log.Printf") {
+ arg = Arg("fmt.Printf.format")
+ } else if IsCallToAnyAST(pass, call, "fmt.Fprintf") {
+ arg = Arg("fmt.Fprintf.format")
+ } else {
+ return
+ }
+ if len(call.Args) != arg+1 {
+ return
+ }
+ switch call.Args[arg].(type) {
+ case *ast.CallExpr, *ast.Ident:
+ default:
+ return
+ }
+ ReportNodef(pass, call.Args[arg],
+ "printf-style function with dynamic format string and no further arguments should use print-style function instead")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckEarlyDefer(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ block := node.(*ast.BlockStmt)
+ if len(block.List) < 2 {
+ return
+ }
+ for i, stmt := range block.List {
+ if i == len(block.List)-1 {
+ break
+ }
+ assign, ok := stmt.(*ast.AssignStmt)
+ if !ok {
+ continue
+ }
+ if len(assign.Rhs) != 1 {
+ continue
+ }
+ if len(assign.Lhs) < 2 {
+ continue
+ }
+ if lhs, ok := assign.Lhs[len(assign.Lhs)-1].(*ast.Ident); ok && lhs.Name == "_" {
+ continue
+ }
+ call, ok := assign.Rhs[0].(*ast.CallExpr)
+ if !ok {
+ continue
+ }
+ sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature)
+ if !ok {
+ continue
+ }
+ if sig.Results().Len() < 2 {
+ continue
+ }
+ last := sig.Results().At(sig.Results().Len() - 1)
+ // FIXME(dh): check that it's error from universe, not
+ // another type of the same name
+ if last.Type().String() != "error" {
+ continue
+ }
+ lhs, ok := assign.Lhs[0].(*ast.Ident)
+ if !ok {
+ continue
+ }
+ def, ok := block.List[i+1].(*ast.DeferStmt)
+ if !ok {
+ continue
+ }
+ sel, ok := def.Call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ continue
+ }
+ ident, ok := selectorX(sel).(*ast.Ident)
+ if !ok {
+ continue
+ }
+ if ident.Obj != lhs.Obj {
+ continue
+ }
+ if sel.Sel.Name != "Close" {
+ continue
+ }
+ ReportNodef(pass, def, "should check returned error before deferring %s", Render(pass, def.Call))
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BlockStmt)(nil)}, fn)
+ return nil, nil
+}
+
+func selectorX(sel *ast.SelectorExpr) ast.Node {
+ switch x := sel.X.(type) {
+ case *ast.SelectorExpr:
+ return selectorX(x)
+ default:
+ return x
+ }
+}
+
+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
+ // number of other instructions between the lock and unlock, while
+ // still constituting an empty critical section. For example,
+ // given `m.x().Lock(); m.x().Unlock()`, there will be a call to
+ // x(). In the AST-based approach, this has a tiny potential for a
+ // false positive (the second call to x might be doing work that
+ // is protected by the mutex). In an SSA-based approach, however,
+ // it would miss a lot of real bugs.
+
+ mutexParams := func(s ast.Stmt) (x ast.Expr, funcName string, ok bool) {
+ expr, ok := s.(*ast.ExprStmt)
+ if !ok {
+ return nil, "", false
+ }
+ call, ok := expr.X.(*ast.CallExpr)
+ if !ok {
+ return nil, "", false
+ }
+ sel, ok := call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return nil, "", false
+ }
+
+ fn, ok := pass.TypesInfo.ObjectOf(sel.Sel).(*types.Func)
+ if !ok {
+ return nil, "", false
+ }
+ sig := fn.Type().(*types.Signature)
+ if sig.Params().Len() != 0 || sig.Results().Len() != 0 {
+ return nil, "", false
+ }
+
+ return sel.X, fn.Name(), true
+ }
+
+ fn := func(node ast.Node) {
+ block := node.(*ast.BlockStmt)
+ if len(block.List) < 2 {
+ return
+ }
+ for i := range block.List[:len(block.List)-1] {
+ sel1, method1, ok1 := mutexParams(block.List[i])
+ sel2, method2, ok2 := mutexParams(block.List[i+1])
+
+ if !ok1 || !ok2 || Render(pass, sel1) != Render(pass, sel2) {
+ continue
+ }
+ if (method1 == "Lock" && method2 == "Unlock") ||
+ (method1 == "RLock" && method2 == "RUnlock") {
+ ReportNodef(pass, block.List[i+1], "empty critical section")
+ }
+ }
+ }
+ 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 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) {
+ 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 {
+ ReportNodef(pass, star, "*&x will be simplified to x. It will not copy x.")
+ }
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.UnaryExpr)(nil), (*ast.StarExpr)(nil)}, fn)
+ return nil, nil
+}
+
+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)
+ if !ok {
+ continue
+ }
+ if binop.Op != token.EQL && binop.Op != token.NEQ {
+ continue
+ }
+ _, ok1 := binop.X.(*ssa.Slice)
+ _, ok2 := binop.Y.(*ssa.Slice)
+ if !ok1 && !ok2 {
+ continue
+ }
+ 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() {
+ pass.Reportf(binop.Pos(), "comparing strings of different sizes for equality will always return false")
+ }
+ }
+ }
+ }
+ return nil, nil
+}
+
+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
+ // example in `h1["foo"] = h2["foo"]` – these edge
+ // cases are probably rare enough to ignore for now.
+ for _, expr := range assign.Lhs {
+ op, ok := expr.(*ast.IndexExpr)
+ if !ok {
+ continue
+ }
+ if IsOfType(pass, op.X, "net/http.Header") {
+ return false
+ }
+ }
+ return true
+ }
+ op, ok := node.(*ast.IndexExpr)
+ if !ok {
+ return true
+ }
+ if !IsOfType(pass, op.X, "net/http.Header") {
+ return true
+ }
+ s, ok := ExprToString(pass, op.Index)
+ if !ok {
+ return true
+ }
+ if s == http.CanonicalHeaderKey(s) {
+ return true
+ }
+ ReportNodef(pass, op, "keys in http.Header are canonicalized, %q is not canonical; fix the constant or use http.CanonicalHeaderKey", s)
+ return true
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.AssignStmt)(nil), (*ast.IndexExpr)(nil)}, fn)
+ return nil, nil
+}
+
+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 {
+ return
+ }
+ sel, ok := assign.Lhs[0].(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+ if sel.Sel.Name != "N" {
+ return
+ }
+ if !IsOfType(pass, sel.X, "*testing.B") {
+ return
+ }
+ ReportNodef(pass, assign, "should not assign to %s", Render(pass, sel))
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckUnreadVariableValues(pass *analysis.Pass) (interface{}, error) {
+ for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs {
+ if IsExample(ssafn) {
+ continue
+ }
+ node := ssafn.Syntax()
+ 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)
+ if !ok {
+ return true
+ }
+ if len(assign.Lhs) > 1 && len(assign.Rhs) == 1 {
+ // Either a function call with multiple return values,
+ // or a comma-ok assignment
+
+ val, _ := ssafn.ValueForExpr(assign.Rhs[0])
+ if val == nil {
+ return true
+ }
+ refs := val.Referrers()
+ if refs == nil {
+ return true
+ }
+ for _, ref := range *refs {
+ ex, ok := ref.(*ssa.Extract)
+ if !ok {
+ continue
+ }
+ if !hasUse(ex) {
+ lhs := assign.Lhs[ex.Index]
+ if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" {
+ continue
+ }
+ ReportNodef(pass, lhs, "this value of %s is never used", lhs)
+ }
+ }
+ return true
+ }
+ for i, lhs := range assign.Lhs {
+ rhs := assign.Rhs[i]
+ if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" {
+ continue
+ }
+ val, _ := ssafn.ValueForExpr(rhs)
+ if val == nil {
+ continue
+ }
+
+ if !hasUse(val) {
+ ReportNodef(pass, lhs, "this value of %s is never used", lhs)
+ }
+ }
+ return true
+ })
+ }
+ return nil, nil
+}
+
+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)
+ if !ok {
+ continue
+ }
+ switch ssabinop.Op {
+ case token.GTR, token.LSS, token.EQL, token.NEQ, token.LEQ, token.GEQ:
+ default:
+ continue
+ }
+
+ xs, ok1 := consts(ssabinop.X, nil, nil)
+ ys, ok2 := consts(ssabinop.Y, nil, nil)
+ if !ok1 || !ok2 || len(xs) == 0 || len(ys) == 0 {
+ continue
+ }
+
+ trues := 0
+ for _, x := range xs {
+ for _, y := range ys {
+ if x.Value == nil {
+ if y.Value == nil {
+ trues++
+ }
+ continue
+ }
+ if constant.Compare(x.Value, ssabinop.Op, y.Value) {
+ trues++
+ }
+ }
+ }
+ b := trues != 0
+ if trues == 0 || trues == len(xs)*len(ys) {
+ 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 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)
+ if !ok {
+ continue
+ }
+ c, ok := mu.Map.(*ssa.Const)
+ if !ok {
+ continue
+ }
+ if c.Value != nil {
+ continue
+ }
+ pass.Reportf(mu.Pos(), "assignment to nil map")
+ }
+ }
+ }
+ return nil, nil
+}
+
+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(pass.TypesInfo.ObjectOf(sel.Sel), name)
+ }
+
+ fn := func(node ast.Node) {
+ expr := node.(*ast.BinaryExpr)
+ tx := pass.TypesInfo.TypeOf(expr.X)
+ basic, ok := tx.Underlying().(*types.Basic)
+ if !ok {
+ return
+ }
+
+ var max string
+ var min string
+
+ switch basic.Kind() {
+ case types.Uint8:
+ max = "math.MaxUint8"
+ case types.Uint16:
+ max = "math.MaxUint16"
+ case types.Uint32:
+ max = "math.MaxUint32"
+ case types.Uint64:
+ max = "math.MaxUint64"
+ case types.Uint:
+ max = "math.MaxUint64"
+
+ case types.Int8:
+ min = "math.MinInt8"
+ max = "math.MaxInt8"
+ case types.Int16:
+ min = "math.MinInt16"
+ max = "math.MaxInt16"
+ case types.Int32:
+ min = "math.MinInt32"
+ max = "math.MaxInt32"
+ case types.Int64:
+ min = "math.MinInt64"
+ max = "math.MaxInt64"
+ case types.Int:
+ min = "math.MinInt64"
+ max = "math.MaxInt64"
+ }
+
+ 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) {
+ 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) {
+ 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") {
+ 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") {
+ 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) {
+ 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) {
+ ReportNodef(pass, expr, "every value of type %s is >= %s", basic, min)
+ }
+ }
+
+ }
+ 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) {
+ if visitedPhis == nil {
+ visitedPhis = map[string]bool{}
+ }
+ var ok bool
+ switch val := val.(type) {
+ case *ssa.Phi:
+ if visitedPhis[val.Name()] {
+ break
+ }
+ visitedPhis[val.Name()] = true
+ vals := val.Operands(nil)
+ for _, phival := range vals {
+ out, ok = consts(*phival, out, visitedPhis)
+ if !ok {
+ return nil, false
+ }
+ }
+ case *ssa.Const:
+ out = append(out, val)
+ case *ssa.Convert:
+ out, ok = consts(val.X, out, visitedPhis)
+ if !ok {
+ return nil, false
+ }
+ default:
+ return nil, false
+ }
+ if len(out) < 2 {
+ return out, true
+ }
+ uniq := []*ssa.Const{out[0]}
+ for _, val := range out[1:] {
+ if val.Value == uniq[len(uniq)-1].Value {
+ continue
+ }
+ uniq = append(uniq, val)
+ }
+ return uniq, true
+}
+
+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 {
+ return true
+ }
+ if loop.Init == nil || loop.Cond == nil || loop.Post == nil {
+ return true
+ }
+ init, ok := loop.Init.(*ast.AssignStmt)
+ if !ok || len(init.Lhs) != 1 || len(init.Rhs) != 1 {
+ return true
+ }
+ cond, ok := loop.Cond.(*ast.BinaryExpr)
+ if !ok {
+ return true
+ }
+ x, ok := cond.X.(*ast.Ident)
+ if !ok {
+ return true
+ }
+ lhs, ok := init.Lhs[0].(*ast.Ident)
+ if !ok {
+ return true
+ }
+ if x.Obj != lhs.Obj {
+ return true
+ }
+ if _, ok := loop.Post.(*ast.IncDecStmt); !ok {
+ return true
+ }
+
+ v, isAddr := ssafn.ValueForExpr(cond.X)
+ if v == nil || isAddr {
+ return true
+ }
+ switch v := v.(type) {
+ case *ssa.Phi:
+ ops := v.Operands(nil)
+ if len(ops) != 2 {
+ return true
+ }
+ _, ok := (*ops[0]).(*ssa.Const)
+ if !ok {
+ return true
+ }
+ sigma, ok := (*ops[1]).(*ssa.Sigma)
+ if !ok {
+ return true
+ }
+ if sigma.X != v {
+ return true
+ }
+ case *ssa.UnOp:
+ return true
+ }
+ ReportNodef(pass, cond, "variable in loop condition never changes")
+
+ return true
+ }
+ Inspect(ssafn.Syntax(), fn)
+ }
+ return nil, nil
+}
+
+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
+ switch fn := node.(type) {
+ case *ast.FuncDecl:
+ typ = fn.Type
+ body = fn.Body
+ case *ast.FuncLit:
+ typ = fn.Type
+ body = fn.Body
+ }
+ if body == nil {
+ return true
+ }
+ if len(typ.Params.List) == 0 {
+ return true
+ }
+ for _, field := range typ.Params.List {
+ for _, arg := range field.Names {
+ obj := pass.TypesInfo.ObjectOf(arg)
+ var ssaobj *ssa.Parameter
+ for _, param := range ssafn.Params {
+ if param.Object() == obj {
+ ssaobj = param
+ break
+ }
+ }
+ if ssaobj == nil {
+ continue
+ }
+ refs := ssaobj.Referrers()
+ if refs == nil {
+ continue
+ }
+ if len(FilterDebug(*refs)) != 0 {
+ continue
+ }
+
+ assigned := false
+ ast.Inspect(body, func(node ast.Node) bool {
+ assign, ok := node.(*ast.AssignStmt)
+ if !ok {
+ return true
+ }
+ for _, lhs := range assign.Lhs {
+ ident, ok := lhs.(*ast.Ident)
+ if !ok {
+ continue
+ }
+ if pass.TypesInfo.ObjectOf(ident) == obj {
+ assigned = true
+ return false
+ }
+ }
+ return true
+ })
+ if assigned {
+ ReportNodef(pass, arg, "argument %s is overwritten before first use", arg)
+ }
+ }
+ }
+ return true
+ }
+ Inspect(ssafn.Syntax(), fn)
+ }
+ return nil, nil
+}
+
+func CheckIneffectiveLoop(pass *analysis.Pass) (interface{}, error) {
+ // This check detects some, but not all unconditional loop exits.
+ // We give up in the following cases:
+ //
+ // - a goto anywhere in the loop. The goto might skip over our
+ // return, and we don't check that it doesn't.
+ //
+ // - any nested, unlabelled continue, even if it is in another
+ // loop or closure.
+ fn := func(node ast.Node) {
+ var body *ast.BlockStmt
+ switch fn := node.(type) {
+ case *ast.FuncDecl:
+ body = fn.Body
+ case *ast.FuncLit:
+ body = fn.Body
+ default:
+ panic(fmt.Sprintf("unreachable: %T", node))
+ }
+ if body == nil {
+ return
+ }
+ labels := map[*ast.Object]ast.Stmt{}
+ ast.Inspect(body, func(node ast.Node) bool {
+ label, ok := node.(*ast.LabeledStmt)
+ if !ok {
+ return true
+ }
+ labels[label.Label.Obj] = label.Stmt
+ return true
+ })
+
+ ast.Inspect(body, func(node ast.Node) bool {
+ var loop ast.Node
+ var body *ast.BlockStmt
+ switch node := node.(type) {
+ case *ast.ForStmt:
+ body = node.Body
+ loop = node
+ case *ast.RangeStmt:
+ 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.
+ return true
+ }
+ body = node.Body
+ loop = node
+ default:
+ return true
+ }
+ if len(body.List) < 2 {
+ // avoid flagging the somewhat common pattern of using
+ // a range loop to get the first element in a slice,
+ // or the first rune in a string.
+ return true
+ }
+ var unconditionalExit ast.Node
+ hasBranching := false
+ for _, stmt := range body.List {
+ switch stmt := stmt.(type) {
+ case *ast.BranchStmt:
+ switch stmt.Tok {
+ case token.BREAK:
+ if stmt.Label == nil || labels[stmt.Label.Obj] == loop {
+ unconditionalExit = stmt
+ }
+ case token.CONTINUE:
+ if stmt.Label == nil || labels[stmt.Label.Obj] == loop {
+ unconditionalExit = nil
+ return false
+ }
+ }
+ case *ast.ReturnStmt:
+ unconditionalExit = stmt
+ case *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.SelectStmt:
+ hasBranching = true
+ }
+ }
+ if unconditionalExit == nil || !hasBranching {
+ return false
+ }
+ ast.Inspect(body, func(node ast.Node) bool {
+ if branch, ok := node.(*ast.BranchStmt); ok {
+
+ switch branch.Tok {
+ case token.GOTO:
+ unconditionalExit = nil
+ return false
+ case token.CONTINUE:
+ if branch.Label != nil && labels[branch.Label.Obj] != loop {
+ return true
+ }
+ unconditionalExit = nil
+ return false
+ }
+ }
+ return true
+ })
+ if unconditionalExit != nil {
+ ReportNodef(pass, unconditionalExit, "the surrounding loop is unconditionally terminated")
+ }
+ return true
+ })
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)}, fn)
+ return nil, nil
+}
+
+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 := pass.TypesInfo.TypeOf(call.Args[0]).(*types.Basic); !ok || typ.Kind() != types.UntypedNil {
+ return
+ }
+ sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature)
+ if !ok {
+ return
+ }
+ if sig.Params().Len() == 0 {
+ return
+ }
+ if !IsType(sig.Params().At(0).Type(), "context.Context") {
+ return
+ }
+ 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")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckSeeker(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ call := node.(*ast.CallExpr)
+ sel, ok := call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+ if sel.Sel.Name != "Seek" {
+ return
+ }
+ if len(call.Args) != 2 {
+ return
+ }
+ arg0, ok := call.Args[Arg("(io.Seeker).Seek.offset")].(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+ switch arg0.Sel.Name {
+ case "SeekStart", "SeekCurrent", "SeekEnd":
+ default:
+ return
+ }
+ pkg, ok := arg0.X.(*ast.Ident)
+ if !ok {
+ return
+ }
+ if pkg.Name != "io" {
+ return
+ }
+ ReportNodef(pass, call, "the first argument of io.Seeker is the offset, but an io.Seek* constant is being used instead")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckIneffectiveAppend(pass *analysis.Pass) (interface{}, error) {
+ isAppend := func(ins ssa.Value) bool {
+ call, ok := ins.(*ssa.Call)
+ if !ok {
+ return false
+ }
+ if call.Call.IsInvoke() {
+ return false
+ }
+ if builtin, ok := call.Call.Value.(*ssa.Builtin); !ok || builtin.Name() != "append" {
+ return false
+ }
+ return true
+ }
+
+ 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)
+ if !ok || !isAppend(val) {
+ continue
+ }
+
+ isUsed := false
+ visited := map[ssa.Instruction]bool{}
+ var walkRefs func(refs []ssa.Instruction)
+ walkRefs = func(refs []ssa.Instruction) {
+ loop:
+ for _, ref := range refs {
+ if visited[ref] {
+ continue
+ }
+ visited[ref] = true
+ if _, ok := ref.(*ssa.DebugRef); ok {
+ continue
+ }
+ switch ref := ref.(type) {
+ case *ssa.Phi:
+ walkRefs(*ref.Referrers())
+ case *ssa.Sigma:
+ walkRefs(*ref.Referrers())
+ case ssa.Value:
+ if !isAppend(ref) {
+ isUsed = true
+ } else {
+ walkRefs(*ref.Referrers())
+ }
+ case ssa.Instruction:
+ isUsed = true
+ break loop
+ }
+ }
+ }
+ refs := val.Referrers()
+ if refs == nil {
+ continue
+ }
+ walkRefs(*refs)
+ if !isUsed {
+ pass.Reportf(ins.Pos(), "this result of append is never used, except maybe in other appends")
+ }
+ }
+ }
+ }
+ return nil, nil
+}
+
+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)
+ if !ok {
+ continue
+ }
+ var fn *ssa.Function
+ switch val := gostmt.Call.Value.(type) {
+ case *ssa.Function:
+ fn = val
+ case *ssa.MakeClosure:
+ fn = val.Fn.(*ssa.Function)
+ default:
+ continue
+ }
+ if fn.Blocks == nil {
+ continue
+ }
+ for _, block := range fn.Blocks {
+ for _, ins := range block.Instrs {
+ call, ok := ins.(*ssa.Call)
+ if !ok {
+ continue
+ }
+ if call.Call.IsInvoke() {
+ continue
+ }
+ callee := call.Call.StaticCallee()
+ if callee == nil {
+ continue
+ }
+ recv := callee.Signature.Recv()
+ if recv == nil {
+ continue
+ }
+ if !IsType(recv.Type(), "*testing.common") {
+ continue
+ }
+ fn, ok := call.Call.StaticCallee().Object().(*types.Func)
+ if !ok {
+ continue
+ }
+ name := fn.Name()
+ switch name {
+ case "FailNow", "Fatal", "Fatalf", "SkipNow", "Skip", "Skipf":
+ default:
+ continue
+ }
+ 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 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 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)
+ if !ok {
+ continue
+ }
+ if _, ok := ia.X.Type().Underlying().(*types.Slice); !ok {
+ continue
+ }
+ sr, ok1 := c.funcDescs.Get(ssafn).Ranges[ia.X].(vrp.SliceInterval)
+ idxr, ok2 := c.funcDescs.Get(ssafn).Ranges[ia.Index].(vrp.IntInterval)
+ if !ok1 || !ok2 || !sr.IsKnown() || !idxr.IsKnown() || sr.Length.Empty() || idxr.Empty() {
+ continue
+ }
+ if idxr.Lower.Cmp(sr.Length.Upper) >= 0 {
+ ReportNodef(pass, ia, "index out of bounds")
+ }
+ }
+ }
+ }
+ return nil, nil
+}
+*/
+
+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 {
+ continue
+ }
+ for i, ins := range instrs[:len(instrs)-1] {
+ call, ok := ins.(*ssa.Call)
+ if !ok {
+ continue
+ }
+ if !IsCallTo(call.Common(), "(*sync.Mutex).Lock") && !IsCallTo(call.Common(), "(*sync.RWMutex).RLock") {
+ continue
+ }
+ nins, ok := instrs[i+1].(*ssa.Defer)
+ if !ok {
+ continue
+ }
+ if !IsCallTo(&nins.Call, "(*sync.Mutex).Lock") && !IsCallTo(&nins.Call, "(*sync.RWMutex).RLock") {
+ continue
+ }
+ if call.Common().Args[0] != nins.Call.Args[0] {
+ continue
+ }
+ name := shortCallName(call.Common())
+ alt := ""
+ switch name {
+ case "Lock":
+ alt = "Unlock"
+ case "RLock":
+ alt = "RUnlock"
+ }
+ pass.Reportf(nins.Pos(), "deferring %s right after having locked already; did you mean to defer %s?", name, alt)
+ }
+ }
+ }
+ return nil, nil
+}
+
+func CheckNaNComparison(pass *analysis.Pass) (interface{}, error) {
+ isNaN := func(v ssa.Value) bool {
+ call, ok := v.(*ssa.Call)
+ if !ok {
+ return false
+ }
+ return IsCallTo(call.Common(), "math.NaN")
+ }
+ 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)
+ if !ok {
+ continue
+ }
+ if isNaN(ins.X) || isNaN(ins.Y) {
+ pass.Reportf(ins.Pos(), "no value is equal to NaN, not even NaN itself")
+ }
+ }
+ }
+ }
+ return nil, nil
+}
+
+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 := site.(*ssa.Go); ok {
+ // Recursively spawning goroutines doesn't consume
+ // stack space infinitely, so don't flag it.
+ return
+ }
+
+ block := site.Block()
+ canReturn := false
+ for _, b := range ssafn.Blocks {
+ if block.Dominates(b) {
+ continue
+ }
+ if len(b.Instrs) == 0 {
+ continue
+ }
+ if _, ok := b.Instrs[len(b.Instrs)-1].(*ssa.Return); ok {
+ canReturn = true
+ break
+ }
+ }
+ if canReturn {
+ return
+ }
+ pass.Reportf(site.Pos(), "infinite recursive call")
+ })
+ }
+ return nil, nil
+}
+
+func objectName(obj types.Object) string {
+ if obj == nil {
+ return ""
+ }
+ var name string
+ if obj.Pkg() != nil && obj.Pkg().Scope().Lookup(obj.Name()) == obj {
+ s := obj.Pkg().Path()
+ if s != "" {
+ name += s + "."
+ }
+ }
+ name += obj.Name()
+ return name
+}
+
+func isName(pass *analysis.Pass, expr ast.Expr, name string) bool {
+ var obj types.Object
+ switch expr := expr.(type) {
+ case *ast.Ident:
+ obj = pass.TypesInfo.ObjectOf(expr)
+ case *ast.SelectorExpr:
+ obj = pass.TypesInfo.ObjectOf(expr.Sel)
+ }
+ return objectName(obj) == name
+}
+
+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 {
+ for _, ins := range block.Instrs {
+ call, ok := ins.(*ssa.Call)
+ if !ok || !IsCallTo(call.Common(), "time.Tick") {
+ continue
+ }
+ if !functions.Terminates(call.Parent()) {
+ continue
+ }
+ 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 CheckDoubleNegation(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ unary1 := node.(*ast.UnaryExpr)
+ unary2, ok := unary1.X.(*ast.UnaryExpr)
+ if !ok {
+ return
+ }
+ if unary1.Op != token.NOT || unary2.Op != token.NOT {
+ return
+ }
+ ReportNodef(pass, unary1, "negating a boolean twice has no effect; is this a typo?")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.UnaryExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func hasSideEffects(node ast.Node) bool {
+ dynamic := false
+ ast.Inspect(node, func(node ast.Node) bool {
+ switch node := node.(type) {
+ case *ast.CallExpr:
+ dynamic = true
+ return false
+ case *ast.UnaryExpr:
+ if node.Op == token.ARROW {
+ dynamic = true
+ return false
+ }
+ }
+ return true
+ })
+ return dynamic
+}
+
+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)
+ collectConds = func(ifstmt *ast.IfStmt, inits []ast.Stmt, conds []ast.Expr) ([]ast.Stmt, []ast.Expr) {
+ seen[ifstmt] = true
+ if ifstmt.Init != nil {
+ inits = append(inits, ifstmt.Init)
+ }
+ conds = append(conds, ifstmt.Cond)
+ if elsestmt, ok := ifstmt.Else.(*ast.IfStmt); ok {
+ return collectConds(elsestmt, inits, conds)
+ }
+ return inits, conds
+ }
+ fn := func(node ast.Node) {
+ ifstmt := node.(*ast.IfStmt)
+ if seen[ifstmt] {
+ return
+ }
+ inits, conds := collectConds(ifstmt, nil, nil)
+ if len(inits) > 0 {
+ return
+ }
+ for _, cond := range conds {
+ if hasSideEffects(cond) {
+ return
+ }
+ }
+ counts := map[string]int{}
+ for _, cond := range conds {
+ s := Render(pass, cond)
+ counts[s]++
+ if counts[s] == 2 {
+ ReportNodef(pass, cond, "this condition occurs multiple times in this if/else if chain")
+ }
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.IfStmt)(nil)}, fn)
+ return nil, nil
+}
+
+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)
+ if !ok {
+ continue
+ }
+
+ if c, ok := ins.Y.(*ssa.Const); !ok || c.Value == nil || c.Value.Kind() != constant.Int || c.Uint64() != 0 {
+ continue
+ }
+ switch ins.Op {
+ case token.AND, token.OR, token.XOR:
+ default:
+ // we do not flag shifts because too often, x<<0 is part
+ // of a pattern, x<<0, x<<8, x<<16, ...
+ continue
+ }
+ path, _ := astutil.PathEnclosingInterval(File(pass, ins), ins.Pos(), ins.Pos())
+ if len(path) == 0 {
+ continue
+ }
+ if node, ok := path[0].(*ast.BinaryExpr); !ok || !IsZero(node.Y) {
+ continue
+ }
+
+ switch ins.Op {
+ case token.AND:
+ pass.Reportf(ins.Pos(), "x & 0 always equals 0")
+ case token.OR, token.XOR:
+ pass.Reportf(ins.Pos(), "x %s 0 always equals x", ins.Op)
+ }
+ }
+ }
+ }
+ return nil, nil
+}
+
+func CheckNonOctalFileMode(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ call := node.(*ast.CallExpr)
+ sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature)
+ if !ok {
+ return
+ }
+ n := sig.Params().Len()
+ var args []int
+ for i := 0; i < n; i++ {
+ typ := sig.Params().At(i).Type()
+ if IsType(typ, "os.FileMode") {
+ args = append(args, i)
+ }
+ }
+ for _, i := range args {
+ lit, ok := call.Args[i].(*ast.BasicLit)
+ if !ok {
+ continue
+ }
+ if len(lit.Value) == 3 &&
+ lit.Value[0] != '0' &&
+ lit.Value[0] >= '0' && lit.Value[0] <= '7' &&
+ lit.Value[1] >= '0' && lit.Value[1] <= '7' &&
+ lit.Value[2] >= '0' && lit.Value[2] <= '7' {
+
+ v, err := strconv.ParseInt(lit.Value, 10, 64)
+ if err != nil {
+ continue
+ }
+ ReportNodef(pass, call.Args[i], "file mode '%s' evaluates to %#o; did you mean '0%s'?", lit.Value, v, lit.Value)
+ }
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckPureFunctions(pass *analysis.Pass) (interface{}, error) {
+ pure := pass.ResultOf[facts.Purity].(facts.PurityResult)
+
+fnLoop:
+ 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)
+ if IsType(param.Type(), "*testing.B") {
+ // Ignore discarded pure functions in code related
+ // to benchmarks. Instead of matching BenchmarkFoo
+ // functions, we match any function accepting a
+ // *testing.B. Benchmarks sometimes call generic
+ // functions for doing the actual work, and
+ // checking for the parameter is a lot easier and
+ // faster than analyzing call trees.
+ continue fnLoop
+ }
+ }
+ }
+
+ for _, b := range ssafn.Blocks {
+ for _, ins := range b.Instrs {
+ ins, ok := ins.(*ssa.Call)
+ if !ok {
+ continue
+ }
+ refs := ins.Referrers()
+ if refs == nil || len(FilterDebug(*refs)) > 0 {
+ continue
+ }
+ callee := ins.Common().StaticCallee()
+ if callee == nil {
+ continue
+ }
+ 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 CheckDeprecated(pass *analysis.Pass) (interface{}, error) {
+ deprs := pass.ResultOf[facts.Deprecated].(facts.DeprecatedResult)
+
+ // Selectors can appear outside of function literals, e.g. when
+ // declaring package level variables.
+
+ var tfn types.Object
+ stack := 0
+ fn := func(node ast.Node, push bool) bool {
+ if !push {
+ stack--
+ return false
+ }
+ stack++
+ if stack == 1 {
+ tfn = nil
+ }
+ if fn, ok := node.(*ast.FuncDecl); ok {
+ tfn = pass.TypesInfo.ObjectOf(fn.Name)
+ }
+ sel, ok := node.(*ast.SelectorExpr)
+ if !ok {
+ return true
+ }
+
+ obj := pass.TypesInfo.ObjectOf(sel.Sel)
+ if obj.Pkg() == nil {
+ return true
+ }
+ if pass.Pkg == obj.Pkg() || obj.Pkg().Path()+"_test" == pass.Pkg.Path() {
+ // Don't flag stuff in our own package
+ return true
+ }
+ 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(pass, sel)].AlternativeAvailableSince
+ if !IsGoVersion(pass, minVersion) {
+ return true
+ }
+
+ if tfn != nil {
+ if _, ok := deprs.Objects[tfn]; ok {
+ // functions that are deprecated may use deprecated
+ // symbols
+ return true
+ }
+ }
+ ReportNodef(pass, sel, "%s is deprecated: %s", Render(pass, sel), depr.Msg)
+ return true
+ }
+ return true
+ }
+
+ imps := map[string]*types.Package{}
+ for _, imp := range pass.Pkg.Imports() {
+ imps[imp.Path()] = imp
+ }
+ 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 {
+ if call.IsInvoke() {
+ return ""
+ }
+ switch v := call.Value.(type) {
+ case *ssa.Function:
+ fn, ok := v.Object().(*types.Func)
+ if !ok {
+ return ""
+ }
+ return fn.Name()
+ case *ssa.Builtin:
+ return v.Name()
+ }
+ return ""
+}
+
+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 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
+ }
+ tArg, ok := sig.Params().At(0).Type().(*types.Slice)
+ if !ok {
+ continue
+ }
+ if basic, ok := tArg.Elem().(*types.Basic); !ok || basic.Kind() != types.Byte {
+ continue
+ }
+ if basic, ok := sig.Results().At(0).Type().(*types.Basic); !ok || basic.Kind() != types.Int {
+ continue
+ }
+ if named, ok := sig.Results().At(1).Type().(*types.Named); !ok || !IsType(named, "error") {
+ continue
+ }
+
+ for _, block := range ssafn.Blocks {
+ for _, ins := range block.Instrs {
+ switch ins := ins.(type) {
+ case *ssa.Store:
+ addr, ok := ins.Addr.(*ssa.IndexAddr)
+ if !ok {
+ continue
+ }
+ if addr.X != ssafn.Params[1] {
+ continue
+ }
+ 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
+ }
+ if ins.Common().Args[0] != ssafn.Params[1] {
+ continue
+ }
+ pass.Reportf(ins.Pos(), "io.Writer.Write must not modify the provided buffer, not even temporarily")
+ }
+ }
+ }
+ }
+ return nil, nil
+}
+
+func loopedRegexp(name string) CallCheck {
+ return func(call *Call) {
+ if len(extractConsts(call.Args[0].Value.Value)) == 0 {
+ return
+ }
+ if !isInLoop(call.Instr.Block()) {
+ return
+ }
+ call.Invalid(fmt.Sprintf("calling %s in a loop has poor performance, consider using regexp.Compile", name))
+ }
+}
+
+func CheckEmptyBranch(pass *analysis.Pass) (interface{}, error) {
+ for _, ssafn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs {
+ if ssafn.Syntax() == nil {
+ continue
+ }
+ if IsExample(ssafn) {
+ continue
+ }
+ fn := func(node ast.Node) bool {
+ ifstmt, ok := node.(*ast.IfStmt)
+ if !ok {
+ return true
+ }
+ if ifstmt.Else != nil {
+ b, ok := ifstmt.Else.(*ast.BlockStmt)
+ if !ok || len(b.List) != 0 {
+ return true
+ }
+ ReportfFG(pass, ifstmt.Else.Pos(), "empty branch")
+ }
+ if len(ifstmt.Body.List) != 0 {
+ return true
+ }
+ ReportfFG(pass, ifstmt.Pos(), "empty branch")
+ return true
+ }
+ Inspect(ssafn.Syntax(), fn)
+ }
+ return nil, nil
+}
+
+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 {
+ // find []byte -> string conversions
+ conv, ok := ins.(*ssa.Convert)
+ if !ok || conv.Type() != types.Universe.Lookup("string").Type() {
+ continue
+ }
+ if s, ok := conv.X.Type().(*types.Slice); !ok || s.Elem() != types.Universe.Lookup("byte").Type() {
+ continue
+ }
+ refs := conv.Referrers()
+ // need at least two (DebugRef) references: the
+ // conversion and the *ast.Ident
+ if refs == nil || len(*refs) < 2 {
+ continue
+ }
+ ident := false
+ // skip first reference, that's the conversion itself
+ for _, ref := range (*refs)[1:] {
+ switch ref := ref.(type) {
+ case *ssa.DebugRef:
+ if _, ok := ref.Expr.(*ast.Ident); !ok {
+ // the string seems to be used somewhere
+ // unexpected; the default branch should
+ // catch this already, but be safe
+ continue insLoop
+ } else {
+ ident = true
+ }
+ case *ssa.Lookup:
+ default:
+ // the string is used somewhere else than a
+ // map lookup
+ continue insLoop
+ }
+ }
+
+ // the result of the conversion wasn't assigned to an
+ // identifier
+ if !ident {
+ continue
+ }
+ pass.Reportf(conv.Pos(), "m[string(key)] would be more efficient than k := string(key); m[k]")
+ }
+ }
+ }
+ return nil, nil
+}
+
+func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) {
+ return sharedcheck.CheckRangeStringRunes(pass)
+}
+
+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(pass, stmt)
+ rrh := Render(pass, assign.Rhs[i])
+ if rlh == rrh {
+ ReportfFG(pass, assign.Pos(), "self-assignment of %s to %s", rrh, rlh)
+ }
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn)
+ return nil, nil
+}
+
+func buildTagsIdentical(s1, s2 []string) bool {
+ if len(s1) != len(s2) {
+ return false
+ }
+ s1s := make([]string, len(s1))
+ copy(s1s, s1)
+ sort.Strings(s1s)
+ s2s := make([]string, len(s2))
+ copy(s2s, s2)
+ sort.Strings(s2s)
+ for i, s := range s1s {
+ if s != s2s[i] {
+ return false
+ }
+ }
+ return true
+}
+
+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 {
+ if i >= j {
+ continue
+ }
+ if buildTagsIdentical(constraint1, constraint2) {
+ ReportfFG(pass, f.Pos(), "identical build constraints %q and %q",
+ strings.Join(constraint1, " "),
+ strings.Join(constraint2, " "))
+ }
+ }
+ }
+ }
+ return nil, nil
+}
+
+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 pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs {
+ for _, b := range fn.Blocks {
+ for _, ins := range b.Instrs {
+ call, ok := ins.(*ssa.Call)
+ if !ok {
+ continue
+ }
+ switch CallName(call.Common()) {
+ case "regexp.MustCompile", "regexp.Compile", "regexp.Match", "regexp.MatchReader", "regexp.MatchString":
+ default:
+ continue
+ }
+ c, ok := call.Common().Args[0].(*ssa.Const)
+ if !ok {
+ continue
+ }
+ s := constant.StringVal(c.Value)
+ re, err := syntax.Parse(s, 0)
+ if err != nil {
+ continue
+ }
+ if re.Op != syntax.OpLiteral && re.Op != syntax.OpEmptyMatch {
+ continue
+ }
+ pass.Reportf(call.Pos(), "regular expression does not contain any meta characters")
+ }
+ }
+ }
+ return nil, nil
+}
+
+func CheckMissingEnumTypesInDeclaration(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ decl := node.(*ast.GenDecl)
+ if !decl.Lparen.IsValid() {
+ return
+ }
+ if decl.Tok != token.CONST {
+ return
+ }
+
+ groups := GroupSpecs(pass.Fset, decl.Specs)
+ groupLoop:
+ for _, group := range groups {
+ if len(group) < 2 {
+ continue
+ }
+ if group[0].(*ast.ValueSpec).Type == nil {
+ // first constant doesn't have a type
+ continue groupLoop
+ }
+ for i, spec := range group {
+ spec := spec.(*ast.ValueSpec)
+ if len(spec.Names) != 1 || len(spec.Values) != 1 {
+ continue groupLoop
+ }
+ switch v := spec.Values[0].(type) {
+ case *ast.BasicLit:
+ case *ast.UnaryExpr:
+ if _, ok := v.X.(*ast.BasicLit); !ok {
+ continue groupLoop
+ }
+ default:
+ // if it's not a literal it might be typed, such as
+ // time.Microsecond = 1000 * Nanosecond
+ continue groupLoop
+ }
+ if i == 0 {
+ continue
+ }
+ if spec.Type != nil {
+ continue groupLoop
+ }
+ }
+ ReportNodef(pass, group[0], "only the first constant in this group has an explicit type")
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.GenDecl)(nil)}, fn)
+ return nil, nil
+}
+
+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)
+ if !ok {
+ continue
+ }
+ if !IsCallTo(call.Common(), "(*time.Timer).Reset") {
+ continue
+ }
+ refs := call.Referrers()
+ if refs == nil {
+ continue
+ }
+ for _, ref := range FilterDebug(*refs) {
+ ifstmt, ok := ref.(*ssa.If)
+ if !ok {
+ continue
+ }
+
+ found := false
+ for _, succ := range ifstmt.Block().Succs {
+ if len(succ.Preds) != 1 {
+ // Merge point, not a branch in the
+ // syntactical sense.
+
+ // FIXME(dh): this is broken for if
+ // statements a la "if x || y"
+ continue
+ }
+ ssautil.Walk(succ, func(b *ssa.BasicBlock) bool {
+ if !succ.Dominates(b) {
+ // We've reached the end of the branch
+ return false
+ }
+ for _, ins := range b.Instrs {
+ // TODO(dh): we should check that
+ // we're receiving from the channel of
+ // a time.Timer to further reduce
+ // false positives. Not a key
+ // priority, considering the rarity of
+ // Reset and the tiny likeliness of a
+ // false positive
+ if ins, ok := ins.(*ssa.UnOp); ok && ins.Op == token.ARROW && IsType(ins.X.Type(), "<-chan time.Time") {
+ found = true
+ return false
+ }
+ }
+ return true
+ })
+ }
+
+ if found {
+ 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 CheckToLowerToUpperComparison(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ binExpr := node.(*ast.BinaryExpr)
+
+ var negative bool
+ switch binExpr.Op {
+ case token.EQL:
+ negative = false
+ case token.NEQ:
+ negative = true
+ default:
+ return
+ }
+
+ const (
+ lo = "strings.ToLower"
+ up = "strings.ToUpper"
+ )
+
+ var call string
+ if IsCallToAST(pass, binExpr.X, lo) && IsCallToAST(pass, binExpr.Y, lo) {
+ call = lo
+ } else if IsCallToAST(pass, binExpr.X, up) && IsCallToAST(pass, binExpr.Y, up) {
+ call = up
+ } else {
+ return
+ }
+
+ bang := ""
+ if negative {
+ bang = "!"
+ }
+
+ ReportNodef(pass, binExpr, "should use %sstrings.EqualFold(a, b) instead of %s(a) %s %s(b)", bang, call, binExpr.Op, call)
+ }
+
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn)
+ return nil, nil
+}
+
+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)
+ if !ok {
+ return false
+ }
+
+ return types.Implements(V, tIface)
+ }
+
+ subsumesAny := func(Ts, Vs []types.Type) (types.Type, types.Type, bool) {
+ for _, T := range Ts {
+ for _, V := range Vs {
+ if subsumes(T, V) {
+ return T, V, true
+ }
+ }
+ }
+
+ return nil, nil, false
+ }
+
+ fn := func(node ast.Node) {
+ tsStmt := node.(*ast.TypeSwitchStmt)
+
+ type ccAndTypes struct {
+ cc *ast.CaseClause
+ types []types.Type
+ }
+
+ // All asserted types in the order of case clauses.
+ ccs := make([]ccAndTypes, 0, len(tsStmt.Body.List))
+ for _, stmt := range tsStmt.Body.List {
+ cc, _ := stmt.(*ast.CaseClause)
+
+ // Exclude the 'default' case.
+ if len(cc.List) == 0 {
+ continue
+ }
+
+ Ts := make([]types.Type, len(cc.List))
+ for i, expr := range cc.List {
+ Ts[i] = pass.TypesInfo.TypeOf(expr)
+ }
+
+ ccs = append(ccs, ccAndTypes{cc: cc, types: Ts})
+ }
+
+ if len(ccs) <= 1 {
+ // Zero or one case clauses, nothing to check.
+ return
+ }
+
+ // Check if case clauses following cc have types that are subsumed by cc.
+ for i, cc := range ccs[:len(ccs)-1] {
+ for _, next := range ccs[i+1:] {
+ if T, V, yes := subsumesAny(cc.types, next.types); yes {
+ ReportNodef(pass, next.cc, "unreachable case clause: %s will always match before %s", T.String(), V.String())
+ }
+ }
+ }
+ }
+
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.TypeSwitchStmt)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckSingleArgAppend(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ if !IsCallToAST(pass, node, "append") {
+ return
+ }
+ call := node.(*ast.CallExpr)
+ if len(call.Args) != 1 {
+ return
+ }
+ ReportfFG(pass, call.Pos(), "x = append(y) is equivalent to x = y")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.CallExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckStructTags(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ for _, field := range node.(*ast.StructType).Fields.List {
+ if field.Tag == nil {
+ continue
+ }
+ tags, err := parseStructTag(field.Tag.Value[1 : len(field.Tag.Value)-1])
+ if err != nil {
+ ReportNodef(pass, field.Tag, "unparseable struct tag: %s", err)
+ continue
+ }
+ for k, v := range tags {
+ if len(v) > 1 {
+ ReportNodef(pass, field.Tag, "duplicate struct tag %q", k)
+ continue
+ }
+
+ switch k {
+ case "json":
+ checkJSONTag(pass, field, v[0])
+ case "xml":
+ checkXMLTag(pass, field, v[0])
+ }
+ }
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.StructType)(nil)}, fn)
+ return nil, nil
+}
+
+func checkJSONTag(pass *analysis.Pass, field *ast.Field, tag string) {
+ //lint:ignore SA9003 TODO(dh): should we flag empty tags?
+ if len(tag) == 0 {
+ }
+ fields := strings.Split(tag, ",")
+ for _, r := range fields[0] {
+ if !unicode.IsLetter(r) && !unicode.IsDigit(r) && !strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", r) {
+ ReportNodef(pass, field.Tag, "invalid JSON field name %q", fields[0])
+ }
+ }
+ var co, cs, ci int
+ for _, s := range fields[1:] {
+ switch s {
+ case "omitempty":
+ co++
+ case "":
+ // allow stuff like "-,"
+ case "string":
+ cs++
+ // only for string, floating point, integer and bool
+ 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 {
+ 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:
+ ReportNodef(pass, field.Tag, "unknown JSON option %q", s)
+ }
+ }
+ if co > 1 {
+ ReportNodef(pass, field.Tag, `duplicate JSON option "omitempty"`)
+ }
+ if cs > 1 {
+ ReportNodef(pass, field.Tag, `duplicate JSON option "string"`)
+ }
+ if ci > 1 {
+ ReportNodef(pass, field.Tag, `duplicate JSON option "inline"`)
+ }
+}
+
+func checkXMLTag(pass *analysis.Pass, field *ast.Field, tag string) {
+ //lint:ignore SA9003 TODO(dh): should we flag empty tags?
+ if len(tag) == 0 {
+ }
+ fields := strings.Split(tag, ",")
+ counts := map[string]int{}
+ var exclusives []string
+ for _, s := range fields[1:] {
+ switch s {
+ case "attr", "chardata", "cdata", "innerxml", "comment":
+ counts[s]++
+ if counts[s] == 1 {
+ exclusives = append(exclusives, s)
+ }
+ case "omitempty", "any":
+ counts[s]++
+ case "":
+ default:
+ ReportNodef(pass, field.Tag, "unknown XML option %q", s)
+ }
+ }
+ for k, v := range counts {
+ if v > 1 {
+ ReportNodef(pass, field.Tag, "duplicate XML option %q", k)
+ }
+ }
+ if len(exclusives) > 1 {
+ 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
new file mode 100644
index 000000000..0152cac1a
--- /dev/null
+++ b/vendor/honnef.co/go/tools/staticcheck/rules.go
@@ -0,0 +1,321 @@
+package staticcheck
+
+import (
+ "fmt"
+ "go/constant"
+ "go/types"
+ "net"
+ "net/url"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+ "unicode/utf8"
+
+ "golang.org/x/tools/go/analysis"
+ . "honnef.co/go/tools/lint/lintdsl"
+ "honnef.co/go/tools/ssa"
+ "honnef.co/go/tools/staticcheck/vrp"
+)
+
+const (
+ MsgInvalidHostPort = "invalid port or service name in host:port pair"
+ MsgInvalidUTF8 = "argument is not a valid UTF-8 encoded string"
+ MsgNonUniqueCutset = "cutset contains duplicate characters"
+)
+
+type Call struct {
+ Pass *analysis.Pass
+ Instr ssa.CallInstruction
+ Args []*Argument
+
+ Parent *ssa.Function
+
+ invalids []string
+}
+
+func (c *Call) Invalid(msg string) {
+ c.invalids = append(c.invalids, msg)
+}
+
+type Argument struct {
+ Value Value
+ invalids []string
+}
+
+func (arg *Argument) Invalid(msg string) {
+ arg.invalids = append(arg.invalids, msg)
+}
+
+type Value struct {
+ Value ssa.Value
+ Range vrp.Range
+}
+
+type CallCheck func(call *Call)
+
+func extractConsts(v ssa.Value) []*ssa.Const {
+ switch v := v.(type) {
+ case *ssa.Const:
+ return []*ssa.Const{v}
+ case *ssa.MakeInterface:
+ return extractConsts(v.X)
+ default:
+ return nil
+ }
+}
+
+func ValidateRegexp(v Value) error {
+ for _, c := range extractConsts(v.Value) {
+ if c.Value == nil {
+ continue
+ }
+ if c.Value.Kind() != constant.String {
+ continue
+ }
+ s := constant.StringVal(c.Value)
+ if _, err := regexp.Compile(s); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func ValidateTimeLayout(v Value) error {
+ for _, c := range extractConsts(v.Value) {
+ if c.Value == nil {
+ continue
+ }
+ if c.Value.Kind() != constant.String {
+ continue
+ }
+ s := constant.StringVal(c.Value)
+ s = strings.Replace(s, "_", " ", -1)
+ s = strings.Replace(s, "Z", "-", -1)
+ _, err := time.Parse(s, s)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func ValidateURL(v Value) error {
+ for _, c := range extractConsts(v.Value) {
+ if c.Value == nil {
+ continue
+ }
+ if c.Value.Kind() != constant.String {
+ continue
+ }
+ s := constant.StringVal(c.Value)
+ _, err := url.Parse(s)
+ if err != nil {
+ return fmt.Errorf("%q is not a valid URL: %s", s, err)
+ }
+ }
+ return nil
+}
+
+func IntValue(v Value, z vrp.Z) bool {
+ r, ok := v.Range.(vrp.IntInterval)
+ if !ok || !r.IsKnown() {
+ return false
+ }
+ if r.Lower != r.Upper {
+ return false
+ }
+ if r.Lower.Cmp(z) == 0 {
+ return true
+ }
+ return false
+}
+
+func InvalidUTF8(v Value) bool {
+ for _, c := range extractConsts(v.Value) {
+ if c.Value == nil {
+ continue
+ }
+ if c.Value.Kind() != constant.String {
+ continue
+ }
+ s := constant.StringVal(c.Value)
+ if !utf8.ValidString(s) {
+ return true
+ }
+ }
+ return false
+}
+
+func UnbufferedChannel(v Value) bool {
+ r, ok := v.Range.(vrp.ChannelInterval)
+ if !ok || !r.IsKnown() {
+ return false
+ }
+ if r.Size.Lower.Cmp(vrp.NewZ(0)) == 0 &&
+ r.Size.Upper.Cmp(vrp.NewZ(0)) == 0 {
+ return true
+ }
+ return false
+}
+
+func Pointer(v Value) bool {
+ switch v.Value.Type().Underlying().(type) {
+ case *types.Pointer, *types.Interface:
+ return true
+ }
+ return false
+}
+
+func ConvertedFromInt(v Value) bool {
+ conv, ok := v.Value.(*ssa.Convert)
+ if !ok {
+ return false
+ }
+ b, ok := conv.X.Type().Underlying().(*types.Basic)
+ if !ok {
+ return false
+ }
+ if (b.Info() & types.IsInteger) == 0 {
+ return false
+ }
+ return true
+}
+
+func validEncodingBinaryType(pass *analysis.Pass, typ types.Type) bool {
+ typ = typ.Underlying()
+ switch typ := typ.(type) {
+ case *types.Basic:
+ switch typ.Kind() {
+ case types.Uint8, types.Uint16, types.Uint32, types.Uint64,
+ types.Int8, types.Int16, types.Int32, types.Int64,
+ types.Float32, types.Float64, types.Complex64, types.Complex128, types.Invalid:
+ return true
+ case types.Bool:
+ return IsGoVersion(pass, 8)
+ }
+ return false
+ case *types.Struct:
+ n := typ.NumFields()
+ for i := 0; i < n; i++ {
+ if !validEncodingBinaryType(pass, typ.Field(i).Type()) {
+ return false
+ }
+ }
+ return true
+ case *types.Array:
+ return validEncodingBinaryType(pass, typ.Elem())
+ case *types.Interface:
+ // we can't determine if it's a valid type or not
+ return true
+ }
+ return false
+}
+
+func CanBinaryMarshal(pass *analysis.Pass, v Value) bool {
+ typ := v.Value.Type().Underlying()
+ if ttyp, ok := typ.(*types.Pointer); ok {
+ typ = ttyp.Elem().Underlying()
+ }
+ if ttyp, ok := typ.(interface {
+ Elem() types.Type
+ }); ok {
+ if _, ok := ttyp.(*types.Pointer); !ok {
+ typ = ttyp.Elem()
+ }
+ }
+
+ return validEncodingBinaryType(pass, typ)
+}
+
+func RepeatZeroTimes(name string, arg int) CallCheck {
+ return func(call *Call) {
+ arg := call.Args[arg]
+ if IntValue(arg.Value, vrp.NewZ(0)) {
+ arg.Invalid(fmt.Sprintf("calling %s with n == 0 will return no results, did you mean -1?", name))
+ }
+ }
+}
+
+func validateServiceName(s string) bool {
+ if len(s) < 1 || len(s) > 15 {
+ return false
+ }
+ if s[0] == '-' || s[len(s)-1] == '-' {
+ return false
+ }
+ if strings.Contains(s, "--") {
+ return false
+ }
+ hasLetter := false
+ for _, r := range s {
+ if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') {
+ hasLetter = true
+ continue
+ }
+ if r >= '0' && r <= '9' {
+ continue
+ }
+ return false
+ }
+ return hasLetter
+}
+
+func validatePort(s string) bool {
+ n, err := strconv.ParseInt(s, 10, 64)
+ if err != nil {
+ return validateServiceName(s)
+ }
+ return n >= 0 && n <= 65535
+}
+
+func ValidHostPort(v Value) bool {
+ for _, k := range extractConsts(v.Value) {
+ if k.Value == nil {
+ continue
+ }
+ if k.Value.Kind() != constant.String {
+ continue
+ }
+ s := constant.StringVal(k.Value)
+ _, port, err := net.SplitHostPort(s)
+ if err != nil {
+ return false
+ }
+ // TODO(dh): check hostname
+ if !validatePort(port) {
+ return false
+ }
+ }
+ return true
+}
+
+// ConvertedFrom reports whether value v was converted from type typ.
+func ConvertedFrom(v Value, typ string) bool {
+ change, ok := v.Value.(*ssa.ChangeType)
+ return ok && IsType(change.X.Type(), typ)
+}
+
+func UniqueStringCutset(v Value) bool {
+ for _, c := range extractConsts(v.Value) {
+ if c.Value == nil {
+ continue
+ }
+ if c.Value.Kind() != constant.String {
+ continue
+ }
+ s := constant.StringVal(c.Value)
+ rs := runeSlice(s)
+ if len(rs) < 2 {
+ continue
+ }
+ sort.Sort(rs)
+ for i, r := range rs[1:] {
+ if rs[i] == r {
+ return false
+ }
+ }
+ }
+ return true
+}
diff --git a/vendor/honnef.co/go/tools/staticcheck/structtag.go b/vendor/honnef.co/go/tools/staticcheck/structtag.go
new file mode 100644
index 000000000..38830a22c
--- /dev/null
+++ b/vendor/honnef.co/go/tools/staticcheck/structtag.go
@@ -0,0 +1,58 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Copyright 2019 Dominik Honnef. All rights reserved.
+
+package staticcheck
+
+import "strconv"
+
+func parseStructTag(tag string) (map[string][]string, error) {
+ // FIXME(dh): detect missing closing quote
+ out := map[string][]string{}
+
+ for tag != "" {
+ // Skip leading space.
+ i := 0
+ for i < len(tag) && tag[i] == ' ' {
+ i++
+ }
+ tag = tag[i:]
+ if tag == "" {
+ break
+ }
+
+ // Scan to colon. A space, a quote or a control character is a syntax error.
+ // Strictly speaking, control chars include the range [0x7f, 0x9f], not just
+ // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
+ // as it is simpler to inspect the tag's bytes than the tag's runes.
+ i = 0
+ for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
+ i++
+ }
+ if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
+ break
+ }
+ name := string(tag[:i])
+ tag = tag[i+1:]
+
+ // Scan quoted string to find value.
+ i = 1
+ for i < len(tag) && tag[i] != '"' {
+ if tag[i] == '\\' {
+ i++
+ }
+ i++
+ }
+ if i >= len(tag) {
+ break
+ }
+ qvalue := string(tag[:i+1])
+ tag = tag[i+1:]
+
+ value, err := strconv.Unquote(qvalue)
+ if err != nil {
+ return nil, err
+ }
+ out[name] = append(out[name], value)
+ }
+ return out, nil
+}
diff --git a/vendor/honnef.co/go/tools/staticcheck/vrp/channel.go b/vendor/honnef.co/go/tools/staticcheck/vrp/channel.go
new file mode 100644
index 000000000..0ef73787b
--- /dev/null
+++ b/vendor/honnef.co/go/tools/staticcheck/vrp/channel.go
@@ -0,0 +1,73 @@
+package vrp
+
+import (
+ "fmt"
+
+ "honnef.co/go/tools/ssa"
+)
+
+type ChannelInterval struct {
+ Size IntInterval
+}
+
+func (c ChannelInterval) Union(other Range) Range {
+ i, ok := other.(ChannelInterval)
+ if !ok {
+ i = ChannelInterval{EmptyIntInterval}
+ }
+ if c.Size.Empty() || !c.Size.IsKnown() {
+ return i
+ }
+ if i.Size.Empty() || !i.Size.IsKnown() {
+ return c
+ }
+ return ChannelInterval{
+ Size: c.Size.Union(i.Size).(IntInterval),
+ }
+}
+
+func (c ChannelInterval) String() string {
+ return c.Size.String()
+}
+
+func (c ChannelInterval) IsKnown() bool {
+ return c.Size.IsKnown()
+}
+
+type MakeChannelConstraint struct {
+ aConstraint
+ Buffer ssa.Value
+}
+type ChannelChangeTypeConstraint struct {
+ aConstraint
+ X ssa.Value
+}
+
+func NewMakeChannelConstraint(buffer, y ssa.Value) Constraint {
+ return &MakeChannelConstraint{NewConstraint(y), buffer}
+}
+func NewChannelChangeTypeConstraint(x, y ssa.Value) Constraint {
+ return &ChannelChangeTypeConstraint{NewConstraint(y), x}
+}
+
+func (c *MakeChannelConstraint) Operands() []ssa.Value { return []ssa.Value{c.Buffer} }
+func (c *ChannelChangeTypeConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
+
+func (c *MakeChannelConstraint) String() string {
+ return fmt.Sprintf("%s = make(chan, %s)", c.Y().Name(), c.Buffer.Name())
+}
+func (c *ChannelChangeTypeConstraint) String() string {
+ return fmt.Sprintf("%s = changetype(%s)", c.Y().Name(), c.X.Name())
+}
+
+func (c *MakeChannelConstraint) Eval(g *Graph) Range {
+ i, ok := g.Range(c.Buffer).(IntInterval)
+ if !ok {
+ return ChannelInterval{NewIntInterval(NewZ(0), PInfinity)}
+ }
+ if i.Lower.Sign() == -1 {
+ i.Lower = NewZ(0)
+ }
+ return ChannelInterval{i}
+}
+func (c *ChannelChangeTypeConstraint) Eval(g *Graph) Range { return g.Range(c.X) }
diff --git a/vendor/honnef.co/go/tools/staticcheck/vrp/int.go b/vendor/honnef.co/go/tools/staticcheck/vrp/int.go
new file mode 100644
index 000000000..926bb7af3
--- /dev/null
+++ b/vendor/honnef.co/go/tools/staticcheck/vrp/int.go
@@ -0,0 +1,476 @@
+package vrp
+
+import (
+ "fmt"
+ "go/token"
+ "go/types"
+ "math/big"
+
+ "honnef.co/go/tools/ssa"
+)
+
+type Zs []Z
+
+func (zs Zs) Len() int {
+ return len(zs)
+}
+
+func (zs Zs) Less(i int, j int) bool {
+ return zs[i].Cmp(zs[j]) == -1
+}
+
+func (zs Zs) Swap(i int, j int) {
+ zs[i], zs[j] = zs[j], zs[i]
+}
+
+type Z struct {
+ infinity int8
+ integer *big.Int
+}
+
+func NewZ(n int64) Z {
+ return NewBigZ(big.NewInt(n))
+}
+
+func NewBigZ(n *big.Int) Z {
+ return Z{integer: n}
+}
+
+func (z1 Z) Infinite() bool {
+ return z1.infinity != 0
+}
+
+func (z1 Z) Add(z2 Z) Z {
+ if z2.Sign() == -1 {
+ return z1.Sub(z2.Negate())
+ }
+ if z1 == NInfinity {
+ return NInfinity
+ }
+ if z1 == PInfinity {
+ return PInfinity
+ }
+ if z2 == PInfinity {
+ return PInfinity
+ }
+
+ if !z1.Infinite() && !z2.Infinite() {
+ n := &big.Int{}
+ n.Add(z1.integer, z2.integer)
+ return NewBigZ(n)
+ }
+
+ panic(fmt.Sprintf("%s + %s is not defined", z1, z2))
+}
+
+func (z1 Z) Sub(z2 Z) Z {
+ if z2.Sign() == -1 {
+ return z1.Add(z2.Negate())
+ }
+ if !z1.Infinite() && !z2.Infinite() {
+ n := &big.Int{}
+ n.Sub(z1.integer, z2.integer)
+ return NewBigZ(n)
+ }
+
+ if z1 != PInfinity && z2 == PInfinity {
+ return NInfinity
+ }
+ if z1.Infinite() && !z2.Infinite() {
+ return Z{infinity: z1.infinity}
+ }
+ if z1 == PInfinity && z2 == PInfinity {
+ return PInfinity
+ }
+ panic(fmt.Sprintf("%s - %s is not defined", z1, z2))
+}
+
+func (z1 Z) Mul(z2 Z) Z {
+ if (z1.integer != nil && z1.integer.Sign() == 0) ||
+ (z2.integer != nil && z2.integer.Sign() == 0) {
+ return NewBigZ(&big.Int{})
+ }
+
+ if z1.infinity != 0 || z2.infinity != 0 {
+ return Z{infinity: int8(z1.Sign() * z2.Sign())}
+ }
+
+ n := &big.Int{}
+ n.Mul(z1.integer, z2.integer)
+ return NewBigZ(n)
+}
+
+func (z1 Z) Negate() Z {
+ if z1.infinity == 1 {
+ return NInfinity
+ }
+ if z1.infinity == -1 {
+ return PInfinity
+ }
+ n := &big.Int{}
+ n.Neg(z1.integer)
+ return NewBigZ(n)
+}
+
+func (z1 Z) Sign() int {
+ if z1.infinity != 0 {
+ return int(z1.infinity)
+ }
+ return z1.integer.Sign()
+}
+
+func (z1 Z) String() string {
+ if z1 == NInfinity {
+ return "-∞"
+ }
+ if z1 == PInfinity {
+ return "∞"
+ }
+ return fmt.Sprintf("%d", z1.integer)
+}
+
+func (z1 Z) Cmp(z2 Z) int {
+ if z1.infinity == z2.infinity && z1.infinity != 0 {
+ return 0
+ }
+ if z1 == PInfinity {
+ return 1
+ }
+ if z1 == NInfinity {
+ return -1
+ }
+ if z2 == NInfinity {
+ return 1
+ }
+ if z2 == PInfinity {
+ return -1
+ }
+ return z1.integer.Cmp(z2.integer)
+}
+
+func MaxZ(zs ...Z) Z {
+ if len(zs) == 0 {
+ panic("Max called with no arguments")
+ }
+ if len(zs) == 1 {
+ return zs[0]
+ }
+ ret := zs[0]
+ for _, z := range zs[1:] {
+ if z.Cmp(ret) == 1 {
+ ret = z
+ }
+ }
+ return ret
+}
+
+func MinZ(zs ...Z) Z {
+ if len(zs) == 0 {
+ panic("Min called with no arguments")
+ }
+ if len(zs) == 1 {
+ return zs[0]
+ }
+ ret := zs[0]
+ for _, z := range zs[1:] {
+ if z.Cmp(ret) == -1 {
+ ret = z
+ }
+ }
+ return ret
+}
+
+var NInfinity = Z{infinity: -1}
+var PInfinity = Z{infinity: 1}
+var EmptyIntInterval = IntInterval{true, PInfinity, NInfinity}
+
+func InfinityFor(v ssa.Value) IntInterval {
+ if b, ok := v.Type().Underlying().(*types.Basic); ok {
+ if (b.Info() & types.IsUnsigned) != 0 {
+ return NewIntInterval(NewZ(0), PInfinity)
+ }
+ }
+ return NewIntInterval(NInfinity, PInfinity)
+}
+
+type IntInterval struct {
+ known bool
+ Lower Z
+ Upper Z
+}
+
+func NewIntInterval(l, u Z) IntInterval {
+ if u.Cmp(l) == -1 {
+ return EmptyIntInterval
+ }
+ return IntInterval{known: true, Lower: l, Upper: u}
+}
+
+func (i IntInterval) IsKnown() bool {
+ return i.known
+}
+
+func (i IntInterval) Empty() bool {
+ return i.Lower == PInfinity && i.Upper == NInfinity
+}
+
+func (i IntInterval) IsMaxRange() bool {
+ return i.Lower == NInfinity && i.Upper == PInfinity
+}
+
+func (i1 IntInterval) Intersection(i2 IntInterval) IntInterval {
+ if !i1.IsKnown() {
+ return i2
+ }
+ if !i2.IsKnown() {
+ return i1
+ }
+ if i1.Empty() || i2.Empty() {
+ return EmptyIntInterval
+ }
+ i3 := NewIntInterval(MaxZ(i1.Lower, i2.Lower), MinZ(i1.Upper, i2.Upper))
+ if i3.Lower.Cmp(i3.Upper) == 1 {
+ return EmptyIntInterval
+ }
+ return i3
+}
+
+func (i1 IntInterval) Union(other Range) Range {
+ i2, ok := other.(IntInterval)
+ if !ok {
+ i2 = EmptyIntInterval
+ }
+ if i1.Empty() || !i1.IsKnown() {
+ return i2
+ }
+ if i2.Empty() || !i2.IsKnown() {
+ return i1
+ }
+ return NewIntInterval(MinZ(i1.Lower, i2.Lower), MaxZ(i1.Upper, i2.Upper))
+}
+
+func (i1 IntInterval) Add(i2 IntInterval) IntInterval {
+ if i1.Empty() || i2.Empty() {
+ return EmptyIntInterval
+ }
+ l1, u1, l2, u2 := i1.Lower, i1.Upper, i2.Lower, i2.Upper
+ return NewIntInterval(l1.Add(l2), u1.Add(u2))
+}
+
+func (i1 IntInterval) Sub(i2 IntInterval) IntInterval {
+ if i1.Empty() || i2.Empty() {
+ return EmptyIntInterval
+ }
+ l1, u1, l2, u2 := i1.Lower, i1.Upper, i2.Lower, i2.Upper
+ return NewIntInterval(l1.Sub(u2), u1.Sub(l2))
+}
+
+func (i1 IntInterval) Mul(i2 IntInterval) IntInterval {
+ if i1.Empty() || i2.Empty() {
+ return EmptyIntInterval
+ }
+ x1, x2 := i1.Lower, i1.Upper
+ y1, y2 := i2.Lower, i2.Upper
+ return NewIntInterval(
+ MinZ(x1.Mul(y1), x1.Mul(y2), x2.Mul(y1), x2.Mul(y2)),
+ MaxZ(x1.Mul(y1), x1.Mul(y2), x2.Mul(y1), x2.Mul(y2)),
+ )
+}
+
+func (i1 IntInterval) String() string {
+ if !i1.IsKnown() {
+ return "[⊥, ⊥]"
+ }
+ if i1.Empty() {
+ return "{}"
+ }
+ return fmt.Sprintf("[%s, %s]", i1.Lower, i1.Upper)
+}
+
+type IntArithmeticConstraint struct {
+ aConstraint
+ A ssa.Value
+ B ssa.Value
+ Op token.Token
+ Fn func(IntInterval, IntInterval) IntInterval
+}
+
+type IntAddConstraint struct{ *IntArithmeticConstraint }
+type IntSubConstraint struct{ *IntArithmeticConstraint }
+type IntMulConstraint struct{ *IntArithmeticConstraint }
+
+type IntConversionConstraint struct {
+ aConstraint
+ X ssa.Value
+}
+
+type IntIntersectionConstraint struct {
+ aConstraint
+ ranges Ranges
+ A ssa.Value
+ B ssa.Value
+ Op token.Token
+ I IntInterval
+ resolved bool
+}
+
+type IntIntervalConstraint struct {
+ aConstraint
+ I IntInterval
+}
+
+func NewIntArithmeticConstraint(a, b, y ssa.Value, op token.Token, fn func(IntInterval, IntInterval) IntInterval) *IntArithmeticConstraint {
+ return &IntArithmeticConstraint{NewConstraint(y), a, b, op, fn}
+}
+func NewIntAddConstraint(a, b, y ssa.Value) Constraint {
+ return &IntAddConstraint{NewIntArithmeticConstraint(a, b, y, token.ADD, IntInterval.Add)}
+}
+func NewIntSubConstraint(a, b, y ssa.Value) Constraint {
+ return &IntSubConstraint{NewIntArithmeticConstraint(a, b, y, token.SUB, IntInterval.Sub)}
+}
+func NewIntMulConstraint(a, b, y ssa.Value) Constraint {
+ return &IntMulConstraint{NewIntArithmeticConstraint(a, b, y, token.MUL, IntInterval.Mul)}
+}
+func NewIntConversionConstraint(x, y ssa.Value) Constraint {
+ return &IntConversionConstraint{NewConstraint(y), x}
+}
+func NewIntIntersectionConstraint(a, b ssa.Value, op token.Token, ranges Ranges, y ssa.Value) Constraint {
+ return &IntIntersectionConstraint{
+ aConstraint: NewConstraint(y),
+ ranges: ranges,
+ A: a,
+ B: b,
+ Op: op,
+ }
+}
+func NewIntIntervalConstraint(i IntInterval, y ssa.Value) Constraint {
+ return &IntIntervalConstraint{NewConstraint(y), i}
+}
+
+func (c *IntArithmeticConstraint) Operands() []ssa.Value { return []ssa.Value{c.A, c.B} }
+func (c *IntConversionConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
+func (c *IntIntersectionConstraint) Operands() []ssa.Value { return []ssa.Value{c.A} }
+func (s *IntIntervalConstraint) Operands() []ssa.Value { return nil }
+
+func (c *IntArithmeticConstraint) String() string {
+ return fmt.Sprintf("%s = %s %s %s", c.Y().Name(), c.A.Name(), c.Op, c.B.Name())
+}
+func (c *IntConversionConstraint) String() string {
+ return fmt.Sprintf("%s = %s(%s)", c.Y().Name(), c.Y().Type(), c.X.Name())
+}
+func (c *IntIntersectionConstraint) String() string {
+ return fmt.Sprintf("%s = %s %s %s (%t branch)", c.Y().Name(), c.A.Name(), c.Op, c.B.Name(), c.Y().(*ssa.Sigma).Branch)
+}
+func (c *IntIntervalConstraint) String() string { return fmt.Sprintf("%s = %s", c.Y().Name(), c.I) }
+
+func (c *IntArithmeticConstraint) Eval(g *Graph) Range {
+ i1, i2 := g.Range(c.A).(IntInterval), g.Range(c.B).(IntInterval)
+ if !i1.IsKnown() || !i2.IsKnown() {
+ return IntInterval{}
+ }
+ return c.Fn(i1, i2)
+}
+func (c *IntConversionConstraint) Eval(g *Graph) Range {
+ s := &types.StdSizes{
+ // XXX is it okay to assume the largest word size, or do we
+ // need to be platform specific?
+ WordSize: 8,
+ MaxAlign: 1,
+ }
+ fromI := g.Range(c.X).(IntInterval)
+ toI := g.Range(c.Y()).(IntInterval)
+ fromT := c.X.Type().Underlying().(*types.Basic)
+ toT := c.Y().Type().Underlying().(*types.Basic)
+ fromB := s.Sizeof(c.X.Type())
+ toB := s.Sizeof(c.Y().Type())
+
+ if !fromI.IsKnown() {
+ return toI
+ }
+ if !toI.IsKnown() {
+ return fromI
+ }
+
+ // uint -> sint/uint, M > N: [max(0, l1), min(2**N-1, u2)]
+ if (fromT.Info()&types.IsUnsigned != 0) &&
+ toB > fromB {
+
+ n := big.NewInt(1)
+ n.Lsh(n, uint(fromB*8))
+ n.Sub(n, big.NewInt(1))
+ return NewIntInterval(
+ MaxZ(NewZ(0), fromI.Lower),
+ MinZ(NewBigZ(n), toI.Upper),
+ )
+ }
+
+ // sint -> sint, M > N; [max(-∞, l1), min(2**N-1, u2)]
+ if (fromT.Info()&types.IsUnsigned == 0) &&
+ (toT.Info()&types.IsUnsigned == 0) &&
+ toB > fromB {
+
+ n := big.NewInt(1)
+ n.Lsh(n, uint(fromB*8))
+ n.Sub(n, big.NewInt(1))
+ return NewIntInterval(
+ MaxZ(NInfinity, fromI.Lower),
+ MinZ(NewBigZ(n), toI.Upper),
+ )
+ }
+
+ return fromI
+}
+func (c *IntIntersectionConstraint) Eval(g *Graph) Range {
+ xi := g.Range(c.A).(IntInterval)
+ if !xi.IsKnown() {
+ return c.I
+ }
+ return xi.Intersection(c.I)
+}
+func (c *IntIntervalConstraint) Eval(*Graph) Range { return c.I }
+
+func (c *IntIntersectionConstraint) Futures() []ssa.Value {
+ return []ssa.Value{c.B}
+}
+
+func (c *IntIntersectionConstraint) Resolve() {
+ r, ok := c.ranges[c.B].(IntInterval)
+ if !ok {
+ c.I = InfinityFor(c.Y())
+ return
+ }
+
+ switch c.Op {
+ case token.EQL:
+ c.I = r
+ case token.GTR:
+ c.I = NewIntInterval(r.Lower.Add(NewZ(1)), PInfinity)
+ case token.GEQ:
+ c.I = NewIntInterval(r.Lower, PInfinity)
+ case token.LSS:
+ // TODO(dh): do we need 0 instead of NInfinity for uints?
+ c.I = NewIntInterval(NInfinity, r.Upper.Sub(NewZ(1)))
+ case token.LEQ:
+ c.I = NewIntInterval(NInfinity, r.Upper)
+ case token.NEQ:
+ c.I = InfinityFor(c.Y())
+ default:
+ panic("unsupported op " + c.Op.String())
+ }
+}
+
+func (c *IntIntersectionConstraint) IsKnown() bool {
+ return c.I.IsKnown()
+}
+
+func (c *IntIntersectionConstraint) MarkUnresolved() {
+ c.resolved = false
+}
+
+func (c *IntIntersectionConstraint) MarkResolved() {
+ c.resolved = true
+}
+
+func (c *IntIntersectionConstraint) IsResolved() bool {
+ return c.resolved
+}
diff --git a/vendor/honnef.co/go/tools/staticcheck/vrp/slice.go b/vendor/honnef.co/go/tools/staticcheck/vrp/slice.go
new file mode 100644
index 000000000..40658dd8d
--- /dev/null
+++ b/vendor/honnef.co/go/tools/staticcheck/vrp/slice.go
@@ -0,0 +1,273 @@
+package vrp
+
+// TODO(dh): most of the constraints have implementations identical to
+// that of strings. Consider reusing them.
+
+import (
+ "fmt"
+ "go/types"
+
+ "honnef.co/go/tools/ssa"
+)
+
+type SliceInterval struct {
+ Length IntInterval
+}
+
+func (s SliceInterval) Union(other Range) Range {
+ i, ok := other.(SliceInterval)
+ if !ok {
+ i = SliceInterval{EmptyIntInterval}
+ }
+ if s.Length.Empty() || !s.Length.IsKnown() {
+ return i
+ }
+ if i.Length.Empty() || !i.Length.IsKnown() {
+ return s
+ }
+ return SliceInterval{
+ Length: s.Length.Union(i.Length).(IntInterval),
+ }
+}
+func (s SliceInterval) String() string { return s.Length.String() }
+func (s SliceInterval) IsKnown() bool { return s.Length.IsKnown() }
+
+type SliceAppendConstraint struct {
+ aConstraint
+ A ssa.Value
+ B ssa.Value
+}
+
+type SliceSliceConstraint struct {
+ aConstraint
+ X ssa.Value
+ Lower ssa.Value
+ Upper ssa.Value
+}
+
+type ArraySliceConstraint struct {
+ aConstraint
+ X ssa.Value
+ Lower ssa.Value
+ Upper ssa.Value
+}
+
+type SliceIntersectionConstraint struct {
+ aConstraint
+ X ssa.Value
+ I IntInterval
+}
+
+type SliceLengthConstraint struct {
+ aConstraint
+ X ssa.Value
+}
+
+type MakeSliceConstraint struct {
+ aConstraint
+ Size ssa.Value
+}
+
+type SliceIntervalConstraint struct {
+ aConstraint
+ I IntInterval
+}
+
+func NewSliceAppendConstraint(a, b, y ssa.Value) Constraint {
+ return &SliceAppendConstraint{NewConstraint(y), a, b}
+}
+func NewSliceSliceConstraint(x, lower, upper, y ssa.Value) Constraint {
+ return &SliceSliceConstraint{NewConstraint(y), x, lower, upper}
+}
+func NewArraySliceConstraint(x, lower, upper, y ssa.Value) Constraint {
+ return &ArraySliceConstraint{NewConstraint(y), x, lower, upper}
+}
+func NewSliceIntersectionConstraint(x ssa.Value, i IntInterval, y ssa.Value) Constraint {
+ return &SliceIntersectionConstraint{NewConstraint(y), x, i}
+}
+func NewSliceLengthConstraint(x, y ssa.Value) Constraint {
+ return &SliceLengthConstraint{NewConstraint(y), x}
+}
+func NewMakeSliceConstraint(size, y ssa.Value) Constraint {
+ return &MakeSliceConstraint{NewConstraint(y), size}
+}
+func NewSliceIntervalConstraint(i IntInterval, y ssa.Value) Constraint {
+ return &SliceIntervalConstraint{NewConstraint(y), i}
+}
+
+func (c *SliceAppendConstraint) Operands() []ssa.Value { return []ssa.Value{c.A, c.B} }
+func (c *SliceSliceConstraint) Operands() []ssa.Value {
+ ops := []ssa.Value{c.X}
+ if c.Lower != nil {
+ ops = append(ops, c.Lower)
+ }
+ if c.Upper != nil {
+ ops = append(ops, c.Upper)
+ }
+ return ops
+}
+func (c *ArraySliceConstraint) Operands() []ssa.Value {
+ ops := []ssa.Value{c.X}
+ if c.Lower != nil {
+ ops = append(ops, c.Lower)
+ }
+ if c.Upper != nil {
+ ops = append(ops, c.Upper)
+ }
+ return ops
+}
+func (c *SliceIntersectionConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
+func (c *SliceLengthConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
+func (c *MakeSliceConstraint) Operands() []ssa.Value { return []ssa.Value{c.Size} }
+func (s *SliceIntervalConstraint) Operands() []ssa.Value { return nil }
+
+func (c *SliceAppendConstraint) String() string {
+ return fmt.Sprintf("%s = append(%s, %s)", c.Y().Name(), c.A.Name(), c.B.Name())
+}
+func (c *SliceSliceConstraint) String() string {
+ var lname, uname string
+ if c.Lower != nil {
+ lname = c.Lower.Name()
+ }
+ if c.Upper != nil {
+ uname = c.Upper.Name()
+ }
+ return fmt.Sprintf("%s[%s:%s]", c.X.Name(), lname, uname)
+}
+func (c *ArraySliceConstraint) String() string {
+ var lname, uname string
+ if c.Lower != nil {
+ lname = c.Lower.Name()
+ }
+ if c.Upper != nil {
+ uname = c.Upper.Name()
+ }
+ return fmt.Sprintf("%s[%s:%s]", c.X.Name(), lname, uname)
+}
+func (c *SliceIntersectionConstraint) String() string {
+ return fmt.Sprintf("%s = %s.%t ⊓ %s", c.Y().Name(), c.X.Name(), c.Y().(*ssa.Sigma).Branch, c.I)
+}
+func (c *SliceLengthConstraint) String() string {
+ return fmt.Sprintf("%s = len(%s)", c.Y().Name(), c.X.Name())
+}
+func (c *MakeSliceConstraint) String() string {
+ return fmt.Sprintf("%s = make(slice, %s)", c.Y().Name(), c.Size.Name())
+}
+func (c *SliceIntervalConstraint) String() string { return fmt.Sprintf("%s = %s", c.Y().Name(), c.I) }
+
+func (c *SliceAppendConstraint) Eval(g *Graph) Range {
+ l1 := g.Range(c.A).(SliceInterval).Length
+ var l2 IntInterval
+ switch r := g.Range(c.B).(type) {
+ case SliceInterval:
+ l2 = r.Length
+ case StringInterval:
+ l2 = r.Length
+ default:
+ return SliceInterval{}
+ }
+ if !l1.IsKnown() || !l2.IsKnown() {
+ return SliceInterval{}
+ }
+ return SliceInterval{
+ Length: l1.Add(l2),
+ }
+}
+func (c *SliceSliceConstraint) Eval(g *Graph) Range {
+ lr := NewIntInterval(NewZ(0), NewZ(0))
+ if c.Lower != nil {
+ lr = g.Range(c.Lower).(IntInterval)
+ }
+ ur := g.Range(c.X).(SliceInterval).Length
+ if c.Upper != nil {
+ ur = g.Range(c.Upper).(IntInterval)
+ }
+ if !lr.IsKnown() || !ur.IsKnown() {
+ return SliceInterval{}
+ }
+
+ ls := []Z{
+ ur.Lower.Sub(lr.Lower),
+ ur.Upper.Sub(lr.Lower),
+ ur.Lower.Sub(lr.Upper),
+ ur.Upper.Sub(lr.Upper),
+ }
+ // TODO(dh): if we don't truncate lengths to 0 we might be able to
+ // easily detect slices with high < low. we'd need to treat -∞
+ // specially, though.
+ for i, l := range ls {
+ if l.Sign() == -1 {
+ ls[i] = NewZ(0)
+ }
+ }
+
+ return SliceInterval{
+ Length: NewIntInterval(MinZ(ls...), MaxZ(ls...)),
+ }
+}
+func (c *ArraySliceConstraint) Eval(g *Graph) Range {
+ lr := NewIntInterval(NewZ(0), NewZ(0))
+ if c.Lower != nil {
+ lr = g.Range(c.Lower).(IntInterval)
+ }
+ var l int64
+ switch typ := c.X.Type().(type) {
+ case *types.Array:
+ l = typ.Len()
+ case *types.Pointer:
+ l = typ.Elem().(*types.Array).Len()
+ }
+ ur := NewIntInterval(NewZ(l), NewZ(l))
+ if c.Upper != nil {
+ ur = g.Range(c.Upper).(IntInterval)
+ }
+ if !lr.IsKnown() || !ur.IsKnown() {
+ return SliceInterval{}
+ }
+
+ ls := []Z{
+ ur.Lower.Sub(lr.Lower),
+ ur.Upper.Sub(lr.Lower),
+ ur.Lower.Sub(lr.Upper),
+ ur.Upper.Sub(lr.Upper),
+ }
+ // TODO(dh): if we don't truncate lengths to 0 we might be able to
+ // easily detect slices with high < low. we'd need to treat -∞
+ // specially, though.
+ for i, l := range ls {
+ if l.Sign() == -1 {
+ ls[i] = NewZ(0)
+ }
+ }
+
+ return SliceInterval{
+ Length: NewIntInterval(MinZ(ls...), MaxZ(ls...)),
+ }
+}
+func (c *SliceIntersectionConstraint) Eval(g *Graph) Range {
+ xi := g.Range(c.X).(SliceInterval)
+ if !xi.IsKnown() {
+ return c.I
+ }
+ return SliceInterval{
+ Length: xi.Length.Intersection(c.I),
+ }
+}
+func (c *SliceLengthConstraint) Eval(g *Graph) Range {
+ i := g.Range(c.X).(SliceInterval).Length
+ if !i.IsKnown() {
+ return NewIntInterval(NewZ(0), PInfinity)
+ }
+ return i
+}
+func (c *MakeSliceConstraint) Eval(g *Graph) Range {
+ i, ok := g.Range(c.Size).(IntInterval)
+ if !ok {
+ return SliceInterval{NewIntInterval(NewZ(0), PInfinity)}
+ }
+ if i.Lower.Sign() == -1 {
+ i.Lower = NewZ(0)
+ }
+ return SliceInterval{i}
+}
+func (c *SliceIntervalConstraint) Eval(*Graph) Range { return SliceInterval{c.I} }
diff --git a/vendor/honnef.co/go/tools/staticcheck/vrp/string.go b/vendor/honnef.co/go/tools/staticcheck/vrp/string.go
new file mode 100644
index 000000000..e05877f9f
--- /dev/null
+++ b/vendor/honnef.co/go/tools/staticcheck/vrp/string.go
@@ -0,0 +1,258 @@
+package vrp
+
+import (
+ "fmt"
+ "go/token"
+ "go/types"
+
+ "honnef.co/go/tools/ssa"
+)
+
+type StringInterval struct {
+ Length IntInterval
+}
+
+func (s StringInterval) Union(other Range) Range {
+ i, ok := other.(StringInterval)
+ if !ok {
+ i = StringInterval{EmptyIntInterval}
+ }
+ if s.Length.Empty() || !s.Length.IsKnown() {
+ return i
+ }
+ if i.Length.Empty() || !i.Length.IsKnown() {
+ return s
+ }
+ return StringInterval{
+ Length: s.Length.Union(i.Length).(IntInterval),
+ }
+}
+
+func (s StringInterval) String() string {
+ return s.Length.String()
+}
+
+func (s StringInterval) IsKnown() bool {
+ return s.Length.IsKnown()
+}
+
+type StringSliceConstraint struct {
+ aConstraint
+ X ssa.Value
+ Lower ssa.Value
+ Upper ssa.Value
+}
+
+type StringIntersectionConstraint struct {
+ aConstraint
+ ranges Ranges
+ A ssa.Value
+ B ssa.Value
+ Op token.Token
+ I IntInterval
+ resolved bool
+}
+
+type StringConcatConstraint struct {
+ aConstraint
+ A ssa.Value
+ B ssa.Value
+}
+
+type StringLengthConstraint struct {
+ aConstraint
+ X ssa.Value
+}
+
+type StringIntervalConstraint struct {
+ aConstraint
+ I IntInterval
+}
+
+func NewStringSliceConstraint(x, lower, upper, y ssa.Value) Constraint {
+ return &StringSliceConstraint{NewConstraint(y), x, lower, upper}
+}
+func NewStringIntersectionConstraint(a, b ssa.Value, op token.Token, ranges Ranges, y ssa.Value) Constraint {
+ return &StringIntersectionConstraint{
+ aConstraint: NewConstraint(y),
+ ranges: ranges,
+ A: a,
+ B: b,
+ Op: op,
+ }
+}
+func NewStringConcatConstraint(a, b, y ssa.Value) Constraint {
+ return &StringConcatConstraint{NewConstraint(y), a, b}
+}
+func NewStringLengthConstraint(x ssa.Value, y ssa.Value) Constraint {
+ return &StringLengthConstraint{NewConstraint(y), x}
+}
+func NewStringIntervalConstraint(i IntInterval, y ssa.Value) Constraint {
+ return &StringIntervalConstraint{NewConstraint(y), i}
+}
+
+func (c *StringSliceConstraint) Operands() []ssa.Value {
+ vs := []ssa.Value{c.X}
+ if c.Lower != nil {
+ vs = append(vs, c.Lower)
+ }
+ if c.Upper != nil {
+ vs = append(vs, c.Upper)
+ }
+ return vs
+}
+func (c *StringIntersectionConstraint) Operands() []ssa.Value { return []ssa.Value{c.A} }
+func (c StringConcatConstraint) Operands() []ssa.Value { return []ssa.Value{c.A, c.B} }
+func (c *StringLengthConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
+func (s *StringIntervalConstraint) Operands() []ssa.Value { return nil }
+
+func (c *StringSliceConstraint) String() string {
+ var lname, uname string
+ if c.Lower != nil {
+ lname = c.Lower.Name()
+ }
+ if c.Upper != nil {
+ uname = c.Upper.Name()
+ }
+ return fmt.Sprintf("%s[%s:%s]", c.X.Name(), lname, uname)
+}
+func (c *StringIntersectionConstraint) String() string {
+ return fmt.Sprintf("%s = %s %s %s (%t branch)", c.Y().Name(), c.A.Name(), c.Op, c.B.Name(), c.Y().(*ssa.Sigma).Branch)
+}
+func (c StringConcatConstraint) String() string {
+ return fmt.Sprintf("%s = %s + %s", c.Y().Name(), c.A.Name(), c.B.Name())
+}
+func (c *StringLengthConstraint) String() string {
+ return fmt.Sprintf("%s = len(%s)", c.Y().Name(), c.X.Name())
+}
+func (c *StringIntervalConstraint) String() string { return fmt.Sprintf("%s = %s", c.Y().Name(), c.I) }
+
+func (c *StringSliceConstraint) Eval(g *Graph) Range {
+ lr := NewIntInterval(NewZ(0), NewZ(0))
+ if c.Lower != nil {
+ lr = g.Range(c.Lower).(IntInterval)
+ }
+ ur := g.Range(c.X).(StringInterval).Length
+ if c.Upper != nil {
+ ur = g.Range(c.Upper).(IntInterval)
+ }
+ if !lr.IsKnown() || !ur.IsKnown() {
+ return StringInterval{}
+ }
+
+ ls := []Z{
+ ur.Lower.Sub(lr.Lower),
+ ur.Upper.Sub(lr.Lower),
+ ur.Lower.Sub(lr.Upper),
+ ur.Upper.Sub(lr.Upper),
+ }
+ // TODO(dh): if we don't truncate lengths to 0 we might be able to
+ // easily detect slices with high < low. we'd need to treat -∞
+ // specially, though.
+ for i, l := range ls {
+ if l.Sign() == -1 {
+ ls[i] = NewZ(0)
+ }
+ }
+
+ return StringInterval{
+ Length: NewIntInterval(MinZ(ls...), MaxZ(ls...)),
+ }
+}
+func (c *StringIntersectionConstraint) Eval(g *Graph) Range {
+ var l IntInterval
+ switch r := g.Range(c.A).(type) {
+ case StringInterval:
+ l = r.Length
+ case IntInterval:
+ l = r
+ }
+
+ if !l.IsKnown() {
+ return StringInterval{c.I}
+ }
+ return StringInterval{
+ Length: l.Intersection(c.I),
+ }
+}
+func (c StringConcatConstraint) Eval(g *Graph) Range {
+ i1, i2 := g.Range(c.A).(StringInterval), g.Range(c.B).(StringInterval)
+ if !i1.Length.IsKnown() || !i2.Length.IsKnown() {
+ return StringInterval{}
+ }
+ return StringInterval{
+ Length: i1.Length.Add(i2.Length),
+ }
+}
+func (c *StringLengthConstraint) Eval(g *Graph) Range {
+ i := g.Range(c.X).(StringInterval).Length
+ if !i.IsKnown() {
+ return NewIntInterval(NewZ(0), PInfinity)
+ }
+ return i
+}
+func (c *StringIntervalConstraint) Eval(*Graph) Range { return StringInterval{c.I} }
+
+func (c *StringIntersectionConstraint) Futures() []ssa.Value {
+ return []ssa.Value{c.B}
+}
+
+func (c *StringIntersectionConstraint) Resolve() {
+ if (c.A.Type().Underlying().(*types.Basic).Info() & types.IsString) != 0 {
+ // comparing two strings
+ r, ok := c.ranges[c.B].(StringInterval)
+ if !ok {
+ c.I = NewIntInterval(NewZ(0), PInfinity)
+ return
+ }
+ switch c.Op {
+ case token.EQL:
+ c.I = r.Length
+ case token.GTR, token.GEQ:
+ c.I = NewIntInterval(r.Length.Lower, PInfinity)
+ case token.LSS, token.LEQ:
+ c.I = NewIntInterval(NewZ(0), r.Length.Upper)
+ case token.NEQ:
+ default:
+ panic("unsupported op " + c.Op.String())
+ }
+ } else {
+ r, ok := c.ranges[c.B].(IntInterval)
+ if !ok {
+ c.I = NewIntInterval(NewZ(0), PInfinity)
+ return
+ }
+ // comparing two lengths
+ switch c.Op {
+ case token.EQL:
+ c.I = r
+ case token.GTR:
+ c.I = NewIntInterval(r.Lower.Add(NewZ(1)), PInfinity)
+ case token.GEQ:
+ c.I = NewIntInterval(r.Lower, PInfinity)
+ case token.LSS:
+ c.I = NewIntInterval(NInfinity, r.Upper.Sub(NewZ(1)))
+ case token.LEQ:
+ c.I = NewIntInterval(NInfinity, r.Upper)
+ case token.NEQ:
+ default:
+ panic("unsupported op " + c.Op.String())
+ }
+ }
+}
+
+func (c *StringIntersectionConstraint) IsKnown() bool {
+ return c.I.IsKnown()
+}
+
+func (c *StringIntersectionConstraint) MarkUnresolved() {
+ c.resolved = false
+}
+
+func (c *StringIntersectionConstraint) MarkResolved() {
+ c.resolved = true
+}
+
+func (c *StringIntersectionConstraint) IsResolved() bool {
+ return c.resolved
+}
diff --git a/vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go b/vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go
new file mode 100644
index 000000000..3c138e512
--- /dev/null
+++ b/vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go
@@ -0,0 +1,1056 @@
+package vrp
+
+// TODO(dh) widening and narrowing have a lot of code in common. Make
+// it reusable.
+
+import (
+ "fmt"
+ "go/constant"
+ "go/token"
+ "go/types"
+ "math/big"
+ "sort"
+ "strings"
+
+ "honnef.co/go/tools/lint"
+ "honnef.co/go/tools/ssa"
+)
+
+type Future interface {
+ Constraint
+ Futures() []ssa.Value
+ Resolve()
+ IsKnown() bool
+ MarkUnresolved()
+ MarkResolved()
+ IsResolved() bool
+}
+
+type Range interface {
+ Union(other Range) Range
+ IsKnown() bool
+}
+
+type Constraint interface {
+ Y() ssa.Value
+ isConstraint()
+ String() string
+ Eval(*Graph) Range
+ Operands() []ssa.Value
+}
+
+type aConstraint struct {
+ y ssa.Value
+}
+
+func NewConstraint(y ssa.Value) aConstraint {
+ return aConstraint{y}
+}
+
+func (aConstraint) isConstraint() {}
+func (c aConstraint) Y() ssa.Value { return c.y }
+
+type PhiConstraint struct {
+ aConstraint
+ Vars []ssa.Value
+}
+
+func NewPhiConstraint(vars []ssa.Value, y ssa.Value) Constraint {
+ uniqm := map[ssa.Value]struct{}{}
+ for _, v := range vars {
+ uniqm[v] = struct{}{}
+ }
+ var uniq []ssa.Value
+ for v := range uniqm {
+ uniq = append(uniq, v)
+ }
+ return &PhiConstraint{
+ aConstraint: NewConstraint(y),
+ Vars: uniq,
+ }
+}
+
+func (c *PhiConstraint) Operands() []ssa.Value {
+ return c.Vars
+}
+
+func (c *PhiConstraint) Eval(g *Graph) Range {
+ i := Range(nil)
+ for _, v := range c.Vars {
+ i = g.Range(v).Union(i)
+ }
+ return i
+}
+
+func (c *PhiConstraint) String() string {
+ names := make([]string, len(c.Vars))
+ for i, v := range c.Vars {
+ names[i] = v.Name()
+ }
+ return fmt.Sprintf("%s = φ(%s)", c.Y().Name(), strings.Join(names, ", "))
+}
+
+func isSupportedType(typ types.Type) bool {
+ switch typ := typ.Underlying().(type) {
+ case *types.Basic:
+ switch typ.Kind() {
+ case types.String, types.UntypedString:
+ return true
+ default:
+ if (typ.Info() & types.IsInteger) == 0 {
+ return false
+ }
+ }
+ case *types.Chan:
+ return true
+ case *types.Slice:
+ return true
+ default:
+ return false
+ }
+ return true
+}
+
+func ConstantToZ(c constant.Value) Z {
+ s := constant.ToInt(c).ExactString()
+ n := &big.Int{}
+ n.SetString(s, 10)
+ return NewBigZ(n)
+}
+
+func sigmaInteger(g *Graph, ins *ssa.Sigma, cond *ssa.BinOp, ops []*ssa.Value) Constraint {
+ op := cond.Op
+ if !ins.Branch {
+ op = (invertToken(op))
+ }
+
+ switch op {
+ case token.EQL, token.GTR, token.GEQ, token.LSS, token.LEQ:
+ default:
+ return nil
+ }
+ var a, b ssa.Value
+ if (*ops[0]) == ins.X {
+ a = *ops[0]
+ b = *ops[1]
+ } else {
+ a = *ops[1]
+ b = *ops[0]
+ op = flipToken(op)
+ }
+ return NewIntIntersectionConstraint(a, b, op, g.ranges, ins)
+}
+
+func sigmaString(g *Graph, ins *ssa.Sigma, cond *ssa.BinOp, ops []*ssa.Value) Constraint {
+ op := cond.Op
+ if !ins.Branch {
+ op = (invertToken(op))
+ }
+
+ switch op {
+ case token.EQL, token.GTR, token.GEQ, token.LSS, token.LEQ:
+ default:
+ return nil
+ }
+
+ if ((*ops[0]).Type().Underlying().(*types.Basic).Info() & types.IsString) == 0 {
+ var a, b ssa.Value
+ call, ok := (*ops[0]).(*ssa.Call)
+ if ok && call.Common().Args[0] == ins.X {
+ a = *ops[0]
+ b = *ops[1]
+ } else {
+ a = *ops[1]
+ b = *ops[0]
+ op = flipToken(op)
+ }
+ return NewStringIntersectionConstraint(a, b, op, g.ranges, ins)
+ }
+ var a, b ssa.Value
+ if (*ops[0]) == ins.X {
+ a = *ops[0]
+ b = *ops[1]
+ } else {
+ a = *ops[1]
+ b = *ops[0]
+ op = flipToken(op)
+ }
+ return NewStringIntersectionConstraint(a, b, op, g.ranges, ins)
+}
+
+func sigmaSlice(g *Graph, ins *ssa.Sigma, cond *ssa.BinOp, ops []*ssa.Value) Constraint {
+ // TODO(dh) sigmaSlice and sigmaString are a lot alike. Can they
+ // be merged?
+ //
+ // XXX support futures
+
+ op := cond.Op
+ if !ins.Branch {
+ op = (invertToken(op))
+ }
+
+ k, ok := (*ops[1]).(*ssa.Const)
+ // XXX investigate in what cases this wouldn't be a Const
+ //
+ // XXX what if left and right are swapped?
+ if !ok {
+ return nil
+ }
+
+ call, ok := (*ops[0]).(*ssa.Call)
+ if !ok {
+ return nil
+ }
+ builtin, ok := call.Common().Value.(*ssa.Builtin)
+ if !ok {
+ return nil
+ }
+ if builtin.Name() != "len" {
+ return nil
+ }
+ callops := call.Operands(nil)
+
+ v := ConstantToZ(k.Value)
+ c := NewSliceIntersectionConstraint(*callops[1], IntInterval{}, ins).(*SliceIntersectionConstraint)
+ switch op {
+ case token.EQL:
+ c.I = NewIntInterval(v, v)
+ case token.GTR, token.GEQ:
+ off := int64(0)
+ if cond.Op == token.GTR {
+ off = 1
+ }
+ c.I = NewIntInterval(
+ v.Add(NewZ(off)),
+ PInfinity,
+ )
+ case token.LSS, token.LEQ:
+ off := int64(0)
+ if cond.Op == token.LSS {
+ off = -1
+ }
+ c.I = NewIntInterval(
+ NInfinity,
+ v.Add(NewZ(off)),
+ )
+ default:
+ return nil
+ }
+ return c
+}
+
+func BuildGraph(f *ssa.Function) *Graph {
+ g := &Graph{
+ Vertices: map[interface{}]*Vertex{},
+ ranges: Ranges{},
+ }
+
+ var cs []Constraint
+
+ ops := make([]*ssa.Value, 16)
+ seen := map[ssa.Value]bool{}
+ for _, block := range f.Blocks {
+ for _, ins := range block.Instrs {
+ ops = ins.Operands(ops[:0])
+ for _, op := range ops {
+ if c, ok := (*op).(*ssa.Const); ok {
+ if seen[c] {
+ continue
+ }
+ seen[c] = true
+ if c.Value == nil {
+ switch c.Type().Underlying().(type) {
+ case *types.Slice:
+ cs = append(cs, NewSliceIntervalConstraint(NewIntInterval(NewZ(0), NewZ(0)), c))
+ }
+ continue
+ }
+ switch c.Value.Kind() {
+ case constant.Int:
+ v := ConstantToZ(c.Value)
+ cs = append(cs, NewIntIntervalConstraint(NewIntInterval(v, v), c))
+ case constant.String:
+ s := constant.StringVal(c.Value)
+ n := NewZ(int64(len(s)))
+ cs = append(cs, NewStringIntervalConstraint(NewIntInterval(n, n), c))
+ }
+ }
+ }
+ }
+ }
+ for _, block := range f.Blocks {
+ for _, ins := range block.Instrs {
+ switch ins := ins.(type) {
+ case *ssa.Convert:
+ switch v := ins.Type().Underlying().(type) {
+ case *types.Basic:
+ if (v.Info() & types.IsInteger) == 0 {
+ continue
+ }
+ cs = append(cs, NewIntConversionConstraint(ins.X, ins))
+ }
+ case *ssa.Call:
+ if static := ins.Common().StaticCallee(); static != nil {
+ if fn, ok := static.Object().(*types.Func); ok {
+ switch lint.FuncName(fn) {
+ case "bytes.Index", "bytes.IndexAny", "bytes.IndexByte",
+ "bytes.IndexFunc", "bytes.IndexRune", "bytes.LastIndex",
+ "bytes.LastIndexAny", "bytes.LastIndexByte", "bytes.LastIndexFunc",
+ "strings.Index", "strings.IndexAny", "strings.IndexByte",
+ "strings.IndexFunc", "strings.IndexRune", "strings.LastIndex",
+ "strings.LastIndexAny", "strings.LastIndexByte", "strings.LastIndexFunc":
+ // TODO(dh): instead of limiting by +∞,
+ // limit by the upper bound of the passed
+ // string
+ cs = append(cs, NewIntIntervalConstraint(NewIntInterval(NewZ(-1), PInfinity), ins))
+ case "bytes.Title", "bytes.ToLower", "bytes.ToTitle", "bytes.ToUpper",
+ "strings.Title", "strings.ToLower", "strings.ToTitle", "strings.ToUpper":
+ cs = append(cs, NewCopyConstraint(ins.Common().Args[0], ins))
+ case "bytes.ToLowerSpecial", "bytes.ToTitleSpecial", "bytes.ToUpperSpecial",
+ "strings.ToLowerSpecial", "strings.ToTitleSpecial", "strings.ToUpperSpecial":
+ cs = append(cs, NewCopyConstraint(ins.Common().Args[1], ins))
+ case "bytes.Compare", "strings.Compare":
+ cs = append(cs, NewIntIntervalConstraint(NewIntInterval(NewZ(-1), NewZ(1)), ins))
+ case "bytes.Count", "strings.Count":
+ // TODO(dh): instead of limiting by +∞,
+ // limit by the upper bound of the passed
+ // string.
+ cs = append(cs, NewIntIntervalConstraint(NewIntInterval(NewZ(0), PInfinity), ins))
+ case "bytes.Map", "bytes.TrimFunc", "bytes.TrimLeft", "bytes.TrimLeftFunc",
+ "bytes.TrimRight", "bytes.TrimRightFunc", "bytes.TrimSpace",
+ "strings.Map", "strings.TrimFunc", "strings.TrimLeft", "strings.TrimLeftFunc",
+ "strings.TrimRight", "strings.TrimRightFunc", "strings.TrimSpace":
+ // TODO(dh): lower = 0, upper = upper of passed string
+ case "bytes.TrimPrefix", "bytes.TrimSuffix",
+ "strings.TrimPrefix", "strings.TrimSuffix":
+ // TODO(dh) range between "unmodified" and len(cutset) removed
+ case "(*bytes.Buffer).Cap", "(*bytes.Buffer).Len", "(*bytes.Reader).Len", "(*bytes.Reader).Size":
+ cs = append(cs, NewIntIntervalConstraint(NewIntInterval(NewZ(0), PInfinity), ins))
+ }
+ }
+ }
+ builtin, ok := ins.Common().Value.(*ssa.Builtin)
+ ops := ins.Operands(nil)
+ if !ok {
+ continue
+ }
+ switch builtin.Name() {
+ case "len":
+ switch op1 := (*ops[1]).Type().Underlying().(type) {
+ case *types.Basic:
+ if op1.Kind() == types.String || op1.Kind() == types.UntypedString {
+ cs = append(cs, NewStringLengthConstraint(*ops[1], ins))
+ }
+ case *types.Slice:
+ cs = append(cs, NewSliceLengthConstraint(*ops[1], ins))
+ }
+
+ case "append":
+ cs = append(cs, NewSliceAppendConstraint(ins.Common().Args[0], ins.Common().Args[1], ins))
+ }
+ case *ssa.BinOp:
+ ops := ins.Operands(nil)
+ basic, ok := (*ops[0]).Type().Underlying().(*types.Basic)
+ if !ok {
+ continue
+ }
+ switch basic.Kind() {
+ case types.Int, types.Int8, types.Int16, types.Int32, types.Int64,
+ types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.UntypedInt:
+ fns := map[token.Token]func(ssa.Value, ssa.Value, ssa.Value) Constraint{
+ token.ADD: NewIntAddConstraint,
+ token.SUB: NewIntSubConstraint,
+ token.MUL: NewIntMulConstraint,
+ // XXX support QUO, REM, SHL, SHR
+ }
+ fn, ok := fns[ins.Op]
+ if ok {
+ cs = append(cs, fn(*ops[0], *ops[1], ins))
+ }
+ case types.String, types.UntypedString:
+ if ins.Op == token.ADD {
+ cs = append(cs, NewStringConcatConstraint(*ops[0], *ops[1], ins))
+ }
+ }
+ case *ssa.Slice:
+ typ := ins.X.Type().Underlying()
+ switch typ := typ.(type) {
+ case *types.Basic:
+ cs = append(cs, NewStringSliceConstraint(ins.X, ins.Low, ins.High, ins))
+ case *types.Slice:
+ cs = append(cs, NewSliceSliceConstraint(ins.X, ins.Low, ins.High, ins))
+ case *types.Array:
+ cs = append(cs, NewArraySliceConstraint(ins.X, ins.Low, ins.High, ins))
+ case *types.Pointer:
+ if _, ok := typ.Elem().(*types.Array); !ok {
+ continue
+ }
+ cs = append(cs, NewArraySliceConstraint(ins.X, ins.Low, ins.High, ins))
+ }
+ case *ssa.Phi:
+ if !isSupportedType(ins.Type()) {
+ continue
+ }
+ ops := ins.Operands(nil)
+ dops := make([]ssa.Value, len(ops))
+ for i, op := range ops {
+ dops[i] = *op
+ }
+ cs = append(cs, NewPhiConstraint(dops, ins))
+ case *ssa.Sigma:
+ pred := ins.Block().Preds[0]
+ instrs := pred.Instrs
+ cond, ok := instrs[len(instrs)-1].(*ssa.If).Cond.(*ssa.BinOp)
+ ops := cond.Operands(nil)
+ if !ok {
+ continue
+ }
+ switch typ := ins.Type().Underlying().(type) {
+ case *types.Basic:
+ var c Constraint
+ switch typ.Kind() {
+ case types.Int, types.Int8, types.Int16, types.Int32, types.Int64,
+ types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.UntypedInt:
+ c = sigmaInteger(g, ins, cond, ops)
+ case types.String, types.UntypedString:
+ c = sigmaString(g, ins, cond, ops)
+ }
+ if c != nil {
+ cs = append(cs, c)
+ }
+ case *types.Slice:
+ c := sigmaSlice(g, ins, cond, ops)
+ if c != nil {
+ cs = append(cs, c)
+ }
+ default:
+ //log.Printf("unsupported sigma type %T", typ) // XXX
+ }
+ case *ssa.MakeChan:
+ cs = append(cs, NewMakeChannelConstraint(ins.Size, ins))
+ case *ssa.MakeSlice:
+ cs = append(cs, NewMakeSliceConstraint(ins.Len, ins))
+ case *ssa.ChangeType:
+ switch ins.X.Type().Underlying().(type) {
+ case *types.Chan:
+ cs = append(cs, NewChannelChangeTypeConstraint(ins.X, ins))
+ }
+ }
+ }
+ }
+
+ for _, c := range cs {
+ if c == nil {
+ panic("nil constraint")
+ }
+ // If V is used in constraint C, then we create an edge V->C
+ for _, op := range c.Operands() {
+ g.AddEdge(op, c, false)
+ }
+ if c, ok := c.(Future); ok {
+ for _, op := range c.Futures() {
+ g.AddEdge(op, c, true)
+ }
+ }
+ // If constraint C defines variable V, then we create an edge
+ // C->V
+ g.AddEdge(c, c.Y(), false)
+ }
+
+ g.FindSCCs()
+ g.sccEdges = make([][]Edge, len(g.SCCs))
+ g.futures = make([][]Future, len(g.SCCs))
+ for _, e := range g.Edges {
+ g.sccEdges[e.From.SCC] = append(g.sccEdges[e.From.SCC], e)
+ if !e.control {
+ continue
+ }
+ if c, ok := e.To.Value.(Future); ok {
+ g.futures[e.From.SCC] = append(g.futures[e.From.SCC], c)
+ }
+ }
+ return g
+}
+
+func (g *Graph) Solve() Ranges {
+ var consts []Z
+ off := NewZ(1)
+ for _, n := range g.Vertices {
+ if c, ok := n.Value.(*ssa.Const); ok {
+ basic, ok := c.Type().Underlying().(*types.Basic)
+ if !ok {
+ continue
+ }
+ if (basic.Info() & types.IsInteger) != 0 {
+ z := ConstantToZ(c.Value)
+ consts = append(consts, z)
+ consts = append(consts, z.Add(off))
+ consts = append(consts, z.Sub(off))
+ }
+ }
+
+ }
+ sort.Sort(Zs(consts))
+
+ for scc, vertices := range g.SCCs {
+ n := 0
+ n = len(vertices)
+ if n == 1 {
+ g.resolveFutures(scc)
+ v := vertices[0]
+ if v, ok := v.Value.(ssa.Value); ok {
+ switch typ := v.Type().Underlying().(type) {
+ case *types.Basic:
+ switch typ.Kind() {
+ case types.String, types.UntypedString:
+ if !g.Range(v).(StringInterval).IsKnown() {
+ g.SetRange(v, StringInterval{NewIntInterval(NewZ(0), PInfinity)})
+ }
+ default:
+ if !g.Range(v).(IntInterval).IsKnown() {
+ g.SetRange(v, InfinityFor(v))
+ }
+ }
+ case *types.Chan:
+ if !g.Range(v).(ChannelInterval).IsKnown() {
+ g.SetRange(v, ChannelInterval{NewIntInterval(NewZ(0), PInfinity)})
+ }
+ case *types.Slice:
+ if !g.Range(v).(SliceInterval).IsKnown() {
+ g.SetRange(v, SliceInterval{NewIntInterval(NewZ(0), PInfinity)})
+ }
+ }
+ }
+ if c, ok := v.Value.(Constraint); ok {
+ g.SetRange(c.Y(), c.Eval(g))
+ }
+ } else {
+ uses := g.uses(scc)
+ entries := g.entries(scc)
+ for len(entries) > 0 {
+ v := entries[len(entries)-1]
+ entries = entries[:len(entries)-1]
+ for _, use := range uses[v] {
+ if g.widen(use, consts) {
+ entries = append(entries, use.Y())
+ }
+ }
+ }
+
+ g.resolveFutures(scc)
+
+ // XXX this seems to be necessary, but shouldn't be.
+ // removing it leads to nil pointer derefs; investigate
+ // where we're not setting values correctly.
+ for _, n := range vertices {
+ if v, ok := n.Value.(ssa.Value); ok {
+ i, ok := g.Range(v).(IntInterval)
+ if !ok {
+ continue
+ }
+ if !i.IsKnown() {
+ g.SetRange(v, InfinityFor(v))
+ }
+ }
+ }
+
+ actives := g.actives(scc)
+ for len(actives) > 0 {
+ v := actives[len(actives)-1]
+ actives = actives[:len(actives)-1]
+ for _, use := range uses[v] {
+ if g.narrow(use) {
+ actives = append(actives, use.Y())
+ }
+ }
+ }
+ }
+ // propagate scc
+ for _, edge := range g.sccEdges[scc] {
+ if edge.control {
+ continue
+ }
+ if edge.From.SCC == edge.To.SCC {
+ continue
+ }
+ if c, ok := edge.To.Value.(Constraint); ok {
+ g.SetRange(c.Y(), c.Eval(g))
+ }
+ if c, ok := edge.To.Value.(Future); ok {
+ if !c.IsKnown() {
+ c.MarkUnresolved()
+ }
+ }
+ }
+ }
+
+ for v, r := range g.ranges {
+ i, ok := r.(IntInterval)
+ if !ok {
+ continue
+ }
+ if (v.Type().Underlying().(*types.Basic).Info() & types.IsUnsigned) == 0 {
+ if i.Upper != PInfinity {
+ s := &types.StdSizes{
+ // XXX is it okay to assume the largest word size, or do we
+ // need to be platform specific?
+ WordSize: 8,
+ MaxAlign: 1,
+ }
+ bits := (s.Sizeof(v.Type()) * 8) - 1
+ n := big.NewInt(1)
+ n = n.Lsh(n, uint(bits))
+ upper, lower := &big.Int{}, &big.Int{}
+ upper.Sub(n, big.NewInt(1))
+ lower.Neg(n)
+
+ if i.Upper.Cmp(NewBigZ(upper)) == 1 {
+ i = NewIntInterval(NInfinity, PInfinity)
+ } else if i.Lower.Cmp(NewBigZ(lower)) == -1 {
+ i = NewIntInterval(NInfinity, PInfinity)
+ }
+ }
+ }
+
+ g.ranges[v] = i
+ }
+
+ return g.ranges
+}
+
+func VertexString(v *Vertex) string {
+ switch v := v.Value.(type) {
+ case Constraint:
+ return v.String()
+ case ssa.Value:
+ return v.Name()
+ case nil:
+ return "BUG: nil vertex value"
+ default:
+ panic(fmt.Sprintf("unexpected type %T", v))
+ }
+}
+
+type Vertex struct {
+ Value interface{} // one of Constraint or ssa.Value
+ SCC int
+ index int
+ lowlink int
+ stack bool
+
+ Succs []Edge
+}
+
+type Ranges map[ssa.Value]Range
+
+func (r Ranges) Get(x ssa.Value) Range {
+ if x == nil {
+ return nil
+ }
+ i, ok := r[x]
+ if !ok {
+ switch x := x.Type().Underlying().(type) {
+ case *types.Basic:
+ switch x.Kind() {
+ case types.String, types.UntypedString:
+ return StringInterval{}
+ default:
+ return IntInterval{}
+ }
+ case *types.Chan:
+ return ChannelInterval{}
+ case *types.Slice:
+ return SliceInterval{}
+ }
+ }
+ return i
+}
+
+type Graph struct {
+ Vertices map[interface{}]*Vertex
+ Edges []Edge
+ SCCs [][]*Vertex
+ ranges Ranges
+
+ // map SCCs to futures
+ futures [][]Future
+ // map SCCs to edges
+ sccEdges [][]Edge
+}
+
+func (g Graph) Graphviz() string {
+ var lines []string
+ lines = append(lines, "digraph{")
+ ids := map[interface{}]int{}
+ i := 1
+ for _, v := range g.Vertices {
+ ids[v] = i
+ shape := "box"
+ if _, ok := v.Value.(ssa.Value); ok {
+ shape = "oval"
+ }
+ lines = append(lines, fmt.Sprintf(`n%d [shape="%s", label=%q, colorscheme=spectral11, style="filled", fillcolor="%d"]`,
+ i, shape, VertexString(v), (v.SCC%11)+1))
+ i++
+ }
+ for _, e := range g.Edges {
+ style := "solid"
+ if e.control {
+ style = "dashed"
+ }
+ lines = append(lines, fmt.Sprintf(`n%d -> n%d [style="%s"]`, ids[e.From], ids[e.To], style))
+ }
+ lines = append(lines, "}")
+ return strings.Join(lines, "\n")
+}
+
+func (g *Graph) SetRange(x ssa.Value, r Range) {
+ g.ranges[x] = r
+}
+
+func (g *Graph) Range(x ssa.Value) Range {
+ return g.ranges.Get(x)
+}
+
+func (g *Graph) widen(c Constraint, consts []Z) bool {
+ setRange := func(i Range) {
+ g.SetRange(c.Y(), i)
+ }
+ widenIntInterval := func(oi, ni IntInterval) (IntInterval, bool) {
+ if !ni.IsKnown() {
+ return oi, false
+ }
+ nlc := NInfinity
+ nuc := PInfinity
+
+ // 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
+ }
+ }
+ }
+
+ if !oi.IsKnown() {
+ return ni, true
+ }
+ if ni.Lower.Cmp(oi.Lower) == -1 && ni.Upper.Cmp(oi.Upper) == 1 {
+ return NewIntInterval(nlc, nuc), true
+ }
+ if ni.Lower.Cmp(oi.Lower) == -1 {
+ return NewIntInterval(nlc, oi.Upper), true
+ }
+ if ni.Upper.Cmp(oi.Upper) == 1 {
+ return NewIntInterval(oi.Lower, nuc), true
+ }
+ return oi, false
+ }
+ switch oi := g.Range(c.Y()).(type) {
+ case IntInterval:
+ ni := c.Eval(g).(IntInterval)
+ si, changed := widenIntInterval(oi, ni)
+ if changed {
+ setRange(si)
+ return true
+ }
+ return false
+ case StringInterval:
+ ni := c.Eval(g).(StringInterval)
+ si, changed := widenIntInterval(oi.Length, ni.Length)
+ if changed {
+ setRange(StringInterval{si})
+ return true
+ }
+ return false
+ case SliceInterval:
+ ni := c.Eval(g).(SliceInterval)
+ si, changed := widenIntInterval(oi.Length, ni.Length)
+ if changed {
+ setRange(SliceInterval{si})
+ return true
+ }
+ return false
+ default:
+ return false
+ }
+}
+
+func (g *Graph) narrow(c Constraint) bool {
+ narrowIntInterval := func(oi, ni IntInterval) (IntInterval, bool) {
+ oLower := oi.Lower
+ oUpper := oi.Upper
+ nLower := ni.Lower
+ nUpper := ni.Upper
+
+ if oLower == NInfinity && nLower != NInfinity {
+ return NewIntInterval(nLower, oUpper), true
+ }
+ if oUpper == PInfinity && nUpper != PInfinity {
+ return NewIntInterval(oLower, nUpper), true
+ }
+ if oLower.Cmp(nLower) == 1 {
+ return NewIntInterval(nLower, oUpper), true
+ }
+ if oUpper.Cmp(nUpper) == -1 {
+ return NewIntInterval(oLower, nUpper), true
+ }
+ return oi, false
+ }
+ switch oi := g.Range(c.Y()).(type) {
+ case IntInterval:
+ ni := c.Eval(g).(IntInterval)
+ si, changed := narrowIntInterval(oi, ni)
+ if changed {
+ g.SetRange(c.Y(), si)
+ return true
+ }
+ return false
+ case StringInterval:
+ ni := c.Eval(g).(StringInterval)
+ si, changed := narrowIntInterval(oi.Length, ni.Length)
+ if changed {
+ g.SetRange(c.Y(), StringInterval{si})
+ return true
+ }
+ return false
+ case SliceInterval:
+ ni := c.Eval(g).(SliceInterval)
+ si, changed := narrowIntInterval(oi.Length, ni.Length)
+ if changed {
+ g.SetRange(c.Y(), SliceInterval{si})
+ return true
+ }
+ return false
+ default:
+ return false
+ }
+}
+
+func (g *Graph) resolveFutures(scc int) {
+ for _, c := range g.futures[scc] {
+ c.Resolve()
+ }
+}
+
+func (g *Graph) entries(scc int) []ssa.Value {
+ var entries []ssa.Value
+ for _, n := range g.Vertices {
+ if n.SCC != scc {
+ continue
+ }
+ if v, ok := n.Value.(ssa.Value); ok {
+ // XXX avoid quadratic runtime
+ //
+ // XXX I cannot think of any code where the future and its
+ // variables aren't in the same SCC, in which case this
+ // code isn't very useful (the variables won't be resolved
+ // yet). Before we have a cross-SCC example, however, we
+ // can't really verify that this code is working
+ // correctly, or indeed doing anything useful.
+ for _, on := range g.Vertices {
+ if c, ok := on.Value.(Future); ok {
+ if c.Y() == v {
+ if !c.IsResolved() {
+ g.SetRange(c.Y(), c.Eval(g))
+ c.MarkResolved()
+ }
+ break
+ }
+ }
+ }
+ if g.Range(v).IsKnown() {
+ entries = append(entries, v)
+ }
+ }
+ }
+ return entries
+}
+
+func (g *Graph) uses(scc int) map[ssa.Value][]Constraint {
+ m := map[ssa.Value][]Constraint{}
+ for _, e := range g.sccEdges[scc] {
+ if e.control {
+ continue
+ }
+ if v, ok := e.From.Value.(ssa.Value); ok {
+ c := e.To.Value.(Constraint)
+ sink := c.Y()
+ if g.Vertices[sink].SCC == scc {
+ m[v] = append(m[v], c)
+ }
+ }
+ }
+ return m
+}
+
+func (g *Graph) actives(scc int) []ssa.Value {
+ var actives []ssa.Value
+ for _, n := range g.Vertices {
+ if n.SCC != scc {
+ continue
+ }
+ if v, ok := n.Value.(ssa.Value); ok {
+ if _, ok := v.(*ssa.Const); !ok {
+ actives = append(actives, v)
+ }
+ }
+ }
+ return actives
+}
+
+func (g *Graph) AddEdge(from, to interface{}, ctrl bool) {
+ vf, ok := g.Vertices[from]
+ if !ok {
+ vf = &Vertex{Value: from}
+ g.Vertices[from] = vf
+ }
+ vt, ok := g.Vertices[to]
+ if !ok {
+ vt = &Vertex{Value: to}
+ g.Vertices[to] = vt
+ }
+ e := Edge{From: vf, To: vt, control: ctrl}
+ g.Edges = append(g.Edges, e)
+ vf.Succs = append(vf.Succs, e)
+}
+
+type Edge struct {
+ From, To *Vertex
+ control bool
+}
+
+func (e Edge) String() string {
+ return fmt.Sprintf("%s -> %s", VertexString(e.From), VertexString(e.To))
+}
+
+func (g *Graph) FindSCCs() {
+ // use Tarjan to find the SCCs
+
+ index := 1
+ var s []*Vertex
+
+ scc := 0
+ var strongconnect func(v *Vertex)
+ strongconnect = func(v *Vertex) {
+ // set the depth index for v to the smallest unused index
+ v.index = index
+ v.lowlink = index
+ index++
+ s = append(s, v)
+ v.stack = true
+
+ for _, e := range v.Succs {
+ w := e.To
+ if w.index == 0 {
+ // successor w has not yet been visited; recurse on it
+ strongconnect(w)
+ if w.lowlink < v.lowlink {
+ v.lowlink = w.lowlink
+ }
+ } else if w.stack {
+ // successor w is in stack s and hence in the current scc
+ if w.index < v.lowlink {
+ v.lowlink = w.index
+ }
+ }
+ }
+
+ if v.lowlink == v.index {
+ for {
+ w := s[len(s)-1]
+ s = s[:len(s)-1]
+ w.stack = false
+ w.SCC = scc
+ if w == v {
+ break
+ }
+ }
+ scc++
+ }
+ }
+ for _, v := range g.Vertices {
+ if v.index == 0 {
+ strongconnect(v)
+ }
+ }
+
+ g.SCCs = make([][]*Vertex, scc)
+ for _, n := range g.Vertices {
+ n.SCC = scc - n.SCC - 1
+ g.SCCs[n.SCC] = append(g.SCCs[n.SCC], n)
+ }
+}
+
+func invertToken(tok token.Token) token.Token {
+ switch tok {
+ case token.LSS:
+ return token.GEQ
+ case token.GTR:
+ return token.LEQ
+ case token.EQL:
+ return token.NEQ
+ case token.NEQ:
+ return token.EQL
+ case token.GEQ:
+ return token.LSS
+ case token.LEQ:
+ return token.GTR
+ default:
+ panic(fmt.Sprintf("unsupported token %s", tok))
+ }
+}
+
+func flipToken(tok token.Token) token.Token {
+ switch tok {
+ case token.LSS:
+ return token.GTR
+ case token.GTR:
+ return token.LSS
+ case token.EQL:
+ return token.EQL
+ case token.NEQ:
+ return token.NEQ
+ case token.GEQ:
+ return token.LEQ
+ case token.LEQ:
+ return token.GEQ
+ default:
+ panic(fmt.Sprintf("unsupported token %s", tok))
+ }
+}
+
+type CopyConstraint struct {
+ aConstraint
+ X ssa.Value
+}
+
+func (c *CopyConstraint) String() string {
+ return fmt.Sprintf("%s = copy(%s)", c.Y().Name(), c.X.Name())
+}
+
+func (c *CopyConstraint) Eval(g *Graph) Range {
+ return g.Range(c.X)
+}
+
+func (c *CopyConstraint) Operands() []ssa.Value {
+ return []ssa.Value{c.X}
+}
+
+func NewCopyConstraint(x, y ssa.Value) Constraint {
+ return &CopyConstraint{
+ aConstraint: aConstraint{
+ y: y,
+ },
+ X: x,
+ }
+}
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 000000000..f252487f7
--- /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
new file mode 100644
index 000000000..9097214d9
--- /dev/null
+++ b/vendor/honnef.co/go/tools/stylecheck/doc.go
@@ -0,0 +1,154 @@
+package stylecheck
+
+import "honnef.co/go/tools/lint"
+
+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.`,
+ Since: "2019.1",
+ NonDefault: true,
+ },
+
+ "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.
+
+Quoting Go Code Review Comments:
+
+ The import . form can be useful in tests that, due to circular
+ dependencies, cannot be made part of the package being tested:
+
+ package foo_test
+
+ import (
+ "bar/testutil" // also imports "foo"
+ . "foo"
+ )
+
+ In this case, the test file cannot be in package foo because it
+ uses bar/testutil, which imports foo. So we use the 'import .'
+ form to let the file pretend to be part of package foo even though
+ 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.`,
+ Since: "2019.1",
+ Options: []string{"dot_import_whitelist"},
+ },
+
+ "ST1003": &lint.Documentation{
+ Title: `Poorly chosen identifier`,
+ Text: `Identifiers, such as variable and package names, follow certain rules.
+
+See the following links for details:
+
+- 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"},
+ },
+
+ "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:
+
+ Error strings should not be capitalized (unless beginning with
+ proper nouns or acronyms) or end with punctuation, since they are
+ 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.`,
+ Since: "2019.1",
+ },
+
+ "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
+ suffices (such as "c" or "cl" for "Client"). Don't use generic
+ names such as "me", "this" or "self", identifiers typical of
+ object-oriented languages that place more emphasis on methods as
+ opposed to functions. The name need not be as descriptive as that
+ of a method argument, as its role is obvious and serves no
+ 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.`,
+ 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`,
+ },
+
+ "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.`,
+ 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",
+ },
+
+ "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.`,
+ Since: "2019.1",
+ Options: []string{"http_status_code_whitelist"},
+ },
+
+ "ST1015": &lint.Documentation{
+ Title: `A switch's default case should be the first or last case`,
+ Since: "2019.1",
+ },
+
+ "ST1016": &lint.Documentation{
+ Title: `Use consistent method receiver names`,
+ Since: "2019.1",
+ NonDefault: true,
+ },
+
+ "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",
+ },
+
+ "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
new file mode 100644
index 000000000..1699d5898
--- /dev/null
+++ b/vendor/honnef.co/go/tools/stylecheck/lint.go
@@ -0,0 +1,629 @@
+package stylecheck // import "honnef.co/go/tools/stylecheck"
+
+import (
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/token"
+ "go/types"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "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"
+)
+
+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
+ // "Package x ...". This has a slight potential for false
+ // positives, as multiple files can have package comments, in
+ // which case they get appended. But that doesn't happen a lot in
+ // the real world.
+
+ if pass.Pkg.Name() == "main" {
+ return nil, nil
+ }
+ hasDocs := false
+ 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) {
+ ReportNodef(pass, f.Doc, `package comment should be of the form "%s..."`, prefix)
+ }
+ f.Doc.Text()
+ }
+ }
+
+ if !hasDocs {
+ for _, f := range pass.Files {
+ if IsInTest(pass, f) {
+ continue
+ }
+ ReportNodef(pass, f, "at least one file in a package should have a package comment")
+ }
+ }
+ return nil, nil
+}
+
+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 config.For(pass).DotImportWhitelist {
+ if w == path {
+ continue imports
+ }
+ }
+
+ if imp.Name != nil && imp.Name.Name == "." && !IsInTest(pass, f) {
+ ReportNodefFG(pass, imp, "should not use dot imports")
+ }
+ }
+ }
+ return nil, nil
+}
+
+func CheckBlankImports(pass *analysis.Pass) (interface{}, error) {
+ fset := pass.Fset
+ for _, f := range pass.Files {
+ if IsInMain(pass, f) || IsInTest(pass, f) {
+ continue
+ }
+
+ // Collect imports of the form `import _ "foo"`, i.e. with no
+ // parentheses, as their comment will be associated with the
+ // (paren-free) GenDecl, not the import spec itself.
+ //
+ // We don't directly process the GenDecl so that we can
+ // correctly handle the following:
+ //
+ // import _ "foo"
+ // import _ "bar"
+ //
+ // where only the first import should get flagged.
+ skip := map[ast.Spec]bool{}
+ ast.Inspect(f, func(node ast.Node) bool {
+ switch node := node.(type) {
+ case *ast.File:
+ return true
+ case *ast.GenDecl:
+ if node.Tok != token.IMPORT {
+ return false
+ }
+ if node.Lparen == token.NoPos && node.Doc != nil {
+ skip[node.Specs[0]] = true
+ }
+ return false
+ }
+ return false
+ })
+ for i, imp := range f.Imports {
+ pos := fset.Position(imp.Pos())
+
+ if !IsBlank(imp.Name) {
+ continue
+ }
+ // Only flag the first blank import in a group of imports,
+ // or don't flag any of them, if the first one is
+ // commented
+ if i > 0 {
+ prev := f.Imports[i-1]
+ prevPos := fset.Position(prev.Pos())
+ if pos.Line-1 == prevPos.Line && IsBlank(prev.Name) {
+ continue
+ }
+ }
+
+ if imp.Doc == nil && imp.Comment == nil && !skip[imp] {
+ 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 CheckIncDec(pass *analysis.Pass) (interface{}, error) {
+ // TODO(dh): this can be noisy for function bodies that look like this:
+ // x += 3
+ // ...
+ // x += 2
+ // ...
+ // x += 1
+ fn := func(node ast.Node) {
+ assign := node.(*ast.AssignStmt)
+ if assign.Tok != token.ADD_ASSIGN && assign.Tok != token.SUB_ASSIGN {
+ return
+ }
+ if (len(assign.Lhs) != 1 || len(assign.Rhs) != 1) ||
+ !IsIntLiteral(assign.Rhs[0], "1") {
+ return
+ }
+
+ suffix := ""
+ switch assign.Tok {
+ case token.ADD_ASSIGN:
+ suffix = "++"
+ case token.SUB_ASSIGN:
+ suffix = "--"
+ }
+
+ ReportNodef(pass, assign, "should replace %s with %s%s", Render(pass, assign), Render(pass, assign.Lhs[0]), suffix)
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckErrorReturn(pass *analysis.Pass) (interface{}, error) {
+fnLoop:
+ for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs {
+ sig := fn.Type().(*types.Signature)
+ rets := sig.Results()
+ if rets == nil || rets.Len() < 2 {
+ continue
+ }
+
+ if rets.At(rets.Len()-1).Type() == types.Universe.Lookup("error").Type() {
+ // Last return type is error. If the function also returns
+ // errors in other positions, that's fine.
+ continue
+ }
+ for i := rets.Len() - 2; i >= 0; i-- {
+ if rets.At(i).Type() == types.Universe.Lookup("error").Type() {
+ 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 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(pass, fn) || IsInTest(pass, fn) {
+ continue
+ }
+ sig := fn.Type().(*types.Signature)
+ if sig.Recv() != nil && !ast.IsExported(Dereference(sig.Recv().Type()).(*types.Named).Obj().Name()) {
+ continue
+ }
+ res := sig.Results()
+ for i := 0; i < res.Len(); i++ {
+ if named, ok := DereferenceR(res.At(i).Type()).(*types.Named); ok &&
+ !ast.IsExported(named.Obj().Name()) &&
+ named != types.Universe.Lookup("error").Type() {
+ pass.Reportf(fn.Pos(), "should not return unexported type")
+ }
+ }
+ }
+ return nil, nil
+}
+
+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 {
+ fn := sel.Obj().(*types.Func)
+ recv := fn.Type().(*types.Signature).Recv()
+ if Dereference(recv.Type()) != T.Type() {
+ // skip embedded methods
+ continue
+ }
+ if recv.Name() == "self" || recv.Name() == "this" {
+ 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() == "_" {
+ ReportfFG(pass, recv.Pos(), "receiver name should not be an underscore, omit the name if it is unused")
+ }
+ }
+ }
+ }
+ return nil, nil
+}
+
+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
+ if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() {
+ ms := typeutil.IntuitiveMethodSet(T.Type(), nil)
+ for _, sel := range ms {
+ fn := sel.Obj().(*types.Func)
+ recv := fn.Type().(*types.Signature).Recv()
+ if Dereference(recv.Type()) != T.Type() {
+ // skip embedded methods
+ continue
+ }
+ if firstFn == nil {
+ firstFn = fn
+ }
+ if recv.Name() != "" && recv.Name() != "_" {
+ names[recv.Name()]++
+ }
+ }
+ }
+
+ if len(names) > 1 {
+ var seen []string
+ for name, count := range names {
+ seen = append(seen, fmt.Sprintf("%dx %q", count, name))
+ }
+
+ pass.Reportf(firstFn.Pos(), "methods on the same type should have the same receiver name (seen %s)", strings.Join(seen, ", "))
+ }
+ }
+ return nil, nil
+}
+
+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 pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs {
+ if fn.Synthetic != "" || fn.Parent() != nil {
+ continue
+ }
+ params := fn.Signature.Params()
+ if params.Len() < 2 {
+ continue
+ }
+ if types.TypeString(params.At(0).Type(), nil) == "context.Context" {
+ continue
+ }
+ for i := 1; i < params.Len(); i++ {
+ param := params.At(i)
+ if types.TypeString(param.Type(), nil) == "context.Context" {
+ pass.Reportf(param.Pos(), "context.Context should be the first argument of a function")
+ continue fnLoop
+ }
+ }
+ }
+ return nil, nil
+}
+
+func CheckErrorStrings(pass *analysis.Pass) (interface{}, error) {
+ objNames := map[*ssa.Package]map[string]bool{}
+ 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 pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs {
+ objNames[fn.Package()][fn.Name()] = true
+ }
+
+ 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
+ continue
+ }
+ for _, block := range fn.Blocks {
+ instrLoop:
+ for _, ins := range block.Instrs {
+ call, ok := ins.(*ssa.Call)
+ if !ok {
+ continue
+ }
+ if !IsCallTo(call.Common(), "errors.New") && !IsCallTo(call.Common(), "fmt.Errorf") {
+ continue
+ }
+
+ k, ok := call.Common().Args[0].(*ssa.Const)
+ if !ok {
+ continue
+ }
+
+ s := constant.StringVal(k.Value)
+ if len(s) == 0 {
+ continue
+ }
+ switch s[len(s)-1] {
+ case '.', ':', '!', '\n':
+ pass.Reportf(call.Pos(), "error strings should not end with punctuation or a newline")
+ }
+ idx := strings.IndexByte(s, ' ')
+ if idx == -1 {
+ // single word error message, probably not a real
+ // error but something used in tests or during
+ // debugging
+ continue
+ }
+ word := s[:idx]
+ first, n := utf8.DecodeRuneInString(word)
+ if !unicode.IsUpper(first) {
+ continue
+ }
+ for _, c := range word[n:] {
+ if unicode.IsUpper(c) {
+ // Word is probably an initialism or
+ // multi-word function name
+ continue instrLoop
+ }
+ }
+
+ word = strings.TrimRightFunc(word, func(r rune) bool { return unicode.IsPunct(r) })
+ if objNames[fn.Package()][word] {
+ // Word is probably the name of a function or type in this package
+ continue
+ }
+ // First word in error starts with a capital
+ // letter, and the word doesn't contain any other
+ // capitals, making it unlikely to be an
+ // initialism or multi-word function name.
+ //
+ // It could still be a proper noun, though.
+
+ pass.Reportf(call.Pos(), "error strings should not be capitalized")
+ }
+ }
+ }
+ return nil, nil
+}
+
+func CheckTimeNames(pass *analysis.Pass) (interface{}, error) {
+ suffixes := []string{
+ "Sec", "Secs", "Seconds",
+ "Msec", "Msecs",
+ "Milli", "Millis", "Milliseconds",
+ "Usec", "Usecs", "Microseconds",
+ "MS", "Ms",
+ }
+ fn := func(T types.Type, names []*ast.Ident) {
+ if !IsType(T, "time.Duration") && !IsType(T, "*time.Duration") {
+ return
+ }
+ for _, name := range names {
+ for _, suffix := range suffixes {
+ if strings.HasSuffix(name.Name, 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 pass.Files {
+ ast.Inspect(f, func(node ast.Node) bool {
+ switch node := node.(type) {
+ case *ast.ValueSpec:
+ T := pass.TypesInfo.TypeOf(node.Type)
+ fn(T, node.Names)
+ case *ast.FieldList:
+ for _, field := range node.List {
+ T := pass.TypesInfo.TypeOf(field.Type)
+ fn(T, field.Names)
+ }
+ }
+ return true
+ })
+ }
+ return nil, nil
+}
+
+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 {
+ continue
+ }
+ for _, spec := range gen.Specs {
+ spec := spec.(*ast.ValueSpec)
+ if len(spec.Names) != len(spec.Values) {
+ continue
+ }
+
+ for i, name := range spec.Names {
+ val := spec.Values[i]
+ if !IsCallToAST(pass, val, "errors.New") && !IsCallToAST(pass, val, "fmt.Errorf") {
+ continue
+ }
+
+ prefix := "err"
+ if name.IsExported() {
+ prefix = "Err"
+ }
+ if !strings.HasPrefix(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{
+ 100: "StatusContinue",
+ 101: "StatusSwitchingProtocols",
+ 102: "StatusProcessing",
+ 200: "StatusOK",
+ 201: "StatusCreated",
+ 202: "StatusAccepted",
+ 203: "StatusNonAuthoritativeInfo",
+ 204: "StatusNoContent",
+ 205: "StatusResetContent",
+ 206: "StatusPartialContent",
+ 207: "StatusMultiStatus",
+ 208: "StatusAlreadyReported",
+ 226: "StatusIMUsed",
+ 300: "StatusMultipleChoices",
+ 301: "StatusMovedPermanently",
+ 302: "StatusFound",
+ 303: "StatusSeeOther",
+ 304: "StatusNotModified",
+ 305: "StatusUseProxy",
+ 307: "StatusTemporaryRedirect",
+ 308: "StatusPermanentRedirect",
+ 400: "StatusBadRequest",
+ 401: "StatusUnauthorized",
+ 402: "StatusPaymentRequired",
+ 403: "StatusForbidden",
+ 404: "StatusNotFound",
+ 405: "StatusMethodNotAllowed",
+ 406: "StatusNotAcceptable",
+ 407: "StatusProxyAuthRequired",
+ 408: "StatusRequestTimeout",
+ 409: "StatusConflict",
+ 410: "StatusGone",
+ 411: "StatusLengthRequired",
+ 412: "StatusPreconditionFailed",
+ 413: "StatusRequestEntityTooLarge",
+ 414: "StatusRequestURITooLong",
+ 415: "StatusUnsupportedMediaType",
+ 416: "StatusRequestedRangeNotSatisfiable",
+ 417: "StatusExpectationFailed",
+ 418: "StatusTeapot",
+ 422: "StatusUnprocessableEntity",
+ 423: "StatusLocked",
+ 424: "StatusFailedDependency",
+ 426: "StatusUpgradeRequired",
+ 428: "StatusPreconditionRequired",
+ 429: "StatusTooManyRequests",
+ 431: "StatusRequestHeaderFieldsTooLarge",
+ 451: "StatusUnavailableForLegalReasons",
+ 500: "StatusInternalServerError",
+ 501: "StatusNotImplemented",
+ 502: "StatusBadGateway",
+ 503: "StatusServiceUnavailable",
+ 504: "StatusGatewayTimeout",
+ 505: "StatusHTTPVersionNotSupported",
+ 506: "StatusVariantAlsoNegotiates",
+ 507: "StatusInsufficientStorage",
+ 508: "StatusLoopDetected",
+ 510: "StatusNotExtended",
+ 511: "StatusNetworkAuthenticationRequired",
+}
+
+func CheckHTTPStatusCodes(pass *analysis.Pass) (interface{}, error) {
+ whitelist := map[string]bool{}
+ 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(pass, call) {
+ case "net/http.Error":
+ arg = 2
+ case "net/http.Redirect":
+ arg = 3
+ case "net/http.StatusText":
+ arg = 0
+ case "net/http.RedirectHandler":
+ arg = 1
+ default:
+ return true
+ }
+ lit, ok := call.Args[arg].(*ast.BasicLit)
+ if !ok {
+ return true
+ }
+ if whitelist[lit.Value] {
+ return true
+ }
+
+ n, err := strconv.Atoi(lit.Value)
+ if err != nil {
+ return true
+ }
+ s, ok := httpStatusCodes[n]
+ if !ok {
+ return true
+ }
+ ReportNodefFG(pass, lit, "should use constant http.%s instead of numeric literal %d", s, n)
+ return true
+ }
+ // OPT(dh): replace with inspector
+ for _, f := range pass.Files {
+ ast.Inspect(f, fn)
+ }
+ return nil, nil
+}
+
+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 {
+ ReportNodefFG(pass, c, "default case should be first or last in switch statement")
+ break
+ }
+ }
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.SwitchStmt)(nil)}, fn)
+ return nil, nil
+}
+
+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 {
+ return
+ }
+ if _, ok := cond.X.(*ast.BasicLit); !ok {
+ return
+ }
+ if _, ok := cond.Y.(*ast.BasicLit); ok {
+ // Don't flag lit == lit conditions, just in case
+ return
+ }
+ ReportNodefFG(pass, cond, "don't use Yoda conditions")
+ }
+ pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn)
+ return nil, nil
+}
+
+func CheckInvisibleCharacters(pass *analysis.Pass) (interface{}, error) {
+ fn := func(node ast.Node) {
+ lit := node.(*ast.BasicLit)
+ if lit.Kind != token.STRING {
+ return
+ }
+ for _, r := range lit.Value {
+ if unicode.Is(unicode.Cf, 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' {
+ ReportNodef(pass, lit, "string literal contains the Unicode control character %U, consider using the %q escape sequence", r, r)
+ }
+ }
+ }
+ 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
new file mode 100644
index 000000000..160f9d7ff
--- /dev/null
+++ b/vendor/honnef.co/go/tools/stylecheck/names.go
@@ -0,0 +1,264 @@
+// Copyright (c) 2013 The Go Authors. All rights reserved.
+// Copyright (c) 2018 Dominik Honnef. All rights reserved.
+
+package stylecheck
+
+import (
+ "go/ast"
+ "go/token"
+ "strings"
+ "unicode"
+
+ "golang.org/x/tools/go/analysis"
+ "honnef.co/go/tools/config"
+ . "honnef.co/go/tools/lint/lintdsl"
+)
+
+// knownNameExceptions is a set of names that are known to be exempt from naming checks.
+// This is usually because they are constrained by having to match names in the
+// standard library.
+var knownNameExceptions = map[string]bool{
+ "LastInsertId": true, // must match database/sql
+ "kWh": true,
+}
+
+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.
+
+ allCaps := func(s string) bool {
+ for _, r := range s {
+ if !((r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_') {
+ return false
+ }
+ }
+ return true
+ }
+
+ check := func(id *ast.Ident, thing string, initialisms map[string]bool) {
+ if id.Name == "_" {
+ return
+ }
+ if knownNameExceptions[id.Name] {
+ return
+ }
+
+ // 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, "_") {
+ ReportfFG(pass, id.Pos(), "should not use ALL_CAPS in Go names; use CamelCase instead")
+ return
+ }
+
+ should := lintName(id.Name, initialisms)
+ if id.Name == should {
+ return
+ }
+
+ if len(id.Name) > 2 && strings.Contains(id.Name[1:len(id.Name)-1], "_") {
+ ReportfFG(pass, id.Pos(), "should not use underscores in Go names; %s %s should be %s", thing, id.Name, should)
+ return
+ }
+ 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 {
+ return
+ }
+ for _, f := range fl.List {
+ for _, id := range f.Names {
+ check(id, thing, initialisms)
+ }
+ }
+ }
+
+ il := config.For(pass).Initialisms
+ initialisms := make(map[string]bool, len(il))
+ for _, word := range il {
+ initialisms[word] = true
+ }
+ 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, "_") {
+ ReportfFG(pass, f.Pos(), "should not use underscores in package names")
+ }
+ if strings.IndexFunc(f.Name.Name, unicode.IsUpper) != -1 {
+ 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 {
+ switch v := node.(type) {
+ case *ast.AssignStmt:
+ if v.Tok != token.DEFINE {
+ return true
+ }
+ for _, exp := range v.Lhs {
+ if id, ok := exp.(*ast.Ident); ok {
+ check(id, "var", initialisms)
+ }
+ }
+ case *ast.FuncDecl:
+ // Functions with no body are defined elsewhere (in
+ // assembly, or via go:linkname). These are likely to
+ // be something very low level (such as the runtime),
+ // where our rules don't apply.
+ if v.Body == nil {
+ return true
+ }
+
+ if IsInTest(pass, v) && (strings.HasPrefix(v.Name.Name, "Example") || strings.HasPrefix(v.Name.Name, "Test") || strings.HasPrefix(v.Name.Name, "Benchmark")) {
+ return true
+ }
+
+ thing := "func"
+ if v.Recv != nil {
+ thing = "method"
+ }
+
+ if !isTechnicallyExported(v) {
+ check(v.Name, thing, initialisms)
+ }
+
+ checkList(v.Type.Params, thing+" parameter", initialisms)
+ checkList(v.Type.Results, thing+" result", initialisms)
+ case *ast.GenDecl:
+ if v.Tok == token.IMPORT {
+ return true
+ }
+ var thing string
+ switch v.Tok {
+ case token.CONST:
+ thing = "const"
+ case token.TYPE:
+ thing = "type"
+ case token.VAR:
+ thing = "var"
+ }
+ for _, spec := range v.Specs {
+ switch s := spec.(type) {
+ case *ast.TypeSpec:
+ check(s.Name, thing, initialisms)
+ case *ast.ValueSpec:
+ for _, id := range s.Names {
+ check(id, thing, initialisms)
+ }
+ }
+ }
+ case *ast.InterfaceType:
+ // Do not check interface method names.
+ // They are often constrainted by the method names of concrete types.
+ for _, x := range v.Methods.List {
+ ft, ok := x.Type.(*ast.FuncType)
+ if !ok { // might be an embedded interface name
+ continue
+ }
+ checkList(ft.Params, "interface method parameter", initialisms)
+ checkList(ft.Results, "interface method result", initialisms)
+ }
+ case *ast.RangeStmt:
+ if v.Tok == token.ASSIGN {
+ return true
+ }
+ if id, ok := v.Key.(*ast.Ident); ok {
+ check(id, "range var", initialisms)
+ }
+ if id, ok := v.Value.(*ast.Ident); ok {
+ check(id, "range var", initialisms)
+ }
+ case *ast.StructType:
+ for _, f := range v.Fields.List {
+ for _, id := range f.Names {
+ check(id, "struct field", initialisms)
+ }
+ }
+ }
+ return true
+ })
+ }
+ return nil, nil
+}
+
+// lintName returns a different name if it should be different.
+func lintName(name string, initialisms map[string]bool) (should string) {
+ // 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.
+
+ // Fast path for simple cases: "_" and all lowercase.
+ if name == "_" {
+ return name
+ }
+ if strings.IndexFunc(name, func(r rune) bool { return !unicode.IsLower(r) }) == -1 {
+ return name
+ }
+
+ // Split camelCase at any lower->upper transition, and split on underscores.
+ // Check each word for common initialisms.
+ runes := []rune(name)
+ w, i := 0, 0 // index of start of word, scan
+ for i+1 <= len(runes) {
+ eow := false // whether we hit the end of a word
+ if i+1 == len(runes) {
+ eow = true
+ } else if runes[i+1] == '_' && i+1 != len(runes)-1 {
+ // underscore; shift the remainder forward over any run of underscores
+ eow = true
+ n := 1
+ for i+n+1 < len(runes) && runes[i+n+1] == '_' {
+ n++
+ }
+
+ // Leave at most one underscore if the underscore is between two digits
+ if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {
+ n--
+ }
+
+ copy(runes[i+1:], runes[i+n+1:])
+ runes = runes[:len(runes)-n]
+ } else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {
+ // lower->non-lower
+ eow = true
+ }
+ i++
+ if !eow {
+ continue
+ }
+
+ // [w,i) is a word.
+ word := string(runes[w:i])
+ if u := strings.ToUpper(word); initialisms[u] {
+ // Keep consistent case, which is lowercase only at the start.
+ if w == 0 && unicode.IsLower(runes[w]) {
+ u = strings.ToLower(u)
+ }
+ // All the common initialisms are ASCII,
+ // so we can replace the bytes exactly.
+ // TODO(dh): this won't be true once we allow custom initialisms
+ copy(runes[w:], []rune(u))
+ } else if w > 0 && strings.ToLower(word) == word {
+ // already all lowercase, and not the first word, so uppercase the first character.
+ runes[w] = unicode.ToUpper(runes[w])
+ }
+ w = i
+ }
+ return string(runes)
+}
+
+func isTechnicallyExported(f *ast.FuncDecl) bool {
+ if f.Recv != nil || f.Doc == nil {
+ return false
+ }
+
+ const export = "//export "
+ const linkname = "//go:linkname "
+ for _, c := range f.Doc.List {
+ if strings.HasPrefix(c.Text, export) && len(c.Text) == len(export)+len(f.Name.Name) && c.Text[len(export):] == f.Name.Name {
+ return true
+ }
+
+ if strings.HasPrefix(c.Text, linkname) {
+ return true
+ }
+ }
+ return false
+}
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 000000000..02e0d09cf
--- /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 000000000..7629636cf
--- /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/implements.go b/vendor/honnef.co/go/tools/unused/implements.go
new file mode 100644
index 000000000..835baac69
--- /dev/null
+++ b/vendor/honnef.co/go/tools/unused/implements.go
@@ -0,0 +1,82 @@
+package unused
+
+import "go/types"
+
+// lookupMethod returns the index of and method with matching package and name, or (-1, nil).
+func lookupMethod(T *types.Interface, pkg *types.Package, name string) (int, *types.Func) {
+ if name != "_" {
+ for i := 0; i < T.NumMethods(); i++ {
+ m := T.Method(i)
+ if sameId(m, pkg, name) {
+ return i, m
+ }
+ }
+ }
+ return -1, nil
+}
+
+func sameId(obj types.Object, pkg *types.Package, name string) bool {
+ // spec:
+ // "Two identifiers are different if they are spelled differently,
+ // or if they appear in different packages and are not exported.
+ // Otherwise, they are the same."
+ if name != obj.Name() {
+ return false
+ }
+ // obj.Name == name
+ if obj.Exported() {
+ return true
+ }
+ // not exported, so packages must be the same (pkg == nil for
+ // fields in Universe scope; this can only happen for types
+ // introduced via Eval)
+ if pkg == nil || obj.Pkg() == nil {
+ return pkg == obj.Pkg()
+ }
+ // pkg != nil && obj.pkg != nil
+ return pkg.Path() == obj.Pkg().Path()
+}
+
+func (g *Graph) implements(V types.Type, T *types.Interface, msV *types.MethodSet) ([]*types.Selection, bool) {
+ // fast path for common case
+ if T.Empty() {
+ return nil, true
+ }
+
+ if ityp, _ := V.Underlying().(*types.Interface); ityp != nil {
+ // TODO(dh): is this code reachable?
+ for i := 0; i < T.NumMethods(); i++ {
+ m := T.Method(i)
+ _, obj := lookupMethod(ityp, m.Pkg(), m.Name())
+ switch {
+ case obj == nil:
+ return nil, false
+ case !types.Identical(obj.Type(), m.Type()):
+ return nil, false
+ }
+ }
+ return nil, true
+ }
+
+ // A concrete type implements T if it implements all methods of T.
+ var sels []*types.Selection
+ for i := 0; i < T.NumMethods(); i++ {
+ m := T.Method(i)
+ sel := msV.Lookup(m.Pkg(), m.Name())
+ if sel == nil {
+ return nil, false
+ }
+
+ f, _ := sel.Obj().(*types.Func)
+ if f == nil {
+ return nil, false
+ }
+
+ if !types.Identical(f.Type(), m.Type()) {
+ return nil, false
+ }
+
+ sels = append(sels, sel)
+ }
+ return sels, true
+}
diff --git a/vendor/honnef.co/go/tools/unused/unused.go b/vendor/honnef.co/go/tools/unused/unused.go
new file mode 100644
index 000000000..152d3692d
--- /dev/null
+++ b/vendor/honnef.co/go/tools/unused/unused.go
@@ -0,0 +1,1964 @@
+package unused
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "go/types"
+ "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
+// used.
+
+// TODO(dh): we cannot observe function calls in assembly files.
+
+/*
+
+- packages use:
+ - (1.1) exported named types (unless in package main)
+ - (1.2) exported functions (unless in package main)
+ - (1.3) exported variables (unless in package main)
+ - (1.4) exported constants (unless in package main)
+ - (1.5) init functions
+ - (1.6) functions exported to cgo
+ - (1.7) the main function iff in the main package
+ - (1.8) symbols linked via go:linkname
+
+- named types use:
+ - (2.1) exported methods
+ - (2.2) the type they're based on
+ - (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
+
+- functions use:
+ - (4.1) all their arguments, return parameters and receivers
+ - (4.2) anonymous functions defined beneath them
+ - (4.3) closures and bound methods.
+ this implements a simplified model where a function is used merely by being referenced, even if it is never called.
+ that way we don't have to keep track of closures escaping functions.
+ - (4.4) functions they return. we assume that someone else will call the returned function
+ - (4.5) functions/interface methods they call
+ - types they instantiate or convert to
+ - (4.7) fields they access
+ - (4.8) types of all instructions
+ - (4.9) package-level variables they assign to iff in tests (sinks for benchmarks)
+
+- conversions use:
+ - (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.
+ - (5.2) when converting to or from unsafe.Pointer, mark all fields as used.
+
+- structs use:
+ - (6.1) fields of type NoCopy sentinel
+ - (6.2) exported fields
+ - (6.3) embedded fields that help implement interfaces (either fully implements it, or contributes required methods) (recursively)
+ - (6.4) embedded fields that have exported methods (recursively)
+ - (6.5) embedded structs that have exported fields (recursively)
+
+- (7.1) field accesses use fields
+- (7.2) fields use their types
+
+- (8.0) How we handle interfaces:
+ - (8.1) We do not technically care about interfaces that only consist of
+ exported methods. Exported methods on concrete types are always
+ marked as used.
+ - Any concrete type implements all known interfaces. Even if it isn't
+ assigned to any interfaces in our code, the user may receive a value
+ of the type and expect to pass it back to us through an interface.
+
+ Concrete types use their methods that implement interfaces. If the
+ type is used, it uses those methods. Otherwise, it doesn't. This
+ way, types aren't incorrectly marked reachable through the edge
+ from method to type.
+
+ - (8.3) All interface methods are marked as used, even if they never get
+ called. This is to accomodate sum types (unexported interface
+ method that must exist but never gets called.)
+
+ - (8.4) All embedded interfaces are marked as used. This is an
+ extension of 8.3, but we have to explicitly track embedded
+ interfaces because in a chain C->B->A, B wouldn't be marked as
+ used by 8.3 just because it contributes A's methods to C.
+
+- Inherent uses:
+ - thunks and other generated wrappers call the real function
+ - (9.2) variables use their types
+ - (9.3) types use their underlying and element types
+ - (9.4) conversions use the type they convert to
+ - (9.5) instructions use their operands
+ - (9.6) instructions use their operands' types
+ - (9.7) variable _reads_ use variables, writes do not, except in tests
+ - (9.8) runtime functions that may be called from user code via the compiler
+
+
+- const groups:
+ (10.1) if one constant out of a block of constants is used, mark all
+ of them used. a lot of the time, unused constants exist for the sake
+ of completeness. See also
+ 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:
+ - (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
+ that all exported fields are used. We also maintain a list of
+ known reflection-based method callers.
+
+*/
+
+func assert(b bool) {
+ if !b {
+ panic("failed assertion")
+ }
+}
+
+func typString(obj types.Object) string {
+ switch obj := obj.(type) {
+ case *types.Func:
+ return "func"
+ case *types.Var:
+ if obj.IsField() {
+ return "field"
+ }
+ return "var"
+ case *types.Const:
+ return "const"
+ case *types.TypeName:
+ return "type"
+ default:
+ return "identifier"
+ }
+}
+
+// /usr/lib/go/src/runtime/proc.go:433:6: func badmorestackg0 is unused (U1000)
+
+// Functions defined in the Go runtime that may be called through
+// compiler magic or via assembly.
+var runtimeFuncs = map[string]bool{
+ // The first part of the list is copied from
+ // cmd/compile/internal/gc/builtin.go, var runtimeDecls
+ "newobject": true,
+ "panicindex": true,
+ "panicslice": true,
+ "panicdivide": true,
+ "panicmakeslicelen": true,
+ "throwinit": true,
+ "panicwrap": true,
+ "gopanic": true,
+ "gorecover": true,
+ "goschedguarded": true,
+ "printbool": true,
+ "printfloat": true,
+ "printint": true,
+ "printhex": true,
+ "printuint": true,
+ "printcomplex": true,
+ "printstring": true,
+ "printpointer": true,
+ "printiface": true,
+ "printeface": true,
+ "printslice": true,
+ "printnl": true,
+ "printsp": true,
+ "printlock": true,
+ "printunlock": true,
+ "concatstring2": true,
+ "concatstring3": true,
+ "concatstring4": true,
+ "concatstring5": true,
+ "concatstrings": true,
+ "cmpstring": true,
+ "intstring": true,
+ "slicebytetostring": true,
+ "slicebytetostringtmp": true,
+ "slicerunetostring": true,
+ "stringtoslicebyte": true,
+ "stringtoslicerune": true,
+ "slicecopy": true,
+ "slicestringcopy": true,
+ "decoderune": true,
+ "countrunes": true,
+ "convI2I": true,
+ "convT16": true,
+ "convT32": true,
+ "convT64": true,
+ "convTstring": true,
+ "convTslice": true,
+ "convT2E": true,
+ "convT2Enoptr": true,
+ "convT2I": true,
+ "convT2Inoptr": true,
+ "assertE2I": true,
+ "assertE2I2": true,
+ "assertI2I": true,
+ "assertI2I2": true,
+ "panicdottypeE": true,
+ "panicdottypeI": true,
+ "panicnildottype": true,
+ "ifaceeq": true,
+ "efaceeq": true,
+ "fastrand": true,
+ "makemap64": true,
+ "makemap": true,
+ "makemap_small": true,
+ "mapaccess1": true,
+ "mapaccess1_fast32": true,
+ "mapaccess1_fast64": true,
+ "mapaccess1_faststr": true,
+ "mapaccess1_fat": true,
+ "mapaccess2": true,
+ "mapaccess2_fast32": true,
+ "mapaccess2_fast64": true,
+ "mapaccess2_faststr": true,
+ "mapaccess2_fat": true,
+ "mapassign": true,
+ "mapassign_fast32": true,
+ "mapassign_fast32ptr": true,
+ "mapassign_fast64": true,
+ "mapassign_fast64ptr": true,
+ "mapassign_faststr": true,
+ "mapiterinit": true,
+ "mapdelete": true,
+ "mapdelete_fast32": true,
+ "mapdelete_fast64": true,
+ "mapdelete_faststr": true,
+ "mapiternext": true,
+ "mapclear": true,
+ "makechan64": true,
+ "makechan": true,
+ "chanrecv1": true,
+ "chanrecv2": true,
+ "chansend1": true,
+ "closechan": true,
+ "writeBarrier": true,
+ "typedmemmove": true,
+ "typedmemclr": true,
+ "typedslicecopy": true,
+ "selectnbsend": true,
+ "selectnbrecv": true,
+ "selectnbrecv2": true,
+ "selectsetpc": true,
+ "selectgo": true,
+ "block": true,
+ "makeslice": true,
+ "makeslice64": true,
+ "growslice": true,
+ "memmove": true,
+ "memclrNoHeapPointers": true,
+ "memclrHasPointers": true,
+ "memequal": true,
+ "memequal8": true,
+ "memequal16": true,
+ "memequal32": true,
+ "memequal64": true,
+ "memequal128": true,
+ "int64div": true,
+ "uint64div": true,
+ "int64mod": true,
+ "uint64mod": true,
+ "float64toint64": true,
+ "float64touint64": true,
+ "float64touint32": true,
+ "int64tofloat64": true,
+ "uint64tofloat64": true,
+ "uint32tofloat64": true,
+ "complex128div": true,
+ "racefuncenter": true,
+ "racefuncenterfp": true,
+ "racefuncexit": true,
+ "raceread": true,
+ "racewrite": true,
+ "racereadrange": true,
+ "racewriterange": true,
+ "msanread": true,
+ "msanwrite": true,
+ "x86HasPOPCNT": true,
+ "x86HasSSE41": true,
+ "arm64HasATOMICS": true,
+
+ // The second part of the list is extracted from assembly code in
+ // the standard library, with the exception of the runtime package itself
+ "abort": true,
+ "aeshashbody": true,
+ "args": true,
+ "asminit": true,
+ "badctxt": true,
+ "badmcall2": true,
+ "badmcall": true,
+ "badmorestackg0": true,
+ "badmorestackgsignal": true,
+ "badsignal2": true,
+ "callbackasm1": true,
+ "callCfunction": true,
+ "cgocallback_gofunc": true,
+ "cgocallbackg": true,
+ "checkgoarm": true,
+ "check": true,
+ "debugCallCheck": true,
+ "debugCallWrap": true,
+ "emptyfunc": true,
+ "entersyscall": true,
+ "exit": true,
+ "exits": true,
+ "exitsyscall": true,
+ "externalthreadhandler": true,
+ "findnull": true,
+ "goexit1": true,
+ "gostring": true,
+ "i386_set_ldt": true,
+ "_initcgo": true,
+ "init_thread_tls": true,
+ "ldt0setup": true,
+ "libpreinit": true,
+ "load_g": true,
+ "morestack": true,
+ "mstart": true,
+ "nacl_sysinfo": true,
+ "nanotimeQPC": true,
+ "nanotime": true,
+ "newosproc0": true,
+ "newproc": true,
+ "newstack": true,
+ "noted": true,
+ "nowQPC": true,
+ "osinit": true,
+ "printf": true,
+ "racecallback": true,
+ "reflectcallmove": true,
+ "reginit": true,
+ "rt0_go": true,
+ "save_g": true,
+ "schedinit": true,
+ "setldt": true,
+ "settls": true,
+ "sighandler": true,
+ "sigprofNonGo": true,
+ "sigtrampgo": true,
+ "_sigtramp": true,
+ "sigtramp": true,
+ "stackcheck": true,
+ "syscall_chdir": true,
+ "syscall_chroot": true,
+ "syscall_close": true,
+ "syscall_dup2": true,
+ "syscall_execve": true,
+ "syscall_exit": true,
+ "syscall_fcntl": true,
+ "syscall_forkx": true,
+ "syscall_gethostname": true,
+ "syscall_getpid": true,
+ "syscall_ioctl": true,
+ "syscall_pipe": true,
+ "syscall_rawsyscall6": true,
+ "syscall_rawSyscall6": true,
+ "syscall_rawsyscall": true,
+ "syscall_RawSyscall": true,
+ "syscall_rawsysvicall6": true,
+ "syscall_setgid": true,
+ "syscall_setgroups": true,
+ "syscall_setpgid": true,
+ "syscall_setsid": true,
+ "syscall_setuid": true,
+ "syscall_syscall6": true,
+ "syscall_syscall": true,
+ "syscall_Syscall": true,
+ "syscall_sysvicall6": true,
+ "syscall_wait4": true,
+ "syscall_write": true,
+ "traceback": true,
+ "tstart": true,
+ "usplitR0": true,
+ "wbBufFlush": true,
+ "write": true,
+}
+
+type pkg struct {
+ Fset *token.FileSet
+ Files []*ast.File
+ Pkg *types.Package
+ TypesInfo *types.Info
+ TypesSizes types.Sizes
+ SSA *ssa.Package
+ SrcFuncs []*ssa.Function
+}
+
+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
+ }
+ 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())
+ }
+ }
+ }
+
+ 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{}) {
+ if c.Debug != nil {
+ fmt.Fprintf(c.Debug, f, v...)
+ }
+}
+
+func (graph *Graph) quieten(node *Node) {
+ if node.seen {
+ return
+ }
+ switch obj := node.obj.(type) {
+ case *types.Named:
+ for i := 0; i < obj.NumMethods(); i++ {
+ m := obj.Method(i)
+ if node, ok := graph.nodeMaybe(m); ok {
+ node.quiet = true
+ }
+ }
+ case *types.Struct:
+ for i := 0; i < obj.NumFields(); i++ {
+ if node, ok := graph.nodeMaybe(obj.Field(i)); ok {
+ node.quiet = true
+ }
+ }
+ case *types.Interface:
+ for i := 0; i < obj.NumExplicitMethods(); i++ {
+ m := obj.ExplicitMethod(i)
+ if node, ok := graph.nodeMaybe(m); ok {
+ node.quiet = true
+ }
+ }
+ }
+}
+
+func (c *Checker) results() []types.Object {
+ if c.graph == nil {
+ // We never analyzed any packages
+ return nil
+ }
+
+ var out []types.Object
+
+ if c.WholeProgram {
+ var ifaces []*types.Interface
+ var notIfaces []types.Type
+
+ // implement as many interfaces as possible
+ c.graph.seenTypes.Iterate(func(t types.Type, _ interface{}) {
+ switch t := t.(type) {
+ case *types.Interface:
+ if t.NumMethods() > 0 {
+ ifaces = append(ifaces, t)
+ }
+ default:
+ if _, ok := t.Underlying().(*types.Interface); !ok {
+ notIfaces = append(notIfaces, t)
+ }
+ }
+ })
+
+ 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 {
+ // 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 := c.graph.implements(t, iface, ms); ok {
+ for _, sel := range sels {
+ c.graph.useMethod(ctx, t, sel, t, edgeImplements)
+ }
+ }
+ }
+ }
+ }
+
+ if c.Debug != nil {
+ debugNode := func(node *Node) {
+ if node.obj == nil {
+ c.debugf("n%d [label=\"Root\"];\n", node.id)
+ } else {
+ c.debugf("n%d [label=%q];\n", node.id, fmt.Sprintf("(%T) %s", node.obj, node.obj))
+ }
+ 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{}
+ 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)
+ }
+ }
+ }
+ }
+ }
+ return true
+ })
+ }
+
+ 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 {
+ ctx.see(m.Object())
+ if g.trackExportedIdentifier(ctx, m.Object()) {
+ // (1.3) packages use exported variables (unless in package main)
+ ctx.use(m.Object(), nil, edgeExportedVariable)
+ }
+ }
+ case *ssa.Function:
+ mObj := owningObject(m)
+ if mObj != nil {
+ ctx.see(mObj)
+ }
+ //lint:ignore SA9003 handled implicitly
+ if m.Name() == "init" {
+ // (1.5) packages use init functions
+ //
+ // 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(ctx, m.Object()) {
+ // (1.2) packages use exported functions (unless in package main)
+ ctx.use(mObj, nil, edgeExportedFunction)
+ }
+ if m.Name() == "main" && pkg.Pkg.Name() == "main" {
+ // (1.7) packages use the main function iff in the main package
+ ctx.use(mObj, nil, edgeMainFunction)
+ }
+ if pkg.Pkg.Path() == "runtime" && runtimeFuncs[m.Name()] {
+ // (9.8) runtime functions that may be called from user code via the compiler
+ ctx.use(mObj, nil, edgeRuntimeFunction)
+ }
+ if m.Syntax() != nil {
+ doc := m.Syntax().(*ast.FuncDecl).Doc
+ if doc != nil {
+ for _, cmt := range doc.List {
+ if strings.HasPrefix(cmt.Text, "//go:cgo_export_") {
+ // (1.6) packages use functions exported to cgo
+ ctx.use(mObj, nil, edgeCgoExported)
+ }
+ }
+ }
+ }
+ g.function(ctx, m)
+ case *ssa.Type:
+ if m.Object() != nil {
+ ctx.see(m.Object())
+ if g.trackExportedIdentifier(ctx, m.Object()) {
+ // (1.1) packages use exported named types (unless in package main)
+ ctx.use(m.Object(), nil, edgeExportedType)
+ }
+ }
+ g.typ(ctx, m.Type(), nil)
+ default:
+ panic(fmt.Sprintf("unreachable: %T", m))
+ }
+ }
+
+ if !g.wholeProgram {
+ // 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
+ // actually care about all interfaces, not just the subset
+ // that has unexported methods.
+
+ var ifaces []*types.Interface
+ var notIfaces []types.Type
+
+ 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
+ ifaces = append(ifaces, t)
+ default:
+ if _, ok := t.Underlying().(*types.Interface); !ok {
+ notIfaces = append(notIfaces, t)
+ }
+ }
+ })
+
+ // (8.0) handle interfaces
+ for _, t := range notIfaces {
+ 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(ctx, t, sel, t, edgeImplements)
+ }
+ }
+ }
+ }
+ }
+}
+
+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)
+ if len(path) > 1 {
+ base := lintdsl.Dereference(t).Underlying().(*types.Struct)
+ for _, idx := range path[:len(path)-1] {
+ next := base.Field(idx)
+ // (6.3) structs use embedded fields that help implement interfaces
+ ctx.see(base)
+ ctx.seeAndUse(next, base, edgeProvidesMethod)
+ base, _ = lintdsl.Dereference(next.Type()).Underlying().(*types.Struct)
+ }
+ }
+ ctx.seeAndUse(obj, by, kind)
+}
+
+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
+ }
+
+ name := fn.RelString(nil)
+ if _, ok := ctx.seenFns[name]; ok {
+ return
+ }
+ ctx.seenFns[name] = struct{}{}
+
+ // (4.1) functions use all their arguments, return parameters and receivers
+ 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
+ //
+ // 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(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() != ctx.pkg.Pkg {
+ return
+ }
+ }
+
+ if g.wholeProgram {
+ g.mu.Lock()
+ }
+ ctx.seenTypes.Set(t, struct{}{})
+ if g.wholeProgram {
+ g.mu.Unlock()
+ }
+ if isIrrelevant(t) {
+ return
+ }
+
+ ctx.see(t)
+ switch t := t.(type) {
+ case *types.Struct:
+ for i := 0; i < t.NumFields(); i++ {
+ ctx.see(t.Field(i))
+ if t.Field(i).Exported() {
+ // (6.2) structs use exported fields
+ ctx.use(t.Field(i), t, edgeExportedField)
+ } else if t.Field(i).Name() == "_" {
+ ctx.use(t.Field(i), t, edgeBlankField)
+ } else if isNoCopyType(t.Field(i).Type()) {
+ // (6.1) structs use fields of type 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.
+ if !g.wholeProgram {
+ // does the embedded field contribute exported methods to the method set?
+ T := t.Field(i).Type()
+ if _, ok := T.Underlying().(*types.Pointer); !ok {
+ // An embedded field is addressable, so check
+ // the pointer type to get the full method set
+ T = types.NewPointer(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)
+ ctx.use(t.Field(i), t, edgeExtendsExportedMethodSet)
+ break
+ }
+ }
+ }
+
+ seen := map[*types.Struct]struct{}{}
+ var hasExportedField func(t types.Type) bool
+ hasExportedField = func(T types.Type) bool {
+ t, ok := lintdsl.Dereference(T).Underlying().(*types.Struct)
+ if !ok {
+ return false
+ }
+ if _, ok := seen[t]; ok {
+ return false
+ }
+ seen[t] = struct{}{}
+ for i := 0; i < t.NumFields(); i++ {
+ field := t.Field(i)
+ if field.Exported() {
+ return true
+ }
+ if field.Embedded() && hasExportedField(field.Type()) {
+ return true
+ }
+ }
+ return false
+ }
+ // does the embedded field contribute exported fields?
+ if hasExportedField(t.Field(i).Type()) {
+ // (6.5) structs use embedded structs that have exported fields (recursively)
+ ctx.use(t.Field(i), t, edgeExtendsExportedFields)
+ }
+
+ }
+ g.variable(ctx, t.Field(i))
+ }
+ case *types.Basic:
+ // Nothing to do
+ case *types.Named:
+ // (9.3) types use their underlying and element types
+ ctx.seeAndUse(t.Underlying(), t, edgeUnderlyingType)
+ ctx.seeAndUse(t.Obj(), t, edgeTypeName)
+ ctx.seeAndUse(t, t.Obj(), edgeNamedType)
+
+ // (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)
+ }
+
+ 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
+ ctx.seeAndUse(t.Elem(), t, edgeElementType)
+ g.typ(ctx, t.Elem(), nil)
+ case *types.Map:
+ // (9.3) types use their underlying and element types
+ ctx.seeAndUse(t.Elem(), t, edgeElementType)
+ // (9.3) types use their underlying and element types
+ ctx.seeAndUse(t.Key(), t, edgeKeyType)
+ g.typ(ctx, t.Elem(), nil)
+ g.typ(ctx, t.Key(), nil)
+ case *types.Signature:
+ 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
+ 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
+ ctx.seeAndUse(tt, t, edgeEmbeddedInterface)
+ }
+ case *types.Array:
+ // (9.3) types use their underlying and element types
+ ctx.seeAndUse(t.Elem(), t, edgeElementType)
+ g.typ(ctx, t.Elem(), nil)
+ case *types.Pointer:
+ // (9.3) types use their underlying and element types
+ ctx.seeAndUse(t.Elem(), t, edgeElementType)
+ g.typ(ctx, t.Elem(), nil)
+ case *types.Chan:
+ // (9.3) types use their underlying and element types
+ 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
+ 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(ctx *context, v *types.Var) {
+ // (9.2) variables use their types
+ ctx.seeAndUse(v.Type(), v, edgeType)
+ g.typ(ctx, v.Type(), nil)
+}
+
+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 {
+ 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)
+ 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)
+ ctx.seeAndUse(param.Type(), user, edgeFunctionResult|edgeType)
+ g.typ(ctx, param.Type(), nil)
+ }
+}
+
+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)
+ switch instr.(type) {
+ case *ssa.Store:
+ // (9.7) variable _reads_ use variables, writes do not
+ ops = ops[1:]
+ case *ssa.DebugRef:
+ ops = nil
+ }
+ for _, arg := range ops {
+ walkPhi(*arg, func(v ssa.Value) {
+ switch v := v.(type) {
+ case *ssa.Function:
+ // (4.3) functions use closures and bound methods.
+ // (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
+ if owningObject(v) != nil {
+ ctx.seeAndUse(owningObject(v), fnObj, edgeInstructionOperand)
+ }
+ g.function(ctx, v)
+ case *ssa.Const:
+ // (9.6) instructions use their operands' types
+ 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
+ ctx.seeAndUse(v.Object(), fnObj, edgeInstructionOperand)
+ }
+ }
+ })
+ }
+ if v, ok := instr.(ssa.Value); ok {
+ if _, ok := v.(*ssa.Range); !ok {
+ // See https://github.com/golang/go/issues/19670
+
+ // (4.8) instructions use their types
+ // (9.4) conversions use the type they convert to
+ ctx.seeAndUse(v.Type(), fnObj, edgeType)
+ g.typ(ctx, v.Type(), nil)
+ }
+ }
+ switch instr := instr.(type) {
+ case *ssa.Field:
+ st := instr.X.Type().Underlying().(*types.Struct)
+ field := st.Field(instr.Field)
+ // (4.7) functions use fields they 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
+ ctx.seeAndUse(field, fnObj, edgeFieldAccess)
+ case *ssa.Store:
+ // nothing to do, handled generically by operands
+ case *ssa.Call:
+ c := instr.Common()
+ if !c.IsInvoke() {
+ // handled generically as an instruction operand
+
+ if g.wholeProgram {
+ // (e3) special case known reflection-based method callers
+ switch lintdsl.CallName(c) {
+ case "net/rpc.Register", "net/rpc.RegisterName", "(*net/rpc.Server).Register", "(*net/rpc.Server).RegisterName":
+ var arg ssa.Value
+ switch lintdsl.CallName(c) {
+ case "net/rpc.Register":
+ arg = c.Args[0]
+ case "net/rpc.RegisterName":
+ arg = c.Args[1]
+ case "(*net/rpc.Server).Register":
+ arg = c.Args[1]
+ case "(*net/rpc.Server).RegisterName":
+ arg = c.Args[2]
+ }
+ walkPhi(arg, func(v ssa.Value) {
+ if v, ok := v.(*ssa.MakeInterface); ok {
+ walkPhi(v.X, func(vv ssa.Value) {
+ ms := ctx.pkg.SSA.Prog.MethodSets.MethodSet(vv.Type())
+ for i := 0; i < ms.Len(); i++ {
+ if ms.At(i).Obj().Exported() {
+ g.useMethod(ctx, vv.Type(), ms.At(i), fnObj, edgeNetRPCRegister)
+ }
+ }
+ })
+ }
+ })
+ }
+ }
+ } else {
+ // (4.5) functions use functions/interface methods they call
+ ctx.seeAndUse(c.Method, fnObj, edgeInterfaceCall)
+ }
+ case *ssa.Return:
+ // nothing to do, handled generically by operands
+ case *ssa.ChangeType:
+ // conversion type handled generically
+
+ s1, ok1 := lintdsl.Dereference(instr.Type()).Underlying().(*types.Struct)
+ s2, ok2 := lintdsl.Dereference(instr.X.Type()).Underlying().(*types.Struct)
+ if ok1 && ok2 {
+ // Converting between two structs. The fields are
+ // relevant for the conversion, but only if the
+ // fields are also used outside of the conversion.
+ // Mark fields as used by each other.
+
+ assert(s1.NumFields() == s2.NumFields())
+ for i := 0; i < s1.NumFields(); 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.
+ ctx.seeAndUse(s1.Field(i), s2.Field(i), edgeStructConversion)
+ ctx.seeAndUse(s2.Field(i), s1.Field(i), edgeStructConversion)
+ }
+ }
+ case *ssa.MakeInterface:
+ // nothing to do, handled generically by operands
+ case *ssa.Slice:
+ // nothing to do, handled generically by operands
+ case *ssa.RunDefers:
+ // nothing to do, the deferred functions are already marked use by defering them.
+ case *ssa.Convert:
+ // to unsafe.Pointer
+ if typ, ok := instr.Type().(*types.Basic); ok && typ.Kind() == types.UnsafePointer {
+ if ptr, ok := instr.X.Type().Underlying().(*types.Pointer); ok {
+ 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.
+ ctx.seeAndUse(st.Field(i), fnObj, edgeUnsafeConversion)
+ }
+ }
+ }
+ }
+ // from unsafe.Pointer
+ if typ, ok := instr.X.Type().(*types.Basic); ok && typ.Kind() == types.UnsafePointer {
+ if ptr, ok := instr.Type().Underlying().(*types.Pointer); ok {
+ 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.
+ ctx.seeAndUse(st.Field(i), fnObj, edgeUnsafeConversion)
+ }
+ }
+ }
+ }
+ case *ssa.TypeAssert:
+ // nothing to do, handled generically by instruction
+ // type (possibly a tuple, which contains the asserted
+ // to type). redundantly handled by the type of
+ // ssa.Extract, too
+ case *ssa.MakeClosure:
+ // nothing to do, handled generically by operands
+ case *ssa.Alloc:
+ // nothing to do
+ case *ssa.UnOp:
+ // nothing to do
+ case *ssa.BinOp:
+ // nothing to do
+ case *ssa.If:
+ // nothing to do
+ case *ssa.Jump:
+ // nothing to do
+ case *ssa.IndexAddr:
+ // nothing to do
+ case *ssa.Extract:
+ // nothing to do
+ case *ssa.Panic:
+ // nothing to do
+ case *ssa.DebugRef:
+ // nothing to do
+ case *ssa.BlankStore:
+ // nothing to do
+ case *ssa.Phi:
+ // nothing to do
+ case *ssa.MakeMap:
+ // nothing to do
+ case *ssa.MapUpdate:
+ // nothing to do
+ case *ssa.Lookup:
+ // nothing to do
+ case *ssa.MakeSlice:
+ // nothing to do
+ case *ssa.Send:
+ // nothing to do
+ case *ssa.MakeChan:
+ // nothing to do
+ case *ssa.Range:
+ // nothing to do
+ case *ssa.Next:
+ // nothing to do
+ case *ssa.Index:
+ // nothing to do
+ case *ssa.Select:
+ // nothing to do
+ case *ssa.ChangeInterface:
+ // nothing to do
+ case *ssa.Go:
+ // nothing to do, handled generically by operands
+ case *ssa.Defer:
+ // nothing to do, handled generically by operands
+ default:
+ panic(fmt.Sprintf("unreachable: %T", instr))
+ }
+ }
+ }
+}
+
+// isNoCopyType reports whether a type represents the NoCopy sentinel
+// type. The NoCopy type is a named struct with no fields and exactly
+// one method `func Lock()` that is empty.
+//
+// FIXME(dh): currently we're not checking that the function body is
+// empty.
+func isNoCopyType(typ types.Type) bool {
+ st, ok := typ.Underlying().(*types.Struct)
+ if !ok {
+ return false
+ }
+ if st.NumFields() != 0 {
+ return false
+ }
+
+ named, ok := typ.(*types.Named)
+ if !ok {
+ return false
+ }
+ if named.NumMethods() != 1 {
+ return false
+ }
+ meth := named.Method(0)
+ if meth.Name() != "Lock" {
+ return false
+ }
+ sig := meth.Type().(*types.Signature)
+ if sig.Params().Len() != 0 || sig.Results().Len() != 0 {
+ return false
+ }
+ return true
+}
+
+func walkPhi(v ssa.Value, fn func(v ssa.Value)) {
+ phi, ok := v.(*ssa.Phi)
+ if !ok {
+ fn(v)
+ return
+ }
+
+ seen := map[ssa.Value]struct{}{}
+ var impl func(v *ssa.Phi)
+ impl = func(v *ssa.Phi) {
+ if _, ok := seen[v]; ok {
+ return
+ }
+ seen[v] = struct{}{}
+ for _, e := range v.Edges {
+ if ev, ok := e.(*ssa.Phi); ok {
+ impl(ev)
+ } else {
+ fn(e)
+ }
+ }
+ }
+ impl(phi)
+}
+
+func interfacesFromExportData(pkg *types.Package) []*types.Interface {
+ var out []*types.Interface
+ scope := pkg.Scope()
+ for _, name := range scope.Names() {
+ obj := scope.Lookup(name)
+ out = append(out, interfacesFromObject(obj)...)
+ }
+ return out
+}
+
+func interfacesFromObject(obj types.Object) []*types.Interface {
+ var out []*types.Interface
+ switch obj := obj.(type) {
+ case *types.Func:
+ sig := obj.Type().(*types.Signature)
+ for i := 0; i < sig.Results().Len(); i++ {
+ out = append(out, interfacesFromObject(sig.Results().At(i))...)
+ }
+ for i := 0; i < sig.Params().Len(); i++ {
+ out = append(out, interfacesFromObject(sig.Params().At(i))...)
+ }
+ case *types.TypeName:
+ if named, ok := obj.Type().(*types.Named); ok {
+ for i := 0; i < named.NumMethods(); i++ {
+ out = append(out, interfacesFromObject(named.Method(i))...)
+ }
+
+ if iface, ok := named.Underlying().(*types.Interface); ok {
+ out = append(out, iface)
+ }
+ }
+ case *types.Var:
+ // No call to Underlying here. We want unnamed interfaces
+ // only. Named interfaces are gotten directly from the
+ // package's scope.
+ if iface, ok := obj.Type().(*types.Interface); ok {
+ out = append(out, iface)
+ }
+ case *types.Const:
+ case *types.Builtin:
+ default:
+ panic(fmt.Sprintf("unhandled type: %T", obj))
+ }
+ return out
+}
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 000000000..b6034bb7d
--- /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 000000000..06aae1e65
--- /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
new file mode 100644
index 000000000..468e8efd6
--- /dev/null
+++ b/vendor/honnef.co/go/tools/version/version.go
@@ -0,0 +1,42 @@
+package version
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "runtime"
+)
+
+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() {
+ 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 (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 e0d811b28..790920b19 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1,9 +1,9 @@
-# cloud.google.com/go v0.49.0 => cloud.google.com/go v0.49.0
+# cloud.google.com/go v0.56.0 => cloud.google.com/go v0.49.0
cloud.google.com/go/compute/metadata
-# github.com/Azure/go-autorest/autorest v0.9.3-0.20191028180845-3492b2aff503 => github.com/Azure/go-autorest/autorest v0.9.0
+# github.com/Azure/go-autorest/autorest v0.10.2 => github.com/Azure/go-autorest/autorest v0.9.0
github.com/Azure/go-autorest/autorest
github.com/Azure/go-autorest/autorest/azure
-# github.com/Azure/go-autorest/autorest/adal v0.8.1-0.20191028180845-3492b2aff503 => github.com/Azure/go-autorest/autorest/adal v0.5.0
+# github.com/Azure/go-autorest/autorest/adal v0.8.3 => github.com/Azure/go-autorest/autorest/adal v0.5.0
github.com/Azure/go-autorest/autorest/adal
# github.com/Azure/go-autorest/autorest/date v0.1.0 => github.com/Azure/go-autorest/autorest/date v0.1.0
github.com/Azure/go-autorest/autorest/date
@@ -11,6 +11,8 @@ github.com/Azure/go-autorest/autorest/date
github.com/Azure/go-autorest/logger
# github.com/Azure/go-autorest/tracing v0.5.0 => github.com/Azure/go-autorest/tracing v0.5.0
github.com/Azure/go-autorest/tracing
+# github.com/BurntSushi/toml v0.3.1
+github.com/BurntSushi/toml
# github.com/NYTimes/gziphandler v1.1.1
github.com/NYTimes/gziphandler
# github.com/PuerkitoBio/purell v1.1.1
@@ -63,7 +65,7 @@ github.com/go-openapi/jsonpointer
github.com/go-openapi/jsonreference
# github.com/go-openapi/spec v0.19.8
github.com/go-openapi/spec
-# github.com/go-openapi/swag v0.19.5
+# github.com/go-openapi/swag v0.19.9
github.com/go-openapi/swag
# github.com/gogo/protobuf v1.3.1 => github.com/gogo/protobuf v1.3.1
github.com/gogo/protobuf/gogoproto
@@ -72,7 +74,7 @@ github.com/gogo/protobuf/protoc-gen-gogo/descriptor
github.com/gogo/protobuf/sortkeys
# github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/glog
-# github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9
+# github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7
github.com/golang/groupcache/lru
# github.com/golang/protobuf v1.4.2 => github.com/golang/protobuf v1.3.2
github.com/golang/protobuf/jsonpb
@@ -92,21 +94,23 @@ github.com/google/go-cmp/cmp/internal/value
github.com/google/gofuzz
# github.com/google/uuid v1.1.1
github.com/google/uuid
-# github.com/googleapis/gnostic v0.3.1 => github.com/googleapis/gnostic v0.3.1
+# github.com/googleapis/gnostic v0.4.0 => github.com/googleapis/gnostic v0.3.1
github.com/googleapis/gnostic/OpenAPIv2
github.com/googleapis/gnostic/compiler
github.com/googleapis/gnostic/extensions
-# github.com/gophercloud/gophercloud v0.6.0
+# github.com/gophercloud/gophercloud v0.11.0
github.com/gophercloud/gophercloud
github.com/gophercloud/gophercloud/openstack
github.com/gophercloud/gophercloud/openstack/identity/v2/tenants
github.com/gophercloud/gophercloud/openstack/identity/v2/tokens
+github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/ec2tokens
+github.com/gophercloud/gophercloud/openstack/identity/v3/extensions/oauth1
github.com/gophercloud/gophercloud/openstack/identity/v3/tokens
github.com/gophercloud/gophercloud/openstack/utils
github.com/gophercloud/gophercloud/pagination
# github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/grpc-ecosystem/go-grpc-prometheus
-# github.com/hashicorp/golang-lru v0.5.3
+# github.com/hashicorp/golang-lru v0.5.4
github.com/hashicorp/golang-lru
github.com/hashicorp/golang-lru/simplelru
# github.com/imdario/mergo v0.3.6 => github.com/imdario/mergo v0.3.5
@@ -117,13 +121,13 @@ github.com/inconshreveable/mousetrap
github.com/jpillora/go-ogle-analytics
# github.com/json-iterator/go v1.1.10
github.com/json-iterator/go
-# github.com/mailru/easyjson v0.7.0
+# github.com/mailru/easyjson v0.7.1
github.com/mailru/easyjson/buffer
github.com/mailru/easyjson/jlexer
github.com/mailru/easyjson/jwriter
# github.com/matttproud/golang_protobuf_extensions v1.0.1
github.com/matttproud/golang_protobuf_extensions/pbutil
-# github.com/mitchellh/mapstructure v1.1.2
+# github.com/mitchellh/mapstructure v1.2.2
github.com/mitchellh/mapstructure
# github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
github.com/modern-go/concurrent
@@ -133,7 +137,7 @@ github.com/modern-go/reflect2
github.com/munnerz/goautoneg
# github.com/pkg/errors v0.9.1
github.com/pkg/errors
-# github.com/prometheus/client_golang v1.2.1 => github.com/prometheus/client_golang v1.7.1
+# github.com/prometheus/client_golang v1.6.0 => github.com/prometheus/client_golang v1.7.1
github.com/prometheus/client_golang/prometheus
github.com/prometheus/client_golang/prometheus/internal
github.com/prometheus/client_golang/prometheus/promhttp
@@ -191,11 +195,13 @@ go.etcd.io/etcd/raft/confchange
go.etcd.io/etcd/raft/quorum
go.etcd.io/etcd/raft/raftpb
go.etcd.io/etcd/raft/tracker
-# go.uber.org/atomic v1.5.0
+# go.uber.org/atomic v1.6.0
go.uber.org/atomic
-# go.uber.org/multierr v1.1.0
+# go.uber.org/multierr v1.3.0
go.uber.org/multierr
-# go.uber.org/zap v1.10.0
+# go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee
+go.uber.org/tools/update-license
+# go.uber.org/zap v1.13.0
go.uber.org/zap
go.uber.org/zap/buffer
go.uber.org/zap/internal/bufferpool
@@ -223,13 +229,13 @@ golang.org/x/net/idna
golang.org/x/net/internal/timeseries
golang.org/x/net/trace
golang.org/x/net/websocket
-# golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
+# golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/oauth2
golang.org/x/oauth2/google
golang.org/x/oauth2/internal
golang.org/x/oauth2/jws
golang.org/x/oauth2/jwt
-# golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
+# golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
golang.org/x/sync/singleflight
# golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1
golang.org/x/sys/cpu
@@ -242,19 +248,37 @@ golang.org/x/text/transform
golang.org/x/text/unicode/bidi
golang.org/x/text/unicode/norm
golang.org/x/text/width
-# golang.org/x/time v0.0.0-20191024005414-555d28b269f0
+# golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
golang.org/x/time/rate
-# golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7
+# golang.org/x/tools v0.0.0-20200603131246-cc40288be839
+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
golang.org/x/tools/go/gcexportdata
golang.org/x/tools/go/internal/gcimporter
+golang.org/x/tools/go/internal/packagesdriver
+golang.org/x/tools/go/packages
+golang.org/x/tools/go/types/objectpath
+golang.org/x/tools/go/types/typeutil
+golang.org/x/tools/internal/analysisinternal
+golang.org/x/tools/internal/event
+golang.org/x/tools/internal/event/core
+golang.org/x/tools/internal/event/keys
+golang.org/x/tools/internal/event/label
+golang.org/x/tools/internal/gocommand
+golang.org/x/tools/internal/packagesinternal
+# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
+golang.org/x/xerrors
+golang.org/x/xerrors/internal
# gomodules.xyz/jsonpatch/v2 v2.1.0
gomodules.xyz/jsonpatch/v2
# gomodules.xyz/sets v0.0.0-20200817084356-ae6e5e14ce3d
gomodules.xyz/sets
# gomodules.xyz/version v0.1.0
gomodules.xyz/version
-# google.golang.org/appengine v1.6.5
+# google.golang.org/appengine v1.6.6
google.golang.org/appengine
google.golang.org/appengine/internal
google.golang.org/appengine/internal/app_identity
@@ -265,9 +289,9 @@ google.golang.org/appengine/internal/modules
google.golang.org/appengine/internal/remote_api
google.golang.org/appengine/internal/urlfetch
google.golang.org/appengine/urlfetch
-# google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9 => google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9
+# google.golang.org/genproto v0.0.0-20200603110839-e855014d5736 => google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9
google.golang.org/genproto/googleapis/rpc/status
-# google.golang.org/grpc v1.26.0 => google.golang.org/grpc v1.26.0
+# google.golang.org/grpc v1.29.1 => google.golang.org/grpc v1.26.0
google.golang.org/grpc
google.golang.org/grpc/attributes
google.golang.org/grpc/backoff
@@ -310,8 +334,34 @@ google.golang.org/grpc/tap
gopkg.in/inf.v0
# gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/natefinch/lumberjack.v2
-# gopkg.in/yaml.v2 v2.2.8
+# gopkg.in/yaml.v2 v2.3.0
gopkg.in/yaml.v2
+# honnef.co/go/tools v0.0.1-2019.2.3
+honnef.co/go/tools/arg
+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/ssautil
+honnef.co/go/tools/staticcheck
+honnef.co/go/tools/staticcheck/vrp
+honnef.co/go/tools/stylecheck
+honnef.co/go/tools/unused
+honnef.co/go/tools/version
# k8s.io/api v0.18.9 => github.com/kmodules/api v0.18.10-0.20200922195318-d60fe725dea0
k8s.io/api/admission/v1
k8s.io/api/admission/v1beta1
@@ -786,7 +836,7 @@ k8s.io/kube-openapi/pkg/util
k8s.io/kube-openapi/pkg/util/proto
# k8s.io/kubernetes v1.18.9 => github.com/kmodules/kubernetes v1.19.0-alpha.0.0.20200922200158-8b13196d8dc4
k8s.io/kubernetes/pkg/util/hash
-# k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 => k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89
+# k8s.io/utils v0.0.0-20200414100711-2df71ebbae66 => k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89
k8s.io/utils/buffer
k8s.io/utils/integer
k8s.io/utils/net