roxxie-proxy-u-prod 0.1.0

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 (481) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +123 -0
  3. package/SOURCE.md +29 -0
  4. package/THIRD_PARTY_NOTICES.md +24 -0
  5. package/dist/assets/filters-Bu_3nHVy.bin +0 -0
  6. package/dist/assets/index-BdRlQ7d_.css +1 -0
  7. package/dist/assets/index-DcvPYWS3.js +3621 -0
  8. package/dist/assets/inject-BnVzMVpl.js +40 -0
  9. package/dist/assets/scramjet-C9HLwI-y.wasm +0 -0
  10. package/dist/defaultfavicon.png +0 -0
  11. package/dist/icon.png +0 -0
  12. package/dist/roxxie-icon.svg +12 -0
  13. package/embed.js +70 -0
  14. package/examples/single-page.html +25 -0
  15. package/package.json +42 -0
  16. package/runtime/controller.sw.js +273 -0
  17. package/runtime/index.html +18 -0
  18. package/runtime/localcontrollersw.js +7 -0
  19. package/runtime/roxxie-network.json +13 -0
  20. package/runtime/sw.js +6 -0
  21. package/scripts/build.mjs +344 -0
  22. package/scripts/smoke.mjs +123 -0
  23. package/scripts/verify.mjs +122 -0
  24. package/source/external/dreamlandjs/LICENSE +21 -0
  25. package/source/external/dreamlandjs/README.md +22 -0
  26. package/source/external/dreamlandjs/dist/core.d.ts +167 -0
  27. package/source/external/dreamlandjs/dist/core.js +2 -0
  28. package/source/external/dreamlandjs/dist/core.js.map +1 -0
  29. package/source/external/dreamlandjs/dist/core.size.html +4950 -0
  30. package/source/external/dreamlandjs/dist/js-runtime.d.ts +3 -0
  31. package/source/external/dreamlandjs/dist/js-runtime.js +2 -0
  32. package/source/external/dreamlandjs/dist/js-runtime.js.map +1 -0
  33. package/source/external/dreamlandjs/dist/jsx-runtime.d.ts +1 -0
  34. package/source/external/dreamlandjs/dist/jsx-runtime.js +2 -0
  35. package/source/external/dreamlandjs/dist/jsx-runtime.js.map +1 -0
  36. package/source/external/dreamlandjs/dist/motion.d.ts +19 -0
  37. package/source/external/dreamlandjs/dist/motion.js +2 -0
  38. package/source/external/dreamlandjs/dist/motion.js.map +1 -0
  39. package/source/external/dreamlandjs/dist/router.d.ts +48 -0
  40. package/source/external/dreamlandjs/dist/router.js +2 -0
  41. package/source/external/dreamlandjs/dist/router.js.map +1 -0
  42. package/source/external/dreamlandjs/dist/ssr.client.d.ts +3 -0
  43. package/source/external/dreamlandjs/dist/ssr.client.js +2 -0
  44. package/source/external/dreamlandjs/dist/ssr.client.js.map +1 -0
  45. package/source/external/dreamlandjs/dist/ssr.client.size.html +4950 -0
  46. package/source/external/dreamlandjs/dist/ssr.hybrid.d.ts +7 -0
  47. package/source/external/dreamlandjs/dist/ssr.hybrid.js +2 -0
  48. package/source/external/dreamlandjs/dist/ssr.hybrid.js.map +1 -0
  49. package/source/external/dreamlandjs/dist/ssr.hybrid.size.html +4950 -0
  50. package/source/external/dreamlandjs/dist/ssr.server.d.ts +11 -0
  51. package/source/external/dreamlandjs/dist/ssr.server.js +2373 -0
  52. package/source/external/dreamlandjs/dist/ssr.server.js.map +1 -0
  53. package/source/external/dreamlandjs/dist/types/core/consts.d.ts +16 -0
  54. package/source/external/dreamlandjs/dist/types/core/css/index.d.ts +11 -0
  55. package/source/external/dreamlandjs/dist/types/core/css/selectorParser.d.ts +50 -0
  56. package/source/external/dreamlandjs/dist/types/core/delegate.d.ts +10 -0
  57. package/source/external/dreamlandjs/dist/types/core/index.d.ts +9 -0
  58. package/source/external/dreamlandjs/dist/types/core/jsx/child.d.ts +2 -0
  59. package/source/external/dreamlandjs/dist/types/core/jsx/definitions.d.ts +76 -0
  60. package/source/external/dreamlandjs/dist/types/core/jsx/dom.d.ts +28 -0
  61. package/source/external/dreamlandjs/dist/types/core/jsx/index.d.ts +14 -0
  62. package/source/external/dreamlandjs/dist/types/core/state/index.d.ts +1 -0
  63. package/source/external/dreamlandjs/dist/types/core/state/pointers.d.ts +37 -0
  64. package/source/external/dreamlandjs/dist/types/core/state/state.d.ts +13 -0
  65. package/source/external/dreamlandjs/dist/types/core/state/use.d.ts +11 -0
  66. package/source/external/dreamlandjs/dist/types/core/utils.d.ts +4 -0
  67. package/source/external/dreamlandjs/dist/types/js-runtime/index.d.ts +1 -0
  68. package/source/external/dreamlandjs/dist/types/jsx-runtime/index.d.ts +2 -0
  69. package/source/external/dreamlandjs/dist/types/motion/index.d.ts +15 -0
  70. package/source/external/dreamlandjs/dist/types/router/index.d.ts +45 -0
  71. package/source/external/dreamlandjs/dist/types/ssr/client/index.d.ts +1 -0
  72. package/source/external/dreamlandjs/dist/types/ssr/common/consts.d.ts +4 -0
  73. package/source/external/dreamlandjs/dist/types/ssr/common/serialize.d.ts +4 -0
  74. package/source/external/dreamlandjs/dist/types/ssr/common/types.d.ts +21 -0
  75. package/source/external/dreamlandjs/dist/types/ssr/hybrid/index.d.ts +4 -0
  76. package/source/external/dreamlandjs/dist/types/ssr/server/index.d.ts +7 -0
  77. package/source/external/dreamlandjs/dist/types/ssr/server/vdom.d.ts +71 -0
  78. package/source/external/dreamlandjs/dist/types/util/index.d.ts +1 -0
  79. package/source/external/dreamlandjs/dist/types/util/store.d.ts +26 -0
  80. package/source/external/dreamlandjs/dist/types/vite/css-minifier.d.ts +7 -0
  81. package/source/external/dreamlandjs/dist/types/vite/index.d.ts +11 -0
  82. package/source/external/dreamlandjs/dist/util.d.ts +29 -0
  83. package/source/external/dreamlandjs/dist/util.js +2 -0
  84. package/source/external/dreamlandjs/dist/util.js.map +1 -0
  85. package/source/external/dreamlandjs/dist/vite.d.ts +19 -0
  86. package/source/external/dreamlandjs/dist/vite.js +2 -0
  87. package/source/external/dreamlandjs/dist/vite.js.map +1 -0
  88. package/source/external/dreamlandjs/package.json +101 -0
  89. package/source/external/dreamlandjs/rollup.config.ts +302 -0
  90. package/source/external/dreamlandjs/rollup.plugins.ts +1134 -0
  91. package/source/external/dreamlandjs/src/core/consts.d.ts +16 -0
  92. package/source/external/dreamlandjs/src/core/consts.js +18 -0
  93. package/source/external/dreamlandjs/src/core/css/index.ts +111 -0
  94. package/source/external/dreamlandjs/src/core/css/selectorParser.ts +313 -0
  95. package/source/external/dreamlandjs/src/core/delegate.ts +28 -0
  96. package/source/external/dreamlandjs/src/core/index.ts +31 -0
  97. package/source/external/dreamlandjs/src/core/jsx/child.ts +91 -0
  98. package/source/external/dreamlandjs/src/core/jsx/definitions.ts +125 -0
  99. package/source/external/dreamlandjs/src/core/jsx/dom.ts +49 -0
  100. package/source/external/dreamlandjs/src/core/jsx/index.ts +289 -0
  101. package/source/external/dreamlandjs/src/core/state/index.ts +1 -0
  102. package/source/external/dreamlandjs/src/core/state/pointers.ts +310 -0
  103. package/source/external/dreamlandjs/src/core/state/state.ts +112 -0
  104. package/source/external/dreamlandjs/src/core/state/use.ts +82 -0
  105. package/source/external/dreamlandjs/src/core/utils.ts +48 -0
  106. package/source/external/dreamlandjs/src/js-runtime/index.ts +4 -0
  107. package/source/external/dreamlandjs/src/jsx-runtime/index.ts +3 -0
  108. package/source/external/dreamlandjs/src/motion/index.ts +156 -0
  109. package/source/external/dreamlandjs/src/router/index.tsx +394 -0
  110. package/source/external/dreamlandjs/src/router/tsconfig.json +9 -0
  111. package/source/external/dreamlandjs/src/ssr/client/index.ts +193 -0
  112. package/source/external/dreamlandjs/src/ssr/common/consts.ts +5 -0
  113. package/source/external/dreamlandjs/src/ssr/common/serialize.ts +115 -0
  114. package/source/external/dreamlandjs/src/ssr/common/types.ts +28 -0
  115. package/source/external/dreamlandjs/src/ssr/hybrid/index.ts +144 -0
  116. package/source/external/dreamlandjs/src/ssr/server/index.ts +157 -0
  117. package/source/external/dreamlandjs/src/ssr/server/tsconfig.json +7 -0
  118. package/source/external/dreamlandjs/src/ssr/server/vdom.ts +285 -0
  119. package/source/external/dreamlandjs/src/util/index.ts +10 -0
  120. package/source/external/dreamlandjs/src/util/store.ts +134 -0
  121. package/source/external/dreamlandjs/src/vite/css-minifier.ts +222 -0
  122. package/source/external/dreamlandjs/src/vite/index.ts +60 -0
  123. package/source/external/dreamlandjs/src/vite/tsconfig.json +7 -0
  124. package/source/external/dreamlandjs/tsconfig.json +15 -0
  125. package/source/package.json +48 -0
  126. package/source/packages/adrift-protocol/package.json +31 -0
  127. package/source/packages/adrift-protocol/src/index.ts +348 -0
  128. package/source/packages/adrift-protocol/tsconfig.json +18 -0
  129. package/source/packages/cdp/LICENSE +21 -0
  130. package/source/packages/cdp/README.md +47 -0
  131. package/source/packages/cdp/dist/chobitsu.js +2 -0
  132. package/source/packages/cdp/dist/chobitsu.js.map +1 -0
  133. package/source/packages/cdp/package.json +73 -0
  134. package/source/packages/cdp/script/build.js +15 -0
  135. package/source/packages/cdp/src/Chobitsu.ts +110 -0
  136. package/source/packages/cdp/src/domains/CSS.ts +378 -0
  137. package/source/packages/cdp/src/domains/CacheStorage.ts +8 -0
  138. package/source/packages/cdp/src/domains/DOM.ts +487 -0
  139. package/source/packages/cdp/src/domains/DOMDebugger.ts +109 -0
  140. package/source/packages/cdp/src/domains/DOMStorage.ts +122 -0
  141. package/source/packages/cdp/src/domains/Debugger.ts +25 -0
  142. package/source/packages/cdp/src/domains/IndexedDB.ts +207 -0
  143. package/source/packages/cdp/src/domains/Input.ts +177 -0
  144. package/source/packages/cdp/src/domains/Network.ts +251 -0
  145. package/source/packages/cdp/src/domains/Overlay.ts +230 -0
  146. package/source/packages/cdp/src/domains/Page.ts +296 -0
  147. package/source/packages/cdp/src/domains/Runtime.ts +273 -0
  148. package/source/packages/cdp/src/domains/Storage.ts +71 -0
  149. package/source/packages/cdp/src/index.d.ts +8 -0
  150. package/source/packages/cdp/src/index.ts +153 -0
  151. package/source/packages/cdp/src/lib/connector.ts +15 -0
  152. package/source/packages/cdp/src/lib/constants.ts +1 -0
  153. package/source/packages/cdp/src/lib/evaluate.ts +59 -0
  154. package/source/packages/cdp/src/lib/mutationObserver.ts +39 -0
  155. package/source/packages/cdp/src/lib/nodeManager.ts +142 -0
  156. package/source/packages/cdp/src/lib/objManager.ts +422 -0
  157. package/source/packages/cdp/src/lib/request.ts +313 -0
  158. package/source/packages/cdp/src/lib/resources.ts +59 -0
  159. package/source/packages/cdp/src/lib/scriptMananger.ts +62 -0
  160. package/source/packages/cdp/src/lib/stylesheet.ts +146 -0
  161. package/source/packages/cdp/src/lib/util.ts +105 -0
  162. package/source/packages/cdp/tsconfig.json +16 -0
  163. package/source/packages/cdp/webpack.config.js +48 -0
  164. package/source/packages/chrome/index.html +17 -0
  165. package/source/packages/chrome/package.json +41 -0
  166. package/source/packages/chrome/public/ROXXIE_NETWORK.md +22 -0
  167. package/source/packages/chrome/public/controller.sw.js +273 -0
  168. package/source/packages/chrome/public/defaultfavicon.png +0 -0
  169. package/source/packages/chrome/public/icon.png +0 -0
  170. package/source/packages/chrome/public/localcontrollersw.js +7 -0
  171. package/source/packages/chrome/public/roxxie-icon.svg +12 -0
  172. package/source/packages/chrome/public/roxxie-network.json +13 -0
  173. package/source/packages/chrome/public/sw.js +6 -0
  174. package/source/packages/chrome/src/App.tsx +269 -0
  175. package/source/packages/chrome/src/CDP.ts +285 -0
  176. package/source/packages/chrome/src/Tab/History.ts +186 -0
  177. package/source/packages/chrome/src/Tab/Tab.tsx +219 -0
  178. package/source/packages/chrome/src/Tab/screenshot.ts +66 -0
  179. package/source/packages/chrome/src/Tab/serialize.ts +221 -0
  180. package/source/packages/chrome/src/adblock/AdBlockPlugin.test.ts +159 -0
  181. package/source/packages/chrome/src/adblock/AdBlockPlugin.ts +232 -0
  182. package/source/packages/chrome/src/adblock/AdBlockService.test.ts +148 -0
  183. package/source/packages/chrome/src/adblock/AdBlockService.ts +166 -0
  184. package/source/packages/chrome/src/adblock/NOTICE.md +14 -0
  185. package/source/packages/chrome/src/adblock/assets/filters.bin +0 -0
  186. package/source/packages/chrome/src/adblock/index.ts +18 -0
  187. package/source/packages/chrome/src/assets/favicon.ts +4 -0
  188. package/source/packages/chrome/src/components/BookmarkPopup.tsx +101 -0
  189. package/source/packages/chrome/src/components/BookmarksStrip.tsx +150 -0
  190. package/source/packages/chrome/src/components/Button.tsx +99 -0
  191. package/source/packages/chrome/src/components/Checkbox.tsx +89 -0
  192. package/source/packages/chrome/src/components/DownloadsPopup.tsx +230 -0
  193. package/source/packages/chrome/src/components/Favicon.tsx +65 -0
  194. package/source/packages/chrome/src/components/Icon.tsx +29 -0
  195. package/source/packages/chrome/src/components/Input.tsx +82 -0
  196. package/source/packages/chrome/src/components/Menu.tsx +287 -0
  197. package/source/packages/chrome/src/components/Omnibar/BookmarkButton.tsx +73 -0
  198. package/source/packages/chrome/src/components/Omnibar/CircularProgress.tsx +83 -0
  199. package/source/packages/chrome/src/components/Omnibar/Omnibar.tsx +455 -0
  200. package/source/packages/chrome/src/components/Omnibar/OmnibarButton.tsx +48 -0
  201. package/source/packages/chrome/src/components/Omnibar/Omnibox.tsx +466 -0
  202. package/source/packages/chrome/src/components/Omnibar/SiteOptionsButton.tsx +52 -0
  203. package/source/packages/chrome/src/components/Omnibar/Suggestion.tsx +186 -0
  204. package/source/packages/chrome/src/components/Omnibar/UrlInput.tsx +198 -0
  205. package/source/packages/chrome/src/components/Omnibar/suggestions.ts +468 -0
  206. package/source/packages/chrome/src/components/Omnibar/utils.ts +11 -0
  207. package/source/packages/chrome/src/components/ReportBrokenSiteModal.tsx +279 -0
  208. package/source/packages/chrome/src/components/Shell.tsx +191 -0
  209. package/source/packages/chrome/src/components/SiteInformationPopup.tsx +96 -0
  210. package/source/packages/chrome/src/components/SmallIconButton.tsx +36 -0
  211. package/source/packages/chrome/src/components/StartScreen.tsx +386 -0
  212. package/source/packages/chrome/src/components/TabStrip/DragTab.tsx +546 -0
  213. package/source/packages/chrome/src/components/TabStrip/Sidebar.tsx +820 -0
  214. package/source/packages/chrome/src/components/TabStrip/TabHoverCard.tsx +87 -0
  215. package/source/packages/chrome/src/components/TabStrip/TabStrip.tsx +510 -0
  216. package/source/packages/chrome/src/components/TopSiteButton.tsx +234 -0
  217. package/source/packages/chrome/src/consts.ts +3 -0
  218. package/source/packages/chrome/src/icons.ts +70 -0
  219. package/source/packages/chrome/src/index.ts +168 -0
  220. package/source/packages/chrome/src/migrations/0-from-legacy.ts +6 -0
  221. package/source/packages/chrome/src/migrations/1-vertical-tab-settings.ts +15 -0
  222. package/source/packages/chrome/src/migrations/index.ts +42 -0
  223. package/source/packages/chrome/src/pages/AboutPage.tsx +37 -0
  224. package/source/packages/chrome/src/pages/DownloadsPage.tsx +133 -0
  225. package/source/packages/chrome/src/pages/HistoryPage.tsx +99 -0
  226. package/source/packages/chrome/src/pages/NewTabPage.tsx +264 -0
  227. package/source/packages/chrome/src/pages/PlaygroundPage.tsx +43 -0
  228. package/source/packages/chrome/src/pages/SettingsPage.tsx +1931 -0
  229. package/source/packages/chrome/src/proxy/Controller.ts +331 -0
  230. package/source/packages/chrome/src/proxy/ProxyFrame.ts +26 -0
  231. package/source/packages/chrome/src/proxy/cache.ts +487 -0
  232. package/source/packages/chrome/src/proxy/codec.ts +11 -0
  233. package/source/packages/chrome/src/proxy/contextitems.ts +180 -0
  234. package/source/packages/chrome/src/proxy/index.ts +5 -0
  235. package/source/packages/chrome/src/proxy/roxxie.ts +85 -0
  236. package/source/packages/chrome/src/proxy/scramjet.ts +642 -0
  237. package/source/packages/chrome/src/reset.css +124 -0
  238. package/source/packages/chrome/src/runtime-base.ts +16 -0
  239. package/source/packages/chrome/src/sentry.tsx +45 -0
  240. package/source/packages/chrome/src/services/DownloadsService.ts +136 -0
  241. package/source/packages/chrome/src/services/FaviconService.ts +161 -0
  242. package/source/packages/chrome/src/services/KVWrapper.ts +169 -0
  243. package/source/packages/chrome/src/services/ProfileService.ts +94 -0
  244. package/source/packages/chrome/src/services/Service.ts +10 -0
  245. package/source/packages/chrome/src/services/SettingsService.ts +108 -0
  246. package/source/packages/chrome/src/services/TabsService.ts +161 -0
  247. package/source/packages/chrome/src/startup.test.ts +129 -0
  248. package/source/packages/chrome/src/startup.ts +161 -0
  249. package/source/packages/chrome/src/style.css +229 -0
  250. package/source/packages/chrome/src/themes.ts +756 -0
  251. package/source/packages/chrome/src/util/StatefulClass.ts +31 -0
  252. package/source/packages/chrome/src/util/index.ts +74 -0
  253. package/source/packages/chrome/src/util/memoize.ts +13 -0
  254. package/source/packages/chrome/src/vite-env.d.ts +16 -0
  255. package/source/packages/chrome/tsconfig.json +30 -0
  256. package/source/packages/chrome/vite.config.ts +47 -0
  257. package/source/packages/inject/package.json +20 -0
  258. package/source/packages/inject/src/context.ts +194 -0
  259. package/source/packages/inject/src/emulators/alwaysLastBubble.ts +168 -0
  260. package/source/packages/inject/src/emulators/anchors.ts +55 -0
  261. package/source/packages/inject/src/emulators/contextmenu.ts +46 -0
  262. package/source/packages/inject/src/emulators/history.ts +51 -0
  263. package/source/packages/inject/src/emulators/titlewatcher.ts +42 -0
  264. package/source/packages/inject/src/emulators/windowopen.ts +134 -0
  265. package/source/packages/inject/src/errorpage/errorpage.css +234 -0
  266. package/source/packages/inject/src/errorpage/errorpage.html +29 -0
  267. package/source/packages/inject/src/errorpage/errorpage.ts +76 -0
  268. package/source/packages/inject/src/index.ts +62 -0
  269. package/source/packages/inject/src/transport.ts +97 -0
  270. package/source/packages/inject/src/types.d.ts +136 -0
  271. package/source/packages/roxxie-transport/package.json +36 -0
  272. package/source/packages/roxxie-transport/src/binary.ts +398 -0
  273. package/source/packages/roxxie-transport/src/config.ts +254 -0
  274. package/source/packages/roxxie-transport/src/connection.ts +767 -0
  275. package/source/packages/roxxie-transport/src/index.ts +5 -0
  276. package/source/packages/roxxie-transport/src/tracker.ts +651 -0
  277. package/source/packages/roxxie-transport/src/transport.ts +414 -0
  278. package/source/packages/roxxie-transport/tsconfig.json +18 -0
  279. package/source/packages/scramjet/packages/controller/package.json +37 -0
  280. package/source/packages/scramjet/packages/controller/src/index.ts +861 -0
  281. package/source/packages/scramjet/packages/controller/src/inject.ts +365 -0
  282. package/source/packages/scramjet/packages/controller/src/sw.ts +231 -0
  283. package/source/packages/scramjet/packages/controller/src/symbols.ts +1 -0
  284. package/source/packages/scramjet/packages/controller/src/types.d.ts +140 -0
  285. package/source/packages/scramjet/packages/controller/src/typesEntry.ts +6 -0
  286. package/source/packages/scramjet/packages/controller/src/version.ts +30 -0
  287. package/source/packages/scramjet/packages/controller/tsconfig.json +24 -0
  288. package/source/packages/scramjet/packages/core/dist/scramjet-external.mjs +57 -0
  289. package/source/packages/scramjet/packages/core/dist/scramjet.js +13599 -0
  290. package/source/packages/scramjet/packages/core/dist/scramjet.js.map +1 -0
  291. package/source/packages/scramjet/packages/core/dist/scramjet.mjs +13649 -0
  292. package/source/packages/scramjet/packages/core/dist/scramjet.mjs.map +1 -0
  293. package/source/packages/scramjet/packages/core/dist/scramjet.wasm +0 -0
  294. package/source/packages/scramjet/packages/core/dist/scramjet_bundled.js +13601 -0
  295. package/source/packages/scramjet/packages/core/dist/scramjet_bundled.js.map +1 -0
  296. package/source/packages/scramjet/packages/core/dist/scramjet_bundled.mjs +13651 -0
  297. package/source/packages/scramjet/packages/core/dist/scramjet_bundled.mjs.map +1 -0
  298. package/source/packages/scramjet/packages/core/dist/temp-types-build/index.js +23 -0
  299. package/source/packages/scramjet/packages/core/dist/temp-types-build/index.js.map +1 -0
  300. package/source/packages/scramjet/packages/core/dist/types/Tap.d.ts +39 -0
  301. package/source/packages/scramjet/packages/core/dist/types/client/client.d.ts +115 -0
  302. package/source/packages/scramjet/packages/core/dist/types/client/entry.d.ts +5 -0
  303. package/source/packages/scramjet/packages/core/dist/types/client/events.d.ts +10 -0
  304. package/source/packages/scramjet/packages/core/dist/types/client/helpers.d.ts +1 -0
  305. package/source/packages/scramjet/packages/core/dist/types/client/index.d.ts +7 -0
  306. package/source/packages/scramjet/packages/core/dist/types/client/location.d.ts +2 -0
  307. package/source/packages/scramjet/packages/core/dist/types/client/shared/eval.d.ts +3 -0
  308. package/source/packages/scramjet/packages/core/dist/types/client/shared/sourcemaps.d.ts +19 -0
  309. package/source/packages/scramjet/packages/core/dist/types/client/shared/wrap.d.ts +4 -0
  310. package/source/packages/scramjet/packages/core/dist/types/client/singletonbox.d.ts +18 -0
  311. package/source/packages/scramjet/packages/core/dist/types/fetch/body.d.ts +3 -0
  312. package/source/packages/scramjet/packages/core/dist/types/fetch/fetch.d.ts +7 -0
  313. package/source/packages/scramjet/packages/core/dist/types/fetch/headers.d.ts +19 -0
  314. package/source/packages/scramjet/packages/core/dist/types/fetch/index.d.ts +128 -0
  315. package/source/packages/scramjet/packages/core/dist/types/fetch/parse.d.ts +23 -0
  316. package/source/packages/scramjet/packages/core/dist/types/fetch/util.d.ts +7 -0
  317. package/source/packages/scramjet/packages/core/dist/types/index.d.ts +12 -0
  318. package/source/packages/scramjet/packages/core/dist/types/shared/cookie.d.ts +26 -0
  319. package/source/packages/scramjet/packages/core/dist/types/shared/headers.d.ts +13 -0
  320. package/source/packages/scramjet/packages/core/dist/types/shared/htmlRules.d.ts +6 -0
  321. package/source/packages/scramjet/packages/core/dist/types/shared/index.d.ts +50 -0
  322. package/source/packages/scramjet/packages/core/dist/types/shared/mime.d.ts +53 -0
  323. package/source/packages/scramjet/packages/core/dist/types/shared/refresh.d.ts +7 -0
  324. package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/css.d.ts +4 -0
  325. package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/html.d.ts +33 -0
  326. package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/index.d.ts +6 -0
  327. package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/js.d.ts +11 -0
  328. package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/url.d.ts +24 -0
  329. package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/wasm.d.ts +7 -0
  330. package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/worker.d.ts +3 -0
  331. package/source/packages/scramjet/packages/core/dist/types/shared/set-cookie-parser.d.ts +20 -0
  332. package/source/packages/scramjet/packages/core/dist/types/shared/snapshot.d.ts +186 -0
  333. package/source/packages/scramjet/packages/core/dist/types/shared/sniffEncoding.d.ts +65 -0
  334. package/source/packages/scramjet/packages/core/dist/types/shared/util.d.ts +2 -0
  335. package/source/packages/scramjet/packages/core/dist/types/symbols.d.ts +6 -0
  336. package/source/packages/scramjet/packages/core/dist/types/types.d.ts +60 -0
  337. package/source/packages/scramjet/packages/core/dist/types/typesEntry.d.ts +5 -0
  338. package/source/packages/scramjet/packages/core/lib/index.cjs +7 -0
  339. package/source/packages/scramjet/packages/core/lib/index.d.ts +8 -0
  340. package/source/packages/scramjet/packages/core/package.json +69 -0
  341. package/source/packages/scramjet/packages/core/rewriter/Cargo.lock +1985 -0
  342. package/source/packages/scramjet/packages/core/rewriter/Cargo.toml +20 -0
  343. package/source/packages/scramjet/packages/core/rewriter/coverage-macro/Cargo.toml +15 -0
  344. package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/analyze.rs +1118 -0
  345. package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/ast_table.rs +927 -0
  346. package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/helper_index.rs +556 -0
  347. package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/lib.rs +455 -0
  348. package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/reachability.rs +85 -0
  349. package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/templates.rs +192 -0
  350. package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/witness.rs +597 -0
  351. package/source/packages/scramjet/packages/core/rewriter/js/Cargo.toml +18 -0
  352. package/source/packages/scramjet/packages/core/rewriter/js/src/cfg.rs +45 -0
  353. package/source/packages/scramjet/packages/core/rewriter/js/src/changes.rs +379 -0
  354. package/source/packages/scramjet/packages/core/rewriter/js/src/lib.rs +171 -0
  355. package/source/packages/scramjet/packages/core/rewriter/js/src/rewrite.rs +290 -0
  356. package/source/packages/scramjet/packages/core/rewriter/js/src/visitor.rs +967 -0
  357. package/source/packages/scramjet/packages/core/rewriter/native/Cargo.toml +22 -0
  358. package/source/packages/scramjet/packages/core/rewriter/native/src/main.rs +166 -0
  359. package/source/packages/scramjet/packages/core/rewriter/native/src/rewriter.rs +161 -0
  360. package/source/packages/scramjet/packages/core/rewriter/native/src/test_runner.rs +156 -0
  361. package/source/packages/scramjet/packages/core/rewriter/rust-toolchain.toml +5 -0
  362. package/source/packages/scramjet/packages/core/rewriter/rustfmt.toml +2 -0
  363. package/source/packages/scramjet/packages/core/rewriter/transform/Cargo.toml +16 -0
  364. package/source/packages/scramjet/packages/core/rewriter/transform/src/lib.rs +242 -0
  365. package/source/packages/scramjet/packages/core/rewriter/transform/src/transform.rs +123 -0
  366. package/source/packages/scramjet/packages/core/rewriter/wasm/Cargo.toml +22 -0
  367. package/source/packages/scramjet/packages/core/rewriter/wasm/build.sh +128 -0
  368. package/source/packages/scramjet/packages/core/rewriter/wasm/src/error.rs +56 -0
  369. package/source/packages/scramjet/packages/core/rewriter/wasm/src/jsr.rs +125 -0
  370. package/source/packages/scramjet/packages/core/rewriter/wasm/src/lib.rs +144 -0
  371. package/source/packages/scramjet/packages/core/src/Tap.ts +166 -0
  372. package/source/packages/scramjet/packages/core/src/client/client.ts +916 -0
  373. package/source/packages/scramjet/packages/core/src/client/dom/attr.ts +94 -0
  374. package/source/packages/scramjet/packages/core/src/client/dom/beacon.ts +11 -0
  375. package/source/packages/scramjet/packages/core/src/client/dom/cookie.ts +21 -0
  376. package/source/packages/scramjet/packages/core/src/client/dom/css.ts +106 -0
  377. package/source/packages/scramjet/packages/core/src/client/dom/document.ts +140 -0
  378. package/source/packages/scramjet/packages/core/src/client/dom/element.ts +783 -0
  379. package/source/packages/scramjet/packages/core/src/client/dom/fontface.ts +11 -0
  380. package/source/packages/scramjet/packages/core/src/client/dom/fragments.ts +32 -0
  381. package/source/packages/scramjet/packages/core/src/client/dom/history.ts +35 -0
  382. package/source/packages/scramjet/packages/core/src/client/dom/open.ts +59 -0
  383. package/source/packages/scramjet/packages/core/src/client/dom/origin.ts +13 -0
  384. package/source/packages/scramjet/packages/core/src/client/dom/performance.ts +50 -0
  385. package/source/packages/scramjet/packages/core/src/client/dom/protocol.ts +17 -0
  386. package/source/packages/scramjet/packages/core/src/client/dom/storage.ts +107 -0
  387. package/source/packages/scramjet/packages/core/src/client/entry.ts +12 -0
  388. package/source/packages/scramjet/packages/core/src/client/events.ts +10 -0
  389. package/source/packages/scramjet/packages/core/src/client/helpers.ts +7 -0
  390. package/source/packages/scramjet/packages/core/src/client/index.ts +7 -0
  391. package/source/packages/scramjet/packages/core/src/client/location.ts +139 -0
  392. package/source/packages/scramjet/packages/core/src/client/shared/antiantidebugger.ts +20 -0
  393. package/source/packages/scramjet/packages/core/src/client/shared/blob.ts +35 -0
  394. package/source/packages/scramjet/packages/core/src/client/shared/caches.ts +31 -0
  395. package/source/packages/scramjet/packages/core/src/client/shared/chrome.ts +90 -0
  396. package/source/packages/scramjet/packages/core/src/client/shared/err.ts +55 -0
  397. package/source/packages/scramjet/packages/core/src/client/shared/error.ts +42 -0
  398. package/source/packages/scramjet/packages/core/src/client/shared/eval.ts +40 -0
  399. package/source/packages/scramjet/packages/core/src/client/shared/event.ts +215 -0
  400. package/source/packages/scramjet/packages/core/src/client/shared/function.ts +59 -0
  401. package/source/packages/scramjet/packages/core/src/client/shared/import.ts +46 -0
  402. package/source/packages/scramjet/packages/core/src/client/shared/indexeddb.ts +18 -0
  403. package/source/packages/scramjet/packages/core/src/client/shared/opfs.ts +27 -0
  404. package/source/packages/scramjet/packages/core/src/client/shared/postmessage.ts +113 -0
  405. package/source/packages/scramjet/packages/core/src/client/shared/realm.ts +26 -0
  406. package/source/packages/scramjet/packages/core/src/client/shared/requests/eventsource.ts +17 -0
  407. package/source/packages/scramjet/packages/core/src/client/shared/requests/fetch.ts +67 -0
  408. package/source/packages/scramjet/packages/core/src/client/shared/requests/websocket.ts +389 -0
  409. package/source/packages/scramjet/packages/core/src/client/shared/requests/xmlhttprequest.ts +163 -0
  410. package/source/packages/scramjet/packages/core/src/client/shared/settimeout.ts +20 -0
  411. package/source/packages/scramjet/packages/core/src/client/shared/sourcemaps.ts +203 -0
  412. package/source/packages/scramjet/packages/core/src/client/shared/worker.ts +72 -0
  413. package/source/packages/scramjet/packages/core/src/client/shared/wrap.ts +197 -0
  414. package/source/packages/scramjet/packages/core/src/client/singletonbox.ts +52 -0
  415. package/source/packages/scramjet/packages/core/src/client/worker/importScripts.ts +13 -0
  416. package/source/packages/scramjet/packages/core/src/fetch/body.ts +93 -0
  417. package/source/packages/scramjet/packages/core/src/fetch/fetch.ts +323 -0
  418. package/source/packages/scramjet/packages/core/src/fetch/headers.ts +528 -0
  419. package/source/packages/scramjet/packages/core/src/fetch/index.ts +198 -0
  420. package/source/packages/scramjet/packages/core/src/fetch/parse.ts +149 -0
  421. package/source/packages/scramjet/packages/core/src/fetch/util.ts +81 -0
  422. package/source/packages/scramjet/packages/core/src/global.d.ts +32 -0
  423. package/source/packages/scramjet/packages/core/src/index.ts +78 -0
  424. package/source/packages/scramjet/packages/core/src/log.ts +117 -0
  425. package/source/packages/scramjet/packages/core/src/shared/cookie.ts +199 -0
  426. package/source/packages/scramjet/packages/core/src/shared/headers.ts +76 -0
  427. package/source/packages/scramjet/packages/core/src/shared/htmlRules.ts +154 -0
  428. package/source/packages/scramjet/packages/core/src/shared/index.ts +79 -0
  429. package/source/packages/scramjet/packages/core/src/shared/mime.ts +234 -0
  430. package/source/packages/scramjet/packages/core/src/shared/refresh.ts +164 -0
  431. package/source/packages/scramjet/packages/core/src/shared/rewriters/css.ts +67 -0
  432. package/source/packages/scramjet/packages/core/src/shared/rewriters/html.ts +603 -0
  433. package/source/packages/scramjet/packages/core/src/shared/rewriters/index.ts +6 -0
  434. package/source/packages/scramjet/packages/core/src/shared/rewriters/js.ts +158 -0
  435. package/source/packages/scramjet/packages/core/src/shared/rewriters/url.ts +248 -0
  436. package/source/packages/scramjet/packages/core/src/shared/rewriters/wasm.ts +58 -0
  437. package/source/packages/scramjet/packages/core/src/shared/rewriters/worker.ts +37 -0
  438. package/source/packages/scramjet/packages/core/src/shared/set-cookie-parser.ts +195 -0
  439. package/source/packages/scramjet/packages/core/src/shared/snapshot.ts +245 -0
  440. package/source/packages/scramjet/packages/core/src/shared/sniffEncoding.ts +1061 -0
  441. package/source/packages/scramjet/packages/core/src/shared/util.ts +35 -0
  442. package/source/packages/scramjet/packages/core/src/symbols.ts +9 -0
  443. package/source/packages/scramjet/packages/core/src/sync.ts +63 -0
  444. package/source/packages/scramjet/packages/core/src/types.ts +66 -0
  445. package/source/packages/scramjet/packages/core/src/typesEntry.ts +6 -0
  446. package/source/packages/scramjet/packages/core/tsconfig.json +21 -0
  447. package/source/packages/scramjet/packages/rpc/index.ts +100 -0
  448. package/source/packages/scramjet/packages/rpc/package.json +7 -0
  449. package/source/packages/scramjet/packages/utils/package.json +39 -0
  450. package/source/packages/scramjet/packages/utils/src/alwaysLastBubble.ts +172 -0
  451. package/source/packages/scramjet/packages/utils/src/catch-escaped-links.ts +34 -0
  452. package/source/packages/scramjet/packages/utils/src/event-handler-plugin.ts +86 -0
  453. package/source/packages/scramjet/packages/utils/src/global.d.ts +8 -0
  454. package/source/packages/scramjet/packages/utils/src/http-cache-plugin.ts +475 -0
  455. package/source/packages/scramjet/packages/utils/src/index.ts +26 -0
  456. package/source/packages/scramjet/packages/utils/src/link-handler-plugin.ts +75 -0
  457. package/source/packages/scramjet/packages/utils/src/typesEntry.ts +5 -0
  458. package/source/packages/scramjet/packages/utils/src/url-watcher.ts +37 -0
  459. package/source/packages/scramjet/packages/utils/src/version.ts +40 -0
  460. package/source/packages/scramjet/packages/utils/tsconfig.json +11 -0
  461. package/source/packages/scramjet/patches/htmlparser2@12.0.0.patch +124 -0
  462. package/source/packages/scramjet/rspack.config.ts +613 -0
  463. package/source/packages/scramjet/tsconfig.json +11 -0
  464. package/source/packages/tracker-protocol/package.json +31 -0
  465. package/source/packages/tracker-protocol/src/index.ts +745 -0
  466. package/source/packages/tracker-protocol/tsconfig.json +18 -0
  467. package/source/pnpm-lock.yaml +12018 -0
  468. package/source/pnpm-workspace.yaml +31 -0
  469. package/source/rspack.config.ts +103 -0
  470. package/source/scripts/update-adblock-assets.mjs +164 -0
  471. package/third_party/filter-lists/LICENSE-CC-BY-SA-3.0.txt +359 -0
  472. package/third_party/filter-lists/LICENSE-GPL-3.0.txt +674 -0
  473. package/third_party/filter-lists/LICENSE-MPL-2.0.txt +375 -0
  474. package/third_party/filter-lists/README.md +24 -0
  475. package/third_party/filter-lists/easylist.txt +83088 -0
  476. package/third_party/filter-lists/easyprivacy.txt +56361 -0
  477. package/third_party/filter-lists/manifest.json +56 -0
  478. package/third_party/filter-lists/ublock-filters.txt +10937 -0
  479. package/third_party/filter-lists/ublock-privacy.txt +3209 -0
  480. package/third_party/filter-lists/ublock-resources.json +1852 -0
  481. package/third_party/filter-lists/ublock-unbreak.txt +6026 -0
