diff --git a/language/go/extractor/go.mod b/language/go/extractor/go.mod new file mode 100644 index 00000000..f3de3c81 --- /dev/null +++ b/language/go/extractor/go.mod @@ -0,0 +1,56 @@ +module alipay.com/code_insight/coref-go-extractor + +go 1.18 + +require ( + github.com/deckarep/golang-set v1.8.0 + github.com/glebarez/sqlite v1.4.5 + github.com/stretchr/testify v1.8.0 + golang.org/x/mod v0.7.0 + golang.org/x/tools v0.4.0 + gorm.io/gorm v1.23.5 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect + golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apimachinery v0.26.2 // indirect + k8s.io/klog/v2 v2.80.1 // indirect + k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect + sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) + +require ( + github.com/glebarez/go-sqlite v1.17.2 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.4 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/client-go v0.26.2 + modernc.org/libc v1.16.8 // indirect + modernc.org/mathutil v1.4.1 + modernc.org/memory v1.1.1 // indirect + modernc.org/sqlite v1.17.2 // indirect +) diff --git a/language/go/extractor/go.sum b/language/go/extractor/go.sum new file mode 100644 index 00000000..f18f8ba0 --- /dev/null +++ b/language/go/extractor/go.sum @@ -0,0 +1,540 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +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/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4= +github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= +github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/glebarez/go-sqlite v1.17.2 h1:gyTyFr2RFFQd2gp6fOOdfnTvUn99zwvVOrQFHA4S+DY= +github.com/glebarez/go-sqlite v1.17.2/go.mod h1:lakPjzvnJ6uSIARV+5dPALDuSLL3879PlzHFMEpbceM= +github.com/glebarez/sqlite v1.4.5 h1:oaJupO4X9iTn4sXRvP5Vs15BNvKh9dx5AQfciKlDvV4= +github.com/glebarez/sqlite v1.4.5/go.mod h1:6D+bB+DdXlEC4mO+pUFJWixVcnrHTIAJ9U6Ynnn4Lxk= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +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/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +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= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/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-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w= +golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-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/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +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= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/gorm v1.23.5 h1:TnlF26wScKSvknUC/Rn8t0NLLM22fypYBlvj1+aH6dM= +gorm.io/gorm v1.23.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.26.2 h1:dM3cinp3PGB6asOySalOZxEG4CZ0IAdJsrYZXE/ovGQ= +k8s.io/api v0.26.2/go.mod h1:1kjMQsFE+QHPfskEcVNgL3+Hp88B80uj0QtSOlj8itU= +k8s.io/apimachinery v0.26.2 h1:da1u3D5wfR5u2RpLhE/ZtZS2P7QvDgLZTi9wrNZl/tQ= +k8s.io/apimachinery v0.26.2/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= +k8s.io/client-go v0.26.2 h1:s1WkVujHX3kTp4Zn4yGNFK+dlDXy1bAAkIl+cFAiuYI= +k8s.io/client-go v0.26.2/go.mod h1:u5EjOuSyBa09yqqyY7m3abZeovO/7D/WehVVlZ2qcqU= +k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= +k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs= +k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o= +modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.17.2 h1:TjmF36Wi5QcPYqRoAacV1cAyJ7xB/CD0ExpVUEMebnw= +modernc.org/sqlite v1.17.2/go.mod h1:GOQmuiXd6pTTes1Fi2s9apiCcD/wbKQtBZ0Nw6/etjM= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/language/go/extractor/main.go b/language/go/extractor/main.go new file mode 100644 index 00000000..076d70bb --- /dev/null +++ b/language/go/extractor/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "log" + + "alipay.com/code_insight/coref-go-extractor/src/config" + "alipay.com/code_insight/coref-go-extractor/src/core" +) + +// Go-extractor-core is the core program for executing extraction in a given package root. +// The root is assumed to have a go.mod file in its path. +func main() { + config := createConfig() + + if err := core.ExtractWithFlags(nil, []string{"./..."}, config); err != nil { + log.Fatalf("Error running go tooling: %s\n", err.Error()) + } +} + +// createConfig returns a configuration for the go-extractor-core. +func createConfig() *config.Config { + return &config.Config{ + Store: createOrmDesc(), + ParseRule: &config.ParseConfig{ + NeedTests: true, + NeedScope: false, + NeedModFile: false, + NeedCompile: false, + }, + } +} + +// createOrmDesc returns a new OrmDesc with configured database settings. +func createOrmDesc() *config.OrmDesc { + return &config.OrmDesc{ + DBDialect: "sqlite3", + DBName: "coref_go_src.db", + DBPath: ".", + } +} diff --git a/language/go/extractor/src/cli/extractor_cli.go b/language/go/extractor/src/cli/extractor_cli.go new file mode 100644 index 00000000..7ad3a2a7 --- /dev/null +++ b/language/go/extractor/src/cli/extractor_cli.go @@ -0,0 +1,132 @@ +package main + +import ( + "alipay.com/code_insight/coref-go-extractor/src/config" + "fmt" + "log" + "os" + "time" + + "alipay.com/code_insight/coref-go-extractor/src/util" +) + +func usage() { + fmt.Fprintf(os.Stderr, + `%s is a wrapper script that installs dependencies and calls the extractor. +In resource-constrained environments, the environment variable SPARROW_EXTRACTOR_GO_MAX_GOROUTINES +(or its legacy alias MAX_GOROUTINES) can be used to limit the number of parallel goroutines +started by the extractor, which reduces CPU and memory requirements. The default value for this +variable is CPU Nums. +The extractor for go tests files<*_test.go | *.test> is extracted by default. +If you don't need extract go tests files, please add extraction flag with ' -ex noneedtests'; +however, types' info of target dependencies is not extracted by default, if you want do extraction for +that, use flag with '-ex needdependency', see the demo below. +`, os.Args[0]) + fmt.Fprintf(os.Stderr, "Usage:\n\n %s [...] [...] [--] \n", os.Args[0]) + fmt.Fprintf(os.Stderr, " Example#1: %s -o ./out/coref_go_src.db $src_root \n", os.Args[0]) + fmt.Fprintf(os.Stderr, " Example#2: %s -o ./out/coref_go_src.db -ex noneedtests -ex needscope -mod=mod $src_root \n", os.Args[0]) +} + +type extractionConf struct { + extractor string + extractArgs []string + buildArgs []string + dbPath string + dbName string + srcRoot string + shouldInstallDependency bool +} + +// DependencyInstallerMode is an enum describing how dependencies should be installed +type DependencyInstallerMode int + +const ( + // GoGetNoModules represents dependency installation using `go get` without modules + GoGetNoModules DependencyInstallerMode = iota + // GoGetWithModules represents dependency installation using `go get` with modules + GoGetWithModules +) + +type GetMode int + +const ( + ModUnset GetMode = iota + ModGet + ModMod + ModVendor +) + +func main() { + startTime := time.Now() + + // os.Args + cfg := parseExtractorRunningParams([]string{"./extractor_cli", "-o", "./out/coref_go_src.db", "-ex", "needmodfile", "-ex", "noneedtests", "."}) + log.Printf("ExtractorRunningParams: %+v\n", cfg) + + currentDir, err := os.Getwd() + if err != nil { + log.Fatalf("Failed to get current directory: %s", err) + } + + // setting up for go env + util.PrepareRunEnv() + + // should load project from extraction src root + if err := os.Chdir(cfg.srcRoot); err != nil { + log.Fatalf("Failed to change directory to the extraction source root: %s", err) + } + + if cfg.shouldInstallDependency { + handleDependencyInstallation(currentDir) + } + + // for compatible with go111 + // see detail @https://maelvls.dev/go111module-everywhere/ + // disable go mod Load by default. + if err := os.Setenv("GO111MODULE", "off"); err != nil { + log.Fatalf("Failed to set GO111MODULE environment variable: %s", err) + } + + log.Printf("Done preparing workspace in %v.\n", time.Since(startTime)) + log.Printf("Running extractor '%s' from directory '%s'.\n", cfg.extractor, cfg.srcRoot) + + runGoExtraction(cfg) +} + +func setExtractorBasicConfig(cfg *extractionConf) *config.Config { + // Initialize the basic configuration with default values + basicConfig := &config.Config{ + Store: &config.OrmDesc{ + DBDialect: "sqlite3", + DBName: "coref_go_src.db", + DBPath: ".", + }, + ParseRule: &config.ParseConfig{ + NeedTests: true, // Default to including tests + }, + } + + // Update the database path and name if provided in the cfg + if cfg.dbPath != "" { + basicConfig.Store.DBPath = cfg.dbPath + } + if cfg.dbName != "" { + basicConfig.Store.DBName = cfg.dbName + } + + // Update the parse rules based on the extraction arguments + for _, arg := range cfg.extractArgs { + switch arg { + case "NeedScope": + basicConfig.ParseRule.NeedScope = true + case "NoNeedTests": + basicConfig.ParseRule.NeedTests = false + case "NeedModFile": + basicConfig.ParseRule.NeedModFile = true + case "NeedCompile": + basicConfig.ParseRule.NeedCompile = true + } + } + + return basicConfig +} diff --git a/language/go/extractor/src/cli/helper.go b/language/go/extractor/src/cli/helper.go new file mode 100644 index 00000000..b0be0ca9 --- /dev/null +++ b/language/go/extractor/src/cli/helper.go @@ -0,0 +1,425 @@ +package main + +import ( + "alipay.com/code_insight/coref-go-extractor/src/core" + "alipay.com/code_insight/coref-go-extractor/src/util" + "fmt" + "golang.org/x/mod/semver" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "regexp" + "runtime" + "runtime/pprof" + "strings" + "text/template" +) + +// parseExtractorRunningParams parses the command-line arguments and sets up the extraction configuration. +func parseExtractorRunningParams(args []string) *extractionConf { + if len(args) < 2 { + usage() + os.Exit(2) + } + + configArgs := strings.Split(strings.Join(args[1:], " "), " ") + srcPath, err := filepath.Abs(configArgs[len(configArgs)-1]) + if err != nil { + log.Fatalf("Unable to resolve the absolute path: %s", err) + } + if _, err := os.Stat(srcPath); os.IsNotExist(err) { + log.Fatalf("Source path does not exist: %s", srcPath) + } + + extractionConfig := &extractionConf{ + extractor: args[0], + srcRoot: srcPath, + } + + for i := 0; i < len(configArgs)-1 && strings.HasPrefix(configArgs[i], "-"); i++ { + arg := configArgs[i] + switch arg { + case "-h", "--help": + usage() + os.Exit(0) + case "-exconfig", "-ex": + handleExtractionArg(configArgs, &i, extractionConfig) + case "-o", "-output", "-dbpath": + handleOutputArg(configArgs, &i, extractionConfig) + default: + handleBuildArg(arg, extractionConfig) + } + } + + return extractionConfig +} + +func handleExtractionArg(args []string, index *int, config *extractionConf) { + nextIndex := *index + 1 + if nextIndex < len(args) { + extractionArg := args[nextIndex] + switch extractionArg { + case "resolvedependency", "needdependencytypes", "needdependency": + config.shouldInstallDependency = true + case "needscopes": + config.extractArgs = append(config.extractArgs, "NeedScope") + case "noneedtests": + config.extractArgs = append(config.extractArgs, "NoNeedTests") + case "needmodfile", "needmodfiles": + config.extractArgs = append(config.extractArgs, "NeedModFile") + case "needcompiledgofiles": + config.extractArgs = append(config.extractArgs, "NeedCompile") + } + *index = nextIndex + } +} + +func handleOutputArg(args []string, index *int, config *extractionConf) { + nextIndex := *index + 1 + if nextIndex < len(args) { + path := args[nextIndex] + dir, file := filepath.Split(path) + if _, err := os.Stat(dir); os.IsNotExist(err) { + if err := os.MkdirAll(dir, os.ModePerm); err != nil { + log.Fatalf("Unable to create directory '%s': %s", dir, err) + } + } + config.dbPath = dir + config.dbName = file + *index = nextIndex + } +} + +func handleBuildArg(arg string, config *extractionConf) { + // args start with '-', all would be treated as buildFlag for the GO complier, + // Maybe one of ["-p", "-asmflags", "-buildmode", "-compiler", "-gccgoflags", "-gcflags", "-installsuffix", + // "-ldflags", "-mod", "-modfile", "-pkgdir", "-tags", "-toolexec", "-overlay"] + // etc. + if strings.HasPrefix(arg, "-") { + config.buildArgs = append(config.buildArgs, arg) + } +} + +func runGoExtraction(cfg *extractionConf) { + config := setExtractorBasicConfig(cfg) + + // Set up CPU profiling if enabled + cpuProfileCleanup := setupCPUProfiling() + defer cpuProfileCleanup() + + log.Printf("Build flags: '%s'; Patterns: '%s'\n", strings.Join(cfg.buildArgs, " "), "./...") + if err := core.ExtractWithFlags(cfg.buildArgs, []string{"./..."}, config); err != nil { + log.Fatalf("Error running go tooling: %s", err) + } + + // Set up memory profiling if enabled + memProfileCleanup := setupMemProfiling() + defer memProfileCleanup() +} + +func setupCPUProfiling() func() { + cpuprofile := os.Getenv("EXTRACTOR_GO_CPU_PROFILE") + if cpuprofile == "" { + return func() {} + } + f, err := os.Create(cpuprofile) + if err != nil { + log.Fatalf("Failed to create CPU profile: %v", err) + } + if err := pprof.StartCPUProfile(f); err != nil { + f.Close() + log.Fatalf("Failed to start CPU profile: %v", err) + } + return func() { + pprof.StopCPUProfile() + if err := f.Close(); err != nil { + log.Printf("Failed to close CPU profile file: %v", err) + } + } +} + +func setupMemProfiling() func() { + memprofile := os.Getenv("EXTRACTOR_GO_MEM_PROFILE") + if memprofile == "" { + return func() {} + } + f, err := os.Create(memprofile) + if err != nil { + log.Fatalf("Failed to create memory profile: %v", err) + } + runtime.GC() // Collect garbage to get up-to-date statistics + if err := pprof.WriteHeapProfile(f); err != nil { + f.Close() + log.Fatalf("Failed to write memory profile: %v", err) + } + return func() { + if err := f.Close(); err != nil { + log.Printf("Failed to close memory profile file: %v", err) + } + } +} + +// addVersionToMod add a go version directive, e.g. `go 1.14` to a `go.mod` file. +func addVersionToMod(goMod []byte, version string) bool { + cmd := exec.Command("go", "mod", "edit", "-go="+version) + return util.RunCmd(cmd) != nil +} + +var goVersion = "" + +// Returns the current Go version as returned by 'go version', e.g. go1.14.4 +func getEnvGoVersion() string { + if goVersion == "" { + gover, err := exec.Command("go", "version").CombinedOutput() + if err != nil { + log.Fatalf("Unable to run the go command, is it installed?\nError: %s", err.Error()) + } + goVersion = strings.Fields(string(gover))[2] + } + return goVersion +} + +// Returns the current Go version in semver format, e.g. v1.14.4 +func getEnvGoSemVer() string { + goVersion := getEnvGoVersion() + if !strings.HasPrefix(goVersion, "go") { + log.Fatalf("Expected 'go version' output of the form 'go1.2.3'; got '%s'", goVersion) + } + return "v" + goVersion[2:] +} + +// checkVendor tests to see whether a vendor directory is inconsistent according to the go frontend +func checkVendor() bool { + vendorCheckCmd := exec.Command("go", "list", "-mod=vendor", "./...") + outp, err := vendorCheckCmd.CombinedOutput() + if err != nil { + badVendorRe := regexp.MustCompile(`(?m)^go: inconsistent vendoring in .*:$`) + return !badVendorRe.Match(outp) + } + + return true +} + +func getDependencyListWithGoGet(repoList []string, mod DependencyInstallerMode, backDir string) error { + if err := setGoModulesEnv(mod); err != nil { + return fmt.Errorf("setting GO111MODULE environment variable: %v", err) + } + + if err := installDependencies("./..."); err != nil { + return fmt.Errorf("installing dependencies: %v", err) + } + + if len(repoList) > 0 { + if err := processRepoList(repoList, backDir); err != nil { + return fmt.Errorf("processing repository list: %v", err) + } + } + + return nil +} + +func setGoModulesEnv(mod DependencyInstallerMode) error { + envValue := "off" + if mod == GoGetWithModules { + envValue = "on" + } + return os.Setenv("GO111MODULE", envValue) +} + +func installDependencies(pattern string) error { + cmd := exec.Command("go", "get", "-v", "-x", pattern) + log.Printf("Installing dependencies using `go get -v -x %s`.", pattern) + return util.RunCmd(cmd) +} + +func processRepoList(repoList []string, backDir string) error { + scratch, err := os.MkdirTemp(backDir, "scratch") + if err != nil { + return fmt.Errorf("creating temporary directory: %v", err) + } + defer os.RemoveAll(scratch) + + fileContent := generateFileContent(repoList) + filePath := filepath.Join(scratch, "main.go") + if err := os.WriteFile(filePath, []byte(fileContent), 0644); err != nil { + return fmt.Errorf("writing main.go file: %v", err) + } + + if err := runGoModCommands(scratch); err != nil { + return fmt.Errorf("running Go mod commands: %v", err) + } + + return moveSourceFilesToGoPath(scratch, backDir) +} + +func generateFileContent(repoList []string) string { + tpl := `package main + +import ( +{{- range .}} + _ "{{.}}" +{{- end}} +) + +func main() { + // Intentionally left empty. +} +` + t := template.Must(template.New("file").Parse(tpl)) + var builder strings.Builder + if err := t.Execute(&builder, repoList); err != nil { + log.Fatalf("executing template: %v", err) + } + return builder.String() +} + +func runGoModCommands(dir string) error { + if err := os.Chdir(dir); err != nil { + return fmt.Errorf("changing to temporary directory: %v", err) + } + log.Printf("Initializing new Go module and downloading dependencies.") + + if output, err := exec.Command("go", "mod", "init", "fake").CombinedOutput(); err != nil { + return fmt.Errorf("initializing go mod: %s: %v", output, err) + } + if output, err := exec.Command("go", "mod", "tidy").CombinedOutput(); err != nil { + return fmt.Errorf("tidying go mod: %s: %v", output, err) + } + return installDependencies("./...") +} + +func moveSourceFilesToGoPath(scratch, backDir string) error { + goPath := os.Getenv("GOPATH") + if goPath == "" { + return nil + } + srcPath := filepath.Join(goPath, "pkg", "mod") + destPath := filepath.Join(goPath, "src") + + if err := util.DirExistsAndClean(destPath); err != nil { + return fmt.Errorf("preparing destination directory: %v", err) + } + if err := os.Rename(srcPath, destPath); err != nil { + return fmt.Errorf("moving source files: %v", err) + } + return os.Chdir(backDir) +} + +func handleDependencyInstallation(currentDir string) { + depMode := determineDependencyMode() + modMode := getModuleMode(depMode) + + if modMode == ModVendor && !fixVendorIssues() { + modMode = ModMod + } + + updateGoModAndGoSumIfNeeded(depMode) + + if modMode == ModVendor && checkVendor() { + log.Printf("Skipping dependency installation because a Go vendor directory was found.") + return + } + + repoList := getRepoList() + if err := getDependencyListWithGoGet(repoList, depMode, currentDir); err != nil { + log.Printf("Failed to run getDepedencyListWithGoMod: %s\n", err) + } +} + +func determineDependencyMode() DependencyInstallerMode { + if util.FileExists("go.mod") { + log.Println("Found go.mod, enabling go modules") + return GoGetWithModules + } + return GoGetNoModules +} + +func getModuleMode(depMode DependencyInstallerMode) GetMode { + if depMode != GoGetWithModules { + return ModUnset + } + if util.FileExists("vendor/modules.txt") { + return ModVendor + } + if util.DirExists("vendor") { + return ModMod + } + return ModUnset +} + +func fixVendorIssues() bool { + const explicitVersion = "1.13" + goModPath := "go.mod" + vendorModulesTxt := "vendor/modules.txt" + + if !util.FileExists(goModPath) || !util.FileExists(vendorModulesTxt) { + return false + } + + goModContent, err := ioutil.ReadFile(goModPath) + if err != nil { + log.Println("Failed to read go.mod to check for missing Go version") + return false + } + + versionRe := regexp.MustCompile(`(?m)^go[ \t\r]+[0-9]+\.[0-9]+$`) + if versionRe.Match(goModContent) { + return true + } + + modulesTxtContent, err := ioutil.ReadFile(vendorModulesTxt) + if err != nil { + log.Println("Failed to read vendor/modules.txt to check for mismatched Go version") + return false + } + + explicitRe := regexp.MustCompile(`(?m)^## explicit$`) + if explicitRe.Match(modulesTxtContent) { + return true + } + + log.Println("Adding a version directive to the go.mod file as the modules.txt does not have explicit annotations") + return addVersionToMod(goModContent, explicitVersion) +} + +func updateGoModAndGoSumIfNeeded(depMode DependencyInstallerMode) { + if depMode != GoGetWithModules || semver.Compare(getEnvGoSemVer(), "v1.16") < 0 { + return + } + + beforeGoModFileInfo, err := os.Stat("go.mod") + if err != nil { + log.Println("Failed to stat go.mod before running `go mod tidy -e`") + return + } + + if err := runGoModTidyE(); err != nil { + log.Println("Failed to run `go mod tidy -e`") + return + } + + checkForFileUpdates("go.mod", beforeGoModFileInfo) + checkForFileUpdates("go.sum", nil) +} + +func runGoModTidyE() error { + return util.RunCmd(exec.Command("go", "mod", "tidy", "-e")) +} + +func checkForFileUpdates(filename string, beforeFileInfo os.FileInfo) { + afterFileInfo, err := os.Stat(filename) + if err != nil || (beforeFileInfo != nil && !afterFileInfo.ModTime().After(beforeFileInfo.ModTime())) { + return + } + log.Printf("We have run `go mod tidy -e` and it altered %s. You may wish to check these changes into version control.", filename) +} + +func getRepoList() []string { + repoURL := os.Getenv("SPARROW_REPO_URL") + if repoURL == "" { + return nil + } + return []string{repoURL} +} diff --git a/language/go/extractor/src/config/config.go b/language/go/extractor/src/config/config.go new file mode 100644 index 00000000..493fddd5 --- /dev/null +++ b/language/go/extractor/src/config/config.go @@ -0,0 +1,37 @@ +package config + +import ( + "encoding/json" +) + +var ( + ExtractorVersion = "v0.0.8" + ReleaseDate = "2022-07-17" + Author = "CodeInsight@AntGroup" +) + +type OrmDesc struct { + DBDialect string `json:"db_dialect"` // Database Dialect, such as `sqlite3` + DBName string `json:"db_name"` // Database Name + DBPath string `json:"db_path"` // Database path for storing on disk +} + +type ParseConfig struct { + NeedCompile bool `json:"need_compile"` // Parse compiled Go files + NeedScope bool `json:"need_scope"` // Parse Scope + NeedTests bool `json:"need_tests"` // Parse including Go test files, enabled by default + NeedModFile bool `json:"need_modfile"` // Parse mod.go file +} + +type Config struct { + Store *OrmDesc `json:"store"` // Database Description struct + ParseRule *ParseConfig `json:"parse_rule"` // Parsing rules +} + +func (c *Config) ToString() string { + jsonStr, err := json.Marshal(c) + if err != nil { + return err.Error() + } + return string(jsonStr) +} diff --git a/language/go/extractor/src/core/extract_file.go b/language/go/extractor/src/core/extract_file.go new file mode 100644 index 00000000..e3709b81 --- /dev/null +++ b/language/go/extractor/src/core/extract_file.go @@ -0,0 +1,1994 @@ +package core + +import ( + "bytes" + "fmt" + "go/ast" + "go/constant" + "go/token" + "go/types" + "log" + "strconv" + "strings" + "time" + + "alipay.com/code_insight/coref-go-extractor/src/orm" + "alipay.com/code_insight/coref-go-extractor/src/util" + + "golang.org/x/tools/go/packages" +) + +// extractFile handles the extraction process for a Go file. +func (ex *Extraction) extractFile(ast *ast.File, pkg *packages.Package) error { + if !ast.Package.IsValid() { + log.Println("Skipping extracting a file without a 'package' declaration.") + return nil + } + fset := pkg.Fset + path := normalizedPath(ast, fset) + log.Printf("Extracting %s", path) + + // Defer logging the time taken to extract until the function completes. + defer func(start time.Time) { + log.Printf("Done extracting %s (%dms).", path, time.Since(start).Milliseconds()) + }(time.Now()) + + profile, err := NewProfile(path, pkg, ex.Db, &ex.IDManager, ex.SrcRootDir) + if err != nil { + return fmt.Errorf("error creating profile for %s: %w", path, err) + } + + // Extract file-level information. + ex.extractFileInfo(profile, path) + + // Extract scopes if necessary. + if ex.Config.ParseRule.NeedScope { + extractScopes(profile, ast, profile.Package) + } + + // Extract AST information. + extractAstInfo(profile, ast) + + // Extract additional type and line number information if necessary. + if ex.Config.ParseRule.NeedScope { + extractObjectTypes(profile) + } + + extractNumLines(profile, path, ast) + return nil +} + +// extractAstInfo extracts information from the abstract syntax tree (AST) of a Go file. +func extractAstInfo(p *Profile, ast *ast.File) { + lbl := p.Labeler.GetFileLabel() + + // Extract the package name. + extractExpr(p, ast.Name, lbl, 0) + + // Extract top-level declaration nodes. + for i, decl := range ast.Decls { + extractDecl(p, decl, lbl, i) + } + + // Extract comment groups. + for i, cg := range ast.Comments { + extractCommentGroup(p, cg, lbl, i) + } + + // Extract documentation associated with the package. + extractDoc(p, ast.Doc, lbl) + + // Emit scope node information. + emitScopeNodeInfo(p, ast, lbl) +} + +// extractTypeOf looks up the type of `expr`, extracts it if it hasn't previously been +// extracted, and associates it with `expr` in the `type_of` table. +func extractTypeOf(p *Profile, expr ast.Expr, lbl Label) { + // Look up the type information for the given expression. + tp := typeOf(p, expr) + + // If the type information exists, proceed to extraction and storage. + if tp == nil { + return + } + + // Extract the type information and obtain a label for it. + tplbl, extracted := extractType(p, tp) + if !extracted { + return + } + + // Store the association between the expression label and the extracted type label + // in the `type_of` table. + typeOfID := util.GetIDFromDigest(fmt.Sprintf("%v#expr:%v", p.Path, lbl.ID), "TypeOf") + p.StoreTable(&orm.TypeOf{ + Oid: typeOfID, + Expr: lbl.ID, + Tp: tplbl.ID, + }) +} + +// extractType extracts type information for `tp` and returns its associated label. +// Types are only extracted once, so the second time `extractType` is invoked it simply returns the label. +func extractType(p *Profile, tp types.Type) (Label, bool) { + // Retrieve the existing label for the type if it has already been processed. + lbl, exists := getTypeLabel(p, tp) + if exists { + // Type has already been processed, return the existing label. + return lbl, true + } + + // Determine the kind of type and process accordingly. + var kind int + var ok = true + + switch tp := tp.(type) { + case *types.Basic: + // Handle basic types. + kind = processBasicType(p, tp, lbl) + case *types.Array: + // Handle array types. + kind = processArrayType(p, tp, lbl) + case *types.Slice: + // Handle slice types. + kind = processSliceType(p, tp, lbl) + case *types.Struct: + // Handle struct types. + kind = processStructType(p, tp, lbl) + case *types.Pointer: + // Handle pointer types. + kind = processPointerType(p, tp, lbl) + case *types.Interface: + // Handle interface types. + kind = processInterfaceType(p, tp, lbl) + case *types.Tuple: + // Handle tuple types. + kind = processTupleType(p, tp, lbl) + case *types.Signature: + // Handle function signature types. + kind = processSignatureType(p, tp, lbl) + case *types.Map: + // Handle map types. + kind = processMapType(p, tp, lbl) + case *types.Chan: + // Handle channel types. + kind = processChanType(p, tp, lbl) + case *types.Named: + // Handle named types. + kind = processNamedType(p, tp, lbl) + case *types.TypeParam: + // Handle type parameter types. + kind, ok = processTypeParamType(p, tp, lbl) + if !ok { + // If the processing of the type parameter was not successful, + // return the label and false. + return lbl, false + } + case *types.Union: + // Handle union types. + kind = processUnionType(p, tp, lbl) + default: + // Log and exit if the type is unknown. + log.Fatalf("unexpected type %T", tp) + } + + // Store the general type information now that specific processing is complete. + storeGeneralTypeInfo(p, tp, lbl, kind) + + return lbl, ok +} + +// processBasicType handles the extraction of basic types. +func processBasicType(p *Profile, tp *types.Basic, lbl Label) int { + branch := orm.BasicTypes[tp.Kind()] + if branch == nil { + log.Fatalf("unknown basic type %v", tp.Kind()) + } + return branch.Index() +} + +// processArrayType handles the extraction of array types. +func processArrayType(p *Profile, tp *types.Array, lbl Label) int { + kind := orm.ArrayType.Index() + p.StoreTable(&orm.ArrayLength{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#type:%v", p.Path, lbl.ID), "ArrayLength"), + FileId: p.Labeler.GetFileLabel().ID, + Tp: lbl.ID, + Len: tp.Len(), + }) + extractElementType(p, lbl, tp.Elem()) + return kind +} + +// processSliceType handles the extraction of slice types. +func processSliceType(p *Profile, tp *types.Slice, lbl Label) int { + kind := orm.SliceType.Index() + extractElementType(p, lbl, tp.Elem()) + return kind +} + +// processStructType handles the extraction of struct types. +func processStructType(p *Profile, tp *types.Struct, lbl Label) int { + kind := orm.StructType.Index() + for i := 0; i < tp.NumFields(); i++ { + field := tp.Field(i) + + // ensure the field is associated with a label - note that + // struct fields do not have a parent scope, so they are not + // dealt with by `extractScopes` + fieldlbl, exists := p.Labeler.FieldID(field, i, lbl) + if !exists { + extractObject(p, field, fieldlbl) + } + + p.StoreTable(&orm.FieldStruct{ + Oid: fieldlbl.ID, + Struct: lbl.ID, + }) + + name := field.Name() + if field.Embedded() { + name = "" + } + extractComponentType(p, lbl, i, name, field.Type()) + } + return kind +} + +// processPointerType handles the extraction of pointer types. +func processPointerType(p *Profile, tp *types.Pointer, lbl Label) int { + kind := orm.PointerType.Index() + extractBaseType(p, lbl, tp.Elem()) + return kind +} + +// processInterfaceType handles the extraction of interface types. +func processInterfaceType(p *Profile, tp *types.Interface, lbl Label) int { + kind := orm.InterfaceType.Index() + for i := 0; i < tp.NumMethods(); i++ { + meth := tp.Method(i) + + extractMethod(p, meth) + + extractComponentType(p, lbl, i, meth.Name(), meth.Type()) + } + for i := 0; i < tp.NumEmbeddeds(); i++ { + component := tp.EmbeddedType(i) + if isNonUnionTypeSetLiteral(component) { + component = createUnionFromType(component) + } + extractComponentType(p, lbl, -(i + 1), "", component) + } + return kind +} + +// processTupleType handles the extraction of tuple types. +func processTupleType(p *Profile, tp *types.Tuple, lbl Label) int { + kind := orm.TupleType.Index() + for i := 0; i < tp.Len(); i++ { + extractComponentType(p, lbl, i, "", tp.At(i).Type()) + } + return kind +} + +// processSignatureType handles the extraction of function signature types. +func processSignatureType(p *Profile, tp *types.Signature, lbl Label) int { + kind := orm.SignatureType.Index() + params, results := tp.Params(), tp.Results() + if params != nil { + for i := 0; i < params.Len(); i++ { + params := params.At(i) + extractComponentType(p, lbl, i+1, "", params.Type()) + } + } + if results != nil { + for i := 0; i < results.Len(); i++ { + result := results.At(i) + extractComponentType(p, lbl, -(i + 1), "", result.Type()) + } + } + if tp.Variadic() { + p.StoreTable(&orm.Variadic{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "Variadic"), + AssociatedNode: lbl.ID, + }) + } + return kind +} + +// processMapType handles the extraction of map types. +func processMapType(p *Profile, tp *types.Map, lbl Label) int { + kind := orm.MapType.Index() + extractKeyType(p, lbl, tp.Key()) + extractElementType(p, lbl, tp.Elem()) + return kind +} + +// processChanType handles the extraction of channel types. +func processChanType(p *Profile, tp *types.Chan, lbl Label) int { + kind := orm.ChanTypes[tp.Dir()].Index() + extractElementType(p, lbl, tp.Elem()) + return kind +} + +// processNamedType handles the extraction of named types. +func processNamedType(p *Profile, tp *types.Named, lbl Label) int { + kind := orm.NamedType.Index() + origintp := tp.Origin() + p.StoreTable(&orm.TypeName{ + Oid: lbl.ID, + Name: tp.Obj().Name(), + }) + underlying := origintp.Underlying() + extractUnderlyingType(p, lbl, underlying) + trackInstantiatedStructFields(p, tp, origintp) + + entitylbl, exists := p.Labeler.LookupObjectID(origintp.Obj(), lbl) + if entitylbl == InvalidLabel { + log.Printf("Omitting type-object binding for unknown object %v.\n", origintp.Obj()) + } else { + if !exists { + extractObject(p, origintp.Obj(), entitylbl) + } + p.StoreTable(&orm.TypeObject{ + Oid: lbl.ID, + Object: entitylbl.ID, + }) + } + + // ensure all methods have labels - note that methods do not have a + // parent scope, so they are not dealt with by `extractScopes` + for i := 0; i < origintp.NumMethods(); i++ { + meth := origintp.Method(i) + + extractMethod(p, meth) + } + + // associate all methods of underlying interface with this type + if underlyingInterface, ok := underlying.(*types.Interface); ok { + for i := 0; i < underlyingInterface.NumMethods(); i++ { + methlbl := extractMethod(p, underlyingInterface.Method(i)) + p.StoreTable(&orm.MethodHost{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, methlbl.ID), "MethodHost"), + Method: methlbl.ID, + Host: lbl.ID, + }) + } + } + return kind +} + +// processTypeParamType handles the extraction of type parameter types. +// It returns the kind of type and a bool indicating if the processing was successful. +func processTypeParamType(p *Profile, tp *types.TypeParam, lbl Label) (int, bool) { + kind := orm.TypeParamType.Index() + parentlbl := getTypeParamParentLabel(p, tp) + + // If the parent label is invalid, return false to indicate failure. + if parentlbl == InvalidLabel { + return kind, false + } + + // Extract the constraint type and get its label. + constraintLabel, _ := extractType(p, tp.Constraint()) + + // Store information about the type parameter in the `orm.TypeParam` table. + p.StoreTable(&orm.TypeParam{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "TypeParam"), + Tp: lbl.ID, + Name: tp.Obj().Name(), + Bound: constraintLabel.ID, + Parent: parentlbl.ID, + Index: tp.Index(), + }) + + // Return the kind of type and true to indicate success. + return kind, true +} + +// processUnionType handles the extraction of union types. +func processUnionType(p *Profile, tp *types.Union, lbl Label) int { + kind := orm.TypeSetLiteral.Index() + for i := 0; i < tp.Len(); i++ { + term := tp.Term(i) + tildeStr := "" + if term.Tilde() { + tildeStr = "~" + } + extractComponentType(p, lbl, i, tildeStr, term.Type()) + } + return kind +} + +// storeGeneralTypeInfo stores the common type information in the ORM table after +// the type-specific processing has been completed. +func storeGeneralTypeInfo(p *Profile, tp types.Type, lbl Label, kind int) { + rawString := trimRootSuffix(p, tp.String()) + formatTypeString := trimRootSuffix(p, getTypeString(tp)) + + // Store the general type information in the `orm.Type` table. + p.StoreTable(&orm.Type{ + Oid: lbl.ID, + Kind: kind, + FormatString: formatTypeString, + RawString: rawString, + }) +} + +func trimRootSuffix(p *Profile, s string) string { + return strings.ReplaceAll(s, fmt.Sprintf("_%s/", p.SrcRoot), "") +} + +type typeWriter struct { + buf *bytes.Buffer + tparams *types.TypeParamList // local type parameters +} + +func newTypeWriter(buf *bytes.Buffer) *typeWriter { + return &typeWriter{buf, nil} +} + +func getTypeString(tp types.Type) string { + var buf bytes.Buffer + w := newTypeWriter(&buf) + w.typ(tp) + return buf.String() +} + +func (w *typeWriter) byte(b byte) { + w.buf.WriteByte(b) + if b == ',' || b == ';' { + w.buf.WriteByte(' ') + } +} + +func (w *typeWriter) string(s string) { + w.buf.WriteString(s) +} + +func (w *typeWriter) typ(typ types.Type) { + switch t := typ.(type) { + case *types.Signature: + w.string("func") + w.signature(t) + default: + // Skip other Types. + // Just treat like as the above context as 'rawString' + w.string(t.String()) + } +} + +func (w *typeWriter) tParamList(list []*types.TypeParam) { + w.byte('[') + var prev types.Type + for i, tpar := range list { + // Determine the type parameter and its constraint. + // list is expected to hold type parameter names + if tpar == nil { + continue + } + if i > 0 { + if tpar.Constraint() != prev { + // bound changed - write previous one before advancing + w.byte(' ') + w.typ(prev) + } + w.byte(',') + } + prev = tpar.Constraint() + w.typ(tpar) + } + if prev != nil { + w.byte(' ') + w.typ(prev) + } + w.byte(']') +} + +func (w *typeWriter) tuple(tup *types.Tuple, variadic bool) { + // fill a filed, just keep type info + w.byte('(') + if tup != nil { + for i := 0; i < tup.Len(); i++ { + if i > 0 { + w.byte(',') + } + v := tup.At(i) + typ := v.Type() + if variadic && i == tup.Len()-1 { + if s, ok := typ.(*types.Slice); ok { + w.string("...") + typ = s.Elem() + } else { + // special case: + // append(s, "foo"...) leads to signature func([]byte, string...) + if t, _ := typ.Underlying().(*types.Basic); t == nil || t.Kind() != types.String { + continue + } + w.typ(typ) + w.string("...") + continue + } + } + w.typ(typ) + } + } + w.byte(')') +} + +func list(l *types.TypeParamList) []*types.TypeParam { + if l == nil { + return nil + } + var rlist []*types.TypeParam + for i := 0; i < l.Len(); i++ { + rlist = append(rlist, l.At(i)) + } + return rlist +} + +func (w *typeWriter) signature(sig *types.Signature) { + if sig.TypeParams().Len() != 0 { + w.tParamList(list(sig.TypeParams())) + } + + w.tuple(sig.Params(), sig.Variadic()) + + n := sig.Results().Len() + if n == 0 { + // no result + return + } + + w.byte(' ') + if n == 1 && (sig.Results().At(0).Name() == "") { + // single unnamed result (if type hashing, name must be ignored) + w.typ(sig.Results().At(0).Type()) + return + } + + // multiple or named result(s) + w.tuple(sig.Results(), false) +} + +// extractMethod extracts a method `meth` and emits it to the objects table, then returns its label +func extractMethod(p *Profile, meth *types.Func) Label { + // get the receiver type of the method + recvtyp := meth.Type().(*types.Signature).Recv().Type() + recvtyplbl, populated := extractType(p, recvtyp) // ensure receiver type has been extracted + + // if the meth has type params, should do a populating before actually extracting it + if !populated { + log.Printf("\t populateTypeParamParents '%s' filePath:%s", recvtyp.String(), p.Path) + // Populate type parameter parents for methods. They do not appear as + // objects in any scope, so they have to be dealt with separately here. + populateTypeParamParents(p, meth.Type().(*types.Signature).TypeParams(), meth) + populateTypeParamParents(p, meth.Type().(*types.Signature).RecvTypeParams(), meth) + + // extract type params and + // update recvtyplbl value + recvtyplbl, populated = extractType(p, recvtyp) + + if !populated { + util.PrintTracebackAfterDetectedBadEntrance(2, + fmt.Errorf("extractMethod() can not populate for '%s' filePath:%s", recvtyp.String(), p.Path)) + } + } + + // if the method label does not exist, extract it + methlbl, exists := p.Labeler.MethodID(meth, recvtyplbl) + if !exists { + // Populate type parameter parents for methods. They do not appear as + // objects in any scope, so they have to be dealt with separately here. + populateTypeParamParents(p, meth.Type().(*types.Signature).TypeParams(), meth) + populateTypeParamParents(p, meth.Type().(*types.Signature).RecvTypeParams(), meth) + extractObject(p, meth, methlbl) + } + + return methlbl +} + +// extractObjectKindAndDebugInfo determines the kind and debug information for the provided object. +func extractObjectKindAndDebugInfo(obj types.Object, isBuiltin bool) (int, string) { + switch obj := obj.(type) { + case *types.PkgName: + return orm.PkgObjectType.Index(), orm.PkgObjectType.String() + case *types.TypeName: + if isBuiltin { + return orm.BuiltinTypeObjectType.Index(), orm.BuiltinTypeObjectType.String() + } + return orm.DeclTypeObjectType.Index(), orm.DeclTypeObjectType.String() + case *types.Const: + if isBuiltin { + return orm.BuiltinConstObjectType.Index(), orm.BuiltinConstObjectType.String() + } + return orm.DeclConstObjectType.Index(), orm.DeclConstObjectType.String() + case *types.Nil: + return orm.BuiltinConstObjectType.Index(), orm.BuiltinConstObjectType.String() + case *types.Var: + return orm.DeclVarObjectType.Index(), orm.DeclVarObjectType.String() + case *types.Builtin: + return orm.BuiltinFuncObjectType.Index(), orm.BuiltinFuncObjectType.String() + case *types.Func: + return orm.DeclFuncObjectType.Index(), orm.DeclFuncObjectType.String() + case *types.Label: + return orm.LabelObjectType.Index(), orm.LabelObjectType.String() + default: + log.Fatalf("unknown object of type %T", obj) + panic("unreachable") // for static analyzers + } +} + +// extractMethodReceiver handles the extraction of method receiver information. +func extractMethodReceiver(p *Profile, obj types.Object, lbl Label) { + sig, ok := obj.Type().(*types.Signature) + if !ok || sig.Recv() == nil { + return // Not a method or no receiver, so nothing to do. + } + + recv := sig.Recv() + recvlbl, exists := p.Labeler.ReceiverObjectID(recv, lbl) + if !exists { + extractObject(p, recv, recvlbl) // Extract the receiver object. + } + + p.StoreTable(&orm.MethodReceiver{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "MethodReceiver"), + Method: lbl.ID, + Receiver: recvlbl.ID, + }) +} + +// extractObject extracts a single object and emits it to the objects table. +func extractObject(p *Profile, obj types.Object, lbl Label) { + name := obj.Name() + isBuiltin := obj.Parent() == types.Universe + + kind, debuginfo := extractObjectKindAndDebugInfo(obj, isBuiltin) + + p.StoreTable(&orm.Object{ + Oid: lbl.ID, + Kind: kind, + Name: name, + DebugInfo: obj.Type().String() + debuginfo, + }) + + // For methods, additionally extract information about the receiver. + extractMethodReceiver(p, obj, lbl) +} + +// ForEachObject iterates over all objects labeled by this labeler, and invokes +// the provided callback with a writer for the trap file, the object, and its +// label. It returns true if any extra objects were labeled and false otherwise. +func (p *Profile) ForEachObject(cb func(*Profile, types.Object, Label)) bool { + // copy the objects into an array so that our behaviour is deterministic even + // if `cb` adds any new objects + i := 0 + objects := make([]types.Object, len(p.Labeler.ObjectLabels)) + for k := range p.Labeler.ObjectLabels { + objects[i] = k + i++ + } + + for _, object := range objects { + //log.Printf("\t %s %s [%v/%v] %v\n", cb, p.Path, idx, len(objects), object.Name()) + cb(p, object, p.Labeler.ObjectLabels[object]) + } + return len(p.Labeler.ObjectLabels) != len(objects) +} + +// getTypeLabelKey generates a unique key for the given type to be used for label lookup or creation. +func getTypeLabelKey(p *Profile, tp types.Type, lbl Label) (string, bool) { + buildTypeKey := func(typeStr string, parts ...string) (string, bool) { + return fmt.Sprintf("%s,%s;%s", p.Path, strings.Join(parts, ","), typeStr), true + } + + switch tp := tp.(type) { + case *types.Basic: + return buildTypeKey("basictype", fmt.Sprint(tp.Kind()), tp.String()) + case *types.Array: + elem, _ := extractType(p, tp.Elem()) + return buildTypeKey("arraytype", fmt.Sprint(tp.Len()), elem.String(), tp.String()) + case *types.Slice: + elem, _ := extractType(p, tp.Elem()) + return buildTypeKey("slicetype", elem.String(), tp.String()) + case *types.Struct: + var fieldParts []string + for i := 0; i < tp.NumFields(); i++ { + field := tp.Field(i) + fieldTypeLbl, _ := extractType(p, field.Type()) + name := field.Name() + if field.Embedded() { + name = "" + } + fieldParts = append(fieldParts, fmt.Sprintf("%s,{%s},%s", name, fieldTypeLbl, util.EscapeTrapSpecialChars(tp.Tag(i)))) + } + return buildTypeKey("structtype", strings.Join(fieldParts, ",")) + case *types.Pointer: + base, _ := extractType(p, tp.Elem()) + return buildTypeKey("pointertype", base.String(), tp.String()) + case *types.Interface: + var methodParts, embeddedParts []string + for i := 0; i < tp.NumMethods(); i++ { + method := tp.Method(i) + methodLabel, _ := extractType(p, method.Type()) + methodParts = append(methodParts, fmt.Sprint(method.Id()), methodLabel.String())) + } + for i := 0; i < tp.NumEmbeddeds(); i++ { + embeddedTypeLabel, _ := extractType(p, tp.EmbeddedType(i)) + embeddedParts = append(embeddedParts, embeddedTypeLabel.String()) + } + compStr := strings.Join(methodParts, ",") + ";" + strings.Join(embeddedParts, ",") + if tp.IsComparable() { + compStr += ";comparable" + } + return buildTypeKey("interfacetype", compStr) + case *types.Tuple: + var tupleParts []string + for i := 0; i < tp.Len(); i++ { + tuplePartLabel, _ := extractType(p, tp.At(i).Type()) + tupleParts = append(tupleParts, tuplePartLabel.String()) + } + return buildTypeKey("tupletype", strings.Join(tupleParts, ",")) + case *types.Signature: + var paramsParts, resultsParts []string + params, results := tp.Params(), tp.Results() + if params != nil { + for i := 0; i < params.Len(); i++ { + paramLabel, _ := extractType(p, params.At(i).Type()) + paramId := params.At(i).Id() + paramsParts = append(paramsParts, fmt.Sprintf("{%s,%s}", paramLabel, paramId)) + } + } + if results != nil { + for i := 0; i < results.Len(); i++ { + resultLabel, _ := extractType(p, results.At(i).Type()) + resultId := results.At(i).Id() + resultsParts = append(resultsParts, fmt.Sprintf("{%s,%s}", resultLabel, resultId)) + } + } + variadicPart := "" + if tp.Variadic() { + variadicPart = ";variadic" + } + return buildTypeKey("signaturetype", strings.Join(paramsParts, ","), ";", strings.Join(resultsParts, ","), variadicPart) + case *types.Map: + key, _ := extractType(p, tp.Key()) + value, _ := extractType(p, tp.Elem()) + return buildTypeKey("maptype", key.String(), value.String()) + case *types.Chan: + dir := tp.Dir() + elem, _ := extractType(p, tp.Elem()) + return buildTypeKey("chantype", fmt.Sprint(dir), elem.String()) + case *types.Named: + originType := tp.Origin() + entityLabel, exists := p.Labeler.LookupObjectID(originType.Obj(), lbl) + if entityLabel == InvalidLabel { + panic(fmt.Sprintf("Cannot construct label for named type %v (underlying object is %v).\n", originType, originType.Obj())) + } + if !exists { + extractObject(p, originType.Obj(), entityLabel) + } + return buildTypeKey("namedtype", entityLabel.String()) + case *types.TypeParam: + parentLabel := getTypeParamParentLabel(p, tp) + if parentLabel == InvalidLabel { + return InvalidLabel.String(), false + } + return buildTypeKey("typeparamtype", parentLabel.String(), tp.Obj().Name(), tp.String()) + case *types.Union: + var unionParts []string + for i := 0; i < tp.Len(); i++ { + term := tp.Term(i) + termLabel, _ := extractType(p, tp.Term(i).Type()) + if term.Tilde() { + unionParts = append(unionParts, "~"+termLabel.String()) + } else { + unionParts = append(unionParts, termLabel.String()) + } + } + return buildTypeKey("uniontype", strings.Join(unionParts, "|")) + default: + log.Fatalf("(getTypeLabelKey) unexpected type %T", tp) + return "", false + } +} + +// createTypeLabel creates a new label for the given type. +func createTypeLabel(p *Profile, tp types.Type, label Label) Label { + key, ok := getTypeLabelKey(p, tp, label) + if ok { + lbl := p.Labeler.GlobalID(key) + p.Labeler.TypeLabels[tp] = lbl + return lbl + } + return InvalidLabel +} + +// getTypeLabel looks up the label associated with `tp`, creating a new label if it does not have one yet. +// The second result indicates whether the label already existed. +func getTypeLabel(p *Profile, tp types.Type) (Label, bool) { + lbl, exists := p.Labeler.TypeLabels[tp] + if exists { + return lbl, true + } + return createTypeLabel(p, tp, lbl), false +} + +// extractKeyType extracts `key` as the key type of the map type `mp` +func extractKeyType(p *Profile, mp Label, key types.Type) { + p.StoreTable(&orm.KeyType{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, mp.ID), "KeyType"), + Map: mp.ID, + Tp: func(p *Profile, key types.Type) int64 { lb, _ := extractType(p, key); return lb.ID }(p, key), + }) +} + +// extractElementType extracts `element` as the element type of the container type `container` +func extractElementType(p *Profile, container Label, element types.Type) { + p.StoreTable(&orm.ElementType{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%vcontaineer#%v", p.Path, container.ID), "ElementType"), + Container: container.ID, + Tp: func(p *Profile, element types.Type) int64 { lb, _ := extractType(p, element); return lb.ID }(p, element), + }) +} + +// extractBaseType extracts `base` as the base type of the pointer type `ptr` +func extractBaseType(p *Profile, ptr Label, base types.Type) { + p.StoreTable(&orm.BaseType{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, ptr.ID), "BaseType"), + Ptr: ptr.ID, + Tp: func(p *Profile, base types.Type) int64 { lb, _ := extractType(p, base); return lb.ID }(p, base), + }) +} + +// extractUnderlyingType extracts `underlying` as the underlying type of the +// named type `named` +func extractUnderlyingType(p *Profile, named Label, underlying types.Type) { + p.StoreTable(&orm.UnderlyingType{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, named.ID), "UnderlyingType"), + Named: named.ID, + Tp: func(p *Profile, underlying types.Type) int64 { lb, _ := extractType(p, underlying); return lb.ID }(p, underlying), + }) +} + +// extractComponentType extracts `component` as the `idx`th component type of `parent` with name `name` +func extractComponentType(p *Profile, parent Label, idx int, name string, component types.Type) { + p.StoreTable(&orm.ComponentType{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, parent.ID), "ComponentType"), + Parent: parent.ID, + Index: idx, + Name: name, + Tp: func(p *Profile, component types.Type) int64 { lb, _ := extractType(p, component); return lb.ID }(p, component), + }) +} + +func extractExpr(p *Profile, expr ast.Expr, parent Label, idx int) { + if expr == nil { + return + } + lbl := p.Labeler.LocalID(expr) //Cautious here! + extractTypeOf(p, expr, lbl) + + var kind int + var debugInfo string + switch expr := expr.(type) { + case *ast.BadExpr: + kind = orm.BadExpr.Index() + debugInfo = orm.BadExpr.String() + case *ast.Ident: + if expr == nil { + return + } + kind = orm.IdentExpr.Index() + debugInfo = orm.IdentExpr.String() + + p.StoreTable(&orm.Literal{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "Literal"), + Expr: lbl.ID, + Value: expr.Name, + Raw: expr.Name, + }) + def := p.Package.TypesInfo.Defs[expr] + if def != nil { + defTyp, _ := extractType(p, def.Type()) + objlbl, exists := p.Labeler.LookupObjectID(def, defTyp) + if objlbl == InvalidLabel { + log.Printf("Omitting def binding to unknown object %v", def) + } else { + if !exists { + extractObject(p, def, objlbl) + } + p.StoreTable(&orm.Def{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "Def"), + Ident: lbl.ID, + Object: objlbl.ID, + }) + } + } + use := getObjectBeingUsed(p, expr) + if use != nil { + useTyp, _ := extractType(p, use.Type()) + objlbl, exists := p.Labeler.LookupObjectID(use, useTyp) + if objlbl == InvalidLabel { + log.Printf("Omitting use binding to unknown object %v", use) + } else { + if !exists { + extractObject(p, use, objlbl) + } + p.StoreTable(&orm.Uses{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "Uses"), + Ident: lbl.ID, + Object: objlbl.ID, + }) + } + } + case *ast.Ellipsis: + if expr == nil { + return + } + kind = orm.EllipsisExpr.Index() + debugInfo = orm.EllipsisExpr.String() + + extractExpr(p, expr.Elt, lbl, 0) + case *ast.BasicLit: + if expr == nil { + return + } + value := "" + switch expr.Kind { + case token.INT: + ival, _ := strconv.ParseInt(expr.Value, 0, 64) + value = strconv.FormatInt(ival, 10) + kind = orm.IntLitExpr.Index() + debugInfo = orm.IntLitExpr.String() + + case token.FLOAT: + value = expr.Value + kind = orm.FloatLitExpr.Index() + debugInfo = orm.FloatLitExpr.String() + + case token.IMAG: + value = expr.Value + kind = orm.ImagLitExpr.Index() + debugInfo = orm.ImagLitExpr.String() + + case token.CHAR: + value, _ = strconv.Unquote(expr.Value) + kind = orm.CharLitExpr.Index() + debugInfo = orm.CharLitExpr.String() + + case token.STRING: + value, _ = strconv.Unquote(expr.Value) + kind = orm.StringLitExpr.Index() + debugInfo = orm.StringLitExpr.String() + + default: + log.Fatalf("unknown literal kind %v", expr.Kind) + } + p.StoreTable(&orm.Literal{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "Literal"), + Expr: lbl.ID, + Value: value, + Raw: expr.Value, + }) + case *ast.FuncLit: + if expr == nil { + return + } + kind = orm.FuncLitExpr.Index() + debugInfo = orm.FuncLitExpr.String() + + extractExpr(p, expr.Type, lbl, 0) + extractStmt(p, expr.Body, lbl, 1) + case *ast.CompositeLit: + if expr == nil { + return + } + kind = orm.CompositeLitExpr.Index() + debugInfo = orm.CompositeLitExpr.String() + + extractExpr(p, expr.Type, lbl, 0) + extractExprs(p, expr.Elts, lbl, 1, 1) + case *ast.ParenExpr: + if expr == nil { + return + } + kind = orm.ParenExpr.Index() + debugInfo = orm.ParenExpr.String() + + extractExpr(p, expr.X, lbl, 0) + case *ast.SelectorExpr: + if expr == nil { + return + } + kind = orm.SelectorExpr.Index() + debugInfo = orm.SelectorExpr.String() + + extractExpr(p, expr.X, lbl, 0) + extractExpr(p, expr.Sel, lbl, 1) + case *ast.IndexExpr: + if expr == nil { + return + } + typeofx := typeOf(p, expr.X) + if typeofx == nil { + // We are missing type information for `expr.X`, so we cannot + // determine whether this is a generic function instantiation + // or not. + kind = orm.IndexExpr.Index() + debugInfo = orm.IndexExpr.String() + } else { + if _, ok := typeofx.Underlying().(*types.Signature); ok { + kind = orm.GenericFunctionInstantiationExpr.Index() + debugInfo = orm.GenericFunctionInstantiationExpr.String() + } else { + // Can't distinguish between actual index expressions (into a + // map, array, slice, string or pointer to array) and generic + // type specialization expression, so we do it later in QL. + kind = orm.IndexExpr.Index() + debugInfo = orm.IndexExpr.String() + } + } + extractExpr(p, expr.X, lbl, 0) + extractExpr(p, expr.Index, lbl, 1) + case *ast.IndexListExpr: + if expr == nil { + return + } + typeofx := typeOf(p, expr.X) + if typeofx == nil { + // We are missing type information for `expr.X`, so we cannot + // determine whether this is a generic function instantiation + // or not. + kind = orm.GenericTypeInstantiationExpr.Index() + debugInfo = orm.GenericTypeInstantiationExpr.String() + } else { + if _, ok := typeofx.Underlying().(*types.Signature); ok { + kind = orm.GenericFunctionInstantiationExpr.Index() + debugInfo = orm.GenericFunctionInstantiationExpr.String() + } else { + kind = orm.GenericTypeInstantiationExpr.Index() + debugInfo = orm.GenericTypeInstantiationExpr.String() + } + } + extractExpr(p, expr.X, lbl, 0) + extractExprs(p, expr.Indices, lbl, 1, 1) + case *ast.SliceExpr: + if expr == nil { + return + } + kind = orm.SliceExpr.Index() + debugInfo = orm.SliceExpr.String() + + extractExpr(p, expr.X, lbl, 0) + extractExpr(p, expr.Low, lbl, 1) + extractExpr(p, expr.High, lbl, 2) + extractExpr(p, expr.Max, lbl, 3) + case *ast.TypeAssertExpr: + if expr == nil { + return + } + kind = orm.TypeAssertExpr.Index() + debugInfo = orm.TypeAssertExpr.String() + + extractExpr(p, expr.X, lbl, 0) + extractExpr(p, expr.Type, lbl, 1) + case *ast.CallExpr: + if expr == nil { + return + } + kind = orm.CallOrConversionExpr.Index() + debugInfo = orm.CallOrConversionExpr.String() + + extractExpr(p, expr.Fun, lbl, 0) + extractExprs(p, expr.Args, lbl, 1, 1) + if expr.Ellipsis.IsValid() { + p.StoreTable(&orm.HasEllipsis{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#expr#%v", p.Path, lbl.ID), "HasEllipsis"), + CallOrConversionExprId: lbl.ID, + }) + } + case *ast.StarExpr: + if expr == nil { + return + } + kind = orm.StarExpr.Index() + debugInfo = orm.StarExpr.String() + + extractExpr(p, expr.X, lbl, 0) + case *ast.KeyValueExpr: + if expr == nil { + return + } + kind = orm.KeyValueExpr.Index() + debugInfo = orm.KeyValueExpr.String() + + extractExpr(p, expr.Key, lbl, 0) + extractExpr(p, expr.Value, lbl, 1) + case *ast.UnaryExpr: + if expr == nil { + return + } + if expr.Op == token.TILDE { + kind = orm.TypeSetLiteralExpr.Index() + debugInfo = orm.TypeSetLiteralExpr.String() + } else { + tp := orm.UnaryExprs[expr.Op] + if tp == nil { + log.Fatalf("unsupported unary operator %s", expr.Op) + } + kind = tp.Index() + debugInfo = tp.String() + } + extractExpr(p, expr.X, lbl, 0) + case *ast.BinaryExpr: + if expr == nil { + return + } + _, isUnionType := typeOf(p, expr).(*types.Union) + if expr.Op == token.OR && isUnionType { + kind = orm.TypeSetLiteralExpr.Index() + flattenBinaryExprTree(p, expr, lbl, 0) + } else { + tp := orm.BinaryExprs[expr.Op] + if tp == nil { + log.Fatalf("unsupported binary operator %s", expr.Op) + } + kind = tp.Index() + debugInfo = tp.String() + + extractExpr(p, expr.X, lbl, 0) + extractExpr(p, expr.Y, lbl, 1) + } + case *ast.ArrayType: + if expr == nil { + return + } + kind = orm.ArrayTypeExpr.Index() + debugInfo = orm.ArrayTypeExpr.String() + + extractExpr(p, expr.Len, lbl, 0) + extractExpr(p, expr.Elt, lbl, 1) + case *ast.StructType: + if expr == nil { + return + } + kind = orm.StructTypeExpr.Index() + extractFields(p, expr.Fields, lbl, 0, 1, 0) + case *ast.FuncType: + if expr == nil { + return + } + kind = orm.FuncTypeExpr.Index() + debugInfo = orm.FuncTypeExpr.String() + + extractFields(p, expr.Params, lbl, 0, 1, 1) + extractFields(p, expr.Results, lbl, -1, -1, 2) + emitScopeNodeInfo(p, expr, lbl) + case *ast.InterfaceType: + if expr == nil { + return + } + kind = orm.InterfaceTypeExpr.Index() + debugInfo = orm.InterfaceTypeExpr.String() + + makeTypeSetLiteralsUnionTyped(p, expr.Methods) + extractFields(p, expr.Methods, lbl, 0, 1, 3) + case *ast.MapType: + if expr == nil { + return + } + kind = orm.MapTypeExpr.Index() + debugInfo = orm.MapTypeExpr.String() + + extractExpr(p, expr.Key, lbl, 0) + extractExpr(p, expr.Value, lbl, 1) + case *ast.ChanType: + if expr == nil { + return + } + tp := orm.ChanTypeExprs[expr.Dir] + if tp == nil { + log.Fatalf("unsupported channel direction %v", expr.Dir) + } + kind = tp.Index() + debugInfo = tp.String() + + extractExpr(p, expr.Value, lbl, 0) + default: + log.Fatalf("unknown expression of type %T", expr) + } + p.StoreTable(&orm.Expr{ + Oid: lbl.ID, + FileId: p.Labeler.GetFileLabel().ID, + Kind: kind, + ParentId: parent.ID, + Idx: idx, + DebugInfo: debugInfo, + }) + extractNodeLocation(p, expr, lbl) + extractValueOf(p, expr, lbl) +} + +// extractValueOf looks up the value of `expr`, and associates it with `expr` in +// the `consts` table +func extractValueOf(p *Profile, expr ast.Expr, lbl Label) { + tpVal := p.Package.TypesInfo.Types[expr] + + if tpVal.Value != nil { + // if Value is non-nil, the expression has a constant value + + // note that string literals in import statements do not have an associated + // Value and so do not get added to the table + + var value string + exact := tpVal.Value.ExactString() + switch tpVal.Value.Kind() { + case constant.String: + // we need to unquote strings + value = constant.StringVal(tpVal.Value) + exact = constant.StringVal(tpVal.Value) + case constant.Float: + flval, _ := constant.Float64Val(tpVal.Value) + value = fmt.Sprintf("%.20g", flval) + case constant.Complex: + rl, _ := constant.Float64Val(constant.Real(tpVal.Value)) + img, _ := constant.Float64Val(constant.Imag(tpVal.Value)) + value = fmt.Sprintf("(%.20g + %.20gi)", rl, img) + default: + value = tpVal.Value.ExactString() + } + + p.StoreTable(&orm.ConstValue{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "ConstValue"), + Expr: lbl.ID, + Value: value, + Exact: exact, + }) + } else if tpVal.IsNil() { + p.StoreTable(&orm.ConstValue{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "ConstValue"), + Expr: lbl.ID, + Value: "nil", + Exact: "nil", + }) + } +} + +// emitScopeNodeInfo associates an AST node with its induced scope, if any +func emitScopeNodeInfo(p *Profile, nd ast.Node, lbl Label) { + scope, exists := p.Package.TypesInfo.Scopes[nd] + if exists { + p.StoreTable(&orm.ScopeNode{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "ScopeNode"), + Node: lbl.ID, + Scope: p.Labeler.ScopeID(scope, p.Package.Types).ID, + }) + } +} + +// extractFields extracts AST information for a list of fields, which are children of +// the given parent +// `idx` is the index of the first child in the list, and `dir` is the index increment of +// each child over its preceding child (usually either 1 for assigning increasing indices, or +// -1 for decreasing indices) +// and 'tp' is type for field kind +func extractFields(p *Profile, fields *ast.FieldList, parent Label, idx int, dir int, tp int) { + if fields == nil || fields.List == nil { + return + } + for _, field := range fields.List { + lbl := p.Labeler.LocalID(field) + p.StoreTable(&orm.Fields{ + Oid: lbl.ID, + ParentId: parent.ID, + Idx: idx, + Kind: tp, + }) + extractNodeLocation(p, field, lbl) + if field.Names != nil { + for i, name := range field.Names { + extractExpr(p, name, lbl, i+1) + } + } + extractExpr(p, field.Type, lbl, 0) + extractExpr(p, field.Tag, lbl, -1) + extractDoc(p, field.Doc, lbl) + idx += dir + } +} + +// extractStmt extracts AST information for a given statement and all other statements or expressions +// nested inside it +func extractStmt(p *Profile, stmt ast.Stmt, parent Label, idx int) { + if stmt == nil { + return + } + + lbl := p.Labeler.LocalID(stmt) + var kind int + var debugInfo string + switch stmt := stmt.(type) { + case *ast.BadStmt: + kind = orm.BadStmtType.Index() + debugInfo = orm.BadStmtType.String() + case *ast.DeclStmt: + if stmt == nil { + return + } + kind = orm.DeclStmtType.Index() + debugInfo = orm.DeclStmtType.String() + + extractDecl(p, stmt.Decl, lbl, 0) + case *ast.EmptyStmt: + kind = orm.EmptyStmtType.Index() + debugInfo = orm.EmptyStmtType.String() + case *ast.LabeledStmt: + if stmt == nil { + return + } + kind = orm.LabeledStmtType.Index() + debugInfo = orm.LabeledStmtType.String() + + extractExpr(p, stmt.Label, lbl, 0) + extractStmt(p, stmt.Stmt, lbl, 1) + case *ast.ExprStmt: + if stmt == nil { + return + } + kind = orm.ExprStmtType.Index() + debugInfo = orm.ExprStmtType.String() + + extractExpr(p, stmt.X, lbl, 0) + case *ast.SendStmt: + if stmt == nil { + return + } + kind = orm.SendStmtType.Index() + debugInfo = orm.SendStmtType.String() + + extractExpr(p, stmt.Chan, lbl, 0) + extractExpr(p, stmt.Value, lbl, 1) + case *ast.IncDecStmt: + if stmt == nil { + return + } + if stmt.Tok == token.INC { + kind = orm.IncStmtType.Index() + debugInfo = orm.IncStmtType.String() + + } else if stmt.Tok == token.DEC { + kind = orm.DecStmtType.Index() + debugInfo = orm.DecStmtType.String() + + } else { + log.Fatalf("unsupported increment/decrement operator %v", stmt.Tok) + } + extractExpr(p, stmt.X, lbl, 0) + case *ast.AssignStmt: + if stmt == nil { + return + } + tp := orm.AssignStmtTypes[stmt.Tok] + if tp == nil { + log.Fatalf("unsupported assignment statement with operator %v", stmt.Tok) + } + kind = tp.Index() + debugInfo = tp.String() + + extractExprs(p, stmt.Lhs, lbl, -1, -1) + extractExprs(p, stmt.Rhs, lbl, 1, 1) + case *ast.GoStmt: + if stmt == nil { + return + } + kind = orm.GoStmtType.Index() + debugInfo = orm.GoStmtType.String() + + extractExpr(p, stmt.Call, lbl, 0) + case *ast.DeferStmt: + if stmt == nil { + return + } + kind = orm.DeferStmtType.Index() + debugInfo = orm.DeferStmtType.String() + + extractExpr(p, stmt.Call, lbl, 0) + case *ast.ReturnStmt: + kind = orm.ReturnStmtType.Index() + debugInfo = orm.ReturnStmtType.String() + + extractExprs(p, stmt.Results, lbl, 0, 1) + case *ast.BranchStmt: + if stmt == nil { + return + } + switch stmt.Tok { + case token.BREAK: + kind = orm.BreakStmtType.Index() + debugInfo = orm.BreakStmtType.String() + + case token.CONTINUE: + kind = orm.ContinueStmtType.Index() + debugInfo = orm.ContinueStmtType.String() + + case token.GOTO: + kind = orm.GotoStmtType.Index() + debugInfo = orm.GotoStmtType.String() + + case token.FALLTHROUGH: + kind = orm.FallthroughStmtType.Index() + debugInfo = orm.FallthroughStmtType.String() + + default: + log.Fatalf("unsupported branch statement type %v", stmt.Tok) + } + extractExpr(p, stmt.Label, lbl, 0) + case *ast.BlockStmt: + if stmt == nil { + return + } + kind = orm.BlockStmtType.Index() + debugInfo = orm.BlockStmtType.String() + + extractStmts(p, stmt.List, lbl, 0, 1) + emitScopeNodeInfo(p, stmt, lbl) + case *ast.IfStmt: + if stmt == nil { + return + } + kind = orm.IfStmtType.Index() + debugInfo = orm.IfStmtType.String() + + extractStmt(p, stmt.Init, lbl, 0) + extractExpr(p, stmt.Cond, lbl, 1) + extractStmt(p, stmt.Body, lbl, 2) + extractStmt(p, stmt.Else, lbl, 3) + emitScopeNodeInfo(p, stmt, lbl) + case *ast.CaseClause: + if stmt == nil { + return + } + kind = orm.CaseClauseType.Index() + debugInfo = orm.CaseClauseType.String() + + extractExprs(p, stmt.List, lbl, -1, -1) + extractStmts(p, stmt.Body, lbl, 0, 1) + emitScopeNodeInfo(p, stmt, lbl) + case *ast.SwitchStmt: + if stmt == nil { + return + } + kind = orm.ExprSwitchStmtType.Index() + debugInfo = orm.ExprSwitchStmtType.String() + + extractStmt(p, stmt.Init, lbl, 0) + extractExpr(p, stmt.Tag, lbl, 1) + extractStmt(p, stmt.Body, lbl, 2) + emitScopeNodeInfo(p, stmt, lbl) + case *ast.TypeSwitchStmt: + if stmt == nil { + return + } + kind = orm.TypeSwitchStmtType.Index() + debugInfo = orm.TypeSwitchStmtType.String() + + extractStmt(p, stmt.Init, lbl, 0) + extractStmt(p, stmt.Assign, lbl, 1) + extractStmt(p, stmt.Body, lbl, 2) + emitScopeNodeInfo(p, stmt, lbl) + case *ast.CommClause: + if stmt == nil { + return + } + kind = orm.CommClauseType.Index() + debugInfo = orm.CommClauseType.String() + + extractStmt(p, stmt.Comm, lbl, 0) + extractStmts(p, stmt.Body, lbl, 1, 1) + emitScopeNodeInfo(p, stmt, lbl) + case *ast.SelectStmt: + kind = orm.SelectStmtType.Index() + debugInfo = orm.SelectStmtType.String() + + extractStmt(p, stmt.Body, lbl, 0) + case *ast.ForStmt: + if stmt == nil { + return + } + kind = orm.ForStmtType.Index() + debugInfo = orm.ForStmtType.String() + + extractStmt(p, stmt.Init, lbl, 0) + extractExpr(p, stmt.Cond, lbl, 1) + extractStmt(p, stmt.Post, lbl, 2) + extractStmt(p, stmt.Body, lbl, 3) + emitScopeNodeInfo(p, stmt, lbl) + case *ast.RangeStmt: + if stmt == nil { + return + } + kind = orm.RangeStmtType.Index() + debugInfo = orm.RangeStmtType.String() + + extractExpr(p, stmt.Key, lbl, 0) + extractExpr(p, stmt.Value, lbl, 1) + extractExpr(p, stmt.X, lbl, 2) + extractStmt(p, stmt.Body, lbl, 3) + emitScopeNodeInfo(p, stmt, lbl) + default: + log.Fatalf("unknown statement of type %T", stmt) + } + p.StoreTable(&orm.Stmt{ + Oid: lbl.ID, + FileId: p.Labeler.GetFileLabel().ID, + Kind: kind, + ParentId: parent.ID, + Idx: idx, + DebugInfo: debugInfo, + }) + extractNodeLocation(p, stmt, lbl) +} + +// extractStmts extracts AST information for a list of statements, which are children of +// the given parent +// `idx` is the index of the first child in the list, and `dir` is the index increment of +// each child over its preceding child (usually either 1 for assigning increasing indices, or +// -1 for decreasing indices) +func extractStmts(p *Profile, stmts []ast.Stmt, parent Label, idx int, dir int) { + if stmts != nil { + for _, stmt := range stmts { + extractStmt(p, stmt, parent, idx) + idx += dir + } + } +} + +// extractExprs extracts AST information for a list of expressions, which are children of +// the given parent +// `idx` is the index of the first child in the list, and `dir` is the index increment of +// each child over its preceding child (usually either 1 for assigning increasing indices, or +// -1 for decreasing indices) +func extractExprs(p *Profile, exprs []ast.Expr, parent Label, idx int, dir int) { + if exprs != nil { + for _, expr := range exprs { + extractExpr(p, expr, parent, idx) + idx += dir + } + } +} + +func extractDecl(p *Profile, decl ast.Decl, parent Label, idx int) { + lbl := p.Labeler.LocalID(decl) + var kind int + var debugInfo string + switch decl := decl.(type) { + case *ast.BadDecl: + kind = orm.BadDeclType.Index() + debugInfo = orm.BadDeclType.String() + case *ast.GenDecl: + if decl == nil { + return + } + switch decl.Tok { + case token.IMPORT: + kind = orm.ImportDeclType.Index() + debugInfo = orm.ImportDeclType.String() + case token.CONST: + kind = orm.ConstDeclType.Index() + debugInfo = orm.ConstDeclType.String() + case token.TYPE: + kind = orm.TypeDeclType.Index() + debugInfo = orm.TypeDeclType.String() + case token.VAR: + kind = orm.VarDeclType.Index() + debugInfo = orm.VarDeclType.String() + default: + log.Fatalf("unknown declaration of kind %v", decl.Tok) + } + for i, spec := range decl.Specs { + extractSpec(p, spec, lbl, i) + } + extractDoc(p, decl.Doc, lbl) + case *ast.FuncDecl: + if decl == nil { + return + } + kind = orm.FuncDeclType.Index() + debugInfo = orm.FuncDeclType.String() + extractFields(p, decl.Recv, lbl, -1, -1, 4) + extractExpr(p, decl.Name, lbl, 0) + extractExpr(p, decl.Type, lbl, 1) + extractStmt(p, decl.Body, lbl, 2) + extractDoc(p, decl.Doc, lbl) + extractTypeParamDecls(p, decl.Type.TypeParams, lbl) + + // Note that we currently don't extract any kind of declaration for + // receiver type parameters. There isn't an explicit declaration, but + // we could consider the index/indices of an IndexExpr/IndexListExpr + // receiver as declarations. + default: + log.Fatalf("unknown declaration of type %T", decl) + } + p.StoreTable(&orm.Decl{ + Oid: lbl.ID, + FileId: p.Labeler.GetFileLabel().ID, + Kind: kind, + ParentId: parent.ID, + Idx: idx, + DebugInfo: debugInfo, + }) + extractNodeLocation(p, decl, lbl) +} + +// extractDoc extracts information about a doc comment group associated with a given element +func extractDoc(p *Profile, doc *ast.CommentGroup, elt Label) { + if doc != nil { + p.StoreTable(&orm.DocComment{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, elt.ID), "DocComment"), + AssociateObj: elt.ID, + CommentGroupId: p.Labeler.LocalID(doc).ID, + }) + } +} + +// extractSpec extracts AST information for the given declaration specifier +func extractSpec(p *Profile, spec ast.Spec, parent Label, idx int) { + lbl := p.Labeler.LocalID(spec) + var kind int + var debugInfo string + switch spec := spec.(type) { + case *ast.ImportSpec: + if spec == nil { + return + } + kind = orm.ImportSpecType.Index() + debugInfo = orm.ImportSpecType.String() + extractExpr(p, spec.Name, lbl, 0) + extractExpr(p, spec.Path, lbl, 1) + extractDoc(p, spec.Doc, lbl) + case *ast.ValueSpec: + if spec == nil { + return + } + kind = orm.ValueSpecType.Index() + debugInfo = orm.ValueSpecType.String() + for i, name := range spec.Names { + extractExpr(p, name, lbl, -(1 + i)) + } + extractExpr(p, spec.Type, lbl, 0) + extractExprs(p, spec.Values, lbl, 1, 1) + extractDoc(p, spec.Doc, lbl) + case *ast.TypeSpec: + if spec == nil { + return + } + if spec.Assign.IsValid() { + kind = orm.AliasSpecType.Index() + debugInfo = orm.AliasSpecType.String() + } else { + kind = orm.TypeDefSpecType.Index() + debugInfo = orm.TypeDefSpecType.String() + } + extractExpr(p, spec.Name, lbl, 0) + extractTypeParamDecls(p, spec.TypeParams, lbl) + extractExpr(p, spec.Type, lbl, 1) + extractDoc(p, spec.Doc, lbl) + } + p.StoreTable(&orm.Spec{ + Oid: lbl.ID, + Kind: kind, + ParentId: parent.ID, + Idx: idx, + DebugInfo: debugInfo, + }) + extractNodeLocation(p, spec, lbl) +} + +func extractCommentGroup(p *Profile, cg *ast.CommentGroup, parent Label, idx int) { + lbl := p.Labeler.LocalID(cg) + + p.StoreTable(&orm.CommentGroup{ + Oid: lbl.ID, + FileId: p.Labeler.GetFileLabel().ID, + Parent: parent.ID, + Idx: idx, + }) + extractNodeLocation(p, cg, lbl) + for i, c := range cg.List { + extractComment(p, c, lbl, i) + } +} + +// extractComment extracts information about a given comment +func extractComment(p *Profile, c *ast.Comment, parent Label, idx int) { + lbl := p.Labeler.LocalID(c) + rawText := c.Text + var kind int + var text string + if rawText[:2] == "//" { + kind = orm.SlashSlashComment.Index() + text = rawText[2:] + } else { + kind = orm.SlashStarComment.Index() + text = rawText[2 : len(rawText)-2] + } + p.StoreTable(&orm.Comment{ + Oid: lbl.ID, + FileId: p.Labeler.GetFileLabel().ID, + CommentType: kind, + Parent: parent.ID, + Index: int64(idx), + DebugInfo: text, + }) + extractNodeLocation(p, c, lbl) +} + +// extractLocation emits a location entity for the given entity +func extractLocation(p *Profile, entity Label, sl int, sc int, el int, ec int, so, eo int) { + hsLbl := Label{util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, entity.ID), "HasLocation")} //p.Labeler.GetFileLabel() + p.StoreTable(&orm.HasLocation{ + Oid: hsLbl.ID, + LocationObj: entity.ID, + LocationId: emitLocation(p, hsLbl, sl, el, sc, ec, so, eo).ID, + }) +} + +// emitLocation emits a location entity +func emitLocation(p *Profile, lbl Label, sl int, sc int, el int, ec int, so, eo int) Label { + locLbl := p.Labeler.GlobalID(fmt.Sprintf("loc,{%s},%d,%d,%d,%d", lbl, sl, sc, el, ec)) + p.StoreTable(&orm.Location{ + Oid: locLbl.ID, + StartLineNumber: sl, + StartColumnNumber: el, + EndLineNumber: sc, + EndColumnNumber: ec, + TokenStartOffset: so, + TokenEndOffset: eo, + }) + return locLbl +} + +// extractNodeLocation extracts location information for the given node +func extractNodeLocation(p *Profile, nd ast.Node, lbl Label) { + if nd == nil { + return + } + fset := p.Package.Fset + start, end, startOffest, endOffset := fset.Position(nd.Pos()), fset.Position(nd.End()), fset.Position(nd.Pos()).Offset, fset.Position(nd.End()).Offset + extractLocation(p, lbl, start.Line, start.Column, end.Line, end.Column-1, startOffest, endOffset) +} + +// extractLocalScope extracts symbol table information for the given scope and all its nested scopes +func extractLocalScope(p *Profile, scope *types.Scope, parentScopeLabel Label) { + scopeLabel := p.Labeler.ScopeID(scope, nil) + p.StoreTable(&orm.Scope{ + Oid: scopeLabel.ID, + Kind: orm.LocalScopeType.Index(), + DebugInfo: orm.LocalScopeType.String(), + }) + extractScopeLocation(p, scope, scopeLabel) + p.StoreTable(&orm.ScopeNesting{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, scopeLabel.ID), "ScopeNesting"), + Inner: scopeLabel.ID, + Outer: parentScopeLabel.ID, + }) + for i := 0; i < scope.NumChildren(); i++ { + childScope := scope.Child(i) + extractLocalScope(p, childScope, scopeLabel) + } + + extractObjects(p, scope, scopeLabel) +} + +// For a type `t` which is the type of a field of an interface type, return +// whether `t` a type set literal which is not a union type. Note that a field +// of an interface must be a method signature, an embedded interface type or a +// type set literal. +func isNonUnionTypeSetLiteral(t types.Type) bool { + if t == nil { + return false + } + switch t.Underlying().(type) { + case *types.Interface, *types.Union, *types.Signature: + return false + default: + return true + } +} + +// Given a type `t`, return a union with a single term that is `t` without a +// tilde. +func createUnionFromType(t types.Type) *types.Union { + return types.NewUnion([]*types.Term{types.NewTerm(false, t)}) +} + +// Go through a `FieldList` and update the types of all type set literals which +// are not already union types to be union types. We do this by changing the +// types stored in `tw.Package.TypesInfo.Types`. Type set literals can only +// occur in two places: a type parameter declaration or a type in an interface. +func makeTypeSetLiteralsUnionTyped(p *Profile, fields *ast.FieldList) { + if fields == nil || fields.List == nil { + return + } + for i := 0; i < len(fields.List); i++ { + x := fields.List[i].Type + if _, alreadyOverridden := p.TypesOverride[x]; !alreadyOverridden { + xtp := typeOf(p, x) + if isNonUnionTypeSetLiteral(xtp) { + p.TypesOverride[x] = createUnionFromType(xtp) + } + } + } +} + +func typeOf(tw *Profile, e ast.Expr) types.Type { + if val, ok := tw.TypesOverride[e]; ok { + return val + } + return tw.Package.TypesInfo.TypeOf(e) +} + +func flattenBinaryExprTree(tw *Profile, e ast.Expr, parent Label, idx int) int { + binaryexpr, ok := e.(*ast.BinaryExpr) + if ok { + idx = flattenBinaryExprTree(tw, binaryexpr.X, parent, idx) + idx = flattenBinaryExprTree(tw, binaryexpr.Y, parent, idx) + } else { + extractExpr(tw, e, parent, idx) + idx = idx + 1 + } + return idx +} + +func extractTypeParamDecls(tw *Profile, fields *ast.FieldList, parent Label) { + if fields == nil || fields.List == nil { + return + } + + // Type set literals can occur as the type in a type parameter declaration, + // so we ensure that they are union typed. + makeTypeSetLiteralsUnionTyped(tw, fields) + + idx := 0 + for _, field := range fields.List { + lbl := tw.Labeler.LocalID(field) + tw.StoreTable(&orm.TypeParamDecls{ + Oid: lbl.ID, + Parent: parent.ID, + Index: idx, + }) + extractNodeLocation(tw, field, lbl) + if field.Names != nil { + for i, name := range field.Names { + extractExpr(tw, name, lbl, i+1) + } + } + extractExpr(tw, field.Type, lbl, 0) + extractDoc(tw, field.Doc, lbl) + idx += 1 + } +} + +// populateTypeParamParents sets `parent` as the parent of the elements of `typeparams` +func populateTypeParamParents(tw *Profile, typeparams *types.TypeParamList, parent types.Object) { + if typeparams != nil { + for idx := 0; idx < typeparams.Len(); idx++ { + setTypeParamParent(tw, typeparams.At(idx), parent) + } + } +} + +// getobjectBeingUsed looks up `ident` in `tw.Package.TypesInfo.Uses` and makes +// some changes to the object to avoid returning objects relating to instantiated +// types. +func getObjectBeingUsed(tw *Profile, ident *ast.Ident) types.Object { + obj := tw.Package.TypesInfo.Uses[ident] + if obj == nil { + return nil + } + if override, ok := tw.ObjectsOverride[obj]; ok { + return override + } + if funcObj, ok := obj.(*types.Func); ok { + sig := funcObj.Type().(*types.Signature) + if recv := sig.Recv(); recv != nil { + recvType := recv.Type() + originType, isSame := tryGetGenericType(recvType) + + if originType == nil { + if pointerType, ok := recvType.(*types.Pointer); ok { + originType, isSame = tryGetGenericType(pointerType.Elem()) + } + } + + if originType == nil || isSame { + return obj + } + + for i := 0; i < originType.NumMethods(); i++ { + meth := originType.Method(i) + if meth.Name() == funcObj.Name() { + return meth + } + } + if interfaceType, ok := originType.Underlying().(*types.Interface); ok { + for i := 0; i < interfaceType.NumMethods(); i++ { + meth := interfaceType.Method(i) + if meth.Name() == funcObj.Name() { + return meth + } + } + } + log.Fatalf("Could not find method %s on type %s", funcObj.Name(), originType) + } + } + + return obj +} + +// tryGetGenericType returns the generic type of `tp`, and a boolean indicating +// whether it is the same as `tp`. +func tryGetGenericType(tp types.Type) (*types.Named, bool) { + if namedType, ok := tp.(*types.Named); ok { + originType := namedType.Origin() + return originType, namedType == originType + } + return nil, false +} + +// trackInstantiatedStructFields tries to give the fields of an instantiated +// struct type underlying `tp` the same labels as the corresponding fields of +// the generic struct type. This is so that when we come across the +// instantiated field in `tw.Package.TypesInfo.Uses` we will get the label for +// the generic field instead. +func trackInstantiatedStructFields(tw *Profile, tp, origintp *types.Named) { + if tp == origintp { + return + } + + if instantiatedStruct, ok := tp.Underlying().(*types.Struct); ok { + genericStruct, ok2 := origintp.Underlying().(*types.Struct) + if !ok2 { + log.Fatalf( + "Error: underlying type of instantiated type is a struct but underlying type of generic type is %s", + origintp.Underlying()) + } + + if instantiatedStruct.NumFields() != genericStruct.NumFields() { + log.Fatalf( + "Error: instantiated struct %s has different number of fields than the generic version %s (%d != %d)", + instantiatedStruct, genericStruct, instantiatedStruct.NumFields(), genericStruct.NumFields()) + } + + for i := 0; i < instantiatedStruct.NumFields(); i++ { + tw.ObjectsOverride[instantiatedStruct.Field(i)] = genericStruct.Field(i) + } + } +} + +func getTypeParamParentLabel(tw *Profile, tp *types.TypeParam) Label { + parent, exists := typeParamParent[tp] + if !exists { + return InvalidLabel + } + parentlbl, _ := tw.Labeler.ScopedObjectID(parent, func() Label { + // the 2 layer skipping the current call. + util.PrintTracebackAfterDetectedBadEntrance(2, + fmt.Errorf("getTypeLabel() called for parent of type parameter %s", tp.String())) + return InvalidLabel + }) + return parentlbl +} + +func setTypeParamParent(tw *Profile, tp *types.TypeParam, newobj types.Object) { + obj, exists := typeParamParent[tp] + if !exists { + typeParamParent[tp] = newobj + } else if newobj != obj { + util.PrintTracebackAfterDetectedBadEntrance(1, + fmt.Errorf("parent of type parameter '%s %s' being set to a different value: '%s' vs '%s' filePath:%s", tp.String(), tp.Constraint().String(), obj, newobj, tw.Path)) + } +} diff --git a/language/go/extractor/src/core/extract_mod.go b/language/go/extractor/src/core/extract_mod.go new file mode 100644 index 00000000..abdf61c7 --- /dev/null +++ b/language/go/extractor/src/core/extract_mod.go @@ -0,0 +1,275 @@ +package core + +import ( + "fmt" + "io" + "log" + "os" + "path/filepath" + "strings" + + "alipay.com/code_insight/coref-go-extractor/src/orm" + "alipay.com/code_insight/coref-go-extractor/src/util" + "golang.org/x/mod/modfile" + "modernc.org/mathutil" +) + +type commentGroupIdxAllocator struct { + nextIdx int +} + +func (cgIdxAlloc *commentGroupIdxAllocator) nextCgIdx() int { + ret := cgIdxAlloc.nextIdx + cgIdxAlloc.nextIdx++ + return ret +} + +// extractGoMod processes a go.mod file and stores its information in the +func (ex *Extraction) extractGoMod(path string) error { + normPath, err := filepath.EvalSymlinks(path) + if err != nil { + return fmt.Errorf("failed to evaluate symlinks for go.mod file %s: %w", path, err) + } + + path = normPath + absPath, err := filepath.Abs(path) + if err != nil { + return fmt.Errorf("failed to get absolute path for go.mod file %s: %w", path, err) + } + + ex.Lock() + if ex.SeenGoMods[absPath] { + ex.Unlock() + return nil + } + ex.SeenGoMods[absPath] = true + ex.Unlock() + + p, err := NewProfile(absPath, nil, ex.Db, &ex.IDManager, ex.SrcRootDir) + if err != nil { + return fmt.Errorf("failed to create a new profile for go.mod file %s: %w", absPath, err) + } + + ex.extractFileInfo(p, absPath) + + file, err := os.Open(absPath) + if err != nil { + return fmt.Errorf("failed to open go.mod file %s: %w", absPath, err) + } + defer file.Close() + + data, err := io.ReadAll(file) + if err != nil { + return fmt.Errorf("failed to read go.mod file %s: %w", absPath, err) + } + + // Call Parse method to parse the go.mod file + // The parsed result will be assigned to 'modfile' + modfile, err := modfile.Parse(absPath, data, nil) + if err != nil { + return fmt.Errorf("failed to parse go.mod file %s: %w", absPath, err) + } + + extractGoModFile(p, modfile) + return nil +} + +// extractGoModFile is responsible for breaking down the contents of a parsed go.mod +// file and storing its various components into the ORM database. +func extractGoModFile(p *Profile, modfile *modfile.File) { + cgIdxAlloc := commentGroupIdxAllocator{0} + + for idx, stmt := range modfile.Syntax.Stmt { + extractGoModExpr(p, stmt, p.Labeler.GetFileLabel(), idx, &cgIdxAlloc) + } + + extractGoModComments(p, modfile.Syntax, p.Labeler.GetFileLabel(), &cgIdxAlloc) + + emitGoModInfo(p, modfile) +} + +// emitGoModInfo stores information about a parsed go.mod file into the ORM +// database. It extracts the module path, version requirements, and the Go version +// specified in the go.mod file. +func emitGoModInfo(p *Profile, file *modfile.File) { + if file != nil { + modfileId := util.GetIDFromDigest(file.Syntax.Name, "ModFile") + // Store the mod file information. + p.StoreTable(&orm.ModFile{ + Oid: modfileId, + Path: file.Syntax.Name, + Name: file.Module.Mod.String(), + GoVersion: file.Go.Version, + }) + + // Read the content of the go.mod file only once. + content, err := util.ReadFile(file.Syntax.Name) + if err != nil { + util.PrintTracebackAfterDetectedBadEntrance(1, err) + content = []byte("") // Use an empty string if reading fails. + } + + // Store the file data with the already read content. + p.StoreTable(&orm.FileData{ + Oid: util.GetIDFromDigest(file.Syntax.Name, "FileData"), + FileId: modfileId, + Type: orm.GoModFile, + Content: string(content), + }) + + // Store each requirement. + for _, req := range file.Require { + p.StoreTable(&orm.ModRequire{ + Oid: util.GetIDFromDigest(file.Syntax.Name, "ModRequire"), + ModId: modfileId, + Require: req.Mod.String(), + }) + } + } +} + +// extractGoModExpr extracts expressions from a go.mod file and stores them in the Profile's data structure. +// It recursively processes various types of expressions and associated comments. +func extractGoModExpr(p *Profile, expr modfile.Expr, parent Label, idx int, cgIdxAlloc *commentGroupIdxAllocator) { + start, end := expr.Span() + lbl := Label{util.GetIDFromDigest(fmt.Sprintf("%s#%v#%v", p.Path, start.Line, end.Line), "ModExpr")} + var kind int + + switch specificExpr := expr.(type) { + case *modfile.CommentBlock: + kind = orm.ModCommentBlockType.Index() + case *modfile.LParen: + kind = orm.ModLParenType.Index() + case *modfile.RParen: + kind = orm.ModRParenType.Index() + case *modfile.Line, *modfile.LineBlock: + if line, ok := specificExpr.(*modfile.Line); ok { + kind = orm.ModLineType.Index() + storeModTokens(p, line.Token, lbl) + } else if block, ok := specificExpr.(*modfile.LineBlock); ok { + kind = orm.ModLineBlockType.Index() + storeModTokens(p, block.Token, lbl) + extractGoModExpr(p, &block.LParen, lbl, 0, cgIdxAlloc) + for lineIdx, line := range block.Line { + extractGoModExpr(p, line, lbl, lineIdx+1, cgIdxAlloc) + } + extractGoModExpr(p, &block.RParen, lbl, len(block.Line)+1, cgIdxAlloc) + } + default: + log.Fatalf("unknown go.mod expression of type %T", expr) + } + + p.StoreTable(&orm.ModExpr{ + Oid: lbl.ID, + Kind: kind, + Parent: parent.ID, + Idx: idx, + }) + + extractGoModComments(p, expr, lbl, cgIdxAlloc) + extractLocation(p, lbl, start.Line, start.LineRune, end.Line, end.LineRune, 0, 0) +} + +// storeModTokens stores tokens associated with a modfile.Line or modfile.LineBlock in the Profile's data structure. +func storeModTokens(p *Profile, tokens []string, lbl Label) { + for tokIdx, tok := range tokens { + p.StoreTable(&orm.ModToken{ + Oid: util.GetIDFromDigest(fmt.Sprintf("#%v:%v", lbl.ID, tokIdx), "ModToken"), + Token: tok, + Parent: lbl.ID, + Idx: tokIdx, + }) + } +} + +// extractGoModComments extracts comments associated with a go.mod expression and stores them in the Profile's data structure. +func extractGoModComments(p *Profile, expr modfile.Expr, exprlbl Label, cgIdxAlloc *commentGroupIdxAllocator) { + comments := expr.Comment() + + if len(comments.Before) == 0 && len(comments.Suffix) == 0 && len(comments.After) == 0 { + return + } + + // extract a pseudo `@commentgroup` for each expr that contains their associated comments + start, end := expr.Span() + grouplbl := Label{util.GetIDFromDigest(fmt.Sprintf("%s#%v#%v", p.Path, start.Line, end.Line), "GoModExprComment")} + p.StoreTable(&orm.CommentGroup{ + Oid: grouplbl.ID, + FileId: p.Labeler.GetFileLabel().ID, + Parent: p.Labeler.GetFileLabel().ID, + Idx: cgIdxAlloc.nextCgIdx(), + }) + + p.StoreTable(&orm.DocComment{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, exprlbl.ID), "DocComment"), + AssociateObj: exprlbl.ID, + CommentGroupId: grouplbl.ID, + }) + + var allComments []modfile.Comment + allComments = append(allComments, comments.Before...) + allComments = append(allComments, comments.Suffix...) + allComments = append(allComments, comments.After...) + + var startLine, startCol, endLine, endCol = 0, 0, 0, 0 + var first = true + idx := 0 + for _, comment := range allComments { + commentToken := strings.TrimSuffix(strings.TrimSuffix(comment.Token, "\n"), "\r") + extractGoModComment(p, comment, commentToken, grouplbl, idx) + idx++ + commentEndCol := comment.Start.LineRune + (len(commentToken) - 1) + if first { + startLine, startCol, endLine, endCol = comment.Start.Line, comment.Start.LineRune, comment.Start.Line, commentEndCol + first = false + } else { + startLine, startCol = lexMin(comment.Start.Line, comment.Start.LineRune, startLine, startCol) + endLine, endCol = lexMax(comment.Start.Line, commentEndCol, endLine, endCol) + } + } + + extractLocation(p, grouplbl, startLine, startCol, endLine, endCol, 0, 0) +} + +type GoModExprCommentWrapper struct { + expr modfile.Expr +} + +// lexMin returns the lexicographically smaller pair of integers. +// It first compares the first elements of the pairs; if equal, it then compares the second elements. +func lexMin(a1 int, a2 int, b1 int, b2 int) (int, int) { + if a1 < b1 { + return a1, a2 + } else if a1 > b1 { + return b1, b2 + } else { + return a1, mathutil.Min(a2, b2) + } +} + +// lexMax returns the lexicographically larger pair of integers. +// It first compares the first elements of the pairs; if equal, it then compares the second elements. +func lexMax(a1 int, a2 int, b1 int, b2 int) (int, int) { + if a1 < b1 { + return b1, b2 + } else if a1 > b1 { + return a1, a2 + } else { + return a1, mathutil.Max(a2, b2) + } +} + +func extractGoModComment(p *Profile, comment modfile.Comment, commentToken string, grouplbl Label, idx int) { + lbl := Label{util.GetIDFromDigest(fmt.Sprintf("%s#%v#%v", p.Path, comment.Start.Line, comment.Token), "GoModComment")} + + p.StoreTable(&orm.Comment{ + Oid: lbl.ID, + FileId: p.Labeler.GetFileLabel().ID, + CommentType: orm.SlashSlashComment.Index(), + Parent: grouplbl.ID, + Index: int64(idx), + DebugInfo: commentToken, + }) + + extractLocation(p, lbl, comment.Start.Line, comment.Start.LineRune, comment.Start.Line, comment.Start.LineRune+(len(commentToken)-1), 0, 0) +} diff --git a/language/go/extractor/src/core/extract_pkg.go b/language/go/extractor/src/core/extract_pkg.go new file mode 100644 index 00000000..ed410571 --- /dev/null +++ b/language/go/extractor/src/core/extract_pkg.go @@ -0,0 +1,614 @@ +package core + +import ( + "fmt" + "go/ast" + "go/scanner" + "go/token" + "go/types" + "io/ioutil" + "log" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + + "alipay.com/code_insight/coref-go-extractor/src/orm" + "alipay.com/code_insight/coref-go-extractor/src/util" + "golang.org/x/tools/go/packages" +) + +// Create a map to keep track of processed paths to avoid duplicates. +var processedPaths = make(map[string]struct{}) + +// pathIgnoreRegex is a regular expression that matches paths that should be ignored. +// The pathIgnoreRegex regular expression is defined to match any string that contains any of the following substrings: +// +// "vendor" +// ".test" +// "autogenerated" +// ".." +// +// Note: The regular expression also includes a placeholder for the file separator character, +// which is escaped and quoted using the regexp.QuoteMeta function. +// This ensures that the regular expression will work on any operating system, regardless of the file separator character used. +var pathIgnoreRegex = regexp.MustCompile(`.*(^|` + regexp.QuoteMeta(string(filepath.Separator)) + `)(vendor|\.test|autogenerated|\.\.)`) + +// isIgnorePath returns true if the given package's path matches the pathIgnoreRegex regular expression. +// Otherwise, it returns false. +func isIgnorePath(path string) bool { + // Check if the path matches the pathIgnoreRegex regular expression. + if pathIgnoreRegex.MatchString(path) { + return true + } + return false +} + +// autoGeneratedSignRegex is a regular expression that matches common phrases +// used in automatically generated code. +// determine whether a Go file was automatically generated: +// specific phrases that are commonly used in automatically generated code: +// +// "automatically generated", +// "do not edit", +// "generated by". +var autoGeneratedSignRegex = regexp.MustCompile(`(?i)(automatically generated|do not edit|generated by|do not modify)`) + +func isAutoGenerated(file *ast.File) bool { + // Check the comments in the file for any indicators of automatic generation. + for _, cg := range file.Comments { + for _, c := range cg.List { + if autoGeneratedSignRegex.MatchString(c.Text) { + return true + } + } + } + // Return false if no indication of automatic generation was found. + return false +} + +// extractPackage extract a package +func (ex *Extraction) extractPackage(pkg *packages.Package) { + // Compile the regular expression only once, before the loop. + sep := regexp.QuoteMeta(string(filepath.Separator)) + extractRe := regexp.MustCompile(fmt.Sprintf(`.*(^|%s)(%s)($|%s).*`, sep, regexp.QuoteMeta(ex.SrcRootDir), sep)) + + for _, astFile := range pkg.Syntax { + path := normalizedPath(astFile, pkg.Fset) + + // Skip files that do not match the extract regexp. + if !extractRe.MatchString(path) { + continue + } + + // Record autogenerated files as unextracted. + if isAutoGenerated(astFile) { + ex.recordAutoGeneratedFile(path) + continue + } + + // Avoid re-extracting a file that has already been processed. + if _, exists := processedPaths[path]; exists { + continue + } + processedPaths[path] = struct{}{} + + // Extract the file contents using a goroutine. + ex.WaitGroup.Add(1) + go func(astFile *ast.File) { + defer ex.WaitGroup.Done() + if err := ex.extractFile(astFile, pkg); err != nil { + util.PrintTracebackAfterDetectedBadEntrance(1, err) + } + }(astFile) + } +} + +// recordAutoGeneratedFile handles storing auto-generated files in the unextracted table. +func (ex *Extraction) recordAutoGeneratedFile(path string) { + ex.StatProfile.StoreTable(&orm.UnExtracted{ + Oid: ex.StatProfile.Labeler.FileLabelFor(path).ID, + Type: orm.TypeFile, + Name: localizedPath(path, ex.SrcRootDir), + }) +} + +// extractScopes extracts symbol table information for the package scope and all local scopes +// of the given package +func extractScopes(p *Profile, nd *ast.File, pkg *packages.Package) { + pkgScopeLabel := extractPackageScope(p, pkg) + fileScope := pkg.TypesInfo.Scopes[nd] + if fileScope != nil { + extractLocalScope(p, fileScope, pkgScopeLabel) + } +} + +// extractPackageScope extracts symbol table information for the given package +func extractPackageScope(p *Profile, pkg *packages.Package) Label { + pkgScope := pkg.Types.Scope() + pkgScopeLabel := Label{util.GetIDFromDigest(fmt.Sprintf("{%s}", util.EscapeTrapSpecialChars(pkg.Types.Path())), "PackageScope")} //p.Labeler.ScopeID(pkgScope, pkg.Types) + p.StoreTable(&orm.Scope{ + Oid: pkgScopeLabel.ID, + Kind: orm.PackageScopeType.Index(), + DebugInfo: orm.PackageScopeType.String(), + }) + p.StoreTable(&orm.ScopeNesting{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, pkgScopeLabel.ID), "ScopeNesting"), + Inner: pkgScopeLabel.ID, + Outer: p.Labeler.ScopeID(types.Universe, nil).ID, + }) + + extractObjects(p, pkgScope, pkgScopeLabel) + return pkgScopeLabel +} + +// extractObjects extracts all objects declared in the given scope +func extractObjects(p *Profile, scope *types.Scope, scopeLabel Label) { + for _, name := range scope.Names() { + obj := scope.Lookup(name) + lbl, exists := p.Labeler.ScopedObjectID(obj, func() Label { lb, _ := extractType(p, obj.Type()); return lb }) + if !exists { + // Populate type parameter parents for functions. Note that methods + // do not appear as objects in any scope, so they have to be dealt + // with separately in extractMethods. + if funcObj, ok := obj.(*types.Func); ok { + populateTypeParamParents(p, funcObj.Type().(*types.Signature).TypeParams(), obj) + populateTypeParamParents(p, funcObj.Type().(*types.Signature).RecvTypeParams(), obj) + } + // Populate type parameter parents for named types. Note that we + // skip type aliases as the original type should be the parent + // of any type parameters. + if typeNameObj, ok := obj.(*types.TypeName); ok && !typeNameObj.IsAlias() { + if tp, ok := typeNameObj.Type().(*types.Named); ok { + populateTypeParamParents(p, tp.TypeParams(), obj) + } + } + + extractObject(p, obj, lbl) + } + + if obj.Parent() != scope { + // this can happen if a scope is embedded into another with a `.` import. + continue + } + p.StoreTable(&orm.ObjectScope{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "ObjectScope"), + Object: lbl.ID, + Scope: scopeLabel.ID, + }) + } +} + +// extractScopeLocation extracts location information for the given scope +func extractScopeLocation(p *Profile, scope *types.Scope, lbl Label) { + fset := p.Package.Fset + start, end, startOffset, endOffset := fset.Position(scope.Pos()), fset.Position(scope.End()), fset.Position(scope.Pos()).Offset, fset.Position(scope.End()).Offset + extractLocation(p, lbl, start.Line, start.Column, end.Line, end.Column-1, startOffset, endOffset) +} + +// emitObjectType emits the type information for a given object +func emitObjectType(p *Profile, obj types.Object, lbl Label) { + if tp := obj.Type(); tp != nil { + p.StoreTable(&orm.ObjectType{ + Object: lbl.ID, + Tp: func(p *Profile, tp types.Type) int64 { lb, _ := extractType(p, tp); return lb.ID }(p, tp), + }) + } +} + +// extractError extracts information about a compilation error and stores it +// in the database using ORM. It parses the error position, extracts file system +// information, and stores error details in ORM table entries. +func (ex *Extraction) extractError(p *Profile, err packages.Error, pkglbl Label, idx int) { + lbl := p.Labeler.FreshID() + tag := orm.ErrorTags[err.Kind] + kind := orm.ErrorTypes[err.Kind].Index() + pos := err.Pos + file, line, col := parseErrorPosition(pos) + + if file != "" { + // Extract file information if the file path is non-empty. + ex.extractFileInfo(p, file) + ex.Lock() + flbl := ex.StatProfile.Labeler.FileLabelFor(file) + ex.Unlock() + + emitErrorDiagnostic(ex, p, flbl, err, tag, file, line, col) + } + + file = filepath.ToSlash(file) // Normalize file path to use forward slashes. + storeError(p, lbl, kind, err, pos, file, line, col, pkglbl, idx) +} + +// parseErrorPosition parses the error position string to extract the file path, +// line number, and column number. It returns a normalized file path and the +// extracted line and column numbers. If the position is empty or "-", a dummy +// error file is created. +func parseErrorPosition(pos string) (file string, line int, col int) { + var e error + + if pos == "" || pos == "-" { + file, e = createDummyErrorFile() + if e != nil { + log.Printf("Warning: failed to create dummy error file: %v", e) + } + return + } + + file, line, col = extractPositionComponents(pos) + file, e = getEvaluatedPath(file) + if e != nil { + log.Printf("Warning: failed to evaluate path for %s: %v", file, e) + } + + return +} + +// createDummyErrorFile creates a dummy file path for errors that lack specific +// position information. It constructs the path based on the current working +// directory and returns it. +func createDummyErrorFile() (string, error) { + wd, err := os.Getwd() + if err != nil { + log.Printf("Warning: failed to get working directory: %v", err) + return ".", nil + } + ewd, err := filepath.EvalSymlinks(wd) + if err != nil { + log.Printf("Warning: failed to evaluate symlinks for %s: %v", wd, err) + return wd, nil + } + return filepath.Join(ewd, "-"), nil +} + +// extractPositionComponents parses the error position string and extracts the +// file path, line number, and column number from it. It returns these components +// for further processing. +func extractPositionComponents(pos string) (file string, line int, col int) { + var ( + // file:line:col + threePartPos = regexp.MustCompile(`^(.+):(\d+):(\d+)$`) + // file:line + twoPartPos = regexp.MustCompile(`^(.+):(\d+)$`) + ) + + if parts := threePartPos.FindStringSubmatch(pos); parts != nil { + // "file:line:col" + col = parseNumber(parts[3], "column") + line = parseNumber(parts[2], "line") + file = parts[1] + } else if parts := twoPartPos.FindStringSubmatch(pos); parts != nil { + // "file:line" + line = parseNumber(parts[2], "line") + file = parts[1] + } else { + log.Printf("Warning: malformed error position `%s`", pos) + } + return +} + +// parseNumber parses a string representing a number and converts it to an integer. +// It is used to parse line and column numbers from the error position string. +// If parsing fails, it logs a warning and returns zero. +func parseNumber(numStr, label string) int { + num, err := strconv.Atoi(numStr) + if err != nil { + log.Printf("Warning: malformed %s number `%s`: %v", label, numStr, err) + } + return num +} + +// getEvaluatedPath evaluates and returns the absolute path for a given file path, +// resolving any symbolic links. +func getEvaluatedPath(rawfile string) (string, error) { + afile, err := filepath.Abs(rawfile) + if err != nil { + return "", err + } + return filepath.EvalSymlinks(afile) +} + +// emitErrorDiagnostic emits diagnostic information associated with an error. +func emitErrorDiagnostic(ex *Extraction, p *Profile, fileLabel Label, err packages.Error, tag, file string, line, col int) { + diagLbl := ex.StatProfile.Labeler.FreshID() + p.StoreTable(&orm.Diagnostic{ + Oid: diagLbl.ID, + Severity: 1, + ErrorTag: tag, + ErrorMessage: err.Msg, + FullErrorMessage: err.Msg, + LocationId: emitLocation(ex.StatProfile, fileLabel, line, col, line, col, line, col).ID, + }) + p.StoreTable(&orm.DiagnosticFor{ + Diagnostic: diagLbl.ID, + Compilation: ex.Label.ID, + FileNumber: ex.GetFileIdx(file), + FileNumberDiagnosticNumber: ex.GetNextErr(file), + }) +} + +// storeError stores information about an error in the ORM database. +func storeError(p *Profile, label Label, kind int, err packages.Error, pos, file string, line, col int, pkgLabel Label, idx int) { + transformed := filepath.ToSlash(file) + p.StoreTable(&orm.Error{ + Oid: label.ID, + Kind: kind, + Msg: err.Msg, + RawPos: pos, + File: transformed, + Line: line, + Col: col, + Pkg: pkgLabel.ID, + Idx: idx, + }) +} + +func localizedPath(absPath, prefix string) string { + if !strings.HasPrefix(absPath, prefix) { + log.Println("[Warning]getFileRelativePath: can't find path prefix") + return absPath + } + rpath := absPath[len(prefix):] + rpath = strings.TrimPrefix(rpath, "/") + return rpath +} + +// extractFileInfo extracts file-system level information for the given file +func (ex *Extraction) extractFileInfo(p *Profile, file string) { + ex.Lock() + if ex.SeenFile(file) { + ex.Unlock() + return + } + ex.MarkFileAsSeen(file) + ex.Unlock() + + md5Sum, err := util.GetFileDigest(file, "md5") + if err != nil { + log.Fatalf("Error getting MD5 digest for file %s: %v", file, err) + } + sha256Sum, err := util.GetFileDigest(file, "sha256") + if err != nil { + log.Fatalf("Error getting SHA256 digest for file %s: %v", file, err) + } + + path := filepath.ToSlash(util.GetFileFullPath(file)) + components := strings.Split(path, "/") + parentPath := "" + var parentLbl Label + + // Use a separate function to avoid repeated file reads for the content. + fileContent := readFileContent(file) + + for i, component := range components { + currentPath := getPath(component, i, parentPath) + isLastComponent := i == len(components)-1 + + // Handle the last component, which is the file itself. + if isLastComponent { + handleFile(p, ex, file, md5Sum, sha256Sum, currentPath, parentLbl, fileContent) + break + } + + // For intermediate components, create labels for folders. + lbl := p.Labeler.GlobalID(util.EscapeTrapSpecialChars(currentPath) + ";folder") + storeFolder(p, lbl, currentPath, parentLbl, i) + + if currentPath != "/" { + parentPath = currentPath + } + parentLbl = lbl + } +} + +// Read the file content once and return it as a string. +func readFileContent(file string) string { + data, err := util.ReadFile(file) + if err != nil { + util.PrintTracebackAfterDetectedBadEntrance(1, err) + return "" + } + return string(data) +} + +// Construct the path for the current component. +func getPath(component string, index int, parentPath string) string { + if index == 0 { + if component == "" { + return "/" + } + return component + } + return parentPath + "/" + component +} + +// Handle the storage of file-related ORM items. +func handleFile(p *Profile, ex *Extraction, file, md5Sum, sha256Sum, path string, parentLbl Label, content string) { + lbl := p.Labeler.FileLabelFor(file) + localPath := localizedPath(path, ex.SrcRootDir) + storeFile(p, lbl, localPath, md5Sum, sha256Sum, content, parentLbl) + storeCompilationCompilingFile(ex, p, file) +} + +// Store the file and related ORM items. +func storeFile(p *Profile, lbl Label, localPath, md5Sum, sha256Sum, content string, parentLbl Label) { + p.StoreTable(&orm.File{ + Oid: lbl.ID, + PkgOid: getPkgLableID(p.Package).ID, + Name: localPath, + Md5Sum: md5Sum, + Sha256Sum: sha256Sum, + }) + p.StoreTable(&orm.FileData{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "FileData"), + FileId: lbl.ID, + Type: orm.GoFile, + Content: content, + }) + p.StoreTable(&orm.ContainerParent{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "ContainerParent"), + Parent: parentLbl.ID, + Child: lbl.ID, + }) + p.StoreTable(&orm.HasLocation{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "HasLocation"), + LocationObj: lbl.ID, + LocationId: emitLocation(p, lbl, 0, 0, 0, 0, 0, 0).ID, + }) +} + +// Store the ORM item for the compilation compiling file. +func storeCompilationCompilingFile(ex *Extraction, p *Profile, file string) { + ex.Lock() + slbl := ex.StatProfile.Labeler.FileLabelFor(file) + p.StoreTable(&orm.CompilationCompilingFile{ + Oid: ex.Label.ID, + Num: ex.GetFileIdx(file), + File: slbl.ID, + }) + ex.Unlock() +} + +// Store folder ORM items for intermediate components. +func storeFolder(p *Profile, lbl Label, path string, parentLbl Label, idx int) { + p.StoreTable(&orm.Folder{ + Oid: lbl.ID, + Name: path, + }) + if idx > 0 { + p.StoreTable(&orm.ContainerParent{ + Oid: util.GetIDFromDigest(fmt.Sprintf("%v#%v", p.Path, lbl.ID), "ContainerParent"), + Parent: parentLbl.ID, + Child: lbl.ID, + }) + } +} + +// extractNumLines extracts the number of total, code, and comment lines in a file. +func extractNumLines(p *Profile, fileName string, ast *ast.File) error { + f := p.Package.Fset.File(ast.Pos()) + lineCount := f.LineCount() + + // Count lines of code by tokenizing. + linesOfCode, err := countLinesOfCode(f, fileName) + if err != nil { + return fmt.Errorf("error counting lines of code for %s: %w", fileName, err) + } + + // Count lines of comments by iterating over ast.Comments. + linesOfComments := countLinesOfComments(p.Package.Fset, ast.Comments) + + // Store the number of lines information. + p.StoreTable(&orm.NumberOfLine{ + Oid: p.Labeler.GetFileLabel().ID, + NumberOfTotalLines: lineCount, + NumberOfCodeLines: linesOfCode, + NumberOfCommentLines: linesOfComments, + }) + + return nil +} + +// countLinesOfCode counts the number of code lines in a file by tokenizing its content. +func countLinesOfCode(fset *token.File, fileName string) (int, error) { + src, err := ioutil.ReadFile(fileName) + if err != nil { + return 0, err + } + + var s scanner.Scanner + linesOfCode := 0 + lastCodeLine := -1 + + s.Init(fset, src, nil, 0) + for { + pos, tok, lit := s.Scan() + if tok == token.EOF { + break + } else if tok != token.ILLEGAL && !(tok == token.SEMICOLON && lit == "\n") { + posLine := fset.Position(pos).Line + if posLine > lastCodeLine { + linesOfCode++ + lastCodeLine = posLine + } + if strings.Contains(lit, "\n") { + // Token contains newlines, indicating multiple lines. + linesInToken := strings.Count(lit, "\n") + linesOfCode += linesInToken + lastCodeLine += linesInToken + } + } + } + + return linesOfCode, nil +} + +// countLinesOfComments counts the number of comment lines by iterating over comment groups. +func countLinesOfComments(fset *token.FileSet, comments []*ast.CommentGroup) int { + linesOfComments := 0 + for _, cg := range comments { + for _, c := range cg.List { + startLine := fset.Position(c.Pos()).Line + endLine := fset.Position(c.End()).Line + linesOfComments += endLine - startLine + 1 + } + } + return linesOfComments +} + +// normalizedPath computes the normalized path (with symlinks resolved) for the given file +func normalizedPath(ast *ast.File, fset *token.FileSet) string { + if !ast.Package.IsValid() { + util.PrintTracebackAfterDetectedBadEntrance(1, fmt.Errorf("ast.Package is invalid"), false) + return "" + } + + file := fset.File(ast.Package).Name() + path, err := filepath.EvalSymlinks(file) + if err != nil { + return file + } + return path +} + +// extractUniverseScope extracts symbol table information for the universe scope +func (ex *Extraction) extractUniverseScope() { + p, err := NewProfile("universe", nil, ex.Db, &ex.IDManager, ex.SrcRootDir) + if err != nil { + util.PrintTracebackAfterDetectedBadEntrance(1, err) + } + + lbl := Label{util.GetIDFromDigest("universe;scope", "Scope")} // p.Labeler.ScopeID(types.Universe, nil) + p.StoreTable(&orm.Scope{ + Oid: lbl.ID, + Kind: orm.UniverseScopeType.Index(), + DebugInfo: orm.UniverseScopeType.String(), + }) + extractObjects(p, types.Universe, lbl) + + // Always extract an empty interface type + extractType(p, types.NewInterfaceType([]*types.Func{}, []types.Type{})) +} + +// extractObjectTypes extracts type and receiver information for all objects +func extractObjectTypes(p *Profile) { + // calling `extractType` on a named type will extract all methods defined + // on it, which will add new objects. Therefore, we need to do this first + // before we loops over all objects and emit them. + changed := true + for changed { + changed = p.ForEachObject(extractObjectType) + } + changed = p.ForEachObject(emitObjectType) + if changed { + log.Printf("Warning: more objects were labeled while emitted object types") + } +} + +// extractObjectType extracts type and receiver information for a given object +func extractObjectType(p *Profile, obj types.Object, lbl Label) { + if tp := obj.Type(); tp != nil { + extractType(p, tp) + } +} diff --git a/language/go/extractor/src/core/extraction.go b/language/go/extractor/src/core/extraction.go new file mode 100644 index 00000000..46557364 --- /dev/null +++ b/language/go/extractor/src/core/extraction.go @@ -0,0 +1,495 @@ +package core + +import ( + "alipay.com/code_insight/coref-go-extractor/src/orm" + "crypto/md5" + "encoding/hex" + "fmt" + "go/types" + "io" + "log" + "os" + "path/filepath" + "runtime" + "strconv" + "strings" + "time" + + "alipay.com/code_insight/coref-go-extractor/src/config" + "alipay.com/code_insight/coref-go-extractor/src/util" + //_ "go.uber.org/automaxprocs" + "golang.org/x/tools/go/packages" +) + +var typeParamParent = make(map[*types.TypeParam]types.Object) + +func init() { + var maxGoRoutines int + envVars := []string{"EXTRACTOR_GO_MAX_GOROUTINES", "MAX_GOROUTINES"} + + // Try to get a valid max goroutines value from environment variables. + found := false + for _, envVar := range envVars { + if value, ok := os.LookupEnv(envVar); ok { + var err error + maxGoRoutines, err = strconv.Atoi(value) + if err == nil { + found = true + break + } else { + log.Printf("Warning: Environment variable %s has invalid value %s, defaulting based on MaxParallelism.", envVar, value) + } + } + } + + // If no valid environment variable was found, use the default value. + if !found { + maxGoRoutines = util.MaxParallelism() + } + + // Set GOMAXPROCS to the determined value + runtime.GOMAXPROCS(maxGoRoutines) + log.Printf("Max goroutines set to %d", maxGoRoutines) +} + +// ExtractWithFlags extracts the packages specified by the given patterns and build flags. +// It prepares the package loading configuration, loads the packages, handles any errors, +// and initiates the extraction process. This function also manages the logic to disable +// Go modules if necessary and logs the progress throughout the extraction process. +func ExtractWithFlags(buildFlags []string, patterns []string, c *config.Config) error { + startTime := time.Now() + extraction := NewExtraction(buildFlags, patterns, c) + defer extraction.Close() + log.Println("GO111MODULE=" + os.Getenv("GO111MODULE")) + + modFlags := filterModFlags(buildFlags) + loadMode := determineLoadMode(c.ParseRule) + cfg := &packages.Config{ + Mode: loadMode, + BuildFlags: buildFlags, + Tests: c.ParseRule.NeedTests, + } + + log.Printf("Extraction Settings:\n- Extraction: %s\n- Load Mode: %s\n- Go Build Flags: %s\n", + extraction.toString(), + cfg.Mode.String(), + strings.Join(cfg.BuildFlags, " "), + ) + log.Println("Running packages.Load.") + pkgs, err := packages.Load(cfg, patterns...) + if err != nil { + n := packages.PrintErrors(pkgs) + log.Printf("packages.Load() error:%v\n, the number of errors printed: %v\n", err, n) + if err := retryWithoutModules(cfg, patterns); err != nil { + return err + } + } + + log.Printf("Done running packages.Load(%v).\n", time.Since(startTime)) + if len(pkgs) == 0 { + log.Println("No packages found.") + return nil + } + + extraction.extractUniverseScope() + log.Printf("Done extracting universe scope(%v).\n", time.Since(startTime)) + + if c.ParseRule.NeedModFile || c.ParseRule.NeedScope { + extraction.extractExtraInfo(pkgs, modFlags, startTime) + } + + extractPackages(pkgs, extraction) + log.Printf("Done extracting packages(%v).\n", time.Since(startTime)) + + // Wait for file extraction finish + extraction.WaitGroup.Wait() + extraction.StatProfile.StoreTable(&orm.CompilationFinished{ + Oid: extraction.Label.ID, + CpuSeconds: 0.0, + ElapsedSeconds: time.Now().Sub(startTime).Seconds(), + }) + + return nil +} + +// filterModFlags filters out the build flags that are related to Go module behavior. +func filterModFlags(buildFlags []string) []string { + var modFlags []string + for _, flag := range buildFlags { + if strings.HasPrefix(flag, "-mod=") { + modFlags = append(modFlags, flag) + } + } + return modFlags +} + +// determineLoadMode calculates the loading mode for package data based on the parsing rules. +// The load mode defines what information should be included during package loading, such as +// file names, types, syntax trees, and more. +func determineLoadMode(parseRule *config.ParseConfig) packages.LoadMode { + loadMode := packages.NeedName | packages.NeedFiles | + packages.NeedTypes | packages.NeedDeps | packages.NeedImports | + packages.NeedTypesInfo | packages.NeedSyntax + if parseRule.NeedCompile { + loadMode |= packages.NeedCompiledGoFiles + } + return loadMode +} + +// retryWithoutModules attempts to reload the packages without Go module support if an error occurs +// during the initial load with modules enabled. This is a fallback mechanism for compatibility with +// codebases not yet using Go modules. +func retryWithoutModules(cfg *packages.Config, patterns []string) error { + log.Println("Go module mode enabled, trying with disabled...") + if err := os.Setenv("GO111MODULE", "off"); err != nil { + return err + } + _, err := packages.Load(cfg, patterns...) + return err +} + +// extractPackages iterates over the loaded packages and passes them to the extraction process. +// It skips packages that are marked to be ignored and logs the extraction progress for each package. +func extractPackages(pkgs []*packages.Package, extraction *Extraction) { + for _, pkg := range pkgs { + if isIgnorePath(pkg.PkgPath) { + log.Printf("Ignore folder: %s", pkg.PkgPath) + extraction.StatProfile.StoreTable(&orm.UnExtracted{ + Oid: getPkgLableID(pkg).ID, + Type: orm.TypeFolder, + Name: trimRootSuffix(extraction.StatProfile, pkg.PkgPath), + }) + continue + } + log.Printf("Extracting package %s ...", pkg.PkgPath) + // fill the table if scope is no extracted + if !extraction.Config.ParseRule.NeedScope { + extraction.StatProfile.StoreTable(&orm.Pkg{ + Oid: getPkgLableID(pkg).ID, + Path: trimRootSuffix(extraction.StatProfile, pkg.PkgPath), + Name: pkg.Name, + Scope: 0, + }) + } + extraction.extractPackage(pkg) + log.Printf("Done extracting package %s", pkg.PkgPath) + } +} + +// extractExtraInfo extracts module .go files or scope info based on the provided configuration settings. +func (ex *Extraction) extractExtraInfo(pkgs []*packages.Package, modFlags []string, startTime time.Time) { + pkgRoots, pkgDirs := ex.getPkgDirs(pkgs, modFlags) + wantedRoots := ex.getWantedRoots(pkgs, pkgRoots, pkgDirs) + + ex.processPackages(pkgs, startTime) + ex.extractModFiles(pkgs, wantedRoots, pkgDirs, pkgRoots) +} + +// getPkgDirs returns maps of package path to package root and source code directories. +func (ex *Extraction) getPkgDirs(pkgs []*packages.Package, modFlags []string) (map[string]string, map[string]string) { + pkgRoots := make(map[string]string) + pkgDirs := make(map[string]string) + + for _, pkg := range pkgs { + if isIgnorePath(pkg.PkgPath) { + continue + } + + if _, ok := pkgRoots[pkg.PkgPath]; !ok { + pkgRoots[pkg.PkgPath], pkgDirs[pkg.PkgPath] = ex.resolvePackageDirs(pkg.PkgPath, modFlags) + } + } + + return pkgRoots, pkgDirs +} + +// resolvePackageDirs determines the module and package directories for a given package path. +func (ex *Extraction) resolvePackageDirs(pkgPath string, modFlags []string) (string, string) { + mdir := util.GetModDir(pkgPath, modFlags...) + pdir := util.GetPkgDir(pkgPath, modFlags...) + + // Fall back to package directory if the module directory can't be determined. + if mdir == "" { + mdir = pdir + } + + return mdir, pdir +} + +// getWantedRoots identifies the root directories of packages to be extracted. +func (ex *Extraction) getWantedRoots(pkgs []*packages.Package, pkgRoots, pkgDirs map[string]string) map[string]bool { + wantedRoots := make(map[string]bool) + + for _, pkg := range pkgs { + if isIgnorePath(pkg.PkgPath) { + continue + } + + wantedRoots[pkgRoots[pkg.PkgPath]] = true + wantedRoots[pkgDirs[pkg.PkgPath]] = true + } + + return wantedRoots +} + +// processPackages processes packages to extract package scopes and handle package errors. +func (ex *Extraction) processPackages(pkgs []*packages.Package, startTime time.Time) { + for _, pkg := range pkgs { + if isIgnorePath(pkg.PkgPath) { + continue + } + + log.Printf("Processing package %s.", pkg.PkgPath) + ex.processSinglePackage(pkg, startTime) + } +} + +// processSinglePackage processes a single package for scope extraction and error handling. +func (ex *Extraction) processSinglePackage(pkg *packages.Package, startTime time.Time) { + log.Printf("Extracting types for package %s.", pkg.PkgPath) + + p, err := NewProfile(pkg.PkgPath, pkg, ex.Db, &ex.IDManager, ex.SrcRootDir) + if err != nil { + log.Fatal(err) + } + + lbl := getPkgLableID(pkg) + if ex.Config.ParseRule.NeedScope { + scope := extractPackageScope(p, pkg) + extractObjectTypes(p) + + p.StoreTable(&orm.Pkg{ + Oid: lbl.ID, + Path: trimRootSuffix(p, pkg.PkgPath), + Name: pkg.Name, + Scope: scope.ID, + }) + } + + // extract package load errors + if len(pkg.Errors) != 0 { + log.Printf("Warning: encountered errors extracting package `%s`:", pkg.PkgPath) + for i, err := range pkg.Errors { + log.Printf(" %s", err.Error()) + ex.extractError(p, err, lbl, i) + } + } + log.Printf("Done extracting types for package %s(%v).", pkg.PkgPath, time.Now().Sub(startTime)) +} + +// extractModFiles extracts the "go.mod" files from wanted package roots. +func (ex *Extraction) extractModFiles(pkgs []*packages.Package, wantedRoots map[string]bool, pkgDirs, pkgRoots map[string]string) { + for _, pkg := range pkgs { + if isIgnorePath(pkg.PkgPath) { + continue + } + + for root := range wantedRoots { + relDir, err := filepath.Rel(root, pkgDirs[pkg.PkgPath]) + if err != nil || isIgnorePath(relDir) { + continue + } + + if modPath := filepath.Join(pkgRoots[pkg.PkgPath], "go.mod"); util.FileExists(modPath) { + ex.extractSingleModFile(modPath) + } + + // We only need to process each root once. + break + } + } +} + +// extractSingleModFile extracts information from a single "go.mod" file. +func (ex *Extraction) extractSingleModFile(modPath string) { + log.Printf("Extracting %s", modPath) + start := time.Now() + + if err := ex.extractGoMod(modPath); err != nil { + log.Printf("Failed to extract go.mod: %s", err) + } + + duration := time.Since(start) + log.Printf("Done extracting %s (%dms)", modPath, duration.Milliseconds()) +} + +// MarkFileAsSeen marks a file as processed in the Extraction object by adding it to FileInfo. +func (ex *Extraction) MarkFileAsSeen(file string) { + // Create the map if it's nil. + if ex.FileInfo == nil { + ex.FileInfo = make(map[string]*FileInfo) + } + // Add the file to the map with a placeholder. + ex.FileInfo[file] = &FileInfo{} +} + +// SeenFile checks if a file has been processed. +func (ex *Extraction) SeenFile(file string) bool { + _, seen := ex.FileInfo[file] + return seen +} + +func (ex *Extraction) GetFileInfo(path string) *FileInfo { + if fileInfo, ok := ex.FileInfo[path]; ok { + return fileInfo + } + + ex.FileInfo[path] = &FileInfo{ex.NextFileID, 0} + ex.NextFileID += 1 + + return ex.FileInfo[path] +} + +// GetFileIdx For parsing order +func (ex *Extraction) GetFileIdx(path string) int { + return ex.GetFileInfo(path).Index +} + +// GetNextErr For recording order of parsed error files +func (ex *Extraction) GetNextErr(path string) int { + finfo := ex.GetFileInfo(path) + res := finfo.NextErr + finfo.NextErr += 1 + return res +} + +// Close finalizes the extraction process, commits the database transaction, and logs database info. +func (ex *Extraction) Close() { + if ex.Db != nil { + if err := orm.CommitTransaction(ex.Db); err != nil { + log.Println(err) + } + fi, err := os.Stat(getDbFile(ex.Config)) + if err != nil { + util.PrintTracebackAfterDetectedBadEntrance(1, err) + } + log.Printf("Successfully created database %v, size:%v \n", getDbFile(ex.Config), util.ByteCountSI(fi.Size())) + } +} + +func getDbFile(c *config.Config) string { + return filepath.Join(util.GetFileFullPath(c.Store.DBPath), c.Store.DBName) +} + +// NewExtraction creates the extraction process, initializes the database connection, +// and records pre-extraction metadata such as configuration parameters. +func NewExtraction(buildFlags []string, patterns []string, c *config.Config) *Extraction { + lblKey, err := generateLabelKey(buildFlags, patterns) + if err != nil { + log.Printf("Failed to generate label key: %v\n", err) + return nil + } + + dbfile := getDbFile(c) + if err := removeExistingDBFile(dbfile); err != nil { + log.Printf("Failed to remove existing database file: %v\n", err) + return nil + } + + wd, err := os.Getwd() + if err != nil { + log.Fatalf("Unable to determine current directory: %v\n", err) + return nil + } + + db, err := orm.SetupDatabaseAndMigrate(dbfile) + if err != nil { + log.Fatalf("Error setting up the database: %v\n", err) + return nil + } + + // sets up the initial profile for the extraction process. + profile, err := NewProfile(c.Store.DBPath, nil, db, &IDGenerator{NextID: 10000}, wd) + if err != nil { + util.PrintTracebackAfterDetectedBadEntrance(1, err) + return nil + } + + recordCompilationMetadata(profile, lblKey, c, buildFlags, patterns) + recordRuntimeInfo(profile, lblKey) + + return &Extraction{ + Label: Label{util.GetIDFromDigest(lblKey, "Lblkey")}, + StatProfile: profile, + NextFileID: 0, // Start from zero + IDManager: IDGenerator{NextID: profile.IDGenerator.NextID}, + FileInfo: make(map[string]*FileInfo), + SeenGoMods: make(map[string]bool), + Db: profile.DB, + Config: c, + SrcRootDir: wd, + } +} + +// generateLabelKey creates a unique key for identification based on the provided build flags and patterns. +func generateLabelKey(buildFlags []string, patterns []string) (string, error) { + hash := md5.New() + data := []string{"go"} + data = append(data, buildFlags...) + data = append(data, "--") + data = append(data, patterns...) + + for _, item := range data { + if _, err := io.WriteString(hash, " "+item); err != nil { + return "", err + } + } + + sum := hash.Sum(nil) + return fmt.Sprintf("#%s;compilation", hex.EncodeToString(sum)), nil +} + +// removeExistingDBFile removes an existing database file if it exists. +func removeExistingDBFile(dbfile string) error { + if util.FileExists(dbfile) { + if err := os.Remove(dbfile); err != nil { + return err + } + } + return nil +} + +// recordCompilationMetadata stores metadata related to the compilation environment. +func recordCompilationMetadata(profile *Profile, lblKey string, c *config.Config, buildFlags []string, patterns []string) { + // Retrieve the extractor path once and handle the error immediately. + extractorPath, err := util.GetExtractorPath() + if err != nil { + log.Fatalf("Unable to get extractor path: %s\n", err) + } + + // Construct a slice of all arguments to be stored, including the extractor path, build flags, + // a separator ("--"), and the extraction context info. + args := append([]string{extractorPath}, buildFlags...) + args = append(args, "--") // Separator to distinguish between flags and patterns. + args = append(args, c.ToString()) + args = append(args, patterns...) + + // Store each argument into the profile, incrementing the index for each argument. + for i, arg := range args { + compilationArg := orm.CompilationArgs{ + Oid: util.GetIDFromDigest(lblKey, fmt.Sprintf("CompilationArgs#%d", i)), + Num: i, + Arg: arg, + } + profile.StoreTable(compilationArg) + } +} + +// recordRuntimeInfo stores runtime information for the current extraction process. +func recordRuntimeInfo(profile *Profile, lblKey string) { + runtimeInfoOID := util.GetIDFromDigest(lblKey, "RuntimeInfo") + profile.StoreTable(&orm.RuntimeInfo{ + Oid: runtimeInfoOID, + BuildVersion: runtime.Version(), + ExtractorInfo: fmt.Sprintf("%v@%v by %v Created Time:%v", config.ExtractorVersion, config.ReleaseDate, config.Author, time.Now().Format("200601021504")), + GoOs: runtime.GOOS, + GoArch: runtime.GOARCH, + }) +} + +func (ex *Extraction) toString() string { + if ex != nil { + return fmt.Sprintf("srcRootDir: %s Config: %s", ex.SrcRootDir, ex.Config.ToString()) + } + return "" +} diff --git a/language/go/extractor/src/core/label.go b/language/go/extractor/src/core/label.go new file mode 100644 index 00000000..ca002f3a --- /dev/null +++ b/language/go/extractor/src/core/label.go @@ -0,0 +1,225 @@ +package core + +import ( + "alipay.com/code_insight/coref-go-extractor/src/util" + "fmt" + "go/types" + "golang.org/x/tools/go/packages" +) + +// Label represents a unique identifier for different elements such as files, nodes, scopes, etc. +type Label struct { + ID int64 +} + +// InvalidLabel is the zero value for a Label, used to represent an uninitialized or invalid state. +var InvalidLabel = Label{} + +// String returns the string representation of the Label. +func (lbl Label) String() string { + return fmt.Sprintf("%d", lbl.ID) +} + +// Labeler assigns unique labels to various program entities during the extraction process. +type Labeler struct { + Profile *Profile + IDGenerator *IDGenerator + FileLabel Label + NodeLabels map[interface{}]Label + ScopeLabels map[*types.Scope]Label + ObjectLabels map[types.Object]Label + TypeLabels map[types.Type]Label +} + +// newLabeler creates a new Labeler associated with the given Profile. +func newLabeler(p *Profile) *Labeler { + return &Labeler{ + Profile: p, + IDGenerator: p.IDGenerator, + FileLabel: InvalidLabel, + NodeLabels: make(map[interface{}]Label), + ScopeLabels: make(map[*types.Scope]Label), + ObjectLabels: make(map[types.Object]Label), + TypeLabels: make(map[types.Type]Label), + } +} + +// nextID safely increments and returns the next available ID from the shared IDGenerator. +func (l *Labeler) nextID() int64 { + l.IDGenerator.Lock() + defer l.IDGenerator.Unlock() + id := l.IDGenerator.NextID + l.IDGenerator.NextID++ + return id +} + +// GlobalID assigns a globally unique label to the given key and returns it. +func (l *Labeler) GlobalID(key string) Label { + return Label{ID: util.GetIDFromDigest(fmt.Sprintf("%v", key), "GlobalID")} +} + +// GetFileLabel returns the label for a file with path `path`. +func (l *Labeler) GetFileLabel() Label { + if l.FileLabel == InvalidLabel { + l.FileLabel = l.FileLabelFor(l.Profile.Path) + } + return l.FileLabel +} + +// FileLabelFor returns the label for the file for which the trap writer `tw` is associated +func (l *Labeler) FileLabelFor(path string) Label { + return Label{util.GetIDFromDigest(util.EscapeTrapSpecialChars(path), ";sourcefile")} +} + +// LocalID associates a label with the given AST node `nd` and returns it +func (l *Labeler) LocalID(nd interface{}) Label { + label, exists := l.NodeLabels[nd] + if !exists { + label = Label{util.GetIDFromDigest(fmt.Sprintf("%v#%v", nd, l.FreshID()), "LocalID")} + l.NodeLabels[nd] = label + } + return label +} + +// FreshID creates a new unique label. +func (l *Labeler) FreshID() Label { + return Label{ID: l.nextID()} +} + +// FieldID associates a label with the given field and returns it, together with +// a flag indicating whether the field already had a label associated with it. +func (l *Labeler) FieldID(field *types.Var, idx int, structlbl Label) (Label, bool) { + if label, exists := l.ObjectLabels[field]; exists { + return label, true + } + name := field.Name() + if name == "_" { + name = fmt.Sprintf("_%d", idx) + } + label := l.GlobalID(fmt.Sprintf("{%v},%s;field", structlbl, name)) + l.ObjectLabels[field] = label + return label, false +} + +// MethodID associates a label with the given method and returns it, together with +// a flag indicating whether the method already had a label associated with it. +func (l *Labeler) MethodID(method types.Object, recvtyplbl Label) (Label, bool) { + label, exists := l.ObjectLabels[method] + if !exists { + label = l.GlobalID(fmt.Sprintf("{%v},%s;method", recvtyplbl, method.Name())) + l.ObjectLabels[method] = label + } + return label, exists +} + +// LookupObjectID looks up the label associated with the given object and returns it; +// if the object does not have a label yet, it tries to construct one based on its scope and/or name. +func (l *Labeler) LookupObjectID(object types.Object, typelbl Label) (Label, bool) { + label, exists := l.ObjectLabels[object] + if !exists { + if object.Parent() == nil { + // blank identifiers and the pseudo-package `.` (from `import . "..."` imports) can only be referenced + // once, so we can use a fresh label for them + if object.Name() == "_" || object.Name() == "." { + label = Label{util.GetIDFromDigest(fmt.Sprintf("%v#%v", object, l.FreshID()), "LocalID")} //l.FreshID() + l.ObjectLabels[object] = label + return label, false + } + label = InvalidLabel + } else { + label, exists = l.ScopedObjectID(object, func() Label { return typelbl }) + } + } + return label, exists +} + +// ScopedObjectID associates a label with the given object and returns it, +// together with a flag indicating whether the object already had a label +// associated with it; the object must have a scope, since the scope's label is +// used to construct the label of the object. +// +// There is a special case for variables that are method receivers. When this is +// detected, we must construct a special label, as the variable can be reached +// from several files via the method. As the type label is required to construct +// the receiver object id, it is also required here. +func (l *Labeler) ScopedObjectID(object types.Object, getTypeLabel func() Label) (Label, bool) { + label, exists := l.ObjectLabels[object] + if !exists { + scope := object.Parent() + if scope == nil { + panic(fmt.Sprintf("Object has no scope: %v :: %v.\n", object, + l.Profile.Package.Fset.Position(object.Pos()))) + } else { + // associate method receiver objects to special keys, because those can be + // referenced from other files via their method + meth := findMethodWithGivenReceiver(object.Type(), object) + if meth == nil { + if pointerType, ok := object.Type().(*types.Pointer); ok { + meth = findMethodWithGivenReceiver(pointerType.Elem(), object) + } + } + + if meth != nil { + methlbl, _ := l.MethodID(meth, getTypeLabel()) + label, _ = l.ReceiverObjectID(object, methlbl) + } else { + scopeLbl := l.ScopeID(scope, object.Pkg()) + label = Label{util.GetIDFromDigest(fmt.Sprintf("{%v},%s;object", scopeLbl, object.Name()), "ScopedObjectID")} //l.GlobalID(fmt.Sprintf("{%v},%s;object", scopeLbl, object.Name())) + } + } + l.ObjectLabels[object] = label + } + return label, exists +} + +func findMethodWithGivenReceiver(tp types.Type, object types.Object) *types.Func { + if namedType, ok := tp.(*types.Named); ok { + for i := 0; i < namedType.NumMethods(); i++ { + meth := namedType.Method(i) + if object == meth.Type().(*types.Signature).Recv() { + return meth + } + } + } + return nil +} + +// ScopeID associates a label with the given scope and returns it +func (l *Labeler) ScopeID(scope *types.Scope, pkg *types.Package) Label { + label, exists := l.ScopeLabels[scope] + if !exists { + if scope == types.Universe { + label = l.GlobalID("universe;scope") + } else { + if pkg != nil && pkg.Scope() == scope { + // if this scope is the package scope + pkgLabel := l.GlobalID(util.EscapeTrapSpecialChars(pkg.Path()) + ";package") + label = l.GlobalID("{" + pkgLabel.String() + "};scope") + } else { + label = l.GlobalID("{" + fmt.Sprintf("scopeinfo:%p%v", scope, l.FreshID()) + "};scope") + } + } + l.ScopeLabels[scope] = label + } + return label +} + +func getPkgLableID(pkg *packages.Package) Label { + if pkg != nil { + return Label{util.GetIDFromDigest(util.EscapeTrapSpecialChars(pkg.PkgPath)+";pkg", "Pkg")} + } + return InvalidLabel +} + +// ReceiverObjectID associates a label with the given object and returns it, together with a flag indicating whether +// the object already had a label associated with it; the object must be the receiver of `methlbl`, since that label +// is used to construct the label of the object +func (l *Labeler) ReceiverObjectID(object types.Object, methlbl Label) (Label, bool) { + label, exists := l.ObjectLabels[object] + if !exists { + // if we can't, construct a special label + label = Label{util.GetIDFromDigest(fmt.Sprintf("{%v},%s;receiver", methlbl, object.Name()), "ReceiverObjectID")} //l.GlobalID(fmt.Sprintf("{%v},%s;receiver", methlbl, object.Name())) + l.ObjectLabels[object] = label + } + return label, exists +} diff --git a/language/go/extractor/src/core/profile.go b/language/go/extractor/src/core/profile.go new file mode 100644 index 00000000..ad1dad9a --- /dev/null +++ b/language/go/extractor/src/core/profile.go @@ -0,0 +1,77 @@ +package core + +import ( + "alipay.com/code_insight/coref-go-extractor/src/config" + "alipay.com/code_insight/coref-go-extractor/src/orm" + "go/ast" + "go/types" + "golang.org/x/tools/go/packages" + "gorm.io/gorm" + "log" + "os" + "sync" +) + +// IDGenerator provides a thread-safe mechanism for generating unique IDs. +type IDGenerator struct { + sync.Mutex // Embedding the mutex for simplicity, provides Lock and Unlock methods. + NextID int64 +} + +// FileInfo holds metadata about a specific file. +type FileInfo struct { + Index int // Index of the file. + NextErr int // Next error code for the file. +} + +// Extraction manages the state and configuration for the extraction process. +type Extraction struct { + sync.Mutex // Protects concurrent access to the fields. + Label Label // Initial label point. + StatProfile *Profile // Profile for database stats. + IDManager IDGenerator // Local ID generator. + WaitGroup sync.WaitGroup + + NextFileID int // Index of the next parsed file. + FileInfo map[string]*FileInfo // Information about each file. + SeenGoMods map[string]bool // Tracks whether a go.mod file has been resolved. + + Db *gorm.DB // Database connection. + Config *config.Config // Configuration settings. + SrcRootDir string // Root directory of source files. +} + +// Profile represents the profiling data for a specific file. +type Profile struct { + SrcRoot string + Labeler *Labeler + DB *gorm.DB + File *os.File + Path string + IDGenerator *IDGenerator + Package *packages.Package + TypesOverride map[ast.Expr]types.Type + ObjectsOverride map[types.Object]types.Object +} + +// NewProfile creates a new Profile instance for capturing profiling data. +func NewProfile(path string, pkg *packages.Package, db *gorm.DB, idGen *IDGenerator, srcRoot string) (*Profile, error) { + profile := &Profile{ + SrcRoot: srcRoot, + DB: db, + Path: path, + IDGenerator: idGen, + Package: pkg, + TypesOverride: make(map[ast.Expr]types.Type), + ObjectsOverride: make(map[types.Object]types.Object), + } + profile.Labeler = newLabeler(profile) + return profile, nil +} + +// StoreTable add one record to the Gorm +func (p *Profile) StoreTable(value interface{}) { + if err := orm.WriteRecord(p.DB, value); err != nil { + log.Fatalf("[Extraction Stop] DB write encounter err:%v", err) + } +} diff --git a/language/go/extractor/src/orm/data_decl.go b/language/go/extractor/src/orm/data_decl.go new file mode 100644 index 00000000..b0d0b972 --- /dev/null +++ b/language/go/extractor/src/orm/data_decl.go @@ -0,0 +1,664 @@ +package orm + +import ( + "go/ast" + "go/token" + gotypes "go/types" + + "golang.org/x/tools/go/packages" +) + +type CompilationTypeKind int + +// LocatableType is the type of program entities that have locations +var LocatableType = NewUnionType("@locatable") + +// NodeType is the type of AST nodes +var NodeType = NewUnionType("@node", LocatableType) + +// DocumentableType is the type of AST nodes to which documentation can be attached +var DocumentableType = NewUnionType("@documentable", NodeType) + +// ExprParentType is the type of AST nodes that can have expressions as children +var ExprParentType = NewUnionType("@exprparent", NodeType) + +// ModExprParentType is the type of go.mod nodes that can have go.mod expressions as children +var ModExprParentType = NewUnionType("@modexprparent", NodeType) + +// FieldParentType is the type of AST nodes that can have fields as children +var FieldParentType = NewUnionType("@fieldparent", NodeType) + +// StmtParentType is the type of AST nodes that can have statements as children +var StmtParentType = NewUnionType("@stmtparent", NodeType) + +// DeclParentType is the type of AST nodes that can have declarations as children +var DeclParentType = NewUnionType("@declparent", NodeType) + +// TypeParamDeclParentType is the type of AST nodes that can have type parameter declarations as children +var TypeParamDeclParentType = NewUnionType("@typeparamdeclparent", NodeType) + +// FuncDefType is the type of AST nodes that define functions, that is, function +// declarations and function literals +var FuncDefType = NewUnionType("@funcdef", StmtParentType, ExprParentType) + +// ScopeNodeType is the type of AST nodes that may have a scope attached to them +var ScopeNodeType = NewUnionType("@scopenode", NodeType) + +// CommentType is the type of comments +var CommentType = NewPrimaryKeyType("@comment", NodeType) + +// ExprType is the type of expression AST nodes +var ExprType = NewPrimaryKeyType("@expr", ExprParentType) + +// StmtType is the type of statement AST nodes +var StmtType = NewPrimaryKeyType("@stmt", ExprParentType, StmtParentType) + +// DeclType is the type of declaration AST nodes +var DeclType = NewPrimaryKeyType("@decl", ExprParentType, StmtParentType, FieldParentType) + +// TypeParamDeclType is the type of type parameter declaration AST nodes +var TypeParamDeclType = NewPrimaryKeyType("@typeparamdecl", DocumentableType, ExprParentType) + +// SpecType is the type of spec AST nodes +var SpecType = NewPrimaryKeyType("@spec", ExprParentType, DocumentableType) + +// TypeType is the type of types +var TypeType = NewPrimaryKeyType("@type") + +// CommentKind is a case type for distinguishing different kinds of comments +var CommentKind = NewCaseType(CommentType, "kind") + +// SlashSlashComment is the type of single-line comments starting with a double slash +var SlashSlashComment = CommentKind.NewBranch("@slashslashcomment") + +// SlashStarComment is the type of block comments delimited by stars and slashes +var SlashStarComment = CommentKind.NewBranch("@slashstarcomment") + +// ExprKind is a case type for distinguishing different kinds of expression AST nodes +var ExprKind = NewCaseType(ExprType, "kind") + +// BadExpr is type of bad (that is, unparseable) expression AST nodes +var BadExpr = ExprKind.NewBranch("@badexpr") + +// IdentExpr is the type of identifier expression AST nodes +var IdentExpr = ExprKind.NewBranch("@ident") + +// EllipsisExpr is the type of ellipsis expression AST nodes +var EllipsisExpr = ExprKind.NewBranch("@ellipsis") + +// BasicLitExpr is the type of basic (that is, primitive) literal expression AST nodes +var BasicLitExpr = NewUnionType("@basiclit") + +// IntLitExpr is a case type for dishinguishing different kinds of literal expression AST nodes +var IntLitExpr = ExprKind.NewBranch("@intlit", BasicLitExpr) + +// FloatLitExpr is the type of floating-point literal expression AST nodes +var FloatLitExpr = ExprKind.NewBranch("@floatlit", BasicLitExpr) + +// ImagLitExpr is the type of imaginary literal expression AST nodes +var ImagLitExpr = ExprKind.NewBranch("@imaglit", BasicLitExpr) + +// CharLitExpr is the type of character literal expression AST nodes +var CharLitExpr = ExprKind.NewBranch("@charlit", BasicLitExpr) + +// StringLitExpr is the type of string literal expression AST nodes +var StringLitExpr = ExprKind.NewBranch("@stringlit", BasicLitExpr) + +// FuncLitExpr is the type of function literal expression AST nodes +var FuncLitExpr = ExprKind.NewBranch("@funclit", FuncDefType) + +// CompositeLitExpr is the type of composite literal expression AST nodes +var CompositeLitExpr = ExprKind.NewBranch("@compositelit") + +// ParenExpr is the type of parenthesis expression AST nodes +var ParenExpr = ExprKind.NewBranch("@parenexpr") + +// SelectorExpr is the type of selector expression AST nodes +var SelectorExpr = ExprKind.NewBranch("@selectorexpr") + +// IndexExpr is the type of AST nodes for index expressions and generic type +// instantiation expressions with one type argument. Note that syntactically +// unambiguous generic instantiations will be extracted as +// `GenericTypeInstantiationExpr`. +var IndexExpr = ExprKind.NewBranch("@indexexpr") + +// GenericFunctionInstantiationExpr is the type of AST nodes that represent an instantiation +// of a generic type. These correspond to some index expression AST nodes and all index +// list expression AST nodes. +var GenericFunctionInstantiationExpr = ExprKind.NewBranch("@genericfunctioninstantiationexpr") + +// GenericTypeInstantiationExpr is the type of AST nodes that represent an instantiation +// of a generic type. These correspond to some index expression AST nodes and all index +// list expression AST nodes. Note some syntactically ambiguous instantations are +// extracted as an `IndexExpr` to be disambiguated in QL later. +var GenericTypeInstantiationExpr = ExprKind.NewBranch("@generictypeinstantiationexpr") + +// SliceExpr is the type of slice expression AST nodes +var SliceExpr = ExprKind.NewBranch("@sliceexpr") + +// TypeAssertExpr is the type of type assertion expression AST nodes +var TypeAssertExpr = ExprKind.NewBranch("@typeassertexpr") + +// CallOrConversionExpr is the type of call and conversion expression AST nodes +// (which cannot be distinguished by purely syntactic criteria) +var CallOrConversionExpr = ExprKind.NewBranch("@callorconversionexpr") + +// StarExpr is the type of star expression AST nodes +var StarExpr = ExprKind.NewBranch("@starexpr") + +// OperatorExpr is the type of operator expression AST nodes +var OperatorExpr = NewUnionType("@operatorexpr") + +// LogicalExpr is the type of logical operator expression AST nodes +var LogicalExpr = NewUnionType("@logicalexpr", OperatorExpr) + +// ArithmeticExpr is the type of arithmetic operator expression AST nodes +var ArithmeticExpr = NewUnionType("@arithmeticexpr", OperatorExpr) + +// BitwiseExpr is the type of bitwise operator expression AST nodes +var BitwiseExpr = NewUnionType("@bitwiseexpr", OperatorExpr) + +// UnaryExpr is the type of unary operator expression AST nodes +var UnaryExpr = NewUnionType("@unaryexpr", OperatorExpr) + +// LogicalUnaryExpr is the type of logical unary operator expression AST nodes +var LogicalUnaryExpr = NewUnionType("@logicalunaryexpr", UnaryExpr, LogicalExpr) + +// BitwiseUnaryExpr is the type of bitwise unary operator expression AST nodes +var BitwiseUnaryExpr = NewUnionType("@bitwiseunaryexpr", UnaryExpr, BitwiseExpr) + +// ArithmeticUnaryExpr is the type of arithmetic unary operator expression AST nodes +var ArithmeticUnaryExpr = NewUnionType("@arithmeticunaryexpr", UnaryExpr, ArithmeticExpr) + +// BinaryExpr is the type of binary operator expression AST nodes +var BinaryExpr = NewUnionType("@binaryexpr", OperatorExpr) + +// LogicalBinaryExpr is the type of logical binary operator expression AST nodes +var LogicalBinaryExpr = NewUnionType("@logicalbinaryexpr", BinaryExpr, LogicalExpr) + +// BitwiseBinaryExpr is the type of bitwise binary operator expression AST nodes +var BitwiseBinaryExpr = NewUnionType("@bitwisebinaryexpr", BinaryExpr, BitwiseExpr) + +// ArithmeticBinaryExpr is the type of arithmetic binary operator expression AST nodes +var ArithmeticBinaryExpr = NewUnionType("@arithmeticbinaryexpr", BinaryExpr, ArithmeticExpr) + +// ShiftExpr is the type of shift operator expression AST nodes +var ShiftExpr = NewUnionType("@shiftexpr", BitwiseBinaryExpr) + +// Comparison is the type of comparison operator expression AST nodes +var Comparison = NewUnionType("@comparison", BinaryExpr) + +// EqualityTest is the type of equality operator expression AST nodes +var EqualityTest = NewUnionType("@equalitytest", Comparison) + +// RelationalComparison is the type of relational operator expression AST nodes +var RelationalComparison = NewUnionType("@relationalcomparison", Comparison) + +// KeyValueExpr is the type of key-value expression AST nodes +var KeyValueExpr = ExprKind.NewBranch("@keyvalueexpr") + +// ArrayTypeExpr is the type of array type AST nodes +var ArrayTypeExpr = ExprKind.NewBranch("@arraytypeexpr") + +// StructTypeExpr is the type of struct type AST nodes +var StructTypeExpr = ExprKind.NewBranch("@structtypeexpr", FieldParentType) + +// FuncTypeExpr is the type of function type AST nodes +var FuncTypeExpr = ExprKind.NewBranch("@functypeexpr", FieldParentType, ScopeNodeType) + +// InterfaceTypeExpr is the type of interface type AST nodes +var InterfaceTypeExpr = ExprKind.NewBranch("@interfacetypeexpr", FieldParentType) + +// MapTypeExpr is the type of map type AST nodes +var MapTypeExpr = ExprKind.NewBranch("@maptypeexpr") + +// TypeSetLiteralExpr is the type of type set literal type AST nodes +var TypeSetLiteralExpr = ExprKind.NewBranch("@typesetliteralexpr") + +// ChanTypeExpr is the type of channel type AST nodes +var ChanTypeExpr = NewUnionType("@chantypeexpr") + +// UnaryExprs is a map from unary operator tokens to the corresponding AST node type +var UnaryExprs = map[token.Token]*BranchType{ + token.ADD: ExprKind.NewBranch("@plusexpr", ArithmeticUnaryExpr), + token.SUB: ExprKind.NewBranch("@minusexpr", ArithmeticUnaryExpr), + token.NOT: ExprKind.NewBranch("@notexpr", LogicalUnaryExpr), + token.XOR: ExprKind.NewBranch("@complementexpr", BitwiseUnaryExpr), + token.MUL: ExprKind.NewBranch("@derefexpr", UnaryExpr), + token.AND: ExprKind.NewBranch("@addressexpr", UnaryExpr), + token.ARROW: ExprKind.NewBranch("@arrowexpr", UnaryExpr), +} + +// BinaryExprs is a map from binary operator tokens to the corresponding AST node type +var BinaryExprs = map[token.Token]*BranchType{ + token.LOR: ExprKind.NewBranch("@lorexpr", LogicalBinaryExpr), + token.LAND: ExprKind.NewBranch("@landexpr", LogicalBinaryExpr), + token.EQL: ExprKind.NewBranch("@eqlexpr", EqualityTest), + token.NEQ: ExprKind.NewBranch("@neqexpr", EqualityTest), + token.LSS: ExprKind.NewBranch("@lssexpr", RelationalComparison), + token.LEQ: ExprKind.NewBranch("@leqexpr", RelationalComparison), + token.GTR: ExprKind.NewBranch("@gtrexpr", RelationalComparison), + token.GEQ: ExprKind.NewBranch("@geqexpr", RelationalComparison), + token.ADD: ExprKind.NewBranch("@addexpr", ArithmeticBinaryExpr), + token.SUB: ExprKind.NewBranch("@subexpr", ArithmeticBinaryExpr), + token.OR: ExprKind.NewBranch("@orexpr", BitwiseBinaryExpr), + token.XOR: ExprKind.NewBranch("@xorexpr", BitwiseBinaryExpr), + token.MUL: ExprKind.NewBranch("@mulexpr", ArithmeticBinaryExpr), + token.QUO: ExprKind.NewBranch("@quoexpr", ArithmeticBinaryExpr), + token.REM: ExprKind.NewBranch("@remexpr", ArithmeticBinaryExpr), + token.SHL: ExprKind.NewBranch("@shlexpr", ShiftExpr), + token.SHR: ExprKind.NewBranch("@shrexpr", ShiftExpr), + token.AND: ExprKind.NewBranch("@andexpr", BitwiseBinaryExpr), + token.AND_NOT: ExprKind.NewBranch("@andnotexpr", BitwiseBinaryExpr), +} + +// ChanTypeExprs is a map from channel type expressions to the corresponding AST node type +var ChanTypeExprs = map[ast.ChanDir]*BranchType{ + ast.SEND: ExprKind.NewBranch("@sendchantypeexpr", ChanTypeExpr), + ast.RECV: ExprKind.NewBranch("@recvchantypeexpr", ChanTypeExpr), + ast.SEND | ast.RECV: ExprKind.NewBranch("@sendrcvchantypeexpr", ChanTypeExpr), +} + +// StmtKind is a case type for distinguishing different kinds of statement AST nodes +var StmtKind = NewCaseType(StmtType, "kind") + +// BadStmtType is the type of bad (that is, unparseable) statement AST nodes +var BadStmtType = StmtKind.NewBranch("@badstmt") + +// DeclStmtType is the type of declaration statement AST nodes +var DeclStmtType = StmtKind.NewBranch("@declstmt", DeclParentType) + +// EmptyStmtType is the type of empty statement AST nodes +var EmptyStmtType = StmtKind.NewBranch("@emptystmt") + +// LabeledStmtType is the type of labeled statement AST nodes +var LabeledStmtType = StmtKind.NewBranch("@labeledstmt") + +// ExprStmtType is the type of expressio statemement AST nodes +var ExprStmtType = StmtKind.NewBranch("@exprstmt") + +// SendStmtType is the type of send statement AST nodes +var SendStmtType = StmtKind.NewBranch("@sendstmt") + +// IncDecStmtType is the type of increment/decrement statement AST nodes +var IncDecStmtType = NewUnionType("@incdecstmt") + +// IncStmtType is the type of increment statement AST nodes +var IncStmtType = StmtKind.NewBranch("@incstmt", IncDecStmtType) + +// DecStmtType is the type of decrement statement AST nodes +var DecStmtType = StmtKind.NewBranch("@decstmt", IncDecStmtType) + +// AssignmentType is the type of assignment statement AST nodes +var AssignmentType = NewUnionType("@assignment") + +// SimpleAssignStmtType is the type of simple (i.e., non-compound) assignment statement AST nodes +var SimpleAssignStmtType = NewUnionType("@simpleassignstmt", AssignmentType) + +// CompoundAssignStmtType is the type of compound assignment statement AST nodes +var CompoundAssignStmtType = NewUnionType("@compoundassignstmt", AssignmentType) + +// GoStmtType is the type of go statement AST nodes +var GoStmtType = StmtKind.NewBranch("@gostmt") + +// DeferStmtType is the type of defer statement AST nodes +var DeferStmtType = StmtKind.NewBranch("@deferstmt") + +// ReturnStmtType is the type of return statement AST nodes +var ReturnStmtType = StmtKind.NewBranch("@returnstmt") + +// BranchStmtType is the type of branch statement AST nodes +var BranchStmtType = NewUnionType("@branchstmt") + +// BreakStmtType is the type of break statement AST nodes +var BreakStmtType = StmtKind.NewBranch("@breakstmt", BranchStmtType) + +// ContinueStmtType is the type of continue statement AST nodes +var ContinueStmtType = StmtKind.NewBranch("@continuestmt", BranchStmtType) + +// GotoStmtType is the type of goto statement AST nodes +var GotoStmtType = StmtKind.NewBranch("@gotostmt", BranchStmtType) + +// FallthroughStmtType is the type of fallthrough statement AST nodes +var FallthroughStmtType = StmtKind.NewBranch("@fallthroughstmt", BranchStmtType) + +// BlockStmtType is the type of block statement AST nodes +var BlockStmtType = StmtKind.NewBranch("@blockstmt", ScopeNodeType) + +// IfStmtType is the type of if statement AST nodes +var IfStmtType = StmtKind.NewBranch("@ifstmt", ScopeNodeType) + +// CaseClauseType is the type of case clause AST nodes +var CaseClauseType = StmtKind.NewBranch("@caseclause", ScopeNodeType) + +// SwitchStmtType is the type of switch statement AST nodes, covering both expression switch and type switch +var SwitchStmtType = NewUnionType("@switchstmt", ScopeNodeType) + +// ExprSwitchStmtType is the type of expression-switch statement AST nodes +var ExprSwitchStmtType = StmtKind.NewBranch("@exprswitchstmt", SwitchStmtType) + +// TypeSwitchStmtType is the type of type-switch statement AST nodes +var TypeSwitchStmtType = StmtKind.NewBranch("@typeswitchstmt", SwitchStmtType) + +// CommClauseType is the type of comm clause AST ndoes +var CommClauseType = StmtKind.NewBranch("@commclause", ScopeNodeType) + +// SelectStmtType is the type of select statement AST nodes +var SelectStmtType = StmtKind.NewBranch("@selectstmt") + +// LoopStmtType is the type of loop statement AST nodes (including for statements and range statements) +var LoopStmtType = NewUnionType("@loopstmt", ScopeNodeType) + +// ForStmtType is the type of for statement AST nodes +var ForStmtType = StmtKind.NewBranch("@forstmt", LoopStmtType) + +// RangeStmtType is the type of range statement AST nodes +var RangeStmtType = StmtKind.NewBranch("@rangestmt", LoopStmtType) + +// AssignStmtTypes is a map from assignmnt operator tokens to corresponding AST node types +var AssignStmtTypes = map[token.Token]*BranchType{ + token.ASSIGN: StmtKind.NewBranch("@assignstmt", SimpleAssignStmtType), + token.DEFINE: StmtKind.NewBranch("@definestmt", SimpleAssignStmtType), + token.ADD_ASSIGN: StmtKind.NewBranch("@addassignstmt", CompoundAssignStmtType), + token.SUB_ASSIGN: StmtKind.NewBranch("@subassignstmt", CompoundAssignStmtType), + token.MUL_ASSIGN: StmtKind.NewBranch("@mulassignstmt", CompoundAssignStmtType), + token.QUO_ASSIGN: StmtKind.NewBranch("@quoassignstmt", CompoundAssignStmtType), + token.REM_ASSIGN: StmtKind.NewBranch("@remassignstmt", CompoundAssignStmtType), + token.AND_ASSIGN: StmtKind.NewBranch("@andassignstmt", CompoundAssignStmtType), + token.OR_ASSIGN: StmtKind.NewBranch("@orassignstmt", CompoundAssignStmtType), + token.XOR_ASSIGN: StmtKind.NewBranch("@xorassignstmt", CompoundAssignStmtType), + token.SHL_ASSIGN: StmtKind.NewBranch("@shlassignstmt", CompoundAssignStmtType), + token.SHR_ASSIGN: StmtKind.NewBranch("@shrassignstmt", CompoundAssignStmtType), + token.AND_NOT_ASSIGN: StmtKind.NewBranch("@andnotassignstmt", CompoundAssignStmtType), +} + +// DeclKind is a case type for distinguishing different kinds of declaration AST nodes +var DeclKind = NewCaseType(DeclType, "kind") + +// BadDeclType is the type of bad (that is, unparseable) declaration AST nodes +var BadDeclType = DeclKind.NewBranch("@baddecl") + +// GenDeclType is the type of generic declaration AST nodes +var GenDeclType = NewUnionType("@gendecl", DocumentableType) + +// ImportDeclType is the type of import declaration AST nodes +var ImportDeclType = DeclKind.NewBranch("@importdecl", GenDeclType) + +// ConstDeclType is the type of constant declaration AST nodes +var ConstDeclType = DeclKind.NewBranch("@constdecl", GenDeclType) + +// TypeDeclType is the type of type declaration AST nodes +var TypeDeclType = DeclKind.NewBranch("@typedecl", GenDeclType) + +// VarDeclType is the type of variable declaration AST nodes +var VarDeclType = DeclKind.NewBranch("@vardecl", GenDeclType) + +// FuncDeclType is the type of function declaration AST nodes +var FuncDeclType = DeclKind.NewBranch("@funcdecl", DocumentableType, FuncDefType) + +// SpecKind is a case type for distinguishing different kinds of declaration specification nodes +var SpecKind = NewCaseType(SpecType, "kind") + +// ImportSpecType is the type of import declaration specification nodes +var ImportSpecType = SpecKind.NewBranch("@importspec") + +// ValueSpecType is the type of value declaration specification nodes +var ValueSpecType = SpecKind.NewBranch("@valuespec") + +// TypeSpecType is the type of type declaration specification nodes +var TypeSpecType = NewUnionType("@typespec", TypeParamDeclParentType) + +// TypeDefSpecType is the type of type declaration specification nodes corresponding to type definitions +var TypeDefSpecType = SpecKind.NewBranch("@typedefspec", TypeSpecType) + +// AliasSpecType is the type of type declaration specification nodes corresponding to alias declarations +var AliasSpecType = SpecKind.NewBranch("@aliasspec", TypeSpecType) + +// BaseObjectType is the type of objects (that is, declared entities) +var BaseObjectType = NewPrimaryKeyType("@object") + +// ObjectKind is a case type for distinguishing different kinds of built-in and declared objects +var ObjectKind = NewCaseType(BaseObjectType, "kind") + +// TypeParamParentObjectType is the type of objects that can have type parameters as children +var TypeParamParentObjectType = NewUnionType("@typeparamparentobject") + +// DeclObjectType is the type of declared objects +var DeclObjectType = NewUnionType("@declobject") + +// BuiltinObjectType is the type of built-in objects +var BuiltinObjectType = NewUnionType("@builtinobject") + +// PkgObjectType is the type of imported packages +var PkgObjectType = ObjectKind.NewBranch("@pkgobject") + +// TypeObjectType is the type of declared or built-in named types +var TypeObjectType = NewUnionType("@typeobject") + +// DeclTypeObjectType is the type of declared named types +var DeclTypeObjectType = ObjectKind.NewBranch("@decltypeobject", TypeObjectType, DeclObjectType, TypeParamParentObjectType) + +// BuiltinTypeObjectType is the type of built-in named types +var BuiltinTypeObjectType = ObjectKind.NewBranch("@builtintypeobject", TypeObjectType, BuiltinObjectType) + +// ValueObjectType is the type of declared or built-in variables or constants +var ValueObjectType = NewUnionType("@valueobject") + +// ConstObjectType is the type of declared or built-in constants +var ConstObjectType = NewUnionType("@constobject", ValueObjectType) + +// DeclConstObjectType is the type of declared constants +var DeclConstObjectType = ObjectKind.NewBranch("@declconstobject", ConstObjectType, DeclObjectType) + +// BuiltinConstObjectType is the type of built-in constants +var BuiltinConstObjectType = ObjectKind.NewBranch("@builtinconstobject", ConstObjectType, BuiltinObjectType) + +// VarObjectType is the type of declared or built-in variables (the latter do not currently exist) +var VarObjectType = NewUnionType("@varobject", ValueObjectType) + +// DeclVarObjectType is the type of declared variables including function parameters, results and struct fields +var DeclVarObjectType = ObjectKind.NewBranch("@declvarobject", VarObjectType, DeclObjectType) + +// FunctionObjectType is the type of declared or built-in functions +var FunctionObjectType = NewUnionType("@functionobject", ValueObjectType) + +// DeclFuncObjectType is the type of declared functions, including (abstract and concrete) methods +var DeclFuncObjectType = ObjectKind.NewBranch("@declfunctionobject", FunctionObjectType, DeclObjectType, TypeParamParentObjectType) + +// BuiltinFuncObjectType is the type of built-in functions +var BuiltinFuncObjectType = ObjectKind.NewBranch("@builtinfunctionobject", FunctionObjectType, BuiltinObjectType) + +// LabelObjectType is the type of statement labels +var LabelObjectType = ObjectKind.NewBranch("@labelobject") + +// ScopeType is the type of scopes +var ScopeType = NewPrimaryKeyType("@scope") + +// ScopeKind is a case type for distinguishing different kinds of scopes +var ScopeKind = NewCaseType(ScopeType, "kind") + +// UniverseScopeType is the type of the universe scope +var UniverseScopeType = ScopeKind.NewBranch("@universescope") + +// PackageScopeType is the type of package scopes +var PackageScopeType = ScopeKind.NewBranch("@packagescope") + +// LocalScopeType is the type of local (that is, non-universe, non-package) scopes +var LocalScopeType = ScopeKind.NewBranch("@localscope", LocatableType) + +// TypeKind is a case type for distinguishing different kinds of types +var TypeKind = NewCaseType(TypeType, "kind") + +// BasicType is the union of all basic types +var BasicType = NewUnionType("@basictype") + +// BoolType is the union of the normal and literal bool types +var BoolType = NewUnionType("@booltype", BasicType) + +// NumericType is the union of numeric types +var NumericType = NewUnionType("@numerictype", BasicType) + +// IntegerType is the union of integer types +var IntegerType = NewUnionType("@integertype", NumericType) + +// SignedIntegerType is the union of signed integer types +var SignedIntegerType = NewUnionType("@signedintegertype", IntegerType) + +// UnsignedIntegerType is the union of unsigned integer types +var UnsignedIntegerType = NewUnionType("@unsignedintegertype", IntegerType) + +// FloatType is the union of floating-point types +var FloatType = NewUnionType("@floattype", NumericType) + +// ComplexType is the union of complex types +var ComplexType = NewUnionType("@complextype", NumericType) + +// StringType is the union of the normal and literal string types +var StringType = NewUnionType("@stringtype", BasicType) + +// LiteralType is the union of literal types +var LiteralType = NewUnionType("@literaltype", BasicType) + +// BasicTypes is a map from basic type kinds to the corresponding entity types +var BasicTypes = map[gotypes.BasicKind]*BranchType{ + gotypes.Invalid: TypeKind.NewBranch("@invalidtype", BasicType), + gotypes.Bool: TypeKind.NewBranch("@boolexprtype", BoolType), + gotypes.Int: TypeKind.NewBranch("@inttype", SignedIntegerType), + gotypes.Int8: TypeKind.NewBranch("@int8type", SignedIntegerType), + gotypes.Int16: TypeKind.NewBranch("@int16type", SignedIntegerType), + gotypes.Int32: TypeKind.NewBranch("@int32type", SignedIntegerType), + gotypes.Int64: TypeKind.NewBranch("@int64type", SignedIntegerType), + gotypes.Uint: TypeKind.NewBranch("@uinttype", UnsignedIntegerType), + gotypes.Uint8: TypeKind.NewBranch("@uint8type", UnsignedIntegerType), + gotypes.Uint16: TypeKind.NewBranch("@uint16type", UnsignedIntegerType), + gotypes.Uint32: TypeKind.NewBranch("@uint32type", UnsignedIntegerType), + gotypes.Uint64: TypeKind.NewBranch("@uint64type", UnsignedIntegerType), + gotypes.Uintptr: TypeKind.NewBranch("@uintptrtype", UnsignedIntegerType), + gotypes.Float32: TypeKind.NewBranch("@float32type", FloatType), + gotypes.Float64: TypeKind.NewBranch("@float64type", FloatType), + gotypes.Complex64: TypeKind.NewBranch("@complex64type", ComplexType), + gotypes.Complex128: TypeKind.NewBranch("@complex128type", ComplexType), + gotypes.String: TypeKind.NewBranch("@stringexprtype", StringType), + gotypes.UnsafePointer: TypeKind.NewBranch("@unsafepointertype", BasicType), + gotypes.UntypedBool: TypeKind.NewBranch("@boolliteraltype", LiteralType, BoolType), + gotypes.UntypedInt: TypeKind.NewBranch("@intliteraltype", LiteralType, SignedIntegerType), + gotypes.UntypedRune: TypeKind.NewBranch("@runeliteraltype", LiteralType, SignedIntegerType), + gotypes.UntypedFloat: TypeKind.NewBranch("@floatliteraltype", LiteralType, FloatType), + gotypes.UntypedComplex: TypeKind.NewBranch("@complexliteraltype", LiteralType, ComplexType), + gotypes.UntypedString: TypeKind.NewBranch("@stringliteraltype", LiteralType, StringType), + gotypes.UntypedNil: TypeKind.NewBranch("@nilliteraltype", LiteralType), +} + +// CompositeType is the type of all composite (that is, non-basic) types +var CompositeType = NewUnionType("@compositetype") + +// TypeParamType is the type of type parameter types +var TypeParamType = TypeKind.NewBranch("@typeparamtype", CompositeType) + +// ElementContainerType is the type of types that have elements, such as arrays +// and channels +var ElementContainerType = NewUnionType("@containertype", CompositeType) + +// ArrayType is the type of array types +var ArrayType = TypeKind.NewBranch("@arraytype", ElementContainerType) + +// SliceType is the type of slice types +var SliceType = TypeKind.NewBranch("@slicetype", ElementContainerType) + +// StructType is the type of struct types +var StructType = TypeKind.NewBranch("@structtype", CompositeType) + +// PointerType is the type of pointer types +var PointerType = TypeKind.NewBranch("@pointertype", CompositeType) + +// InterfaceType is the type of interface types +var InterfaceType = TypeKind.NewBranch("@interfacetype", CompositeType) + +// TupleType is the type of tuple types +var TupleType = TypeKind.NewBranch("@tupletype", CompositeType) + +// SignatureType is the type of signature types +var SignatureType = TypeKind.NewBranch("@signaturetype", CompositeType) + +// MapType is the type of map types +var MapType = TypeKind.NewBranch("@maptype", ElementContainerType) + +// ChanType is the type of channel types +var ChanType = NewUnionType("@chantype", ElementContainerType) + +// ChanTypes is a map from channel type directions to the corresponding type +var ChanTypes = map[gotypes.ChanDir]*BranchType{ + gotypes.SendOnly: TypeKind.NewBranch("@sendchantype", ChanType), + gotypes.RecvOnly: TypeKind.NewBranch("@recvchantype", ChanType), + gotypes.SendRecv: TypeKind.NewBranch("@sendrcvchantype", ChanType), +} + +// NamedType is the type of named types +var NamedType = TypeKind.NewBranch("@namedtype", CompositeType) + +// PackageType is the type of packages +var PackageType = NewPrimaryKeyType("@package") + +// TypeSetLiteral is the type of type set literals +var TypeSetLiteral = TypeKind.NewBranch("@typesetliteraltype", CompositeType) + +// ModExprType is the type of go.mod expression nodes +var ModExprType = NewPrimaryKeyType("@modexpr", ModExprParentType, DocumentableType) + +// ModExprKind is a case type for distinguishing different kinds of go.mod expression nodes +var ModExprKind = NewCaseType(ModExprType, "kind") + +// ModCommentBlockType is the type of go.mod comment block AST nodes +var ModCommentBlockType = ModExprKind.NewBranch("@modcommentblock") + +// ModLineType is the type of go.mod line AST nodes +var ModLineType = ModExprKind.NewBranch("@modline") + +// ModLineBlockType is the type of go.mod line block AST nodes +var ModLineBlockType = ModExprKind.NewBranch("@modlineblock") + +// ModLParenType is the type of go.mod line block start AST nodes +var ModLParenType = ModExprKind.NewBranch("@modlparen") + +// ModRParenType is the type of go.mod line block end AST nodes +var ModRParenType = ModExprKind.NewBranch("@modrparen") + +// ErrorType is the type of frontend errors +var ErrorType = NewPrimaryKeyType("@error") + +// ErrorKind is a case type for distinguishing different kinds of frontend errors +var ErrorKind = NewCaseType(ErrorType, "kind") + +// ErrorTypes is a map from error kinds to the corresponding type +var ErrorTypes = map[packages.ErrorKind]*BranchType{ + packages.UnknownError: ErrorKind.NewBranch("@unknownerror"), + packages.ListError: ErrorKind.NewBranch("@listerror"), + packages.ParseError: ErrorKind.NewBranch("@parseerror"), + packages.TypeError: ErrorKind.NewBranch("@typeerror"), +} + +// ErrorTags is a map from error kinds to the corresponding tag +var ErrorTags = map[packages.ErrorKind]string{ + packages.UnknownError: "@unknownerror", + packages.ListError: "@listerror", + packages.ParseError: "@parseerror", + packages.TypeError: "@typeerror", +} + +// FileType defines a set of constants for supported file types as integers +type FileType int + +const ( + _ FileType = iota // it's common in Go to have the first enumerated constant 0 as the default case + GoFile // Indicates that the file is a go code file (with .go extension) + GoModFile // Indicates that the file is a go mod file (with go.mod extension) +) + +// UnExtractedType which is of integer type. +type UnExtractedType int + +const ( + _ UnExtractedType = iota + TypeFile //UnExtracted File + TypeFolder //UnExtracted Folder +) diff --git a/language/go/extractor/src/orm/data_types.go b/language/go/extractor/src/orm/data_types.go new file mode 100644 index 00000000..ee8d08b9 --- /dev/null +++ b/language/go/extractor/src/orm/data_types.go @@ -0,0 +1,287 @@ +package orm + +import ( + "fmt" + "strings" +) + +// A DBType represents a database type +type DBType interface { + def() string + ref() string + repr() string + valid(val interface{}) bool +} + +// A PrimitiveType represents a primitive dataase type +type PrimitiveType int + +const ( + // INT represents the primitive database type `int` + INT PrimitiveType = iota + // FLOAT represents the primitive database type `float` + FLOAT + // BOOLEAN represents the primitive database type `boolean` + BOOLEAN + // DATE represents the primitive database type `date` + DATE + // STRING represents the primitive database type `string` + STRING +) + +// A PrimaryKeyType represents a database type defined by a primary key column +type PrimaryKeyType struct { + name string +} + +func (pkt PrimaryKeyType) valid(val interface{}) bool { + //TODO implement me + panic("implement me") +} + +// A UnionType represents a database type defined as the union of other database types +type UnionType struct { + name string + components []DBType +} + +func (ut UnionType) valid(val interface{}) bool { + //TODO implement me + panic("implement me") +} + +// An AliasType represents a database type which is an alias of another database type +type AliasType struct { + name string + underlying DBType +} + +// A CaseType represents a database type defined by a primary key column with a supplementary kind column +type CaseType struct { + base DBType + column string + branches []*BranchType +} + +func (ct CaseType) valid(val interface{}) bool { + //TODO implement me + panic("implement me") +} + +// A BranchType represents one branch of a case type +type BranchType struct { + idx int + name string +} + +func (bt BranchType) valid(val interface{}) bool { + //TODO implement me + panic("implement me") +} + +func (pt PrimitiveType) def() string { + return "" +} + +func (pt PrimitiveType) ref() string { + switch pt { + case INT: + return "int" + case FLOAT: + return "float" + case BOOLEAN: + return "boolean" + case DATE: + return "date" + case STRING: + return "string" + default: + panic(fmt.Sprintf("Unexpected primitive type %d", pt)) + } +} + +func (pt PrimitiveType) repr() string { + switch pt { + case INT: + return "int" + case FLOAT: + return "float" + case BOOLEAN: + return "boolean" + case DATE: + return "date" + case STRING: + return "string" + default: + panic(fmt.Sprintf("Unexpected primitive type %d", pt)) + } +} + +func (pt PrimitiveType) valid(value interface{}) bool { + switch value.(type) { + case int: + return pt == INT + case float64: + return pt == FLOAT + case bool: + return pt == BOOLEAN + case string: + return pt == STRING + } + return false +} + +func (pkt PrimaryKeyType) def() string { + return "" +} + +func (pkt PrimaryKeyType) ref() string { + return pkt.name +} + +func (pkt PrimaryKeyType) repr() string { + return "int" +} + +func (ut UnionType) def() string { + var b strings.Builder + nl := 0 + fmt.Fprintf(&b, "%s = ", ut.name) + for i, comp := range ut.components { + if i > 0 { + if i < len(ut.components)-1 && b.Len()-nl > 100 { + fmt.Fprintf(&b, "\n%s", strings.Repeat(" ", len(ut.name))) + nl = b.Len() + } + fmt.Fprint(&b, " | ") + } + fmt.Fprint(&b, comp.ref()) + } + fmt.Fprint(&b, ";") + return b.String() +} + +func (ut UnionType) ref() string { + return ut.name +} + +func (ut UnionType) repr() string { + return "int" +} + +func (at AliasType) def() string { + return at.name + " = " + at.underlying.ref() + ";" +} + +func (at AliasType) ref() string { + return at.name +} + +func (at AliasType) repr() string { + return at.underlying.repr() +} + +func (at AliasType) valid(value interface{}) bool { + return at.underlying.valid(value) +} + +func (ct CaseType) def() string { + var b strings.Builder + fmt.Fprintf(&b, "case %s.%s of", ct.base.ref(), ct.column) + sep := " " + for _, branch := range ct.branches { + fmt.Fprintf(&b, "\n%s%s", sep, branch.def()) + sep = "| " + } + fmt.Fprint(&b, ";") + return b.String() +} + +func (ct CaseType) ref() string { + panic("case types do not have a name") +} + +func (ct CaseType) repr() string { + return "int" +} + +func (bt BranchType) def() string { + return fmt.Sprintf("%d = %s", bt.idx, bt.name) +} + +func (bt BranchType) ref() string { + return bt.name +} + +func (bt BranchType) repr() string { + return "int" +} + +// Index returns the numeric index of this branch type +func (bt BranchType) Index() int { + return bt.idx +} + +// Tostring returns the name of this branch type +func (bt BranchType) String() string { + return bt.name +} + +var dbtypes = []DBType{} + +// NewPrimaryKeyType constructs a new primary key type with the given `name`, +// and adds it to the union types `parents` (if any) +func NewPrimaryKeyType(name string, parents ...*UnionType) *PrimaryKeyType { + tp := &PrimaryKeyType{name} + dbtypes = append(dbtypes, tp) + for _, parent := range parents { + parent.components = append(parent.components, tp) + } + return tp +} + +// NewUnionType constructs a new union type with the given `name`, +// and adds it to the union types `parents` (if any) +func NewUnionType(name string, parents ...*UnionType) *UnionType { + tp := &UnionType{name, []DBType{}} + dbtypes = append(dbtypes, tp) + for _, parent := range parents { + parent.components = append(parent.components, tp) + } + return tp +} + +// AddChild adds the type with given `name` to the union type. +// This is useful if a type defined in a snippet should be a child of a type defined in Go. +func (parent *UnionType) AddChild(name string) bool { + tp := &PrimaryKeyType{name} + // don't add tp to types; it's expected that it's already in the db somehow. + parent.components = append(parent.components, tp) + return true +} + +// NewAliasType constructs a new alias type with the given `name` that aliases `underlying` +func NewAliasType(name string, underlying DBType) *AliasType { + tp := &AliasType{name, underlying} + dbtypes = append(dbtypes, tp) + return tp +} + +// NewCaseType constructs a new case type on the given `base` type whose discriminator values +// come from `column` +func NewCaseType(base DBType, column string) *CaseType { + tp := &CaseType{base, column, []*BranchType{}} + dbtypes = append(dbtypes, tp) + return tp +} + +// NewBranch adds a new branch with the given `name` to this case type +// and adds it to the union types `parents` (if any) +func (ct *CaseType) NewBranch(name string, parents ...*UnionType) *BranchType { + tp := &BranchType{len(ct.branches), name} + ct.branches = append(ct.branches, tp) + for _, parent := range parents { + parent.components = append(parent.components, tp) + } + return tp +} diff --git a/language/go/extractor/src/orm/db_model.go b/language/go/extractor/src/orm/db_model.go new file mode 100644 index 00000000..e56a1dd0 --- /dev/null +++ b/language/go/extractor/src/orm/db_model.go @@ -0,0 +1,437 @@ +package orm + +// Comment is the table describing the comment element info +type Comment struct { + Oid int64 + FileId int64 + CommentType int + Parent int64 + Index int64 + DebugInfo string +} + +// Location is the table describing the location info in the containing file for locatable entity +type Location struct { + Oid int64 + StartLineNumber int + StartColumnNumber int + EndLineNumber int + EndColumnNumber int + + TokenStartOffset int + TokenEndOffset int +} + +// HasLocation is the table associating entities with their locations +type HasLocation struct { + Oid int64 + LocationObj int64 + LocationId int64 +} + +// CommentGroup is the table defining comment group entities +type CommentGroup struct { + Oid int64 + FileId int64 + Parent int64 + Idx int +} + +// DocComment is the table associating doc comments with the nodes they document +type DocComment struct { + Oid int64 + AssociateObj int64 + CommentGroupId int64 +} + +// TypeOf is the table associating expressions with their types (if known) +type TypeOf struct { + Oid int64 + Expr int64 + Tp int64 +} + +// ArrayLength is the table associating array types with their length (represented as a string +type ArrayLength struct { + Oid int64 + Tp int64 + FileId int64 + Len int64 +} + +// ElementType is the table associating container types with their element type +type ElementType struct { + Oid int64 + Container int64 + Tp int64 +} + +// FieldStruct maps fields to the structs they are in +type FieldStruct struct { + Oid int64 + Struct int64 +} + +// TypeName is the table associating named types with their names +type TypeName struct { + Oid int64 + Name string +} + +// TypeObject maps types to their corresponding objects, if any +type TypeObject struct { + Oid int64 + Object int64 +} + +// Def maps identifiers to the objects they define +type Def struct { + Oid int64 + Ident int64 + Object int64 +} + +// Uses maps identifiers to the objects they denote 'uses' +type Uses struct { + Oid int64 + Ident int64 + Object int64 +} + +// Variadic is the table describing which functions are variadic +type Variadic struct { + Oid int64 + AssociatedNode int64 +} + +// MethodHost maps interface methods to the named type they belong to +type MethodHost struct { + Oid int64 + Method int64 + Host int64 +} + +// Type is the table describing types +type Type struct { + Oid int64 + Kind int + FormatString string + RawString string +} + +// NumberOfLine is the table describing the classified line +type NumberOfLine struct { + Oid int64 + NumberOfTotalLines int + NumberOfCodeLines int + NumberOfCommentLines int +} + +// Pkg is the table describing packages +type Pkg struct { + Oid int64 + Path string + Name string + Scope int64 +} + +// Expr is the table defining expression AST nodes +type Expr struct { + Oid int64 + FileId int64 + Kind int + ParentId int64 + Idx int + DebugInfo string +} + +// Literal is the table associating literal expression AST nodes with their values +type Literal struct { + Oid int64 + Expr int64 + Value string + Raw string +} + +// ConstValue is the table associating constant expressions with their values +type ConstValue struct { + Oid int64 + Expr int64 + Value string + Exact string +} + +// Fields is the table defining field AST nodes +type Fields struct { + Oid int64 + ParentId int64 + Idx int + Kind int +} + +// Stmt is the table defining statement AST nodes +type Stmt struct { + Oid int64 + FileId int64 + Kind int + ParentId int64 + Idx int + DebugInfo string +} + +// Decl is the table defining declaration AST nodes +type Decl struct { + Oid int64 + FileId int64 + Kind int + ParentId int64 + Idx int + DebugInfo string +} + +// Spec is the table defining declaration specification AST nodes +type Spec struct { + Oid int64 + Kind int + ParentId int64 + Idx int + DebugInfo string +} + +// Object is the table describing objects (that is, declared entities) +type Object struct { + Oid int64 + //Object int64 + Kind int + Name string + DebugInfo string +} + +// ObjectScope is the table describing the scope to which an object belongs (if any) +type ObjectScope struct { + Oid int64 + Object int64 + Scope int64 +} + +// MethodReceiver maps methods to their receiver +type MethodReceiver struct { + Oid int64 + Method int64 + Receiver int64 +} + +// KeyType is the table associating maps with their key type +type KeyType struct { + Oid int64 + Map int64 + Tp int64 +} + +// BaseType is the table associating pointer types with their base type +type BaseType struct { + Oid int64 + Ptr int64 + Tp int64 +} + +// UnderlyingType is the table associating named types with their underlying type +type UnderlyingType struct { + Oid int64 + Named int64 + Tp int64 +} + +// ComponentType is the table associating composite types with their component types +type ComponentType struct { + Oid int64 + Parent int64 + Index int + Name string + Tp int64 +} + +// Scope is the table defining scopes +type Scope struct { + Oid int64 + Kind int + DebugInfo string +} + +// ScopeNesting is the table describing scope nesting +type ScopeNesting struct { + Oid int64 + Inner int64 + Outer int64 +} + +// ScopeNode is the table associating local scopes with the AST nodes that induce them +type ScopeNode struct { + Oid int64 + Node int64 + Scope int64 +} + +// ObjectType is the table describing the type of an object (if any) +type ObjectType struct { + Object int64 + Tp int64 +} + +// Typeiadic is the table describing which functions are typeiadic +type Typeiadic struct { + Oid int64 + AssociationObjId int64 +} + +// HasEllipsis is the table containing all call expressions that have ellipses +type HasEllipsis struct { + Oid int64 + CallOrConversionExprId int64 +} + +// ModFile is the table defining the basic information about go.mod files +type ModFile struct { + Oid int64 + Path string + Name string + GoVersion string +} + +// ModRequire is the table defining the mod requires +type ModRequire struct { + Oid int64 + ModId int64 + Require string +} + +// ModExpr is the table defining expression AST nodes for go.mod files +type ModExpr struct { + Oid int64 + Kind int + Parent int64 + Idx int +} + +// ModToken is the table associating go.mod tokens with their Line or LineBlock +type ModToken struct { + Oid int64 + Token string + Parent int64 + Idx int +} + +type DiagnosticFor struct { + Diagnostic int64 + Compilation int64 + FileNumber int + FileNumberDiagnosticNumber int +} + +type CompilationFinished struct { + Oid int64 + CpuSeconds float64 + ElapsedSeconds float64 +} + +type Diagnostic struct { + Oid int64 + Severity int + ErrorTag string + ErrorMessage string + FullErrorMessage string + LocationId int64 +} + +// Error is the table describing frontend errors +type Error struct { + Oid int64 + Kind int + Msg string + RawPos string + File string + Line int + Col int + Pkg int64 + Idx int +} + +type Compilation struct { + Oid int64 + Cwd string +} + +type CompilationArgs struct { + Oid int64 + Num int + Arg string +} + +type CompilationCompilingFile struct { + Oid int64 + Num int + File int64 +} + +// File is the table defining file nodes +type File struct { + Oid int64 + PkgOid int64 // belongs package + Name string + Md5Sum string + Sha256Sum string +} + +// Folder is the table defining folder entities +type Folder struct { + Oid int64 + Name string +} + +// ContainerParent is the table defining the parent-child relation among container entities +type ContainerParent struct { + Oid int64 + Parent int64 + Child int64 +} + +// TypeParamDecls is the table defining type param declaration AST nodes +type TypeParamDecls struct { + Oid int64 + Parent int64 + Index int +} + +// TypeParam is the table describing type parameter types +type TypeParam struct { + Oid int64 + Tp int64 + Name string + Bound int64 + Parent int64 + Index int +} + +// RuntimeInfo is the table defining the extractor runtine infor +type RuntimeInfo struct { + Oid int64 + BuildVersion string //go编译器版本 + ExtractorInfo string + GoOs string + GoArch string +} + +// UnExtracted is the table defining folders/files unExtracted +type UnExtracted struct { + Oid int64 + Type UnExtractedType + Name string +} + +// FileData is the table store Go/GoMod Files info +type FileData struct { + Oid int64 // Unique identifier for the record + FileId int64 // Associated file identifier + Type FileType // Represents type of file: 1 - Go file, 2 - GoMod file + Content string // Content of the file in string format +} diff --git a/language/go/extractor/src/orm/db_writer.go b/language/go/extractor/src/orm/db_writer.go new file mode 100644 index 00000000..7fb8d1e5 --- /dev/null +++ b/language/go/extractor/src/orm/db_writer.go @@ -0,0 +1,164 @@ +package orm + +import ( + "errors" + "fmt" + "github.com/glebarez/sqlite" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "gorm.io/gorm/schema" + "log" + "os" + "reflect" + "time" +) + +// TablesRegistry is used to register the table list and the entry +// for GORM AutoMigration. +var TablesRegistry = make(map[string]reflect.Type) + +// registerType adds a new type to the TablesRegistry using its fully qualified name. +func registerType(typedNil interface{}) { + t := reflect.TypeOf(typedNil).Elem() + fullName := t.PkgPath() + "." + t.Name() + TablesRegistry[fullName] = t +} + +// getInstance creates a new instance of the type associated with the given name. +func getInstance(name string) interface{} { + t, exists := TablesRegistry[name] + if !exists { + log.Fatalf("Type %v not found in TablesRegistry", name) + } + return reflect.New(t).Interface() +} + +// init registers types for all table entities. +func init() { + // Add all type registrations here using registerType function. + registerType((*Comment)(nil)) + registerType((*Location)(nil)) + registerType((*HasLocation)(nil)) + registerType((*CommentGroup)(nil)) + registerType((*DocComment)(nil)) + registerType((*TypeOf)(nil)) + registerType((*ArrayLength)(nil)) + registerType((*FieldStruct)(nil)) + registerType((*TypeName)(nil)) + registerType((*TypeObject)(nil)) + registerType((*Def)(nil)) + registerType((*Uses)(nil)) + registerType((*Variadic)(nil)) + registerType((*MethodHost)(nil)) + registerType((*Type)(nil)) + registerType((*NumberOfLine)(nil)) + registerType((*Pkg)(nil)) + registerType((*Expr)(nil)) + registerType((*Literal)(nil)) + registerType((*ConstValue)(nil)) + registerType((*Fields)(nil)) + registerType((*Stmt)(nil)) + registerType((*Decl)(nil)) + registerType((*Spec)(nil)) + registerType((*ElementType)(nil)) + registerType((*Object)(nil)) + registerType((*ObjectScope)(nil)) + registerType((*MethodReceiver)(nil)) + registerType((*KeyType)(nil)) + registerType((*BaseType)(nil)) + registerType((*UnderlyingType)(nil)) + registerType((*ComponentType)(nil)) + registerType((*Scope)(nil)) + registerType((*ScopeNesting)(nil)) + registerType((*ScopeNode)(nil)) + registerType((*ObjectType)(nil)) + registerType((*Typeiadic)(nil)) + registerType((*HasEllipsis)(nil)) + registerType((*ModFile)(nil)) + registerType((*ModRequire)(nil)) + registerType((*ModExpr)(nil)) + registerType((*ModToken)(nil)) + registerType((*DiagnosticFor)(nil)) + registerType((*CompilationFinished)(nil)) + registerType((*Diagnostic)(nil)) + registerType((*Error)(nil)) + registerType((*Compilation)(nil)) + registerType((*CompilationArgs)(nil)) + registerType((*CompilationCompilingFile)(nil)) + registerType((*File)(nil)) + registerType((*Folder)(nil)) + registerType((*ContainerParent)(nil)) + registerType((*RuntimeInfo)(nil)) + registerType((*TypeParamDecls)(nil)) + registerType((*TypeParam)(nil)) + registerType((*UnExtracted)(nil)) + registerType((*FileData)(nil)) +} + +// SetupDatabaseAndMigrate sets up the database connection and performs schema migration. +func SetupDatabaseAndMigrate(dbPath string) (*gorm.DB, error) { + db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{ + SkipDefaultTransaction: true, + NamingStrategy: schema.NamingStrategy{ + SingularTable: true, + }, + Logger: logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{ + SlowThreshold: 10 * time.Second, + LogLevel: logger.Warn, + IgnoreRecordNotFoundError: false, + Colorful: true, + }), + }) + if err != nil { + return nil, fmt.Errorf("failed to connect to database: %v", err) + } + + // Set SQLite pragmas + if err := db.Exec("PRAGMA synchronous = OFF").Error; err != nil { + return nil, fmt.Errorf("failed to set PRAGMA synchronous: %v", err) + } + if err := db.Exec("PRAGMA journal_mode = MEMORY").Error; err != nil { + return nil, fmt.Errorf("failed to set PRAGMA journal_mode: %v", err) + } + + // Perform schema migration + for k := range TablesRegistry { + entity := getInstance(k) // Assume getInstance is a function that returns an entity based on a key + if err := db.AutoMigrate(entity); err != nil { + return nil, fmt.Errorf("DB AutoMigrate failed for %v: %w", k, err) + } + } + + // Begin transaction here + tx := db.Begin() + if tx.Error != nil { + log.Fatalf("Create Db transaction error:%v\n", tx.Error) + } + + return tx, nil +} + +// WriteRecord inserts a new record into the database using GORM's Create. +// If the operation fails, it returns the error. +func WriteRecord(db *gorm.DB, value interface{}) error { + if db == nil { + return errors.New("db cannot be nil") + } + if value == nil { + return errors.New("value to insert cannot be nil") + } + + if err := db.Create(value).Error; err != nil { + db.Rollback() + return err + } + return nil +} + +// CommitTransaction commits the ongoing transaction. +func CommitTransaction(tx *gorm.DB) error { + if tx == nil { + return errors.New("transaction cannot be nil") + } + return tx.Commit().Error +} diff --git a/language/go/extractor/src/util/setup.go b/language/go/extractor/src/util/setup.go new file mode 100644 index 00000000..9768292e --- /dev/null +++ b/language/go/extractor/src/util/setup.go @@ -0,0 +1,119 @@ +package util + +import ( + "fmt" + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" +) + +// checkTargetGoInstallInformation checks if the target Go installation is valid and compares its version. +func checkTargetGoInstallInformation(installDir, version string) (string, error) { + libDir := filepath.Join(installDir, "go", "src") + targetGoExe := filepath.Join(installDir, "go", "bin", "go") + + for _, dir := range []string{installDir, libDir, targetGoExe} { + if _, err := os.Stat(dir); os.IsNotExist(err) { + return "", fmt.Errorf("invalid go package: %v", dir) + } + } + + output, err := exec.Command(targetGoExe, "version").CombinedOutput() + if err != nil { + return "", fmt.Errorf("unable to run go version: %v", err) + } + installVersion := strings.Fields(string(output))[2] + + if installVersion < version { + if err := RemoveContents(installDir); err != nil { + return installVersion, fmt.Errorf("unable to remove old version: %v", err) + } + } + return installVersion, nil +} + +// configGoEnv sets up the Go environment variables. +func configGoEnv(goRoot string) error { + envVars := map[string]string{ + "GOROOT": goRoot, + "GOPATH": filepath.Join(goRoot, "project"), + "PATH": filepath.Join(goRoot, "bin") + string(os.PathListSeparator) + os.Getenv("PATH"), + "GOPROXY": "https://goproxy.cn,direct", + "GOPRIVATE": "*.alipay.com,*.alipay-inc.com", + } + + for key, value := range envVars { + if err := os.Setenv(key, value); err != nil { + return fmt.Errorf("unable to set %s: %v", key, err) + } + } + + return nil +} + +// installDesiredGoEnv installs the desired Go version and sets up the environment. +func installDesiredGoEnv(location, version string) (string, error) { + exe, err := os.Executable() + if err != nil { + return "", fmt.Errorf("unable to get executable path: %v", err) + } + path := filepath.Dir(exe) + + log.Printf("extracting go sdk") + script := fmt.Sprintf(` + set -e + mkdir -p %q + rm -rf %q/* + tar -C %q -xzf %q/gosdk.tar.gz + `, location, location, location, path) + cmd := exec.Command("/bin/bash", "-c", script) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + return "", fmt.Errorf("install script failed: %v", err) + } + log.Printf("done extracting go sdk") + + goRoot := filepath.Join(location, "go") + if err := configGoEnv(goRoot); err != nil { + return "", fmt.Errorf("unable to configure Go environment: %v", err) + } + + versioning, err := exec.Command(filepath.Join(goRoot, "bin", "go"), "version").CombinedOutput() + if err != nil { + return "", fmt.Errorf("unable to run go command: %s", err) + } + installedVersion := strings.Fields(string(versioning))[2] + if installedVersion < version { + return "", fmt.Errorf("go version must be >= %s, got %s", version, installedVersion) + } + return installedVersion, nil +} + +// settingUpGoEnv checks the Go installation and sets up the environment. +func settingUpGoEnv(version string) { + goInstallDir := filepath.Join(os.Getenv("HOME"), "sparrowgoextractor") + goVersion, err := checkTargetGoInstallInformation(goInstallDir, version) + if err != nil { + goVersion, err = installDesiredGoEnv(goInstallDir, version) + if err != nil { + log.Fatal(err) + } + } else { + err = configGoEnv(filepath.Join(goInstallDir, "go")) + if err != nil { + log.Fatal(err) + } + } + log.Printf("using Go sdk %v-%v-%v", goVersion, runtime.GOOS, runtime.GOARCH) +} + +// PrepareRunEnv sets up the Go environment with the minimum required Go version. +func PrepareRunEnv() { + const minGoVersion = "go1.11" + settingUpGoEnv(minGoVersion) +} diff --git a/language/go/extractor/src/util/util.go b/language/go/extractor/src/util/util.go new file mode 100644 index 00000000..ce6b8710 --- /dev/null +++ b/language/go/extractor/src/util/util.go @@ -0,0 +1,372 @@ +package util + +import ( + "crypto/md5" + "crypto/sha256" + "encoding/hex" + "fmt" + "hash" + "hash/fnv" + "io" + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" +) + +var extractorPath string + +// Getenv retrieves the value of the environment variable named by the key. +// If that variable is not present, it iterates over the given aliases until +// it finds one that is. If none are present, the empty string is returned. +func Getenv(key string, aliases ...string) string { + val := os.Getenv(key) + if val != "" { + return val + } + for _, alias := range aliases { + val = os.Getenv(alias) + if val != "" { + return val + } + } + return "" +} + +// runGoList is a helper function for running go list with format `format` and flags `flags` on +// package `pkgpath`. +func runGoList(format string, pkgpath string, flags ...string) (string, error) { + return runGoListWithEnv(format, pkgpath, nil, flags...) +} + +func runGoListWithEnv(format string, pkgpath string, additionalEnv []string, flags ...string) (string, error) { + args := append([]string{"list", "-e", "-f", format}, flags...) + args = append(args, pkgpath) + cmd := exec.Command("go", args...) + cmd.Env = append(os.Environ(), additionalEnv...) + out, err := cmd.CombinedOutput() + if err != nil { + log.Printf("Warning: go list command failed with error: %s\nOutput:\n%s\n", err.Error(), out) + return "", err + } + return strings.TrimSpace(string(out)), nil +} + +// GetModDir gets the absolute directory of the module containing the package with path `pkgpath`. +// It passes the `go list` the flags specified by `flags`. +func GetModDir(pkgpath string, flags ...string) string { + modDir, err := runGoListWithEnv("{{.Module.Dir}}", pkgpath, []string{"GO111MODULE=on"}, flags...) + if err != nil { + return "" + } + absModDir, err := filepath.Abs(modDir) + if err != nil { + log.Printf("Warning: unable to make %s absolute: %s", modDir, err.Error()) + return "" + } + return absModDir +} + +// GetPkgDir gets the absolute directory containing the package with path `pkgpath`. +// It passes the `go list` command the flags specified by `flags`. +func GetPkgDir(pkgpath string, flags ...string) string { + pkgDir, err := runGoList("{{.Dir}}", pkgpath, flags...) + if err != nil { + return "" + } + absPkgDir, err := filepath.Abs(pkgDir) + if err != nil { + log.Printf("Warning: unable to make %s absolute: %s", pkgDir, err.Error()) + return "" + } + return absPkgDir +} + +// FileExists tests whether the file at `filename` exists and is not a directory. +func FileExists(filename string) bool { + info, err := os.Stat(filename) + if err != nil { + if !os.IsNotExist(err) { + log.Printf("Unable to stat %s: %s\n", filename, err.Error()) + } + return false + } + return !info.IsDir() +} + +// DirExists tests whether `filename` exists and is a directory. +func DirExists(filename string) bool { + info, err := os.Stat(filename) + if err != nil { + if !os.IsNotExist(err) { + log.Printf("Unable to stat %s: %s\n", filename, err.Error()) + } + return false + } + return info.IsDir() +} + +// RunCmd runs a command and logs any errors without stopping the execution. +func RunCmd(cmd *exec.Cmd) error { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Start(); err != nil { + return err + } + if err := cmd.Wait(); err != nil { + return err + } + return nil +} + +// getOsToolsSubdir determines the subdirectory name based on the OS. +func getOsToolsSubdir() (string, error) { + platform := Getenv("PLATFORM") + if platform != "" { + return platform, nil + } + switch runtime.GOOS { + case "darwin": + return "osx64", nil + case "linux": + return "linux64", nil + default: + return "", fmt.Errorf("unsupported OS: %s", runtime.GOOS) + } +} + +// getExtractorDir computes the directory for the extractor. +func getExtractorDir() (string, error) { + extractorRoot := Getenv("EXTRACTOR_GO_ROOT") + if extractorRoot == "" { + log.Println("Falling back to guess the root based on this executable's path.") + mypath, err := os.Executable() + if err != nil { + return "", fmt.Errorf("EXTRACTOR_GO_ROOT not set, and could not determine path of this executable: %v", err) + } + return filepath.Dir(mypath), nil + } + + osSubdir, err := getOsToolsSubdir() + if err != nil { + return "", err + } + return filepath.Join(extractorRoot, "tools", osSubdir), nil +} + +// GetExtractorPath retrieves the path to the go-extractor. +func GetExtractorPath() (string, error) { + if extractorPath != "" { + return extractorPath, nil + } + dir, err := getExtractorDir() + if err != nil { + return "", err + } + extractorPath = filepath.Join(dir, "go-extractor") + return extractorPath, nil +} + +// EscapeTrapSpecialChars replaces special characters with their HTML entities. +func EscapeTrapSpecialChars(s string) string { + specialChars := map[string]string{ + "&": "&", + "{": "{", + "}": "}", + "\"": """, + "@": "@", + "#": "#", + } + for old, new := range specialChars { + s = strings.ReplaceAll(s, old, new) + } + return s +} + +// RemoveContents removes all the contents in the specified directory. +func RemoveContents(dir string) error { + d, err := os.Open(dir) + if err != nil { + return err + } + defer d.Close() + + names, err := d.Readdirnames(-1) + if err != nil { + return err + } + for _, name := range names { + err := os.RemoveAll(filepath.Join(dir, name)) + if err != nil { + return err + } + } + return nil +} + +// PrintTracebackAfterDetectedBadEntrance prints the caller after detecting a bad entrance. +// args should be passed with [error, [shouldExit]] +// where 'error' is the error msg and shouldExit is a boolean value indicating if the program should exit. +func PrintTracebackAfterDetectedBadEntrance(skip int, args ...interface{}) { + var msg string + var shouldStop bool + + for i, arg := range args { + switch v := arg.(type) { + case error: + if i == 0 { + msg = v.Error() + } + case bool: + if i == 1 { + shouldStop = v + } + default: + msg = fmt.Sprintf("%v %v", msg, arg) + } + } + + pc, fn, line, ok := runtime.Caller(skip + 1) + var printMsg string + if ok { + printMsg = fmt.Sprintf("[BadEntrance] in %s[%s:%d] %v", + runtime.FuncForPC(pc).Name(), fn, line, msg) + } else { + printMsg = fmt.Sprintf("[BadEntrance] %v", msg) + } + + if shouldStop { + log.Fatalf(printMsg) + } else { + log.Printf(printMsg) + } +} + +// ByteCountSI converts a size in bytes to a human-readable string in SI (decimal) format. +func ByteCountSI(b int64) string { + const unit = 1000 + if b < unit { + return fmt.Sprintf("%d B", b) + } + div, exp := int64(unit), 0 + for n := b / unit; n >= unit; n /= unit { + div *= unit + exp++ + } + return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp]) +} + +// ReadFile reads the content of a file by path as input parameter and returns the content or an error. +func ReadFile(path string) ([]byte, error) { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + + fileInfo, err := file.Stat() + if err != nil { + return nil, err + } + fileSize := fileInfo.Size() + buffer := make([]byte, fileSize) + + _, err = file.Read(buffer) + if err != nil { + return nil, err + } + return buffer, nil +} + +// GetFileDigest calculates the hash digest of a file using the specified algorithm. +// Supported algorithms are "md5" and "sha256". +// It returns a hex-encoded hash string and any error encountered. +func GetFileDigest(file, algo string) (string, error) { + f, err := os.Open(file) + if err != nil { + return "", fmt.Errorf("failed to open file: %v", err) + } + defer func() { + if cerr := f.Close(); cerr != nil { + log.Printf("Failed to close file: %v\n", cerr) + } + }() + + var m hash.Hash + switch algo { + case "md5": + m = md5.New() + case "sha256": + m = sha256.New() + default: + return "", fmt.Errorf("unsupported hash algorithm: %s", algo) + } + + if _, err := io.Copy(m, f); err != nil { + PrintTracebackAfterDetectedBadEntrance(1, err) + } + + return hex.EncodeToString(m.Sum(nil)), nil +} + +// GetIDFromDigest Generate an int64 ID with hash for a given string +func GetIDFromDigest(str, surfix string) int64 { + h := fnv.New64a() + format := fmt.Sprintf("coref://go?digest=%s#%s", str, surfix) + _, err := h.Write([]byte(format)) + if err != nil { + return 0 + } + return int64(h.Sum64()) +} + +// GetFileFullPath Wrapper to return an absolute representation of path. +func GetFileFullPath(f string) string { + abs, err := filepath.Abs(f) + if err == nil { + return abs + } + return "" +} + +func MaxParallelism() int { + maxProcs := runtime.GOMAXPROCS(0) + numCPU := runtime.NumCPU() + if maxProcs > numCPU { + return numCPU + } + return maxProcs +} + +// DirExistsAndClean checks if a directory exists and cleans its contents if it does. +func DirExistsAndClean(dirPath string) error { + // Check if the directory exists + if _, err := os.Stat(dirPath); os.IsNotExist(err) { + // Directory does not exist, nothing to clean + return nil + } else if err != nil { + // An error other than "not exist" occurred + return err + } + + // Directory exists, read all of its contents + files, err := os.ReadDir(dirPath) + if err != nil { + return err + } + + // Loop through and delete each file + for _, file := range files { + // Generate the full path for the file or directory + fullPath := filepath.Join(dirPath, file.Name()) + + // Remove the file or directory + if err := os.RemoveAll(fullPath); err != nil { + return err + } + } + + return nil +} diff --git a/language/go/extractor/src/util/util_test.go b/language/go/extractor/src/util/util_test.go new file mode 100644 index 00000000..3546606b --- /dev/null +++ b/language/go/extractor/src/util/util_test.go @@ -0,0 +1,74 @@ +package util + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var golden = []struct { + filename string + algo string + hash string +}{ + {"./util.go", "md5", "6d3b7c7dbbe07422f6cc9163dcc11d33"}, + {"./util.go", "sha256", "d3b66349ffb04e1276cb059df8a514025ce900e8d25980c2453b468d151c3fef"}, +} + +func TestGetFileDigest(t *testing.T) { + for _, g := range golden { + t.Run(g.algo, func(t *testing.T) { + hash, err := GetFileDigest(g.filename, g.algo) + require.NoError(t, err, "GetFileDigest should not return an error") + assert.Equal(t, g.hash, hash, "GetFileDigest should return the correct hash") + }) + } +} + +func TestGetFileFullPath(t *testing.T) { + curDir, err := os.Getwd() + assert.NoError(t, err, "os.Getwd should not return an error") + + parentDir, err := filepath.Abs(filepath.Join(curDir, "..")) + assert.NoError(t, err, "filepath.Abs should not return an error") + + testFilePath := filepath.Join(curDir, "util.go") + + testCases := []struct { + path string + fullpath string + }{ + {".", curDir}, + {"..", parentDir}, + {"util.go", testFilePath}, + {"../../src/util", curDir}, + } + + for _, tc := range testCases { + t.Run(tc.path, func(t *testing.T) { + realPath := GetFileFullPath(tc.path) + assert.Equal(t, tc.fullpath, realPath, "GetFileFullPath should return the correct full path") + }) + } +} + +func TestGetIDFromDigest(t *testing.T) { + testCases := []struct { + content string + digest int64 + }{ + {"test", 829678970432003591}, + // The following test case has been adjusted to use a platform-neutral path. + {"# /path/to/temporary/file", 5363033782971372267}, + } + + for _, tc := range testCases { + t.Run(tc.content, func(t *testing.T) { + realVal := GetIDFromDigest(tc.content, "") + assert.Equal(t, tc.digest, realVal, "GetIDFromDigest should return the correct digest") + }) + } +}