goscript 0.0.84 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/README.md +267 -243
  2. package/cmd/goscript/cmd-test.go +193 -0
  3. package/cmd/goscript/cmd-test_test.go +76 -0
  4. package/cmd/goscript/cmd_compile.go +70 -69
  5. package/cmd/goscript/cmd_compile_test.go +79 -0
  6. package/cmd/goscript/main.go +11 -5
  7. package/compiler/build-flags.go +38 -0
  8. package/compiler/compile-request.go +220 -0
  9. package/compiler/compiler.go +16 -1336
  10. package/compiler/compliance_test.go +188 -0
  11. package/compiler/config.go +6 -13
  12. package/compiler/diagnostic.go +70 -0
  13. package/compiler/gotest/owner.go +24 -0
  14. package/compiler/gotest/package-result.go +67 -0
  15. package/compiler/gotest/request.go +145 -0
  16. package/compiler/gotest/result.go +28 -0
  17. package/compiler/gotest/runner.go +588 -0
  18. package/compiler/gotest/runner_test.go +627 -0
  19. package/compiler/gotest/test.go +9 -0
  20. package/compiler/index.test.ts +28 -28
  21. package/compiler/index.ts +40 -72
  22. package/compiler/lowered-program.go +184 -0
  23. package/compiler/lowering.go +8072 -0
  24. package/compiler/override-facts.go +307 -0
  25. package/compiler/override-registry.go +283 -0
  26. package/compiler/override-registry_test.go +254 -0
  27. package/compiler/package-graph.go +254 -0
  28. package/compiler/package-graph_test.go +316 -0
  29. package/compiler/package-test-function.go +9 -0
  30. package/compiler/package-test-graph-package.go +40 -0
  31. package/compiler/package-test-graph-variant.go +105 -0
  32. package/compiler/package-test-graph.go +117 -0
  33. package/compiler/package-test-graph_test.go +144 -0
  34. package/compiler/result.go +13 -0
  35. package/compiler/runtime-contract.go +439 -0
  36. package/compiler/runtime-contract_test.go +104 -0
  37. package/compiler/semantic-model-types.go +113 -0
  38. package/compiler/semantic-model.go +1422 -0
  39. package/compiler/semantic-model_test.go +471 -0
  40. package/compiler/service.go +133 -0
  41. package/compiler/skeleton_test.go +1775 -0
  42. package/compiler/tsworkspace/owner.go +334 -0
  43. package/compiler/tsworkspace/owner_test.go +93 -0
  44. package/compiler/tsworkspace/result.go +17 -0
  45. package/compiler/typescript-emitter.go +1040 -0
  46. package/compiler/wasm/compile.go +2 -3
  47. package/compiler/wasm/compile_test.go +79 -0
  48. package/compiler/wasm_api.go +140 -124
  49. package/dist/compiler/index.d.ts +1 -3
  50. package/dist/compiler/index.js +31 -55
  51. package/dist/compiler/index.js.map +1 -1
  52. package/dist/gs/builtin/builtin.d.ts +33 -2
  53. package/dist/gs/builtin/builtin.js +217 -6
  54. package/dist/gs/builtin/builtin.js.map +1 -1
  55. package/dist/gs/builtin/channel.d.ts +11 -3
  56. package/dist/gs/builtin/channel.js +12 -0
  57. package/dist/gs/builtin/channel.js.map +1 -1
  58. package/dist/gs/builtin/hostio.d.ts +15 -1
  59. package/dist/gs/builtin/hostio.js +134 -49
  60. package/dist/gs/builtin/hostio.js.map +1 -1
  61. package/dist/gs/builtin/index.d.ts +1 -0
  62. package/dist/gs/builtin/index.js +1 -0
  63. package/dist/gs/builtin/index.js.map +1 -1
  64. package/dist/gs/builtin/slice.d.ts +23 -3
  65. package/dist/gs/builtin/slice.js +216 -44
  66. package/dist/gs/builtin/slice.js.map +1 -1
  67. package/dist/gs/builtin/type.d.ts +16 -2
  68. package/dist/gs/builtin/type.js +134 -21
  69. package/dist/gs/builtin/type.js.map +1 -1
  70. package/dist/gs/builtin/varRef.d.ts +5 -0
  71. package/dist/gs/builtin/varRef.js +23 -0
  72. package/dist/gs/builtin/varRef.js.map +1 -1
  73. package/dist/gs/bytes/buffer.gs.js +48 -44
  74. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  75. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  76. package/dist/gs/bytes/reader.gs.js +20 -18
  77. package/dist/gs/bytes/reader.gs.js.map +1 -1
  78. package/dist/gs/context/context.d.ts +5 -4
  79. package/dist/gs/context/context.js +10 -10
  80. package/dist/gs/context/context.js.map +1 -1
  81. package/dist/gs/crypto/internal/fips140deps/byteorder/index.d.ts +1 -0
  82. package/dist/gs/crypto/internal/fips140deps/byteorder/index.js +2 -0
  83. package/dist/gs/crypto/internal/fips140deps/byteorder/index.js.map +1 -0
  84. package/dist/gs/crypto/internal/fips140deps/godebug/index.d.ts +1 -0
  85. package/dist/gs/crypto/internal/fips140deps/godebug/index.js +2 -0
  86. package/dist/gs/crypto/internal/fips140deps/godebug/index.js.map +1 -0
  87. package/dist/gs/crypto/rand/index.d.ts +5 -0
  88. package/dist/gs/crypto/rand/index.js +77 -0
  89. package/dist/gs/crypto/rand/index.js.map +1 -0
  90. package/dist/gs/embed/index.d.ts +7 -0
  91. package/dist/gs/embed/index.js +16 -0
  92. package/dist/gs/embed/index.js.map +1 -0
  93. package/dist/gs/encoding/json/index.d.ts +4 -0
  94. package/dist/gs/encoding/json/index.js +178 -0
  95. package/dist/gs/encoding/json/index.js.map +1 -0
  96. package/dist/gs/errors/errors.d.ts +4 -0
  97. package/dist/gs/errors/errors.js +81 -0
  98. package/dist/gs/errors/errors.js.map +1 -1
  99. package/dist/gs/fmt/fmt.d.ts +4 -4
  100. package/dist/gs/fmt/fmt.js +42 -11
  101. package/dist/gs/fmt/fmt.js.map +1 -1
  102. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +36 -1
  103. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +212 -2
  104. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  105. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +189 -0
  106. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +825 -0
  107. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -0
  108. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +163 -0
  109. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +449 -0
  110. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +1 -0
  111. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  112. package/dist/gs/github.com/klauspost/compress/internal/le/index.d.ts +9 -0
  113. package/dist/gs/github.com/klauspost/compress/internal/le/index.js +71 -0
  114. package/dist/gs/github.com/klauspost/compress/internal/le/index.js.map +1 -0
  115. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  116. package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
  117. package/dist/gs/go/internal/scannerhooks/index.d.ts +3 -0
  118. package/dist/gs/go/internal/scannerhooks/index.js +5 -0
  119. package/dist/gs/go/internal/scannerhooks/index.js.map +1 -0
  120. package/dist/gs/go/scanner/index.d.ts +42 -0
  121. package/dist/gs/go/scanner/index.js +155 -0
  122. package/dist/gs/go/scanner/index.js.map +1 -0
  123. package/dist/gs/go/token/index.d.ts +187 -0
  124. package/dist/gs/go/token/index.js +578 -0
  125. package/dist/gs/go/token/index.js.map +1 -0
  126. package/dist/gs/internal/abi/index.d.ts +4 -0
  127. package/dist/gs/internal/abi/index.js +10 -0
  128. package/dist/gs/internal/abi/index.js.map +1 -1
  129. package/dist/gs/internal/bytealg/index.d.ts +2 -0
  130. package/dist/gs/internal/bytealg/index.js +14 -0
  131. package/dist/gs/internal/bytealg/index.js.map +1 -1
  132. package/dist/gs/internal/byteorder/index.d.ts +8 -2
  133. package/dist/gs/internal/byteorder/index.js +56 -25
  134. package/dist/gs/internal/byteorder/index.js.map +1 -1
  135. package/dist/gs/internal/godebug/index.d.ts +12 -0
  136. package/dist/gs/internal/godebug/index.js +30 -0
  137. package/dist/gs/internal/godebug/index.js.map +1 -0
  138. package/dist/gs/io/fs/fs.js.map +1 -1
  139. package/dist/gs/io/fs/index.d.ts +1 -0
  140. package/dist/gs/io/fs/index.js +1 -0
  141. package/dist/gs/io/fs/index.js.map +1 -1
  142. package/dist/gs/io/fs/readdir.js.map +1 -1
  143. package/dist/gs/io/fs/readfile.js.map +1 -1
  144. package/dist/gs/io/fs/readlink.d.ts +8 -0
  145. package/dist/gs/io/fs/readlink.js +64 -0
  146. package/dist/gs/io/fs/readlink.js.map +1 -0
  147. package/dist/gs/io/fs/stat.js.map +1 -1
  148. package/dist/gs/io/fs/sub.js.map +1 -1
  149. package/dist/gs/io/fs/walk.d.ts +3 -3
  150. package/dist/gs/io/fs/walk.js +7 -7
  151. package/dist/gs/io/fs/walk.js.map +1 -1
  152. package/dist/gs/io/io.d.ts +40 -6
  153. package/dist/gs/io/io.js +151 -26
  154. package/dist/gs/io/io.js.map +1 -1
  155. package/dist/gs/maps/iter.d.ts +3 -3
  156. package/dist/gs/maps/iter.js +3 -3
  157. package/dist/gs/maps/iter.js.map +1 -1
  158. package/dist/gs/maps/maps.d.ts +2 -2
  159. package/dist/gs/maps/maps.js +1 -1
  160. package/dist/gs/maps/maps.js.map +1 -1
  161. package/dist/gs/math/bits/index.d.ts +13 -4
  162. package/dist/gs/math/bits/index.js +66 -34
  163. package/dist/gs/math/bits/index.js.map +1 -1
  164. package/dist/gs/math/const.gs.d.ts +5 -5
  165. package/dist/gs/math/const.gs.js +4 -4
  166. package/dist/gs/math/const.gs.js.map +1 -1
  167. package/dist/gs/mime/index.d.ts +1 -0
  168. package/dist/gs/mime/index.js +50 -0
  169. package/dist/gs/mime/index.js.map +1 -0
  170. package/dist/gs/net/http/httptest/index.d.ts +11 -0
  171. package/dist/gs/net/http/httptest/index.js +21 -0
  172. package/dist/gs/net/http/httptest/index.js.map +1 -0
  173. package/dist/gs/net/http/index.d.ts +27 -0
  174. package/dist/gs/net/http/index.js +61 -0
  175. package/dist/gs/net/http/index.js.map +1 -0
  176. package/dist/gs/os/dir_unix.gs.js +2 -2
  177. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  178. package/dist/gs/os/error.gs.js +2 -4
  179. package/dist/gs/os/error.gs.js.map +1 -1
  180. package/dist/gs/os/exec.gs.js.map +1 -1
  181. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  182. package/dist/gs/os/rawconn_js.gs.js.map +1 -1
  183. package/dist/gs/os/root_js.gs.js.map +1 -1
  184. package/dist/gs/os/tempfile.gs.js +66 -9
  185. package/dist/gs/os/tempfile.gs.js.map +1 -1
  186. package/dist/gs/os/types.gs.js.map +1 -1
  187. package/dist/gs/os/types_js.gs.js +9 -9
  188. package/dist/gs/os/types_js.gs.js.map +1 -1
  189. package/dist/gs/os/types_unix.gs.js.map +1 -1
  190. package/dist/gs/path/filepath/match.js +165 -3
  191. package/dist/gs/path/filepath/match.js.map +1 -1
  192. package/dist/gs/path/filepath/path.d.ts +3 -1
  193. package/dist/gs/path/filepath/path.js +133 -4
  194. package/dist/gs/path/filepath/path.js.map +1 -1
  195. package/dist/gs/path/match.js.map +1 -1
  196. package/dist/gs/path/path.d.ts +4 -1
  197. package/dist/gs/path/path.js +16 -4
  198. package/dist/gs/path/path.js.map +1 -1
  199. package/dist/gs/reflect/index.d.ts +3 -3
  200. package/dist/gs/reflect/index.js +2 -2
  201. package/dist/gs/reflect/index.js.map +1 -1
  202. package/dist/gs/reflect/map.js +3 -0
  203. package/dist/gs/reflect/map.js.map +1 -1
  204. package/dist/gs/reflect/type.d.ts +9 -5
  205. package/dist/gs/reflect/type.js +233 -21
  206. package/dist/gs/reflect/type.js.map +1 -1
  207. package/dist/gs/reflect/types.js.map +1 -1
  208. package/dist/gs/reflect/visiblefields.js.map +1 -1
  209. package/dist/gs/runtime/debug/index.d.ts +2 -0
  210. package/dist/gs/runtime/debug/index.js +8 -0
  211. package/dist/gs/runtime/debug/index.js.map +1 -0
  212. package/dist/gs/runtime/runtime.d.ts +35 -3
  213. package/dist/gs/runtime/runtime.js +72 -0
  214. package/dist/gs/runtime/runtime.js.map +1 -1
  215. package/dist/gs/slices/slices.d.ts +24 -5
  216. package/dist/gs/slices/slices.js +214 -5
  217. package/dist/gs/slices/slices.js.map +1 -1
  218. package/dist/gs/sort/slice.gs.d.ts +3 -3
  219. package/dist/gs/sort/slice.gs.js +6 -6
  220. package/dist/gs/sort/slice.gs.js.map +1 -1
  221. package/dist/gs/sort/sort.gs.d.ts +4 -4
  222. package/dist/gs/sort/sort.gs.js +11 -8
  223. package/dist/gs/sort/sort.gs.js.map +1 -1
  224. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  225. package/dist/gs/strconv/quote.gs.js.map +1 -1
  226. package/dist/gs/strings/builder.d.ts +1 -1
  227. package/dist/gs/strings/builder.js +3 -2
  228. package/dist/gs/strings/builder.js.map +1 -1
  229. package/dist/gs/strings/reader.js.map +1 -1
  230. package/dist/gs/strings/replace.js.map +1 -1
  231. package/dist/gs/sync/atomic/type.gs.d.ts +9 -8
  232. package/dist/gs/sync/atomic/type.gs.js +0 -2
  233. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  234. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  235. package/dist/gs/sync/sync.d.ts +3 -0
  236. package/dist/gs/sync/sync.js +39 -0
  237. package/dist/gs/sync/sync.js.map +1 -1
  238. package/dist/gs/syscall/constants.d.ts +36 -24
  239. package/dist/gs/syscall/constants.js +12 -0
  240. package/dist/gs/syscall/constants.js.map +1 -1
  241. package/dist/gs/syscall/errors.d.ts +2 -0
  242. package/dist/gs/syscall/errors.js +8 -0
  243. package/dist/gs/syscall/errors.js.map +1 -1
  244. package/dist/gs/syscall/fs.d.ts +43 -0
  245. package/dist/gs/syscall/fs.js +102 -0
  246. package/dist/gs/syscall/fs.js.map +1 -1
  247. package/dist/gs/syscall/js/index.d.ts +90 -0
  248. package/dist/gs/syscall/js/index.js +375 -0
  249. package/dist/gs/syscall/js/index.js.map +1 -0
  250. package/dist/gs/syscall/types.d.ts +22 -0
  251. package/dist/gs/syscall/types.js +45 -1
  252. package/dist/gs/syscall/types.js.map +1 -1
  253. package/dist/gs/testing/index.d.ts +1 -0
  254. package/dist/gs/testing/index.js +2 -0
  255. package/dist/gs/testing/index.js.map +1 -0
  256. package/dist/gs/testing/testing.d.ts +77 -0
  257. package/dist/gs/testing/testing.js +301 -0
  258. package/dist/gs/testing/testing.js.map +1 -0
  259. package/dist/gs/time/time.d.ts +41 -4
  260. package/dist/gs/time/time.js +205 -36
  261. package/dist/gs/time/time.js.map +1 -1
  262. package/dist/gs/unicode/unicode.d.ts +23 -1
  263. package/dist/gs/unicode/unicode.js +79 -10
  264. package/dist/gs/unicode/unicode.js.map +1 -1
  265. package/dist/gs/unicode/utf8/utf8.d.ts +4 -4
  266. package/dist/gs/unicode/utf8/utf8.js +24 -11
  267. package/dist/gs/unicode/utf8/utf8.js.map +1 -1
  268. package/dist/gs/unique/index.d.ts +11 -0
  269. package/dist/gs/unique/index.js +71 -0
  270. package/dist/gs/unique/index.js.map +1 -0
  271. package/go.mod +2 -2
  272. package/go.sum +9 -0
  273. package/gs/builtin/builtin.ts +266 -8
  274. package/gs/builtin/channel.ts +22 -0
  275. package/gs/builtin/hostio.test.ts +177 -0
  276. package/gs/builtin/hostio.ts +171 -56
  277. package/gs/builtin/index.ts +1 -0
  278. package/gs/builtin/runtime-contract.test.ts +356 -0
  279. package/gs/builtin/slice.ts +259 -50
  280. package/gs/builtin/type.ts +188 -30
  281. package/gs/builtin/varRef.ts +38 -1
  282. package/gs/bytes/buffer.gs.ts +48 -44
  283. package/gs/bytes/meta.json +8 -3
  284. package/gs/bytes/reader.gs.ts +20 -19
  285. package/gs/context/context.test.ts +41 -0
  286. package/gs/context/context.ts +22 -26
  287. package/gs/crypto/internal/fips140deps/byteorder/index.ts +1 -0
  288. package/gs/crypto/internal/fips140deps/godebug/index.ts +1 -0
  289. package/gs/crypto/rand/index.test.ts +32 -0
  290. package/gs/crypto/rand/index.ts +90 -0
  291. package/gs/crypto/rand/meta.json +5 -0
  292. package/gs/embed/index.ts +20 -0
  293. package/gs/embed/meta.json +5 -0
  294. package/gs/encoding/json/index.test.ts +79 -0
  295. package/gs/encoding/json/index.ts +210 -0
  296. package/gs/errors/errors.test.ts +82 -0
  297. package/gs/errors/errors.ts +104 -0
  298. package/gs/fmt/fmt.ts +56 -16
  299. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +95 -0
  300. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +300 -2
  301. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +159 -0
  302. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +1005 -0
  303. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +719 -0
  304. package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +40 -0
  305. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
  306. package/gs/github.com/klauspost/compress/internal/le/index.test.ts +36 -0
  307. package/gs/github.com/klauspost/compress/internal/le/index.ts +114 -0
  308. package/gs/go/internal/scannerhooks/index.test.ts +14 -0
  309. package/gs/go/internal/scannerhooks/index.ts +9 -0
  310. package/gs/go/scanner/index.test.ts +72 -0
  311. package/gs/go/scanner/index.ts +204 -0
  312. package/gs/go/token/index.test.ts +67 -0
  313. package/gs/go/token/index.ts +686 -0
  314. package/gs/internal/abi/index.test.ts +18 -0
  315. package/gs/internal/abi/index.ts +14 -0
  316. package/gs/internal/bytealg/index.test.ts +18 -0
  317. package/gs/internal/bytealg/index.ts +16 -0
  318. package/gs/internal/byteorder/index.test.ts +39 -0
  319. package/gs/internal/byteorder/index.ts +100 -27
  320. package/gs/internal/godebug/index.test.ts +16 -0
  321. package/gs/internal/godebug/index.ts +35 -0
  322. package/gs/io/fs/index.ts +1 -0
  323. package/gs/io/fs/meta.json +5 -0
  324. package/gs/io/fs/readlink.test.ts +43 -0
  325. package/gs/io/fs/readlink.ts +77 -0
  326. package/gs/io/fs/walk.test.ts +61 -0
  327. package/gs/io/fs/walk.ts +9 -9
  328. package/gs/io/io.ts +174 -31
  329. package/gs/io/meta.json +10 -2
  330. package/gs/maps/iter.ts +12 -6
  331. package/gs/maps/maps.ts +8 -6
  332. package/gs/math/bits/index.ts +103 -47
  333. package/gs/math/const.gs.test.ts +11 -5
  334. package/gs/math/const.gs.ts +5 -6
  335. package/gs/mime/index.ts +54 -0
  336. package/gs/net/http/httptest/index.ts +25 -0
  337. package/gs/net/http/index.test.ts +20 -0
  338. package/gs/net/http/index.ts +81 -0
  339. package/gs/os/dir_unix.gs.ts +2 -3
  340. package/gs/os/file_unix_js.test.ts +50 -0
  341. package/gs/os/meta.json +1 -2
  342. package/gs/os/tempfile.gs.test.ts +85 -0
  343. package/gs/os/tempfile.gs.ts +71 -11
  344. package/gs/os/types_js.gs.ts +11 -11
  345. package/gs/path/filepath/match.test.ts +31 -12
  346. package/gs/path/filepath/match.ts +178 -3
  347. package/gs/path/filepath/path.test.ts +25 -0
  348. package/gs/path/filepath/path.ts +159 -5
  349. package/gs/path/path.ts +20 -5
  350. package/gs/reflect/index.ts +2 -1
  351. package/gs/reflect/map.test.ts +19 -0
  352. package/gs/reflect/map.ts +4 -0
  353. package/gs/reflect/type.ts +298 -29
  354. package/gs/reflect/typefor.test.ts +75 -0
  355. package/gs/runtime/debug/index.test.ts +24 -0
  356. package/gs/runtime/debug/index.ts +8 -0
  357. package/gs/runtime/runtime.test.ts +19 -0
  358. package/gs/runtime/runtime.ts +98 -3
  359. package/gs/slices/slices.test.ts +94 -0
  360. package/gs/slices/slices.ts +245 -5
  361. package/gs/sort/meta.json +7 -0
  362. package/gs/sort/slice.gs.ts +16 -7
  363. package/gs/sort/sort.gs.ts +16 -13
  364. package/gs/strings/builder.ts +4 -3
  365. package/gs/sync/atomic/type.gs.ts +13 -14
  366. package/gs/sync/meta.json +3 -1
  367. package/gs/sync/sync.test.ts +36 -0
  368. package/gs/sync/sync.ts +39 -0
  369. package/gs/syscall/constants.ts +39 -24
  370. package/gs/syscall/errors.ts +10 -0
  371. package/gs/syscall/fs.ts +195 -0
  372. package/gs/syscall/js/index.ts +458 -0
  373. package/gs/syscall/js/meta.json +4 -0
  374. package/gs/syscall/net.test.ts +85 -0
  375. package/gs/syscall/types.ts +56 -0
  376. package/gs/testing/index.ts +1 -0
  377. package/gs/testing/meta.json +5 -0
  378. package/gs/testing/testing.test.ts +90 -0
  379. package/gs/testing/testing.ts +382 -0
  380. package/gs/time/time.test.ts +106 -0
  381. package/gs/time/time.ts +278 -57
  382. package/gs/unicode/unicode.test.ts +25 -0
  383. package/gs/unicode/unicode.ts +119 -9
  384. package/gs/unicode/utf8/utf8.test.ts +13 -0
  385. package/gs/unicode/utf8/utf8.ts +28 -16
  386. package/gs/unique/index.ts +91 -0
  387. package/package.json +14 -13
  388. package/compiler/analysis.go +0 -3475
  389. package/compiler/analysis_test.go +0 -338
  390. package/compiler/assignment.go +0 -580
  391. package/compiler/builtin_test.go +0 -92
  392. package/compiler/code-writer.go +0 -115
  393. package/compiler/compiler_test.go +0 -149
  394. package/compiler/composite-lit.go +0 -779
  395. package/compiler/config_test.go +0 -62
  396. package/compiler/constraint.go +0 -86
  397. package/compiler/decl.go +0 -801
  398. package/compiler/expr-call-async.go +0 -188
  399. package/compiler/expr-call-builtins.go +0 -208
  400. package/compiler/expr-call-helpers.go +0 -382
  401. package/compiler/expr-call-make.go +0 -318
  402. package/compiler/expr-call-type-conversion.go +0 -520
  403. package/compiler/expr-call.go +0 -413
  404. package/compiler/expr-selector.go +0 -343
  405. package/compiler/expr-star.go +0 -82
  406. package/compiler/expr-type.go +0 -442
  407. package/compiler/expr-value.go +0 -89
  408. package/compiler/expr.go +0 -773
  409. package/compiler/field.go +0 -183
  410. package/compiler/gs_dependencies_test.go +0 -298
  411. package/compiler/lit.go +0 -322
  412. package/compiler/output.go +0 -72
  413. package/compiler/primitive.go +0 -149
  414. package/compiler/protobuf.go +0 -697
  415. package/compiler/sanitize.go +0 -100
  416. package/compiler/spec-struct.go +0 -995
  417. package/compiler/spec-value.go +0 -540
  418. package/compiler/spec.go +0 -725
  419. package/compiler/stmt-assign.go +0 -664
  420. package/compiler/stmt-for.go +0 -266
  421. package/compiler/stmt-range.go +0 -475
  422. package/compiler/stmt-select.go +0 -262
  423. package/compiler/stmt-type-switch.go +0 -147
  424. package/compiler/stmt.go +0 -1308
  425. package/compiler/type-assert.go +0 -386
  426. package/compiler/type-info.go +0 -156
  427. package/compiler/type-utils.go +0 -207
  428. package/compiler/type.go +0 -892
