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). 1 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