@@ -0,0 +1,1118 @@
1
+ //! Coverage analysis with helper recursion.
2
+ //!
3
+ //! For each `#[coverage_checked]` method (and each helper indexed from
4
+ //! visitor.rs), we compute the set of R-reaching fields covered on every
5
+ //! control-flow path that exits the method. Coverage is contributed by:
6
+ //!
7
+ //! - The marker macros `walk_all!(it)`, `walk_field!(it.foo)`,
8
+ //! `skip_field!(it.foo, "…")`.
9
+ //! - Bare `walk::walk_*(self, it)` / `walk::walk_*(self, &it.foo)` calls.
10
+ //! - Calls to helper methods that the index has proven to fully cover
11
+ //! their AST arguments — the call credits the corresponding field at
12
+ //! the call site (via origin tracking on local bindings).
13
+ //!
14
+ //! Control flow:
15
+ //! - sequential stmts → union
16
+ //! - if/else → intersect at merge (no-else branch contributes nothing)
17
+ //! - match → intersect across arms
18
+ //! - return / unreachable / panic / ? → terminate; check coverage there
19
+ //! - loops → body might run 0 times; conservative = body's covers don't
20
+ //! count toward post-loop coverage. (But field credits FROM the loop
21
+ //! itself — i.e. covering `it.foo` because the loop iterates `it.foo`
22
+ //! and processes every element — DO count.)
23
+
24
+ use std::collections::{BTreeSet, HashMap};
25
+
26
+ use proc_macro2::TokenStream;
27
+ use syn::{Block, Expr, ExprForLoop, ExprIf, ExprMatch, Pat, Stmt};
28
+
29
+ use crate::helper_index::{
30
+ Binding, HelperInfo, Origin, OriginMap, iter_element_type, origin_from_iter, resolve_origin,
31
+ resolve_type,
32
+ };
33
+ use crate::reachability::AstGraph;
34
+
35
+ #[derive(Clone, Debug)]
36
+ pub struct Covered {
37
+ pub all: bool,
38
+ pub fields: BTreeSet<String>,
39
+ }
40
+
41
+ impl Covered {
42
+ pub fn empty() -> Self { Self { all: false, fields: BTreeSet::new() } }
43
+ pub fn full() -> Self { Self { all: true, fields: BTreeSet::new() } }
44
+ pub fn add_field(&mut self, name: &str) { self.fields.insert(name.to_string()); }
45
+ pub fn intersect(a: &Self, b: &Self) -> Self {
46
+ if a.all && b.all { return Self::full(); }
47
+ let af: BTreeSet<String> = if a.all { b.fields.clone() } else { a.fields.clone() };
48
+ let bf: BTreeSet<String> = if b.all { a.fields.clone() } else { b.fields.clone() };
49
+ Self { all: a.all && b.all, fields: af.intersection(&bf).cloned().collect() }
50
+ }
51
+ pub fn covers(&self, name: &str) -> bool {
52
+ self.all || self.fields.iter().any(|f| f == name)
53
+ }
54
+ }
55
+
56
+ #[derive(Clone, Debug)]
57
+ pub struct Witness {
58
+ pub kind: PathKind,
59
+ pub covered: Covered,
60
+ }
61
+
62
+ #[derive(Clone, Debug)]
63
+ pub enum PathKind { FallThrough, Return }
64
+
65
+ #[derive(Default)]
66
+ pub struct Findings {
67
+ pub missing: Vec<MissingCover>,
68
+ pub bad_skips: Vec<BadSkip>,
69
+ }
70
+
71
+ #[derive(Clone, Debug)]
72
+ pub struct MissingCover {
73
+ pub field: String,
74
+ pub field_ty: String,
75
+ pub path_label: String,
76
+ }
77
+
78
+ #[derive(Clone, Debug)]
79
+ pub struct BadSkip {
80
+ pub field: String,
81
+ pub field_ty: String,
82
+ pub site_repr: String,
83
+ }
84
+
85
+ /// Backward-compatible entry — used when no helper index is available.
86
+ pub fn analyze(body: &Block, _findings: &mut Findings, graph: &AstGraph) -> Vec<Witness> {
87
+ let helpers = HashMap::new();
88
+ let origins = OriginMap::new();
89
+ analyze_with_helpers_typed(body, "it", None, &helpers, graph, origins)
90
+ }
91
+
92
+ pub fn analyze_with_helpers(
93
+ body: &Block,
94
+ it_name: &str,
95
+ helpers: &HashMap<String, HelperInfo>,
96
+ graph: &AstGraph,
97
+ origins: OriginMap,
98
+ ) -> Vec<Witness> {
99
+ analyze_with_helpers_typed(body, it_name, None, helpers, graph, origins)
100
+ }
101
+
102
+ pub fn analyze_with_helpers_typed(
103
+ body: &Block,
104
+ it_name: &str,
105
+ it_node_type: Option<&str>,
106
+ helpers: &HashMap<String, HelperInfo>,
107
+ graph: &AstGraph,
108
+ mut origins: OriginMap,
109
+ ) -> Vec<Witness> {
110
+ // Seed `it`'s binding with its type so resolve_type / enum-coverage on
111
+ // `it`-rooted expressions can find the right NodeDef.
112
+ if let Some(t) = it_node_type {
113
+ origins.set_binding(it_name, Binding::it(Some(t.to_string())));
114
+ }
115
+ let mut completed: Vec<Witness> = Vec::new();
116
+ let ctx = Ctx { it_name, it_node_type, helpers, graph };
117
+ let final_cov = walk_block(body, Covered::empty(), origins, &mut completed, &ctx);
118
+ completed.push(Witness { kind: PathKind::FallThrough, covered: final_cov });
119
+ completed
120
+ }
121
+
122
+ struct Ctx<'a> {
123
+ it_name: &'a str,
124
+ it_node_type: Option<&'a str>,
125
+ helpers: &'a HashMap<String, HelperInfo>,
126
+ graph: &'a AstGraph,
127
+ }
128
+
129
+ fn walk_block(
130
+ block: &Block,
131
+ mut cov: Covered,
132
+ mut origins: OriginMap,
133
+ completed: &mut Vec<Witness>,
134
+ ctx: &Ctx,
135
+ ) -> Covered {
136
+ for stmt in &block.stmts {
137
+ let out = walk_stmt(stmt, cov.clone(), &mut origins, completed, ctx);
138
+ match out {
139
+ Out::Continue(c) => cov = c,
140
+ Out::Terminated => return Covered::full(),
141
+ }
142
+ }
143
+ cov
144
+ }
145
+
146
+ enum Out { Continue(Covered), Terminated }
147
+
148
+ fn walk_stmt(
149
+ stmt: &Stmt,
150
+ cov: Covered,
151
+ origins: &mut OriginMap,
152
+ completed: &mut Vec<Witness>,
153
+ ctx: &Ctx,
154
+ ) -> Out {
155
+ match stmt {
156
+ Stmt::Expr(e, _) => walk_expr_stmt(e, cov, origins, completed, ctx),
157
+ Stmt::Local(loc) => {
158
+ if let Some(init) = &loc.init {
159
+ let (new_cov, terminated) = inspect_expr(&init.expr, cov.clone(), origins, completed, ctx);
160
+ if let Pat::Ident(pid) = &loc.pat {
161
+ let name = pid.ident.to_string();
162
+ let origin = resolve_origin(&init.expr, origins, ctx.it_name);
163
+ let ty = resolve_type(&init.expr, origins, ctx.it_name, ctx.it_node_type, ctx.graph);
164
+ origins.set_binding(&name, Binding { origin, ast_type: ty });
165
+ }
166
+ if terminated { return Out::Terminated; }
167
+ return Out::Continue(new_cov);
168
+ }
169
+ Out::Continue(cov)
170
+ }
171
+ Stmt::Item(_) => Out::Continue(cov),
172
+ Stmt::Macro(m) => Out::Continue(handle_macro_invocation(&m.mac, cov)),
173
+ }
174
+ }
175
+
176
+ fn walk_expr_stmt(
177
+ expr: &Expr,
178
+ cov: Covered,
179
+ origins: &mut OriginMap,
180
+ completed: &mut Vec<Witness>,
181
+ ctx: &Ctx,
182
+ ) -> Out {
183
+ let (new_cov, terminated) = inspect_expr(expr, cov, origins, completed, ctx);
184
+ if terminated { Out::Terminated } else { Out::Continue(new_cov) }
185
+ }
186
+
187
+ fn inspect_expr(
188
+ expr: &Expr,
189
+ mut cov: Covered,
190
+ origins: &mut OriginMap,
191
+ completed: &mut Vec<Witness>,
192
+ ctx: &Ctx,
193
+ ) -> (Covered, bool) {
194
+ match expr {
195
+ Expr::Return(_) => {
196
+ completed.push(Witness { kind: PathKind::Return, covered: cov });
197
+ (Covered::full(), true)
198
+ }
199
+ Expr::Break(_) | Expr::Continue(_) => {
200
+ // Abnormal terminator — control leaves the current arm/loop
201
+ // body without producing a fall-through value. We don't record
202
+ // a coverage witness, because the caller never observes a
203
+ // partially-rewritten state through this path: any rewrites
204
+ // already pushed into the bag are still emitted; nothing
205
+ // downstream from here would have run.
206
+ (Covered::full(), true)
207
+ }
208
+ Expr::Try(t) => {
209
+ // `expr?` — the Err path returns Err to the function caller
210
+ // without observable partial state at this point (rewrites
211
+ // already pushed remain). Don't record a witness; continue
212
+ // analyzing the Ok path through the inner expression.
213
+ return inspect_expr(&t.expr, cov, origins, completed, ctx);
214
+ }
215
+ Expr::Macro(m) => {
216
+ if is_terminating_macro(&m.mac) {
217
+ // panic!() / unreachable!() / todo!() / unimplemented!() —
218
+ // function aborts. Same as Break/Continue: no observable
219
+ // partial state, no witness recorded.
220
+ return (Covered::full(), true);
221
+ }
222
+ (handle_macro_invocation(&m.mac, cov), false)
223
+ }
224
+ Expr::If(eif) => (walk_if(eif, cov, origins, completed, ctx), false),
225
+ Expr::Match(em) => (walk_match(em, cov, origins, completed, ctx), false),
226
+ Expr::Block(b) => (walk_block(&b.block, cov, origins.clone(), completed, ctx), false),
227
+ Expr::Unsafe(b) => (walk_block(&b.block, cov, origins.clone(), completed, ctx), false),
228
+ Expr::ForLoop(efl) => {
229
+ cov = walk_for_loop(efl, cov, origins.clone(), completed, ctx);
230
+ (cov, false)
231
+ }
232
+ Expr::While(ew) => {
233
+ // While-body may run zero times. Don't merge body cov into outer.
234
+ // But field credits emitted directly by the loop iterator (e.g.
235
+ // `for v in &it.foo`) DO count via walk_for_loop. While doesn't
236
+ // have that pattern, so just walk the body for its own assertions.
237
+ let _ = walk_block(&ew.body, cov.clone(), origins.clone(), completed, ctx);
238
+ (cov, false)
239
+ }
240
+ Expr::Loop(el) => {
241
+ let _ = walk_block(&el.body, cov.clone(), origins.clone(), completed, ctx);
242
+ (cov, false)
243
+ }
244
+ Expr::Call(call) => {
245
+ cov = handle_call(call, cov, origins, ctx);
246
+ (cov, false)
247
+ }
248
+ Expr::MethodCall(mc) => {
249
+ cov = handle_method_call(mc, cov, origins, ctx);
250
+ (cov, false)
251
+ }
252
+ Expr::Let(_) => (cov, false),
253
+ Expr::Assign(a) => {
254
+ let (c, _) = inspect_expr(&a.right, cov, origins, completed, ctx);
255
+ (c, false)
256
+ }
257
+ _ => (cov, false),
258
+ }
259
+ }
260
+
261
+ fn walk_if(
262
+ eif: &ExprIf,
263
+ cov: Covered,
264
+ origins: &mut OriginMap,
265
+ completed: &mut Vec<Witness>,
266
+ ctx: &Ctx,
267
+ ) -> Covered {
268
+ // Detect `if let Pat = scrutinee { ... }` and update origins inside then.
269
+ let mut then_origins = origins.clone();
270
+ let mut scrut_field: Option<String> = None;
271
+ let mut scrut_type: Option<String> = None;
272
+ let mut scrut_origin = Origin::Unknown;
273
+ let mut scrut_variant: Option<String> = None;
274
+ let mut scrut_expr_for_if_let: Option<&Expr> = None;
275
+ if let Expr::Let(el) = eif.cond.as_ref() {
276
+ bind_pattern_origins(&el.pat, &el.expr, &mut then_origins, ctx);
277
+ scrut_origin = resolve_origin(&el.expr, origins, ctx.it_name);
278
+ if let Origin::Field(name) = &scrut_origin {
279
+ scrut_field = Some(name.clone());
280
+ }
281
+ scrut_type = resolve_type(&el.expr, origins, ctx.it_name, ctx.it_node_type, ctx.graph);
282
+ scrut_variant = pattern_variant_name(&el.pat, ctx);
283
+ scrut_expr_for_if_let = Some(&el.expr);
284
+ }
285
+ let then_cov = walk_block(&eif.then_branch, cov.clone(), then_origins, completed, ctx);
286
+ // Enum-aware if-let-else: when cond is `if let EnumType::Variant(v) = scrut`
287
+ // and else handles the remaining variants, treat the whole if-else as
288
+ // covering the enum.
289
+ if let (Some(variant), Some(ty), Some(scrut_e)) =
290
+ (&scrut_variant, &scrut_type, scrut_expr_for_if_let)
291
+ {
292
+ if let Some(def) = ctx.graph.nodes.get(ty.as_str()) {
293
+ if !def.variants.is_empty() {
294
+ // Variant covered iff then-branch covers it (we check by
295
+ // seeing if the then-branch's coverage of v's binding type
296
+ // is complete — equivalent to running an arm-coverage check).
297
+ let variant_covered = if_let_variant_covered(
298
+ &then_cov,
299
+ &eif.then_branch,
300
+ variant,
301
+ scrut_e,
302
+ ctx,
303
+ );
304
+
305
+ // Other variants covered iff else-branch fully covers them.
306
+ let other_variants_covered = else_covers_other_variants(
307
+ eif.else_branch.as_ref().map(|(_, e)| e.as_ref()),
308
+ ty.as_str(),
309
+ variant,
310
+ scrut_e,
311
+ cov.clone(),
312
+ origins,
313
+ completed,
314
+ ctx,
315
+ );
316
+
317
+ if variant_covered && other_variants_covered {
318
+ let mut c = cov;
319
+ // Also retain whatever the then-branch added beyond
320
+ // variant-level (e.g. unrelated walks).
321
+ if then_cov.all {
322
+ c.all = true;
323
+ } else {
324
+ for f in &then_cov.fields {
325
+ c.fields.insert(f.clone());
326
+ }
327
+ }
328
+ if let Some(field_name) = &scrut_field {
329
+ c.add_field(field_name);
330
+ } else if matches!(scrut_origin, Origin::It) {
331
+ c.all = true;
332
+ }
333
+ return c;
334
+ }
335
+ }
336
+ }
337
+ }
338
+
339
+ match &eif.else_branch {
340
+ Some((_, e)) => {
341
+ let else_cov = match e.as_ref() {
342
+ Expr::Block(b) => {
343
+ walk_block(&b.block, cov.clone(), origins.clone(), completed, ctx)
344
+ }
345
+ Expr::If(inner) => walk_if(inner, cov.clone(), origins, completed, ctx),
346
+ _ => cov.clone(),
347
+ };
348
+ Covered::intersect(&then_cov, &else_cov)
349
+ }
350
+ None => {
351
+ if let Some(f) = &scrut_field {
352
+ let mut c = then_cov;
353
+ c.add_field(f);
354
+ c
355
+ } else {
356
+ cov
357
+ }
358
+ }
359
+ }
360
+ }
361
+
362
+ /// Extract the variant name from an if-let pattern. Returns None for
363
+ /// non-variant patterns (Some(_), generic Or-patterns, etc.).
364
+ fn pattern_variant_name(pat: &Pat, ctx: &Ctx) -> Option<String> {
365
+ match pat {
366
+ Pat::TupleStruct(ts) => {
367
+ let last = ts.path.segments.last()?.ident.to_string();
368
+ // Only return a variant name we know to be an AST type — that
369
+ // way `Some(...)` (where `Some` isn't in our table) doesn't
370
+ // trigger enum-aware coverage.
371
+ if ctx.graph.nodes.contains_key(last.as_str()) {
372
+ Some(last)
373
+ } else {
374
+ None
375
+ }
376
+ }
377
+ Pat::Struct(s) => {
378
+ let last = s.path.segments.last()?.ident.to_string();
379
+ if ctx.graph.nodes.contains_key(last.as_str()) {
380
+ Some(last)
381
+ } else {
382
+ None
383
+ }
384
+ }
385
+ Pat::Reference(r) => pattern_variant_name(&r.pat, ctx),
386
+ Pat::Paren(p) => pattern_variant_name(&p.pat, ctx),
387
+ _ => None,
388
+ }
389
+ }
390
+
391
+ fn if_let_variant_covered(
392
+ then_cov: &Covered,
393
+ then_block: &Block,
394
+ variant: &str,
395
+ scrut_expr: &Expr,
396
+ ctx: &Ctx,
397
+ ) -> bool {
398
+ // If the then-block's coverage of the outer it includes this variant
399
+ // name (credited by a helper that fully covers the variant's type) OR
400
+ // cov.all is set, the variant is covered.
401
+ if then_cov.all { return true; }
402
+ if then_cov.fields.iter().any(|n| n == variant) { return true; }
403
+ // Fallback: scan for a helper call on the scrutinee that covers this
404
+ // variant (handles `if let V(_) = scrut { self.h(scrut) }` patterns).
405
+ if scan_block_for_helper_on(then_block, scrut_expr, variant, ctx) {
406
+ return true;
407
+ }
408
+ false
409
+ }
410
+
411
+ fn else_covers_other_variants(
412
+ else_expr: Option<&Expr>,
413
+ enum_ty: &str,
414
+ matched_variant: &str,
415
+ scrut_expr: &Expr,
416
+ cov: Covered,
417
+ origins: &mut OriginMap,
418
+ completed: &mut Vec<Witness>,
419
+ ctx: &Ctx,
420
+ ) -> bool {
421
+ let Some(else_expr) = else_expr else { return false };
422
+ let Some(def) = ctx.graph.nodes.get(enum_ty) else { return false };
423
+
424
+ // Run the else-branch analysis to gather its coverage / variant credits.
425
+ let else_block = match else_expr {
426
+ Expr::Block(b) => b.block.clone(),
427
+ Expr::If(inner) => {
428
+ // chained `else if let ...` — wrap as a block stmt and recurse.
429
+ syn::Block {
430
+ brace_token: Default::default(),
431
+ stmts: vec![Stmt::Expr(Expr::If(inner.clone()), None)],
432
+ }
433
+ }
434
+ other => syn::Block {
435
+ brace_token: Default::default(),
436
+ stmts: vec![Stmt::Expr(other.clone(), None)],
437
+ },
438
+ };
439
+ let else_cov = walk_block(&else_block, cov, origins.clone(), completed, ctx);
440
+
441
+ // For each other R-variant of the enum, check whether it's covered by
442
+ // the else branch — either:
443
+ // - else_cov.all
444
+ // - else_cov.fields contains the variant name (variant-level credit
445
+ // bubbled up from an inner match's enum-aware analysis)
446
+ // - the else body has a helper call on scrut_expr whose covered_set
447
+ // contains the variant.
448
+ for v in def.variants {
449
+ if !ctx.graph.in_r(v) { continue; }
450
+ if *v == matched_variant { continue; }
451
+ if else_cov.all { continue; }
452
+ if else_cov.fields.iter().any(|n| n == v) { continue; }
453
+ if scan_block_for_helper_on(&else_block, scrut_expr, v, ctx) { continue; }
454
+ return false;
455
+ }
456
+ true
457
+ }
458
+
459
+ fn scan_block_for_helper_on(
460
+ block: &Block,
461
+ scrut_expr: &Expr,
462
+ variant: &str,
463
+ ctx: &Ctx,
464
+ ) -> bool {
465
+ let scrut_repr = expr_canonical(scrut_expr);
466
+ let mut hit = false;
467
+ for stmt in &block.stmts {
468
+ if let Stmt::Expr(e, _) = stmt {
469
+ scan_helper_calls(e, &mut |mc| {
470
+ let Expr::Path(p) = mc.receiver.as_ref() else { return };
471
+ if !p.path.is_ident("self") { return; }
472
+ let name = mc.method.to_string();
473
+ let Some(info) = ctx.helpers.get(&name) else { return };
474
+ if !info.covered_set.contains(variant) && !info.covered_all { return; }
475
+ let Some(arg0) = mc.args.first() else { return };
476
+ if expr_canonical(arg0) == scrut_repr || ref_canonical(arg0) == scrut_repr {
477
+ hit = true;
478
+ }
479
+ });
480
+ }
481
+ if hit { break; }
482
+ }
483
+ hit
484
+ }
485
+
486
+ fn walk_match(
487
+ em: &ExprMatch,
488
+ cov: Covered,
489
+ origins: &mut OriginMap,
490
+ completed: &mut Vec<Witness>,
491
+ ctx: &Ctx,
492
+ ) -> Covered {
493
+ // Enum-aware coverage. The scrutinee can be:
494
+ // - `&it.<field>` → if field's type is an enum, full match coverage
495
+ // credits the field name in `base`.
496
+ // - `it` itself (Origin::It) → if `it`'s type is an enum, full match
497
+ // coverage promotes `base.all = true`.
498
+ // - any local with a known enum AST type (via Binding's `ast_type`)
499
+ // → same as Origin::It case at that local's level.
500
+ let scrut_origin = resolve_origin(&em.expr, origins, ctx.it_name);
501
+ let scrut_field = match &scrut_origin {
502
+ Origin::Field(name) => Some(name.clone()),
503
+ _ => None,
504
+ };
505
+ let scrut_type =
506
+ resolve_type(&em.expr, origins, ctx.it_name, ctx.it_node_type, ctx.graph);
507
+
508
+ let mut arm_covs = Vec::new();
509
+ for arm in &em.arms {
510
+ let mut arm_origins = origins.clone();
511
+ bind_pattern_origins(&arm.pat, &em.expr, &mut arm_origins, ctx);
512
+ let mut arm_cov = cov.clone();
513
+ match arm.body.as_ref() {
514
+ Expr::Block(b) => {
515
+ arm_cov = walk_block(&b.block, arm_cov, arm_origins, completed, ctx);
516
+ }
517
+ other => {
518
+ let (c, terminated) = inspect_expr(other, arm_cov.clone(), &mut arm_origins, completed, ctx);
519
+ arm_cov = if terminated { Covered::full() } else { c };
520
+ }
521
+ }
522
+ arm_covs.push(arm_cov);
523
+ }
524
+
525
+ let mut base = {
526
+ let mut it = arm_covs.clone().into_iter();
527
+ let first = it.next().unwrap_or_else(Covered::full);
528
+ it.fold(first, |acc, c| Covered::intersect(&acc, &c))
529
+ };
530
+
531
+ // Enum-aware promotion using the scrutinee's actual AST type.
532
+ if let Some(t) = scrut_type.as_deref() {
533
+ if let Some(def) = ctx.graph.nodes.get(t) {
534
+ if !def.variants.is_empty() {
535
+ let mut all_covered = true;
536
+ for v in def.variants {
537
+ if !ctx.graph.in_r(v) {
538
+ continue;
539
+ }
540
+ let arm = find_arm_for_variant(em, v);
541
+ let covered = arm
542
+ .map(|a| arm_covers_variant(a, v, &em.expr, ctx))
543
+ .unwrap_or(false);
544
+ if covered {
545
+ base.add_field(v);
546
+ } else {
547
+ all_covered = false;
548
+ }
549
+ }
550
+ if all_covered {
551
+ if let Some(field_name) = &scrut_field {
552
+ base.add_field(field_name);
553
+ } else if matches!(scrut_origin, Origin::It) {
554
+ base.all = true;
555
+ }
556
+ }
557
+ }
558
+ }
559
+ }
560
+
561
+ base
562
+ }
563
+
564
+
565
+ #[allow(dead_code)]
566
+ fn check_match_covers_enum(
567
+ em: &ExprMatch,
568
+ enum_def: &crate::ast_table::NodeDef,
569
+ ctx: &Ctx,
570
+ ) -> bool {
571
+ use crate::ast_table::NodeDef;
572
+
573
+ // Build the set of R-variants we need to cover.
574
+ let mut needed: Vec<&str> = Vec::new();
575
+ for v in enum_def.variants {
576
+ if ctx.graph.in_r(v) {
577
+ needed.push(*v);
578
+ }
579
+ }
580
+
581
+ // For each variant, find an arm that matches it and check its body covers V.
582
+ for variant in &needed {
583
+ let arm = find_arm_for_variant(em, variant);
584
+ let Some(arm) = arm else { return false; };
585
+ if !arm_covers_variant(arm, variant, &em.expr, ctx) {
586
+ return false;
587
+ }
588
+ }
589
+ let _: &NodeDef = enum_def;
590
+ true
591
+ }
592
+
593
+ /// Find the first arm whose pattern matches `variant`. A wildcard arm
594
+ /// matches everything.
595
+ fn find_arm_for_variant<'a>(em: &'a ExprMatch, variant: &str) -> Option<&'a syn::Arm> {
596
+ for arm in &em.arms {
597
+ if arm_matches_variant(&arm.pat, variant) {
598
+ return Some(arm);
599
+ }
600
+ }
601
+ None
602
+ }
603
+
604
+ fn arm_matches_variant(pat: &Pat, variant: &str) -> bool {
605
+ match pat {
606
+ Pat::Wild(_) => true,
607
+ Pat::Ident(p) if p.ident == "_" => true,
608
+ Pat::TupleStruct(ts) => {
609
+ // Path like `T::Variant(_)` or `Variant(_)`.
610
+ ts.path
611
+ .segments
612
+ .last()
613
+ .map(|s| s.ident == variant)
614
+ .unwrap_or(false)
615
+ }
616
+ Pat::Path(p) => p
617
+ .path
618
+ .segments
619
+ .last()
620
+ .map(|s| s.ident == variant)
621
+ .unwrap_or(false),
622
+ Pat::Struct(s) => s
623
+ .path
624
+ .segments
625
+ .last()
626
+ .map(|s| s.ident == variant)
627
+ .unwrap_or(false),
628
+ Pat::Or(o) => o.cases.iter().any(|c| arm_matches_variant(c, variant)),
629
+ Pat::Paren(p) => arm_matches_variant(&p.pat, variant),
630
+ Pat::Reference(r) => arm_matches_variant(&r.pat, variant),
631
+ _ => false,
632
+ }
633
+ }
634
+
635
+ fn arm_covers_variant(
636
+ arm: &syn::Arm,
637
+ variant: &str,
638
+ scrut_expr: &Expr,
639
+ ctx: &Ctx,
640
+ ) -> bool {
641
+ let v_def = ctx.graph.nodes.get(variant);
642
+ let binding = extract_arm_binding(&arm.pat);
643
+
644
+ // Wildcard / multi-variant arm with no specific binding: the body might
645
+ // still cover the variant if it (a) is trivially-covered by virtue of
646
+ // having no R-content, (b) calls a helper on the original scrutinee that
647
+ // covers this variant, or (c) contains `audit_skip!("...")` documenting
648
+ // a runtime-safe cull.
649
+ if binding.is_none() {
650
+ if let Some(d) = v_def {
651
+ let has_r_field = d.fields.iter().any(|f| ctx.graph.field_in_r(f));
652
+ let has_r_variant = d.variants.iter().any(|v| ctx.graph.in_r(v));
653
+ if !has_r_field && !has_r_variant {
654
+ return true;
655
+ }
656
+ }
657
+ // Run a mini analyze on the arm body so audit_skip!/walks on the
658
+ // outer scope (rare) are picked up. cov.all suffices.
659
+ let body_block = match arm.body.as_ref() {
660
+ Expr::Block(b) => b.block.clone(),
661
+ other => syn::Block {
662
+ brace_token: Default::default(),
663
+ stmts: vec![Stmt::Expr(other.clone(), None)],
664
+ },
665
+ };
666
+ let paths = analyze_with_helpers_typed(
667
+ &body_block,
668
+ ctx.it_name,
669
+ ctx.it_node_type,
670
+ ctx.helpers,
671
+ ctx.graph,
672
+ OriginMap::new(),
673
+ );
674
+ if paths.iter().all(|p| p.covered.all) {
675
+ return true;
676
+ }
677
+ // Otherwise fall back to the helper-on-scrutinee check.
678
+ return body_covers_variant_via_helper(arm, scrut_expr, variant, ctx);
679
+ }
680
+
681
+ let bname = binding.unwrap();
682
+ let Some(v_def) = v_def else { return false };
683
+
684
+ // Run a mini analyze on the arm body, with `bname` as the new "it" and
685
+ // v_def as the target type. This credits walks/helpers on `bname`.
686
+ let body_block = match arm.body.as_ref() {
687
+ Expr::Block(b) => b.block.clone(),
688
+ other => syn::Block {
689
+ brace_token: Default::default(),
690
+ stmts: vec![Stmt::Expr(other.clone(), None)],
691
+ },
692
+ };
693
+ let mut origins = OriginMap::new();
694
+ origins.set_binding(&bname, Binding::it(Some(variant.to_string())));
695
+ let paths = analyze_with_helpers_typed(
696
+ &body_block,
697
+ &bname,
698
+ Some(variant),
699
+ ctx.helpers,
700
+ ctx.graph,
701
+ origins,
702
+ );
703
+
704
+ for p in &paths {
705
+ if !p.covered.all {
706
+ if v_def.fields.is_empty() && !v_def.variants.is_empty() {
707
+ return false;
708
+ }
709
+ for f in v_def.fields {
710
+ if !ctx.graph.field_in_r(f) {
711
+ continue;
712
+ }
713
+ if !p.covered.fields.iter().any(|n| n == f.name) {
714
+ return false;
715
+ }
716
+ }
717
+ }
718
+ }
719
+ true
720
+ }
721
+
722
+ /// Scan an arm's body for a `self.helper(scrut_expr)` call where the helper's
723
+ /// covered_set contains `variant`. This is what makes
724
+ /// StaticMember(_) | ComputedMember(_) => self.handle_assignment_target_member(target)
725
+ /// cover those variants when `target` IS the match scrutinee.
726
+ fn body_covers_variant_via_helper(
727
+ arm: &syn::Arm,
728
+ scrut_expr: &Expr,
729
+ variant: &str,
730
+ ctx: &Ctx,
731
+ ) -> bool {
732
+ // Compare expressions structurally for "same as scrut_expr."
733
+ let scrut_repr = expr_canonical(scrut_expr);
734
+ let body = arm.body.as_ref();
735
+ let mut hit = false;
736
+ scan_helper_calls(body, &mut |mc| {
737
+ // Receiver must be `self`.
738
+ let Expr::Path(p) = mc.receiver.as_ref() else { return };
739
+ if !p.path.is_ident("self") { return; }
740
+ let name = mc.method.to_string();
741
+ let Some(info) = ctx.helpers.get(&name) else { return };
742
+ // The helper must cover the variant.
743
+ if !info.covered_set.contains(variant) && !info.covered_all { return; }
744
+ // First arg must be the scrutinee (or an &-ref to it).
745
+ let Some(arg0) = mc.args.first() else { return };
746
+ if expr_canonical(arg0) == scrut_repr || ref_canonical(arg0) == scrut_repr {
747
+ hit = true;
748
+ }
749
+ });
750
+ hit
751
+ }
752
+
753
+ fn expr_canonical(e: &Expr) -> String {
754
+ use quote::ToTokens;
755
+ e.to_token_stream().to_string()
756
+ }
757
+
758
+ fn ref_canonical(e: &Expr) -> String {
759
+ if let Expr::Reference(r) = e {
760
+ return expr_canonical(&r.expr);
761
+ }
762
+ expr_canonical(e)
763
+ }
764
+
765
+ fn scan_helper_calls<F: FnMut(&syn::ExprMethodCall)>(e: &Expr, f: &mut F) {
766
+ match e {
767
+ Expr::MethodCall(m) => {
768
+ f(m);
769
+ scan_helper_calls(&m.receiver, f);
770
+ for a in &m.args { scan_helper_calls(a, f); }
771
+ }
772
+ Expr::Call(c) => {
773
+ scan_helper_calls(&c.func, f);
774
+ for a in &c.args { scan_helper_calls(a, f); }
775
+ }
776
+ Expr::Block(b) => {
777
+ for s in &b.block.stmts {
778
+ if let Stmt::Expr(e, _) = s { scan_helper_calls(e, f); }
779
+ }
780
+ }
781
+ Expr::If(eif) => {
782
+ for s in &eif.then_branch.stmts {
783
+ if let Stmt::Expr(e, _) = s { scan_helper_calls(e, f); }
784
+ }
785
+ if let Some((_, els)) = &eif.else_branch { scan_helper_calls(els, f); }
786
+ }
787
+ Expr::Match(em) => {
788
+ for arm in &em.arms { scan_helper_calls(&arm.body, f); }
789
+ }
790
+ Expr::Paren(p) => scan_helper_calls(&p.expr, f),
791
+ Expr::Reference(r) => scan_helper_calls(&r.expr, f),
792
+ _ => {}
793
+ }
794
+ }
795
+
796
+ fn extract_arm_binding(pat: &Pat) -> Option<String> {
797
+ match pat {
798
+ Pat::Ident(p) if p.ident == "_" => None,
799
+ Pat::Ident(p) => Some(p.ident.to_string()),
800
+ Pat::TupleStruct(ts) => {
801
+ if let Some(inner) = ts.elems.first() {
802
+ return extract_arm_binding(inner);
803
+ }
804
+ None
805
+ }
806
+ Pat::Tuple(t) => {
807
+ if let Some(inner) = t.elems.first() {
808
+ return extract_arm_binding(inner);
809
+ }
810
+ None
811
+ }
812
+ Pat::Struct(s) => {
813
+ if let Some(f) = s.fields.first() {
814
+ return extract_arm_binding(&f.pat);
815
+ }
816
+ None
817
+ }
818
+ Pat::Reference(r) => extract_arm_binding(&r.pat),
819
+ Pat::Paren(p) => extract_arm_binding(&p.pat),
820
+ Pat::Wild(_) => None,
821
+ _ => None,
822
+ }
823
+ }
824
+
825
+ fn walk_for_loop(
826
+ efl: &ExprForLoop,
827
+ mut cov: Covered,
828
+ mut origins: OriginMap,
829
+ completed: &mut Vec<Witness>,
830
+ ctx: &Ctx,
831
+ ) -> Covered {
832
+ // Bind loop var → origin & element-type of iter.
833
+ if let Pat::Ident(pid) = efl.pat.as_ref() {
834
+ let name = pid.ident.to_string();
835
+ let origin = origin_from_iter(&efl.expr, &origins, ctx.it_name);
836
+ let ty = iter_element_type(&efl.expr, &origins, ctx.it_name, ctx.it_node_type, ctx.graph);
837
+ origins.set_binding(&name, Binding { origin, ast_type: ty });
838
+ }
839
+ // The loop body runs over EVERY element. If the body fully covers each
840
+ // element, the parent field IS covered. We detect this by running the
841
+ // body and observing whether any field credits from `it.<field>` appear
842
+ // via the loop variable's origin propagating to walks/helpers inside.
843
+ let body_cov = walk_block(&efl.body, Covered::empty(), origins.clone(), completed, ctx);
844
+ // Promote body covers up: anything credited in the body during this loop
845
+ // applies to the outer cov as well (since the iteration covers ALL items
846
+ // when the loop iterates over an `it.<field>` collection).
847
+ if body_cov.all {
848
+ cov.all = true;
849
+ } else {
850
+ for f in &body_cov.fields {
851
+ cov.fields.insert(f.clone());
852
+ }
853
+ }
854
+ cov
855
+ }
856
+
857
+ /// Bind locals from a pattern matched against a scrutinee expression. Used
858
+ /// for `if let`, `match`, and similar. Handles common pattern shapes and
859
+ /// derives variant types from `EnumPath::Variant(binding)` shapes.
860
+ fn bind_pattern_origins(
861
+ pat: &Pat,
862
+ scrut: &Expr,
863
+ origins: &mut OriginMap,
864
+ ctx: &Ctx,
865
+ ) {
866
+ let scrut_origin = resolve_origin(scrut, origins, ctx.it_name);
867
+ let scrut_type = resolve_type(scrut, origins, ctx.it_name, ctx.it_node_type, ctx.graph);
868
+ bind_pat_with(pat, &scrut_origin, scrut_type.as_deref(), origins, ctx);
869
+ }
870
+
871
+ fn bind_pat_with(
872
+ pat: &Pat,
873
+ origin: &Origin,
874
+ scrut_ty: Option<&str>,
875
+ origins: &mut OriginMap,
876
+ ctx: &Ctx,
877
+ ) {
878
+ match pat {
879
+ Pat::Ident(p) => {
880
+ origins.set_binding(
881
+ &p.ident.to_string(),
882
+ Binding { origin: origin.clone(), ast_type: scrut_ty.map(String::from) },
883
+ );
884
+ if let Some((_, sub)) = &p.subpat {
885
+ bind_pat_with(sub, origin, scrut_ty, origins, ctx);
886
+ }
887
+ }
888
+ Pat::Reference(r) => bind_pat_with(&r.pat, origin, scrut_ty, origins, ctx),
889
+ Pat::TupleStruct(ts) => {
890
+ // Variant pattern: the binding(s) inside the tuple-struct carry
891
+ // the variant's type, not the enum's type.
892
+ let variant_name = ts.path.segments.last().map(|s| s.ident.to_string());
893
+ let variant_ty = variant_name
894
+ .as_deref()
895
+ .filter(|n| ctx.graph.nodes.contains_key(*n))
896
+ .map(String::from);
897
+ let inner_ty = variant_ty.as_deref().or(scrut_ty);
898
+ for elem in &ts.elems {
899
+ bind_pat_with(elem, origin, inner_ty, origins, ctx);
900
+ }
901
+ }
902
+ Pat::Tuple(t) => {
903
+ for elem in &t.elems {
904
+ bind_pat_with(elem, origin, scrut_ty, origins, ctx);
905
+ }
906
+ }
907
+ Pat::Struct(s) => {
908
+ let variant_name = s.path.segments.last().map(|s| s.ident.to_string());
909
+ let variant_ty = variant_name
910
+ .as_deref()
911
+ .filter(|n| ctx.graph.nodes.contains_key(*n))
912
+ .map(String::from);
913
+ let inner_ty = variant_ty.as_deref().or(scrut_ty);
914
+ for f in &s.fields {
915
+ bind_pat_with(&f.pat, origin, inner_ty, origins, ctx);
916
+ }
917
+ }
918
+ Pat::Or(o) => {
919
+ for case in &o.cases {
920
+ bind_pat_with(case, origin, scrut_ty, origins, ctx);
921
+ }
922
+ }
923
+ Pat::Paren(p) => bind_pat_with(&p.pat, origin, scrut_ty, origins, ctx),
924
+ Pat::Slice(s) => {
925
+ for elem in &s.elems {
926
+ bind_pat_with(elem, origin, scrut_ty, origins, ctx);
927
+ }
928
+ }
929
+ _ => {}
930
+ }
931
+ }
932
+
933
+ fn handle_call(call: &syn::ExprCall, mut cov: Covered, origins: &OriginMap, ctx: &Ctx) -> Covered {
934
+ // Recognize `walk::walk_*(self, target)` patterns.
935
+ let Expr::Path(p) = call.func.as_ref() else { return cov };
936
+ let segs: Vec<String> = p.path.segments.iter().map(|s| s.ident.to_string()).collect();
937
+ if segs.len() < 2 || segs[0] != "walk" || !segs[1].starts_with("walk_") {
938
+ return cov;
939
+ }
940
+ if call.args.len() < 2 { return cov; }
941
+ let target = &call.args[1];
942
+ apply_origin_to_cov_typed(
943
+ target,
944
+ origins,
945
+ ctx.it_name,
946
+ ctx.it_node_type,
947
+ Some(ctx.graph),
948
+ &mut cov,
949
+ true,
950
+ );
951
+ cov
952
+ }
953
+
954
+ fn handle_method_call(
955
+ mc: &syn::ExprMethodCall,
956
+ mut cov: Covered,
957
+ origins: &OriginMap,
958
+ ctx: &Ctx,
959
+ ) -> Covered {
960
+ // self.helper(args) — receiver must be `self`.
961
+ let Expr::Path(p) = mc.receiver.as_ref() else { return cov };
962
+ if !p.path.is_ident("self") { return cov; }
963
+ let name = mc.method.to_string();
964
+ let Some(info) = ctx.helpers.get(&name) else { return cov };
965
+ // For each AST-typed arg the helper tracks, look up the positional call
966
+ // arg by its `call_pos` and credit if the helper fully covers it.
967
+ let call_args: Vec<&Expr> = mc.args.iter().collect();
968
+ for ha in &info.args {
969
+ if !ha.fully_covers { continue; }
970
+ let Some(call_arg) = call_args.get(ha.call_pos) else { continue };
971
+ apply_origin_to_cov_typed(
972
+ call_arg,
973
+ origins,
974
+ ctx.it_name,
975
+ ctx.it_node_type,
976
+ Some(ctx.graph),
977
+ &mut cov,
978
+ true,
979
+ );
980
+ }
981
+ cov
982
+ }
983
+
984
+ fn apply_origin_to_cov(
985
+ e: &Expr,
986
+ origins: &OriginMap,
987
+ it_name: &str,
988
+ cov: &mut Covered,
989
+ all_if_root: bool,
990
+ ) {
991
+ apply_origin_to_cov_typed(e, origins, it_name, None, None, cov, all_if_root);
992
+ }
993
+
994
+ /// Type-aware variant. When the expression has Origin::It but its AST type
995
+ /// differs from the caller's `it_node_type`, we know we're walking a
996
+ /// *variant* of the caller's it (typical pattern: an `if let
997
+ /// EnumPath::Variant(v) = it` extracts a v of variant type — calling a
998
+ /// helper that fully covers v doesn't cover ALL variants of the outer it,
999
+ /// just the one we destructured).
1000
+ fn apply_origin_to_cov_typed(
1001
+ e: &Expr,
1002
+ origins: &OriginMap,
1003
+ it_name: &str,
1004
+ it_node_type: Option<&str>,
1005
+ graph: Option<&AstGraph>,
1006
+ cov: &mut Covered,
1007
+ all_if_root: bool,
1008
+ ) {
1009
+ let o = resolve_origin(e, origins, it_name);
1010
+ match o {
1011
+ Origin::It => {
1012
+ if !all_if_root { return; }
1013
+ // Try to figure out the arg's type. If it's the same as the
1014
+ // outer `it`'s type, credit cov.all. If it's a R-variant of the
1015
+ // outer `it`'s enum type, credit that variant name.
1016
+ let arg_ty = graph.and_then(|g| {
1017
+ crate::helper_index::resolve_type(e, origins, it_name, it_node_type, g)
1018
+ });
1019
+ match (arg_ty.as_deref(), it_node_type) {
1020
+ (Some(at), Some(ot)) if at == ot => cov.all = true,
1021
+ (Some(at), Some(ot)) => {
1022
+ // If `at` is listed as a variant of `ot` in our table,
1023
+ // we covered just that variant.
1024
+ let is_variant = graph
1025
+ .and_then(|g| g.nodes.get(ot))
1026
+ .map(|d| d.variants.iter().any(|v| *v == at))
1027
+ .unwrap_or(false);
1028
+ if is_variant {
1029
+ cov.add_field(at);
1030
+ } else {
1031
+ // Mismatched type but not a known variant — be
1032
+ // conservative and credit cov.all (preserves the
1033
+ // earlier behavior).
1034
+ cov.all = true;
1035
+ }
1036
+ }
1037
+ _ => cov.all = true,
1038
+ }
1039
+ }
1040
+ Origin::Field(name) => cov.add_field(&name),
1041
+ Origin::Unknown => {}
1042
+ }
1043
+ }
1044
+
1045
+ fn is_terminating_macro(mac: &syn::Macro) -> bool {
1046
+ let name = mac
1047
+ .path
1048
+ .segments
1049
+ .last()
1050
+ .map(|s| s.ident.to_string())
1051
+ .unwrap_or_default();
1052
+ matches!(name.as_str(), "panic" | "unreachable" | "todo" | "unimplemented")
1053
+ }
1054
+
1055
+ fn handle_macro_invocation(mac: &syn::Macro, mut cov: Covered) -> Covered {
1056
+ let name = mac.path.segments.iter().last().map(|s| s.ident.to_string()).unwrap_or_default();
1057
+ match name.as_str() {
1058
+ "walk_all" => cov.all = true,
1059
+ "walk_field" | "walk_field_ctx" | "skip_field" => {
1060
+ if let Some(f) = extract_field_name(&mac.tokens) {
1061
+ cov.add_field(&f);
1062
+ }
1063
+ }
1064
+ "audit_skip" => {
1065
+ // Two forms:
1066
+ // audit_skip!(it.<field>, "reason") — credits a specific field
1067
+ // audit_skip!("reason") — credits the entire current
1068
+ // scope (use inside the
1069
+ // match arm / branch where
1070
+ // the cull actually happens)
1071
+ if let Some(f) = extract_field_name(&mac.tokens) {
1072
+ cov.add_field(&f);
1073
+ } else {
1074
+ cov.all = true;
1075
+ }
1076
+ }
1077
+ _ => {}
1078
+ }
1079
+ cov
1080
+ }
1081
+
1082
+ pub fn extract_field_name(ts: &TokenStream) -> Option<String> {
1083
+ use proc_macro2::TokenTree;
1084
+ let toks: Vec<TokenTree> = ts.clone().into_iter().collect();
1085
+ let mut last: Option<String> = None;
1086
+ let mut i = 0;
1087
+ while i + 2 < toks.len() {
1088
+ if let (TokenTree::Ident(a), TokenTree::Punct(p), TokenTree::Ident(c)) =
1089
+ (&toks[i], &toks[i + 1], &toks[i + 2])
1090
+ {
1091
+ if a == "it" && p.as_char() == '.' {
1092
+ last = Some(c.to_string());
1093
+ i += 3;
1094
+ continue;
1095
+ }
1096
+ }
1097
+ i += 1;
1098
+ }
1099
+ last
1100
+ }
1101
+
1102
+ pub fn extract_receiver_and_field(ts: &TokenStream) -> Option<(syn::Ident, String)> {
1103
+ use proc_macro2::TokenTree;
1104
+ let toks: Vec<TokenTree> = ts.clone().into_iter().collect();
1105
+ let mut found = None;
1106
+ let mut i = 0;
1107
+ while i + 2 < toks.len() {
1108
+ if let (TokenTree::Ident(a), TokenTree::Punct(p), TokenTree::Ident(c)) =
1109
+ (&toks[i], &toks[i + 1], &toks[i + 2])
1110
+ {
1111
+ if p.as_char() == '.' {
1112
+ found = Some((a.clone(), c.to_string()));
1113
+ }
1114
+ }
1115
+ i += 1;
1116
+ }
1117
+ found
1118
+ }