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.
- package/LICENSE +661 -0
- package/README.md +123 -0
- package/SOURCE.md +29 -0
- package/THIRD_PARTY_NOTICES.md +24 -0
- package/dist/assets/filters-Bu_3nHVy.bin +0 -0
- package/dist/assets/index-BdRlQ7d_.css +1 -0
- package/dist/assets/index-DcvPYWS3.js +3621 -0
- package/dist/assets/inject-BnVzMVpl.js +40 -0
- package/dist/assets/scramjet-C9HLwI-y.wasm +0 -0
- package/dist/defaultfavicon.png +0 -0
- package/dist/icon.png +0 -0
- package/dist/roxxie-icon.svg +12 -0
- package/embed.js +70 -0
- package/examples/single-page.html +25 -0
- package/package.json +42 -0
- package/runtime/controller.sw.js +273 -0
- package/runtime/index.html +18 -0
- package/runtime/localcontrollersw.js +7 -0
- package/runtime/roxxie-network.json +13 -0
- package/runtime/sw.js +6 -0
- package/scripts/build.mjs +344 -0
- package/scripts/smoke.mjs +123 -0
- package/scripts/verify.mjs +122 -0
- package/source/external/dreamlandjs/LICENSE +21 -0
- package/source/external/dreamlandjs/README.md +22 -0
- package/source/external/dreamlandjs/dist/core.d.ts +167 -0
- package/source/external/dreamlandjs/dist/core.js +2 -0
- package/source/external/dreamlandjs/dist/core.js.map +1 -0
- package/source/external/dreamlandjs/dist/core.size.html +4950 -0
- package/source/external/dreamlandjs/dist/js-runtime.d.ts +3 -0
- package/source/external/dreamlandjs/dist/js-runtime.js +2 -0
- package/source/external/dreamlandjs/dist/js-runtime.js.map +1 -0
- package/source/external/dreamlandjs/dist/jsx-runtime.d.ts +1 -0
- package/source/external/dreamlandjs/dist/jsx-runtime.js +2 -0
- package/source/external/dreamlandjs/dist/jsx-runtime.js.map +1 -0
- package/source/external/dreamlandjs/dist/motion.d.ts +19 -0
- package/source/external/dreamlandjs/dist/motion.js +2 -0
- package/source/external/dreamlandjs/dist/motion.js.map +1 -0
- package/source/external/dreamlandjs/dist/router.d.ts +48 -0
- package/source/external/dreamlandjs/dist/router.js +2 -0
- package/source/external/dreamlandjs/dist/router.js.map +1 -0
- package/source/external/dreamlandjs/dist/ssr.client.d.ts +3 -0
- package/source/external/dreamlandjs/dist/ssr.client.js +2 -0
- package/source/external/dreamlandjs/dist/ssr.client.js.map +1 -0
- package/source/external/dreamlandjs/dist/ssr.client.size.html +4950 -0
- package/source/external/dreamlandjs/dist/ssr.hybrid.d.ts +7 -0
- package/source/external/dreamlandjs/dist/ssr.hybrid.js +2 -0
- package/source/external/dreamlandjs/dist/ssr.hybrid.js.map +1 -0
- package/source/external/dreamlandjs/dist/ssr.hybrid.size.html +4950 -0
- package/source/external/dreamlandjs/dist/ssr.server.d.ts +11 -0
- package/source/external/dreamlandjs/dist/ssr.server.js +2373 -0
- package/source/external/dreamlandjs/dist/ssr.server.js.map +1 -0
- package/source/external/dreamlandjs/dist/types/core/consts.d.ts +16 -0
- package/source/external/dreamlandjs/dist/types/core/css/index.d.ts +11 -0
- package/source/external/dreamlandjs/dist/types/core/css/selectorParser.d.ts +50 -0
- package/source/external/dreamlandjs/dist/types/core/delegate.d.ts +10 -0
- package/source/external/dreamlandjs/dist/types/core/index.d.ts +9 -0
- package/source/external/dreamlandjs/dist/types/core/jsx/child.d.ts +2 -0
- package/source/external/dreamlandjs/dist/types/core/jsx/definitions.d.ts +76 -0
- package/source/external/dreamlandjs/dist/types/core/jsx/dom.d.ts +28 -0
- package/source/external/dreamlandjs/dist/types/core/jsx/index.d.ts +14 -0
- package/source/external/dreamlandjs/dist/types/core/state/index.d.ts +1 -0
- package/source/external/dreamlandjs/dist/types/core/state/pointers.d.ts +37 -0
- package/source/external/dreamlandjs/dist/types/core/state/state.d.ts +13 -0
- package/source/external/dreamlandjs/dist/types/core/state/use.d.ts +11 -0
- package/source/external/dreamlandjs/dist/types/core/utils.d.ts +4 -0
- package/source/external/dreamlandjs/dist/types/js-runtime/index.d.ts +1 -0
- package/source/external/dreamlandjs/dist/types/jsx-runtime/index.d.ts +2 -0
- package/source/external/dreamlandjs/dist/types/motion/index.d.ts +15 -0
- package/source/external/dreamlandjs/dist/types/router/index.d.ts +45 -0
- package/source/external/dreamlandjs/dist/types/ssr/client/index.d.ts +1 -0
- package/source/external/dreamlandjs/dist/types/ssr/common/consts.d.ts +4 -0
- package/source/external/dreamlandjs/dist/types/ssr/common/serialize.d.ts +4 -0
- package/source/external/dreamlandjs/dist/types/ssr/common/types.d.ts +21 -0
- package/source/external/dreamlandjs/dist/types/ssr/hybrid/index.d.ts +4 -0
- package/source/external/dreamlandjs/dist/types/ssr/server/index.d.ts +7 -0
- package/source/external/dreamlandjs/dist/types/ssr/server/vdom.d.ts +71 -0
- package/source/external/dreamlandjs/dist/types/util/index.d.ts +1 -0
- package/source/external/dreamlandjs/dist/types/util/store.d.ts +26 -0
- package/source/external/dreamlandjs/dist/types/vite/css-minifier.d.ts +7 -0
- package/source/external/dreamlandjs/dist/types/vite/index.d.ts +11 -0
- package/source/external/dreamlandjs/dist/util.d.ts +29 -0
- package/source/external/dreamlandjs/dist/util.js +2 -0
- package/source/external/dreamlandjs/dist/util.js.map +1 -0
- package/source/external/dreamlandjs/dist/vite.d.ts +19 -0
- package/source/external/dreamlandjs/dist/vite.js +2 -0
- package/source/external/dreamlandjs/dist/vite.js.map +1 -0
- package/source/external/dreamlandjs/package.json +101 -0
- package/source/external/dreamlandjs/rollup.config.ts +302 -0
- package/source/external/dreamlandjs/rollup.plugins.ts +1134 -0
- package/source/external/dreamlandjs/src/core/consts.d.ts +16 -0
- package/source/external/dreamlandjs/src/core/consts.js +18 -0
- package/source/external/dreamlandjs/src/core/css/index.ts +111 -0
- package/source/external/dreamlandjs/src/core/css/selectorParser.ts +313 -0
- package/source/external/dreamlandjs/src/core/delegate.ts +28 -0
- package/source/external/dreamlandjs/src/core/index.ts +31 -0
- package/source/external/dreamlandjs/src/core/jsx/child.ts +91 -0
- package/source/external/dreamlandjs/src/core/jsx/definitions.ts +125 -0
- package/source/external/dreamlandjs/src/core/jsx/dom.ts +49 -0
- package/source/external/dreamlandjs/src/core/jsx/index.ts +289 -0
- package/source/external/dreamlandjs/src/core/state/index.ts +1 -0
- package/source/external/dreamlandjs/src/core/state/pointers.ts +310 -0
- package/source/external/dreamlandjs/src/core/state/state.ts +112 -0
- package/source/external/dreamlandjs/src/core/state/use.ts +82 -0
- package/source/external/dreamlandjs/src/core/utils.ts +48 -0
- package/source/external/dreamlandjs/src/js-runtime/index.ts +4 -0
- package/source/external/dreamlandjs/src/jsx-runtime/index.ts +3 -0
- package/source/external/dreamlandjs/src/motion/index.ts +156 -0
- package/source/external/dreamlandjs/src/router/index.tsx +394 -0
- package/source/external/dreamlandjs/src/router/tsconfig.json +9 -0
- package/source/external/dreamlandjs/src/ssr/client/index.ts +193 -0
- package/source/external/dreamlandjs/src/ssr/common/consts.ts +5 -0
- package/source/external/dreamlandjs/src/ssr/common/serialize.ts +115 -0
- package/source/external/dreamlandjs/src/ssr/common/types.ts +28 -0
- package/source/external/dreamlandjs/src/ssr/hybrid/index.ts +144 -0
- package/source/external/dreamlandjs/src/ssr/server/index.ts +157 -0
- package/source/external/dreamlandjs/src/ssr/server/tsconfig.json +7 -0
- package/source/external/dreamlandjs/src/ssr/server/vdom.ts +285 -0
- package/source/external/dreamlandjs/src/util/index.ts +10 -0
- package/source/external/dreamlandjs/src/util/store.ts +134 -0
- package/source/external/dreamlandjs/src/vite/css-minifier.ts +222 -0
- package/source/external/dreamlandjs/src/vite/index.ts +60 -0
- package/source/external/dreamlandjs/src/vite/tsconfig.json +7 -0
- package/source/external/dreamlandjs/tsconfig.json +15 -0
- package/source/package.json +48 -0
- package/source/packages/adrift-protocol/package.json +31 -0
- package/source/packages/adrift-protocol/src/index.ts +348 -0
- package/source/packages/adrift-protocol/tsconfig.json +18 -0
- package/source/packages/cdp/LICENSE +21 -0
- package/source/packages/cdp/README.md +47 -0
- package/source/packages/cdp/dist/chobitsu.js +2 -0
- package/source/packages/cdp/dist/chobitsu.js.map +1 -0
- package/source/packages/cdp/package.json +73 -0
- package/source/packages/cdp/script/build.js +15 -0
- package/source/packages/cdp/src/Chobitsu.ts +110 -0
- package/source/packages/cdp/src/domains/CSS.ts +378 -0
- package/source/packages/cdp/src/domains/CacheStorage.ts +8 -0
- package/source/packages/cdp/src/domains/DOM.ts +487 -0
- package/source/packages/cdp/src/domains/DOMDebugger.ts +109 -0
- package/source/packages/cdp/src/domains/DOMStorage.ts +122 -0
- package/source/packages/cdp/src/domains/Debugger.ts +25 -0
- package/source/packages/cdp/src/domains/IndexedDB.ts +207 -0
- package/source/packages/cdp/src/domains/Input.ts +177 -0
- package/source/packages/cdp/src/domains/Network.ts +251 -0
- package/source/packages/cdp/src/domains/Overlay.ts +230 -0
- package/source/packages/cdp/src/domains/Page.ts +296 -0
- package/source/packages/cdp/src/domains/Runtime.ts +273 -0
- package/source/packages/cdp/src/domains/Storage.ts +71 -0
- package/source/packages/cdp/src/index.d.ts +8 -0
- package/source/packages/cdp/src/index.ts +153 -0
- package/source/packages/cdp/src/lib/connector.ts +15 -0
- package/source/packages/cdp/src/lib/constants.ts +1 -0
- package/source/packages/cdp/src/lib/evaluate.ts +59 -0
- package/source/packages/cdp/src/lib/mutationObserver.ts +39 -0
- package/source/packages/cdp/src/lib/nodeManager.ts +142 -0
- package/source/packages/cdp/src/lib/objManager.ts +422 -0
- package/source/packages/cdp/src/lib/request.ts +313 -0
- package/source/packages/cdp/src/lib/resources.ts +59 -0
- package/source/packages/cdp/src/lib/scriptMananger.ts +62 -0
- package/source/packages/cdp/src/lib/stylesheet.ts +146 -0
- package/source/packages/cdp/src/lib/util.ts +105 -0
- package/source/packages/cdp/tsconfig.json +16 -0
- package/source/packages/cdp/webpack.config.js +48 -0
- package/source/packages/chrome/index.html +17 -0
- package/source/packages/chrome/package.json +41 -0
- package/source/packages/chrome/public/ROXXIE_NETWORK.md +22 -0
- package/source/packages/chrome/public/controller.sw.js +273 -0
- package/source/packages/chrome/public/defaultfavicon.png +0 -0
- package/source/packages/chrome/public/icon.png +0 -0
- package/source/packages/chrome/public/localcontrollersw.js +7 -0
- package/source/packages/chrome/public/roxxie-icon.svg +12 -0
- package/source/packages/chrome/public/roxxie-network.json +13 -0
- package/source/packages/chrome/public/sw.js +6 -0
- package/source/packages/chrome/src/App.tsx +269 -0
- package/source/packages/chrome/src/CDP.ts +285 -0
- package/source/packages/chrome/src/Tab/History.ts +186 -0
- package/source/packages/chrome/src/Tab/Tab.tsx +219 -0
- package/source/packages/chrome/src/Tab/screenshot.ts +66 -0
- package/source/packages/chrome/src/Tab/serialize.ts +221 -0
- package/source/packages/chrome/src/adblock/AdBlockPlugin.test.ts +159 -0
- package/source/packages/chrome/src/adblock/AdBlockPlugin.ts +232 -0
- package/source/packages/chrome/src/adblock/AdBlockService.test.ts +148 -0
- package/source/packages/chrome/src/adblock/AdBlockService.ts +166 -0
- package/source/packages/chrome/src/adblock/NOTICE.md +14 -0
- package/source/packages/chrome/src/adblock/assets/filters.bin +0 -0
- package/source/packages/chrome/src/adblock/index.ts +18 -0
- package/source/packages/chrome/src/assets/favicon.ts +4 -0
- package/source/packages/chrome/src/components/BookmarkPopup.tsx +101 -0
- package/source/packages/chrome/src/components/BookmarksStrip.tsx +150 -0
- package/source/packages/chrome/src/components/Button.tsx +99 -0
- package/source/packages/chrome/src/components/Checkbox.tsx +89 -0
- package/source/packages/chrome/src/components/DownloadsPopup.tsx +230 -0
- package/source/packages/chrome/src/components/Favicon.tsx +65 -0
- package/source/packages/chrome/src/components/Icon.tsx +29 -0
- package/source/packages/chrome/src/components/Input.tsx +82 -0
- package/source/packages/chrome/src/components/Menu.tsx +287 -0
- package/source/packages/chrome/src/components/Omnibar/BookmarkButton.tsx +73 -0
- package/source/packages/chrome/src/components/Omnibar/CircularProgress.tsx +83 -0
- package/source/packages/chrome/src/components/Omnibar/Omnibar.tsx +455 -0
- package/source/packages/chrome/src/components/Omnibar/OmnibarButton.tsx +48 -0
- package/source/packages/chrome/src/components/Omnibar/Omnibox.tsx +466 -0
- package/source/packages/chrome/src/components/Omnibar/SiteOptionsButton.tsx +52 -0
- package/source/packages/chrome/src/components/Omnibar/Suggestion.tsx +186 -0
- package/source/packages/chrome/src/components/Omnibar/UrlInput.tsx +198 -0
- package/source/packages/chrome/src/components/Omnibar/suggestions.ts +468 -0
- package/source/packages/chrome/src/components/Omnibar/utils.ts +11 -0
- package/source/packages/chrome/src/components/ReportBrokenSiteModal.tsx +279 -0
- package/source/packages/chrome/src/components/Shell.tsx +191 -0
- package/source/packages/chrome/src/components/SiteInformationPopup.tsx +96 -0
- package/source/packages/chrome/src/components/SmallIconButton.tsx +36 -0
- package/source/packages/chrome/src/components/StartScreen.tsx +386 -0
- package/source/packages/chrome/src/components/TabStrip/DragTab.tsx +546 -0
- package/source/packages/chrome/src/components/TabStrip/Sidebar.tsx +820 -0
- package/source/packages/chrome/src/components/TabStrip/TabHoverCard.tsx +87 -0
- package/source/packages/chrome/src/components/TabStrip/TabStrip.tsx +510 -0
- package/source/packages/chrome/src/components/TopSiteButton.tsx +234 -0
- package/source/packages/chrome/src/consts.ts +3 -0
- package/source/packages/chrome/src/icons.ts +70 -0
- package/source/packages/chrome/src/index.ts +168 -0
- package/source/packages/chrome/src/migrations/0-from-legacy.ts +6 -0
- package/source/packages/chrome/src/migrations/1-vertical-tab-settings.ts +15 -0
- package/source/packages/chrome/src/migrations/index.ts +42 -0
- package/source/packages/chrome/src/pages/AboutPage.tsx +37 -0
- package/source/packages/chrome/src/pages/DownloadsPage.tsx +133 -0
- package/source/packages/chrome/src/pages/HistoryPage.tsx +99 -0
- package/source/packages/chrome/src/pages/NewTabPage.tsx +264 -0
- package/source/packages/chrome/src/pages/PlaygroundPage.tsx +43 -0
- package/source/packages/chrome/src/pages/SettingsPage.tsx +1931 -0
- package/source/packages/chrome/src/proxy/Controller.ts +331 -0
- package/source/packages/chrome/src/proxy/ProxyFrame.ts +26 -0
- package/source/packages/chrome/src/proxy/cache.ts +487 -0
- package/source/packages/chrome/src/proxy/codec.ts +11 -0
- package/source/packages/chrome/src/proxy/contextitems.ts +180 -0
- package/source/packages/chrome/src/proxy/index.ts +5 -0
- package/source/packages/chrome/src/proxy/roxxie.ts +85 -0
- package/source/packages/chrome/src/proxy/scramjet.ts +642 -0
- package/source/packages/chrome/src/reset.css +124 -0
- package/source/packages/chrome/src/runtime-base.ts +16 -0
- package/source/packages/chrome/src/sentry.tsx +45 -0
- package/source/packages/chrome/src/services/DownloadsService.ts +136 -0
- package/source/packages/chrome/src/services/FaviconService.ts +161 -0
- package/source/packages/chrome/src/services/KVWrapper.ts +169 -0
- package/source/packages/chrome/src/services/ProfileService.ts +94 -0
- package/source/packages/chrome/src/services/Service.ts +10 -0
- package/source/packages/chrome/src/services/SettingsService.ts +108 -0
- package/source/packages/chrome/src/services/TabsService.ts +161 -0
- package/source/packages/chrome/src/startup.test.ts +129 -0
- package/source/packages/chrome/src/startup.ts +161 -0
- package/source/packages/chrome/src/style.css +229 -0
- package/source/packages/chrome/src/themes.ts +756 -0
- package/source/packages/chrome/src/util/StatefulClass.ts +31 -0
- package/source/packages/chrome/src/util/index.ts +74 -0
- package/source/packages/chrome/src/util/memoize.ts +13 -0
- package/source/packages/chrome/src/vite-env.d.ts +16 -0
- package/source/packages/chrome/tsconfig.json +30 -0
- package/source/packages/chrome/vite.config.ts +47 -0
- package/source/packages/inject/package.json +20 -0
- package/source/packages/inject/src/context.ts +194 -0
- package/source/packages/inject/src/emulators/alwaysLastBubble.ts +168 -0
- package/source/packages/inject/src/emulators/anchors.ts +55 -0
- package/source/packages/inject/src/emulators/contextmenu.ts +46 -0
- package/source/packages/inject/src/emulators/history.ts +51 -0
- package/source/packages/inject/src/emulators/titlewatcher.ts +42 -0
- package/source/packages/inject/src/emulators/windowopen.ts +134 -0
- package/source/packages/inject/src/errorpage/errorpage.css +234 -0
- package/source/packages/inject/src/errorpage/errorpage.html +29 -0
- package/source/packages/inject/src/errorpage/errorpage.ts +76 -0
- package/source/packages/inject/src/index.ts +62 -0
- package/source/packages/inject/src/transport.ts +97 -0
- package/source/packages/inject/src/types.d.ts +136 -0
- package/source/packages/roxxie-transport/package.json +36 -0
- package/source/packages/roxxie-transport/src/binary.ts +398 -0
- package/source/packages/roxxie-transport/src/config.ts +254 -0
- package/source/packages/roxxie-transport/src/connection.ts +767 -0
- package/source/packages/roxxie-transport/src/index.ts +5 -0
- package/source/packages/roxxie-transport/src/tracker.ts +651 -0
- package/source/packages/roxxie-transport/src/transport.ts +414 -0
- package/source/packages/roxxie-transport/tsconfig.json +18 -0
- package/source/packages/scramjet/packages/controller/package.json +37 -0
- package/source/packages/scramjet/packages/controller/src/index.ts +861 -0
- package/source/packages/scramjet/packages/controller/src/inject.ts +365 -0
- package/source/packages/scramjet/packages/controller/src/sw.ts +231 -0
- package/source/packages/scramjet/packages/controller/src/symbols.ts +1 -0
- package/source/packages/scramjet/packages/controller/src/types.d.ts +140 -0
- package/source/packages/scramjet/packages/controller/src/typesEntry.ts +6 -0
- package/source/packages/scramjet/packages/controller/src/version.ts +30 -0
- package/source/packages/scramjet/packages/controller/tsconfig.json +24 -0
- package/source/packages/scramjet/packages/core/dist/scramjet-external.mjs +57 -0
- package/source/packages/scramjet/packages/core/dist/scramjet.js +13599 -0
- package/source/packages/scramjet/packages/core/dist/scramjet.js.map +1 -0
- package/source/packages/scramjet/packages/core/dist/scramjet.mjs +13649 -0
- package/source/packages/scramjet/packages/core/dist/scramjet.mjs.map +1 -0
- package/source/packages/scramjet/packages/core/dist/scramjet.wasm +0 -0
- package/source/packages/scramjet/packages/core/dist/scramjet_bundled.js +13601 -0
- package/source/packages/scramjet/packages/core/dist/scramjet_bundled.js.map +1 -0
- package/source/packages/scramjet/packages/core/dist/scramjet_bundled.mjs +13651 -0
- package/source/packages/scramjet/packages/core/dist/scramjet_bundled.mjs.map +1 -0
- package/source/packages/scramjet/packages/core/dist/temp-types-build/index.js +23 -0
- package/source/packages/scramjet/packages/core/dist/temp-types-build/index.js.map +1 -0
- package/source/packages/scramjet/packages/core/dist/types/Tap.d.ts +39 -0
- package/source/packages/scramjet/packages/core/dist/types/client/client.d.ts +115 -0
- package/source/packages/scramjet/packages/core/dist/types/client/entry.d.ts +5 -0
- package/source/packages/scramjet/packages/core/dist/types/client/events.d.ts +10 -0
- package/source/packages/scramjet/packages/core/dist/types/client/helpers.d.ts +1 -0
- package/source/packages/scramjet/packages/core/dist/types/client/index.d.ts +7 -0
- package/source/packages/scramjet/packages/core/dist/types/client/location.d.ts +2 -0
- package/source/packages/scramjet/packages/core/dist/types/client/shared/eval.d.ts +3 -0
- package/source/packages/scramjet/packages/core/dist/types/client/shared/sourcemaps.d.ts +19 -0
- package/source/packages/scramjet/packages/core/dist/types/client/shared/wrap.d.ts +4 -0
- package/source/packages/scramjet/packages/core/dist/types/client/singletonbox.d.ts +18 -0
- package/source/packages/scramjet/packages/core/dist/types/fetch/body.d.ts +3 -0
- package/source/packages/scramjet/packages/core/dist/types/fetch/fetch.d.ts +7 -0
- package/source/packages/scramjet/packages/core/dist/types/fetch/headers.d.ts +19 -0
- package/source/packages/scramjet/packages/core/dist/types/fetch/index.d.ts +128 -0
- package/source/packages/scramjet/packages/core/dist/types/fetch/parse.d.ts +23 -0
- package/source/packages/scramjet/packages/core/dist/types/fetch/util.d.ts +7 -0
- package/source/packages/scramjet/packages/core/dist/types/index.d.ts +12 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/cookie.d.ts +26 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/headers.d.ts +13 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/htmlRules.d.ts +6 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/index.d.ts +50 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/mime.d.ts +53 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/refresh.d.ts +7 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/css.d.ts +4 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/html.d.ts +33 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/index.d.ts +6 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/js.d.ts +11 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/url.d.ts +24 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/wasm.d.ts +7 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/rewriters/worker.d.ts +3 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/set-cookie-parser.d.ts +20 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/snapshot.d.ts +186 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/sniffEncoding.d.ts +65 -0
- package/source/packages/scramjet/packages/core/dist/types/shared/util.d.ts +2 -0
- package/source/packages/scramjet/packages/core/dist/types/symbols.d.ts +6 -0
- package/source/packages/scramjet/packages/core/dist/types/types.d.ts +60 -0
- package/source/packages/scramjet/packages/core/dist/types/typesEntry.d.ts +5 -0
- package/source/packages/scramjet/packages/core/lib/index.cjs +7 -0
- package/source/packages/scramjet/packages/core/lib/index.d.ts +8 -0
- package/source/packages/scramjet/packages/core/package.json +69 -0
- package/source/packages/scramjet/packages/core/rewriter/Cargo.lock +1985 -0
- package/source/packages/scramjet/packages/core/rewriter/Cargo.toml +20 -0
- package/source/packages/scramjet/packages/core/rewriter/coverage-macro/Cargo.toml +15 -0
- package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/analyze.rs +1118 -0
- package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/ast_table.rs +927 -0
- package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/helper_index.rs +556 -0
- package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/lib.rs +455 -0
- package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/reachability.rs +85 -0
- package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/templates.rs +192 -0
- package/source/packages/scramjet/packages/core/rewriter/coverage-macro/src/witness.rs +597 -0
- package/source/packages/scramjet/packages/core/rewriter/js/Cargo.toml +18 -0
- package/source/packages/scramjet/packages/core/rewriter/js/src/cfg.rs +45 -0
- package/source/packages/scramjet/packages/core/rewriter/js/src/changes.rs +379 -0
- package/source/packages/scramjet/packages/core/rewriter/js/src/lib.rs +171 -0
- package/source/packages/scramjet/packages/core/rewriter/js/src/rewrite.rs +290 -0
- package/source/packages/scramjet/packages/core/rewriter/js/src/visitor.rs +967 -0
- package/source/packages/scramjet/packages/core/rewriter/native/Cargo.toml +22 -0
- package/source/packages/scramjet/packages/core/rewriter/native/src/main.rs +166 -0
- package/source/packages/scramjet/packages/core/rewriter/native/src/rewriter.rs +161 -0
- package/source/packages/scramjet/packages/core/rewriter/native/src/test_runner.rs +156 -0
- package/source/packages/scramjet/packages/core/rewriter/rust-toolchain.toml +5 -0
- package/source/packages/scramjet/packages/core/rewriter/rustfmt.toml +2 -0
- package/source/packages/scramjet/packages/core/rewriter/transform/Cargo.toml +16 -0
- package/source/packages/scramjet/packages/core/rewriter/transform/src/lib.rs +242 -0
- package/source/packages/scramjet/packages/core/rewriter/transform/src/transform.rs +123 -0
- package/source/packages/scramjet/packages/core/rewriter/wasm/Cargo.toml +22 -0
- package/source/packages/scramjet/packages/core/rewriter/wasm/build.sh +128 -0
- package/source/packages/scramjet/packages/core/rewriter/wasm/src/error.rs +56 -0
- package/source/packages/scramjet/packages/core/rewriter/wasm/src/jsr.rs +125 -0
- package/source/packages/scramjet/packages/core/rewriter/wasm/src/lib.rs +144 -0
- package/source/packages/scramjet/packages/core/src/Tap.ts +166 -0
- package/source/packages/scramjet/packages/core/src/client/client.ts +916 -0
- package/source/packages/scramjet/packages/core/src/client/dom/attr.ts +94 -0
- package/source/packages/scramjet/packages/core/src/client/dom/beacon.ts +11 -0
- package/source/packages/scramjet/packages/core/src/client/dom/cookie.ts +21 -0
- package/source/packages/scramjet/packages/core/src/client/dom/css.ts +106 -0
- package/source/packages/scramjet/packages/core/src/client/dom/document.ts +140 -0
- package/source/packages/scramjet/packages/core/src/client/dom/element.ts +783 -0
- package/source/packages/scramjet/packages/core/src/client/dom/fontface.ts +11 -0
- package/source/packages/scramjet/packages/core/src/client/dom/fragments.ts +32 -0
- package/source/packages/scramjet/packages/core/src/client/dom/history.ts +35 -0
- package/source/packages/scramjet/packages/core/src/client/dom/open.ts +59 -0
- package/source/packages/scramjet/packages/core/src/client/dom/origin.ts +13 -0
- package/source/packages/scramjet/packages/core/src/client/dom/performance.ts +50 -0
- package/source/packages/scramjet/packages/core/src/client/dom/protocol.ts +17 -0
- package/source/packages/scramjet/packages/core/src/client/dom/storage.ts +107 -0
- package/source/packages/scramjet/packages/core/src/client/entry.ts +12 -0
- package/source/packages/scramjet/packages/core/src/client/events.ts +10 -0
- package/source/packages/scramjet/packages/core/src/client/helpers.ts +7 -0
- package/source/packages/scramjet/packages/core/src/client/index.ts +7 -0
- package/source/packages/scramjet/packages/core/src/client/location.ts +139 -0
- package/source/packages/scramjet/packages/core/src/client/shared/antiantidebugger.ts +20 -0
- package/source/packages/scramjet/packages/core/src/client/shared/blob.ts +35 -0
- package/source/packages/scramjet/packages/core/src/client/shared/caches.ts +31 -0
- package/source/packages/scramjet/packages/core/src/client/shared/chrome.ts +90 -0
- package/source/packages/scramjet/packages/core/src/client/shared/err.ts +55 -0
- package/source/packages/scramjet/packages/core/src/client/shared/error.ts +42 -0
- package/source/packages/scramjet/packages/core/src/client/shared/eval.ts +40 -0
- package/source/packages/scramjet/packages/core/src/client/shared/event.ts +215 -0
- package/source/packages/scramjet/packages/core/src/client/shared/function.ts +59 -0
- package/source/packages/scramjet/packages/core/src/client/shared/import.ts +46 -0
- package/source/packages/scramjet/packages/core/src/client/shared/indexeddb.ts +18 -0
- package/source/packages/scramjet/packages/core/src/client/shared/opfs.ts +27 -0
- package/source/packages/scramjet/packages/core/src/client/shared/postmessage.ts +113 -0
- package/source/packages/scramjet/packages/core/src/client/shared/realm.ts +26 -0
- package/source/packages/scramjet/packages/core/src/client/shared/requests/eventsource.ts +17 -0
- package/source/packages/scramjet/packages/core/src/client/shared/requests/fetch.ts +67 -0
- package/source/packages/scramjet/packages/core/src/client/shared/requests/websocket.ts +389 -0
- package/source/packages/scramjet/packages/core/src/client/shared/requests/xmlhttprequest.ts +163 -0
- package/source/packages/scramjet/packages/core/src/client/shared/settimeout.ts +20 -0
- package/source/packages/scramjet/packages/core/src/client/shared/sourcemaps.ts +203 -0
- package/source/packages/scramjet/packages/core/src/client/shared/worker.ts +72 -0
- package/source/packages/scramjet/packages/core/src/client/shared/wrap.ts +197 -0
- package/source/packages/scramjet/packages/core/src/client/singletonbox.ts +52 -0
- package/source/packages/scramjet/packages/core/src/client/worker/importScripts.ts +13 -0
- package/source/packages/scramjet/packages/core/src/fetch/body.ts +93 -0
- package/source/packages/scramjet/packages/core/src/fetch/fetch.ts +323 -0
- package/source/packages/scramjet/packages/core/src/fetch/headers.ts +528 -0
- package/source/packages/scramjet/packages/core/src/fetch/index.ts +198 -0
- package/source/packages/scramjet/packages/core/src/fetch/parse.ts +149 -0
- package/source/packages/scramjet/packages/core/src/fetch/util.ts +81 -0
- package/source/packages/scramjet/packages/core/src/global.d.ts +32 -0
- package/source/packages/scramjet/packages/core/src/index.ts +78 -0
- package/source/packages/scramjet/packages/core/src/log.ts +117 -0
- package/source/packages/scramjet/packages/core/src/shared/cookie.ts +199 -0
- package/source/packages/scramjet/packages/core/src/shared/headers.ts +76 -0
- package/source/packages/scramjet/packages/core/src/shared/htmlRules.ts +154 -0
- package/source/packages/scramjet/packages/core/src/shared/index.ts +79 -0
- package/source/packages/scramjet/packages/core/src/shared/mime.ts +234 -0
- package/source/packages/scramjet/packages/core/src/shared/refresh.ts +164 -0
- package/source/packages/scramjet/packages/core/src/shared/rewriters/css.ts +67 -0
- package/source/packages/scramjet/packages/core/src/shared/rewriters/html.ts +603 -0
- package/source/packages/scramjet/packages/core/src/shared/rewriters/index.ts +6 -0
- package/source/packages/scramjet/packages/core/src/shared/rewriters/js.ts +158 -0
- package/source/packages/scramjet/packages/core/src/shared/rewriters/url.ts +248 -0
- package/source/packages/scramjet/packages/core/src/shared/rewriters/wasm.ts +58 -0
- package/source/packages/scramjet/packages/core/src/shared/rewriters/worker.ts +37 -0
- package/source/packages/scramjet/packages/core/src/shared/set-cookie-parser.ts +195 -0
- package/source/packages/scramjet/packages/core/src/shared/snapshot.ts +245 -0
- package/source/packages/scramjet/packages/core/src/shared/sniffEncoding.ts +1061 -0
- package/source/packages/scramjet/packages/core/src/shared/util.ts +35 -0
- package/source/packages/scramjet/packages/core/src/symbols.ts +9 -0
- package/source/packages/scramjet/packages/core/src/sync.ts +63 -0
- package/source/packages/scramjet/packages/core/src/types.ts +66 -0
- package/source/packages/scramjet/packages/core/src/typesEntry.ts +6 -0
- package/source/packages/scramjet/packages/core/tsconfig.json +21 -0
- package/source/packages/scramjet/packages/rpc/index.ts +100 -0
- package/source/packages/scramjet/packages/rpc/package.json +7 -0
- package/source/packages/scramjet/packages/utils/package.json +39 -0
- package/source/packages/scramjet/packages/utils/src/alwaysLastBubble.ts +172 -0
- package/source/packages/scramjet/packages/utils/src/catch-escaped-links.ts +34 -0
- package/source/packages/scramjet/packages/utils/src/event-handler-plugin.ts +86 -0
- package/source/packages/scramjet/packages/utils/src/global.d.ts +8 -0
- package/source/packages/scramjet/packages/utils/src/http-cache-plugin.ts +475 -0
- package/source/packages/scramjet/packages/utils/src/index.ts +26 -0
- package/source/packages/scramjet/packages/utils/src/link-handler-plugin.ts +75 -0
- package/source/packages/scramjet/packages/utils/src/typesEntry.ts +5 -0
- package/source/packages/scramjet/packages/utils/src/url-watcher.ts +37 -0
- package/source/packages/scramjet/packages/utils/src/version.ts +40 -0
- package/source/packages/scramjet/packages/utils/tsconfig.json +11 -0
- package/source/packages/scramjet/patches/htmlparser2@12.0.0.patch +124 -0
- package/source/packages/scramjet/rspack.config.ts +613 -0
- package/source/packages/scramjet/tsconfig.json +11 -0
- package/source/packages/tracker-protocol/package.json +31 -0
- package/source/packages/tracker-protocol/src/index.ts +745 -0
- package/source/packages/tracker-protocol/tsconfig.json +18 -0
- package/source/pnpm-lock.yaml +12018 -0
- package/source/pnpm-workspace.yaml +31 -0
- package/source/rspack.config.ts +103 -0
- package/source/scripts/update-adblock-assets.mjs +164 -0
- package/third_party/filter-lists/LICENSE-CC-BY-SA-3.0.txt +359 -0
- package/third_party/filter-lists/LICENSE-GPL-3.0.txt +674 -0
- package/third_party/filter-lists/LICENSE-MPL-2.0.txt +375 -0
- package/third_party/filter-lists/README.md +24 -0
- package/third_party/filter-lists/easylist.txt +83088 -0
- package/third_party/filter-lists/easyprivacy.txt +56361 -0
- package/third_party/filter-lists/manifest.json +56 -0
- package/third_party/filter-lists/ublock-filters.txt +10937 -0
- package/third_party/filter-lists/ublock-privacy.txt +3209 -0
- package/third_party/filter-lists/ublock-resources.json +1852 -0
- 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
|
+
}
|