@@ -1,3475 +0,0 @@
1
- package compiler
2
-
3
- import (
4
- "encoding/json"
5
- "go/ast"
6
- "go/token"
7
- "go/types"
8
- "path/filepath"
9
- "slices"
10
- "strings"
11
-
12
- "github.com/aperturerobotics/goscript"
13
- "golang.org/x/tools/go/packages"
14
- )
15
-
16
- // fileImport tracks an import in a file.
17
- type fileImport struct {
18
- importPath string
19
- importVars map[string]struct{}
20
- }
21
-
22
- // AssignmentType indicates how a variable's value was assigned or used.
23
- type AssignmentType int
24
-
25
- const (
26
- // DirectAssignment represents a direct value copy (e.g., x = y)
27
- DirectAssignment AssignmentType = iota
28
- // AddressOfAssignment represents taking the address (e.g., p = &y)
29
- // or assigning to a dereferenced pointer (*p = y) - indicating the pointer p is used.
30
- AddressOfAssignment
31
- )
32
-
33
- // AssignmentInfo stores information about a single assignment source or destination.
34
- type AssignmentInfo struct {
35
- Object types.Object // The source or destination variable object
36
- Type AssignmentType // The type of assignment involved
37
- }
38
-
39
- // VariableUsageInfo tracks how a variable is used throughout the code.
40
- type VariableUsageInfo struct {
41
- // Sources lists variables whose values (or addresses) are assigned TO this variable.
42
- // Example: For `x = y`, y is a source for x. For `x = &y`, y is a source for x.
43
- Sources []AssignmentInfo
44
- // Destinations lists variables that are assigned the value (or address) FROM this variable.
45
- // Example: For `y = x`, y is a destination for x. For `p = &x`, p is a destination for x.
46
- Destinations []AssignmentInfo
47
- }
48
-
49
- // ShadowingInfo tracks variable shadowing in if statement initializations
50
- type ShadowingInfo struct {
51
- // ShadowedVariables maps shadowed variable names to their outer scope objects
52
- ShadowedVariables map[string]types.Object
53
- // TempVariables maps shadowed variable names to temporary variable names
54
- TempVariables map[string]string
55
- // TypeShadowedVars maps variable names that shadow type names to their renamed identifier
56
- // This happens when a variable name matches a type name used in its initialization
57
- // e.g., field := field{...} where the variable 'field' shadows the type 'field'
58
- TypeShadowedVars map[string]string
59
- }
60
-
61
- // FunctionTypeInfo represents Go function type information for reflection
62
- type FunctionTypeInfo struct {
63
- Params []types.Type // Parameter types
64
- Results []types.Type // Return types
65
- Variadic bool // Whether the function is variadic
66
- }
67
-
68
- // FunctionInfo consolidates function-related tracking data.
69
- type FunctionInfo struct {
70
- ReceiverUsed bool
71
- NamedReturns []string
72
- }
73
-
74
- // ReflectedFunctionInfo tracks functions that need reflection support
75
- type ReflectedFunctionInfo struct {
76
- FuncType *types.Signature // The function's type signature
77
- NeedsReflect bool // Whether this function is used with reflection
78
- }
79
-
80
- // NodeInfo consolidates node-related tracking data.
81
- type NodeInfo struct {
82
- NeedsDefer bool
83
- InAsyncContext bool
84
- IsBareReturn bool
85
- EnclosingFuncDecl *ast.FuncDecl
86
- EnclosingFuncLit *ast.FuncLit
87
- IsInsideFunction bool // true if this declaration is inside a function body
88
- IsMethodValue bool // true if this SelectorExpr is a method value that needs binding
89
- ShadowingInfo *ShadowingInfo // variable shadowing information for if statements
90
- IdentifierMapping string // replacement name for this identifier (e.g., receiver -> "receiver")
91
- }
92
-
93
- // GsMetadata represents the structure of a meta.json file in gs/ packages
94
- type GsMetadata struct {
95
- Dependencies []string `json:"dependencies,omitempty"`
96
- AsyncMethods map[string]bool `json:"asyncMethods,omitempty"`
97
- }
98
-
99
- // InterfaceMethodKey uniquely identifies an interface method
100
- type InterfaceMethodKey struct {
101
- InterfaceType string // The string representation of the interface type
102
- MethodName string // The method name
103
- }
104
-
105
- // MethodKey uniquely identifies a method for async analysis
106
- type MethodKey struct {
107
- PackagePath string // Package path
108
- ReceiverType string // Receiver type name (empty for package-level functions)
109
- MethodName string // Method or function name
110
- }
111
-
112
- // ImplementationInfo tracks information about a struct that implements an interface method
113
- type ImplementationInfo struct {
114
- StructType *types.Named // The struct type that implements the interface
115
- Method *types.Func // The method object
116
- }
117
-
118
- // Analysis holds information gathered during the analysis phase of the Go code compilation.
119
- // This data is used to make decisions about how to generate TypeScript code.
120
- // Analysis is read-only after being built and should not be modified during code generation.
121
- type Analysis struct {
122
- // VariableUsage tracks how variables are assigned and used, particularly for pointer analysis.
123
- // The key is the variable's types.Object.
124
- VariableUsage map[types.Object]*VariableUsageInfo
125
-
126
- // Imports stores the imports for the file
127
- Imports map[string]*fileImport
128
-
129
- // Cmap stores the comment map for the file
130
- Cmap ast.CommentMap
131
-
132
- // FunctionData consolidates function-related tracking into one map
133
- FunctionData map[types.Object]*FunctionInfo
134
-
135
- // NodeData consolidates node-related tracking into one map
136
- NodeData map[ast.Node]*NodeInfo
137
-
138
- // Keep specialized maps that serve different purposes
139
- // FuncLitData tracks function literal specific data since they don't have types.Object
140
- FuncLitData map[*ast.FuncLit]*FunctionInfo
141
-
142
- // ReflectedFunctions tracks functions that need reflection type metadata
143
- ReflectedFunctions map[ast.Node]*ReflectedFunctionInfo
144
-
145
- // FunctionAssignments tracks which function literals are assigned to which variables
146
- FunctionAssignments map[types.Object]ast.Node
147
-
148
- // AsyncReturningVars tracks variables whose function type returns async values
149
- // This happens when a variable is assigned from a higher-order function (like sync.OnceValue)
150
- // that receives an async function literal as an argument
151
- AsyncReturningVars map[types.Object]bool
152
-
153
- // NamedBasicTypes tracks types that should be implemented as type aliases with standalone functions
154
- // This includes named types with basic underlying types (like uint32, string) that have methods
155
- NamedBasicTypes map[types.Type]bool
156
-
157
- // AllPackages stores all loaded packages for dependency analysis
158
- // Key: package path, Value: package data
159
- AllPackages map[string]*packages.Package
160
-
161
- // InterfaceImplementations tracks which struct types implement which interface methods
162
- // This is used to determine interface method async status based on implementations
163
- InterfaceImplementations map[InterfaceMethodKey][]ImplementationInfo
164
-
165
- // MethodAsyncStatus stores the async status of all methods analyzed
166
- // This is computed once during analysis and reused during code generation
167
- MethodAsyncStatus map[MethodKey]bool
168
-
169
- // ReferencedTypesPerFile tracks which named types are referenced in each file.
170
- // This is used to filter synthetic imports to only include packages needed
171
- // by types actually used in each specific file, not all types in the package.
172
- // Key: file path, Value: set of named types referenced in that file
173
- ReferencedTypesPerFile map[string]map[*types.Named]bool
174
-
175
- // SyntheticImportsPerFile stores synthetic imports needed per file.
176
- // Key: file path, Value: map of package name to import info
177
- SyntheticImportsPerFile map[string]map[string]*fileImport
178
- }
179
-
180
- // PackageAnalysis holds cross-file analysis data for a package
181
- type PackageAnalysis struct {
182
- // FunctionDefs maps file names to the functions defined in that file
183
- // Key: filename (without .go extension), Value: list of function names
184
- FunctionDefs map[string][]string
185
-
186
- // FunctionCalls maps file names to the functions they call from other files
187
- // Key: filename (without .go extension), Value: map[sourceFile][]functionNames
188
- FunctionCalls map[string]map[string][]string
189
-
190
- // TypeDefs maps file names to the types defined in that file
191
- // Key: filename (without .go extension), Value: list of type names
192
- TypeDefs map[string][]string
193
-
194
- // TypeCalls maps file names to the types they reference from other files
195
- // Key: filename (without .go extension), Value: map[sourceFile][]typeNames
196
- TypeCalls map[string]map[string][]string
197
-
198
- // VariableDefs maps file names to the package-level variables defined in that file
199
- // Key: filename (without .go extension), Value: list of variable names
200
- VariableDefs map[string][]string
201
-
202
- // VariableCalls maps file names to the package-level variables they reference from other files
203
- // Key: filename (without .go extension), Value: map[sourceFile][]variableNames
204
- VariableCalls map[string]map[string][]string
205
- }
206
-
207
- // NewAnalysis creates a new Analysis instance.
208
- func NewAnalysis(allPackages map[string]*packages.Package) *Analysis {
209
- if allPackages == nil {
210
- allPackages = make(map[string]*packages.Package)
211
- }
212
-
213
- return &Analysis{
214
- VariableUsage: make(map[types.Object]*VariableUsageInfo),
215
- Imports: make(map[string]*fileImport),
216
- FunctionData: make(map[types.Object]*FunctionInfo),
217
- NodeData: make(map[ast.Node]*NodeInfo),
218
- FuncLitData: make(map[*ast.FuncLit]*FunctionInfo),
219
- ReflectedFunctions: make(map[ast.Node]*ReflectedFunctionInfo),
220
- FunctionAssignments: make(map[types.Object]ast.Node),
221
- AsyncReturningVars: make(map[types.Object]bool),
222
- NamedBasicTypes: make(map[types.Type]bool),
223
- AllPackages: allPackages,
224
- InterfaceImplementations: make(map[InterfaceMethodKey][]ImplementationInfo),
225
- MethodAsyncStatus: make(map[MethodKey]bool),
226
- ReferencedTypesPerFile: make(map[string]map[*types.Named]bool),
227
- SyntheticImportsPerFile: make(map[string]map[string]*fileImport),
228
- }
229
- }
230
-
231
- // NewPackageAnalysis creates a new PackageAnalysis instance
232
- func NewPackageAnalysis() *PackageAnalysis {
233
- return &PackageAnalysis{
234
- FunctionDefs: make(map[string][]string),
235
- FunctionCalls: make(map[string]map[string][]string),
236
- TypeDefs: make(map[string][]string),
237
- TypeCalls: make(map[string]map[string][]string),
238
- VariableDefs: make(map[string][]string),
239
- VariableCalls: make(map[string]map[string][]string),
240
- }
241
- }
242
-
243
- // collectZeroValueTypeNames records named struct types whose zero value emits a
244
- // runtime constructor call.
245
- func collectZeroValueTypeNames(typ types.Type, names map[string]struct{}) {
246
- switch t := typ.(type) {
247
- case *types.Array:
248
- collectZeroValueTypeNames(t.Elem(), names)
249
- case *types.Named:
250
- if _, isStruct := t.Underlying().(*types.Struct); isStruct {
251
- names[t.Obj().Name()] = struct{}{}
252
- return
253
- }
254
- collectZeroValueTypeNames(t.Underlying(), names)
255
- case *types.Alias:
256
- collectZeroValueTypeNames(t.Underlying(), names)
257
- }
258
- }
259
-
260
- // addTypeRefsFromZeroValue adds same-package type imports needed when code
261
- // generation synthesizes a zero value without an explicit AST type reference.
262
- func addTypeRefsFromZeroValue(analysis *PackageAnalysis, currentFileName string, typ types.Type, refs map[string][]string) {
263
- typeNames := make(map[string]struct{})
264
- collectZeroValueTypeNames(typ, typeNames)
265
- if len(typeNames) == 0 {
266
- return
267
- }
268
-
269
- currentFileTypes := analysis.TypeDefs[currentFileName]
270
- for typeName := range typeNames {
271
- if slices.Contains(currentFileTypes, typeName) {
272
- continue
273
- }
274
-
275
- for sourceFile, types := range analysis.TypeDefs {
276
- if sourceFile == currentFileName || !slices.Contains(types, typeName) {
277
- continue
278
- }
279
-
280
- if refs[sourceFile] == nil {
281
- refs[sourceFile] = []string{}
282
- }
283
- if !slices.Contains(refs[sourceFile], typeName) {
284
- refs[sourceFile] = append(refs[sourceFile], typeName)
285
- }
286
- }
287
- }
288
- }
289
-
290
- // ensureNodeData ensures that NodeData exists for a given node and returns it
291
- func (a *Analysis) ensureNodeData(node ast.Node) *NodeInfo {
292
- if node == nil {
293
- return nil
294
- }
295
- if a.NodeData[node] == nil {
296
- a.NodeData[node] = &NodeInfo{}
297
- }
298
- return a.NodeData[node]
299
- }
300
-
301
- // ensureFunctionData ensures that FunctionData exists for a given object and returns it
302
- func (a *Analysis) ensureFunctionData(obj types.Object) *FunctionInfo {
303
- if obj == nil {
304
- return nil
305
- }
306
- if a.FunctionData[obj] == nil {
307
- a.FunctionData[obj] = &FunctionInfo{}
308
- }
309
- return a.FunctionData[obj]
310
- }
311
-
312
- // GetFunctionInfoFromContext returns FunctionInfo based on the enclosing function context
313
- func (a *Analysis) GetFunctionInfoFromContext(nodeInfo *NodeInfo, pkg *packages.Package) *FunctionInfo {
314
- if nodeInfo == nil {
315
- return nil
316
- }
317
- if nodeInfo.EnclosingFuncDecl != nil {
318
- if obj := pkg.TypesInfo.ObjectOf(nodeInfo.EnclosingFuncDecl.Name); obj != nil {
319
- return a.FunctionData[obj]
320
- }
321
- }
322
- if nodeInfo.EnclosingFuncLit != nil {
323
- return a.FuncLitData[nodeInfo.EnclosingFuncLit]
324
- }
325
- return nil
326
- }
327
-
328
- // NeedsDefer returns whether the given node needs defer handling.
329
- func (a *Analysis) NeedsDefer(node ast.Node) bool {
330
- if node == nil {
331
- return false
332
- }
333
- nodeInfo := a.NodeData[node]
334
- if nodeInfo == nil {
335
- return false
336
- }
337
- return nodeInfo.NeedsDefer
338
- }
339
-
340
- // IsInAsyncFunction returns whether the given node is inside an async function.
341
- func (a *Analysis) IsInAsyncFunction(node ast.Node) bool {
342
- if node == nil {
343
- return false
344
- }
345
- nodeInfo := a.NodeData[node]
346
- if nodeInfo == nil {
347
- return false
348
- }
349
- return nodeInfo.InAsyncContext
350
- }
351
-
352
- // IsAsyncFunc returns whether the given object represents an async function.
353
- func (a *Analysis) IsAsyncFunc(obj types.Object) bool {
354
- if obj == nil {
355
- return false
356
- }
357
-
358
- // Use MethodAsyncStatus for all async status lookups
359
- funcObj, ok := obj.(*types.Func)
360
- if !ok {
361
- return false
362
- }
363
-
364
- // Create MethodKey for lookup
365
- methodKey := MethodKey{
366
- PackagePath: funcObj.Pkg().Path(),
367
- ReceiverType: "", // Functions have no receiver, methods are handled separately
368
- MethodName: funcObj.Name(),
369
- }
370
-
371
- // Check if it's a method with receiver
372
- if sig, ok := funcObj.Type().(*types.Signature); ok && sig.Recv() != nil {
373
- // For methods, get the receiver type name using same format as analysis
374
- recv := sig.Recv()
375
- recvType := recv.Type()
376
- // Handle pointer receivers
377
- if ptr, isPtr := recvType.(*types.Pointer); isPtr {
378
- recvType = ptr.Elem()
379
- }
380
- // Use short type name, not full path (consistent with analysis)
381
- if named, ok := recvType.(*types.Named); ok {
382
- methodKey.ReceiverType = named.Obj().Name()
383
- } else {
384
- methodKey.ReceiverType = recvType.String()
385
- }
386
- }
387
-
388
- if isAsync, exists := a.MethodAsyncStatus[methodKey]; exists {
389
- return isAsync
390
- }
391
- return false
392
- }
393
-
394
- // IsAsyncReturningVar returns whether the given variable holds a function that returns async values.
395
- // This is true when the variable is assigned from a higher-order function that receives an async function literal.
396
- func (a *Analysis) IsAsyncReturningVar(obj types.Object) bool {
397
- if obj == nil {
398
- return false
399
- }
400
- return a.AsyncReturningVars[obj]
401
- }
402
-
403
- func (a *Analysis) IsReceiverUsed(obj types.Object) bool {
404
- if obj == nil {
405
- return false
406
- }
407
- funcInfo := a.FunctionData[obj]
408
- if funcInfo == nil {
409
- return false
410
- }
411
- return funcInfo.ReceiverUsed
412
- }
413
-
414
- // IsFuncLitAsync checks if a function literal is async based on our analysis.
415
- func (a *Analysis) IsFuncLitAsync(funcLit *ast.FuncLit) bool {
416
- if funcLit == nil {
417
- return false
418
- }
419
- // Check function literal specific data first - but IsAsync field was removed
420
- // Function literals don't have types.Object, so fall back to node data
421
- nodeInfo := a.NodeData[funcLit]
422
- if nodeInfo == nil {
423
- return false
424
- }
425
- return nodeInfo.InAsyncContext
426
- }
427
-
428
- // NeedsVarRef returns whether the given object needs to be variable referenced.
429
- // This is true when the object's address is taken (e.g., &myVar) in the analyzed code.
430
- // Variables that have their address taken must be wrapped in VarRef to maintain identity.
431
- func (a *Analysis) NeedsVarRef(obj types.Object) bool {
432
- if obj == nil {
433
- return false
434
- }
435
-
436
- usageInfo, exists := a.VariableUsage[obj]
437
- if !exists {
438
- return false
439
- }
440
- // Check if any destination assignment involves taking the address of 'obj'
441
- for _, destInfo := range usageInfo.Destinations {
442
- if destInfo.Type == AddressOfAssignment {
443
- return true
444
- }
445
- }
446
- return false
447
- }
448
-
449
- // NeedsVarRefAccess returns whether accessing the given object requires '.value' access in TypeScript.
450
- // This is more nuanced than NeedsVarRef and considers both direct variable references and
451
- // pointers that may point to variable-referenced values.
452
- //
453
- // Examples:
454
- // - Direct variable reference (NeedsVarRef = true):
455
- // Example: let x = $.varRef(10) => x.value
456
- // - Pointer pointing to a variable-referenced value:
457
- // Example: let p: VarRef<number> | null = x => p!.value
458
- // - Regular pointer (NeedsVarRef = false, but points to variable reference):
459
- // Example: let q = x => q!.value (where x is VarRef)
460
- func (a *Analysis) NeedsVarRefAccess(obj types.Object) bool {
461
- if obj == nil {
462
- return false
463
- }
464
-
465
- // If the variable itself is variable referenced, it needs .value access
466
- if a.NeedsVarRef(obj) {
467
- return true
468
- }
469
-
470
- // For pointer variables, check if they point to a variable-referenced value
471
- if _, ok := obj.Type().(*types.Pointer); ok {
472
- // Check all assignments to this pointer variable
473
- for varObj, info := range a.VariableUsage {
474
- if varObj == obj {
475
- for _, src := range info.Sources {
476
- if src.Type == AddressOfAssignment && src.Object != nil {
477
- // This pointer was assigned &someVar, check if someVar is variable referenced
478
- return a.NeedsVarRef(src.Object)
479
- }
480
- }
481
- }
482
- }
483
- }
484
-
485
- return false
486
- }
487
-
488
- // IsMethodValue returns whether the given SelectorExpr node is a method value that needs binding.
489
- func (a *Analysis) IsMethodValue(node *ast.SelectorExpr) bool {
490
- if node == nil {
491
- return false
492
- }
493
- nodeInfo := a.NodeData[node]
494
- if nodeInfo == nil {
495
- return false
496
- }
497
- return nodeInfo.IsMethodValue
498
- }
499
-
500
- // HasVariableShadowing returns whether the given node has variable shadowing issues
501
- func (a *Analysis) HasVariableShadowing(node ast.Node) bool {
502
- if node == nil {
503
- return false
504
- }
505
- nodeInfo := a.NodeData[node]
506
- if nodeInfo == nil {
507
- return false
508
- }
509
- return nodeInfo.ShadowingInfo != nil
510
- }
511
-
512
- // GetShadowingInfo returns the variable shadowing information for the given node
513
- func (a *Analysis) GetShadowingInfo(node ast.Node) *ShadowingInfo {
514
- if node == nil {
515
- return nil
516
- }
517
- nodeInfo := a.NodeData[node]
518
- if nodeInfo == nil {
519
- return nil
520
- }
521
- return nodeInfo.ShadowingInfo
522
- }
523
-
524
- // analysisVisitor implements ast.Visitor and is used to traverse the AST during analysis.
525
- type analysisVisitor struct {
526
- // analysis stores information gathered during the traversal
527
- analysis *Analysis
528
-
529
- // pkg provides type information and other package details
530
- pkg *packages.Package
531
-
532
- // inAsyncFunction tracks if we're currently inside an async function
533
- inAsyncFunction bool
534
-
535
- // currentFuncName tracks the name of the function we're currently analyzing
536
- currentFuncName string
537
-
538
- // currentReceiver tracks the object of the receiver if inside a method
539
- currentReceiver *types.Var
540
-
541
- // currentFuncObj tracks the object of the function declaration we're currently analyzing
542
- currentFuncObj types.Object
543
-
544
- // currentFuncDecl tracks the *ast.FuncDecl of the function we're currently analyzing.
545
- currentFuncDecl *ast.FuncDecl
546
-
547
- // currentFuncLit tracks the *ast.FuncLit of the function literal we're currently analyzing.
548
- currentFuncLit *ast.FuncLit
549
-
550
- // currentFilePath tracks the file path of the file we're currently analyzing.
551
- // This is used to track which types are referenced in each file.
552
- currentFilePath string
553
- }
554
-
555
- // getOrCreateUsageInfo retrieves or creates the VariableUsageInfo for a given object.
556
- func (v *analysisVisitor) getOrCreateUsageInfo(obj types.Object) *VariableUsageInfo {
557
- if obj == nil {
558
- return nil // Should not happen with valid objects
559
- }
560
- info, exists := v.analysis.VariableUsage[obj]
561
- if !exists {
562
- info = &VariableUsageInfo{}
563
- v.analysis.VariableUsage[obj] = info
564
- }
565
- return info
566
- }
567
-
568
- // Visit implements the ast.Visitor interface.
569
- // It analyzes each node in the AST to gather information needed for code generation.
570
- func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
571
- if node == nil {
572
- return nil
573
- }
574
-
575
- // Initialize and store async state for the current node
576
- nodeInfo := v.analysis.ensureNodeData(node)
577
- nodeInfo.InAsyncContext = v.inAsyncFunction
578
-
579
- switch n := node.(type) {
580
- case *ast.GenDecl:
581
- // Handle general declarations (var, const, type, import)
582
- if n.Tok == token.VAR {
583
- for _, spec := range n.Specs {
584
- if valueSpec, ok := spec.(*ast.ValueSpec); ok {
585
- // Track type references from variable declarations for synthetic import filtering
586
- if valueSpec.Type != nil {
587
- if t := v.pkg.TypesInfo.TypeOf(valueSpec.Type); t != nil {
588
- v.trackTypeReference(t)
589
- }
590
- }
591
- for _, name := range valueSpec.Names {
592
- if obj := v.pkg.TypesInfo.ObjectOf(name); obj != nil {
593
- v.trackTypeReference(obj.Type())
594
- }
595
- }
596
-
597
- // Process each declared variable (LHS)
598
- for i, lhsIdent := range valueSpec.Names {
599
- if lhsIdent.Name == "_" {
600
- continue
601
- }
602
- lhsObj := v.pkg.TypesInfo.ObjectOf(lhsIdent)
603
- if lhsObj == nil {
604
- continue
605
- }
606
- // Check if there's a corresponding initial value (RHS)
607
- if valueSpec.Values != nil && i < len(valueSpec.Values) {
608
- rhsExpr := valueSpec.Values[i]
609
-
610
- // --- Analyze RHS and Update Usage Info (similar to AssignStmt) ---
611
- assignmentType := DirectAssignment
612
- var sourceObj types.Object
613
- shouldTrackUsage := true
614
-
615
- if unaryExpr, ok := rhsExpr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
616
- // Case: var lhs = &rhs_expr
617
- assignmentType = AddressOfAssignment
618
- if rhsIdent, ok := unaryExpr.X.(*ast.Ident); ok {
619
- // Case: var lhs = &rhs_ident (taking address of a variable)
620
- sourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
621
- } else {
622
- // Case: var lhs = &CompositeLit{} (taking address of composite literal)
623
- // No variable tracking needed - this doesn't create VarRef requirements
624
- shouldTrackUsage = false
625
- }
626
- } else if rhsIdent, ok := rhsExpr.(*ast.Ident); ok {
627
- // Case: var lhs = rhs_ident
628
- assignmentType = DirectAssignment
629
- sourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
630
- } else if funcLit, ok := rhsExpr.(*ast.FuncLit); ok {
631
- // Case: var lhs = func(...) { ... }
632
- // Track function literal assignment
633
- v.analysis.FunctionAssignments[lhsObj] = funcLit
634
- }
635
-
636
- // --- Record Usage ---
637
- // Only create usage tracking if we're dealing with variable references
638
- if shouldTrackUsage {
639
- // Ensure usage info exists for LHS only when needed
640
- lhsUsageInfo := v.getOrCreateUsageInfo(lhsObj)
641
-
642
- if sourceObj != nil {
643
- // Record source for LHS
644
- lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
645
- Object: sourceObj,
646
- Type: assignmentType,
647
- })
648
-
649
- // Record destination for RHS source
650
- sourceUsageInfo := v.getOrCreateUsageInfo(sourceObj)
651
- sourceUsageInfo.Destinations = append(sourceUsageInfo.Destinations, AssignmentInfo{
652
- Object: lhsObj,
653
- Type: assignmentType,
654
- })
655
- }
656
- }
657
- }
658
- }
659
- }
660
- }
661
- }
662
- return v
663
-
664
- case *ast.FuncDecl:
665
- return v.visitFuncDecl(n)
666
-
667
- case *ast.FuncLit:
668
- return v.visitFuncLit(n)
669
-
670
- case *ast.BlockStmt:
671
- return v.visitBlockStmt(n)
672
-
673
- case *ast.UnaryExpr:
674
- // We handle address-of (&) within AssignStmt where it's actually used.
675
- return v
676
-
677
- case *ast.CallExpr:
678
- return v.visitCallExpr(n)
679
-
680
- case *ast.SelectorExpr:
681
- return v.visitSelectorExpr(n)
682
-
683
- case *ast.AssignStmt:
684
- return v.visitAssignStmt(n)
685
-
686
- case *ast.ReturnStmt:
687
- return v.visitReturnStmt(n)
688
-
689
- case *ast.DeclStmt:
690
- return v.visitDeclStmt(n)
691
-
692
- case *ast.IfStmt:
693
- return v.visitIfStmt(n)
694
-
695
- case *ast.TypeAssertExpr:
696
- return v.visitTypeAssertExpr(n)
697
-
698
- case *ast.CompositeLit:
699
- // Traverse into composite literal elements to detect &variable expressions
700
- // This is important for cases like: arr := []interface{}{value1, &value2}
701
- // where value2 needs to be marked as NeedsVarRef due to the &value2 usage
702
- return v.visitCompositeLit(n)
703
- }
704
-
705
- // For all other nodes, continue traversal
706
- return v
707
- }
708
-
709
- // visitFuncDecl handles function declaration analysis
710
- func (v *analysisVisitor) visitFuncDecl(n *ast.FuncDecl) ast.Visitor {
711
- // Save original states to restore after visiting
712
- originalInAsync := v.inAsyncFunction
713
- originalFuncObj := v.currentFuncObj
714
- originalFuncDecl := v.currentFuncDecl
715
- originalFuncLit := v.currentFuncLit
716
- originalReceiver := v.currentReceiver
717
-
718
- // Reset for current function
719
- v.currentFuncName = n.Name.Name
720
- v.currentFuncDecl = n
721
- v.currentFuncLit = nil
722
- v.currentReceiver = nil
723
-
724
- nodeInfo := v.analysis.ensureNodeData(n)
725
- // InAsyncContext will be set by the second analysis phase
726
-
727
- // Set current receiver if this is a method
728
- if n.Recv != nil && len(n.Recv.List) > 0 {
729
- // Assuming a single receiver for simplicity for now
730
- if len(n.Recv.List[0].Names) > 0 {
731
- if ident := n.Recv.List[0].Names[0]; ident != nil && ident.Name != "_" {
732
- if def := v.pkg.TypesInfo.Defs[ident]; def != nil {
733
- if vr, ok := def.(*types.Var); ok {
734
- v.currentReceiver = vr
735
- // Add the receiver variable to the VariableUsage map
736
- v.getOrCreateUsageInfo(v.currentReceiver)
737
-
738
- // Check if receiver is used in method body
739
- receiverUsed := false
740
- if n.Body != nil {
741
- receiverUsed = v.containsReceiverUsage(n.Body, vr)
742
- }
743
-
744
- // Update function data with receiver usage info
745
- if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
746
- funcInfo := v.analysis.ensureFunctionData(obj)
747
- funcInfo.ReceiverUsed = receiverUsed
748
- }
749
-
750
- // If receiver is used, mark all identifiers that refer to the receiver variable
751
- if receiverUsed && n.Body != nil {
752
- recvName := ident.Name
753
- ast.Inspect(n.Body, func(nn ast.Node) bool {
754
- id, ok := nn.(*ast.Ident)
755
- if !ok {
756
- return true
757
- }
758
- if obj := v.pkg.TypesInfo.Uses[id]; obj != nil && obj == vr {
759
- ni := v.analysis.ensureNodeData(id)
760
- ni.IdentifierMapping = recvName
761
- }
762
- return true
763
- })
764
- }
765
- }
766
- }
767
- }
768
- }
769
- }
770
-
771
- // Store named return variables (sanitized for TypeScript)
772
- if n.Type != nil && n.Type.Results != nil {
773
- var namedReturns []string
774
- for _, field := range n.Type.Results.List {
775
- for _, name := range field.Names {
776
- namedReturns = append(namedReturns, sanitizeIdentifier(name.Name))
777
- }
778
- }
779
- if len(namedReturns) > 0 {
780
- if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
781
- funcInfo := v.analysis.ensureFunctionData(obj)
782
- funcInfo.NamedReturns = namedReturns
783
- }
784
- }
785
- }
786
-
787
- // Update visitor state for this function
788
- // Note: inAsyncFunction will be determined later by comprehensive analysis phase
789
- v.currentFuncObj = v.pkg.TypesInfo.ObjectOf(n.Name)
790
-
791
- if n.Body != nil {
792
- // Check if the body contains any defer statements
793
- if v.containsDefer(n.Body) {
794
- nodeInfo.NeedsDefer = true
795
- }
796
-
797
- // Visit the body with updated state
798
- ast.Walk(v, n.Body)
799
- }
800
-
801
- // Restore states after visiting
802
- defer func() {
803
- v.currentFuncName = ""
804
- v.inAsyncFunction = originalInAsync
805
- v.currentReceiver = originalReceiver
806
- v.currentFuncObj = originalFuncObj
807
- v.currentFuncDecl = originalFuncDecl
808
- v.currentFuncLit = originalFuncLit
809
- }()
810
- return nil // Stop traversal here, ast.Walk handled the body
811
- }
812
-
813
- // visitFuncLit handles function literal analysis
814
- func (v *analysisVisitor) visitFuncLit(n *ast.FuncLit) ast.Visitor {
815
- // Save original inAsyncFunction state to restore after visiting
816
- originalInAsync := v.inAsyncFunction
817
- originalFuncDecl := v.currentFuncDecl
818
- originalFuncLit := v.currentFuncLit
819
-
820
- // Set current function literal
821
- v.currentFuncDecl = nil
822
- v.currentFuncLit = n
823
-
824
- // Note: Function literal async analysis is handled by comprehensive analysis phase
825
- nodeInfo := v.analysis.ensureNodeData(n)
826
-
827
- // Store named return variables for function literal
828
- if n.Type != nil && n.Type.Results != nil {
829
- var namedReturns []string
830
- for _, field := range n.Type.Results.List {
831
- for _, name := range field.Names {
832
- namedReturns = append(namedReturns, name.Name)
833
- }
834
- }
835
- if len(namedReturns) > 0 {
836
- v.analysis.FuncLitData[n] = &FunctionInfo{
837
- NamedReturns: namedReturns,
838
- // IsAsync will be set by comprehensive analysis
839
- }
840
- }
841
- }
842
-
843
- // Check if the body contains any defer statements
844
- if n.Body != nil && v.containsDefer(n.Body) {
845
- nodeInfo.NeedsDefer = true
846
- }
847
-
848
- // Visit the body with updated state
849
- ast.Walk(v, n.Body)
850
-
851
- // Restore inAsyncFunction state after visiting
852
- v.inAsyncFunction = originalInAsync
853
- v.currentFuncDecl = originalFuncDecl
854
- v.currentFuncLit = originalFuncLit
855
- return nil // Stop traversal here, ast.Walk handled the body
856
- }
857
-
858
- // visitBlockStmt handles block statement analysis
859
- func (v *analysisVisitor) visitBlockStmt(n *ast.BlockStmt) ast.Visitor {
860
- if n == nil || len(n.List) == 0 {
861
- return v
862
- }
863
-
864
- // Initialize NodeData for this block
865
- nodeInfo := v.analysis.ensureNodeData(n)
866
-
867
- // Check for defer statements in this block
868
- if v.containsDefer(n) {
869
- nodeInfo.NeedsDefer = true
870
- }
871
-
872
- return v
873
- }
874
-
875
- // visitCallExpr handles call expression analysis
876
- func (v *analysisVisitor) visitCallExpr(n *ast.CallExpr) ast.Visitor {
877
- // Check for reflect function calls that operate on functions
878
- v.checkReflectUsage(n)
879
-
880
- // Track interface implementations from function call arguments
881
- v.trackInterfaceCallArguments(n)
882
-
883
- // Check for implicit address-taking in method calls with pointer receivers
884
- v.checkImplicitAddressTaking(n)
885
-
886
- // Check for address-of expressions in function arguments
887
- v.checkAddressOfInArguments(n)
888
-
889
- return v
890
- }
891
-
892
- // checkAddressOfInArguments detects when &variable is passed as a function argument.
893
- // Example: json.Unmarshal(data, &person) where person needs to be marked as NeedsVarRef
894
- func (v *analysisVisitor) checkAddressOfInArguments(callExpr *ast.CallExpr) {
895
- for _, arg := range callExpr.Args {
896
- if unaryExpr, ok := arg.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
897
- if ident, ok := unaryExpr.X.(*ast.Ident); ok {
898
- if obj := v.pkg.TypesInfo.ObjectOf(ident); obj != nil {
899
- usageInfo := v.getOrCreateUsageInfo(obj)
900
- usageInfo.Destinations = append(usageInfo.Destinations, AssignmentInfo{
901
- Object: nil,
902
- Type: AddressOfAssignment,
903
- })
904
- }
905
- }
906
- }
907
- }
908
- }
909
-
910
- // checkImplicitAddressTaking detects when a method call with a pointer receiver
911
- // is called on a non-pointer variable, which requires implicit address-taking.
912
- // Example: var s MySlice; s.Add(10) where Add has receiver *MySlice
913
- // This is equivalent to (&s).Add(10), so s needs to be marked as NeedsVarRef
914
- func (v *analysisVisitor) checkImplicitAddressTaking(callExpr *ast.CallExpr) {
915
- // Check if this is a method call (selector expression)
916
- selExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
917
- if !ok {
918
- return
919
- }
920
-
921
- // Get the selection information
922
- selection := v.pkg.TypesInfo.Selections[selExpr]
923
- if selection == nil || selection.Kind() != types.MethodVal {
924
- return
925
- }
926
-
927
- // Get the method object
928
- methodObj := selection.Obj()
929
- if methodObj == nil {
930
- return
931
- }
932
-
933
- // Get the method's signature to check the receiver type
934
- methodFunc, ok := methodObj.(*types.Func)
935
- if !ok {
936
- return
937
- }
938
-
939
- sig := methodFunc.Type().(*types.Signature)
940
- recv := sig.Recv()
941
- if recv == nil {
942
- return
943
- }
944
-
945
- // Check if the method has a pointer receiver
946
- recvType := recv.Type()
947
- _, hasPointerReceiver := recvType.(*types.Pointer)
948
- if !hasPointerReceiver {
949
- return
950
- }
951
-
952
- // Get the type of the receiver expression (the thing before the dot)
953
- exprType := v.pkg.TypesInfo.TypeOf(selExpr.X)
954
- if exprType == nil {
955
- return
956
- }
957
-
958
- // Check if the receiver expression is NOT already a pointer
959
- _, exprIsPointer := exprType.(*types.Pointer)
960
- if exprIsPointer {
961
- // Expression is already a pointer, no implicit address-taking needed
962
- return
963
- }
964
-
965
- // At this point, we have:
966
- // - A method with a pointer receiver
967
- // - Being called on a non-pointer expression
968
- // This means Go will implicitly take the address
969
-
970
- // Check if the receiver expression is an identifier (variable)
971
- ident, ok := selExpr.X.(*ast.Ident)
972
- if !ok {
973
- return
974
- }
975
-
976
- // Get the variable object
977
- obj := v.pkg.TypesInfo.ObjectOf(ident)
978
- if obj == nil {
979
- return
980
- }
981
-
982
- // Mark this variable as needing VarRef (its address is being taken)
983
- usageInfo := v.getOrCreateUsageInfo(obj)
984
- usageInfo.Destinations = append(usageInfo.Destinations, AssignmentInfo{
985
- Object: nil, // No specific destination for method calls
986
- Type: AddressOfAssignment,
987
- })
988
- }
989
-
990
- // visitSelectorExpr handles selector expression analysis
991
- func (v *analysisVisitor) visitSelectorExpr(n *ast.SelectorExpr) ast.Visitor {
992
- // Check if this is a method value (method being used as a value, not called immediately)
993
- if selection := v.pkg.TypesInfo.Selections[n]; selection != nil {
994
- if selection.Kind() == types.MethodVal {
995
- // This is a method value - mark it for binding during code generation
996
- nodeInfo := v.analysis.ensureNodeData(n)
997
- nodeInfo.IsMethodValue = true
998
- }
999
- }
1000
- return v
1001
- }
1002
-
1003
- // visitAssignStmt handles assignment statement analysis
1004
- func (v *analysisVisitor) visitAssignStmt(n *ast.AssignStmt) ast.Visitor {
1005
- // Handle variable assignment tracking and generate shadowing information
1006
- shadowingInfo := v.detectVariableShadowing(n)
1007
-
1008
- // Store shadowing information if needed for code generation
1009
- if shadowingInfo != nil {
1010
- nodeInfo := v.analysis.ensureNodeData(n)
1011
- nodeInfo.ShadowingInfo = shadowingInfo
1012
- }
1013
-
1014
- // Track assignment relationships for pointer analysis
1015
- for i, lhsExpr := range n.Lhs {
1016
- if i < len(n.Rhs) {
1017
- v.analyzeAssignment(lhsExpr, n.Rhs[i])
1018
- }
1019
- }
1020
-
1021
- // Track interface implementations for assignments to interface variables
1022
- v.trackInterfaceAssignments(n)
1023
-
1024
- // Track function assignments (function literals assigned to variables)
1025
- if len(n.Lhs) == 1 && len(n.Rhs) == 1 {
1026
- if lhsIdent, ok := n.Lhs[0].(*ast.Ident); ok {
1027
- if rhsFuncLit, ok := n.Rhs[0].(*ast.FuncLit); ok {
1028
- // Get the object for the LHS variable
1029
- if obj := v.pkg.TypesInfo.ObjectOf(lhsIdent); obj != nil {
1030
- v.analysis.FunctionAssignments[obj] = rhsFuncLit
1031
- }
1032
- }
1033
- }
1034
- }
1035
-
1036
- // NOTE: Async-returning variable tracking (trackAsyncReturningVar) is done in a separate pass
1037
- // after function literals are analyzed for async status. See trackAsyncReturningVarsAllFiles.
1038
-
1039
- return v
1040
- }
1041
-
1042
- // trackAsyncReturningVar tracks variables that are assigned from higher-order function calls
1043
- // where one of the arguments is an async function literal.
1044
- // Pattern: x := higherOrderFunc(asyncFuncLit)
1045
- // This is needed because when sync.OnceValue(asyncFunc) is called, the result is a function
1046
- // that returns a Promise, and callers of x() need to await the result.
1047
- func (v *analysisVisitor) trackAsyncReturningVar(lhs ast.Expr, rhs ast.Expr) {
1048
- // LHS must be an identifier
1049
- lhsIdent, ok := lhs.(*ast.Ident)
1050
- if !ok {
1051
- return
1052
- }
1053
-
1054
- // RHS must be a call expression
1055
- callExpr, ok := rhs.(*ast.CallExpr)
1056
- if !ok {
1057
- return
1058
- }
1059
-
1060
- // The result type of the call must be a function type
1061
- rhsType := v.pkg.TypesInfo.TypeOf(rhs)
1062
- if rhsType == nil {
1063
- return
1064
- }
1065
- _, isFunc := rhsType.Underlying().(*types.Signature)
1066
- if !isFunc {
1067
- return
1068
- }
1069
-
1070
- // Check if any argument is an async function literal
1071
- // Use containsAsyncOperationsComplete to check the function body directly
1072
- // rather than relying on the InAsyncContext flag which may not be set yet
1073
- hasAsyncArg := false
1074
- for _, arg := range callExpr.Args {
1075
- if funcLit, ok := arg.(*ast.FuncLit); ok {
1076
- if funcLit.Body != nil && v.containsAsyncOperationsComplete(funcLit.Body, v.pkg) {
1077
- hasAsyncArg = true
1078
- break
1079
- }
1080
- }
1081
- }
1082
-
1083
- if !hasAsyncArg {
1084
- return
1085
- }
1086
-
1087
- // Mark the LHS variable as returning async values
1088
- if obj := v.pkg.TypesInfo.ObjectOf(lhsIdent); obj != nil {
1089
- v.analysis.AsyncReturningVars[obj] = true
1090
- }
1091
- }
1092
-
1093
- // visitReturnStmt handles return statement analysis
1094
- func (v *analysisVisitor) visitReturnStmt(n *ast.ReturnStmt) ast.Visitor {
1095
- nodeInfo := v.analysis.ensureNodeData(n)
1096
-
1097
- // Record the enclosing function/literal for this return statement
1098
- if v.currentFuncDecl != nil {
1099
- nodeInfo.EnclosingFuncDecl = v.currentFuncDecl
1100
- } else if v.currentFuncLit != nil {
1101
- nodeInfo.EnclosingFuncLit = v.currentFuncLit
1102
- }
1103
-
1104
- // Check if it's a bare return
1105
- if len(n.Results) == 0 {
1106
- if v.analysis.GetFunctionInfoFromContext(nodeInfo, v.pkg) != nil {
1107
- nodeInfo.IsBareReturn = true
1108
- }
1109
- }
1110
- return v
1111
- }
1112
-
1113
- // visitDeclStmt handles declaration statement analysis
1114
- func (v *analysisVisitor) visitDeclStmt(n *ast.DeclStmt) ast.Visitor {
1115
- // Handle declarations inside functions (const, var, type declarations within function bodies)
1116
- // These should not have export modifiers in TypeScript
1117
- if genDecl, ok := n.Decl.(*ast.GenDecl); ok {
1118
- // Check if we're inside a function (either FuncDecl or FuncLit)
1119
- isInsideFunction := v.currentFuncDecl != nil || v.currentFuncLit != nil
1120
-
1121
- for _, spec := range genDecl.Specs {
1122
- if isInsideFunction {
1123
- // Mark all specs in this declaration as being inside a function
1124
- nodeInfo := v.analysis.ensureNodeData(spec)
1125
- nodeInfo.IsInsideFunction = true
1126
- }
1127
-
1128
- // Track type references from variable declarations (e.g., var w MyWriter)
1129
- if valueSpec, ok := spec.(*ast.ValueSpec); ok {
1130
- // Track explicit type if present
1131
- if valueSpec.Type != nil {
1132
- if t := v.pkg.TypesInfo.TypeOf(valueSpec.Type); t != nil {
1133
- v.trackTypeReference(t)
1134
- }
1135
- }
1136
- // Also track types inferred from values
1137
- for _, name := range valueSpec.Names {
1138
- if obj := v.pkg.TypesInfo.ObjectOf(name); obj != nil {
1139
- v.trackTypeReference(obj.Type())
1140
- }
1141
- }
1142
- }
1143
- }
1144
- }
1145
- return v
1146
- }
1147
-
1148
- // visitIfStmt handles if statement analysis
1149
- func (v *analysisVisitor) visitIfStmt(n *ast.IfStmt) ast.Visitor {
1150
- // Detect variable shadowing in if statement initializations
1151
- if n.Init != nil {
1152
- if assignStmt, ok := n.Init.(*ast.AssignStmt); ok && assignStmt.Tok == token.DEFINE {
1153
- shadowingInfo := v.detectVariableShadowing(assignStmt)
1154
- if shadowingInfo != nil {
1155
- nodeInfo := v.analysis.ensureNodeData(n)
1156
- nodeInfo.ShadowingInfo = shadowingInfo
1157
- }
1158
- }
1159
- }
1160
- return v
1161
- }
1162
-
1163
- // visitTypeAssertExpr handles type assertion analysis for interface method implementations
1164
- func (v *analysisVisitor) visitTypeAssertExpr(typeAssert *ast.TypeAssertExpr) ast.Visitor {
1165
- // Get the type being asserted to
1166
- assertedType := v.pkg.TypesInfo.TypeOf(typeAssert.Type)
1167
- if assertedType == nil {
1168
- return v
1169
- }
1170
-
1171
- // Track the asserted type for synthetic import filtering
1172
- v.trackTypeReference(assertedType)
1173
-
1174
- // Check if the asserted type is an interface
1175
- interfaceType, isInterface := assertedType.Underlying().(*types.Interface)
1176
- if !isInterface {
1177
- return v
1178
- }
1179
-
1180
- // Get the type of the expression being asserted
1181
- exprType := v.pkg.TypesInfo.TypeOf(typeAssert.X)
1182
- if exprType == nil {
1183
- return v
1184
- }
1185
-
1186
- // Handle pointer types by getting the element type
1187
- if ptrType, isPtr := exprType.(*types.Pointer); isPtr {
1188
- exprType = ptrType.Elem()
1189
- }
1190
-
1191
- // Check if the expression type is a named struct type
1192
- namedType, isNamed := exprType.(*types.Named)
1193
- if !isNamed {
1194
- return v
1195
- }
1196
-
1197
- // For each method in the interface, check if the struct implements it
1198
- for interfaceMethod := range interfaceType.ExplicitMethods() {
1199
-
1200
- // Find the corresponding method in the struct type
1201
- structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
1202
- if structMethod != nil {
1203
- // Track this interface implementation
1204
- v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod)
1205
- }
1206
- }
1207
- return v
1208
- }
1209
-
1210
- // trackTypeReference records that a named type is referenced in the current file.
1211
- // This is used to filter synthetic imports to only include packages actually needed.
1212
- func (v *analysisVisitor) trackTypeReference(t types.Type) {
1213
- if t == nil || v.currentFilePath == "" {
1214
- return
1215
- }
1216
- // Unwrap pointers
1217
- if ptr, ok := t.(*types.Pointer); ok {
1218
- t = ptr.Elem()
1219
- }
1220
- // Track named types per file
1221
- if named, ok := t.(*types.Named); ok {
1222
- if v.analysis.ReferencedTypesPerFile[v.currentFilePath] == nil {
1223
- v.analysis.ReferencedTypesPerFile[v.currentFilePath] = make(map[*types.Named]bool)
1224
- }
1225
- v.analysis.ReferencedTypesPerFile[v.currentFilePath][named] = true
1226
- }
1227
- }
1228
-
1229
- // visitCompositeLit analyzes composite literals for address-of expressions
1230
- // This is important for detecting cases like: arr := []interface{}{value1, &value2}
1231
- // where value2 needs to be marked as NeedsVarRef due to the &value2 usage
1232
- func (v *analysisVisitor) visitCompositeLit(compLit *ast.CompositeLit) ast.Visitor {
1233
- // Track the type of this composite literal for synthetic import filtering
1234
- if compLit.Type != nil {
1235
- if t := v.pkg.TypesInfo.TypeOf(compLit.Type); t != nil {
1236
- v.trackTypeReference(t)
1237
- }
1238
- }
1239
-
1240
- // Analyze each element of the composite literal
1241
- for _, elt := range compLit.Elts {
1242
- // Handle both direct elements and key-value pairs
1243
- var expr ast.Expr
1244
- if kv, ok := elt.(*ast.KeyValueExpr); ok {
1245
- // For key-value pairs, analyze the value expression
1246
- expr = kv.Value
1247
- } else {
1248
- // For direct elements, analyze the element expression
1249
- expr = elt
1250
- }
1251
-
1252
- // Check if this element is an address-of expression
1253
- if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
1254
- // Found &something in the composite literal
1255
- if ident, ok := unaryExpr.X.(*ast.Ident); ok {
1256
- // Found &variable - mark the variable as needing VarRef
1257
- if obj := v.pkg.TypesInfo.ObjectOf(ident); obj != nil {
1258
- // Record that this variable has its address taken
1259
- usageInfo := v.getOrCreateUsageInfo(obj)
1260
- usageInfo.Destinations = append(usageInfo.Destinations, AssignmentInfo{
1261
- Object: nil, // No specific destination object for composite literals
1262
- Type: AddressOfAssignment,
1263
- })
1264
- }
1265
- }
1266
- }
1267
- }
1268
- return v
1269
- }
1270
-
1271
- // containsAsyncOperations checks if a node contains any async operations like channel operations.
1272
-
1273
- // containsDefer checks if a block contains any defer statements.
1274
- func (v *analysisVisitor) containsDefer(block *ast.BlockStmt) bool {
1275
- hasDefer := false
1276
-
1277
- ast.Inspect(block, func(n ast.Node) bool {
1278
- if n == nil {
1279
- return true
1280
- }
1281
- if _, ok := n.(*ast.DeferStmt); ok {
1282
- hasDefer = true
1283
- return false
1284
- }
1285
- return true
1286
- })
1287
-
1288
- return hasDefer
1289
- }
1290
-
1291
- // containsReceiverUsage checks if a method body contains any references to the receiver variable.
1292
- func (v *analysisVisitor) containsReceiverUsage(node ast.Node, receiver *types.Var) bool {
1293
- if receiver == nil {
1294
- return false
1295
- }
1296
-
1297
- var hasReceiverUsage bool
1298
-
1299
- ast.Inspect(node, func(n ast.Node) bool {
1300
- if n == nil {
1301
- return true
1302
- }
1303
-
1304
- switch expr := n.(type) {
1305
- case *ast.Ident:
1306
- // Check if this identifier refers to the receiver variable
1307
- if obj := v.pkg.TypesInfo.Uses[expr]; obj != nil && obj == receiver {
1308
- hasReceiverUsage = true
1309
- return false
1310
- }
1311
- case *ast.SelectorExpr:
1312
- // Check if selector expression uses the receiver (e.g., m.Field, m.Method())
1313
- if ident, ok := expr.X.(*ast.Ident); ok {
1314
- if obj := v.pkg.TypesInfo.Uses[ident]; obj != nil && obj == receiver {
1315
- hasReceiverUsage = true
1316
- return false
1317
- }
1318
- }
1319
- }
1320
-
1321
- return true
1322
- })
1323
-
1324
- return hasReceiverUsage
1325
- }
1326
-
1327
- // AnalyzePackageFiles analyzes all Go source files in a package and populates the Analysis struct
1328
- // with information that will be used during code generation to properly handle pointers,
1329
- // variables that need varRefing, receiver usage, etc. This replaces the old file-by-file analysis.
1330
- func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages.Package) *Analysis {
1331
- analysis := NewAnalysis(allPackages)
1332
-
1333
- // Load package metadata for async function detection
1334
- analysis.LoadPackageMetadata()
1335
-
1336
- // Process imports from all files in the package
1337
- for _, file := range pkg.Syntax {
1338
- // Create comment map for each file and store it (we'll merge them if needed)
1339
- cmap := ast.NewCommentMap(pkg.Fset, file, file.Comments)
1340
- if len(analysis.Cmap) == 0 {
1341
- analysis.Cmap = cmap
1342
- } else {
1343
- // Merge comment maps from multiple files
1344
- for node, comments := range cmap {
1345
- analysis.Cmap[node] = append(analysis.Cmap[node], comments...)
1346
- }
1347
- }
1348
-
1349
- // Process imports from this file
1350
- for _, imp := range file.Imports {
1351
- path := ""
1352
- if imp.Path != nil {
1353
- path = imp.Path.Value
1354
- // Remove quotes from the import path string
1355
- path = path[1 : len(path)-1]
1356
- }
1357
-
1358
- // Store the import in the analysis
1359
- if path != "" {
1360
- name := ""
1361
- if imp.Name != nil {
1362
- name = imp.Name.Name
1363
- }
1364
-
1365
- fileImp := &fileImport{
1366
- importPath: path,
1367
- importVars: make(map[string]struct{}),
1368
- }
1369
-
1370
- // Use the import name or the actual package name as the key
1371
- var key string
1372
- if name != "" {
1373
- // Explicit alias provided
1374
- key = name
1375
- } else {
1376
- // No explicit alias, use the actual package name from type information
1377
- // This handles cases where package name differs from the last path segment
1378
- if actualName, err := getActualPackageName(path, pkg.Imports); err == nil {
1379
- key = actualName
1380
- } else {
1381
- // Fallback to last segment of path if package not found in type information
1382
- pts := strings.Split(path, "/")
1383
- key = pts[len(pts)-1]
1384
- }
1385
- }
1386
-
1387
- analysis.Imports[key] = fileImp
1388
- }
1389
- }
1390
- }
1391
-
1392
- // Create visitor for the entire package
1393
- visitor := &analysisVisitor{
1394
- analysis: analysis,
1395
- pkg: pkg,
1396
- }
1397
-
1398
- // First pass: analyze all declarations and statements across all files
1399
- for i, file := range pkg.Syntax {
1400
- // Set the current file path for per-file type tracking
1401
- if i < len(pkg.CompiledGoFiles) {
1402
- visitor.currentFilePath = pkg.CompiledGoFiles[i]
1403
- }
1404
- ast.Walk(visitor, file)
1405
- }
1406
-
1407
- // Post-processing: Find all CallExpr nodes and unmark their Fun SelectorExpr as method values
1408
- // This distinguishes between method calls (obj.Method()) and method values (obj.Method)
1409
- for _, file := range pkg.Syntax {
1410
- ast.Inspect(file, func(n ast.Node) bool {
1411
- if callExpr, ok := n.(*ast.CallExpr); ok {
1412
- if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
1413
- // This SelectorExpr is the function being called, so it's NOT a method value
1414
- if nodeInfo := analysis.NodeData[selExpr]; nodeInfo != nil {
1415
- nodeInfo.IsMethodValue = false
1416
- }
1417
- }
1418
- }
1419
- return true
1420
- })
1421
- }
1422
-
1423
- // Second pass: analyze interface implementations first
1424
- interfaceVisitor := &interfaceImplementationVisitor{
1425
- analysis: analysis,
1426
- pkg: pkg,
1427
- }
1428
- for _, file := range pkg.Syntax {
1429
- ast.Walk(interfaceVisitor, file)
1430
- }
1431
-
1432
- // Third pass: comprehensive async analysis for all methods
1433
- // Interface implementation async status is now updated on-demand in IsInterfaceMethodAsync
1434
- visitor.analyzeAllMethodsAsync()
1435
-
1436
- // Fourth pass: collect imports needed by promoted methods from embedded structs
1437
- analysis.addImportsForPromotedMethods(pkg)
1438
-
1439
- return analysis
1440
- }
1441
-
1442
- // addImportsForPromotedMethods scans struct types that are actually referenced in each file
1443
- // and adds imports for any packages referenced by the promoted methods' parameter/return types.
1444
- // This generates per-file synthetic imports to avoid adding unused imports.
1445
- func (a *Analysis) addImportsForPromotedMethods(pkg *packages.Package) {
1446
- // Process each file's referenced types separately
1447
- for filePath, referencedTypes := range a.ReferencedTypesPerFile {
1448
- // Collect package imports needed for this specific file
1449
- packagesToAdd := make(map[string]*types.Package)
1450
-
1451
- // Only process types that are actually referenced in this file
1452
- // and are defined in the current package
1453
- for namedType := range referencedTypes {
1454
- // Skip types from other packages - we only need to process types defined in this package
1455
- if namedType.Obj().Pkg() != pkg.Types {
1456
- continue
1457
- }
1458
-
1459
- // Check if it's a struct
1460
- structType, ok := namedType.Underlying().(*types.Struct)
1461
- if !ok {
1462
- continue
1463
- }
1464
-
1465
- // Look for embedded fields
1466
- for field := range structType.Fields() {
1467
- if !field.Embedded() {
1468
- continue
1469
- }
1470
-
1471
- // Get the type of the embedded field
1472
- embeddedType := field.Type()
1473
-
1474
- // Handle pointer to embedded type
1475
- if ptr, ok := embeddedType.(*types.Pointer); ok {
1476
- embeddedType = ptr.Elem()
1477
- }
1478
-
1479
- // Use method set to get all promoted methods including pointer receiver methods
1480
- // This matches Go's behavior where embedding T promotes both T and *T methods
1481
- methodSetType := embeddedType
1482
- if _, isPtr := embeddedType.(*types.Pointer); !isPtr {
1483
- if _, isInterface := embeddedType.Underlying().(*types.Interface); !isInterface {
1484
- methodSetType = types.NewPointer(embeddedType)
1485
- }
1486
- }
1487
- embeddedMethodSet := types.NewMethodSet(methodSetType)
1488
-
1489
- // Scan all methods in the method set
1490
- for selection := range embeddedMethodSet.Methods() {
1491
- method := selection.Obj()
1492
- sig, ok := method.Type().(*types.Signature)
1493
- if !ok {
1494
- continue
1495
- }
1496
-
1497
- // Scan parameters
1498
- if sig.Params() != nil {
1499
- for param := range sig.Params().Variables() {
1500
- a.collectPackageFromType(param.Type(), pkg.Types, packagesToAdd)
1501
- }
1502
- }
1503
-
1504
- // Scan results
1505
- if sig.Results() != nil {
1506
- for result := range sig.Results().Variables() {
1507
- a.collectPackageFromType(result.Type(), pkg.Types, packagesToAdd)
1508
- }
1509
- }
1510
- }
1511
- }
1512
- }
1513
-
1514
- // Store the synthetic imports for this file
1515
- if len(packagesToAdd) > 0 {
1516
- fileImports := make(map[string]*fileImport)
1517
- for pkgName, pkgObj := range packagesToAdd {
1518
- tsImportPath := translateGoImportPathToTypescriptModulePath(pkgObj.Path())
1519
- fileImports[pkgName] = &fileImport{
1520
- importPath: tsImportPath,
1521
- importVars: make(map[string]struct{}),
1522
- }
1523
- }
1524
- a.SyntheticImportsPerFile[filePath] = fileImports
1525
- }
1526
- }
1527
- }
1528
-
1529
- // collectPackageFromType recursively collects packages referenced by a type.
1530
- func (a *Analysis) collectPackageFromType(t types.Type, currentPkg *types.Package, packagesToAdd map[string]*types.Package) {
1531
- switch typ := t.(type) {
1532
- case *types.Named:
1533
- pkg := typ.Obj().Pkg()
1534
- if pkg != nil && pkg != currentPkg {
1535
- packagesToAdd[pkg.Name()] = pkg
1536
- }
1537
- // Check type arguments for generics
1538
- if typ.TypeArgs() != nil {
1539
- for t := range typ.TypeArgs().Types() {
1540
- a.collectPackageFromType(t, currentPkg, packagesToAdd)
1541
- }
1542
- }
1543
- case *types.Interface:
1544
- // For interfaces, we need to check embedded interfaces and method signatures
1545
- for etyp := range typ.EmbeddedTypes() {
1546
- a.collectPackageFromType(etyp, currentPkg, packagesToAdd)
1547
- }
1548
- for method := range typ.ExplicitMethods() {
1549
- a.collectPackageFromType(method.Type(), currentPkg, packagesToAdd)
1550
- }
1551
- case *types.Pointer:
1552
- a.collectPackageFromType(typ.Elem(), currentPkg, packagesToAdd)
1553
- case *types.Slice:
1554
- a.collectPackageFromType(typ.Elem(), currentPkg, packagesToAdd)
1555
- case *types.Array:
1556
- a.collectPackageFromType(typ.Elem(), currentPkg, packagesToAdd)
1557
- case *types.Map:
1558
- a.collectPackageFromType(typ.Key(), currentPkg, packagesToAdd)
1559
- a.collectPackageFromType(typ.Elem(), currentPkg, packagesToAdd)
1560
- case *types.Chan:
1561
- a.collectPackageFromType(typ.Elem(), currentPkg, packagesToAdd)
1562
- case *types.Signature:
1563
- // Collect from parameters
1564
- if typ.Params() != nil {
1565
- for v := range typ.Params().Variables() {
1566
- a.collectPackageFromType(v.Type(), currentPkg, packagesToAdd)
1567
- }
1568
- }
1569
- // Collect from results
1570
- if typ.Results() != nil {
1571
- for v := range typ.Results().Variables() {
1572
- a.collectPackageFromType(v.Type(), currentPkg, packagesToAdd)
1573
- }
1574
- }
1575
- }
1576
- }
1577
-
1578
- // AnalyzePackageImports performs package-level analysis to collect function definitions
1579
- // and calls across all files in the package for auto-import generation
1580
- func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
1581
- analysis := NewPackageAnalysis()
1582
-
1583
- // First pass: collect all function definitions per file
1584
- for i, syntax := range pkg.Syntax {
1585
- fileName := pkg.CompiledGoFiles[i]
1586
- baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
1587
-
1588
- var functions []string
1589
- var typeNames []string
1590
- var variables []string
1591
- for _, decl := range syntax.Decls {
1592
- if funcDecl, ok := decl.(*ast.FuncDecl); ok {
1593
- // Collect top-level functions (not methods)
1594
- if funcDecl.Recv == nil {
1595
- functions = append(functions, funcDecl.Name.Name)
1596
- } else {
1597
- // Check if this is a method on a wrapper type (named basic type)
1598
- // If so, it will be compiled as TypeName_MethodName function
1599
- if len(funcDecl.Recv.List) > 0 {
1600
- recvType := funcDecl.Recv.List[0].Type
1601
- // Handle pointer receiver (*Type)
1602
- if starExpr, ok := recvType.(*ast.StarExpr); ok {
1603
- recvType = starExpr.X
1604
- }
1605
- if recvIdent, ok := recvType.(*ast.Ident); ok {
1606
- // Check if this receiver type is a wrapper type
1607
- if obj := pkg.TypesInfo.Uses[recvIdent]; obj != nil {
1608
- if typeName, ok := obj.(*types.TypeName); ok {
1609
- if namedType, ok := typeName.Type().(*types.Named); ok {
1610
- if _, ok := namedType.Underlying().(*types.Basic); ok {
1611
- // This is a method on a wrapper type
1612
- funcName := recvIdent.Name + "_" + funcDecl.Name.Name
1613
- functions = append(functions, funcName)
1614
- }
1615
- }
1616
- }
1617
- }
1618
- }
1619
- }
1620
- }
1621
- }
1622
- if genDecl, ok := decl.(*ast.GenDecl); ok {
1623
- // Collect type declarations
1624
- for _, spec := range genDecl.Specs {
1625
- if typeSpec, ok := spec.(*ast.TypeSpec); ok {
1626
- typeNames = append(typeNames, typeSpec.Name.Name)
1627
- }
1628
- // Collect variable/constant declarations
1629
- if valueSpec, ok := spec.(*ast.ValueSpec); ok {
1630
- for _, name := range valueSpec.Names {
1631
- variables = append(variables, name.Name)
1632
- }
1633
- }
1634
- }
1635
- }
1636
- }
1637
-
1638
- if len(functions) > 0 {
1639
- analysis.FunctionDefs[baseFileName] = functions
1640
- }
1641
- if len(typeNames) > 0 {
1642
- analysis.TypeDefs[baseFileName] = typeNames
1643
- }
1644
- if len(variables) > 0 {
1645
- analysis.VariableDefs[baseFileName] = variables
1646
- }
1647
- }
1648
-
1649
- // Second pass: analyze function calls and determine which need imports
1650
- for i, syntax := range pkg.Syntax {
1651
- fileName := pkg.CompiledGoFiles[i]
1652
- baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
1653
-
1654
- // Find all function calls in this file
1655
- callsFromOtherFiles := make(map[string][]string)
1656
-
1657
- ast.Inspect(syntax, func(n ast.Node) bool {
1658
- if callExpr, ok := n.(*ast.CallExpr); ok {
1659
- if ident, ok := callExpr.Fun.(*ast.Ident); ok {
1660
- funcName := ident.Name
1661
-
1662
- // Check if this function is defined in the current file
1663
- currentFileFuncs := analysis.FunctionDefs[baseFileName]
1664
- isDefinedInCurrentFile := slices.Contains(currentFileFuncs, funcName)
1665
-
1666
- // If not defined in current file, find which file defines it
1667
- if !isDefinedInCurrentFile {
1668
- for sourceFile, funcs := range analysis.FunctionDefs {
1669
- if sourceFile == baseFileName {
1670
- continue // Skip current file
1671
- }
1672
- if slices.Contains(funcs, funcName) {
1673
- // Found the function in another file
1674
- if callsFromOtherFiles[sourceFile] == nil {
1675
- callsFromOtherFiles[sourceFile] = []string{}
1676
- }
1677
- // Check if already added to avoid duplicates
1678
- found := slices.Contains(callsFromOtherFiles[sourceFile], funcName)
1679
- if !found {
1680
- callsFromOtherFiles[sourceFile] = append(callsFromOtherFiles[sourceFile], funcName)
1681
- }
1682
- }
1683
- }
1684
- }
1685
- }
1686
- }
1687
- return true
1688
- })
1689
-
1690
- if len(callsFromOtherFiles) > 0 {
1691
- analysis.FunctionCalls[baseFileName] = callsFromOtherFiles
1692
- }
1693
- }
1694
-
1695
- // Third pass: analyze type references and determine which need imports
1696
- for i, syntax := range pkg.Syntax {
1697
- fileName := pkg.CompiledGoFiles[i]
1698
- baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
1699
-
1700
- // Find all type references in this file
1701
- typeRefsFromOtherFiles := make(map[string][]string)
1702
-
1703
- ast.Inspect(syntax, func(n ast.Node) bool {
1704
- // Look for type references in struct fields, function parameters, etc.
1705
- if ident, ok := n.(*ast.Ident); ok {
1706
- // Check if this identifier refers to a type
1707
- if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
1708
- if _, ok := obj.(*types.TypeName); ok {
1709
- typeName := ident.Name
1710
-
1711
- // Check if this type is defined in the current file
1712
- currentFileTypes := analysis.TypeDefs[baseFileName]
1713
- isDefinedInCurrentFile := slices.Contains(currentFileTypes, typeName)
1714
-
1715
- // If not defined in current file, find which file defines it
1716
- if !isDefinedInCurrentFile {
1717
- for sourceFile, types := range analysis.TypeDefs {
1718
- if sourceFile == baseFileName {
1719
- continue // Skip current file
1720
- }
1721
- if slices.Contains(types, typeName) {
1722
- // Found the type in another file
1723
- if typeRefsFromOtherFiles[sourceFile] == nil {
1724
- typeRefsFromOtherFiles[sourceFile] = []string{}
1725
- }
1726
- // Check if already added to avoid duplicates
1727
- found := slices.Contains(typeRefsFromOtherFiles[sourceFile], typeName)
1728
- if !found {
1729
- typeRefsFromOtherFiles[sourceFile] = append(typeRefsFromOtherFiles[sourceFile], typeName)
1730
- }
1731
- }
1732
- }
1733
- }
1734
- }
1735
- }
1736
- }
1737
-
1738
- if callExpr, ok := n.(*ast.CallExpr); ok {
1739
- if funIdent, ok := callExpr.Fun.(*ast.Ident); ok && funIdent.Name == "make" && len(callExpr.Args) > 0 {
1740
- if typ := pkg.TypesInfo.TypeOf(callExpr.Args[0]); typ != nil {
1741
- if chanType, ok := typ.Underlying().(*types.Chan); ok {
1742
- addTypeRefsFromZeroValue(analysis, baseFileName, chanType.Elem(), typeRefsFromOtherFiles)
1743
- }
1744
- }
1745
- }
1746
- }
1747
-
1748
- if indexExpr, ok := n.(*ast.IndexExpr); ok {
1749
- if tv, ok := pkg.TypesInfo.Types[indexExpr.X]; ok {
1750
- if mapType, ok := tv.Type.Underlying().(*types.Map); ok {
1751
- addTypeRefsFromZeroValue(analysis, baseFileName, mapType.Elem(), typeRefsFromOtherFiles)
1752
- return true
1753
- }
1754
-
1755
- if typeParam, ok := tv.Type.(*types.TypeParam); ok {
1756
- constraint := typeParam.Constraint()
1757
- if constraint == nil {
1758
- return true
1759
- }
1760
- if iface, ok := constraint.Underlying().(*types.Interface); ok && hasMapConstraint(iface) {
1761
- if mapValueType := getMapValueTypeFromConstraint(iface); mapValueType != nil {
1762
- addTypeRefsFromZeroValue(analysis, baseFileName, mapValueType, typeRefsFromOtherFiles)
1763
- }
1764
- }
1765
- }
1766
- }
1767
- }
1768
- return true
1769
- })
1770
-
1771
- if len(typeRefsFromOtherFiles) > 0 {
1772
- analysis.TypeCalls[baseFileName] = typeRefsFromOtherFiles
1773
- }
1774
- }
1775
-
1776
- // Fourth pass: analyze variable references and determine which need imports
1777
- for i, syntax := range pkg.Syntax {
1778
- fileName := pkg.CompiledGoFiles[i]
1779
- baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
1780
-
1781
- // Find all variable references in this file
1782
- varRefsFromOtherFiles := make(map[string][]string)
1783
-
1784
- ast.Inspect(syntax, func(n ast.Node) bool {
1785
- // Look for identifier references
1786
- if ident, ok := n.(*ast.Ident); ok {
1787
- // Check if this identifier refers to a package-level variable
1788
- if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
1789
- if varObj, ok := obj.(*types.Var); ok {
1790
- // Only track package-level variables (not function parameters or local vars)
1791
- if varObj.Parent() == pkg.Types.Scope() {
1792
- varName := ident.Name
1793
-
1794
- // Check if this variable is defined in the current file
1795
- currentFileVars := analysis.VariableDefs[baseFileName]
1796
- isDefinedInCurrentFile := slices.Contains(currentFileVars, varName)
1797
-
1798
- // If not defined in current file, find which file defines it
1799
- if !isDefinedInCurrentFile {
1800
- for sourceFile, vars := range analysis.VariableDefs {
1801
- if sourceFile == baseFileName {
1802
- continue // Skip current file
1803
- }
1804
- if slices.Contains(vars, varName) {
1805
- // Found the variable in another file
1806
- if varRefsFromOtherFiles[sourceFile] == nil {
1807
- varRefsFromOtherFiles[sourceFile] = []string{}
1808
- }
1809
- // Check if already added to avoid duplicates
1810
- found := slices.Contains(varRefsFromOtherFiles[sourceFile], varName)
1811
- if !found {
1812
- varRefsFromOtherFiles[sourceFile] = append(varRefsFromOtherFiles[sourceFile], varName)
1813
- }
1814
- }
1815
- }
1816
- }
1817
- }
1818
- }
1819
- // Also check for constants
1820
- if constObj, ok := obj.(*types.Const); ok {
1821
- // Only track package-level constants
1822
- if constObj.Parent() == pkg.Types.Scope() {
1823
- constName := ident.Name
1824
-
1825
- // Check if this constant is defined in the current file
1826
- currentFileVars := analysis.VariableDefs[baseFileName]
1827
- isDefinedInCurrentFile := slices.Contains(currentFileVars, constName)
1828
-
1829
- // If not defined in current file, find which file defines it
1830
- if !isDefinedInCurrentFile {
1831
- for sourceFile, vars := range analysis.VariableDefs {
1832
- if sourceFile == baseFileName {
1833
- continue // Skip current file
1834
- }
1835
- if slices.Contains(vars, constName) {
1836
- // Found the constant in another file
1837
- if varRefsFromOtherFiles[sourceFile] == nil {
1838
- varRefsFromOtherFiles[sourceFile] = []string{}
1839
- }
1840
- // Check if already added to avoid duplicates
1841
- found := slices.Contains(varRefsFromOtherFiles[sourceFile], constName)
1842
- if !found {
1843
- varRefsFromOtherFiles[sourceFile] = append(varRefsFromOtherFiles[sourceFile], constName)
1844
- }
1845
- }
1846
- }
1847
- }
1848
- }
1849
- }
1850
- }
1851
- }
1852
- return true
1853
- })
1854
-
1855
- if len(varRefsFromOtherFiles) > 0 {
1856
- analysis.VariableCalls[baseFileName] = varRefsFromOtherFiles
1857
- }
1858
- }
1859
-
1860
- // Fifth pass: analyze method calls on wrapper types (named basic types with methods)
1861
- // These generate TypeName_MethodName function calls that need to be imported
1862
- for i, syntax := range pkg.Syntax {
1863
- fileName := pkg.CompiledGoFiles[i]
1864
- baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
1865
-
1866
- // Find all method calls on wrapper types in this file
1867
- ast.Inspect(syntax, func(n ast.Node) bool {
1868
- callExpr, ok := n.(*ast.CallExpr)
1869
- if !ok {
1870
- return true
1871
- }
1872
-
1873
- // Check if this is a method call (selector expression)
1874
- selectorExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
1875
- if !ok {
1876
- return true
1877
- }
1878
-
1879
- // Get the type of the receiver
1880
- receiverType := pkg.TypesInfo.TypeOf(selectorExpr.X)
1881
- if receiverType == nil {
1882
- return true
1883
- }
1884
-
1885
- // Check if this is a wrapper type (named type with basic underlying type and methods)
1886
- namedType, ok := receiverType.(*types.Named)
1887
- if !ok {
1888
- return true
1889
- }
1890
-
1891
- // Check if it has a basic underlying type
1892
- if _, ok := namedType.Underlying().(*types.Basic); !ok {
1893
- return true
1894
- }
1895
-
1896
- // Check if this type is defined in the same package
1897
- obj := namedType.Obj()
1898
- if obj == nil || obj.Pkg() == nil || obj.Pkg() != pkg.Types {
1899
- return true // Not from this package
1900
- }
1901
-
1902
- // Check if this type has the method being called
1903
- methodName := selectorExpr.Sel.Name
1904
- found := false
1905
- for method := range namedType.Methods() {
1906
- if method.Name() == methodName {
1907
- found = true
1908
- break
1909
- }
1910
- }
1911
- if !found {
1912
- return true
1913
- }
1914
-
1915
- // Generate the function name: TypeName_MethodName
1916
- funcName := obj.Name() + "_" + methodName
1917
-
1918
- // Find which file defines this function
1919
- for sourceFile, funcs := range analysis.FunctionDefs {
1920
- if sourceFile == baseFileName {
1921
- continue // Skip current file
1922
- }
1923
- if slices.Contains(funcs, funcName) {
1924
- // Found the function in another file
1925
- if analysis.FunctionCalls[baseFileName] == nil {
1926
- analysis.FunctionCalls[baseFileName] = make(map[string][]string)
1927
- }
1928
- if analysis.FunctionCalls[baseFileName][sourceFile] == nil {
1929
- analysis.FunctionCalls[baseFileName][sourceFile] = []string{}
1930
- }
1931
- // Check if already added to avoid duplicates
1932
- if !slices.Contains(analysis.FunctionCalls[baseFileName][sourceFile], funcName) {
1933
- analysis.FunctionCalls[baseFileName][sourceFile] = append(analysis.FunctionCalls[baseFileName][sourceFile], funcName)
1934
- }
1935
- }
1936
- }
1937
-
1938
- return true
1939
- })
1940
- }
1941
-
1942
- return analysis
1943
- }
1944
-
1945
- // LoadPackageMetadata loads metadata from gs packages using embedded JSON files
1946
- func (a *Analysis) LoadPackageMetadata() {
1947
- // Discover all packages in the embedded gs/ directory
1948
- packagePaths := a.discoverEmbeddedGsPackages()
1949
-
1950
- for _, pkgPath := range packagePaths {
1951
- metaFilePath := filepath.Join("gs", pkgPath, "meta.json")
1952
-
1953
- // Try to read the meta.json file from embedded filesystem
1954
- // We need access to the embedded FS, which should be imported from the parent package
1955
- if metadata := a.loadGsMetadata(metaFilePath); metadata != nil {
1956
- // Store async method information
1957
- for methodKey, isAsync := range metadata.AsyncMethods {
1958
- // Convert method key to our internal key format
1959
- parts := strings.Split(methodKey, ".")
1960
- var typeName, methodName string
1961
-
1962
- if len(parts) == 2 {
1963
- // "Type.Method" format for methods
1964
- typeName = parts[0]
1965
- methodName = parts[1]
1966
- } else if len(parts) == 1 {
1967
- // "Function" format for package-level functions
1968
- typeName = "" // Empty type name for package-level functions
1969
- methodName = parts[0]
1970
- } else {
1971
- // Skip invalid formats
1972
- continue
1973
- }
1974
-
1975
- // Use MethodKey instead of PackageMetadataKey for consistency
1976
- key := MethodKey{
1977
- PackagePath: pkgPath,
1978
- ReceiverType: typeName,
1979
- MethodName: methodName,
1980
- }
1981
-
1982
- // Store the async value directly in MethodAsyncStatus
1983
- a.MethodAsyncStatus[key] = isAsync
1984
- }
1985
- }
1986
- }
1987
- }
1988
-
1989
- // discoverEmbeddedGsPackages finds all packages in the embedded gs/ directory
1990
- func (a *Analysis) discoverEmbeddedGsPackages() []string {
1991
- var packageList []string
1992
-
1993
- // Read the gs/ directory from the embedded filesystem
1994
- entries, err := goscript.GsOverrides.ReadDir("gs")
1995
- if err != nil {
1996
- // If we can't read the gs/ directory, return empty list
1997
- return packageList
1998
- }
1999
-
2000
- // Iterate through all entries in gs/
2001
- for _, entry := range entries {
2002
- if entry.IsDir() {
2003
- packageList = append(packageList, entry.Name())
2004
- }
2005
- }
2006
-
2007
- return packageList
2008
- }
2009
-
2010
- // loadGsMetadata loads metadata from a meta.json file in the embedded filesystem
2011
- func (a *Analysis) loadGsMetadata(metaFilePath string) *GsMetadata {
2012
- // Read the meta.json file from the embedded filesystem
2013
- content, err := goscript.GsOverrides.ReadFile(metaFilePath)
2014
- if err != nil {
2015
- return nil // No metadata file found
2016
- }
2017
-
2018
- var metadata GsMetadata
2019
- if err := json.Unmarshal(content, &metadata); err != nil {
2020
- return nil // Invalid JSON
2021
- }
2022
-
2023
- return &metadata
2024
- }
2025
-
2026
- // isHandwrittenPackage checks if a package path corresponds to a handwritten package in gs/
2027
- func (a *Analysis) isHandwrittenPackage(pkgPath string) bool {
2028
- // Check if the package exists in the embedded gs/ directory
2029
- metaFilePath := filepath.Join("gs", pkgPath, "meta.json")
2030
- _, err := goscript.GsOverrides.ReadFile(metaFilePath)
2031
- return err == nil
2032
- }
2033
-
2034
- // IsMethodAsync checks if a method call is async based on package metadata
2035
- func (a *Analysis) IsMethodAsync(pkgPath, typeName, methodName string) bool {
2036
- // First, check pre-computed method async status
2037
- methodKey := MethodKey{
2038
- PackagePath: pkgPath,
2039
- ReceiverType: typeName,
2040
- MethodName: methodName,
2041
- }
2042
-
2043
- if status, exists := a.MethodAsyncStatus[methodKey]; exists {
2044
- return status
2045
- }
2046
-
2047
- // If no pre-computed status found, external methods default to sync
2048
- // Comprehensive analysis should have already analyzed all packages and loaded metadata
2049
- return false
2050
- }
2051
-
2052
- // NeedsReflectionMetadata returns whether the given function node needs reflection type metadata
2053
- func (a *Analysis) NeedsReflectionMetadata(node ast.Node) bool {
2054
- if node == nil {
2055
- return false
2056
- }
2057
- reflectInfo := a.ReflectedFunctions[node]
2058
- return reflectInfo != nil && reflectInfo.NeedsReflect
2059
- }
2060
-
2061
- // GetFunctionTypeInfo returns the function type information for reflection
2062
- func (a *Analysis) GetFunctionTypeInfo(node ast.Node) *ReflectedFunctionInfo {
2063
- if node == nil {
2064
- return nil
2065
- }
2066
- return a.ReflectedFunctions[node]
2067
- }
2068
-
2069
- // MarkFunctionForReflection marks a function node as needing reflection support
2070
- func (a *Analysis) MarkFunctionForReflection(node ast.Node, funcType *types.Signature) {
2071
- if node == nil || funcType == nil {
2072
- return
2073
- }
2074
- a.ReflectedFunctions[node] = &ReflectedFunctionInfo{
2075
- FuncType: funcType,
2076
- NeedsReflect: true,
2077
- }
2078
- }
2079
-
2080
- // checkReflectUsage checks for reflect function calls that operate on functions
2081
- func (v *analysisVisitor) checkReflectUsage(callExpr *ast.CallExpr) {
2082
- // Check if this is a reflect package function call
2083
- if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
2084
- // Check if the selector is from reflect package
2085
- if ident, ok := selExpr.X.(*ast.Ident); ok {
2086
- // Check if this is a reflect package call (reflect.TypeOf, reflect.ValueOf, etc.)
2087
- if obj := v.pkg.TypesInfo.Uses[ident]; obj != nil {
2088
- if pkgName, ok := obj.(*types.PkgName); ok && pkgName.Imported().Path() == "reflect" {
2089
- methodName := selExpr.Sel.Name
2090
-
2091
- // Check for reflect.TypeOf and reflect.ValueOf calls
2092
- if methodName == "TypeOf" || methodName == "ValueOf" {
2093
- // Check if any argument is a function
2094
- for _, arg := range callExpr.Args {
2095
- v.checkReflectArgument(arg)
2096
- }
2097
- }
2098
- }
2099
- }
2100
- }
2101
- }
2102
- }
2103
-
2104
- // checkReflectArgument checks if an argument to a reflect function is a function that needs metadata
2105
- func (v *analysisVisitor) checkReflectArgument(arg ast.Expr) {
2106
- // Check if the argument is an identifier (variable)
2107
- if ident, ok := arg.(*ast.Ident); ok {
2108
- // Get the object this identifier refers to
2109
- if obj := v.pkg.TypesInfo.Uses[ident]; obj != nil {
2110
- // Check if this object has a function type
2111
- if funcType, ok := obj.Type().(*types.Signature); ok {
2112
- // This is a function variable being passed to reflect
2113
- // We need to find the original function definition/assignment
2114
- v.markFunctionVariable(ident, funcType)
2115
- }
2116
- }
2117
- } else if funcLit, ok := arg.(*ast.FuncLit); ok {
2118
- // This is a function literal being passed directly to reflect
2119
- if funcType := v.pkg.TypesInfo.Types[funcLit].Type.(*types.Signature); funcType != nil {
2120
- v.analysis.MarkFunctionForReflection(funcLit, funcType)
2121
- }
2122
- }
2123
- }
2124
-
2125
- // markFunctionVariable finds the function definition/assignment for a variable and marks it for reflection
2126
- func (v *analysisVisitor) markFunctionVariable(ident *ast.Ident, funcType *types.Signature) {
2127
- // Get the object for this identifier
2128
- obj := v.pkg.TypesInfo.Uses[ident]
2129
- if obj == nil {
2130
- return
2131
- }
2132
-
2133
- // Check if we have a tracked function assignment for this variable
2134
- if funcNode := v.analysis.FunctionAssignments[obj]; funcNode != nil {
2135
- // Mark the function node for reflection
2136
- v.analysis.MarkFunctionForReflection(funcNode, funcType)
2137
- }
2138
- }
2139
-
2140
- // detectVariableShadowing detects variable shadowing in any := assignment
2141
- func (v *analysisVisitor) detectVariableShadowing(assignStmt *ast.AssignStmt) *ShadowingInfo {
2142
- shadowingInfo := &ShadowingInfo{
2143
- ShadowedVariables: make(map[string]types.Object),
2144
- TempVariables: make(map[string]string),
2145
- TypeShadowedVars: make(map[string]string),
2146
- }
2147
-
2148
- hasShadowing := false
2149
-
2150
- // First, collect all LHS variable names that are being declared
2151
- lhsVarNames := make(map[string]*ast.Ident)
2152
- for _, lhsExpr := range assignStmt.Lhs {
2153
- if lhsIdent, ok := lhsExpr.(*ast.Ident); ok && lhsIdent.Name != "_" {
2154
- lhsVarNames[lhsIdent.Name] = lhsIdent
2155
- }
2156
- }
2157
-
2158
- // Next, check all RHS expressions for usage of variables that are also being declared on LHS
2159
- for _, rhsExpr := range assignStmt.Rhs {
2160
- v.findVariableUsageInExpr(rhsExpr, lhsVarNames, shadowingInfo, &hasShadowing)
2161
- }
2162
-
2163
- // Check for type shadowing: variable name matches a type name used in its initialization
2164
- // e.g., field := field{...} where the variable 'field' shadows the type 'field'
2165
- if assignStmt.Tok == token.DEFINE {
2166
- for i, lhsExpr := range assignStmt.Lhs {
2167
- if i < len(assignStmt.Rhs) {
2168
- if lhsIdent, ok := lhsExpr.(*ast.Ident); ok && lhsIdent.Name != "_" {
2169
- if typeName := v.findTypeShadowing(lhsIdent.Name, assignStmt.Rhs[i]); typeName != "" {
2170
- shadowingInfo.TypeShadowedVars[lhsIdent.Name] = lhsIdent.Name + "_"
2171
- hasShadowing = true
2172
- }
2173
- }
2174
- }
2175
- }
2176
- }
2177
-
2178
- if hasShadowing {
2179
- return shadowingInfo
2180
- }
2181
- return nil
2182
- }
2183
-
2184
- // findTypeShadowing checks if the given variable name matches a type name used in the RHS expression.
2185
- // Returns the type name if shadowing is detected, empty string otherwise.
2186
- func (v *analysisVisitor) findTypeShadowing(varName string, rhsExpr ast.Expr) string {
2187
- // Handle address-of expressions: field := &field{...}
2188
- if unary, ok := rhsExpr.(*ast.UnaryExpr); ok && unary.Op == token.AND {
2189
- rhsExpr = unary.X
2190
- }
2191
-
2192
- // Check if RHS is a composite literal with a type name matching varName
2193
- compLit, ok := rhsExpr.(*ast.CompositeLit)
2194
- if !ok {
2195
- return ""
2196
- }
2197
-
2198
- // Get the type name from the composite literal
2199
- var typeName string
2200
- switch t := compLit.Type.(type) {
2201
- case *ast.Ident:
2202
- typeName = t.Name
2203
- case *ast.SelectorExpr:
2204
- // pkg.Type - just use the type name part
2205
- typeName = t.Sel.Name
2206
- default:
2207
- return ""
2208
- }
2209
-
2210
- // Check if variable name matches type name
2211
- if typeName == varName {
2212
- return typeName
2213
- }
2214
- return ""
2215
- }
2216
-
2217
- // findVariableUsageInExpr recursively searches for variable usage in an expression
2218
- func (v *analysisVisitor) findVariableUsageInExpr(expr ast.Expr, lhsVarNames map[string]*ast.Ident, shadowingInfo *ShadowingInfo, hasShadowing *bool) {
2219
- if expr == nil {
2220
- return
2221
- }
2222
-
2223
- switch e := expr.(type) {
2224
- case *ast.Ident:
2225
- // Check if this identifier is being shadowed
2226
- if lhsIdent, exists := lhsVarNames[e.Name]; exists {
2227
- // This variable is being used on RHS but also declared on LHS - this is shadowing!
2228
-
2229
- // Get the outer scope object for this variable
2230
- if outerObj := v.pkg.TypesInfo.Uses[e]; outerObj != nil {
2231
- // Make sure this isn't the same object as the LHS (which would mean no shadowing)
2232
- if lhsObj := v.pkg.TypesInfo.Defs[lhsIdent]; lhsObj != outerObj {
2233
- shadowingInfo.ShadowedVariables[e.Name] = outerObj
2234
- shadowingInfo.TempVariables[e.Name] = "_temp_" + e.Name
2235
- *hasShadowing = true
2236
- }
2237
- }
2238
- }
2239
-
2240
- case *ast.CallExpr:
2241
- // Check function arguments
2242
- for _, arg := range e.Args {
2243
- v.findVariableUsageInExpr(arg, lhsVarNames, shadowingInfo, hasShadowing)
2244
- }
2245
- // Check function expression itself
2246
- v.findVariableUsageInExpr(e.Fun, lhsVarNames, shadowingInfo, hasShadowing)
2247
-
2248
- case *ast.SelectorExpr:
2249
- // Check the base expression (e.g., x in x.Method())
2250
- v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
2251
-
2252
- case *ast.IndexExpr:
2253
- // Check both the expression and index (e.g., arr[i])
2254
- v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
2255
- v.findVariableUsageInExpr(e.Index, lhsVarNames, shadowingInfo, hasShadowing)
2256
-
2257
- case *ast.SliceExpr:
2258
- // Check the expression and slice bounds
2259
- v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
2260
- if e.Low != nil {
2261
- v.findVariableUsageInExpr(e.Low, lhsVarNames, shadowingInfo, hasShadowing)
2262
- }
2263
- if e.High != nil {
2264
- v.findVariableUsageInExpr(e.High, lhsVarNames, shadowingInfo, hasShadowing)
2265
- }
2266
- if e.Max != nil {
2267
- v.findVariableUsageInExpr(e.Max, lhsVarNames, shadowingInfo, hasShadowing)
2268
- }
2269
-
2270
- case *ast.UnaryExpr:
2271
- // Check the operand (e.g., &x, -x, !x)
2272
- v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
2273
-
2274
- case *ast.BinaryExpr:
2275
- // Check both operands (e.g., x + y)
2276
- v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
2277
- v.findVariableUsageInExpr(e.Y, lhsVarNames, shadowingInfo, hasShadowing)
2278
-
2279
- case *ast.ParenExpr:
2280
- // Check the parenthesized expression
2281
- v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
2282
-
2283
- case *ast.TypeAssertExpr:
2284
- // Check the expression being type-asserted
2285
- v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
2286
-
2287
- case *ast.StarExpr:
2288
- // Check the expression being dereferenced
2289
- v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
2290
-
2291
- // Add more expression types as needed
2292
- default:
2293
- // For other expression types, we might need to add specific handling
2294
- // For now, we'll ignore them as they're less common in shadowing scenarios
2295
- }
2296
- }
2297
-
2298
- // trackInterfaceImplementation records that a struct type implements an interface method
2299
- func (a *Analysis) trackInterfaceImplementation(interfaceType *types.Interface, structType *types.Named, method *types.Func) {
2300
- key := InterfaceMethodKey{
2301
- InterfaceType: interfaceType.String(),
2302
- MethodName: method.Name(),
2303
- }
2304
-
2305
- implementation := ImplementationInfo{
2306
- StructType: structType,
2307
- Method: method,
2308
- }
2309
-
2310
- a.InterfaceImplementations[key] = append(a.InterfaceImplementations[key], implementation)
2311
- }
2312
-
2313
- // IsInterfaceMethodAsync determines if an interface method should be async based on its implementations
2314
- func (a *Analysis) IsInterfaceMethodAsync(interfaceType *types.Interface, methodName string) bool {
2315
- key := InterfaceMethodKey{
2316
- InterfaceType: interfaceType.String(),
2317
- MethodName: methodName,
2318
- }
2319
-
2320
- // Find all implementations of this interface method
2321
- implementations, exists := a.InterfaceImplementations[key]
2322
- if !exists {
2323
- return false
2324
- }
2325
-
2326
- // If ANY implementation is async, the interface method is async
2327
- for _, impl := range implementations {
2328
- methodKey := MethodKey{
2329
- PackagePath: impl.StructType.Obj().Pkg().Path(),
2330
- ReceiverType: impl.StructType.Obj().Name(),
2331
- MethodName: impl.Method.Name(),
2332
- }
2333
-
2334
- if isAsync, exists := a.MethodAsyncStatus[methodKey]; exists && isAsync {
2335
- return true
2336
- }
2337
- }
2338
-
2339
- return false
2340
- }
2341
-
2342
- // MustBeAsyncDueToInterface checks if a struct method must be async due to interface constraints
2343
- func (a *Analysis) MustBeAsyncDueToInterface(structType *types.Named, methodName string) bool {
2344
- // Find all interfaces that this struct implements
2345
- for key, implementations := range a.InterfaceImplementations {
2346
- if key.MethodName != methodName {
2347
- continue
2348
- }
2349
-
2350
- // Check if this struct is among the implementations
2351
- for _, impl := range implementations {
2352
- if impl.StructType == structType {
2353
- // This struct implements this interface method
2354
- // Check if the interface method is marked as async
2355
- interfaceType := a.findInterfaceTypeByString(key.InterfaceType)
2356
- if interfaceType != nil && a.IsInterfaceMethodAsync(interfaceType, methodName) {
2357
- return true
2358
- }
2359
- }
2360
- }
2361
- }
2362
-
2363
- return false
2364
- }
2365
-
2366
- // findInterfaceTypeByString finds an interface type by its string representation
2367
- // This is a helper method for MustBeAsyncDueToInterface
2368
- func (a *Analysis) findInterfaceTypeByString(interfaceString string) *types.Interface {
2369
- // This is a simplified implementation - in practice, we might need to store
2370
- // the actual interface types in our tracking data structure
2371
- for _, pkg := range a.AllPackages {
2372
- for _, syntax := range pkg.Syntax {
2373
- for _, decl := range syntax.Decls {
2374
- if genDecl, ok := decl.(*ast.GenDecl); ok {
2375
- for _, spec := range genDecl.Specs {
2376
- if typeSpec, ok := spec.(*ast.TypeSpec); ok {
2377
- if interfaceType, ok := typeSpec.Type.(*ast.InterfaceType); ok {
2378
- if goType := pkg.TypesInfo.TypeOf(interfaceType); goType != nil {
2379
- if iface, ok := goType.(*types.Interface); ok {
2380
- if iface.String() == interfaceString {
2381
- return iface
2382
- }
2383
- }
2384
- }
2385
- }
2386
- }
2387
- }
2388
- }
2389
- }
2390
- }
2391
- }
2392
- return nil
2393
- }
2394
-
2395
- // GetReceiverMapping returns the receiver variable mapping for a function declaration
2396
- func (a *Analysis) GetReceiverMapping(funcDecl *ast.FuncDecl) string {
2397
- if funcDecl.Recv != nil && len(funcDecl.Recv.List) > 0 {
2398
- for _, field := range funcDecl.Recv.List {
2399
- for _, name := range field.Names {
2400
- if name != nil && name.Name != "_" {
2401
- return "receiver"
2402
- }
2403
- }
2404
- }
2405
- }
2406
- return ""
2407
- }
2408
-
2409
- // GetIdentifierMapping returns the replacement name for an identifier
2410
- func (a *Analysis) GetIdentifierMapping(ident *ast.Ident) string {
2411
- if ident == nil {
2412
- return ""
2413
- }
2414
-
2415
- // Check if this identifier has a mapping in NodeData
2416
- if nodeInfo := a.NodeData[ident]; nodeInfo != nil {
2417
- return nodeInfo.IdentifierMapping
2418
- }
2419
-
2420
- return ""
2421
- }
2422
-
2423
- // findStructMethod finds a method with the given name on a named type
2424
- func (v *analysisVisitor) findStructMethod(namedType *types.Named, methodName string) *types.Func {
2425
- // Check methods directly on the type
2426
- for method := range namedType.Methods() {
2427
- if method.Name() == methodName {
2428
- return method
2429
- }
2430
- }
2431
- return nil
2432
- }
2433
-
2434
- // analyzeAssignment analyzes a single assignment for pointer analysis
2435
- func (v *analysisVisitor) analyzeAssignment(lhsExpr, rhsExpr ast.Expr) {
2436
- // Determine RHS assignment type and source object
2437
- rhsAssignmentType := DirectAssignment
2438
- var rhsSourceObj types.Object
2439
-
2440
- if unaryExpr, ok := rhsExpr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
2441
- // RHS is &some_expr
2442
- rhsAssignmentType = AddressOfAssignment
2443
- if rhsIdent, ok := unaryExpr.X.(*ast.Ident); ok {
2444
- rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
2445
- }
2446
- } else if rhsIdent, ok := rhsExpr.(*ast.Ident); ok {
2447
- // RHS is variable
2448
- rhsAssignmentType = DirectAssignment
2449
- rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
2450
- }
2451
-
2452
- // Determine LHS object
2453
- var lhsTrackedObj types.Object
2454
-
2455
- if lhsIdent, ok := lhsExpr.(*ast.Ident); ok {
2456
- if lhsIdent.Name != "_" {
2457
- lhsTrackedObj = v.pkg.TypesInfo.ObjectOf(lhsIdent)
2458
- }
2459
- } else if selExpr, ok := lhsExpr.(*ast.SelectorExpr); ok {
2460
- if selection := v.pkg.TypesInfo.Selections[selExpr]; selection != nil {
2461
- lhsTrackedObj = selection.Obj()
2462
- }
2463
- }
2464
-
2465
- // Record usage information
2466
- if _, isVar := lhsTrackedObj.(*types.Var); isVar {
2467
- lhsUsageInfo := v.getOrCreateUsageInfo(lhsTrackedObj)
2468
- if rhsSourceObj != nil {
2469
- lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
2470
- Object: rhsSourceObj,
2471
- Type: rhsAssignmentType,
2472
- })
2473
- } else if rhsAssignmentType == AddressOfAssignment {
2474
- lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
2475
- Object: nil,
2476
- Type: rhsAssignmentType,
2477
- })
2478
- }
2479
- }
2480
-
2481
- if rhsSourceObj != nil {
2482
- sourceUsageInfo := v.getOrCreateUsageInfo(rhsSourceObj)
2483
- sourceUsageInfo.Destinations = append(sourceUsageInfo.Destinations, AssignmentInfo{
2484
- Object: lhsTrackedObj,
2485
- Type: rhsAssignmentType,
2486
- })
2487
- }
2488
- }
2489
-
2490
- // trackInterfaceAssignments tracks interface implementations in assignment statements
2491
- func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt) {
2492
- // For each assignment, check if we're assigning a struct to an interface variable
2493
- for i, lhsExpr := range assignStmt.Lhs {
2494
- if i >= len(assignStmt.Rhs) {
2495
- continue
2496
- }
2497
- rhsExpr := assignStmt.Rhs[i]
2498
-
2499
- // Get the type of the LHS (destination)
2500
- lhsType := v.pkg.TypesInfo.TypeOf(lhsExpr)
2501
- if lhsType == nil {
2502
- continue
2503
- }
2504
-
2505
- // Check if LHS is an interface type
2506
- interfaceType, isInterface := lhsType.Underlying().(*types.Interface)
2507
- if !isInterface {
2508
- continue
2509
- }
2510
-
2511
- // Get the type of the RHS (source)
2512
- rhsType := v.pkg.TypesInfo.TypeOf(rhsExpr)
2513
- if rhsType == nil {
2514
- continue
2515
- }
2516
-
2517
- // Handle pointer types
2518
- if ptrType, isPtr := rhsType.(*types.Pointer); isPtr {
2519
- rhsType = ptrType.Elem()
2520
- }
2521
-
2522
- // Check if RHS is a named struct type
2523
- namedType, isNamed := rhsType.(*types.Named)
2524
- if !isNamed {
2525
- continue
2526
- }
2527
-
2528
- // Track implementations for all interface methods
2529
- for interfaceMethod := range interfaceType.ExplicitMethods() {
2530
-
2531
- structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
2532
- if structMethod != nil {
2533
- v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod)
2534
- }
2535
- }
2536
- }
2537
- }
2538
-
2539
- // trackInterfaceCallArguments analyzes function call arguments to detect interface implementations
2540
- func (v *analysisVisitor) trackInterfaceCallArguments(callExpr *ast.CallExpr) {
2541
- // Get the function signature to determine parameter types
2542
- var funcType *types.Signature
2543
-
2544
- if callFunType := v.pkg.TypesInfo.TypeOf(callExpr.Fun); callFunType != nil {
2545
- if sig, ok := callFunType.(*types.Signature); ok {
2546
- funcType = sig
2547
- }
2548
- }
2549
-
2550
- if funcType == nil {
2551
- return
2552
- }
2553
-
2554
- // Check each argument against its corresponding parameter
2555
- params := funcType.Params()
2556
- for i, arg := range callExpr.Args {
2557
- if i >= params.Len() {
2558
- break // More arguments than parameters (variadic case)
2559
- }
2560
-
2561
- paramType := params.At(i).Type()
2562
-
2563
- // Check if the parameter is an interface type
2564
- interfaceType, isInterface := paramType.Underlying().(*types.Interface)
2565
- if !isInterface {
2566
- continue
2567
- }
2568
-
2569
- // Get the type of the argument
2570
- argType := v.pkg.TypesInfo.TypeOf(arg)
2571
- if argType == nil {
2572
- continue
2573
- }
2574
-
2575
- // Handle pointer types
2576
- if ptrType, isPtr := argType.(*types.Pointer); isPtr {
2577
- argType = ptrType.Elem()
2578
- }
2579
-
2580
- // Check if argument is a named struct type
2581
- namedType, isNamed := argType.(*types.Named)
2582
- if !isNamed {
2583
- continue
2584
- }
2585
-
2586
- // Track implementations for all interface methods
2587
- for interfaceMethod := range interfaceType.ExplicitMethods() {
2588
-
2589
- structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
2590
- if structMethod != nil {
2591
- v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod)
2592
- }
2593
- }
2594
- }
2595
- }
2596
-
2597
- // IsNamedBasicType returns whether the given type should be implemented as a type alias with standalone functions
2598
- // This applies to named types with basic underlying types (like uint32, string, etc.) that have methods
2599
- // It excludes struct types, which should remain as classes
2600
- func (a *Analysis) IsNamedBasicType(t types.Type) bool {
2601
- if t == nil {
2602
- return false
2603
- }
2604
-
2605
- // Check if we already have this result cached
2606
- if result, exists := a.NamedBasicTypes[t]; exists {
2607
- return result
2608
- }
2609
-
2610
- var originalType types.Type = t
2611
- var foundMethods bool
2612
-
2613
- // Traverse the type chain to find any type with methods
2614
- for {
2615
- switch typed := t.(type) {
2616
- case *types.Named:
2617
- // Built-in types cannot be named basic types
2618
- if typed.Obj().Pkg() == nil {
2619
- return false
2620
- }
2621
-
2622
- // Check if this named type has methods
2623
- if typed.NumMethods() > 0 {
2624
- foundMethods = true
2625
- }
2626
-
2627
- // Check underlying type
2628
- underlying := typed.Underlying()
2629
- switch underlying.(type) {
2630
- case *types.Struct, *types.Interface:
2631
- return false
2632
- }
2633
- t = underlying
2634
-
2635
- case *types.Alias:
2636
- // Built-in types cannot be named basic types
2637
- if typed.Obj().Pkg() == nil {
2638
- return false
2639
- }
2640
- t = typed.Underlying()
2641
-
2642
- default:
2643
- // We've reached a non-named, non-alias type
2644
- // Check if it's a supported type with methods
2645
- switch t.(type) {
2646
- case *types.Basic, *types.Slice, *types.Array, *types.Map:
2647
- if foundMethods {
2648
- a.NamedBasicTypes[originalType] = true
2649
- return true
2650
- }
2651
- return false
2652
- default:
2653
- return false
2654
- }
2655
- }
2656
- }
2657
- }
2658
-
2659
- // interfaceImplementationVisitor performs a second pass to analyze interface implementations
2660
- type interfaceImplementationVisitor struct {
2661
- analysis *Analysis
2662
- pkg *packages.Package
2663
- }
2664
-
2665
- func (v *interfaceImplementationVisitor) Visit(node ast.Node) ast.Visitor {
2666
- switch n := node.(type) {
2667
- case *ast.GenDecl:
2668
- // Look for interface type specifications
2669
- for _, spec := range n.Specs {
2670
- if typeSpec, ok := spec.(*ast.TypeSpec); ok {
2671
- if interfaceType, ok := typeSpec.Type.(*ast.InterfaceType); ok {
2672
- // This is an interface declaration, find all potential implementations
2673
- v.findInterfaceImplementations(interfaceType)
2674
- }
2675
- }
2676
- }
2677
- }
2678
- return v
2679
- }
2680
-
2681
- // findInterfaceImplementations finds all struct types that implement the given interface
2682
- func (v *interfaceImplementationVisitor) findInterfaceImplementations(interfaceAST *ast.InterfaceType) {
2683
- // Get the interface type from TypesInfo
2684
- interfaceGoType := v.pkg.TypesInfo.TypeOf(interfaceAST)
2685
- if interfaceGoType == nil {
2686
- return
2687
- }
2688
-
2689
- interfaceType, ok := interfaceGoType.(*types.Interface)
2690
- if !ok {
2691
- return
2692
- }
2693
-
2694
- // Look through all packages for potential implementations
2695
- for _, pkg := range v.analysis.AllPackages {
2696
- v.findImplementationsInPackage(interfaceType, pkg)
2697
- }
2698
- }
2699
-
2700
- // findImplementationsInPackage finds implementations of an interface in a specific package
2701
- func (v *interfaceImplementationVisitor) findImplementationsInPackage(interfaceType *types.Interface, pkg *packages.Package) {
2702
- // Get all named types in the package
2703
- scope := pkg.Types.Scope()
2704
- for _, name := range scope.Names() {
2705
- obj := scope.Lookup(name)
2706
- if obj == nil {
2707
- continue
2708
- }
2709
-
2710
- // Check if this is a type name
2711
- typeName, ok := obj.(*types.TypeName)
2712
- if !ok {
2713
- continue
2714
- }
2715
-
2716
- namedType, ok := typeName.Type().(*types.Named)
2717
- if !ok {
2718
- continue
2719
- }
2720
-
2721
- // Check if this type implements the interface
2722
- if types.Implements(namedType, interfaceType) || types.Implements(types.NewPointer(namedType), interfaceType) {
2723
- v.trackImplementation(interfaceType, namedType)
2724
- }
2725
- }
2726
- }
2727
-
2728
- // trackImplementation records that a named type implements an interface
2729
- func (v *interfaceImplementationVisitor) trackImplementation(interfaceType *types.Interface, namedType *types.Named) {
2730
- // For each method in the interface, find the corresponding implementation
2731
- for interfaceMethod := range interfaceType.ExplicitMethods() {
2732
-
2733
- // Find the method in the implementing type
2734
- structMethod := v.findMethodInType(namedType, interfaceMethod.Name())
2735
- if structMethod != nil {
2736
- v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod)
2737
- }
2738
- }
2739
- }
2740
-
2741
- // findMethodInType finds a method with the given name in a named type
2742
- func (v *interfaceImplementationVisitor) findMethodInType(namedType *types.Named, methodName string) *types.Func {
2743
- for method := range namedType.Methods() {
2744
- if method.Name() == methodName {
2745
- return method
2746
- }
2747
- }
2748
- return nil
2749
- }
2750
-
2751
- // analyzeAllMethodsAsync performs comprehensive async analysis on all methods in all packages using topological sort
2752
- func (v *analysisVisitor) analyzeAllMethodsAsync() {
2753
- // Build the method call graph for all packages
2754
- methodCalls := v.buildMethodCallGraph()
2755
-
2756
- // Topologically sort methods by their dependencies
2757
- sorted, cycles := v.topologicalSortMethods(methodCalls)
2758
-
2759
- // Analyze methods in dependency order (dependencies analyzed before dependents)
2760
- for _, methodKey := range sorted {
2761
- v.analyzeMethodAsyncTopological(methodKey, methodCalls[methodKey])
2762
- }
2763
-
2764
- // Analyze methods in cycles after the acyclic portion of the graph is known.
2765
- // This lets recursive methods observe async callees outside the cycle while
2766
- // still converging over recursive edges inside the cycle.
2767
- maxIterations := 10
2768
- for range maxIterations {
2769
- changed := false
2770
-
2771
- for _, methodKey := range cycles {
2772
- pkg := v.analysis.AllPackages[methodKey.PackagePath]
2773
- if pkg == nil && methodKey.PackagePath == v.pkg.Types.Path() {
2774
- pkg = v.pkg
2775
- }
2776
-
2777
- isAsync := false
2778
- if pkg != nil {
2779
- var funcDecl *ast.FuncDecl
2780
- if methodKey.ReceiverType == "" {
2781
- funcDecl = v.findFunctionDecl(methodKey.MethodName, pkg)
2782
- } else {
2783
- funcDecl = v.findMethodDecl(methodKey.ReceiverType, methodKey.MethodName, pkg)
2784
- }
2785
-
2786
- if funcDecl != nil && funcDecl.Body != nil {
2787
- isAsync = v.containsAsyncOperationsComplete(funcDecl.Body, pkg)
2788
- if !isAsync {
2789
- for _, callee := range methodCalls[methodKey] {
2790
- if calleeAsync, exists := v.analysis.MethodAsyncStatus[callee]; exists && calleeAsync {
2791
- isAsync = true
2792
- break
2793
- }
2794
- }
2795
- }
2796
- }
2797
- }
2798
-
2799
- if oldStatus, exists := v.analysis.MethodAsyncStatus[methodKey]; !exists || oldStatus != isAsync {
2800
- changed = true
2801
- }
2802
- v.analysis.MethodAsyncStatus[methodKey] = isAsync
2803
- }
2804
-
2805
- if !changed {
2806
- break
2807
- }
2808
- }
2809
-
2810
- // Track async-returning variables BEFORE analyzing function literals
2811
- // This detects variables assigned from higher-order functions with async function literal args
2812
- // e.g., indirect := sync.OnceValue(asyncFunc)
2813
- // This must happen first so that function literals containing calls to these variables
2814
- // will be correctly identified as async.
2815
- v.trackAsyncReturningVarsAllFiles()
2816
-
2817
- // Finally, analyze function literals in the current package only
2818
- // (external packages' function literals are not accessible)
2819
- // This must run AFTER trackAsyncReturningVarsAllFiles so that function literals
2820
- // containing calls to async-returning variables are correctly marked as async.
2821
- v.analyzeFunctionLiteralsAsync(v.pkg)
2822
- }
2823
-
2824
- // buildMethodCallGraph builds a graph of which methods call which other methods
2825
- func (v *analysisVisitor) analyzeFunctionLiteralsAsync(pkg *packages.Package) {
2826
- for _, file := range pkg.Syntax {
2827
- ast.Inspect(file, func(n ast.Node) bool {
2828
- if funcLit, ok := n.(*ast.FuncLit); ok {
2829
- v.analyzeFunctionLiteralAsync(funcLit, pkg)
2830
- }
2831
- return true
2832
- })
2833
- }
2834
- }
2835
-
2836
- // trackAsyncReturningVarsAllFiles scans all files for assignment statements
2837
- // and marks variables that are assigned from higher-order functions with async function literal args
2838
- func (v *analysisVisitor) trackAsyncReturningVarsAllFiles() {
2839
- for _, file := range v.pkg.Syntax {
2840
- ast.Inspect(file, func(n ast.Node) bool {
2841
- if assignStmt, ok := n.(*ast.AssignStmt); ok {
2842
- if len(assignStmt.Lhs) == 1 && len(assignStmt.Rhs) == 1 {
2843
- v.trackAsyncReturningVar(assignStmt.Lhs[0], assignStmt.Rhs[0])
2844
- }
2845
- }
2846
- return true
2847
- })
2848
- }
2849
- }
2850
-
2851
- // analyzeFunctionLiteralAsync determines if a function literal is async and stores the result
2852
- func (v *analysisVisitor) analyzeFunctionLiteralAsync(funcLit *ast.FuncLit, pkg *packages.Package) {
2853
- // Check if already analyzed
2854
- nodeInfo := v.analysis.NodeData[funcLit]
2855
- if nodeInfo != nil && nodeInfo.InAsyncContext {
2856
- // Already marked as async, skip
2857
- return
2858
- }
2859
-
2860
- // Analyze function literal body for async operations
2861
- isAsync := false
2862
- if funcLit.Body != nil {
2863
- isAsync = v.containsAsyncOperationsComplete(funcLit.Body, pkg)
2864
- }
2865
-
2866
- // Store result in NodeData
2867
- if nodeInfo == nil {
2868
- nodeInfo = v.analysis.ensureNodeData(funcLit)
2869
- }
2870
- nodeInfo.InAsyncContext = isAsync
2871
- }
2872
-
2873
- // buildMethodCallGraph builds a graph of which methods call which other methods
2874
- func (v *analysisVisitor) buildMethodCallGraph() map[MethodKey][]MethodKey {
2875
- methodCalls := make(map[MethodKey][]MethodKey)
2876
-
2877
- // Iterate through all packages
2878
- allPkgs := []*packages.Package{v.pkg}
2879
- for _, pkg := range v.analysis.AllPackages {
2880
- if pkg != v.pkg {
2881
- allPkgs = append(allPkgs, pkg)
2882
- }
2883
- }
2884
-
2885
- for _, pkg := range allPkgs {
2886
- for _, file := range pkg.Syntax {
2887
- for _, decl := range file.Decls {
2888
- if funcDecl, ok := decl.(*ast.FuncDecl); ok {
2889
- methodKey := v.getMethodKey(funcDecl, pkg)
2890
-
2891
- // Initialize the entry for this method
2892
- if _, exists := methodCalls[methodKey]; !exists {
2893
- methodCalls[methodKey] = []MethodKey{}
2894
- }
2895
-
2896
- // Extract method calls from the function body
2897
- if funcDecl.Body != nil {
2898
- callees := v.extractMethodCalls(funcDecl.Body, pkg)
2899
- methodCalls[methodKey] = callees
2900
- }
2901
- }
2902
- }
2903
- }
2904
- }
2905
-
2906
- return methodCalls
2907
- }
2908
-
2909
- // extractMethodCalls extracts all method and function calls from a node
2910
- func (v *analysisVisitor) extractMethodCalls(node ast.Node, pkg *packages.Package) []MethodKey {
2911
- var calls []MethodKey
2912
- seen := make(map[MethodKey]bool)
2913
-
2914
- ast.Inspect(node, func(n ast.Node) bool {
2915
- if n == nil {
2916
- return false
2917
- }
2918
-
2919
- if callExpr, ok := n.(*ast.CallExpr); ok {
2920
- methodKeys := v.extractMethodKeysFromCall(callExpr, pkg)
2921
- for _, methodKey := range methodKeys {
2922
- if !seen[methodKey] {
2923
- seen[methodKey] = true
2924
- calls = append(calls, methodKey)
2925
- }
2926
- }
2927
- }
2928
-
2929
- return true
2930
- })
2931
-
2932
- return calls
2933
- }
2934
-
2935
- // extractMethodKeysFromCall extracts MethodKeys from a call expression
2936
- // Returns multiple keys for interface method calls (one for each implementation)
2937
- func (v *analysisVisitor) extractMethodKeysFromCall(callExpr *ast.CallExpr, pkg *packages.Package) []MethodKey {
2938
- singleKey := v.extractMethodKeyFromCall(callExpr, pkg)
2939
- if singleKey != nil {
2940
- return []MethodKey{*singleKey}
2941
- }
2942
-
2943
- // Check if this is an interface method call
2944
- if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
2945
- if receiverType := pkg.TypesInfo.TypeOf(selExpr.X); receiverType != nil {
2946
- if interfaceType, isInterface := receiverType.Underlying().(*types.Interface); isInterface {
2947
- methodName := selExpr.Sel.Name
2948
- // Find all implementations of this interface method
2949
- key := InterfaceMethodKey{
2950
- InterfaceType: interfaceType.String(),
2951
- MethodName: methodName,
2952
- }
2953
- if implementations, exists := v.analysis.InterfaceImplementations[key]; exists {
2954
- var keys []MethodKey
2955
- for _, impl := range implementations {
2956
- keys = append(keys, MethodKey{
2957
- PackagePath: impl.StructType.Obj().Pkg().Path(),
2958
- ReceiverType: impl.StructType.Obj().Name(),
2959
- MethodName: impl.Method.Name(),
2960
- })
2961
- }
2962
- return keys
2963
- }
2964
- }
2965
- }
2966
- }
2967
-
2968
- return nil
2969
- }
2970
-
2971
- // extractMethodKeyFromCall extracts a MethodKey from a call expression
2972
- func (v *analysisVisitor) extractMethodKeyFromCall(callExpr *ast.CallExpr, pkg *packages.Package) *MethodKey {
2973
- switch fun := callExpr.Fun.(type) {
2974
- case *ast.Ident:
2975
- // Direct function call
2976
- if obj := pkg.TypesInfo.Uses[fun]; obj != nil {
2977
- if funcObj, ok := obj.(*types.Func); ok {
2978
- pkgPath := pkg.Types.Path()
2979
- if funcObj.Pkg() != nil {
2980
- pkgPath = funcObj.Pkg().Path()
2981
- }
2982
- return &MethodKey{
2983
- PackagePath: pkgPath,
2984
- ReceiverType: "",
2985
- MethodName: funcObj.Name(),
2986
- }
2987
- }
2988
- }
2989
-
2990
- case *ast.SelectorExpr:
2991
- // Package-level function call (e.g., time.Sleep)
2992
- if ident, ok := fun.X.(*ast.Ident); ok {
2993
- if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
2994
- if pkgName, isPkg := obj.(*types.PkgName); isPkg {
2995
- return &MethodKey{
2996
- PackagePath: pkgName.Imported().Path(),
2997
- ReceiverType: "",
2998
- MethodName: fun.Sel.Name,
2999
- }
3000
- }
3001
- }
3002
- }
3003
-
3004
- // Check if this is an interface method call - if so, return nil
3005
- // so extractMethodKeysFromCall can expand it to all implementations
3006
- if receiverType := pkg.TypesInfo.TypeOf(fun.X); receiverType != nil {
3007
- if _, isInterface := receiverType.Underlying().(*types.Interface); isInterface {
3008
- // This is an interface method call - return nil to let
3009
- // extractMethodKeysFromCall handle expanding to implementations
3010
- return nil
3011
- }
3012
- }
3013
-
3014
- // Method call on concrete objects
3015
- if selection := pkg.TypesInfo.Selections[fun]; selection != nil {
3016
- if methodObj := selection.Obj(); methodObj != nil {
3017
- receiverType := ""
3018
- methodPkgPath := ""
3019
-
3020
- // Get receiver type
3021
- switch x := fun.X.(type) {
3022
- case *ast.Ident:
3023
- if obj := pkg.TypesInfo.Uses[x]; obj != nil {
3024
- if varObj, ok := obj.(*types.Var); ok {
3025
- receiverType = v.getTypeName(varObj.Type())
3026
- }
3027
- }
3028
- case *ast.SelectorExpr:
3029
- if typeExpr := pkg.TypesInfo.TypeOf(x); typeExpr != nil {
3030
- receiverType = v.getTypeName(typeExpr)
3031
- }
3032
- }
3033
-
3034
- // Get method's package path
3035
- if methodFunc, ok := methodObj.(*types.Func); ok {
3036
- if methodFunc.Pkg() != nil {
3037
- methodPkgPath = methodFunc.Pkg().Path()
3038
- }
3039
- }
3040
-
3041
- if methodPkgPath == "" {
3042
- methodPkgPath = pkg.Types.Path()
3043
- }
3044
-
3045
- return &MethodKey{
3046
- PackagePath: methodPkgPath,
3047
- ReceiverType: receiverType,
3048
- MethodName: methodObj.Name(),
3049
- }
3050
- }
3051
- }
3052
- }
3053
-
3054
- return nil
3055
- }
3056
-
3057
- // topologicalSortMethods performs a topological sort of methods based on their call dependencies
3058
- // Returns sorted methods and methods involved in cycles
3059
- func (v *analysisVisitor) topologicalSortMethods(methodCalls map[MethodKey][]MethodKey) ([]MethodKey, []MethodKey) {
3060
- // Kahn's algorithm for topological sorting
3061
- inDegree := make(map[MethodKey]int)
3062
- graph := make(map[MethodKey][]MethodKey)
3063
-
3064
- // Initialize in-degree counts and reverse graph
3065
- for method := range methodCalls {
3066
- inDegree[method] = 0
3067
- graph[method] = []MethodKey{}
3068
- }
3069
-
3070
- // Build reverse graph and count in-degrees
3071
- // graph[dependency] = methods that depend on it
3072
- for method, callees := range methodCalls {
3073
- for _, callee := range callees {
3074
- // Only create dependency edges for callees that exist in the call graph
3075
- // This automatically excludes handwritten packages that aren't being compiled
3076
- if _, exists := inDegree[callee]; exists {
3077
- // Additionally, skip if the callee is from a handwritten package
3078
- // This prevents our code from being blocked by unresolved handwritten package dependencies
3079
- if !v.analysis.isHandwrittenPackage(callee.PackagePath) {
3080
- graph[callee] = append(graph[callee], method)
3081
- inDegree[method]++
3082
- }
3083
- }
3084
- }
3085
- }
3086
-
3087
- // Find methods with no dependencies (in-degree == 0)
3088
- var queue []MethodKey
3089
- for method, degree := range inDegree {
3090
- if degree == 0 {
3091
- queue = append(queue, method)
3092
- }
3093
- }
3094
-
3095
- var sorted []MethodKey
3096
-
3097
- for len(queue) > 0 {
3098
- // Remove method from queue
3099
- current := queue[0]
3100
- queue = queue[1:]
3101
- sorted = append(sorted, current)
3102
-
3103
- // For each method that depends on current
3104
- for _, dependent := range graph[current] {
3105
- inDegree[dependent]--
3106
- if inDegree[dependent] == 0 {
3107
- queue = append(queue, dependent)
3108
- }
3109
- }
3110
- }
3111
-
3112
- // Find methods in cycles (not in sorted list)
3113
- var cycles []MethodKey
3114
- if len(sorted) != len(methodCalls) {
3115
- for method := range methodCalls {
3116
- found := slices.Contains(sorted, method)
3117
- if !found {
3118
- cycles = append(cycles, method)
3119
- }
3120
- }
3121
- }
3122
-
3123
- return sorted, cycles
3124
- }
3125
-
3126
- // analyzeMethodAsyncTopological analyzes a single method for async operations in topological order
3127
- func (v *analysisVisitor) analyzeMethodAsyncTopological(methodKey MethodKey, callees []MethodKey) {
3128
- // Check if method is from handwritten package
3129
- isHandwrittenPackage := v.analysis.isHandwrittenPackage(methodKey.PackagePath)
3130
-
3131
- if isHandwrittenPackage {
3132
- // For handwritten packages, check if we have pre-loaded metadata
3133
- _, hasMetadata := v.analysis.MethodAsyncStatus[methodKey]
3134
- if hasMetadata {
3135
- // Already set from metadata, don't override
3136
- return
3137
- }
3138
- // No metadata means assume sync
3139
- v.analysis.MethodAsyncStatus[methodKey] = false
3140
- return
3141
- }
3142
-
3143
- // Find the method declaration
3144
- pkg := v.analysis.AllPackages[methodKey.PackagePath]
3145
- if pkg == nil {
3146
- if methodKey.PackagePath == v.pkg.Types.Path() {
3147
- pkg = v.pkg
3148
- }
3149
- }
3150
-
3151
- if pkg == nil {
3152
- // Can't find package, assume sync
3153
- v.analysis.MethodAsyncStatus[methodKey] = false
3154
- return
3155
- }
3156
-
3157
- var funcDecl *ast.FuncDecl
3158
- if methodKey.ReceiverType == "" {
3159
- // Package-level function
3160
- funcDecl = v.findFunctionDecl(methodKey.MethodName, pkg)
3161
- } else {
3162
- // Method with receiver
3163
- funcDecl = v.findMethodDecl(methodKey.ReceiverType, methodKey.MethodName, pkg)
3164
- }
3165
-
3166
- if funcDecl == nil || funcDecl.Body == nil {
3167
- // No body to analyze, assume sync
3168
- v.analysis.MethodAsyncStatus[methodKey] = false
3169
- return
3170
- }
3171
-
3172
- // Check if method contains async operations (including calls to async external methods)
3173
- isAsync := v.containsAsyncOperationsComplete(funcDecl.Body, pkg)
3174
-
3175
- // If not directly async, check if any callee from the call graph is async
3176
- // (This catches calls to other methods in the same codebase)
3177
- if !isAsync {
3178
- for _, callee := range callees {
3179
- if calleeAsync, exists := v.analysis.MethodAsyncStatus[callee]; exists && calleeAsync {
3180
- isAsync = true
3181
- break
3182
- }
3183
- }
3184
- }
3185
-
3186
- v.analysis.MethodAsyncStatus[methodKey] = isAsync
3187
- }
3188
-
3189
- // getMethodKey creates a unique key for a method
3190
- func (v *analysisVisitor) getMethodKey(funcDecl *ast.FuncDecl, pkg *packages.Package) MethodKey {
3191
- packagePath := pkg.Types.Path()
3192
- methodName := funcDecl.Name.Name
3193
- receiverType := ""
3194
-
3195
- if funcDecl.Recv != nil && len(funcDecl.Recv.List) > 0 {
3196
- // Try to get receiver type from TypesInfo first
3197
- if len(funcDecl.Recv.List[0].Names) > 0 && pkg.TypesInfo != nil {
3198
- if def := pkg.TypesInfo.Defs[funcDecl.Recv.List[0].Names[0]]; def != nil {
3199
- if vr, ok := def.(*types.Var); ok {
3200
- receiverType = v.getTypeName(vr.Type())
3201
- }
3202
- }
3203
- }
3204
-
3205
- // Fallback to AST if TypesInfo is unavailable or failed
3206
- if receiverType == "" {
3207
- receiverType = v.getReceiverTypeFromAST(funcDecl.Recv.List[0].Type)
3208
- }
3209
- }
3210
-
3211
- return MethodKey{
3212
- PackagePath: packagePath,
3213
- ReceiverType: receiverType,
3214
- MethodName: methodName,
3215
- }
3216
- }
3217
-
3218
- // getTypeName extracts a clean type name from a types.Type
3219
- func (v *analysisVisitor) getTypeName(t types.Type) string {
3220
- switch typ := t.(type) {
3221
- case *types.Named:
3222
- return typ.Obj().Name()
3223
- case *types.Pointer:
3224
- return v.getTypeName(typ.Elem())
3225
- default:
3226
- return typ.String()
3227
- }
3228
- }
3229
-
3230
- // getReceiverTypeFromAST extracts the receiver type name from AST when TypesInfo is unavailable
3231
- func (v *analysisVisitor) getReceiverTypeFromAST(expr ast.Expr) string {
3232
- switch t := expr.(type) {
3233
- case *ast.StarExpr:
3234
- // Pointer receiver: *Type
3235
- return v.getReceiverTypeFromAST(t.X)
3236
- case *ast.Ident:
3237
- // Simple type name
3238
- return t.Name
3239
- case *ast.SelectorExpr:
3240
- // Qualified type: pkg.Type
3241
- return t.Sel.Name
3242
- default:
3243
- return ""
3244
- }
3245
- }
3246
-
3247
- // containsAsyncOperationsComplete is a comprehensive async detection that handles method calls
3248
- func (v *analysisVisitor) containsAsyncOperationsComplete(node ast.Node, pkg *packages.Package) bool {
3249
- var hasAsync bool
3250
- var asyncReasons []string
3251
-
3252
- ast.Inspect(node, func(n ast.Node) bool {
3253
- if n == nil {
3254
- return false
3255
- }
3256
- if n != node {
3257
- if _, ok := n.(*ast.FuncLit); ok {
3258
- return false
3259
- }
3260
- }
3261
-
3262
- switch s := n.(type) {
3263
- case *ast.SendStmt:
3264
- // Channel send operation (ch <- value)
3265
- hasAsync = true
3266
- asyncReasons = append(asyncReasons, "channel send")
3267
- return false
3268
-
3269
- case *ast.UnaryExpr:
3270
- // Channel receive operation (<-ch)
3271
- if s.Op == token.ARROW {
3272
- hasAsync = true
3273
- asyncReasons = append(asyncReasons, "channel receive")
3274
- return false
3275
- }
3276
-
3277
- case *ast.SelectStmt:
3278
- // Select statement with channel operations
3279
- hasAsync = true
3280
- asyncReasons = append(asyncReasons, "select statement")
3281
- return false
3282
-
3283
- case *ast.CallExpr:
3284
- // Check if we're calling a function known to be async
3285
- if v.isCallAsync(s, pkg) {
3286
- hasAsync = true
3287
- return false
3288
- }
3289
- }
3290
-
3291
- return true
3292
- })
3293
-
3294
- return hasAsync
3295
- }
3296
-
3297
- // isCallAsync determines if a call expression is async
3298
- func (v *analysisVisitor) isCallAsync(callExpr *ast.CallExpr, pkg *packages.Package) bool {
3299
- switch fun := callExpr.Fun.(type) {
3300
- case *ast.Ident:
3301
- // Direct function call
3302
- if obj := pkg.TypesInfo.Uses[fun]; obj != nil {
3303
- if funcObj, ok := obj.(*types.Func); ok {
3304
- result := v.isFunctionAsync(funcObj, pkg)
3305
- return result
3306
- }
3307
- // Check if this is a variable that returns async values
3308
- // (e.g., indirect := sync.OnceValue(asyncFunc))
3309
- if v.analysis.IsAsyncReturningVar(obj) {
3310
- return true
3311
- }
3312
- }
3313
-
3314
- case *ast.SelectorExpr:
3315
- // Handle package-level function calls (e.g., time.Sleep)
3316
- if ident, ok := fun.X.(*ast.Ident); ok {
3317
- if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
3318
- if pkgName, isPkg := obj.(*types.PkgName); isPkg {
3319
- methodName := fun.Sel.Name
3320
- pkgPath := pkgName.Imported().Path()
3321
- // Check if this package-level function is async (empty TypeName)
3322
- isAsync := v.analysis.IsMethodAsync(pkgPath, "", methodName)
3323
- return isAsync
3324
- }
3325
- }
3326
- }
3327
-
3328
- // Check if this is an interface method call
3329
- if receiverType := pkg.TypesInfo.TypeOf(fun.X); receiverType != nil {
3330
- if interfaceType, isInterface := receiverType.Underlying().(*types.Interface); isInterface {
3331
- methodName := fun.Sel.Name
3332
- // For interface method calls, check if the interface method is async
3333
- result := v.analysis.IsInterfaceMethodAsync(interfaceType, methodName)
3334
- return result
3335
- }
3336
- }
3337
-
3338
- // Method call on concrete objects
3339
- if selection := pkg.TypesInfo.Selections[fun]; selection != nil {
3340
- if methodObj := selection.Obj(); methodObj != nil {
3341
- result := v.isMethodAsyncFromSelection(fun, methodObj, pkg)
3342
- return result
3343
- }
3344
- }
3345
- }
3346
-
3347
- return false
3348
- }
3349
-
3350
- // isFunctionAsync checks if a function object is async
3351
- func (v *analysisVisitor) isFunctionAsync(funcObj *types.Func, pkg *packages.Package) bool {
3352
- // Check if it's from external package metadata
3353
- if funcObj.Pkg() != nil && funcObj.Pkg() != pkg.Types {
3354
- return v.analysis.IsMethodAsync(funcObj.Pkg().Path(), "", funcObj.Name())
3355
- }
3356
-
3357
- // Check internal method status (should already be computed during analysis)
3358
- methodKey := MethodKey{
3359
- PackagePath: pkg.Types.Path(),
3360
- ReceiverType: "",
3361
- MethodName: funcObj.Name(),
3362
- }
3363
-
3364
- if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
3365
- return status
3366
- }
3367
-
3368
- // Not found - should have been analyzed during analyzeAllMethodsAsync
3369
- return false
3370
- }
3371
-
3372
- // isMethodAsyncFromSelection checks if a method call is async based on selection
3373
- func (v *analysisVisitor) isMethodAsyncFromSelection(selExpr *ast.SelectorExpr, methodObj types.Object, pkg *packages.Package) bool {
3374
- // Get receiver type - handle both direct identifiers and field access
3375
- var receiverType string
3376
- var methodPkgPath string
3377
-
3378
- // Handle different receiver patterns
3379
- switch x := selExpr.X.(type) {
3380
- case *ast.Ident:
3381
- // Direct variable (e.g., mtx.Lock())
3382
- if obj := pkg.TypesInfo.Uses[x]; obj != nil {
3383
- if varObj, ok := obj.(*types.Var); ok {
3384
- receiverType = v.getTypeName(varObj.Type())
3385
- }
3386
- }
3387
- case *ast.SelectorExpr:
3388
- // Field access (e.g., l.m.Lock() or d.mu.Lock())
3389
- if typeExpr := pkg.TypesInfo.TypeOf(x); typeExpr != nil {
3390
- receiverType = v.getTypeName(typeExpr)
3391
- }
3392
- default:
3393
- // For other cases, try to get type directly
3394
- if typeExpr := pkg.TypesInfo.TypeOf(x); typeExpr != nil {
3395
- receiverType = v.getTypeName(typeExpr)
3396
- }
3397
- }
3398
-
3399
- // Get the method's package path
3400
- if methodFunc, ok := methodObj.(*types.Func); ok {
3401
- if methodFunc.Pkg() != nil {
3402
- methodPkgPath = methodFunc.Pkg().Path()
3403
- }
3404
- }
3405
-
3406
- if methodPkgPath == "" {
3407
- methodPkgPath = pkg.Types.Path()
3408
- }
3409
-
3410
- // For external packages, check unified MethodAsyncStatus first
3411
- // For internal packages, try analysis first, then fallback to lookup
3412
- methodKey := MethodKey{
3413
- PackagePath: methodPkgPath,
3414
- ReceiverType: receiverType,
3415
- MethodName: methodObj.Name(),
3416
- }
3417
-
3418
- if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
3419
- return status
3420
- }
3421
-
3422
- // Not found - should have been analyzed during analyzeAllMethodsAsync
3423
- return false
3424
- }
3425
-
3426
- // findFunctionDecl finds a function declaration by name in a package
3427
- func (v *analysisVisitor) findFunctionDecl(funcName string, pkg *packages.Package) *ast.FuncDecl {
3428
- for _, file := range pkg.Syntax {
3429
- for _, decl := range file.Decls {
3430
- if funcDecl, ok := decl.(*ast.FuncDecl); ok {
3431
- if funcDecl.Name.Name == funcName && funcDecl.Recv == nil {
3432
- return funcDecl
3433
- }
3434
- }
3435
- }
3436
- }
3437
- return nil
3438
- }
3439
-
3440
- // findMethodDecl finds a method declaration by receiver type and method name
3441
- func (v *analysisVisitor) findMethodDecl(receiverType, methodName string, pkg *packages.Package) *ast.FuncDecl {
3442
- for _, file := range pkg.Syntax {
3443
- for _, decl := range file.Decls {
3444
- if funcDecl, ok := decl.(*ast.FuncDecl); ok {
3445
- if funcDecl.Name.Name == methodName && funcDecl.Recv != nil {
3446
- if len(funcDecl.Recv.List) > 0 && len(funcDecl.Recv.List[0].Names) > 0 {
3447
- if def := pkg.TypesInfo.Defs[funcDecl.Recv.List[0].Names[0]]; def != nil {
3448
- if vr, ok := def.(*types.Var); ok {
3449
- if v.getTypeName(vr.Type()) == receiverType {
3450
- return funcDecl
3451
- }
3452
- }
3453
- }
3454
- }
3455
- }
3456
- }
3457
- }
3458
- }
3459
- return nil
3460
- }
3461
-
3462
- // IsLocalMethodAsync checks if a local method is async using pre-computed analysis
3463
- func (a *Analysis) IsLocalMethodAsync(pkgPath, receiverType, methodName string) bool {
3464
- methodKey := MethodKey{
3465
- PackagePath: pkgPath,
3466
- ReceiverType: receiverType,
3467
- MethodName: methodName,
3468
- }
3469
-
3470
- if status, exists := a.MethodAsyncStatus[methodKey]; exists {
3471
- return status
3472
- }
3473
-
3474
- return false
3475
- }