diff --git a/CHANGELOG.md b/CHANGELOG.md index 67c600c9c..f535501a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ##### Enhancements - Added support for Bazel 9.x. +- Significant performance improvements. Scanning the Reddit iOS codebase reduced by 80.4% from 77.2s to 15.1s. ##### Bug Fixes diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index c8c4adfe8..a67817f22 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -240,6 +240,123 @@ } } }, + "@@pybind11_bazel+//:python_configure.bzl%extension": { + "general": { + "bzlTransitiveDigest": "c9ZWWeXeu6bctL4/SsY2otFWyeFN0JJ20+ymGyJZtWk=", + "usagesDigest": "fycyB39YnXIJkfWCIXLUKJMZzANcuLy9ZE73hRucjFk=", + "recordedFileInputs": { + "@@pybind11_bazel+//MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e" + }, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_python": { + "repoRuleId": "@@pybind11_bazel+//:python_configure.bzl%python_configure", + "attributes": {} + }, + "pybind11": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file": "@@pybind11_bazel+//:pybind11.BUILD", + "strip_prefix": "pybind11-2.11.1", + "urls": [ + "https://github.com/pybind/pybind11/archive/v2.11.1.zip" + ] + } + } + }, + "recordedRepoMappingEntries": [ + [ + "pybind11_bazel+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@rules_fuzzing+//fuzzing/private:extensions.bzl%non_module_dependencies": { + "general": { + "bzlTransitiveDigest": "WHRlQQnxW7e7XMRBhq7SARkDarLDOAbg6iLaJpk5QYM=", + "usagesDigest": "wy6ISK6UOcBEjj/mvJ/S3WeXoO67X+1llb9yPyFtPgc=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "platforms": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz", + "https://github.com/bazelbuild/platforms/releases/download/0.0.8/platforms-0.0.8.tar.gz" + ], + "sha256": "8150406605389ececb6da07cbcb509d5637a3ab9a24bc69b1101531367d89d74" + } + }, + "rules_python": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "d70cd72a7a4880f0000a6346253414825c19cdd40a28289bdf67b8e6480edff8", + "strip_prefix": "rules_python-0.28.0", + "url": "https://github.com/bazelbuild/rules_python/releases/download/0.28.0/rules_python-0.28.0.tar.gz" + } + }, + "bazel_skylib": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "sha256": "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94", + "urls": [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz" + ] + } + }, + "com_google_absl": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "urls": [ + "https://github.com/abseil/abseil-cpp/archive/refs/tags/20240116.1.zip" + ], + "strip_prefix": "abseil-cpp-20240116.1", + "integrity": "sha256-7capMWOvWyoYbUaHF/b+I2U6XLMaHmky8KugWvfXYuk=" + } + }, + "rules_fuzzing_oss_fuzz": { + "repoRuleId": "@@rules_fuzzing+//fuzzing/private/oss_fuzz:repository.bzl%oss_fuzz_repository", + "attributes": {} + }, + "honggfuzz": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive", + "attributes": { + "build_file": "@@rules_fuzzing+//:honggfuzz.BUILD", + "sha256": "6b18ba13bc1f36b7b950c72d80f19ea67fbadc0ac0bb297ec89ad91f2eaa423e", + "url": "https://github.com/google/honggfuzz/archive/2.5.zip", + "strip_prefix": "honggfuzz-2.5" + } + }, + "rules_fuzzing_jazzer": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", + "attributes": { + "sha256": "ee6feb569d88962d59cb59e8a31eb9d007c82683f3ebc64955fd5b96f277eec2", + "url": "https://repo1.maven.org/maven2/com/code-intelligence/jazzer/0.20.1/jazzer-0.20.1.jar" + } + }, + "rules_fuzzing_jazzer_api": { + "repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_jar", + "attributes": { + "sha256": "f5a60242bc408f7fa20fccf10d6c5c5ea1fcb3c6f44642fec5af88373ae7aa1b", + "url": "https://repo1.maven.org/maven2/com/code-intelligence/jazzer-api/0.20.1/jazzer-api-0.20.1.jar" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_fuzzing+", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, "@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { "bzlTransitiveDigest": "ABI1D/sbS1ovwaW/kHDoj8nnXjQ0oKU9fzmzEG4iT8o=", @@ -465,6 +582,2651 @@ } } }, + "@@rules_python+//python/extensions:pip.bzl%pip": { + "general": { + "bzlTransitiveDigest": "94KJF9Ae9ecps56pBah1kZXjzRMH/Ue41G2hY5UpuBU=", + "usagesDigest": "fztm9ST3ki9E77tct2ZwJe0w38LvfMY8mRYp+YHxXQE=", + "recordedFileInputs": { + "@@protobuf+//python/requirements.txt": "983be60d3cec4b319dcab6d48aeb3f5b2f7c3350f26b3a9e97486c37967c73c5", + "@@rules_fuzzing+//fuzzing/requirements.txt": "ab04664be026b632a0d2a2446c4f65982b7654f5b6851d2f9d399a19b7242a5b", + "@@rules_python+//tools/publish/requirements_darwin.txt": "095d4a4f3d639dce831cd493367631cd51b53665292ab20194bac2c0c6458fa8", + "@@rules_python+//tools/publish/requirements_linux.txt": "d576e0d8542df61396a9b38deeaa183c24135ed5e8e73bb9622f298f2671811e", + "@@rules_python+//tools/publish/requirements_windows.txt": "d18538a3982beab378fd5687f4db33162ee1ece69801f9a451661b1b64286b76" + }, + "recordedDirentsInputs": {}, + "envVariables": { + "RULES_PYTHON_REPO_DEBUG": null, + "RULES_PYTHON_REPO_DEBUG_VERBOSITY": null + }, + "generatedRepoSpecs": { + "pip_deps_310_numpy": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@pip_deps//{name}:{target}", + "python_interpreter_target": "@@rules_python++python+python_3_10_host//:python", + "repo": "pip_deps_310", + "requirement": "numpy<=1.26.1" + } + }, + "pip_deps_310_setuptools": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@pip_deps//{name}:{target}", + "python_interpreter_target": "@@rules_python++python+python_3_10_host//:python", + "repo": "pip_deps_310", + "requirement": "setuptools<=70.3.0" + } + }, + "pip_deps_311_numpy": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@pip_deps//{name}:{target}", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "pip_deps_311", + "requirement": "numpy<=1.26.1" + } + }, + "pip_deps_311_setuptools": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@pip_deps//{name}:{target}", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "pip_deps_311", + "requirement": "setuptools<=70.3.0" + } + }, + "pip_deps_312_numpy": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@pip_deps//{name}:{target}", + "python_interpreter_target": "@@rules_python++python+python_3_12_host//:python", + "repo": "pip_deps_312", + "requirement": "numpy<=1.26.1" + } + }, + "pip_deps_312_setuptools": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@pip_deps//{name}:{target}", + "python_interpreter_target": "@@rules_python++python+python_3_12_host//:python", + "repo": "pip_deps_312", + "requirement": "setuptools<=70.3.0" + } + }, + "pip_deps_38_numpy": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@pip_deps//{name}:{target}", + "python_interpreter_target": "@@rules_python++python+python_3_8_host//:python", + "repo": "pip_deps_38", + "requirement": "numpy<=1.26.1" + } + }, + "pip_deps_38_setuptools": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@pip_deps//{name}:{target}", + "python_interpreter_target": "@@rules_python++python+python_3_8_host//:python", + "repo": "pip_deps_38", + "requirement": "setuptools<=70.3.0" + } + }, + "pip_deps_39_numpy": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@pip_deps//{name}:{target}", + "python_interpreter_target": "@@rules_python++python+python_3_9_host//:python", + "repo": "pip_deps_39", + "requirement": "numpy<=1.26.1" + } + }, + "pip_deps_39_setuptools": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@pip_deps//{name}:{target}", + "python_interpreter_target": "@@rules_python++python+python_3_9_host//:python", + "repo": "pip_deps_39", + "requirement": "setuptools<=70.3.0" + } + }, + "rules_fuzzing_py_deps_310_absl_py": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", + "extra_pip_args": [ + "--require-hashes" + ], + "python_interpreter_target": "@@rules_python++python+python_3_10_host//:python", + "repo": "rules_fuzzing_py_deps_310", + "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" + } + }, + "rules_fuzzing_py_deps_310_six": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", + "extra_pip_args": [ + "--require-hashes" + ], + "python_interpreter_target": "@@rules_python++python+python_3_10_host//:python", + "repo": "rules_fuzzing_py_deps_310", + "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + } + }, + "rules_fuzzing_py_deps_311_absl_py": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", + "extra_pip_args": [ + "--require-hashes" + ], + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_fuzzing_py_deps_311", + "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" + } + }, + "rules_fuzzing_py_deps_311_six": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", + "extra_pip_args": [ + "--require-hashes" + ], + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_fuzzing_py_deps_311", + "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + } + }, + "rules_fuzzing_py_deps_312_absl_py": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", + "extra_pip_args": [ + "--require-hashes" + ], + "python_interpreter_target": "@@rules_python++python+python_3_12_host//:python", + "repo": "rules_fuzzing_py_deps_312", + "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" + } + }, + "rules_fuzzing_py_deps_312_six": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", + "extra_pip_args": [ + "--require-hashes" + ], + "python_interpreter_target": "@@rules_python++python+python_3_12_host//:python", + "repo": "rules_fuzzing_py_deps_312", + "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + } + }, + "rules_fuzzing_py_deps_38_absl_py": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", + "extra_pip_args": [ + "--require-hashes" + ], + "python_interpreter_target": "@@rules_python++python+python_3_8_host//:python", + "repo": "rules_fuzzing_py_deps_38", + "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" + } + }, + "rules_fuzzing_py_deps_38_six": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", + "extra_pip_args": [ + "--require-hashes" + ], + "python_interpreter_target": "@@rules_python++python+python_3_8_host//:python", + "repo": "rules_fuzzing_py_deps_38", + "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + } + }, + "rules_fuzzing_py_deps_39_absl_py": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", + "extra_pip_args": [ + "--require-hashes" + ], + "python_interpreter_target": "@@rules_python++python+python_3_9_host//:python", + "repo": "rules_fuzzing_py_deps_39", + "requirement": "absl-py==2.0.0 --hash=sha256:9a28abb62774ae4e8edbe2dd4c49ffcd45a6a848952a5eccc6a49f3f0fc1e2f3" + } + }, + "rules_fuzzing_py_deps_39_six": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_fuzzing_py_deps//{name}:{target}", + "extra_pip_args": [ + "--require-hashes" + ], + "python_interpreter_target": "@@rules_python++python+python_3_9_host//:python", + "repo": "rules_fuzzing_py_deps_39", + "requirement": "six==1.16.0 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + } + }, + "rules_python_publish_deps_311_backports_tarfile_py3_none_any_77e284d7": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "backports.tarfile-1.2.0-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "backports-tarfile==1.2.0", + "sha256": "77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", + "urls": [ + "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_backports_tarfile_sdist_d75e02c2": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "backports_tarfile-1.2.0.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "backports-tarfile==1.2.0", + "sha256": "d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", + "urls": [ + "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_certifi_py3_none_any_922820b5": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "certifi-2024.8.30-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "certifi==2024.8.30", + "sha256": "922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", + "urls": [ + "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_certifi_sdist_bec941d2": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "certifi-2024.8.30.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "certifi==2024.8.30", + "sha256": "bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", + "urls": [ + "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_aarch64_a1ed2dd2": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "cffi==1.17.1", + "sha256": "a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", + "urls": [ + "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + ] + } + }, + "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_s390x_a24ed04c": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "cffi==1.17.1", + "sha256": "a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", + "urls": [ + "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl" + ] + } + }, + "rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_x86_64_610faea7": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "cffi==1.17.1", + "sha256": "610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", + "urls": [ + "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + ] + } + }, + "rules_python_publish_deps_311_cffi_cp311_cp311_musllinux_1_1_aarch64_a9b15d49": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "cffi==1.17.1", + "sha256": "a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", + "urls": [ + "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl" + ] + } + }, + "rules_python_publish_deps_311_cffi_cp311_cp311_musllinux_1_1_x86_64_fc48c783": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "cffi==1.17.1", + "sha256": "fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", + "urls": [ + "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl" + ] + } + }, + "rules_python_publish_deps_311_cffi_sdist_1c39c601": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "cffi-1.17.1.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "cffi==1.17.1", + "sha256": "1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", + "urls": [ + "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_10_9_universal2_0d99dd8f": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "charset-normalizer==3.4.0", + "sha256": "0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", + "urls": [ + "https://files.pythonhosted.org/packages/9c/61/73589dcc7a719582bf56aae309b6103d2762b526bffe189d635a7fcfd998/charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl" + ] + } + }, + "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_10_9_x86_64_c57516e5": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "charset-normalizer==3.4.0", + "sha256": "c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", + "urls": [ + "https://files.pythonhosted.org/packages/77/d5/8c982d58144de49f59571f940e329ad6e8615e1e82ef84584c5eeb5e1d72/charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl" + ] + } + }, + "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_11_0_arm64_6dba5d19": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "charset-normalizer==3.4.0", + "sha256": "6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", + "urls": [ + "https://files.pythonhosted.org/packages/bf/19/411a64f01ee971bed3231111b69eb56f9331a769072de479eae7de52296d/charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl" + ] + } + }, + "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_aarch64_bf4475b8": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "charset-normalizer==3.4.0", + "sha256": "bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", + "urls": [ + "https://files.pythonhosted.org/packages/4c/92/97509850f0d00e9f14a46bc751daabd0ad7765cff29cdfb66c68b6dad57f/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + ] + } + }, + "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_s390x_8ff4e7cd": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "charset-normalizer==3.4.0", + "sha256": "8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", + "urls": [ + "https://files.pythonhosted.org/packages/13/bc/87c2c9f2c144bedfa62f894c3007cd4530ba4b5351acb10dc786428a50f0/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl" + ] + } + }, + "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_x86_64_3710a975": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "charset-normalizer==3.4.0", + "sha256": "3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", + "urls": [ + "https://files.pythonhosted.org/packages/eb/5b/6f10bad0f6461fa272bfbbdf5d0023b5fb9bc6217c92bf068fa5a99820f5/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + ] + } + }, + "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_aarch64_47334db7": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "charset-normalizer==3.4.0", + "sha256": "47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", + "urls": [ + "https://files.pythonhosted.org/packages/d7/a1/493919799446464ed0299c8eef3c3fad0daf1c3cd48bff9263c731b0d9e2/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl" + ] + } + }, + "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_s390x_63bc5c4a": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "charset-normalizer==3.4.0", + "sha256": "63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", + "urls": [ + "https://files.pythonhosted.org/packages/8d/c9/27e41d481557be53d51e60750b85aa40eaf52b841946b3cdeff363105737/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl" + ] + } + }, + "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_x86_64_bcb4f8ea": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "charset-normalizer==3.4.0", + "sha256": "bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", + "urls": [ + "https://files.pythonhosted.org/packages/ee/44/4f62042ca8cdc0cabf87c0fc00ae27cd8b53ab68be3605ba6d071f742ad3/charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl" + ] + } + }, + "rules_python_publish_deps_311_charset_normalizer_cp311_cp311_win_amd64_cee4373f": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "charset-normalizer==3.4.0", + "sha256": "cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", + "urls": [ + "https://files.pythonhosted.org/packages/0b/6e/b13bd47fa9023b3699e94abf565b5a2f0b0be6e9ddac9812182596ee62e4/charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl" + ] + } + }, + "rules_python_publish_deps_311_charset_normalizer_py3_none_any_fe9f97fe": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "charset_normalizer-3.4.0-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "charset-normalizer==3.4.0", + "sha256": "fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", + "urls": [ + "https://files.pythonhosted.org/packages/bf/9b/08c0432272d77b04803958a4598a51e2a4b51c06640af8b8f0f908c18bf2/charset_normalizer-3.4.0-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_charset_normalizer_sdist_223217c3": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "charset_normalizer-3.4.0.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "charset-normalizer==3.4.0", + "sha256": "223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", + "urls": [ + "https://files.pythonhosted.org/packages/f2/4f/e1808dc01273379acc506d18f1504eb2d299bd4131743b9fc54d7be4df1e/charset_normalizer-3.4.0.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_17_aarch64_846da004": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "cryptography==43.0.3", + "sha256": "846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5", + "urls": [ + "https://files.pythonhosted.org/packages/2f/78/55356eb9075d0be6e81b59f45c7b48df87f76a20e73893872170471f3ee8/cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + ] + } + }, + "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_17_x86_64_0f996e72": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "cryptography==43.0.3", + "sha256": "0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4", + "urls": [ + "https://files.pythonhosted.org/packages/2a/2c/488776a3dc843f95f86d2f957ca0fc3407d0242b50bede7fad1e339be03f/cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + ] + } + }, + "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_28_aarch64_f7b178f1": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "cryptography==43.0.3", + "sha256": "f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7", + "urls": [ + "https://files.pythonhosted.org/packages/7c/04/2345ca92f7a22f601a9c62961741ef7dd0127c39f7310dffa0041c80f16f/cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl" + ] + } + }, + "rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_28_x86_64_c2e6fc39": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "cryptography==43.0.3", + "sha256": "c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405", + "urls": [ + "https://files.pythonhosted.org/packages/ac/25/e715fa0bc24ac2114ed69da33adf451a38abb6f3f24ec207908112e9ba53/cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl" + ] + } + }, + "rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_2_aarch64_e1be4655": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "cryptography==43.0.3", + "sha256": "e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16", + "urls": [ + "https://files.pythonhosted.org/packages/21/ce/b9c9ff56c7164d8e2edfb6c9305045fbc0df4508ccfdb13ee66eb8c95b0e/cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl" + ] + } + }, + "rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_2_x86_64_df6b6c6d": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "cryptography==43.0.3", + "sha256": "df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73", + "urls": [ + "https://files.pythonhosted.org/packages/2a/33/b3682992ab2e9476b9c81fff22f02c8b0a1e6e1d49ee1750a67d85fd7ed2/cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl" + ] + } + }, + "rules_python_publish_deps_311_cryptography_sdist_315b9001": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "cryptography-43.0.3.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "cryptography==43.0.3", + "sha256": "315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805", + "urls": [ + "https://files.pythonhosted.org/packages/0d/05/07b55d1fa21ac18c3a8c79f764e2514e6f6a9698f1be44994f5adf0d29db/cryptography-43.0.3.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_docutils_py3_none_any_dafca5b9": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "docutils-0.21.2-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "docutils==0.21.2", + "sha256": "dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", + "urls": [ + "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_docutils_sdist_3a6b1873": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "docutils-0.21.2.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "docutils==0.21.2", + "sha256": "3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", + "urls": [ + "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_idna_py3_none_any_946d195a": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "idna-3.10-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "idna==3.10", + "sha256": "946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", + "urls": [ + "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_idna_sdist_12f65c9b": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "idna-3.10.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "idna==3.10", + "sha256": "12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "urls": [ + "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_importlib_metadata_py3_none_any_45e54197": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "importlib_metadata-8.5.0-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "importlib-metadata==8.5.0", + "sha256": "45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", + "urls": [ + "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_importlib_metadata_sdist_71522656": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "importlib_metadata-8.5.0.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "importlib-metadata==8.5.0", + "sha256": "71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", + "urls": [ + "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_jaraco_classes_py3_none_any_f662826b": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "jaraco.classes-3.4.0-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "jaraco-classes==3.4.0", + "sha256": "f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", + "urls": [ + "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_jaraco_classes_sdist_47a024b5": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "jaraco.classes-3.4.0.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "jaraco-classes==3.4.0", + "sha256": "47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", + "urls": [ + "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_jaraco_context_py3_none_any_f797fc48": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "jaraco.context-6.0.1-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "jaraco-context==6.0.1", + "sha256": "f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4", + "urls": [ + "https://files.pythonhosted.org/packages/ff/db/0c52c4cf5e4bd9f5d7135ec7669a3a767af21b3a308e1ed3674881e52b62/jaraco.context-6.0.1-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_jaraco_context_sdist_9bae4ea5": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "jaraco_context-6.0.1.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "jaraco-context==6.0.1", + "sha256": "9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", + "urls": [ + "https://files.pythonhosted.org/packages/df/ad/f3777b81bf0b6e7bc7514a1656d3e637b2e8e15fab2ce3235730b3e7a4e6/jaraco_context-6.0.1.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_jaraco_functools_py3_none_any_ad159f13": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "jaraco.functools-4.1.0-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "jaraco-functools==4.1.0", + "sha256": "ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649", + "urls": [ + "https://files.pythonhosted.org/packages/9f/4f/24b319316142c44283d7540e76c7b5a6dbd5db623abd86bb7b3491c21018/jaraco.functools-4.1.0-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_jaraco_functools_sdist_70f7e0e2": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "jaraco_functools-4.1.0.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "jaraco-functools==4.1.0", + "sha256": "70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d", + "urls": [ + "https://files.pythonhosted.org/packages/ab/23/9894b3df5d0a6eb44611c36aec777823fc2e07740dabbd0b810e19594013/jaraco_functools-4.1.0.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_jeepney_py3_none_any_c0a454ad": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "jeepney-0.8.0-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "jeepney==0.8.0", + "sha256": "c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755", + "urls": [ + "https://files.pythonhosted.org/packages/ae/72/2a1e2290f1ab1e06f71f3d0f1646c9e4634e70e1d37491535e19266e8dc9/jeepney-0.8.0-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_jeepney_sdist_5efe48d2": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "jeepney-0.8.0.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "jeepney==0.8.0", + "sha256": "5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806", + "urls": [ + "https://files.pythonhosted.org/packages/d6/f4/154cf374c2daf2020e05c3c6a03c91348d59b23c5366e968feb198306fdf/jeepney-0.8.0.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_keyring_py3_none_any_5426f817": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "keyring-25.4.1-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "keyring==25.4.1", + "sha256": "5426f817cf7f6f007ba5ec722b1bcad95a75b27d780343772ad76b17cb47b0bf", + "urls": [ + "https://files.pythonhosted.org/packages/83/25/e6d59e5f0a0508d0dca8bb98c7f7fd3772fc943ac3f53d5ab18a218d32c0/keyring-25.4.1-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_keyring_sdist_b07ebc55": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "keyring-25.4.1.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "keyring==25.4.1", + "sha256": "b07ebc55f3e8ed86ac81dd31ef14e81ace9dd9c3d4b5d77a6e9a2016d0d71a1b", + "urls": [ + "https://files.pythonhosted.org/packages/a5/1c/2bdbcfd5d59dc6274ffb175bc29aa07ecbfab196830e0cfbde7bd861a2ea/keyring-25.4.1.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_markdown_it_py_py3_none_any_35521684": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "markdown_it_py-3.0.0-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "markdown-it-py==3.0.0", + "sha256": "355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", + "urls": [ + "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_markdown_it_py_sdist_e3f60a94": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "markdown-it-py-3.0.0.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "markdown-it-py==3.0.0", + "sha256": "e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", + "urls": [ + "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_mdurl_py3_none_any_84008a41": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "mdurl-0.1.2-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "mdurl==0.1.2", + "sha256": "84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", + "urls": [ + "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_mdurl_sdist_bb413d29": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "mdurl-0.1.2.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "mdurl==0.1.2", + "sha256": "bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", + "urls": [ + "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_more_itertools_py3_none_any_037b0d32": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "more_itertools-10.5.0-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "more-itertools==10.5.0", + "sha256": "037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef", + "urls": [ + "https://files.pythonhosted.org/packages/48/7e/3a64597054a70f7c86eb0a7d4fc315b8c1ab932f64883a297bdffeb5f967/more_itertools-10.5.0-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_more_itertools_sdist_5482bfef": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "more-itertools-10.5.0.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "more-itertools==10.5.0", + "sha256": "5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6", + "urls": [ + "https://files.pythonhosted.org/packages/51/78/65922308c4248e0eb08ebcbe67c95d48615cc6f27854b6f2e57143e9178f/more-itertools-10.5.0.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_nh3_cp37_abi3_macosx_10_12_x86_64_14c5a72e": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "nh3-0.2.18-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "nh3==0.2.18", + "sha256": "14c5a72e9fe82aea5fe3072116ad4661af5cf8e8ff8fc5ad3450f123e4925e86", + "urls": [ + "https://files.pythonhosted.org/packages/b3/89/1daff5d9ba5a95a157c092c7c5f39b8dd2b1ddb4559966f808d31cfb67e0/nh3-0.2.18-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl" + ] + } + }, + "rules_python_publish_deps_311_nh3_cp37_abi3_macosx_10_12_x86_64_7b7c2a3c": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "nh3-0.2.18-cp37-abi3-macosx_10_12_x86_64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "nh3==0.2.18", + "sha256": "7b7c2a3c9eb1a827d42539aa64091640bd275b81e097cd1d8d82ef91ffa2e811", + "urls": [ + "https://files.pythonhosted.org/packages/2c/b6/42fc3c69cabf86b6b81e4c051a9b6e249c5ba9f8155590222c2622961f58/nh3-0.2.18-cp37-abi3-macosx_10_12_x86_64.whl" + ] + } + }, + "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_aarch64_42c64511": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "nh3==0.2.18", + "sha256": "42c64511469005058cd17cc1537578eac40ae9f7200bedcfd1fc1a05f4f8c200", + "urls": [ + "https://files.pythonhosted.org/packages/45/b9/833f385403abaf0023c6547389ec7a7acf141ddd9d1f21573723a6eab39a/nh3-0.2.18-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + ] + } + }, + "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_armv7l_0411beb0": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "nh3==0.2.18", + "sha256": "0411beb0589eacb6734f28d5497ca2ed379eafab8ad8c84b31bb5c34072b7164", + "urls": [ + "https://files.pythonhosted.org/packages/05/2b/85977d9e11713b5747595ee61f381bc820749daf83f07b90b6c9964cf932/nh3-0.2.18-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl" + ] + } + }, + "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_ppc64_5f36b271": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "nh3==0.2.18", + "sha256": "5f36b271dae35c465ef5e9090e1fdaba4a60a56f0bb0ba03e0932a66f28b9189", + "urls": [ + "https://files.pythonhosted.org/packages/72/f2/5c894d5265ab80a97c68ca36f25c8f6f0308abac649aaf152b74e7e854a8/nh3-0.2.18-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl" + ] + } + }, + "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_s390x_19aaba96": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "nh3==0.2.18", + "sha256": "19aaba96e0f795bd0a6c56291495ff59364f4300d4a39b29a0abc9cb3774a84b", + "urls": [ + "https://files.pythonhosted.org/packages/c2/a8/3bb02d0c60a03ad3a112b76c46971e9480efa98a8946677b5a59f60130ca/nh3-0.2.18-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl" + ] + } + }, + "rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_x86_64_de3ceed6": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "nh3-0.2.18-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "nh3==0.2.18", + "sha256": "de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307", + "urls": [ + "https://files.pythonhosted.org/packages/1b/63/6ab90d0e5225ab9780f6c9fb52254fa36b52bb7c188df9201d05b647e5e1/nh3-0.2.18-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + ] + } + }, + "rules_python_publish_deps_311_nh3_cp37_abi3_musllinux_1_2_aarch64_f0eca9ca": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "nh3-0.2.18-cp37-abi3-musllinux_1_2_aarch64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "nh3==0.2.18", + "sha256": "f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe", + "urls": [ + "https://files.pythonhosted.org/packages/a3/da/0c4e282bc3cff4a0adf37005fa1fb42257673fbc1bbf7d1ff639ec3d255a/nh3-0.2.18-cp37-abi3-musllinux_1_2_aarch64.whl" + ] + } + }, + "rules_python_publish_deps_311_nh3_cp37_abi3_musllinux_1_2_armv7l_3a157ab1": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "nh3-0.2.18-cp37-abi3-musllinux_1_2_armv7l.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "nh3==0.2.18", + "sha256": "3a157ab149e591bb638a55c8c6bcb8cdb559c8b12c13a8affaba6cedfe51713a", + "urls": [ + "https://files.pythonhosted.org/packages/de/81/c291231463d21da5f8bba82c8167a6d6893cc5419b0639801ee5d3aeb8a9/nh3-0.2.18-cp37-abi3-musllinux_1_2_armv7l.whl" + ] + } + }, + "rules_python_publish_deps_311_nh3_cp37_abi3_musllinux_1_2_x86_64_36c95d4b": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "nh3-0.2.18-cp37-abi3-musllinux_1_2_x86_64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "nh3==0.2.18", + "sha256": "36c95d4b70530b320b365659bb5034341316e6a9b30f0b25fa9c9eff4c27a204", + "urls": [ + "https://files.pythonhosted.org/packages/eb/61/73a007c74c37895fdf66e0edcd881f5eaa17a348ff02f4bb4bc906d61085/nh3-0.2.18-cp37-abi3-musllinux_1_2_x86_64.whl" + ] + } + }, + "rules_python_publish_deps_311_nh3_cp37_abi3_win_amd64_8ce0f819": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "nh3-0.2.18-cp37-abi3-win_amd64.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "nh3==0.2.18", + "sha256": "8ce0f819d2f1933953fca255db2471ad58184a60508f03e6285e5114b6254844", + "urls": [ + "https://files.pythonhosted.org/packages/26/8d/53c5b19c4999bdc6ba95f246f4ef35ca83d7d7423e5e38be43ad66544e5d/nh3-0.2.18-cp37-abi3-win_amd64.whl" + ] + } + }, + "rules_python_publish_deps_311_nh3_sdist_94a16692": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "nh3-0.2.18.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "nh3==0.2.18", + "sha256": "94a166927e53972a9698af9542ace4e38b9de50c34352b962f4d9a7d4c927af4", + "urls": [ + "https://files.pythonhosted.org/packages/62/73/10df50b42ddb547a907deeb2f3c9823022580a7a47281e8eae8e003a9639/nh3-0.2.18.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_pkginfo_py3_none_any_889a6da2": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "pkginfo-1.10.0-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "pkginfo==1.10.0", + "sha256": "889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097", + "urls": [ + "https://files.pythonhosted.org/packages/56/09/054aea9b7534a15ad38a363a2bd974c20646ab1582a387a95b8df1bfea1c/pkginfo-1.10.0-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_pkginfo_sdist_5df73835": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "pkginfo-1.10.0.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "pkginfo==1.10.0", + "sha256": "5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297", + "urls": [ + "https://files.pythonhosted.org/packages/2f/72/347ec5be4adc85c182ed2823d8d1c7b51e13b9a6b0c1aae59582eca652df/pkginfo-1.10.0.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_pycparser_py3_none_any_c3702b6d": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "pycparser-2.22-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "pycparser==2.22", + "sha256": "c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", + "urls": [ + "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_pycparser_sdist_491c8be9": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "pycparser-2.22.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "pycparser==2.22", + "sha256": "491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", + "urls": [ + "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_pygments_py3_none_any_b8e6aca0": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "pygments-2.18.0-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "pygments==2.18.0", + "sha256": "b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", + "urls": [ + "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_pygments_sdist_786ff802": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "pygments-2.18.0.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "pygments==2.18.0", + "sha256": "786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", + "urls": [ + "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_pywin32_ctypes_py3_none_any_8a151337": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_windows_x86_64" + ], + "filename": "pywin32_ctypes-0.2.3-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "pywin32-ctypes==0.2.3", + "sha256": "8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", + "urls": [ + "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_pywin32_ctypes_sdist_d162dc04": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "pywin32-ctypes-0.2.3.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "pywin32-ctypes==0.2.3", + "sha256": "d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", + "urls": [ + "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_readme_renderer_py3_none_any_2fbca89b": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "readme_renderer-44.0-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "readme-renderer==44.0", + "sha256": "2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", + "urls": [ + "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_readme_renderer_sdist_8712034e": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "readme_renderer-44.0.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "readme-renderer==44.0", + "sha256": "8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", + "urls": [ + "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_requests_py3_none_any_70761cfe": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "requests-2.32.3-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "requests==2.32.3", + "sha256": "70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", + "urls": [ + "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_requests_sdist_55365417": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "requests-2.32.3.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "requests==2.32.3", + "sha256": "55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", + "urls": [ + "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_requests_toolbelt_py2_none_any_cccfdd66": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "requests_toolbelt-1.0.0-py2.py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "requests-toolbelt==1.0.0", + "sha256": "cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", + "urls": [ + "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_requests_toolbelt_sdist_7681a0a3": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "requests-toolbelt-1.0.0.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "requests-toolbelt==1.0.0", + "sha256": "7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", + "urls": [ + "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_rfc3986_py2_none_any_50b1502b": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "rfc3986-2.0.0-py2.py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "rfc3986==2.0.0", + "sha256": "50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", + "urls": [ + "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_rfc3986_sdist_97aacf9d": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "rfc3986-2.0.0.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "rfc3986==2.0.0", + "sha256": "97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", + "urls": [ + "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_rich_py3_none_any_6049d5e6": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "rich-13.9.4-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "rich==13.9.4", + "sha256": "6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", + "urls": [ + "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_rich_sdist_43959497": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "rich-13.9.4.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "rich==13.9.4", + "sha256": "439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", + "urls": [ + "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_secretstorage_py3_none_any_f356e662": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "filename": "SecretStorage-3.3.3-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "secretstorage==3.3.3", + "sha256": "f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99", + "urls": [ + "https://files.pythonhosted.org/packages/54/24/b4293291fa1dd830f353d2cb163295742fa87f179fcc8a20a306a81978b7/SecretStorage-3.3.3-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_secretstorage_sdist_2403533e": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "SecretStorage-3.3.3.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "secretstorage==3.3.3", + "sha256": "2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", + "urls": [ + "https://files.pythonhosted.org/packages/53/a4/f48c9d79cb507ed1373477dbceaba7401fd8a23af63b837fa61f1dcd3691/SecretStorage-3.3.3.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_twine_py3_none_any_215dbe7b": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "twine-5.1.1-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "twine==5.1.1", + "sha256": "215dbe7b4b94c2c50a7315c0275d2258399280fbb7d04182c7e55e24b5f93997", + "urls": [ + "https://files.pythonhosted.org/packages/5d/ec/00f9d5fd040ae29867355e559a94e9a8429225a0284a3f5f091a3878bfc0/twine-5.1.1-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_twine_sdist_9aa08251": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "twine-5.1.1.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "twine==5.1.1", + "sha256": "9aa0825139c02b3434d913545c7b847a21c835e11597f5255842d457da2322db", + "urls": [ + "https://files.pythonhosted.org/packages/77/68/bd982e5e949ef8334e6f7dcf76ae40922a8750aa2e347291ae1477a4782b/twine-5.1.1.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_urllib3_py3_none_any_ca899ca0": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "urllib3-2.2.3-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "urllib3==2.2.3", + "sha256": "ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", + "urls": [ + "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_urllib3_sdist_e7d814a8": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "urllib3-2.2.3.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "urllib3==2.2.3", + "sha256": "e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", + "urls": [ + "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz" + ] + } + }, + "rules_python_publish_deps_311_zipp_py3_none_any_a817ac80": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "filename": "zipp-3.20.2-py3-none-any.whl", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "zipp==3.20.2", + "sha256": "a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350", + "urls": [ + "https://files.pythonhosted.org/packages/62/8b/5ba542fa83c90e09eac972fc9baca7a88e7e7ca4b221a89251954019308b/zipp-3.20.2-py3-none-any.whl" + ] + } + }, + "rules_python_publish_deps_311_zipp_sdist_bc9eb26f": { + "repoRuleId": "@@rules_python+//python/private/pypi:whl_library.bzl%whl_library", + "attributes": { + "dep_template": "@rules_python_publish_deps//{name}:{target}", + "experimental_target_platforms": [ + "cp311_linux_aarch64", + "cp311_linux_arm", + "cp311_linux_ppc", + "cp311_linux_s390x", + "cp311_linux_x86_64", + "cp311_osx_aarch64", + "cp311_osx_x86_64", + "cp311_windows_x86_64" + ], + "extra_pip_args": [ + "--index-url", + "https://pypi.org/simple" + ], + "filename": "zipp-3.20.2.tar.gz", + "python_interpreter_target": "@@rules_python++python+python_3_11_host//:python", + "repo": "rules_python_publish_deps_311", + "requirement": "zipp==3.20.2", + "sha256": "bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29", + "urls": [ + "https://files.pythonhosted.org/packages/54/bf/5c0000c44ebc80123ecbdddba1f5dcd94a5ada602a9c225d84b5aaa55e86/zipp-3.20.2.tar.gz" + ] + } + }, + "pip_deps": { + "repoRuleId": "@@rules_python+//python/private/pypi:hub_repository.bzl%hub_repository", + "attributes": { + "repo_name": "pip_deps", + "extra_hub_aliases": {}, + "whl_map": { + "numpy": "{\"pip_deps_310_numpy\":[{\"version\":\"3.10\"}],\"pip_deps_311_numpy\":[{\"version\":\"3.11\"}],\"pip_deps_312_numpy\":[{\"version\":\"3.12\"}],\"pip_deps_38_numpy\":[{\"version\":\"3.8\"}],\"pip_deps_39_numpy\":[{\"version\":\"3.9\"}]}", + "setuptools": "{\"pip_deps_310_setuptools\":[{\"version\":\"3.10\"}],\"pip_deps_311_setuptools\":[{\"version\":\"3.11\"}],\"pip_deps_312_setuptools\":[{\"version\":\"3.12\"}],\"pip_deps_38_setuptools\":[{\"version\":\"3.8\"}],\"pip_deps_39_setuptools\":[{\"version\":\"3.9\"}]}" + }, + "packages": [ + "numpy", + "setuptools" + ], + "groups": {} + } + }, + "rules_fuzzing_py_deps": { + "repoRuleId": "@@rules_python+//python/private/pypi:hub_repository.bzl%hub_repository", + "attributes": { + "repo_name": "rules_fuzzing_py_deps", + "extra_hub_aliases": {}, + "whl_map": { + "absl_py": "{\"rules_fuzzing_py_deps_310_absl_py\":[{\"version\":\"3.10\"}],\"rules_fuzzing_py_deps_311_absl_py\":[{\"version\":\"3.11\"}],\"rules_fuzzing_py_deps_312_absl_py\":[{\"version\":\"3.12\"}],\"rules_fuzzing_py_deps_38_absl_py\":[{\"version\":\"3.8\"}],\"rules_fuzzing_py_deps_39_absl_py\":[{\"version\":\"3.9\"}]}", + "six": "{\"rules_fuzzing_py_deps_310_six\":[{\"version\":\"3.10\"}],\"rules_fuzzing_py_deps_311_six\":[{\"version\":\"3.11\"}],\"rules_fuzzing_py_deps_312_six\":[{\"version\":\"3.12\"}],\"rules_fuzzing_py_deps_38_six\":[{\"version\":\"3.8\"}],\"rules_fuzzing_py_deps_39_six\":[{\"version\":\"3.9\"}]}" + }, + "packages": [ + "absl_py", + "six" + ], + "groups": {} + } + }, + "rules_python_publish_deps": { + "repoRuleId": "@@rules_python+//python/private/pypi:hub_repository.bzl%hub_repository", + "attributes": { + "repo_name": "rules_python_publish_deps", + "extra_hub_aliases": {}, + "whl_map": { + "backports_tarfile": "{\"rules_python_publish_deps_311_backports_tarfile_py3_none_any_77e284d7\":[{\"filename\":\"backports.tarfile-1.2.0-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_backports_tarfile_sdist_d75e02c2\":[{\"filename\":\"backports_tarfile-1.2.0.tar.gz\",\"version\":\"3.11\"}]}", + "certifi": "{\"rules_python_publish_deps_311_certifi_py3_none_any_922820b5\":[{\"filename\":\"certifi-2024.8.30-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_certifi_sdist_bec941d2\":[{\"filename\":\"certifi-2024.8.30.tar.gz\",\"version\":\"3.11\"}]}", + "cffi": "{\"rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_aarch64_a1ed2dd2\":[{\"filename\":\"cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_s390x_a24ed04c\":[{\"filename\":\"cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_cffi_cp311_cp311_manylinux_2_17_x86_64_610faea7\":[{\"filename\":\"cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_cffi_cp311_cp311_musllinux_1_1_aarch64_a9b15d49\":[{\"filename\":\"cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_cffi_cp311_cp311_musllinux_1_1_x86_64_fc48c783\":[{\"filename\":\"cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_cffi_sdist_1c39c601\":[{\"filename\":\"cffi-1.17.1.tar.gz\",\"version\":\"3.11\"}]}", + "charset_normalizer": "{\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_10_9_universal2_0d99dd8f\":[{\"filename\":\"charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_10_9_x86_64_c57516e5\":[{\"filename\":\"charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_macosx_11_0_arm64_6dba5d19\":[{\"filename\":\"charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_aarch64_bf4475b8\":[{\"filename\":\"charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_s390x_8ff4e7cd\":[{\"filename\":\"charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_manylinux_2_17_x86_64_3710a975\":[{\"filename\":\"charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_aarch64_47334db7\":[{\"filename\":\"charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_s390x_63bc5c4a\":[{\"filename\":\"charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_musllinux_1_2_x86_64_bcb4f8ea\":[{\"filename\":\"charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_charset_normalizer_cp311_cp311_win_amd64_cee4373f\":[{\"filename\":\"charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_charset_normalizer_py3_none_any_fe9f97fe\":[{\"filename\":\"charset_normalizer-3.4.0-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_charset_normalizer_sdist_223217c3\":[{\"filename\":\"charset_normalizer-3.4.0.tar.gz\",\"version\":\"3.11\"}]}", + "cryptography": "{\"rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_17_aarch64_846da004\":[{\"filename\":\"cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_17_x86_64_0f996e72\":[{\"filename\":\"cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_28_aarch64_f7b178f1\":[{\"filename\":\"cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_cryptography_cp39_abi3_manylinux_2_28_x86_64_c2e6fc39\":[{\"filename\":\"cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_2_aarch64_e1be4655\":[{\"filename\":\"cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_cryptography_cp39_abi3_musllinux_1_2_x86_64_df6b6c6d\":[{\"filename\":\"cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_cryptography_sdist_315b9001\":[{\"filename\":\"cryptography-43.0.3.tar.gz\",\"version\":\"3.11\"}]}", + "docutils": "{\"rules_python_publish_deps_311_docutils_py3_none_any_dafca5b9\":[{\"filename\":\"docutils-0.21.2-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_docutils_sdist_3a6b1873\":[{\"filename\":\"docutils-0.21.2.tar.gz\",\"version\":\"3.11\"}]}", + "idna": "{\"rules_python_publish_deps_311_idna_py3_none_any_946d195a\":[{\"filename\":\"idna-3.10-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_idna_sdist_12f65c9b\":[{\"filename\":\"idna-3.10.tar.gz\",\"version\":\"3.11\"}]}", + "importlib_metadata": "{\"rules_python_publish_deps_311_importlib_metadata_py3_none_any_45e54197\":[{\"filename\":\"importlib_metadata-8.5.0-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_importlib_metadata_sdist_71522656\":[{\"filename\":\"importlib_metadata-8.5.0.tar.gz\",\"version\":\"3.11\"}]}", + "jaraco_classes": "{\"rules_python_publish_deps_311_jaraco_classes_py3_none_any_f662826b\":[{\"filename\":\"jaraco.classes-3.4.0-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_jaraco_classes_sdist_47a024b5\":[{\"filename\":\"jaraco.classes-3.4.0.tar.gz\",\"version\":\"3.11\"}]}", + "jaraco_context": "{\"rules_python_publish_deps_311_jaraco_context_py3_none_any_f797fc48\":[{\"filename\":\"jaraco.context-6.0.1-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_jaraco_context_sdist_9bae4ea5\":[{\"filename\":\"jaraco_context-6.0.1.tar.gz\",\"version\":\"3.11\"}]}", + "jaraco_functools": "{\"rules_python_publish_deps_311_jaraco_functools_py3_none_any_ad159f13\":[{\"filename\":\"jaraco.functools-4.1.0-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_jaraco_functools_sdist_70f7e0e2\":[{\"filename\":\"jaraco_functools-4.1.0.tar.gz\",\"version\":\"3.11\"}]}", + "jeepney": "{\"rules_python_publish_deps_311_jeepney_py3_none_any_c0a454ad\":[{\"filename\":\"jeepney-0.8.0-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_jeepney_sdist_5efe48d2\":[{\"filename\":\"jeepney-0.8.0.tar.gz\",\"version\":\"3.11\"}]}", + "keyring": "{\"rules_python_publish_deps_311_keyring_py3_none_any_5426f817\":[{\"filename\":\"keyring-25.4.1-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_keyring_sdist_b07ebc55\":[{\"filename\":\"keyring-25.4.1.tar.gz\",\"version\":\"3.11\"}]}", + "markdown_it_py": "{\"rules_python_publish_deps_311_markdown_it_py_py3_none_any_35521684\":[{\"filename\":\"markdown_it_py-3.0.0-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_markdown_it_py_sdist_e3f60a94\":[{\"filename\":\"markdown-it-py-3.0.0.tar.gz\",\"version\":\"3.11\"}]}", + "mdurl": "{\"rules_python_publish_deps_311_mdurl_py3_none_any_84008a41\":[{\"filename\":\"mdurl-0.1.2-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_mdurl_sdist_bb413d29\":[{\"filename\":\"mdurl-0.1.2.tar.gz\",\"version\":\"3.11\"}]}", + "more_itertools": "{\"rules_python_publish_deps_311_more_itertools_py3_none_any_037b0d32\":[{\"filename\":\"more_itertools-10.5.0-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_more_itertools_sdist_5482bfef\":[{\"filename\":\"more-itertools-10.5.0.tar.gz\",\"version\":\"3.11\"}]}", + "nh3": "{\"rules_python_publish_deps_311_nh3_cp37_abi3_macosx_10_12_x86_64_14c5a72e\":[{\"filename\":\"nh3-0.2.18-cp37-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_nh3_cp37_abi3_macosx_10_12_x86_64_7b7c2a3c\":[{\"filename\":\"nh3-0.2.18-cp37-abi3-macosx_10_12_x86_64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_aarch64_42c64511\":[{\"filename\":\"nh3-0.2.18-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_armv7l_0411beb0\":[{\"filename\":\"nh3-0.2.18-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_ppc64_5f36b271\":[{\"filename\":\"nh3-0.2.18-cp37-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_s390x_19aaba96\":[{\"filename\":\"nh3-0.2.18-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_nh3_cp37_abi3_manylinux_2_17_x86_64_de3ceed6\":[{\"filename\":\"nh3-0.2.18-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_nh3_cp37_abi3_musllinux_1_2_aarch64_f0eca9ca\":[{\"filename\":\"nh3-0.2.18-cp37-abi3-musllinux_1_2_aarch64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_nh3_cp37_abi3_musllinux_1_2_armv7l_3a157ab1\":[{\"filename\":\"nh3-0.2.18-cp37-abi3-musllinux_1_2_armv7l.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_nh3_cp37_abi3_musllinux_1_2_x86_64_36c95d4b\":[{\"filename\":\"nh3-0.2.18-cp37-abi3-musllinux_1_2_x86_64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_nh3_cp37_abi3_win_amd64_8ce0f819\":[{\"filename\":\"nh3-0.2.18-cp37-abi3-win_amd64.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_nh3_sdist_94a16692\":[{\"filename\":\"nh3-0.2.18.tar.gz\",\"version\":\"3.11\"}]}", + "pkginfo": "{\"rules_python_publish_deps_311_pkginfo_py3_none_any_889a6da2\":[{\"filename\":\"pkginfo-1.10.0-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_pkginfo_sdist_5df73835\":[{\"filename\":\"pkginfo-1.10.0.tar.gz\",\"version\":\"3.11\"}]}", + "pycparser": "{\"rules_python_publish_deps_311_pycparser_py3_none_any_c3702b6d\":[{\"filename\":\"pycparser-2.22-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_pycparser_sdist_491c8be9\":[{\"filename\":\"pycparser-2.22.tar.gz\",\"version\":\"3.11\"}]}", + "pygments": "{\"rules_python_publish_deps_311_pygments_py3_none_any_b8e6aca0\":[{\"filename\":\"pygments-2.18.0-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_pygments_sdist_786ff802\":[{\"filename\":\"pygments-2.18.0.tar.gz\",\"version\":\"3.11\"}]}", + "pywin32_ctypes": "{\"rules_python_publish_deps_311_pywin32_ctypes_py3_none_any_8a151337\":[{\"filename\":\"pywin32_ctypes-0.2.3-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_pywin32_ctypes_sdist_d162dc04\":[{\"filename\":\"pywin32-ctypes-0.2.3.tar.gz\",\"version\":\"3.11\"}]}", + "readme_renderer": "{\"rules_python_publish_deps_311_readme_renderer_py3_none_any_2fbca89b\":[{\"filename\":\"readme_renderer-44.0-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_readme_renderer_sdist_8712034e\":[{\"filename\":\"readme_renderer-44.0.tar.gz\",\"version\":\"3.11\"}]}", + "requests": "{\"rules_python_publish_deps_311_requests_py3_none_any_70761cfe\":[{\"filename\":\"requests-2.32.3-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_requests_sdist_55365417\":[{\"filename\":\"requests-2.32.3.tar.gz\",\"version\":\"3.11\"}]}", + "requests_toolbelt": "{\"rules_python_publish_deps_311_requests_toolbelt_py2_none_any_cccfdd66\":[{\"filename\":\"requests_toolbelt-1.0.0-py2.py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_requests_toolbelt_sdist_7681a0a3\":[{\"filename\":\"requests-toolbelt-1.0.0.tar.gz\",\"version\":\"3.11\"}]}", + "rfc3986": "{\"rules_python_publish_deps_311_rfc3986_py2_none_any_50b1502b\":[{\"filename\":\"rfc3986-2.0.0-py2.py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_rfc3986_sdist_97aacf9d\":[{\"filename\":\"rfc3986-2.0.0.tar.gz\",\"version\":\"3.11\"}]}", + "rich": "{\"rules_python_publish_deps_311_rich_py3_none_any_6049d5e6\":[{\"filename\":\"rich-13.9.4-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_rich_sdist_43959497\":[{\"filename\":\"rich-13.9.4.tar.gz\",\"version\":\"3.11\"}]}", + "secretstorage": "{\"rules_python_publish_deps_311_secretstorage_py3_none_any_f356e662\":[{\"filename\":\"SecretStorage-3.3.3-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_secretstorage_sdist_2403533e\":[{\"filename\":\"SecretStorage-3.3.3.tar.gz\",\"version\":\"3.11\"}]}", + "twine": "{\"rules_python_publish_deps_311_twine_py3_none_any_215dbe7b\":[{\"filename\":\"twine-5.1.1-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_twine_sdist_9aa08251\":[{\"filename\":\"twine-5.1.1.tar.gz\",\"version\":\"3.11\"}]}", + "urllib3": "{\"rules_python_publish_deps_311_urllib3_py3_none_any_ca899ca0\":[{\"filename\":\"urllib3-2.2.3-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_urllib3_sdist_e7d814a8\":[{\"filename\":\"urllib3-2.2.3.tar.gz\",\"version\":\"3.11\"}]}", + "zipp": "{\"rules_python_publish_deps_311_zipp_py3_none_any_a817ac80\":[{\"filename\":\"zipp-3.20.2-py3-none-any.whl\",\"version\":\"3.11\"}],\"rules_python_publish_deps_311_zipp_sdist_bc9eb26f\":[{\"filename\":\"zipp-3.20.2.tar.gz\",\"version\":\"3.11\"}]}" + }, + "packages": [ + "backports_tarfile", + "certifi", + "charset_normalizer", + "docutils", + "idna", + "importlib_metadata", + "jaraco_classes", + "jaraco_context", + "jaraco_functools", + "keyring", + "markdown_it_py", + "mdurl", + "more_itertools", + "nh3", + "pkginfo", + "pygments", + "readme_renderer", + "requests", + "requests_toolbelt", + "rfc3986", + "rich", + "twine", + "urllib3", + "zipp" + ], + "groups": {} + } + } + }, + "recordedRepoMappingEntries": [ + [ + "bazel_features+", + "bazel_features_globals", + "bazel_features++version_extension+bazel_features_globals" + ], + [ + "bazel_features+", + "bazel_features_version", + "bazel_features++version_extension+bazel_features_version" + ], + [ + "rules_python+", + "bazel_features", + "bazel_features+" + ], + [ + "rules_python+", + "bazel_skylib", + "bazel_skylib+" + ], + [ + "rules_python+", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_python+", + "pypi__build", + "rules_python++internal_deps+pypi__build" + ], + [ + "rules_python+", + "pypi__click", + "rules_python++internal_deps+pypi__click" + ], + [ + "rules_python+", + "pypi__colorama", + "rules_python++internal_deps+pypi__colorama" + ], + [ + "rules_python+", + "pypi__importlib_metadata", + "rules_python++internal_deps+pypi__importlib_metadata" + ], + [ + "rules_python+", + "pypi__installer", + "rules_python++internal_deps+pypi__installer" + ], + [ + "rules_python+", + "pypi__more_itertools", + "rules_python++internal_deps+pypi__more_itertools" + ], + [ + "rules_python+", + "pypi__packaging", + "rules_python++internal_deps+pypi__packaging" + ], + [ + "rules_python+", + "pypi__pep517", + "rules_python++internal_deps+pypi__pep517" + ], + [ + "rules_python+", + "pypi__pip", + "rules_python++internal_deps+pypi__pip" + ], + [ + "rules_python+", + "pypi__pip_tools", + "rules_python++internal_deps+pypi__pip_tools" + ], + [ + "rules_python+", + "pypi__pyproject_hooks", + "rules_python++internal_deps+pypi__pyproject_hooks" + ], + [ + "rules_python+", + "pypi__setuptools", + "rules_python++internal_deps+pypi__setuptools" + ], + [ + "rules_python+", + "pypi__tomli", + "rules_python++internal_deps+pypi__tomli" + ], + [ + "rules_python+", + "pypi__wheel", + "rules_python++internal_deps+pypi__wheel" + ], + [ + "rules_python+", + "pypi__zipp", + "rules_python++internal_deps+pypi__zipp" + ], + [ + "rules_python+", + "pythons_hub", + "rules_python++python+pythons_hub" + ], + [ + "rules_python++python+pythons_hub", + "python_3_10_host", + "rules_python++python+python_3_10_host" + ], + [ + "rules_python++python+pythons_hub", + "python_3_11_host", + "rules_python++python+python_3_11_host" + ], + [ + "rules_python++python+pythons_hub", + "python_3_12_host", + "rules_python++python+python_3_12_host" + ], + [ + "rules_python++python+pythons_hub", + "python_3_8_host", + "rules_python++python+python_3_8_host" + ], + [ + "rules_python++python+pythons_hub", + "python_3_9_host", + "rules_python++python+python_3_9_host" + ] + ] + } + }, "@@rules_python+//python/uv:uv.bzl%uv": { "general": { "bzlTransitiveDigest": "ijW9KS7qsIY+yBVvJ+Nr1mzwQox09j13DnE3iIwaeTM=", diff --git a/Package.swift b/Package.swift index 8227849e0..5aa8a875d 100644 --- a/Package.swift +++ b/Package.swift @@ -103,6 +103,7 @@ var targets: [PackageDescription.Target] = [ name: "SourceGraph", dependencies: [ .target(name: "Configuration"), + .target(name: "Extensions"), .product(name: "SwiftSyntax", package: "swift-syntax"), .product(name: "SystemPackage", package: "swift-system"), .target(name: "Shared"), diff --git a/Sources/BUILD.bazel b/Sources/BUILD.bazel index 3631704b1..1f6d24eb1 100644 --- a/Sources/BUILD.bazel +++ b/Sources/BUILD.bazel @@ -52,9 +52,14 @@ swift_library( "SourceGraph/Elements/DeclarationAttribute.swift", "SourceGraph/Elements/ImportStatement.swift", "SourceGraph/Elements/Location.swift", + "SourceGraph/Elements/ModuleBitset.swift", + "SourceGraph/Elements/ModuleID.swift", + "SourceGraph/Elements/ModuleNameInterner.swift", "SourceGraph/Elements/ProjectFileKind.swift", "SourceGraph/Elements/Reference.swift", "SourceGraph/Elements/SourceFile.swift", + "SourceGraph/Elements/USRID.swift", + "SourceGraph/Elements/USRInterner.swift", "SourceGraph/Mutators/AccessibilityCascader.swift", "SourceGraph/Mutators/AncestralReferenceEliminator.swift", "SourceGraph/Mutators/AppIntentsRetainer.swift", @@ -98,6 +103,7 @@ swift_library( module_name = "SourceGraph", deps = [ "//Sources:Configuration", + "//Sources:Extensions", "//Sources:Shared", "@swift-syntax//:SwiftSyntax", "@swift-system//:SystemPackage", diff --git a/Sources/Frontend/Commands/ScanCommand.swift b/Sources/Frontend/Commands/ScanCommand.swift index 8ac9efbb5..fdf052ed5 100644 --- a/Sources/Frontend/Commands/ScanCommand.swift +++ b/Sources/Frontend/Commands/ScanCommand.swift @@ -177,6 +177,7 @@ struct ScanCommand: ParsableCommand { configuration.guidedSetup = setup configuration.projectRoot = projectRoot + configuration.apply(\.$project, project) configuration.apply(\.$schemes, schemes) configuration.apply(\.$indexExclude, indexExclude) diff --git a/Sources/Frontend/Scan.swift b/Sources/Frontend/Scan.swift index 63bd169d9..9179d8f0b 100644 --- a/Sources/Frontend/Scan.swift +++ b/Sources/Frontend/Scan.swift @@ -17,7 +17,7 @@ final class Scan { self.configuration = configuration self.logger = logger self.swiftVersion = swiftVersion - graph = SourceGraph(configuration: configuration, logger: logger) + graph = SourceGraph(configuration: configuration) } func perform(project: Project) throws -> [ScanResult] { @@ -41,7 +41,11 @@ final class Scan { try build(driver) try index(driver) try analyze() - return buildResults() + let results = buildResults() + // Let the OS reclaim memory at process exit instead of paying + // the cost of tearing down the entire SourceGraph via ARC. + _ = Unmanaged.passRetained(graph) + return results } // MARK: - Private @@ -68,7 +72,9 @@ final class Scan { } let indexLogger = logger.contextualized(with: "index") + let planInterval = logger.beginInterval("index:plan") let plan = try driver.plan(logger: indexLogger) + logger.endInterval(planInterval) let graphMutex = SourceGraphMutex(graph: graph) let pipeline = IndexPipeline(plan: plan, graph: graphMutex, logger: indexLogger, configuration: configuration, swiftVersion: swiftVersion) try pipeline.perform() diff --git a/Sources/Indexer/IndexPipeline.swift b/Sources/Indexer/IndexPipeline.swift index 906900e81..98711e817 100644 --- a/Sources/Indexer/IndexPipeline.swift +++ b/Sources/Indexer/IndexPipeline.swift @@ -64,6 +64,8 @@ public struct IndexPipeline { ).perform() } + let interval = logger.beginInterval("index:complete") graph.withLock { $0.indexingComplete() } + logger.endInterval(interval) } } diff --git a/Sources/Indexer/SourceFileCollector.swift b/Sources/Indexer/SourceFileCollector.swift index cb2e53bf2..c5c13dcb7 100644 --- a/Sources/Indexer/SourceFileCollector.swift +++ b/Sources/Indexer/SourceFileCollector.swift @@ -43,12 +43,12 @@ public struct SourceFileCollector { let file = FilePath.makeAbsolute(filePath, relativeTo: currentFilePath) if !isExcluded(file) { - guard file.exists else { - logger.debug("Source file does not exist: \(file.string)") + if excludedTargets.contains(unit.moduleName) { return nil } - if excludedTargets.contains(unit.moduleName) { + guard file.exists else { + logger.debug("Source file does not exist: \(file.string)") return nil } diff --git a/Sources/Indexer/SwiftIndexer.swift b/Sources/Indexer/SwiftIndexer.swift index ef5cbb5a7..2ccc32f6a 100644 --- a/Sources/Indexer/SwiftIndexer.swift +++ b/Sources/Indexer/SwiftIndexer.swift @@ -4,6 +4,7 @@ import IndexStore import Logger import Shared import SourceGraph +import SwiftSyntax import SyntaxAnalysis import SystemPackage @@ -12,6 +13,12 @@ public struct IndexUnit { let unit: UnitReader } +private struct DanglingReferenceTables { + let declsByLocation: [Location: [Declaration]] + let declsByLine: [Int: [Declaration]] + let sortedDeclLines: ReversedCollection<[Int]> +} + final class SwiftIndexer: Indexer { private let sourceFiles: [SourceFile: [IndexUnit]] private let graph: SourceGraphMutex @@ -47,6 +54,8 @@ final class SwiftIndexer: Indexer { ) } + graph.reserveCapacity(forFileCount: sourceFiles.count) + let phaseOneInterval = logger.beginInterval("index:swift:phase:one") try JobPool(jobs: jobs).forEach { job in @@ -154,7 +163,7 @@ final class SwiftIndexer: Indexer { /// phase two. func phaseOne() throws { var rawDeclsByKey: [RawDeclaration.Key: [(RawDeclaration, [RawRelation])]] = [:] - var references: Set = [] + var references: [Reference] = [] for unit in units { for recordName in unit.unit.recordNames { @@ -162,8 +171,7 @@ final class SwiftIndexer: Indexer { record.forEach(occurrence: { occurrence in let usr = occurrence.symbol.usr - guard let location = self.transformLocation(occurrence.location) - else { return } + let location = self.transformLocation(occurrence.location) var relations: [RawRelation] = [] occurrence.forEach(relation: { relSymbol, relRoles in @@ -190,7 +198,7 @@ final class SwiftIndexer: Indexer { } if occurrence.roles.contains(.reference) { - references.formUnion(self.parseReference( + references.append(contentsOf: self.parseReference( occurrence, usr, location, @@ -199,7 +207,7 @@ final class SwiftIndexer: Indexer { } if occurrence.roles.contains(.implicit) { - references.formUnion(self.parseImplicit( + references.append(contentsOf: self.parseImplicit( usr, location, relations @@ -210,32 +218,35 @@ final class SwiftIndexer: Indexer { } var newDeclarations: Set = [] + var retainedDecls: Set = [] for (key, values) in rawDeclsByKey { let usrs = values.mapSet { $0.0.usr } - let decl = Declaration(name: key.name, kind: key.kind, usrs: usrs, location: key.location) + let usrIDs = usrs.map { cachedIntern($0) } + let decl = Declaration(name: key.name, kind: key.kind, usrs: usrs, usrIDs: usrIDs, location: key.location) decl.isImplicit = key.isImplicit decl.isObjcAccessible = key.isObjcAccessible if decl.isImplicit { - graph.withLock { $0.markRetained(decl) } + retainedDecls.insert(decl) } if decl.isObjcAccessible, configuration.retainObjcAccessible { - graph.withLock { $0.markRetained(decl) } + retainedDecls.insert(decl) } let relations = values.flatMap(\.1) - references.formUnion(parseDeclaration(decl, relations)) + references.append(contentsOf: parseDeclaration(decl, relations)) newDeclarations.insert(decl) declarations.append(decl) } - graph.withLock { graph in + try graph.withLock { graph in graph.add(references) - graph.add(newDeclarations) + try graph.add(newDeclarations) + graph.markRetained(retainedDecls) if retainAllDeclarations { graph.markRetained(newDeclarations) @@ -243,56 +254,104 @@ final class SwiftIndexer: Indexer { } establishDeclarationHierarchy() + + if !danglingReferences.isEmpty { + sortedDeclarationsCache = declarations.sorted() + } } /// Phase two associates latent references, and performs other actions that depend on the completed source graph. func phaseTwo() throws { - if !configuration.disableUnusedImportAnalysis { - graph.withLock { graph in - graph.addIndexedSourceFile(sourceFile) - graph.addIndexedModules(sourceFile.modules) - } + let hasWork = !declarations.isEmpty || !referencesByUsr.isEmpty || !danglingReferences.isEmpty + + if !hasWork, configuration.disableUnusedImportAnalysis { + return } let multiplexingSyntaxVisitor = try MultiplexingSyntaxVisitor(file: sourceFile, swiftVersion: swiftVersion) let declarationSyntaxVisitor = multiplexingSyntaxVisitor.add(DeclarationSyntaxVisitor.self) let importSyntaxVisitor = multiplexingSyntaxVisitor.add(ImportSyntaxVisitor.self) - + let functionCollector = multiplexingSyntaxVisitor.add(FunctionSyntaxCollector.self) multiplexingSyntaxVisitor.visit() sourceFile.importStatements = importSyntaxVisitor.importStatements sourceFile.importsSwiftTesting = importSyntaxVisitor.importStatements.contains(where: { $0.module == "Testing" }) - if !configuration.disableUnusedImportAnalysis { - for stmt in sourceFile.importStatements where stmt.isExported { - graph.withLock { graph in + if !hasWork { + graph.withLock { graph in + graph.addIndexedSourceFile(sourceFile) + graph.addIndexedModules(sourceFile.modules) + for stmt in sourceFile.importStatements where stmt.isExported { + graph.addExportedModule(stmt.module, exportedBy: sourceFile.modules) + } + } + return + } + + let declarationsByLocation = declarationSyntaxVisitor.resultsByLocation + let fileCommands = multiplexingSyntaxVisitor.parseComments() + + let danglingRefTables = buildDanglingReferenceTables() + + let analyzer = UnusedParameterAnalyzer() + let paramsByFunction = analyzer.analyze( + collectedFunctionDecls: functionCollector.functionDecls, + collectedInitializerDecls: functionCollector.initializerDecls, + file: multiplexingSyntaxVisitor.sourceFile, + locationConverter: multiplexingSyntaxVisitor.locationConverter, + parseProtocols: true + ) + + var unmatchedFunctions: [(String, Location)] = [] + + try graph.withLock { graph in + if !configuration.disableUnusedImportAnalysis { + graph.addIndexedSourceFile(sourceFile) + graph.addIndexedModules(sourceFile.modules) + for stmt in sourceFile.importStatements where stmt.isExported { graph.addExportedModule(stmt.module, exportedBy: sourceFile.modules) } } + + associateLatentReferencesUnsafe(graph: graph) + associateDanglingReferencesUnsafe(tables: danglingRefTables) + applyDeclarationMetadataUnsafe(declarationsByLocation) + try identifyUnusedParametersUnsafe( + paramsByFunction: paramsByFunction, + graph: graph, + unmatchedFunctions: &unmatchedFunctions + ) + applyCommentCommandsUnsafe(fileCommands: fileCommands, graph: graph) } - associateLatentReferences() - associateDanglingReferences() - visitDeclarations(using: declarationSyntaxVisitor) - identifyUnusedParameters(using: multiplexingSyntaxVisitor) - applyCommentCommands(using: multiplexingSyntaxVisitor) + for (name, location) in unmatchedFunctions { + logger.debug("Failed to associate indexed function for parameter function '\(name)' at \(location).") + } } // MARK: - Private private var declarations: [Declaration] = [] + private var sortedDeclarationsCache: [Declaration]? private var childDeclsByParentUsr: [String: Set] = [:] private var referencesByUsr: [String: Set] = [:] private var danglingReferences: [Reference] = [] private var varParameterUsrs: Set = [] private var extensionUsrMap: [String: String] = [:] + private var usrCache: [String: USRID] = [:] + + private func cachedIntern(_ usr: String) -> USRID { + if let cached = usrCache[usr] { return cached } + let id = graph.usrInterner.intern(usr) + usrCache[usr] = id + return id + } private func establishDeclarationHierarchy() { graph.withLock { graph in for (parent, decls) in childDeclsByParentUsr { guard let parentDecl = graph.declaration(withUsr: parent) else { if varParameterUsrs.contains(parent) { - // These declarations are children of a parameter and are redundant. decls.forEach { graph.remove($0) } } @@ -303,34 +362,25 @@ final class SwiftIndexer: Indexer { decl.parent = parentDecl } - parentDecl.declarations.formUnion(decls) + parentDecl.declarations.append(contentsOf: decls) } } } - private func associateLatentReferences() { + private func associateLatentReferencesUnsafe(graph: SourceGraph) { for (usr, refs) in referencesByUsr { - graph.withLock { graph in - if let decl = graph.declaration(withUsr: usr) { - for ref in refs { - associateUnsafe(ref, with: decl) - } - } else { - danglingReferences.append(contentsOf: refs) + if let decl = graph.declaration(withUsr: usr) { + for ref in refs { + associateUnsafe(ref, with: decl) } + } else { + danglingReferences.append(contentsOf: refs) } } } - // Swift does not associate some type references with the containing declaration, resulting in references - // with no clear parent. Property references are one example: https://github.com/apple/swift/issues/56163 - private func associateDanglingReferences() { - guard !danglingReferences.isEmpty else { return } - - // Sort declarations to ensure deterministic candidate selection when - // multiple declarations exist at the same location. - let sortedDeclarations = declarations.sorted() - + private func buildDanglingReferenceTables() -> DanglingReferenceTables { + let sortedDeclarations = sortedDeclarationsCache ?? declarations.sorted() let declsByLocation = sortedDeclarations .reduce(into: [Location: [Declaration]]()) { result, decl in result[decl.location, default: []].append(decl) @@ -340,124 +390,122 @@ final class SwiftIndexer: Indexer { result[decl.location.line, default: []].append(decl) } let sortedDeclLines = declsByLine.keys.sorted().reversed() + return DanglingReferenceTables( + declsByLocation: declsByLocation, + declsByLine: declsByLine, + sortedDeclLines: sortedDeclLines + ) + } + + // Swift does not associate some type references with the containing declaration, resulting in references + // with no clear parent. Property references are one example: https://github.com/apple/swift/issues/56163 + private func associateDanglingReferencesUnsafe(tables: DanglingReferenceTables) { + guard !danglingReferences.isEmpty else { return } for ref in danglingReferences { - let sameLineCandidateDecls = declsByLocation[ref.location] ?? - declsByLine[ref.location.line] + let sameLineCandidateDecls = tables.declsByLocation[ref.location] ?? + tables.declsByLine[ref.location.line] var candidateDecls = [Declaration]() if let sameLineCandidateDecls { candidateDecls = sameLineCandidateDecls } else { - // For references with no declaration on the same line, find the nearest preceding declaration. - if let line = sortedDeclLines.first(where: { $0 < ref.location.line }) { - candidateDecls = declsByLine[line]?.filter { - !$0.usrs.contains(ref.usr) && - !$0.ancestralDeclarations.contains(where: { $0.usrs.contains(ref.usr) }) + if let line = tables.sortedDeclLines.first(where: { $0 < ref.location.line }) { + candidateDecls = tables.declsByLine[line]?.filter { + !$0.usrIDs.contains(ref.usrID) && + !$0.ancestralDeclarations.contains(where: { $0.usrIDs.contains(ref.usrID) }) } ?? [] } } - // The vast majority of the time there will only be a single declaration for this location, - // however it is possible for there to be more than one. In that case, first attempt to associate with - // a decl without a parent, as the reference may be a related type of a class/struct/etc. if let decl = candidateDecls.first(where: { $0.parent == nil }) { - associate(ref, with: decl) + associateUnsafe(ref, with: decl) } else if let decl = candidateDecls.min() { - // Fallback to using the first declaration. - // Sorting the declarations helps in the situation where the candidate declarations includes a - // property/subscript, and a getter on the same line. The property/subscript is more likely to be - // the declaration that should hold the references. - associate(ref, with: decl) + associateUnsafe(ref, with: decl) } } } - private func applyCommentCommands(using syntaxVisitor: MultiplexingSyntaxVisitor) { - let fileCommands = syntaxVisitor.parseComments() + private func applyDeclarationMetadataUnsafe(_ declarationsByLocation: [Location: DeclarationSyntaxVisitor.Result]) { + for decl in declarations { + guard let result = declarationsByLocation[decl.location] else { continue } + + applyDeclarationMetadata(to: decl, with: result) + } + } + private func applyCommentCommandsUnsafe(fileCommands: [CommentCommand], graph: SourceGraph) { if fileCommands.contains(.ignoreAll) { - commandIgnore(declarations, kind: .file) + commandIgnoreUnsafe(declarations, kind: .file, graph: graph) } else { for decl in declarations where decl.commentCommands.contains(.ignore) { - commandIgnore([decl], kind: .declaration) + commandIgnoreUnsafe([decl], kind: .declaration, graph: graph) } } } - private func visitDeclarations(using declarationVisitor: DeclarationSyntaxVisitor) { - let declarationsByLocation = declarationVisitor.resultsByLocation + /// Must be called while holding the graph lock. + private func applyDeclarationMetadata(to decl: Declaration, with result: DeclarationSyntaxVisitor.Result) { + if let accessibility = result.accessibility { + decl.accessibility = .init(value: accessibility, isExplicit: true) + } - for decl in declarations { - guard let result = declarationsByLocation[decl.location] else { continue } + decl.attributes = result.attributes + decl.modifiers = Set(result.modifiers) + decl.commentCommands = Set(result.commentCommands) + decl.declaredType = result.variableType - applyDeclarationMetadata(to: decl, with: result) + decl.hasGenericFunctionReturnedMetatypeParameters = result.hasGenericFunctionReturnedMetatypeParameters + + for ref in decl.references { + assignRole(to: ref, decl: decl, result: result) + } + for ref in decl.related { + assignRole(to: ref, decl: decl, result: result) } } - private func applyDeclarationMetadata(to decl: Declaration, with result: DeclarationSyntaxVisitor.Result) { - graph.withLock { _ in - if let accessibility = result.accessibility { - decl.accessibility = .init(value: accessibility, isExplicit: true) - } - - decl.attributes = Set(result.attributes) - decl.modifiers = Set(result.modifiers) - decl.commentCommands = Set(result.commentCommands) - decl.declaredType = result.variableType - - decl.hasGenericFunctionReturnedMetatypeParameters = result.hasGenericFunctionReturnedMetatypeParameters - - for ref in decl.references.union(decl.related) { - if result.inheritedTypeLocations.contains(ref.location) { - if decl.kind.isConformableKind, ref.declarationKind == .protocol { - ref.role = .conformedType - } else if decl.kind == .protocol, ref.declarationKind == .protocol { - ref.role = .refinedProtocolType - } else if decl.kind == .class || decl.kind == .associatedtype { - ref.role = .inheritedType - } - } else if result.variableTypeLocations.contains(ref.location) { - ref.role = .varType - } else if result.returnTypeLocations.contains(ref.location) { - ref.role = .returnType - } else if result.throwTypeLocations.contains(ref.location) { - ref.role = .throwType - } else if result.parameterTypeLocations.contains(ref.location) { - ref.role = .parameterType - } else if result.genericParameterLocations.contains(ref.location) { - ref.role = .genericParameterType - } else if result.genericConformanceRequirementLocations.contains(ref.location) { - ref.role = .genericRequirementType - } else if result.variableInitFunctionCallLocations.contains(ref.location) { - ref.role = .variableInitFunctionCall - } else if result.functionCallMetatypeArgumentLocations.contains(ref.location) { - ref.role = .functionCallMetatypeArgument - } else if result.typeInitializerLocations.contains(ref.location) { - ref.role = .initializerType - } else if result.variableInitExprLocations.contains(ref.location) { - ref.role = .initializerType - } + private func assignRole(to ref: Reference, decl: Declaration, result: DeclarationSyntaxVisitor.Result) { + if result.inheritedTypeLocations.contains(ref.location) { + if decl.kind.isConformableKind, ref.declarationKind == .protocol { + ref.role = .conformedType + } else if decl.kind == .protocol, ref.declarationKind == .protocol { + ref.role = .refinedProtocolType + } else if decl.kind == .class || decl.kind == .associatedtype { + ref.role = .inheritedType } + } else if result.variableTypeLocations.contains(ref.location) { + ref.role = .varType + } else if result.returnTypeLocations.contains(ref.location) { + ref.role = .returnType + } else if result.throwTypeLocations.contains(ref.location) { + ref.role = .throwType + } else if result.parameterTypeLocations.contains(ref.location) { + ref.role = .parameterType + } else if result.genericParameterLocations.contains(ref.location) { + ref.role = .genericParameterType + } else if result.genericConformanceRequirementLocations.contains(ref.location) { + ref.role = .genericRequirementType + } else if result.variableInitFunctionCallLocations.contains(ref.location) { + ref.role = .variableInitFunctionCall + } else if result.functionCallMetatypeArgumentLocations.contains(ref.location) { + ref.role = .functionCallMetatypeArgument + } else if result.typeInitializerLocations.contains(ref.location) { + ref.role = .initializerType + } else if result.variableInitExprLocations.contains(ref.location) { + ref.role = .initializerType } } - private func commandIgnore(_ decls: [Declaration], kind: CommandIgnoreKind) { + private func commandIgnoreUnsafe(_ decls: [Declaration], kind: CommandIgnoreKind, graph: SourceGraph) { for decl in decls { - graph.withLock { graph in - graph.markRetained(decl) - decl.unusedParameters.forEach { graph.markRetained($0) } + graph.markRetained(decl) + decl.unusedParameters.forEach { graph.markRetained($0) } - graph.markCommandIgnored(decl, kind: kind) - decl.unusedParameters.forEach { graph.markCommandIgnored($0, kind: kind) } - } - commandIgnore(Array(decl.declarations), kind: kind) - } - } + graph.markCommandIgnored(decl, kind: kind) + decl.unusedParameters.forEach { graph.markCommandIgnored($0, kind: kind) } - private func associate(_ ref: Reference, with decl: Declaration) { - graph.withLock { _ in - associateUnsafe(ref, with: decl) + commandIgnoreUnsafe(decl.declarations, kind: kind, graph: graph) } } @@ -465,58 +513,54 @@ final class SwiftIndexer: Indexer { ref.parent = decl if ref.kind == .related { - decl.related.insert(ref) + decl.related.append(ref) } else { - decl.references.insert(ref) + decl.references.append(ref) } } - private func identifyUnusedParameters(using syntaxVisitor: MultiplexingSyntaxVisitor) { + private func identifyUnusedParametersUnsafe( + paramsByFunction: [Function: Set], + graph: SourceGraph, + unmatchedFunctions: inout [(String, Location)] + ) throws { let functionDecls = declarations.filter(\.kind.isFunctionKind) let functionDeclsByLocation = functionDecls.reduce(into: [Location: Declaration]()) { $0[$1.location] = $1 } - // Build a map of ignored param names per function, and track functions with ignored - // params so ScanResultBuilder can efficiently detect superfluous ignores. var ignoredParamsByLocation: [Location: [String]] = [:] + var functionsWithIgnoredParams: [Declaration] = [] for functionDecl in functionDecls { let ignoredParamNames = functionDecl.commentCommands.ignoredParameterNames if !ignoredParamNames.isEmpty { ignoredParamsByLocation[functionDecl.location] = ignoredParamNames - graph.withLock { $0.markHasIgnoredParameters(functionDecl) } + functionsWithIgnoredParams.append(functionDecl) } } - let analyzer = UnusedParameterAnalyzer() - let paramsByFunction = analyzer.analyze( - file: syntaxVisitor.sourceFile, - syntax: syntaxVisitor.syntax, - locationConverter: syntaxVisitor.locationConverter, - parseProtocols: true - ) + for functionDecl in functionsWithIgnoredParams { + graph.markHasIgnoredParameters(functionDecl) + } for (function, params) in paramsByFunction { guard let functionDecl = functionDeclsByLocation[function.location] else { - // The declaration may not exist if the code was not compiled due to build conditions, e.g #if. - logger.debug("Failed to associate indexed function for parameter function '\(function.name)' at \(function.location).") + unmatchedFunctions.append((function.name, function.location)) continue } let ignoredParamNames = ignoredParamsByLocation[functionDecl.location] ?? [] - graph.withLock { graph in - for param in params { - let paramDecl = param.makeDeclaration(withParent: functionDecl) - functionDecl.unusedParameters.insert(paramDecl) - graph.add(paramDecl) - - if retainAllDeclarations || (functionDecl.isObjcAccessible && configuration.retainObjcAccessible) { - graph.markRetained(paramDecl) - } else if ignoredParamNames.contains(param.name.text) { - graph.markRetained(paramDecl) - graph.markCommandIgnored(paramDecl, kind: .declaration) - } + for param in params { + let paramDecl = param.makeDeclaration(withParent: functionDecl, interner: self.graph.usrInterner) + functionDecl.unusedParameters.append(paramDecl) + try graph.add(paramDecl) + + if retainAllDeclarations || (functionDecl.isObjcAccessible && configuration.retainObjcAccessible) { + graph.markRetained(paramDecl) + } else if ignoredParamNames.contains(param.name.text) { + graph.markRetained(paramDecl) + graph.markCommandIgnored(paramDecl, kind: .declaration) } } } @@ -563,8 +607,8 @@ final class SwiftIndexer: Indexer { private func parseDeclaration( _ decl: Declaration, _ relations: [RawRelation] - ) -> Set { - var references: Set = [] + ) -> [Reference] { + var references: [Reference] = [] for rel in relations { if rel.roles.contains(.childOf) { @@ -581,16 +625,18 @@ final class SwiftIndexer: Indexer { let baseFunc = rel.symbol if let baseFuncUsr = baseFunc.usr, let baseFuncKind = transformDeclarationKind(baseFunc.kind, baseFunc.subKind) { + let usrID = cachedIntern(baseFuncUsr) let reference = Reference( name: baseFunc.name, kind: .related, declarationKind: baseFuncKind, + usrID: usrID, usr: baseFuncUsr, location: decl.location ) reference.parent = decl - decl.related.insert(reference) - references.insert(reference) + decl.related.append(reference) + references.append(reference) } } @@ -598,15 +644,17 @@ final class SwiftIndexer: Indexer { let referencer = rel.symbol if let referencerUsr = referencer.usr { - for usr in decl.usrs { + for usrID in decl.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let reference = Reference( name: decl.name, kind: rel.roles.contains(.baseOf) ? .related : .normal, declarationKind: decl.kind, + usrID: usrID, usr: usr, location: decl.location ) - references.insert(reference) + references.append(reference) referencesByUsr[referencerUsr, default: []].insert(reference) } } @@ -628,10 +676,12 @@ final class SwiftIndexer: Indexer { let baseFunc = relation.symbol if let baseFuncUsr = baseFunc.usr, let baseFuncKind = transformDeclarationKind(baseFunc.kind, baseFunc.subKind) { + let usrID = cachedIntern(baseFuncUsr) let reference = Reference( name: baseFunc.name, kind: .related, declarationKind: baseFuncKind, + usrID: usrID, usr: baseFuncUsr, location: location ) @@ -657,6 +707,7 @@ final class SwiftIndexer: Indexer { return [] } + let usrID = cachedIntern(occurrenceUsr) var refs = [Reference]() for relation in relations { @@ -668,6 +719,7 @@ final class SwiftIndexer: Indexer { name: occurrence.symbol.name, kind: relation.roles.contains(.baseOf) ? .related : .normal, declarationKind: kind, + usrID: usrID, usr: occurrenceUsr, location: location ) @@ -682,6 +734,7 @@ final class SwiftIndexer: Indexer { name: occurrence.symbol.name, kind: .normal, declarationKind: kind, + usrID: usrID, usr: occurrenceUsr, location: location ) @@ -697,7 +750,7 @@ final class SwiftIndexer: Indexer { return refs } - private func transformLocation(_ input: (line: Int, column: Int)) -> Location? { + private func transformLocation(_ input: (line: Int, column: Int)) -> Location { Location(file: sourceFile, line: input.line, column: input.column) } diff --git a/Sources/PeripheryKit/Results/OutputDeclarationFilter.swift b/Sources/PeripheryKit/Results/OutputDeclarationFilter.swift index cb33c793f..fb3a81d0f 100644 --- a/Sources/PeripheryKit/Results/OutputDeclarationFilter.swift +++ b/Sources/PeripheryKit/Results/OutputDeclarationFilter.swift @@ -20,12 +20,10 @@ public final class OutputDeclarationFilter { if let baseline { var didFilterDeclaration = false - let ignoredUsrs = declarations - .flatMapSet(\.usrs) - .intersection(baseline.usrs) + let baselineUsrs = baseline.usrs declarations = declarations.filter { - let isIgnored = $0.usrs.contains { ignoredUsrs.contains($0) } + let isIgnored = $0.usrs.contains { baselineUsrs.contains($0) } if isIgnored { didFilterDeclaration = true } @@ -45,7 +43,6 @@ public final class OutputDeclarationFilter { .filter { [contextualLogger] in var path = $0.declaration.location.file.path - // If the declaration has a location override, use it as the path for filtering. if let override = $0.declaration.commentCommands.locationOverride { let (overridePath, _, _) = override path = overridePath diff --git a/Sources/PeripheryKit/ScanResult.swift b/Sources/PeripheryKit/ScanResult.swift index 790cbd45d..7d141f1ed 100644 --- a/Sources/PeripheryKit/ScanResult.swift +++ b/Sources/PeripheryKit/ScanResult.swift @@ -5,7 +5,7 @@ public struct ScanResult { enum Annotation { case unused case assignOnlyProperty - case redundantProtocol(references: Set, inherited: Set) + case redundantProtocol(references: [Reference], inherited: Set) case redundantPublicAccessibility(modules: Set) case superfluousIgnoreCommand } diff --git a/Sources/PeripheryKit/ScanResultBuilder.swift b/Sources/PeripheryKit/ScanResultBuilder.swift index c0093d5c7..ef501173b 100644 --- a/Sources/PeripheryKit/ScanResultBuilder.swift +++ b/Sources/PeripheryKit/ScanResultBuilder.swift @@ -18,14 +18,13 @@ public enum ScanResultBuilder { !graph.retainedDeclarations.contains(removableDeclaration), !graph.ignoredDeclarations.contains(removableDeclaration) { - let decls = removableDeclaration.descendentDeclarations.union([removableDeclaration]) - - for decl in decls { - let extensions = graph.extensions[decl, default: []] - for ext in extensions { + let addExtensions = { (decl: Declaration) in + for ext in graph.extensions[decl, default: []] { extensionResults.append(ScanResult(declaration: ext, annotation: .unused)) } } + addExtensions(removableDeclaration) + removableDeclaration.forEachDescendentDeclaration(addExtensions) } return [ScanResult(declaration: removableDeclaration, annotation: .unused)] + extensionResults @@ -102,7 +101,7 @@ public enum ScanResultBuilder { if isProtocolMember, ref.kind != .normal { continue } if graph.commandIgnoredDeclarations[parent] == nil { - if graph.usedDeclarations.contains(parent) { + if parent.isUsed { return true } } @@ -125,10 +124,12 @@ public enum ScanResultBuilder { // The ignored parameter is actually used - create a result for it let parentUsrs = decl.usrs.sorted().joined(separator: "-") let usr = "param-\(ignoredParamName)-\(decl.name)-\(parentUsrs)" + let usrID = graph.usrInterner.intern(usr) let paramDecl = Declaration( name: ignoredParamName, kind: .varParameter, usrs: [usr], + usrIDs: [usrID], location: decl.location ) paramDecl.parent = decl diff --git a/Sources/SourceGraph/Elements/Declaration.swift b/Sources/SourceGraph/Elements/Declaration.swift index 382c58403..ef39d2c6b 100644 --- a/Sources/SourceGraph/Elements/Declaration.swift +++ b/Sources/SourceGraph/Elements/Declaration.swift @@ -1,7 +1,7 @@ import Foundation public final class Declaration { - public enum Kind: String, RawRepresentable, CaseIterable { + public enum Kind: String, RawRepresentable, CaseIterable, Hashable { case `associatedtype` case `class` case `enum` @@ -45,25 +45,81 @@ public final class Declaration { case varStatic = "var.static" case macro - static var functionKinds: Set { - Set(Kind.allCases.filter(\.isFunctionKind)) + // Integer discriminator for O(1) hashing/equality, bypassing the default + // RawRepresentable conformance which hashes the String raw value. + @usableFromInline var ordinal: Int { + switch self { + case .associatedtype: 0 + case .class: 1 + case .enum: 2 + case .enumelement: 3 + case .extension: 4 + case .extensionClass: 5 + case .extensionEnum: 6 + case .extensionProtocol: 7 + case .extensionStruct: 8 + case .functionAccessorAddress: 9 + case .functionAccessorDidset: 10 + case .functionAccessorGetter: 11 + case .functionAccessorMutableaddress: 12 + case .functionAccessorSetter: 13 + case .functionAccessorWillset: 14 + case .functionAccessorRead: 15 + case .functionAccessorModify: 16 + case .functionAccessorInit: 17 + case .functionConstructor: 18 + case .functionDestructor: 19 + case .functionFree: 20 + case .functionMethodClass: 21 + case .functionMethodInstance: 22 + case .functionMethodStatic: 23 + case .functionOperator: 24 + case .functionOperatorInfix: 25 + case .functionOperatorPostfix: 26 + case .functionOperatorPrefix: 27 + case .functionSubscript: 28 + case .genericTypeParam: 29 + case .module: 30 + case .precedenceGroup: 31 + case .protocol: 32 + case .struct: 33 + case .typealias: 34 + case .varClass: 35 + case .varGlobal: 36 + case .varInstance: 37 + case .varLocal: 38 + case .varParameter: 39 + case .varStatic: 40 + case .macro: 41 + } } - static var variableKinds: Set { - Set(Kind.allCases.filter(\.isVariableKind)) + @inlinable + public func hash(into hasher: inout Hasher) { + hasher.combine(ordinal) + } + + @inlinable + public static func == (lhs: Kind, rhs: Kind) -> Bool { + lhs.ordinal == rhs.ordinal } - static var protocolMemberKinds: [Kind] { + static let functionKinds: Set = + Set(Kind.allCases.filter(\.isFunctionKind)) + + static let variableKinds: Set = + Set(Kind.allCases.filter(\.isVariableKind)) + + static let protocolMemberKinds: [Kind] = { let functionKinds: [Kind] = [.functionMethodInstance, .functionMethodStatic, .functionSubscript, .functionOperator, .functionOperatorInfix, .functionOperatorPostfix, .functionOperatorPrefix, .functionConstructor] let variableKinds: [Kind] = [.varInstance, .varStatic] return functionKinds + variableKinds - } + }() - static var protocolMemberConformingKinds: [Kind] { - // Protocols cannot declare 'class' members, yet classes can fulfill the requirement with either a 'class' - // or 'static' member. + // Protocols cannot declare 'class' members, yet classes can fulfill the requirement + // with either a 'class' or 'static' member. + static let protocolMemberConformingKinds: [Kind] = protocolMemberKinds + [.varClass, .functionMethodClass, .associatedtype] - } public var isProtocolMemberKind: Bool { Self.protocolMemberKinds.contains(self) @@ -74,11 +130,27 @@ public final class Declaration { } public var isFunctionKind: Bool { - rawValue.hasPrefix("function") + switch self { + case .functionAccessorAddress, .functionAccessorDidset, .functionAccessorGetter, + .functionAccessorMutableaddress, .functionAccessorSetter, .functionAccessorWillset, + .functionAccessorRead, .functionAccessorModify, .functionAccessorInit, + .functionConstructor, .functionDestructor, .functionFree, + .functionMethodClass, .functionMethodInstance, .functionMethodStatic, + .functionOperator, .functionOperatorInfix, .functionOperatorPostfix, + .functionOperatorPrefix, .functionSubscript: + true + default: + false + } } public var isVariableKind: Bool { - rawValue.hasPrefix("var") + switch self { + case .varClass, .varGlobal, .varInstance, .varLocal, .varParameter, .varStatic: + true + default: + false + } } static var globalKinds: Set = [ @@ -94,9 +166,8 @@ public final class Declaration { .varGlobal, ] - static var extensionKinds: Set { + static let extensionKinds: Set = Set(Kind.allCases.filter(\.isExtensionKind)) - } public var extendedKind: Kind? { switch self { @@ -129,7 +200,12 @@ public final class Declaration { } public var isExtensionKind: Bool { - rawValue.hasPrefix("extension") + switch self { + case .extension, .extensionClass, .extensionEnum, .extensionProtocol, .extensionStruct: + true + default: + false + } } public var isExtendableKind: Bool { @@ -144,37 +220,41 @@ public final class Declaration { Self.discreteConformableKinds.contains(self) } - static var discreteConformableKinds: Set { + static let discreteConformableKinds: Set = [.class, .struct, .enum] - } public var isConcreteTypeDeclarableKind: Bool { - Self.concreteTypeDeclarableKinds.contains(self) + switch self { + case .class, .struct, .enum, .typealias: true + default: false + } } - static var concreteTypeDeclarableKinds: Set { + static let concreteTypeDeclarableKinds: Set = [.class, .struct, .enum, .typealias] - } - static var accessorKinds: Set { + static let accessorKinds: Set = Set(Kind.allCases.filter(\.isAccessorKind)) - } - static var accessibleKinds: Set { + static let accessibleKinds: Set = functionKinds.union(variableKinds).union(globalKinds) - } - static var overrideKinds: Set { + static let overrideKinds: Set = [.functionMethodInstance, .varInstance] - } public var isAccessorKind: Bool { - rawValue.hasPrefix("function.accessor") + switch self { + case .functionAccessorAddress, .functionAccessorDidset, .functionAccessorGetter, + .functionAccessorMutableaddress, .functionAccessorSetter, .functionAccessorWillset, + .functionAccessorRead, .functionAccessorModify, .functionAccessorInit: + true + default: + false + } } - static var toplevelAttributableKind: Set { + static let toplevelAttributableKind: Set = [.class, .struct, .enum] - } public var displayName: String { switch self { @@ -216,21 +296,27 @@ public final class Declaration { public let location: Location public var attributes: Set = [] - public var modifiers: Set = [] + public var modifiers: Set = [] { + didSet { _isOverride = modifiers.contains("override") } + } + + private var _isOverride: Bool = false public var accessibility: DeclarationAccessibility = .init(value: .internal, isExplicit: false) public let kind: Kind public let name: String public let usrs: Set - public var unusedParameters: Set = [] - public var declarations: Set = [] + public let usrIDs: [USRID] + public var unusedParameters: [Declaration] = [] + public var declarations: [Declaration] = [] public var commentCommands: Set = [] - public var references: Set = [] + public var references: [Reference] = [] public var declaredType: String? public var hasGenericFunctionReturnedMetatypeParameters: Bool = false public var parent: Declaration? - public var related: Set = [] + public var related: [Reference] = [] public var isImplicit: Bool = false public var isObjcAccessible: Bool = false + public internal(set) var isUsed: Bool = false private let hashValueCache: Int @@ -246,19 +332,23 @@ public final class Declaration { return declarations } - public var descendentDeclarations: Set { - declarations - .flatMapSet { $0.descendentDeclarations } - .union(declarations) - .union(unusedParameters) + public func forEachDescendentDeclaration(_ body: (Declaration) -> Void) { + for decl in declarations { + body(decl) + decl.forEachDescendentDeclaration(body) + } + for param in unusedParameters { + body(param) + param.forEachDescendentDeclaration(body) + } } - public var immediateInheritedTypeReferences: Set { + public var immediateInheritedTypeReferences: [Reference] { let superclassReferences = related.filter { [.class, .struct, .protocol].contains($0.declarationKind) } // Inherited typealiases are References instead of a Related. let typealiasReferences = references.filter { $0.declarationKind == .typealias } - return superclassReferences.union(typealiasReferences) + return superclassReferences + typealiasReferences } public var isComplexProperty: Bool { @@ -278,9 +368,7 @@ public final class Declaration { } } - public var isOverride: Bool { - modifiers.contains("override") - } + public var isOverride: Bool { _isOverride } public var relatedEquivalentReferences: [Reference] { related.filter { $0.declarationKind == kind && $0.name == name } @@ -290,13 +378,23 @@ public final class Declaration { name: String, kind: Kind, usrs: Set, + usrIDs: [USRID], location: Location ) { self.name = name self.kind = kind self.usrs = usrs + self.usrIDs = usrIDs.count > 1 ? usrIDs.sorted(by: { $0.rawValue < $1.rawValue }) : usrIDs self.location = location - hashValueCache = usrs.hashValue + if self.usrIDs.count == 1 { + hashValueCache = self.usrIDs[0].hashValue + } else { + var hasher = Hasher() + for id in self.usrIDs { + hasher.combine(id) + } + hashValueCache = hasher.finalize() + } } func isDeclaredInExtension(kind: Declaration.Kind) -> Bool { @@ -314,7 +412,10 @@ extension Declaration: Hashable { extension Declaration: Equatable { public static func == (lhs: Declaration, rhs: Declaration) -> Bool { - lhs.usrs == rhs.usrs + if lhs === rhs { return true } + guard lhs.hashValueCache == rhs.hashValueCache else { return false } + + return lhs.usrIDs == rhs.usrIDs } } diff --git a/Sources/SourceGraph/Elements/Location.swift b/Sources/SourceGraph/Elements/Location.swift index 03c7933cb..84b002c32 100644 --- a/Sources/SourceGraph/Elements/Location.swift +++ b/Sources/SourceGraph/Elements/Location.swift @@ -1,7 +1,8 @@ +import Extensions import Foundation import SystemPackage -public final class Location { +public struct Location { public let file: SourceFile public let line: Int public let column: Int @@ -12,14 +13,11 @@ public final class Location { self.file = file self.line = line self.column = column - hashValueCache = [file.hashValue, line, column].hashValue - } - - func relativeTo(_ path: FilePath) -> Location { - let newPath = file.path.relativeTo(path) - let newFile = SourceFile(path: newPath, modules: file.modules) - newFile.importStatements = file.importStatements - return Location(file: newFile, line: line, column: column) + var hasher = Hasher() + hasher.combine(file) + hasher.combine(line) + hasher.combine(column) + hashValueCache = hasher.finalize() } // MARK: - Private @@ -27,10 +25,6 @@ public final class Location { private func buildDescription(path: String) -> String { [path, line.description, column.description].joined(separator: ":") } - - private lazy var descriptionInternal: String = buildDescription(path: file.path.string) - - private lazy var shortDescriptionInternal: String = buildDescription(path: file.path.lastComponent?.string ?? "") } extension Location: Equatable { @@ -47,11 +41,11 @@ extension Location: Hashable { extension Location: CustomStringConvertible { public var description: String { - descriptionInternal + buildDescription(path: file.path.string) } public var shortDescription: String { - shortDescriptionInternal + buildDescription(path: file.path.lastComponent?.string ?? "") } } diff --git a/Sources/SourceGraph/Elements/ModuleBitset.swift b/Sources/SourceGraph/Elements/ModuleBitset.swift new file mode 100644 index 000000000..d28f8894e --- /dev/null +++ b/Sources/SourceGraph/Elements/ModuleBitset.swift @@ -0,0 +1,45 @@ +/// Dynamically-sized bitset over `ModuleID` values. Backed by `[UInt64]` +/// sized to the number of interned modules, replacing `Set` in +/// hot paths where `formUnion` / `contains` dominated the profile. +struct ModuleBitset { + var words: [UInt64] + private var _nonEmpty: Bool = false + + init(wordCount: Int) { + words = [UInt64](repeating: 0, count: wordCount) + } + + var isEmpty: Bool { !_nonEmpty } + + mutating func insert(_ id: ModuleID) { + let (word, bit) = id.rawValue.quotientAndRemainder(dividingBy: 64) + if word >= words.count { + words.append(contentsOf: [UInt64](repeating: 0, count: word + 1 - words.count)) + } + words[word] |= 1 &<< bit + _nonEmpty = true + } + + func contains(_ id: ModuleID) -> Bool { + let (word, bit) = id.rawValue.quotientAndRemainder(dividingBy: 64) + guard word < words.count else { return false } + + return words[word] & (1 &<< bit) != 0 + } + + mutating func formUnion(_ other: ModuleBitset) { + if other.words.count > words.count { + words.append(contentsOf: [UInt64](repeating: 0, count: other.words.count - words.count)) + } + other.words.withUnsafeBufferPointer { src in + words.withUnsafeMutableBufferPointer { dst in + var i = 0 + while i < src.count { + dst[i] |= src[i] + i &+= 1 + } + } + } + if other._nonEmpty { _nonEmpty = true } + } +} diff --git a/Sources/SourceGraph/Elements/ModuleID.swift b/Sources/SourceGraph/Elements/ModuleID.swift new file mode 100644 index 000000000..556020818 --- /dev/null +++ b/Sources/SourceGraph/Elements/ModuleID.swift @@ -0,0 +1,26 @@ +/// Lightweight integer handle representing an interned module name. +/// Used in place of `String` in hot-path `Set`/`Dictionary` operations +/// where string hashing and equality dominate the profile. +public struct ModuleID: Hashable, Comparable, Sendable { + /// Exclusive upper bound for `rawValue` so that two `ModuleID`s can be + /// packed into a single `Int` without collisions using base-`packingRadix`. + @usableFromInline static let packingRadix = 65537 + + @usableFromInline let rawValue: Int + + @inlinable + init(_ rawValue: Int) { + precondition(rawValue < Self.packingRadix, "ModuleID rawValue exceeds supported packing range") + self.rawValue = rawValue + } + + @inlinable + public static func < (lhs: ModuleID, rhs: ModuleID) -> Bool { + lhs.rawValue < rhs.rawValue + } + + @inlinable + public func hash(into hasher: inout Hasher) { + hasher.combine(rawValue) + } +} diff --git a/Sources/SourceGraph/Elements/ModuleNameInterner.swift b/Sources/SourceGraph/Elements/ModuleNameInterner.swift new file mode 100644 index 000000000..b276b9af1 --- /dev/null +++ b/Sources/SourceGraph/Elements/ModuleNameInterner.swift @@ -0,0 +1,33 @@ +/// Maps module name strings to compact `ModuleID` integers and back. +/// Not thread-safe -- callers must serialize access externally +/// (e.g. via `SourceGraphMutex` during indexing, serial execution during mutation). +final class ModuleNameInterner { + private var nameToID: [String: ModuleID] = [:] + private var idToName: [String] = [] + + init() {} + + var moduleCount: Int { idToName.count } + + var wordCount: Int { (moduleCount + 63) / 64 } + + func intern(_ name: String) -> ModuleID { + if let id = nameToID[name] { return id } + let id = ModuleID(idToName.count) + nameToID[name] = id + idToName.append(name) + return id + } + + func intern(_ names: Set) -> Set { + Set(names.map { intern($0) }) + } + + func internBitset(_ names: Set, wordCount: Int) -> ModuleBitset { + var bitset = ModuleBitset(wordCount: wordCount) + for name in names { + bitset.insert(intern(name)) + } + return bitset + } +} diff --git a/Sources/SourceGraph/Elements/Reference.swift b/Sources/SourceGraph/Elements/Reference.swift index 7dade0259..2de8a3217 100644 --- a/Sources/SourceGraph/Elements/Reference.swift +++ b/Sources/SourceGraph/Elements/Reference.swift @@ -1,11 +1,31 @@ public final class Reference { - public enum Kind: String { + // Explicit Hashable/Equatable using integer ordinals instead of the default + // RawRepresentable conformance, which hashes/compares the String raw value. + public enum Kind: String, Hashable, Equatable { case normal case related case retained + + @usableFromInline var ordinal: Int { + switch self { + case .normal: 0 + case .related: 1 + case .retained: 2 + } + } + + @inlinable + public func hash(into hasher: inout Hasher) { + hasher.combine(ordinal) + } + + @inlinable + public static func == (lhs: Kind, rhs: Kind) -> Bool { + lhs.ordinal == rhs.ordinal + } } - public enum Role: String { + public enum Role: String, Hashable, Equatable { case varType case returnType case parameterType @@ -20,6 +40,34 @@ public final class Reference { case functionCallMetatypeArgument case unknown + @usableFromInline var ordinal: Int { + switch self { + case .varType: 0 + case .returnType: 1 + case .parameterType: 2 + case .throwType: 3 + case .genericParameterType: 4 + case .genericRequirementType: 5 + case .inheritedType: 6 + case .refinedProtocolType: 7 + case .conformedType: 8 + case .initializerType: 9 + case .variableInitFunctionCall: 10 + case .functionCallMetatypeArgument: 11 + case .unknown: 12 + } + } + + @inlinable + public func hash(into hasher: inout Hasher) { + hasher.combine(ordinal) + } + + @inlinable + public static func == (lhs: Role, rhs: Role) -> Bool { + lhs.ordinal == rhs.ordinal + } + var isPubliclyExposable: Bool { switch self { case .varType, .returnType, .parameterType, .throwType, .genericParameterType, .genericRequirementType, .inheritedType, .refinedProtocolType, .initializerType, .variableInitFunctionCall, .functionCallMetatypeArgument: @@ -35,8 +83,8 @@ public final class Reference { public let declarationKind: Declaration.Kind public let name: String public var parent: Declaration? - public var references: Set = [] public let usr: String + public let usrID: USRID public var role: Role = .unknown private let hashValueCache: Int @@ -45,19 +93,21 @@ public final class Reference { name: String, kind: Kind, declarationKind: Declaration.Kind, + usrID: USRID, usr: String, location: Location ) { self.name = name self.kind = kind self.declarationKind = declarationKind + self.usrID = usrID self.usr = usr self.location = location - hashValueCache = [usr.hashValue, location.hashValue, kind.hashValue].hashValue - } - - var descendentReferences: Set { - references.flatMapSet { $0.descendentReferences }.union(references) + var hasher = Hasher() + hasher.combine(usrID) + hasher.combine(location) + hasher.combine(kind) + hashValueCache = hasher.finalize() } } @@ -69,7 +119,7 @@ extension Reference: Hashable { extension Reference: Equatable { public static func == (lhs: Reference, rhs: Reference) -> Bool { - lhs.usr == rhs.usr && lhs.location == rhs.location && lhs.kind == rhs.kind + lhs.usrID == rhs.usrID && lhs.kind == rhs.kind && lhs.location == rhs.location } } diff --git a/Sources/SourceGraph/Elements/SourceFile.swift b/Sources/SourceGraph/Elements/SourceFile.swift index a92a223b5..17cd7d437 100644 --- a/Sources/SourceGraph/Elements/SourceFile.swift +++ b/Sources/SourceGraph/Elements/SourceFile.swift @@ -7,15 +7,20 @@ public final class SourceFile { public var importStatements: [ImportStatement] = [] public var importsSwiftTesting = false + // Pre-computed to avoid re-hashing the FilePath on every Set/Dictionary operation, + // since SourceFile is used as a key in many hot-path collections. + private let hashValueCache: Int + public init(path: FilePath, modules: Set) { self.path = path self.modules = modules + hashValueCache = path.hashValue } } extension SourceFile: Hashable { public func hash(into hasher: inout Hasher) { - hasher.combine(path) + hasher.combine(hashValueCache) } } diff --git a/Sources/SourceGraph/Elements/USRID.swift b/Sources/SourceGraph/Elements/USRID.swift new file mode 100644 index 000000000..04ce09a4d --- /dev/null +++ b/Sources/SourceGraph/Elements/USRID.swift @@ -0,0 +1,16 @@ +/// Lightweight integer handle representing an interned USR string. +/// Replaces raw `String` keys in hot-path dictionary operations where +/// string hashing and equality (memcmp) dominate the profile. +public struct USRID: Hashable { + @usableFromInline let rawValue: Int + + @inlinable + init(_ rawValue: Int) { + self.rawValue = rawValue + } + + @inlinable + public func hash(into hasher: inout Hasher) { + hasher.combine(rawValue) + } +} diff --git a/Sources/SourceGraph/Elements/USRInterner.swift b/Sources/SourceGraph/Elements/USRInterner.swift new file mode 100644 index 000000000..dfe1cb552 --- /dev/null +++ b/Sources/SourceGraph/Elements/USRInterner.swift @@ -0,0 +1,64 @@ +import Synchronization + +/// Maps USR strings to compact `USRID` integers and back. +/// Thread-safe: uses 16 sharded locks for the string-to-ID direction to reduce +/// contention during concurrent indexing, with an atomic counter for ID +/// allocation and a separate lock for the ID-to-string reverse map. +public final class USRInterner: @unchecked Sendable { + private static let shardCount = 16 + private static let shardMask = shardCount - 1 + + private final class Shard: @unchecked Sendable { + let dict = Mutex<[String: USRID]>([:]) + + func reserveCapacity(_ n: Int) { + dict.withLock { $0.reserveCapacity(n) } + } + } + + private let shards: [Shard] + private let nextID = Atomic(0) + private let reverseMap = Mutex<[String]>([]) + + public init() { + shards = (0.. USRID { + let shard = shards[usr.hashValue & Self.shardMask] + return shard.dict.withLock { dict in + if let id = dict[usr] { return id } + let (rawID, _) = nextID.add(1, ordering: .relaxed) + let id = USRID(rawID) + dict[usr] = id + reverseMap.withLock { storage in + if rawID >= storage.count { + storage.append(contentsOf: repeatElement("", count: rawID + 1 - storage.count)) + } + storage[rawID] = usr + } + return id + } + } + + public func existing(_ usr: String) -> USRID? { + let shard = shards[usr.hashValue & Self.shardMask] + return shard.dict.withLock { $0[usr] } + } + + public func string(for id: USRID) -> String { + reverseMap.withLock { $0[id.rawValue] } + } + + public func reserveCapacity(_ n: Int) { + let perShard = n / Self.shardCount + 1 + for shard in shards { + shard.reserveCapacity(perShard) + } + reverseMap.withLock { $0.reserveCapacity(n) } + } +} diff --git a/Sources/SourceGraph/Mutators/AncestralReferenceEliminator.swift b/Sources/SourceGraph/Mutators/AncestralReferenceEliminator.swift index bd50674e3..f752e8b57 100644 --- a/Sources/SourceGraph/Mutators/AncestralReferenceEliminator.swift +++ b/Sources/SourceGraph/Mutators/AncestralReferenceEliminator.swift @@ -11,28 +11,31 @@ final class AncestralReferenceEliminator: SourceGraphMutator { func mutate() { for declaration in graph.rootDeclarations { - eliminateAncestralReferences(in: declaration, stack: declaration.usrs) + var stack = declaration.usrIDs + eliminateAncestralReferences(in: declaration, stack: &stack) } } - private func eliminateAncestralReferences(in declaration: Declaration, stack: Set) { + // Uses an Array rather than a Set for the ancestor stack because the stack depth is + // typically 5-20 elements, where linear scan of contiguous integers is cheaper than hashing. + private func eliminateAncestralReferences(in declaration: Declaration, stack: inout [USRID]) { guard !graph.isRetained(declaration) else { return } eliminateAncestralReferences(in: declaration.references, stack: stack) for childDeclaration in declaration.declarations { - let newStack = stack.union(childDeclaration.usrs) - eliminateAncestralReferences(in: childDeclaration, stack: newStack) + let prevCount = stack.count + stack.append(contentsOf: childDeclaration.usrIDs) + eliminateAncestralReferences(in: childDeclaration, stack: &stack) + stack.removeSubrange(prevCount...) } } - private func eliminateAncestralReferences(in references: Set, stack: Set) { + private func eliminateAncestralReferences(in references: [Reference], stack: [USRID]) { for reference in references { - if stack.contains(reference.usr) { + if stack.contains(reference.usrID) { graph.remove(reference) } - - eliminateAncestralReferences(in: reference.references, stack: stack) } } } diff --git a/Sources/SourceGraph/Mutators/AssetReferenceRetainer.swift b/Sources/SourceGraph/Mutators/AssetReferenceRetainer.swift index c1a29180e..97f1ddc81 100644 --- a/Sources/SourceGraph/Mutators/AssetReferenceRetainer.swift +++ b/Sources/SourceGraph/Mutators/AssetReferenceRetainer.swift @@ -42,7 +42,7 @@ final class AssetReferenceRetainer: SourceGraphMutator { if sources.contains(.xcDataModel) { // ValueTransformer subclasses are referenced by generated code that Periphery cannot analyze. graph.unmarkRedundantPublicAccessibility(declaration) - declaration.descendentDeclarations.forEach { graph.unmarkRedundantPublicAccessibility($0) } + declaration.forEachDescendentDeclaration { graph.unmarkRedundantPublicAccessibility($0) } } if sources.contains(.interfaceBuilder) { diff --git a/Sources/SourceGraph/Mutators/AssignOnlyPropertyReferenceEliminator.swift b/Sources/SourceGraph/Mutators/AssignOnlyPropertyReferenceEliminator.swift index 4f0c78895..d6bdafb20 100644 --- a/Sources/SourceGraph/Mutators/AssignOnlyPropertyReferenceEliminator.swift +++ b/Sources/SourceGraph/Mutators/AssignOnlyPropertyReferenceEliminator.swift @@ -3,18 +3,19 @@ import Foundation import Shared enum AssignOnlyPropertyAnalyzer { - static func isAssignOnlyProperty( - _ property: Declaration, - graph: SourceGraph, - configuration: Configuration - ) -> Bool { + static func retainedTypes(for configuration: Configuration) -> [String] { let defaultRetainedTypes = ["AnyCancellable", "Set", "[AnyCancellable]", "NSKeyValueObservation"] - let retainAssignOnlyPropertyTypes = defaultRetainedTypes + configuration.retainAssignOnlyPropertyTypes.map { + return defaultRetainedTypes + configuration.retainAssignOnlyPropertyTypes.map { PropertyTypeSanitizer.sanitize($0) } + } - guard !configuration.retainAssignOnlyProperties, - property.kind.isVariableKind, + static func isAssignOnlyProperty( + _ property: Declaration, + graph: SourceGraph, + retainAssignOnlyPropertyTypes: [String] + ) -> Bool { + guard property.kind.isVariableKind, let declaredType = property.declaredType, !retainAssignOnlyPropertyTypes.contains(declaredType), property.attributes.isEmpty, @@ -47,8 +48,10 @@ final class AssignOnlyPropertyReferenceEliminator: SourceGraphMutator { func mutate() throws { guard !configuration.retainAssignOnlyProperties else { return } + let retainedTypes = AssignOnlyPropertyAnalyzer.retainedTypes(for: configuration) + for property in graph.declarations(ofKinds: Declaration.Kind.variableKinds) { - if AssignOnlyPropertyAnalyzer.isAssignOnlyProperty(property, graph: graph, configuration: configuration) { + if AssignOnlyPropertyAnalyzer.isAssignOnlyProperty(property, graph: graph, retainAssignOnlyPropertyTypes: retainedTypes) { if graph.isRetained(property) { graph.markSuppressedAssignOnlyProperty(property) } else { diff --git a/Sources/SourceGraph/Mutators/CodingKeyEnumReferenceBuilder.swift b/Sources/SourceGraph/Mutators/CodingKeyEnumReferenceBuilder.swift index 1de90afa1..bbb69ca1d 100644 --- a/Sources/SourceGraph/Mutators/CodingKeyEnumReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/CodingKeyEnumReferenceBuilder.swift @@ -28,12 +28,13 @@ final class CodingKeyEnumReferenceBuilder: SourceGraphMutator { } if graph.isCodable(parent) { - // Build a reference from the Codable type to the CodingKey enum. - for usr in enumDeclaration.usrs { + for usrID in enumDeclaration.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let newReference = Reference( name: enumDeclaration.name, kind: .normal, declarationKind: .enum, + usrID: usrID, usr: usr, location: enumDeclaration.location ) diff --git a/Sources/SourceGraph/Mutators/ComplexPropertyAccessorReferenceBuilder.swift b/Sources/SourceGraph/Mutators/ComplexPropertyAccessorReferenceBuilder.swift index b2cd48356..ca922519a 100644 --- a/Sources/SourceGraph/Mutators/ComplexPropertyAccessorReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/ComplexPropertyAccessorReferenceBuilder.swift @@ -20,11 +20,13 @@ final class ComplexPropertyAccessorReferenceBuilder: SourceGraphMutator { guard let parent = declaration.parent else { continue } if parent.isComplexProperty { - for usr in declaration.usrs { + for usrID in declaration.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let reference = Reference( name: declaration.name, kind: .normal, declarationKind: declaration.kind, + usrID: usrID, usr: usr, location: declaration.location ) diff --git a/Sources/SourceGraph/Mutators/DefaultConstructorReferenceBuilder.swift b/Sources/SourceGraph/Mutators/DefaultConstructorReferenceBuilder.swift index 743c2bbc4..dbea26b1c 100644 --- a/Sources/SourceGraph/Mutators/DefaultConstructorReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/DefaultConstructorReferenceBuilder.swift @@ -25,11 +25,13 @@ final class DefaultConstructorReferenceBuilder: SourceGraphMutator { for constructor in defaultConstructors { if let parent = constructor.parent { - for usr in constructor.usrs { + for usrID in constructor.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let reference = Reference( name: constructor.name, kind: .normal, declarationKind: .functionConstructor, + usrID: usrID, usr: usr, location: parent.location ) @@ -43,11 +45,13 @@ final class DefaultConstructorReferenceBuilder: SourceGraphMutator { private func referenceDestructors() { for destructor in graph.declarations(ofKind: .functionDestructor) { if let parent = destructor.parent { - for usr in destructor.usrs { + for usrID in destructor.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let reference = Reference( name: destructor.name, kind: .normal, declarationKind: .functionDestructor, + usrID: usrID, usr: usr, location: parent.location ) diff --git a/Sources/SourceGraph/Mutators/EnumCaseReferenceBuilder.swift b/Sources/SourceGraph/Mutators/EnumCaseReferenceBuilder.swift index d32d303b8..989f708fb 100644 --- a/Sources/SourceGraph/Mutators/EnumCaseReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/EnumCaseReferenceBuilder.swift @@ -20,11 +20,13 @@ final class EnumCaseReferenceBuilder: SourceGraphMutator { let enumCases = enumDeclaration.declarations.filter { $0.kind == .enumelement } for enumCase in enumCases { - for usr in enumCase.usrs { + for usrID in enumCase.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let reference = Reference( name: enumCase.name, kind: .normal, declarationKind: .enumelement, + usrID: usrID, usr: usr, location: enumCase.location ) diff --git a/Sources/SourceGraph/Mutators/ExtensionReferenceBuilder.swift b/Sources/SourceGraph/Mutators/ExtensionReferenceBuilder.swift index 450c90428..62451ae36 100644 --- a/Sources/SourceGraph/Mutators/ExtensionReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/ExtensionReferenceBuilder.swift @@ -20,7 +20,7 @@ final class ExtensionReferenceBuilder: SourceGraphMutator { private func foldExtension(kind: Declaration.Kind) throws { for extensionDeclaration in graph.declarations(ofKind: kind) { guard let extendedTypeReference = try graph.extendedDeclarationReference(forExtension: extensionDeclaration) else { continue } - guard let extendedDeclaration = graph.declaration(withUsr: extendedTypeReference.usr) else { + guard let extendedDeclaration = graph.declaration(withUsrID: extendedTypeReference.usrID) else { // This is an extension on an external type and cannot be folded. graph.markRetained(extensionDeclaration) continue @@ -29,9 +29,9 @@ final class ExtensionReferenceBuilder: SourceGraphMutator { // Don't fold protocol extensions as they must be treated differently. guard kind != .extensionProtocol else { continue } - extendedDeclaration.declarations.formUnion(extensionDeclaration.declarations) - extendedDeclaration.references.formUnion(extensionDeclaration.references) - extendedDeclaration.related.formUnion(extensionDeclaration.related) + extendedDeclaration.declarations.append(contentsOf: extensionDeclaration.declarations) + extendedDeclaration.references.append(contentsOf: extensionDeclaration.references) + extendedDeclaration.related.append(contentsOf: extensionDeclaration.related) extensionDeclaration.declarations.forEach { $0.parent = extendedDeclaration } extensionDeclaration.references.forEach { $0.parent = extendedDeclaration } diff --git a/Sources/SourceGraph/Mutators/ExternalOverrideRetainer.swift b/Sources/SourceGraph/Mutators/ExternalOverrideRetainer.swift index 761625d11..c8416db06 100644 --- a/Sources/SourceGraph/Mutators/ExternalOverrideRetainer.swift +++ b/Sources/SourceGraph/Mutators/ExternalOverrideRetainer.swift @@ -23,7 +23,7 @@ final class ExternalOverrideRetainer: SourceGraphMutator { $0.location == decl.location } - let hasExternalMatch = matchingRelatedRefs.contains { graph.declaration(withUsr: $0.usr) == nil } + let hasExternalMatch = matchingRelatedRefs.contains { graph.declaration(withUsrID: $0.usrID) == nil } if hasExternalMatch { // One or more matching related declarations are external. diff --git a/Sources/SourceGraph/Mutators/ExternalTypeProtocolConformanceReferenceRemover.swift b/Sources/SourceGraph/Mutators/ExternalTypeProtocolConformanceReferenceRemover.swift index 9c52b097e..ac5ec57b5 100644 --- a/Sources/SourceGraph/Mutators/ExternalTypeProtocolConformanceReferenceRemover.swift +++ b/Sources/SourceGraph/Mutators/ExternalTypeProtocolConformanceReferenceRemover.swift @@ -17,7 +17,9 @@ final class ExternalTypeProtocolConformanceReferenceRemover: SourceGraphMutator guard try graph.extendedDeclaration(forExtension: extDecl) == nil else { continue } // Ensure the type is extended by local protocols. - let protocolDecls = extDecl.related.filter { $0.declarationKind == .protocol }.map { graph.declaration(withUsr: $0.usr) } + let protocolDecls = extDecl.related + .filter { $0.declarationKind == .protocol } + .compactMap { graph.declaration(withUsrID: $0.usrID) } guard !protocolDecls.isEmpty else { continue } // Find all related references that may be protocol members. @@ -26,7 +28,7 @@ final class ExternalTypeProtocolConformanceReferenceRemover: SourceGraphMutator for relatedRef in relatedRefs { // Ensure the relatedDecl is a member of a protocol. guard - let relatedDecl = graph.declaration(withUsr: relatedRef.usr), + let relatedDecl = graph.declaration(withUsrID: relatedRef.usrID), let parentDecl = relatedDecl.parent, protocolDecls.contains(parentDecl) else { continue } diff --git a/Sources/SourceGraph/Mutators/GenericClassAndStructConstructorReferenceBuilder.swift b/Sources/SourceGraph/Mutators/GenericClassAndStructConstructorReferenceBuilder.swift index e8fb478d3..5a73991c7 100644 --- a/Sources/SourceGraph/Mutators/GenericClassAndStructConstructorReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/GenericClassAndStructConstructorReferenceBuilder.swift @@ -21,11 +21,13 @@ final class GenericClassAndStructConstructorReferenceBuilder: SourceGraphMutator let constructors = declaration.declarations.filter { $0.kind == .functionConstructor } for constructor in constructors { - for usr in constructor.usrs { + for usrID in constructor.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let reference = Reference( name: constructor.name, kind: .normal, declarationKind: .functionConstructor, + usrID: usrID, usr: usr, location: declaration.location ) diff --git a/Sources/SourceGraph/Mutators/InheritedImplicitInitializerReferenceBuilder.swift b/Sources/SourceGraph/Mutators/InheritedImplicitInitializerReferenceBuilder.swift index d62a27b1f..3214c707d 100644 --- a/Sources/SourceGraph/Mutators/InheritedImplicitInitializerReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/InheritedImplicitInitializerReferenceBuilder.swift @@ -28,17 +28,19 @@ final class InheritedImplicitInitializerReferenceBuilder: SourceGraphMutator { guard relatedRef.declarationKind == .functionConstructor else { continue } // Find the declaration this related reference points to - guard let implicitInit = graph.declaration(withUsr: relatedRef.usr), + guard let implicitInit = graph.declaration(withUsrID: relatedRef.usrID), implicitInit.isImplicit, implicitInit.kind == .functionConstructor else { continue } // Add the inverse reference: implicit init -> explicit init - for usr in explicitInit.usrs { + for usrID in explicitInit.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let reference = Reference( name: explicitInit.name, kind: .related, declarationKind: .functionConstructor, + usrID: usrID, usr: usr, location: implicitInit.location ) diff --git a/Sources/SourceGraph/Mutators/InterfaceBuilderPropertyRetainer.swift b/Sources/SourceGraph/Mutators/InterfaceBuilderPropertyRetainer.swift index 3cd0f1e39..a0afbda4d 100644 --- a/Sources/SourceGraph/Mutators/InterfaceBuilderPropertyRetainer.swift +++ b/Sources/SourceGraph/Mutators/InterfaceBuilderPropertyRetainer.swift @@ -38,7 +38,7 @@ final class InterfaceBuilderPropertyRetainer { ) { let inheritedDeclarations = graph.inheritedDeclarations(of: declaration) let descendentInheritedDeclarations = inheritedDeclarations.map(\.declarations).joined() - let allDeclarations = declaration.declarations.union(descendentInheritedDeclarations) + let allDeclarations = declaration.declarations + descendentInheritedDeclarations for decl in allDeclarations { // Check IBOutlet properties diff --git a/Sources/SourceGraph/Mutators/ProtocolConformanceReferenceBuilder.swift b/Sources/SourceGraph/Mutators/ProtocolConformanceReferenceBuilder.swift index 4fe04d8e7..ba0f565c8 100644 --- a/Sources/SourceGraph/Mutators/ProtocolConformanceReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/ProtocolConformanceReferenceBuilder.swift @@ -50,7 +50,7 @@ final class ProtocolConformanceReferenceBuilder: SourceGraphMutator { // Find all superclasses. let superclassDecls = graph.inheritedTypeReferences(of: conformingClass) .filter { $0.declarationKind == .class } - .compactMap { graph.declaration(withUsr: $0.usr) } + .compactMap { graph.declaration(withUsrID: $0.usrID) } .flatMap(\.declarations) for unimplementedProtoDecl in unimplementedProtoDecls { @@ -63,13 +63,13 @@ final class ProtocolConformanceReferenceBuilder: SourceGraphMutator { .min() if let declInSuperclass { - // Build a reference from the protocol declarations to the - // declaration implemented by the superclass. - for usr in declInSuperclass.usrs { + for usrID in declInSuperclass.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let reference = Reference( name: declInSuperclass.name, kind: .related, declarationKind: declInSuperclass.kind, + usrID: usrID, usr: usr, location: declInSuperclass.location ) @@ -93,7 +93,13 @@ final class ProtocolConformanceReferenceBuilder: SourceGraphMutator { } private func invertReferencesFromProtocolToDeclaration(_ nonInvertableReferences: Set) { - let relatedReferences = graph.allReferences.filter { $0.kind == .related && $0.declarationKind.isProtocolMemberConformingKind } + // Collect matching references into an Array to avoid rebuilding a Set (rehashing every element). + var relatedReferences: [Reference] = [] + for ref in graph.allReferences { + if ref.kind == .related, ref.declarationKind.isProtocolMemberConformingKind, !nonInvertableReferences.contains(ref) { + relatedReferences.append(ref) + } + } // Collect all mutations before applying them to avoid order-dependent behavior // when iterating non-deterministically ordered Sets. @@ -101,7 +107,7 @@ final class ProtocolConformanceReferenceBuilder: SourceGraphMutator { var referencesToAdd: [(reference: Reference, parent: Declaration)] = [] var declarationsToRetain: [Declaration] = [] - for relatedReference in relatedReferences.subtracting(nonInvertableReferences) { + for relatedReference in relatedReferences { guard let conformingDeclaration = relatedReference.parent else { continue } @@ -124,7 +130,7 @@ final class ProtocolConformanceReferenceBuilder: SourceGraphMutator { guard equivalentDeclarationKinds.contains(conformingDeclaration.kind), conformingDeclaration.name == relatedReference.name else { continue } - if let protocolDeclaration = graph.declaration(withUsr: relatedReference.usr) { + if let protocolDeclaration = graph.declaration(withUsrID: relatedReference.usrID) { // Invert the related reference such that instead of the conforming declaration // referencing the declaration within the protocol, the protocol declaration // now references the conforming declaration. @@ -135,11 +141,13 @@ final class ProtocolConformanceReferenceBuilder: SourceGraphMutator { referencesToRemove.append(relatedReference) } - for usr in conformingDeclaration.usrs { + for usrID in conformingDeclaration.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let newReference = Reference( name: relatedReference.name, kind: .related, declarationKind: relatedReference.declarationKind, + usrID: usrID, usr: usr, location: relatedReference.location ) diff --git a/Sources/SourceGraph/Mutators/ProtocolExtensionReferenceBuilder.swift b/Sources/SourceGraph/Mutators/ProtocolExtensionReferenceBuilder.swift index 9cf9d2344..f5151f5d9 100644 --- a/Sources/SourceGraph/Mutators/ProtocolExtensionReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/ProtocolExtensionReferenceBuilder.swift @@ -20,11 +20,13 @@ final class ProtocolExtensionReferenceBuilder: SourceGraphMutator { for extensionDeclaration in graph.declarations(ofKind: .extensionProtocol) { // First, create a reference from each protocol to the extension. if let extendedProtocol = try graph.extendedDeclaration(forExtension: extensionDeclaration) { - for usr in extensionDeclaration.usrs { + for usrID in extensionDeclaration.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let reference = Reference( name: extendedProtocol.name, kind: .normal, declarationKind: .extensionProtocol, + usrID: usrID, usr: usr, location: extendedProtocol.location ) @@ -32,8 +34,8 @@ final class ProtocolExtensionReferenceBuilder: SourceGraphMutator { graph.add(reference, from: extendedProtocol) } - // Now remove the reference from the extension to the protocol - for reference in extensionDeclaration.references.filter({ extendedProtocol.usrs.contains($0.usr) }) { + let extendedProtocolUsrIDs = Set(extendedProtocol.usrIDs) + for reference in extensionDeclaration.references.filter({ extendedProtocolUsrIDs.contains($0.usrID) }) { graph.remove(reference) } @@ -43,11 +45,13 @@ final class ProtocolExtensionReferenceBuilder: SourceGraphMutator { for memberDeclaration in extensionDeclaration.declarations { for reference in graph.references(to: memberDeclaration) { if let parentDeclaration = reference.parent { - for usr in extensionDeclaration.usrs { + for usrID in extensionDeclaration.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let extensionReference = Reference( name: extensionDeclaration.name, kind: .normal, declarationKind: .extensionProtocol, + usrID: usrID, usr: usr, location: reference.location ) @@ -55,11 +59,13 @@ final class ProtocolExtensionReferenceBuilder: SourceGraphMutator { graph.add(extensionReference, from: parentDeclaration) } - for usr in extendedProtocol.usrs { + for usrID in extendedProtocol.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let protocolReference = Reference( name: extendedProtocol.name, kind: .normal, declarationKind: .protocol, + usrID: usrID, usr: usr, location: reference.location ) @@ -89,7 +95,7 @@ final class ProtocolExtensionReferenceBuilder: SourceGraphMutator { let constrainingProtocolRefs = extensionDeclaration.references.filter { $0.role == .genericRequirementType && $0.declarationKind == .protocol } for constrainingProtocolRef in constrainingProtocolRefs { - guard let constrainingProtocol = graph.declaration(withUsr: constrainingProtocolRef.usr) else { continue } + guard let constrainingProtocol = graph.declaration(withUsrID: constrainingProtocolRef.usrID) else { continue } // For each member declared in the extension, check if it satisfies a requirement of the constraining protocol for memberDeclaration in extensionDeclaration.declarations { @@ -105,11 +111,13 @@ final class ProtocolExtensionReferenceBuilder: SourceGraphMutator { // declaration has a related reference to the protocol member it implements. // ProtocolConformanceReferenceBuilder will then invert this to create a reference // from the protocol requirement to the extension's implementation. - for usr in matchingRequirement.usrs { + for usrID in matchingRequirement.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let relatedReference = Reference( name: matchingRequirement.name, kind: .related, declarationKind: matchingRequirement.kind, + usrID: usrID, usr: usr, location: memberDeclaration.location ) diff --git a/Sources/SourceGraph/Mutators/RedundantExplicitPublicAccessibilityMarker.swift b/Sources/SourceGraph/Mutators/RedundantExplicitPublicAccessibilityMarker.swift index 20bb55c76..50e8e2ba8 100644 --- a/Sources/SourceGraph/Mutators/RedundantExplicitPublicAccessibilityMarker.swift +++ b/Sources/SourceGraph/Mutators/RedundantExplicitPublicAccessibilityMarker.swift @@ -94,7 +94,7 @@ final class RedundantExplicitPublicAccessibilityMarker: SourceGraphMutator { // the call site arguments with function parameters, as parameters with // default types can cause misalignment between the positions of the two. if let functionRef = $0.parent?.references.first(where: { $0.role == .variableInitFunctionCall }), - let functionDecl = graph.declaration(withUsr: functionRef.usr), + let functionDecl = graph.declaration(withUsrID: functionRef.usrID), functionDecl.hasGenericFunctionReturnedMetatypeParameters { return $0.parent diff --git a/Sources/SourceGraph/Mutators/RedundantProtocolMarker.swift b/Sources/SourceGraph/Mutators/RedundantProtocolMarker.swift index 8a6a1125b..94a7460b9 100644 --- a/Sources/SourceGraph/Mutators/RedundantProtocolMarker.swift +++ b/Sources/SourceGraph/Mutators/RedundantProtocolMarker.swift @@ -34,7 +34,7 @@ final class RedundantProtocolMarker: SourceGraphMutator { .references .lazy .filter { $0.declarationKind == .extensionProtocol } - .compactMap { self.graph.declaration(withUsr: $0.usr) } + .compactMap { self.graph.declaration(withUsrID: $0.usrID) } .flatMap(\.declarations) .allSatisfy { unusedDeclarations.contains($0) } @@ -59,7 +59,7 @@ final class RedundantProtocolMarker: SourceGraphMutator { if areAllReferencesConformances { // The protocol is redundant. - let inherited = graph.inheritedTypeReferences(of: protocolDecl).filter { $0.declarationKind == .protocol } + let inherited = Array(graph.inheritedTypeReferences(of: protocolDecl).filter { $0.declarationKind == .protocol }) graph.markRedundantProtocol(protocolDecl, references: protocolReferences, inherited: inherited) protocolDecl.declarations.forEach { graph.markIgnored($0) } } diff --git a/Sources/SourceGraph/Mutators/StructImplicitInitializerReferenceBuilder.swift b/Sources/SourceGraph/Mutators/StructImplicitInitializerReferenceBuilder.swift index f3425be30..998ef5aba 100644 --- a/Sources/SourceGraph/Mutators/StructImplicitInitializerReferenceBuilder.swift +++ b/Sources/SourceGraph/Mutators/StructImplicitInitializerReferenceBuilder.swift @@ -33,11 +33,13 @@ final class StructImplicitInitializerReferenceBuilder: SourceGraphMutator { else { continue } for decl in [propertyDecl, setterDecl] { - for usr in decl.usrs { + for usrID in decl.usrIDs { + let usr = graph.usrInterner.string(for: usrID) let ref = Reference( name: decl.name, kind: .normal, declarationKind: decl.kind, + usrID: usrID, usr: usr, location: implicitInitDecl.location ) @@ -59,7 +61,8 @@ final class StructImplicitInitializerReferenceBuilder: SourceGraphMutator { let sortedReferences = caller.references.sorted() for initPropertyDecl in initPropertyDecls { - let firstRef = sortedReferences.first { initPropertyDecl.usrs.contains($0.usr) && $0.location > initRef.location } + let initPropertyUsrIDs = Set(initPropertyDecl.usrIDs) + let firstRef = sortedReferences.first { initPropertyUsrIDs.contains($0.usrID) && $0.location > initRef.location } if let firstRef { graph.remove(firstRef) diff --git a/Sources/SourceGraph/Mutators/UnusedImportMarker.swift b/Sources/SourceGraph/Mutators/UnusedImportMarker.swift index f57dc80f4..07b972f05 100644 --- a/Sources/SourceGraph/Mutators/UnusedImportMarker.swift +++ b/Sources/SourceGraph/Mutators/UnusedImportMarker.swift @@ -17,79 +17,145 @@ final class UnusedImportMarker: SourceGraphMutator { retainedModules = Set(configuration.retainUnusedImportedModules) } + private var wordCount: Int = 0 + private var emptyBitset = ModuleBitset(wordCount: 0) + private var fileModuleBitsetCache: [ObjectIdentifier: ModuleBitset] = [:] + private var extCacheFilled: [Bool] = [] + private var extCacheValues: [ModuleBitset] = [] + private var refTypesCached: [Bool] = [] + private var refTypesCacheValues: [Set] = [] + + private func fileModuleBitset(_ file: SourceFile) -> ModuleBitset { + let oid = ObjectIdentifier(file) + if let cached = fileModuleBitsetCache[oid] { return cached } + let bitset = graph.moduleInterner.internBitset(file.modules, wordCount: wordCount) + fileModuleBitsetCache[oid] = bitset + return bitset + } + func mutate() throws { guard !configuration.disableUnusedImportAnalysis else { return } - var referencedModulesByFile = graph.indexedSourceFiles.reduce(into: [SourceFile: Set]()) { result, file in - result[file] = [] - } - var moduleCache: [String: Set] = [:] + let files = graph.indexedSourceFiles - // Build a mapping of source files and the modules they reference. - for ref in graph.allReferences { - if let modules = moduleCache[ref.usr] { - referencedModulesByFile[ref.location.file, default: []].formUnion(modules) - continue + // Pre-intern all module names (source file modules, import statements, + // retained modules) so that wordCount is stable for all bitsets. + var fileToIndex = [ObjectIdentifier: Int](minimumCapacity: files.count) + for (i, file) in files.enumerated() { + fileToIndex[ObjectIdentifier(file)] = i + for name in file.modules { + _ = graph.moduleInterner.intern(name) + } + for stmt in file.importStatements { + _ = graph.moduleInterner.intern(stmt.module) } + } + for name in retainedModules { + _ = graph.moduleInterner.intern(name) + } - var directModules: Set = [] - var indirectModules: Set = [] + wordCount = graph.moduleInterner.wordCount + emptyBitset = ModuleBitset(wordCount: wordCount) + let retainedModuleIDs = graph.moduleInterner.internBitset(retainedModules, wordCount: wordCount) - if let decl = graph.declaration(withUsr: ref.usr) { - directModules = decl.location.file.modules + // Manual pointer instead of [UInt64] to eliminate bounds checking and + // copy-on-write overhead in the inner loops that accumulate module bits. + let totalWords = files.count * wordCount + let perFileModules = UnsafeMutablePointer.allocate(capacity: totalWords) + perFileModules.initialize(repeating: 0, count: totalWords) + defer { perFileModules.deallocate() } + + let usrCount = graph.usrInterner.count + extCacheFilled = [Bool](repeating: false, count: usrCount) + extCacheValues = [ModuleBitset](repeating: emptyBitset, count: usrCount) + refTypesCached = [Bool](repeating: false, count: usrCount) + refTypesCacheValues = [Set](repeating: [], count: usrCount) + + // Build a mapping of source files and the modules they reference. + // Process each USR once, then fan its module bitset out to + // every file that references it. The bitset OR is idempotent so + // duplicate file hits from multiple references are harmless. + for idx in 0 ..< usrCount { + let usrID = USRID(idx) + let refs = graph.references(toUsrID: usrID) + guard let firstRef = refs.first else { continue } + + var referencedModules = ModuleBitset(wordCount: wordCount) + + if let decl = graph.declaration(withUsrID: usrID) { + referencedModules.formUnion(fileModuleBitset(decl.location.file)) let indirectRefs = referencedTypes(from: decl) - indirectModules = indirectRefs - .compactMap { graph.declaration(withUsr: $0.usr) } - .flatMapSet(\.location.file.modules) - .union(indirectRefs.flatMapSet { modulesExtending($0) }) + for indirectRef in indirectRefs { + if let indirectDecl = graph.declaration(withUsrID: indirectRef.usrID) { + referencedModules.formUnion(fileModuleBitset(indirectDecl.location.file)) + } + referencedModules.formUnion(modulesExtending(indirectRef)) + } if decl.isOverride { - let overrideModules = graph.allSuperDeclarationsInOverrideChain(from: decl) - .flatMapSet(\.location.file.modules) - indirectModules.formUnion(overrideModules) + for superDecl in graph.allSuperDeclarationsInOverrideChain(from: decl) { + referencedModules.formUnion(fileModuleBitset(superDecl.location.file)) + } } } - let referencedModules = directModules - .union(indirectModules) - .union(modulesExtending(ref)) - moduleCache[ref.usr] = referencedModules - referencedModulesByFile[ref.location.file, default: []].formUnion(referencedModules) + referencedModules.formUnion(modulesExtending(firstRef)) + guard !referencedModules.isEmpty else { continue } + + for ref in refs { + guard let fileIdx = fileToIndex[ObjectIdentifier(ref.location.file)] else { continue } + + let base = fileIdx * wordCount + for j in 0 ..< wordCount { + perFileModules[base + j] |= referencedModules.words[j] + } + } } // For each source file, determine whether its imports are unused. - for (file, referencedModules) in referencedModulesByFile { - // Ignore retained files. + for (fileIdx, file) in files.enumerated() { if configuration.retainFilesMatchers.anyMatch(filename: file.path.string) { continue } + // `perFileModules` is flattened as [file][word] so we can keep the + // bitsets in one contiguous buffer and index via a simple offset. + let base = fileIdx * wordCount let unreferencedImports = file.importStatements .filter { - // Exclude conditional imports as they may provide symbols for sections of code - // that are also conditionally compiled. - !$0.isConditional && - // Exclude ignore commented imports + let moduleID = graph.moduleInterner.intern($0.module) + // Split the dense module ID into a 64-bit word index and + // an in-word bit offset for the flattened file bitset. + let (word, bit) = moduleID.rawValue.quotientAndRemainder(dividingBy: 64) + return !$0.isConditional && !$0.commentCommands.contains(.ignore) && - // Exclude exported/public imports because even though they may be unreferenced - // in the current file, their exported symbols may be referenced in others. !$0.isExported && - // Exclude explicitly retained modules. - !retainedModules.contains($0.module) && - // Only Consider modules that have been indexed as we need to see which modules - // they export. - graph.isModuleIndexed($0.module) && - !referencedModules.contains($0.module) && - !graph.moduleExportsUnindexedModules($0.module) + !retainedModuleIDs.contains(moduleID) && + graph.isModuleIndexed(moduleID) && + // If the module's bit is unset, this file never referenced it. + (perFileModules[base + word] & (1 &<< bit) == 0) && + !graph.moduleExportsUnindexedModules(moduleID) } for unreferencedImport in unreferencedImports { - // In the simple case, a module is unused if it's not referenced. However, it's - // possible the module exports other referenced modules. - guard !referencedModules.contains(where: { - graph.isModule($0, exportedBy: unreferencedImport.module) - }) else { continue } + let importModuleID = graph.moduleInterner.intern(unreferencedImport.module) + + // Check if any referenced module is exported by this import. + var isExported = false + for j in 0 ..< wordCount { + var bits = perFileModules[base + j] + while bits != 0 { + let bit = bits.trailingZeroBitCount + if graph.isModule(ModuleID(j * 64 + bit), exportedBy: importModuleID) { + isExported = true + break + } + bits &= bits &- 1 + } + if isExported { break } + } + guard !isExported else { continue } graph.markUnusedModuleImport(unreferencedImport) } @@ -98,59 +164,78 @@ final class UnusedImportMarker: SourceGraphMutator { // MARK: - Private - private var extendedDeclCache: [String: Set] = [:] - /// Identifies any modules that extend the given declaration reference, as they may provide /// members and conformances that are required for compilation. - private func modulesExtending(_ ref: Reference) -> Set { - guard ref.declarationKind.isExtendableKind else { return [] } + private func modulesExtending(_ ref: Reference) -> ModuleBitset { + guard ref.declarationKind.isExtendableKind else { + return emptyBitset + } - if let modules = extendedDeclCache[ref.usr] { - return modules + let idx = ref.usrID.rawValue + + if extCacheFilled[idx] { + return extCacheValues[idx] } - let modules: Set = graph.references(to: ref.usr) - .flatMapSet { - guard let parent = $0.parent, - parent.kind == ref.declarationKind.extensionKind, - parent.name == ref.name - else { return [] } + var modules = ModuleBitset(wordCount: wordCount) + for extRef in graph.references(toUsrID: ref.usrID) { + guard let parent = extRef.parent, + parent.kind == ref.declarationKind.extensionKind, + parent.name == ref.name + else { continue } - return parent.location.file.modules - } - extendedDeclCache[ref.usr] = modules + modules.formUnion(fileModuleBitset(parent.location.file)) + } + extCacheFilled[idx] = true + extCacheValues[idx] = modules return modules } /// Identifies types referenced by a declaration whose module must be imported for compilation. private func referencedTypes(from decl: Declaration) -> Set { + let cacheIdx: Int? + if decl.usrIDs.count == 1 { + let idx = decl.usrIDs[0].rawValue + if refTypesCached[idx] { + return refTypesCacheValues[idx] + } + cacheIdx = idx + } else { + cacheIdx = nil + } + let references: Set if decl.kind.isVariableKind { - references = decl.references.filter { $0.role == .varType } + references = Set(decl.references.filter { $0.role == .varType }) } else if decl.kind == .enumelement { - references = decl.references + references = Set(decl.references) } else if decl.kind == .typealias { let transitiveReferences = decl.references.flatMapSet { ref -> Set in - guard let refDecl = graph.declaration(withUsr: ref.usr) else { return [] } + guard let refDecl = graph.declaration(withUsrID: ref.usrID) else { return [] } return referencedTypes(from: refDecl) } - references = decl.references.union(transitiveReferences) + references = Set(decl.references).union(transitiveReferences) } else if decl.kind.isFunctionKind { - references = decl.references + references = Set(decl.references .filter { [ .returnType, .parameterType, ].contains($0.role) - } + }) } else if decl.kind == .protocol { - references = decl.related.filter { $0.role == .refinedProtocolType } + references = Set(decl.related.filter { $0.role == .refinedProtocolType }) } else { references = [] } + if let cacheIdx { + refTypesCached[cacheIdx] = true + refTypesCacheValues[cacheIdx] = references + } + return references } } diff --git a/Sources/SourceGraph/Mutators/UnusedParameterRetainer.swift b/Sources/SourceGraph/Mutators/UnusedParameterRetainer.swift index c5c692327..4301a11a4 100644 --- a/Sources/SourceGraph/Mutators/UnusedParameterRetainer.swift +++ b/Sources/SourceGraph/Mutators/UnusedParameterRetainer.swift @@ -24,7 +24,7 @@ final class UnusedParameterRetainer: SourceGraphMutator { for protoFuncDecl in protoFuncDecls { let relatedFuncDecls = protoFuncDecl.related .filter(\.declarationKind.isFunctionKind) - .compactMapSet { graph.declaration(withUsr: $0.usr) } + .compactMapSet { graph.declaration(withUsrID: $0.usrID) } let extFuncDecls = relatedFuncDecls.filter { $0.parent?.kind.isExtensionKind ?? false } let conformingDecls = relatedFuncDecls.subtracting(extFuncDecls) @@ -43,7 +43,7 @@ final class UnusedParameterRetainer: SourceGraphMutator { if configuration.retainUnusedProtocolFuncParams { functionDecl.unusedParameters.forEach { graph.markRetained($0) } } else { - retain(params: Array(functionDecl.unusedParameters), usedIn: allFunctionDecls) + retain(params: functionDecl.unusedParameters, usedIn: allFunctionDecls) } } } diff --git a/Sources/SourceGraph/Mutators/UsedDeclarationMarker.swift b/Sources/SourceGraph/Mutators/UsedDeclarationMarker.swift index b3b054558..6c30f2b9b 100644 --- a/Sources/SourceGraph/Mutators/UsedDeclarationMarker.swift +++ b/Sources/SourceGraph/Mutators/UsedDeclarationMarker.swift @@ -13,7 +13,9 @@ final class UsedDeclarationMarker: SourceGraphMutator { removeErroneousProtocolReferences() markUsed(graph.retainedDeclarations) - graph.rootReferences.forEach { markUsed(declarationsReferenced(by: $0)) } + for ref in graph.rootReferences { + markUsed(from: ref) + } ignoreUnusedDescendents(in: graph.rootDeclarations, unusedDeclarations: graph.unusedDeclarations) @@ -23,10 +25,12 @@ final class UsedDeclarationMarker: SourceGraphMutator { // Removes references from protocol member decls to conforming decls that have a dereferenced ancestor. private func removeErroneousProtocolReferences() { - // Evaluate ancestor dereference status from a stable pre-mutation snapshot to avoid - // order-dependent behavior when iterating sets and removing references. + // When a declaration has zero references, isRetained() simplifies to + // retainedDeclarations.contains() — no need to scan references for + // .retained kind. When it HAS references, it's not dereferenced regardless. + let retained = graph.retainedDeclarations let dereferencedDeclarations = graph.allDeclarations.filter { - !(graph.isRetained($0) || graph.hasReferences(to: $0)) + !graph.hasReferences(to: $0) && !retained.contains($0) } let dereferencedAncestors = Set(dereferencedDeclarations) @@ -35,7 +39,7 @@ final class UsedDeclarationMarker: SourceGraphMutator { for protocolDecl in graph.declarations(ofKind: .protocol) { for memberDecl in protocolDecl.declarations { for relatedRef in memberDecl.related { - guard let relatedDecl = graph.declaration(withUsr: relatedRef.usr) else { continue } + guard let relatedDecl = graph.declaration(withUsrID: relatedRef.usrID) else { continue } let hasDereferencedAncestor = relatedDecl.ancestralDeclarations.contains { dereferencedAncestors.contains($0) @@ -53,39 +57,43 @@ final class UsedDeclarationMarker: SourceGraphMutator { } } - private func markUsed(_ declarations: Set) { - for declaration in declarations { - guard !graph.isUsed(declaration) else { continue } - - graph.markUsed(declaration) + private func markUsed(_ declarations: some Collection) { + for decl in declarations { + markUsed(decl) + } + } - for ref in declaration.references { - markUsed(declarationsReferenced(by: ref)) - } + private func markUsed(from reference: Reference) { + guard let decl = graph.declaration(withUsrID: reference.usrID) else { return } - for ref in declaration.related { - markUsed(declarationsReferenced(by: ref)) - } - } + markUsed(decl) } - private func declarationsReferenced(by reference: Reference) -> Set { - var declarations: Set = [] + private func markUsed(_ decl: Declaration) { + guard !graph.isUsed(decl) else { return } + + graph.markUsed(decl) - if let declaration = graph.declaration(withUsr: reference.usr) { - declarations.insert(declaration) + for ref in decl.references { + if let d = graph.declaration(withUsrID: ref.usrID) { + markUsed(d) + } } - return declarations + for ref in decl.related { + if let d = graph.declaration(withUsrID: ref.usrID) { + markUsed(d) + } + } } - private func ignoreUnusedDescendents(in decls: Set, unusedDeclarations: Set) { + private func ignoreUnusedDescendents(in decls: some Collection, unusedDeclarations: Set) { for decl in decls { guard !decl.declarations.isEmpty || !decl.unusedParameters.isEmpty else { continue } if unusedDeclarations.contains(decl) { - decl.descendentDeclarations.forEach { graph.markIgnored($0) } + decl.forEachDescendentDeclaration { graph.markIgnored($0) } } else { ignoreUnusedDescendents(in: decl.declarations, unusedDeclarations: unusedDeclarations) diff --git a/Sources/SourceGraph/SourceGraph.swift b/Sources/SourceGraph/SourceGraph.swift index 63534fb5c..89d10c399 100644 --- a/Sources/SourceGraph/SourceGraph.swift +++ b/Sources/SourceGraph/SourceGraph.swift @@ -1,21 +1,20 @@ import Configuration import Foundation -import Logger import Shared public final class SourceGraph { - public private(set) var allDeclarations: Set = [] - public private(set) var usedDeclarations: Set = [] - public private(set) var redundantProtocols: [Declaration: (references: Set, inherited: Set)] = [:] + public private(set) var redundantProtocols: [Declaration: (references: [Reference], inherited: [Reference])] = [:] public private(set) var rootDeclarations: Set = [] public private(set) var redundantPublicAccessibility: [Declaration: Set] = [:] - public private(set) var rootReferences: Set = [] - public private(set) var allReferences: Set = [] + public private(set) var rootReferences: [Reference] = [] public private(set) var retainedDeclarations: Set = [] public private(set) var ignoredDeclarations: Set = [] public private(set) var assetReferences: Set = [] public private(set) var mainAttributedDeclarations: Set = [] - public private(set) var allReferencesByUsr: [String: Set] = [:] + /// Flat array indexed by `USRID.rawValue` for O(1) reference lookup by USR. + /// Maintained incrementally during indexing so that `indexingComplete()` has + /// no per-reference hashing work to do. + private var referencesByUsrID: [[Reference]] = [] public private(set) var indexedSourceFiles: [SourceFile] = [] public private(set) var unusedModuleImports: Set = [] public private(set) var assignOnlyProperties: Set = [] @@ -24,28 +23,48 @@ public final class SourceGraph { public private(set) var commandIgnoredDeclarations: [Declaration: CommandIgnoreKind] = [:] public private(set) var functionsWithIgnoredParameters: Set = [] - private var indexedModules: Set = [] - private var unindexedExportedModules: Set = [] + let moduleInterner = ModuleNameInterner() + public let usrInterner = USRInterner() + private var indexedModules: Set = [] + private var unindexedExportedModules: Set = [] private var allDeclarationsByKind: [Declaration.Kind: Set] = [:] - private var allDeclarationsByUsr: [String: Declaration] = [:] - private var moduleToExportingModules: [String: Set] = [:] + private var declarationsOfKindsCache: [Set: Set] = [:] + private var declarationsOfKindsCacheDirty = false + /// Flat array indexed by `USRID.rawValue` for O(1) declaration lookup by USR. + private var declarationsByUsrID: [Declaration?] = [] + private var moduleToExportingModules: [ModuleID: Set] = [:] + private var moduleExportsUnindexedCache: [ModuleID: Bool] = [:] private let configuration: Configuration - private let logger: Logger - public init(configuration: Configuration, logger: Logger) { + public init(configuration: Configuration) { self.configuration = configuration - self.logger = logger + } + + public func reserveCapacity(forFileCount fileCount: Int) { + let estimatedDeclarations = fileCount * 100 + let estimatedUsrs = fileCount * 80 + usrInterner.reserveCapacity(estimatedUsrs) + retainedDeclarations.reserveCapacity(estimatedDeclarations / 4) + referencesByUsrID.reserveCapacity(estimatedUsrs) } public func indexingComplete() { - rootDeclarations = allDeclarations.filter { $0.parent == nil } - rootReferences = allReferences.filter { $0.parent == nil } + rootDeclarations = Set(allDeclarationsByKind.values.joined().filter { $0.parent == nil }) + rootReferences = referencesByUsrID.flatMap { $0.filter { $0.parent == nil } } unindexedExportedModules = Set(moduleToExportingModules.keys).subtracting(indexedModules) } + public var allDeclarations: FlattenSequence>.Values> { + allDeclarationsByKind.values.joined() + } + + public var allReferences: FlattenSequence<[[Reference]]> { + referencesByUsrID.joined() + } + public var unusedDeclarations: Set { - allDeclarations.subtracting(usedDeclarations) + Set(allDeclarationsByKind.values.joined().filter { !$0.isUsed }) } public func declarations(ofKind kind: Declaration.Kind) -> Set { @@ -53,30 +72,88 @@ public final class SourceGraph { } public func declarations(ofKinds kinds: Set) -> Set { - declarations(ofKinds: Array(kinds)) + if declarationsOfKindsCacheDirty { + declarationsOfKindsCache.removeAll(keepingCapacity: true) + declarationsOfKindsCacheDirty = false + } + if let cached = declarationsOfKindsCache[kinds] { return cached } + // Pre-size the set to avoid incremental rehashing during formUnion. + // Per-kind sets are disjoint so the sum is exact. + let totalCount = kinds.reduce(0) { $0 + (allDeclarationsByKind[$1]?.count ?? 0) } + var result = Set(minimumCapacity: totalCount) + for kind in kinds { + result.formUnion(allDeclarationsByKind[kind, default: []]) + } + declarationsOfKindsCache[kinds] = result + return result } public func declarations(ofKinds kinds: [Declaration.Kind]) -> Set { - kinds.flatMapSet { allDeclarationsByKind[$0, default: []] } + declarations(ofKinds: Set(kinds)) } public func declaration(withUsr usr: String) -> Declaration? { - allDeclarationsByUsr[usr] + guard let usrID = usrInterner.existing(usr) else { return nil } + + let idx = usrID.rawValue + guard idx < declarationsByUsrID.count else { return nil } + + return declarationsByUsrID[idx] } - public func references(to decl: Declaration) -> Set { - decl.usrs.flatMapSet { references(to: $0) } + func declaration(withUsrID usrID: USRID) -> Declaration? { + let idx = usrID.rawValue + guard idx < declarationsByUsrID.count else { return nil } + + return declarationsByUsrID[idx] } - public func references(to usr: String) -> Set { - allReferencesByUsr[usr, default: []] + public func references(to decl: Declaration) -> [Reference] { + if decl.usrIDs.count == 1 { + return refsForUsr(decl.usrIDs[0]) + } + return decl.usrIDs.flatMap { refsForUsr($0) } + } + + public func references(to usr: String) -> [Reference] { + guard let usrID = usrInterner.existing(usr) else { return [] } + + return refsForUsr(usrID) + } + + func references(toUsrID usrID: USRID) -> [Reference] { + refsForUsr(usrID) } public func hasReferences(to decl: Declaration) -> Bool { - decl.usrs.contains { !allReferencesByUsr[$0, default: []].isEmpty } + decl.usrIDs.contains { usrHasRefs($0) } + } + + private func refsForUsr(_ usrID: USRID) -> [Reference] { + let idx = usrID.rawValue + return idx < referencesByUsrID.count ? referencesByUsrID[idx] : [] + } + + private func usrHasRefs(_ usrID: USRID) -> Bool { + let idx = usrID.rawValue + return idx < referencesByUsrID.count && !referencesByUsrID[idx].isEmpty + } + + private func ensureUsrBucket(for usrID: USRID) { + let idx = usrID.rawValue + if idx >= referencesByUsrID.count { + referencesByUsrID.append(contentsOf: repeatElement([Reference](), count: idx + 1 - referencesByUsrID.count)) + } } - func markRedundantProtocol(_ declaration: Declaration, references: Set, inherited: Set) { + private func ensureUsrDeclBucket(for usrID: USRID) { + let idx = usrID.rawValue + if idx >= declarationsByUsrID.count { + declarationsByUsrID.append(contentsOf: repeatElement(nil, count: idx + 1 - declarationsByUsrID.count)) + } + } + + func markRedundantProtocol(_ declaration: Declaration, references: [Reference], inherited: [Reference]) { redundantProtocols[declaration] = (references, inherited) } @@ -102,11 +179,13 @@ public final class SourceGraph { public func markRetained(_ declaration: Declaration) { if let parent = declaration.parent { - for usr in declaration.usrs { + for usrID in declaration.usrIDs { + let usr = usrInterner.string(for: usrID) let reference = Reference( name: declaration.name, kind: .retained, declarationKind: declaration.kind, + usrID: usrID, usr: usr, location: declaration.location ) @@ -121,11 +200,8 @@ public final class SourceGraph { func unmarkRetained(_ declaration: Declaration) { retainedDeclarations.remove(declaration) - let retainedReferences = declaration.usrs.flatMapSet { usr in - allReferencesByUsr[usr, default: []] - }.filter { reference in - reference.kind == .retained && reference.parent === declaration.parent - } + let retainedReferences = references(to: declaration) + .filter { $0.kind == .retained && $0.parent === declaration.parent } for reference in retainedReferences { remove(reference) @@ -152,72 +228,80 @@ public final class SourceGraph { retainedDeclarations.contains(declaration) || references(to: declaration).contains { $0.kind == .retained } } - public func add(_ declaration: Declaration) { - allDeclarations.insert(declaration) + public func add(_ declaration: Declaration) throws { + try validateNoDeclarationConflict(for: declaration) + allDeclarationsByKind[declaration.kind, default: []].insert(declaration) - for usr in declaration.usrs { - if let existingDecl = allDeclarationsByUsr[usr] { - logger.warn(""" - Declaration conflict detected: a declaration with the USR '\(usr)' has already been indexed. - This issue can cause inconsistent and incorrect results. - Existing declaration: \(existingDecl), declared in modules: \(existingDecl.location.file.modules.sorted()) - Conflicting declaration: \(declaration), declared in modules: \(declaration.location.file.modules.sorted()) - To resolve this warning, make sure all build modules are uniquely named. - """) - // Keep the declaration that sorts first to ensure deterministic results - // regardless of indexing order. - if declaration < existingDecl { - allDeclarationsByUsr[usr] = declaration - } - } else { - allDeclarationsByUsr[usr] = declaration - } + declarationsOfKindsCacheDirty = true + for usrID in declaration.usrIDs { + ensureUsrDeclBucket(for: usrID) + declarationsByUsrID[usrID.rawValue] = declaration } } - public func add(_ declarations: Set) { - declarations.forEach { add($0) } + public func add(_ declarations: Set) throws { + for declaration in declarations { + try add(declaration) + } } public func remove(_ declaration: Declaration) { - declaration.parent?.declarations.remove(declaration) - allDeclarations.remove(declaration) + if let i = declaration.parent?.declarations.firstIndex(where: { $0 === declaration }) { + declaration.parent?.declarations.remove(at: i) + } allDeclarationsByKind[declaration.kind]?.remove(declaration) + declarationsOfKindsCacheDirty = true rootDeclarations.remove(declaration) - usedDeclarations.remove(declaration) + declaration.isUsed = false assignOnlyProperties.remove(declaration) suppressedAssignOnlyProperties.remove(declaration) - declaration.usrs.forEach { allDeclarationsByUsr.removeValue(forKey: $0) } + for usrID in declaration.usrIDs { + let idx = usrID.rawValue + if idx < declarationsByUsrID.count { + declarationsByUsrID[idx] = nil + } + } } public func add(_ reference: Reference) { - _ = allReferences.insert(reference) - allReferencesByUsr[reference.usr, default: []].insert(reference) + ensureUsrBucket(for: reference.usrID) + referencesByUsrID[reference.usrID.rawValue].append(reference) } - public func add(_ references: Set) { - allReferences.formUnion(references) - references.forEach { allReferencesByUsr[$0.usr, default: []].insert($0) } + public func add(_ references: [Reference]) { + if let maxID = references.lazy.map(\.usrID.rawValue).max() { + ensureUsrBucket(for: USRID(maxID)) + } + for ref in references { + referencesByUsrID[ref.usrID.rawValue].append(ref) + } } public func add(_ reference: Reference, from declaration: Declaration) { if reference.kind == .related { - _ = declaration.related.insert(reference) + declaration.related.append(reference) } else { - _ = declaration.references.insert(reference) + declaration.references.append(reference) } add(reference) } func remove(_ reference: Reference) { - _ = allReferences.remove(reference) - allReferences.subtract(reference.descendentReferences) - allReferencesByUsr[reference.usr]?.remove(reference) + let idx = reference.usrID.rawValue + if idx < referencesByUsrID.count, + let i = referencesByUsrID[idx].firstIndex(where: { $0 === reference }) + { + referencesByUsrID[idx].remove(at: i) + } if let parent = reference.parent { - parent.references.remove(reference) - parent.related.remove(reference) + if let i = parent.references.firstIndex(where: { $0 === reference }) { + parent.references.remove(at: i) + } + if let i = parent.related.firstIndex(where: { $0 === reference }) { + parent.related.remove(at: i) + } } } @@ -226,15 +310,15 @@ public final class SourceGraph { } func markUsed(_ declaration: Declaration) { - _ = usedDeclarations.insert(declaration) + declaration.isUsed = true } func isUsed(_ declaration: Declaration) -> Bool { - usedDeclarations.contains(declaration) + declaration.isUsed } func isExternal(_ reference: Reference) -> Bool { - declaration(withUsr: reference.usr) == nil + declaration(withUsrID: reference.usrID) == nil } public func addIndexedSourceFile(_ file: SourceFile) { @@ -242,42 +326,64 @@ public final class SourceGraph { } public func addIndexedModules(_ modules: Set) { - indexedModules.formUnion(modules) + indexedModules.formUnion(moduleInterner.intern(modules)) } - public func isModuleIndexed(_ module: String) -> Bool { - indexedModules.contains(module) + func isModuleIndexed(_ moduleID: ModuleID) -> Bool { + indexedModules.contains(moduleID) } public func addExportedModule(_ module: String, exportedBy exportingModules: Set) { - moduleToExportingModules[module, default: []].formUnion(exportingModules) + let moduleID = moduleInterner.intern(module) + let exportingIDs = moduleInterner.intern(exportingModules) + moduleToExportingModules[moduleID, default: []].formUnion(exportingIDs) } - public func moduleExportsUnindexedModules(_ module: String) -> Bool { - unindexedExportedModules.contains { unindexedModule in - isModule(unindexedModule, exportedBy: module) + func moduleExportsUnindexedModules(_ moduleID: ModuleID) -> Bool { + if let cached = moduleExportsUnindexedCache[moduleID] { return cached } + let result = unindexedExportedModules.contains { unindexedModule in + isModule(unindexedModule, exportedBy: moduleID) } + moduleExportsUnindexedCache[moduleID] = result + return result } - public func isModule(_ module: String, exportedBy exportingModule: String) -> Bool { - let exportingModules = moduleToExportingModules[module, default: []] + private var isModuleExportedCache: [Int: Bool] = [:] - if exportingModules.contains(exportingModule) { - // The module is exported directly. - return true - } + func isModule(_ module: ModuleID, exportedBy exportingModule: ModuleID) -> Bool { + // Pack the module pair into a single Int to keep this hot-path cache + // lookup cheaper than hashing a tuple or custom key type. `ModuleID` + // enforces the radix bound so this base-`packingRadix` encoding is unique. + let key = module.rawValue &* ModuleID.packingRadix &+ exportingModule.rawValue + if let cached = isModuleExportedCache[key] { return cached } - // Recursively check if the module is exported transitively. - return exportingModules.contains { nestedExportingModule in - isModule(nestedExportingModule, exportedBy: exportingModule) && - isModule(module, exportedBy: nestedExportingModule) + let exportingModules = moduleToExportingModules[module, default: []] + var result = exportingModules.contains(exportingModule) + if !result { + result = exportingModules.contains { nestedExportingModule in + isModule(nestedExportingModule, exportedBy: exportingModule) && + isModule(module, exportedBy: nestedExportingModule) + } } + isModuleExportedCache[key] = result + return result } + private var relativePathCache: [ObjectIdentifier: String] = [:] + func markUnusedModuleImport(_ statement: ImportStatement) { - let location = statement.location.relativeTo(configuration.projectRoot) - let usr = "import-\(statement.module)-\(location)" - let decl = Declaration(name: statement.module, kind: .module, usrs: [usr], location: statement.location) + let loc = statement.location + let fileOID = ObjectIdentifier(loc.file) + let relativePath: String + if let cached = relativePathCache[fileOID] { + relativePath = cached + } else { + relativePath = loc.file.path.relativeTo(configuration.projectRoot).string + relativePathCache[fileOID] = relativePath + } + let usr = "import-\(statement.module)-\(relativePath):\(loc.line):\(loc.column)" + let usrID = usrInterner.intern(usr) + let decl = Declaration(name: statement.module, kind: .module, usrs: [usr], usrIDs: [usrID], location: loc) unusedModuleImports.insert(decl) } @@ -291,11 +397,7 @@ public final class SourceGraph { for reference in decl.immediateInheritedTypeReferences { references.insert(reference) - if let inheritedDecl = declaration(withUsr: reference.usr) { - // Detect circular references. The following is valid Swift. - // class SomeClass {} - // extension SomeClass: SomeProtocol {} - // protocol SomeProtocol: SomeClass {} + if let inheritedDecl = declaration(withUsrID: reference.usrID) { guard !seenDeclarations.contains(inheritedDecl) else { continue } references = inheritedTypeReferences(of: inheritedDecl, seenDeclarations: seenDeclarations.union([decl])).union(references) @@ -306,14 +408,14 @@ public final class SourceGraph { } func inheritedDeclarations(of decl: Declaration) -> [Declaration] { - inheritedTypeReferences(of: decl).compactMap { declaration(withUsr: $0.usr) } + inheritedTypeReferences(of: decl).compactMap { declaration(withUsrID: $0.usrID) } } func immediateSubclasses(of decl: Declaration) -> Set { references(to: decl) .filter { $0.kind == .related && $0.declarationKind == .class } - .flatMap { $0.parent?.usrs ?? [] } - .compactMapSet { declaration(withUsr: $0) } + .flatMap { $0.parent?.usrIDs ?? [] } + .compactMapSet { declaration(withUsrID: $0) } } func subclasses(of decl: Declaration) -> Set { @@ -335,7 +437,7 @@ public final class SourceGraph { func extendedDeclaration(forExtension extensionDeclaration: Declaration) throws -> Declaration? { guard let extendedReference = try extendedDeclarationReference(forExtension: extensionDeclaration) else { return nil } - if let extendedDeclaration = declaration(withUsr: extendedReference.usr) { + if let extendedDeclaration = declaration(withUsrID: extendedReference.usrID) { return extendedDeclaration } @@ -347,7 +449,7 @@ public final class SourceGraph { let overridenDecl = decl.related .filter { $0.declarationKind == decl.kind && $0.name == decl.name } - .compactMap { declaration(withUsr: $0.usr) } + .compactMap { declaration(withUsrID: $0.usrID) } .min() guard let overridenDecl else { @@ -379,7 +481,7 @@ public final class SourceGraph { func allOverrideDeclarations(fromBase decl: Declaration) -> Set { decl.relatedEquivalentReferences - .compactMap { declaration(withUsr: $0.usr) } + .compactMap { declaration(withUsrID: $0.usrID) } .reduce(into: .init()) { result, decl in guard decl.isOverride else { return } @@ -403,4 +505,19 @@ public final class SourceGraph { [.protocol, .typealias].contains($0.declarationKind) && encodableTypes.contains($0.name) } } + + private func validateNoDeclarationConflict(for declaration: Declaration) throws { + for usrID in declaration.usrIDs { + let idx = usrID.rawValue + if idx < declarationsByUsrID.count, let existingDecl = declarationsByUsrID[idx] { + let usr = usrInterner.string(for: usrID) + throw PeripheryError.sourceGraphIntegrityError(message: """ + Declaration conflict detected: a declaration with the USR '\(usr)' has already been indexed. + Existing declaration: \(existingDecl), declared in modules: \(existingDecl.location.file.modules.sorted()) + Conflicting declaration: \(declaration), declared in modules: \(declaration.location.file.modules.sorted()) + Make sure all build modules are uniquely named. + """) + } + } + } } diff --git a/Sources/SourceGraph/SourceGraphDebugger.swift b/Sources/SourceGraph/SourceGraphDebugger.swift index e62f6ddba..ae1bdbea6 100644 --- a/Sources/SourceGraph/SourceGraphDebugger.swift +++ b/Sources/SourceGraph/SourceGraphDebugger.swift @@ -54,9 +54,5 @@ final class SourceGraphDebugger { private func describe(_ reference: Reference, depth: Int = 0) { let inset = String(repeating: "··", count: depth) print(inset + reference.description) - - for reference in reference.references.sorted() { - describe(reference, depth: depth + 1) - } } } diff --git a/Sources/SourceGraph/SourceGraphMutex.swift b/Sources/SourceGraph/SourceGraphMutex.swift index c05dc324d..80de0d8ef 100644 --- a/Sources/SourceGraph/SourceGraphMutex.swift +++ b/Sources/SourceGraph/SourceGraphMutex.swift @@ -7,8 +7,16 @@ import Synchronization public final class SourceGraphMutex: @unchecked Sendable { private let graph: Mutex + /// Self-synchronizing interner, safe to call without the graph lock. + public let usrInterner: USRInterner + public init(graph: SourceGraph) { self.graph = Mutex(graph) + usrInterner = graph.usrInterner + } + + public func reserveCapacity(forFileCount fileCount: Int) { + graph.withLock { $0.reserveCapacity(forFileCount: fileCount) } } @discardableResult diff --git a/Sources/SyntaxAnalysis/DeclarationSyntaxVisitor.swift b/Sources/SyntaxAnalysis/DeclarationSyntaxVisitor.swift index cd0432643..c5ce70c28 100644 --- a/Sources/SyntaxAnalysis/DeclarationSyntaxVisitor.swift +++ b/Sources/SyntaxAnalysis/DeclarationSyntaxVisitor.swift @@ -4,37 +4,71 @@ import SourceGraph import SwiftSyntax public final class DeclarationSyntaxVisitor: PeripherySyntaxVisitor { - public typealias Result = ( - location: Location, - accessibility: Accessibility?, - modifiers: [String], - attributes: Set, - commentCommands: [CommentCommand], - variableType: String?, - variableTypeLocations: Set, - parameterTypeLocations: Set, - returnTypeLocations: Set, - throwTypeLocations: Set, - inheritedTypeLocations: Set, - genericParameterLocations: Set, - genericConformanceRequirementLocations: Set, - variableInitFunctionCallLocations: Set, - functionCallMetatypeArgumentLocations: Set, - typeInitializerLocations: Set, - variableInitExprLocations: Set, - hasGenericFunctionReturnedMetatypeParameters: Bool - ) + // Reference type (not a tuple) to avoid expensive copies when stored in + // both the results array and the resultsByLocation dictionary. + public final class Result { + public let accessibility: Accessibility? + public let modifiers: [String] + public let attributes: Set + public let commentCommands: [CommentCommand] + public let variableType: String? + public let variableTypeLocations: Set + public let parameterTypeLocations: Set + public let returnTypeLocations: Set + public let throwTypeLocations: Set + public let inheritedTypeLocations: Set + public let genericParameterLocations: Set + public let genericConformanceRequirementLocations: Set + public let variableInitFunctionCallLocations: Set + public let functionCallMetatypeArgumentLocations: Set + public let typeInitializerLocations: Set + public let variableInitExprLocations: Set + public let hasGenericFunctionReturnedMetatypeParameters: Bool + + init( + accessibility: Accessibility?, + modifiers: [String], + attributes: Set, + commentCommands: [CommentCommand], + variableType: String?, + variableTypeLocations: Set, + parameterTypeLocations: Set, + returnTypeLocations: Set, + throwTypeLocations: Set, + inheritedTypeLocations: Set, + genericParameterLocations: Set, + genericConformanceRequirementLocations: Set, + variableInitFunctionCallLocations: Set, + functionCallMetatypeArgumentLocations: Set, + typeInitializerLocations: Set, + variableInitExprLocations: Set, + hasGenericFunctionReturnedMetatypeParameters: Bool + ) { + self.accessibility = accessibility + self.modifiers = modifiers + self.attributes = attributes + self.commentCommands = commentCommands + self.variableType = variableType + self.variableTypeLocations = variableTypeLocations + self.parameterTypeLocations = parameterTypeLocations + self.returnTypeLocations = returnTypeLocations + self.throwTypeLocations = throwTypeLocations + self.inheritedTypeLocations = inheritedTypeLocations + self.genericParameterLocations = genericParameterLocations + self.genericConformanceRequirementLocations = genericConformanceRequirementLocations + self.variableInitFunctionCallLocations = variableInitFunctionCallLocations + self.functionCallMetatypeArgumentLocations = functionCallMetatypeArgumentLocations + self.typeInitializerLocations = typeInitializerLocations + self.variableInitExprLocations = variableInitExprLocations + self.hasGenericFunctionReturnedMetatypeParameters = hasGenericFunctionReturnedMetatypeParameters + } + } private let sourceLocationBuilder: SourceLocationBuilder private let typeSyntaxInspector: TypeSyntaxInspector private let swiftVersion: SwiftVersion private(set) var results: [Result] = [] - - public var resultsByLocation: [Location: Result] { - results.reduce(into: [Location: Result]()) { dict, result in - dict[result.location] = result - } - } + public private(set) var resultsByLocation: [Location: Result] = [:] public init(sourceLocationBuilder: SourceLocationBuilder, swiftVersion: SwiftVersion) { self.sourceLocationBuilder = sourceLocationBuilder @@ -349,8 +383,7 @@ public final class DeclarationSyntaxVisitor: PeripherySyntaxVisitor { let allParameterClauseLocations = parameterClauseLocations.union(enumCaseParameterClauseLocations) .union(closureParameterClauseLocations) - results.append(( - location: location, + let result = Result( accessibility: accessibility, modifiers: modifierNames, attributes: parsedAttributes, @@ -368,7 +401,9 @@ public final class DeclarationSyntaxVisitor: PeripherySyntaxVisitor { typeInitializerLocations: typeLocations(for: typeInitializerClause?.value), variableInitExprLocations: memberBaseLocations(for: variableInitExpr), hasGenericFunctionReturnedMetatypeParameters: hasGenericFunctionReturnedMetatypeParameters - )) + ) + results.append(result) + resultsByLocation[location] = result } /// Determines whether the function has generic metatype parameters that are also returned. diff --git a/Sources/SyntaxAnalysis/UnusedParameterAnalyzer.swift b/Sources/SyntaxAnalysis/UnusedParameterAnalyzer.swift index 793e5e968..8f012bd95 100644 --- a/Sources/SyntaxAnalysis/UnusedParameterAnalyzer.swift +++ b/Sources/SyntaxAnalysis/UnusedParameterAnalyzer.swift @@ -20,7 +20,28 @@ public final class UnusedParameterAnalyzer { parseProtocols: parseProtocols ) - return functions.reduce(into: [Function: Set]()) { result, function in + return analyzeAll(functions) + } + + public func analyze( + collectedFunctionDecls: [FunctionDeclSyntax], + collectedInitializerDecls: [InitializerDeclSyntax], + file: SourceFile, + locationConverter: SourceLocationConverter, + parseProtocols: Bool + ) -> [Function: Set] { + let functions = UnusedParameterParser.parseCollected( + functionDecls: collectedFunctionDecls, + initializerDecls: collectedInitializerDecls, + file: file, + locationConverter: locationConverter, + parseProtocols: parseProtocols + ) + return analyzeAll(functions) + } + + private func analyzeAll(_ functions: [Function]) -> [Function: Set] { + functions.reduce(into: [Function: Set]()) { result, function in let params = analyze(function: function) if !params.isEmpty { diff --git a/Sources/SyntaxAnalysis/UnusedParameterParser.swift b/Sources/SyntaxAnalysis/UnusedParameterParser.swift index a585af117..ea48aa0c3 100644 --- a/Sources/SyntaxAnalysis/UnusedParameterParser.swift +++ b/Sources/SyntaxAnalysis/UnusedParameterParser.swift @@ -1,4 +1,5 @@ import Foundation +import Shared import SourceGraph import SwiftParser import SwiftSyntax @@ -61,13 +62,15 @@ public struct Parameter: Item, Hashable { public let items: [Item] = [] - public func makeDeclaration(withParent parent: Declaration) -> Declaration { + public func makeDeclaration(withParent parent: Declaration, interner: USRInterner) -> Declaration { let parentUsrs = parent.usrs.sorted().joined(separator: "-") let usr = "param-\(name.text)-\(parent.name)-\(parentUsrs)" + let usrID = interner.intern(usr) let decl = Declaration( name: name.text, kind: .varParameter, usrs: [usr], + usrIDs: [usrID], location: location ) decl.parent = parent @@ -95,8 +98,36 @@ struct GenericItem: Item { let items: [Item] } +/// Collects root function and initializer syntax nodes during a +/// MultiplexingSyntaxVisitor walk. Nested functions are still discovered +/// recursively when UnusedParameterParser parses those collected nodes. +public final class FunctionSyntaxCollector: PeripherySyntaxVisitor { + public private(set) var functionDecls: [FunctionDeclSyntax] = [] + public private(set) var initializerDecls: [InitializerDeclSyntax] = [] + private var functionDepth = 0 + + public required init(sourceLocationBuilder _: SourceLocationBuilder, swiftVersion _: SwiftVersion) {} + + public func visit(_ node: FunctionDeclSyntax) { + if functionDepth == 0 { functionDecls.append(node) } + functionDepth += 1 + } + + public func visitPost(_: FunctionDeclSyntax) { + functionDepth -= 1 + } + + public func visit(_ node: InitializerDeclSyntax) { + if functionDepth == 0 { initializerDecls.append(node) } + functionDepth += 1 + } + + public func visitPost(_: InitializerDeclSyntax) { + functionDepth -= 1 + } +} + struct UnusedParameterParser { - private let syntax: SourceFileSyntax private let parseProtocols: Bool private let file: SourceFile private let locationConverter: SourceLocationConverter @@ -109,11 +140,10 @@ struct UnusedParameterParser { ) -> [Function] { let parser = self.init( file: file, - syntax: syntax, locationConverter: locationConverter, parseProtocols: parseProtocols ) - return parser.parse() + return parser.parse(syntax: syntax) } static func parse(file: SourceFile, parseProtocols: Bool) throws -> [Function] { @@ -128,30 +158,54 @@ struct UnusedParameterParser { ) } - private init(file: SourceFile, syntax: SourceFileSyntax, locationConverter: SourceLocationConverter, parseProtocols: Bool) { + static func parseCollected( + functionDecls: [FunctionDeclSyntax], + initializerDecls: [InitializerDeclSyntax], + file: SourceFile, + locationConverter: SourceLocationConverter, + parseProtocols: Bool + ) -> [Function] { + let parser = self.init( + file: file, + locationConverter: locationConverter, + parseProtocols: parseProtocols + ) + return parser.parseFunctionNodes(functionDecls, initializerDecls) + } + + private init(file: SourceFile, locationConverter: SourceLocationConverter, parseProtocols: Bool) { self.file = file - self.syntax = syntax self.locationConverter = locationConverter self.parseProtocols = parseProtocols } - func parse() -> [Function] { + func parse(syntax: SourceFileSyntax) -> [Function] { parse(node: syntax, collecting: Function.self) } - // MARK: - Private - - private func parse(node: SyntaxProtocol, collecting: T.Type) -> [T] { - parse(children: node.children(viewMode: .sourceAccurate), collecting: collecting) + private func parseFunctionNodes( + _ functionDecls: [FunctionDeclSyntax], + _ initializerDecls: [InitializerDeclSyntax] + ) -> [Function] { + let collector = Collector() + for decl in functionDecls { + let item = parse(functionDecl: decl, collector) + if let item { collector.add(item) } + } + for decl in initializerDecls { + let item = parse(initializerDecl: decl, collector) + if let item { collector.add(item) } + } + return collector.collection } - private func parse(children: SyntaxChildren, collecting: T.Type) -> [T] { - parse(nodes: Array(children), collecting: collecting) - } + // MARK: - Private - private func parse(nodes: [Syntax], collecting _: T.Type) -> [T] { + private func parse(node: SyntaxProtocol, collecting _: T.Type) -> [T] { let collector = Collector() - nodes.forEach { _ = parse(node: $0, collector) } + for child in node.children(viewMode: .sourceAccurate) { + _ = parse(node: child, collector) + } return collector.collection } @@ -201,7 +255,12 @@ struct UnusedParameterParser { } private func parse(childrenFrom node: Syntax, _ collector: Collector?) -> Item? { - let items = node.children(viewMode: .sourceAccurate).compactMap { parse(node: $0, collector) } + var items: [Item] = [] + for child in node.children(viewMode: .sourceAccurate) { + if let item = parse(node: child, collector) { + items.append(item) + } + } if !items.isEmpty { return GenericItem(node: node, items: items) } @@ -333,7 +392,7 @@ struct UnusedParameterParser { } // Swift supports nested functions, so it's possible this function captures a param from an outer function. - let params = parse(children: syntax.children(viewMode: .sourceAccurate), collecting: Parameter.self) + let params = parse(node: syntax, collecting: Parameter.self) let items = parse(node: body, collector)?.items ?? [] let fullName = buildFullName(for: name, with: params) let genericParamNames = genericParams?.parameters.map(\.name.text) ?? [] diff --git a/Tests/Fixtures/Sources/UnusedParameterFixtures/testNestedFunctionAnalyzerPath.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testNestedFunctionAnalyzerPath.swift new file mode 100644 index 000000000..6b570b197 --- /dev/null +++ b/Tests/Fixtures/Sources/UnusedParameterFixtures/testNestedFunctionAnalyzerPath.swift @@ -0,0 +1,11 @@ +import Foundation + +class SyntaxFixtureNestedAnalyzer { + func outer(outerParam: String) { + func innerFunc(param: String) { + print(outerParam) + } + + innerFunc(param: "value") + } +} diff --git a/Tests/PeripheryTests/DeterminismRegressionTest.swift b/Tests/PeripheryTests/DeterminismRegressionTest.swift index a51352500..92cedf664 100644 --- a/Tests/PeripheryTests/DeterminismRegressionTest.swift +++ b/Tests/PeripheryTests/DeterminismRegressionTest.swift @@ -8,8 +8,7 @@ import XCTest final class DeterminismRegressionTest: XCTestCase { private func makeGraph() -> SourceGraph { let configuration = Configuration() - let logger = Logger(quiet: true, verbose: false, colorMode: .never) - return SourceGraph(configuration: configuration, logger: logger) + return SourceGraph(configuration: configuration) } private func makeSwiftVersion() -> SwiftVersion { @@ -27,10 +26,11 @@ final class DeterminismRegressionTest: XCTestCase { kind: Declaration.Kind, name: String, usr: String, - location: Location + location: Location, + graph: SourceGraph ) -> Declaration { - let declaration = Declaration(name: name, kind: kind, usrs: [usr], location: location) - return declaration + let usrID = graph.usrInterner.intern(usr) + return Declaration(name: name, kind: kind, usrs: [usr], usrIDs: [usrID], location: location) } private func makeReference( @@ -40,12 +40,15 @@ final class DeterminismRegressionTest: XCTestCase { name: String, location: Location, parent: Declaration, - role: Reference.Role = .unknown + role: Reference.Role = .unknown, + graph: SourceGraph ) -> Reference { + let usrID = graph.usrInterner.intern(usr) let reference = Reference( name: name, kind: kind, declarationKind: declarationKind, + usrID: usrID, usr: usr, location: location ) @@ -61,7 +64,8 @@ final class DeterminismRegressionTest: XCTestCase { kind: .extensionClass, name: "Widget", usr: "ext_widget", - location: makeLocation("/tmp/ext.swift", module: "Feature", line: 1) + location: makeLocation("/tmp/ext.swift", module: "Feature", line: 1), + graph: graph ) let later = makeReference( @@ -70,7 +74,8 @@ final class DeterminismRegressionTest: XCTestCase { usr: "class_later", name: "Widget", location: makeLocation("/tmp/widget.swift", module: "A", line: 20), - parent: extDecl + parent: extDecl, + graph: graph ) let earlier = makeReference( kind: .normal, @@ -78,7 +83,8 @@ final class DeterminismRegressionTest: XCTestCase { usr: "class_earlier", name: "Widget", location: makeLocation("/tmp/widget.swift", module: "B", line: 10), - parent: extDecl + parent: extDecl, + graph: graph ) extDecl.references = [later, earlier] @@ -87,14 +93,15 @@ final class DeterminismRegressionTest: XCTestCase { XCTAssertEqual(selected.usr, "class_earlier") } - func testBaseDeclarationUsesDeterministicSelection() { + func testBaseDeclarationUsesDeterministicSelection() throws { let graph = makeGraph() let overrideDecl = makeDeclaration( kind: .functionMethodInstance, name: "configure()", usr: "child_configure", - location: makeLocation("/tmp/child.swift", module: "Feature", line: 30) + location: makeLocation("/tmp/child.swift", module: "Feature", line: 30), + graph: graph ) overrideDecl.modifiers.insert("override") @@ -102,13 +109,15 @@ final class DeterminismRegressionTest: XCTestCase { kind: .functionMethodInstance, name: "configure()", usr: "base_later", - location: makeLocation("/tmp/later.swift", module: "BaseA", line: 50) + location: makeLocation("/tmp/later.swift", module: "BaseA", line: 50), + graph: graph ) let baseEarlier = makeDeclaration( kind: .functionMethodInstance, name: "configure()", usr: "base_earlier", - location: makeLocation("/tmp/earlier.swift", module: "BaseB", line: 10) + location: makeLocation("/tmp/earlier.swift", module: "BaseB", line: 10), + graph: graph ) let refToLater = makeReference( @@ -117,7 +126,8 @@ final class DeterminismRegressionTest: XCTestCase { usr: "child_configure", name: "configure()", location: makeLocation("/tmp/ref_later.swift", module: "Feature", line: 1), - parent: baseLater + parent: baseLater, + graph: graph ) let refToEarlier = makeReference( kind: .related, @@ -125,18 +135,21 @@ final class DeterminismRegressionTest: XCTestCase { usr: "child_configure", name: "configure()", location: makeLocation("/tmp/ref_earlier.swift", module: "Feature", line: 2), - parent: baseEarlier + parent: baseEarlier, + graph: graph ) + try graph.add(overrideDecl) graph.add(refToLater) graph.add(refToEarlier) + graph.indexingComplete() let (base, resolved) = graph.baseDeclaration(fromOverride: overrideDecl) XCTAssertTrue(resolved) XCTAssertEqual(base.usrs, ["base_earlier"]) } - func testExternalOverrideRetainerRetainsWhenAnyMatchingRelatedReferenceIsExternal() { + func testExternalOverrideRetainerRetainsWhenAnyMatchingRelatedReferenceIsExternal() throws { let graph = makeGraph() let configuration = Configuration() let swiftVersion = makeSwiftVersion() @@ -145,7 +158,8 @@ final class DeterminismRegressionTest: XCTestCase { kind: .functionMethodInstance, name: "configure()", usr: "feature_configure", - location: makeLocation("/tmp/feature.swift", module: "Feature", line: 100) + location: makeLocation("/tmp/feature.swift", module: "Feature", line: 100), + graph: graph ) overrideDecl.modifiers.insert("override") @@ -153,10 +167,11 @@ final class DeterminismRegressionTest: XCTestCase { kind: .functionMethodInstance, name: "configure()", usr: "internal_base", - location: makeLocation("/tmp/base.swift", module: "Core", line: 10) + location: makeLocation("/tmp/base.swift", module: "Core", line: 10), + graph: graph ) - graph.add(internalBase) - graph.add(overrideDecl) + try graph.add(internalBase) + try graph.add(overrideDecl) let matchingInternal = makeReference( kind: .related, @@ -164,7 +179,8 @@ final class DeterminismRegressionTest: XCTestCase { usr: "internal_base", name: "configure()", location: overrideDecl.location, - parent: overrideDecl + parent: overrideDecl, + graph: graph ) let matchingExternal = makeReference( kind: .related, @@ -172,7 +188,8 @@ final class DeterminismRegressionTest: XCTestCase { usr: "external_base", name: "configure()", location: overrideDecl.location, - parent: overrideDecl + parent: overrideDecl, + graph: graph ) overrideDecl.related = [matchingInternal, matchingExternal] @@ -180,7 +197,7 @@ final class DeterminismRegressionTest: XCTestCase { XCTAssertTrue(graph.isRetained(overrideDecl)) } - func testAssetReferenceRetainerHandlesAllMatchingSourcesForClassName() { + func testAssetReferenceRetainerHandlesAllMatchingSourcesForClassName() throws { let graph = makeGraph() let configuration = Configuration() @@ -188,20 +205,22 @@ final class DeterminismRegressionTest: XCTestCase { kind: .class, name: "FixtureController", usr: "fixture_controller", - location: makeLocation("/tmp/controller.swift", module: "UI", line: 1) + location: makeLocation("/tmp/controller.swift", module: "UI", line: 1), + graph: graph ) let outletDecl = makeDeclaration( kind: .varInstance, name: "submitButton", usr: "fixture_controller_submit_button", - location: makeLocation("/tmp/controller.swift", module: "UI", line: 5) + location: makeLocation("/tmp/controller.swift", module: "UI", line: 5), + graph: graph ) outletDecl.attributes.insert(DeclarationAttribute(name: "IBOutlet", arguments: nil)) outletDecl.parent = classDecl - classDecl.declarations.insert(outletDecl) + classDecl.declarations.append(outletDecl) - graph.add(classDecl) - graph.add(outletDecl) + try graph.add(classDecl) + try graph.add(outletDecl) graph.markRedundantPublicAccessibility(classDecl, modules: ["UI"]) graph.markRedundantPublicAccessibility(outletDecl, modules: ["UI"]) @@ -215,6 +234,7 @@ final class DeterminismRegressionTest: XCTestCase { ) graph.add(xcDataModelRef) graph.add(ibRef) + graph.indexingComplete() AssetReferenceRetainer(graph: graph, configuration: configuration, swiftVersion: makeSwiftVersion()).mutate() @@ -224,7 +244,7 @@ final class DeterminismRegressionTest: XCTestCase { XCTAssertNil(graph.redundantPublicAccessibility[outletDecl]) } - func testProtocolConformanceReferenceBuilderDeterministicallySelectsSuperclassImplementation() { + func testProtocolConformanceReferenceBuilderDeterministicallySelectsSuperclassImplementation() throws { let graph = makeGraph() let configuration = Configuration() @@ -232,61 +252,68 @@ final class DeterminismRegressionTest: XCTestCase { kind: .protocol, name: "Renderable", usr: "proto_renderable", - location: makeLocation("/tmp/proto.swift", module: "Core", line: 1) + location: makeLocation("/tmp/proto.swift", module: "Core", line: 1), + graph: graph ) let requirement = makeDeclaration( kind: .functionMethodInstance, name: "configure()", usr: "proto_requirement_configure", - location: makeLocation("/tmp/proto.swift", module: "Core", line: 2) + location: makeLocation("/tmp/proto.swift", module: "Core", line: 2), + graph: graph ) requirement.parent = proto - proto.declarations.insert(requirement) + proto.declarations.append(requirement) let conformingClass = makeDeclaration( kind: .class, name: "FeatureWidget", usr: "feature_widget", - location: makeLocation("/tmp/feature.swift", module: "Feature", line: 1) + location: makeLocation("/tmp/feature.swift", module: "Feature", line: 1), + graph: graph ) let superLater = makeDeclaration( kind: .class, name: "BaseWidgetA", usr: "base_widget_a", - location: makeLocation("/tmp/base.swift", module: "BaseA", line: 40) + location: makeLocation("/tmp/base.swift", module: "BaseA", line: 40), + graph: graph ) let superEarlier = makeDeclaration( kind: .class, name: "BaseWidgetB", usr: "base_widget_b", - location: makeLocation("/tmp/base.swift", module: "BaseB", line: 5) + location: makeLocation("/tmp/base.swift", module: "BaseB", line: 5), + graph: graph ) let superLaterMethod = makeDeclaration( kind: .functionMethodInstance, name: "configure()", usr: "base_widget_a_configure", - location: makeLocation("/tmp/base_method.swift", module: "BaseA", line: 41) + location: makeLocation("/tmp/base_method.swift", module: "BaseA", line: 41), + graph: graph ) superLaterMethod.parent = superLater - superLater.declarations.insert(superLaterMethod) + superLater.declarations.append(superLaterMethod) let superEarlierMethod = makeDeclaration( kind: .functionMethodInstance, name: "configure()", usr: "base_widget_b_configure", - location: makeLocation("/tmp/base_method.swift", module: "BaseB", line: 6) + location: makeLocation("/tmp/base_method.swift", module: "BaseB", line: 6), + graph: graph ) superEarlierMethod.parent = superEarlier - superEarlier.declarations.insert(superEarlierMethod) + superEarlier.declarations.append(superEarlierMethod) - graph.add(proto) - graph.add(requirement) - graph.add(conformingClass) - graph.add(superLater) - graph.add(superEarlier) - graph.add(superLaterMethod) - graph.add(superEarlierMethod) + try graph.add(proto) + try graph.add(requirement) + try graph.add(conformingClass) + try graph.add(superLater) + try graph.add(superEarlier) + try graph.add(superLaterMethod) + try graph.add(superEarlierMethod) let conformsRef = makeReference( kind: .related, @@ -294,7 +321,8 @@ final class DeterminismRegressionTest: XCTestCase { usr: "proto_renderable", name: "Renderable", location: makeLocation("/tmp/feature.swift", module: "Feature", line: 3), - parent: conformingClass + parent: conformingClass, + graph: graph ) let inheritsLater = makeReference( kind: .related, @@ -302,7 +330,8 @@ final class DeterminismRegressionTest: XCTestCase { usr: "base_widget_a", name: "BaseWidgetA", location: makeLocation("/tmp/feature.swift", module: "Feature", line: 4), - parent: conformingClass + parent: conformingClass, + graph: graph ) let inheritsEarlier = makeReference( kind: .related, @@ -310,12 +339,14 @@ final class DeterminismRegressionTest: XCTestCase { usr: "base_widget_b", name: "BaseWidgetB", location: makeLocation("/tmp/feature.swift", module: "Feature", line: 5), - parent: conformingClass + parent: conformingClass, + graph: graph ) graph.add(conformsRef, from: conformingClass) graph.add(inheritsLater, from: conformingClass) graph.add(inheritsEarlier, from: conformingClass) + graph.indexingComplete() ProtocolConformanceReferenceBuilder(graph: graph, configuration: configuration, swiftVersion: makeSwiftVersion()).mutate() @@ -331,26 +362,30 @@ final class DeterminismRegressionTest: XCTestCase { kind: .protocol, name: "BaseWidget", usr: "base_protocol", - location: makeLocation("/tmp/base_protocol.swift", module: "Core", line: 1) + location: makeLocation("/tmp/base_protocol.swift", module: "Core", line: 1), + graph: graph ) let constrainingProtocol = makeDeclaration( kind: .protocol, name: "Configurable", usr: "constraining_protocol", - location: makeLocation("/tmp/configurable.swift", module: "Core", line: 1) + location: makeLocation("/tmp/configurable.swift", module: "Core", line: 1), + graph: graph ) let requirementLater = makeDeclaration( kind: .functionMethodInstance, name: "configure()", usr: "requirement_later", - location: makeLocation("/tmp/configurable.swift", module: "A", line: 40) + location: makeLocation("/tmp/configurable.swift", module: "A", line: 40), + graph: graph ) requirementLater.parent = constrainingProtocol let requirementEarlier = makeDeclaration( kind: .functionMethodInstance, name: "configure()", usr: "requirement_earlier", - location: makeLocation("/tmp/configurable.swift", module: "B", line: 5) + location: makeLocation("/tmp/configurable.swift", module: "B", line: 5), + graph: graph ) requirementEarlier.parent = constrainingProtocol constrainingProtocol.declarations = [requirementLater, requirementEarlier] @@ -359,16 +394,18 @@ final class DeterminismRegressionTest: XCTestCase { kind: .extensionProtocol, name: "BaseWidget", usr: "base_protocol_extension", - location: makeLocation("/tmp/base_protocol_ext.swift", module: "Feature", line: 1) + location: makeLocation("/tmp/base_protocol_ext.swift", module: "Feature", line: 1), + graph: graph ) let member = makeDeclaration( kind: .functionMethodInstance, name: "configure()", usr: "extension_member_configure", - location: makeLocation("/tmp/base_protocol_ext.swift", module: "Feature", line: 2) + location: makeLocation("/tmp/base_protocol_ext.swift", module: "Feature", line: 2), + graph: graph ) member.parent = extensionDecl - extensionDecl.declarations.insert(member) + extensionDecl.declarations.append(member) let extendsBaseProtocolRef = makeReference( kind: .normal, @@ -376,7 +413,8 @@ final class DeterminismRegressionTest: XCTestCase { usr: "base_protocol", name: "BaseWidget", location: makeLocation("/tmp/base_protocol_ext.swift", module: "Feature", line: 1), - parent: extensionDecl + parent: extensionDecl, + graph: graph ) let whereConstraintRef = makeReference( kind: .normal, @@ -385,16 +423,17 @@ final class DeterminismRegressionTest: XCTestCase { name: "Configurable", location: makeLocation("/tmp/base_protocol_ext.swift", module: "Feature", line: 1), parent: extensionDecl, - role: .genericRequirementType + role: .genericRequirementType, + graph: graph ) extensionDecl.references = [extendsBaseProtocolRef, whereConstraintRef] - graph.add(baseProtocol) - graph.add(constrainingProtocol) - graph.add(requirementLater) - graph.add(requirementEarlier) - graph.add(extensionDecl) - graph.add(member) + try graph.add(baseProtocol) + try graph.add(constrainingProtocol) + try graph.add(requirementLater) + try graph.add(requirementEarlier) + try graph.add(extensionDecl) + try graph.add(member) graph.add(extendsBaseProtocolRef, from: extensionDecl) graph.add(whereConstraintRef, from: extensionDecl) @@ -406,63 +445,74 @@ final class DeterminismRegressionTest: XCTestCase { // MARK: - USR Conflict Resolution - func testDuplicateUSRResolutionKeepsEarlierDeclaration() { + func testDuplicateUSRResolutionThrowsOnConflict() throws { let graph = makeGraph() let earlier = makeDeclaration( kind: .class, name: "MyClass", usr: "shared_usr", - location: makeLocation("/tmp/a.swift", module: "A", line: 10) + location: makeLocation("/tmp/a.swift", module: "A", line: 10), + graph: graph ) let later = makeDeclaration( kind: .class, name: "MyClass", usr: "shared_usr", - location: makeLocation("/tmp/b.swift", module: "B", line: 20) + location: makeLocation("/tmp/b.swift", module: "B", line: 20), + graph: graph ) - // Add earlier first, then later. Before the fix, SourceGraph.add always - // overwrote allDeclarationsByUsr unconditionally, so `later` would win. - // After the fix, the declaration that sorts first is kept. - graph.add(earlier) - graph.add(later) + try graph.add(earlier) + + XCTAssertThrowsError(try graph.add(later)) { error in + guard case let PeripheryError.sourceGraphIntegrityError(message) = error else { + return XCTFail("Unexpected error: \(error)") + } + + XCTAssertTrue(message.contains("shared_usr")) + } XCTAssertIdentical(graph.declaration(withUsr: "shared_usr"), earlier) } - func testDuplicateUSRResolutionIsDeterministicRegardlessOfInsertionOrder() { - let earlier = makeDeclaration( - kind: .class, - name: "MyClass", - usr: "shared_usr", - location: makeLocation("/tmp/a.swift", module: "A", line: 10) - ) - let later = makeDeclaration( - kind: .class, - name: "MyClass", - usr: "shared_usr", - location: makeLocation("/tmp/b.swift", module: "B", line: 20) - ) + func testDuplicateUSRResolutionThrowsRegardlessOfInsertionOrder() throws { + let locA = makeLocation("/tmp/a.swift", module: "A", line: 10) + let locB = makeLocation("/tmp/b.swift", module: "B", line: 20) let graph1 = makeGraph() - graph1.add(earlier) - graph1.add(later) + let earlier1 = makeDeclaration(kind: .class, name: "MyClass", usr: "shared_usr", location: locA, graph: graph1) + let later1 = makeDeclaration(kind: .class, name: "MyClass", usr: "shared_usr", location: locB, graph: graph1) + try graph1.add(earlier1) + + XCTAssertThrowsError(try graph1.add(later1)) { error in + guard case let PeripheryError.sourceGraphIntegrityError(message) = error else { + return XCTFail("Unexpected error: \(error)") + } + + XCTAssertTrue(message.contains("shared_usr")) + } let graph2 = makeGraph() - graph2.add(later) - graph2.add(earlier) - - // Both graphs should resolve to the same declaration (the earlier-sorting one), - // regardless of insertion order. Before the fix, graph1 would have `later` and - // graph2 would have `earlier`, producing different results from the same input. - XCTAssertIdentical(graph1.declaration(withUsr: "shared_usr"), earlier) - XCTAssertIdentical(graph2.declaration(withUsr: "shared_usr"), earlier) + let later2 = makeDeclaration(kind: .class, name: "MyClass", usr: "shared_usr", location: locB, graph: graph2) + let earlier2 = makeDeclaration(kind: .class, name: "MyClass", usr: "shared_usr", location: locA, graph: graph2) + try graph2.add(later2) + + XCTAssertThrowsError(try graph2.add(earlier2)) { error in + guard case let PeripheryError.sourceGraphIntegrityError(message) = error else { + return XCTFail("Unexpected error: \(error)") + } + + XCTAssertTrue(message.contains("shared_usr")) + } + + XCTAssertIdentical(graph1.declaration(withUsr: "shared_usr"), earlier1) + XCTAssertIdentical(graph2.declaration(withUsr: "shared_usr"), later2) } // MARK: - AncestralReferenceEliminator with Same-Location Declarations - func testAncestralReferenceEliminatorWithSameLocationDeclarations() { + func testAncestralReferenceEliminatorWithSameLocationDeclarations() throws { // Models the real-world scenario: a struct and a macro-generated class exist at // the same source location. If a dangling reference (to the struct's own USR) is // associated with the struct itself (instead of the class), it becomes a @@ -477,24 +527,27 @@ final class DeterminismRegressionTest: XCTestCase { kind: .struct, name: "FeatureRunner", usr: "feature_runner_struct", - location: makeLocation("/tmp/feature.swift", module: "Feature", line: 7) + location: makeLocation("/tmp/feature.swift", module: "Feature", line: 7), + graph: graph ) let classDecl = makeDeclaration( kind: .class, name: "$FeatureRunner", usr: "feature_runner_class", - location: makeLocation("/tmp/feature.swift", module: "Feature", line: 7) + location: makeLocation("/tmp/feature.swift", module: "Feature", line: 7), + graph: graph ) classDecl.parent = structDecl - structDecl.declarations.insert(classDecl) + structDecl.declarations.append(classDecl) // A reference TO the struct from an external declaration (the correct scenario). let externalParent = makeDeclaration( kind: .class, name: "AppRunner", usr: "app_runner", - location: makeLocation("/tmp/app.swift", module: "App", line: 1) + location: makeLocation("/tmp/app.swift", module: "App", line: 1), + graph: graph ) let externalRef = makeReference( kind: .normal, @@ -502,24 +555,26 @@ final class DeterminismRegressionTest: XCTestCase { usr: "feature_runner_struct", name: "FeatureRunner", location: makeLocation("/tmp/app.swift", module: "App", line: 5), - parent: externalParent + parent: externalParent, + graph: graph ) - graph.add(structDecl) - graph.add(classDecl) - graph.add(externalParent) + try graph.add(structDecl) + try graph.add(classDecl) + try graph.add(externalParent) graph.add(externalRef, from: externalParent) + graph.indexingComplete() + XCTAssertTrue(graph.hasReferences(to: structDecl)) - graph.indexingComplete() AncestralReferenceEliminator(graph: graph, configuration: configuration, swiftVersion: makeSwiftVersion()).mutate() // The external reference should survive since it's not a self-reference. XCTAssertTrue(graph.hasReferences(to: structDecl)) } - func testAncestralReferenceEliminatorRemovesSelfReferences() { + func testAncestralReferenceEliminatorRemovesSelfReferences() throws { // When a dangling reference is incorrectly associated with the declaration it // references (creating a self-reference), AncestralReferenceEliminator correctly // removes it. This test verifies the eliminator's behavior is correct — the fix @@ -532,7 +587,8 @@ final class DeterminismRegressionTest: XCTestCase { kind: .struct, name: "FeatureRunner", usr: "feature_runner_struct", - location: makeLocation("/tmp/feature.swift", module: "Feature", line: 7) + location: makeLocation("/tmp/feature.swift", module: "Feature", line: 7), + graph: graph ) // Self-reference: a reference to the struct's own USR, parented by the struct. @@ -542,15 +598,17 @@ final class DeterminismRegressionTest: XCTestCase { usr: "feature_runner_struct", name: "FeatureRunner", location: makeLocation("/tmp/feature.swift", module: "Feature", line: 7), - parent: structDecl + parent: structDecl, + graph: graph ) - graph.add(structDecl) + try graph.add(structDecl) graph.add(selfRef, from: structDecl) + graph.indexingComplete() + XCTAssertTrue(graph.hasReferences(to: structDecl)) - graph.indexingComplete() AncestralReferenceEliminator(graph: graph, configuration: configuration, swiftVersion: makeSwiftVersion()).mutate() // Self-reference should be eliminated, leaving the struct unreferenced. @@ -559,7 +617,7 @@ final class DeterminismRegressionTest: XCTestCase { // MARK: - Protocol Conformance Inversion with Multiple Conformances - func testProtocolConformanceInversionHandlesMultipleConformances() { + func testProtocolConformanceInversionHandlesMultipleConformances() throws { // When multiple classes conform to the same protocol, the inversion must // process all conformances correctly. Before the batch mutation fix, graph // mutations during iteration could cause order-dependent skipping. @@ -570,53 +628,59 @@ final class DeterminismRegressionTest: XCTestCase { kind: .protocol, name: "Runnable", usr: "proto_runnable", - location: makeLocation("/tmp/proto.swift", module: "Core", line: 1) + location: makeLocation("/tmp/proto.swift", module: "Core", line: 1), + graph: graph ) let requirement = makeDeclaration( kind: .functionMethodInstance, name: "run()", usr: "proto_run", - location: makeLocation("/tmp/proto.swift", module: "Core", line: 2) + location: makeLocation("/tmp/proto.swift", module: "Core", line: 2), + graph: graph ) requirement.parent = proto - proto.declarations.insert(requirement) + proto.declarations.append(requirement) let classA = makeDeclaration( kind: .class, name: "RunnerA", usr: "class_a", - location: makeLocation("/tmp/a.swift", module: "A", line: 1) + location: makeLocation("/tmp/a.swift", module: "A", line: 1), + graph: graph ) let implA = makeDeclaration( kind: .functionMethodInstance, name: "run()", usr: "impl_a_run", - location: makeLocation("/tmp/a.swift", module: "A", line: 2) + location: makeLocation("/tmp/a.swift", module: "A", line: 2), + graph: graph ) implA.parent = classA - classA.declarations.insert(implA) + classA.declarations.append(implA) let classB = makeDeclaration( kind: .class, name: "RunnerB", usr: "class_b", - location: makeLocation("/tmp/b.swift", module: "B", line: 1) + location: makeLocation("/tmp/b.swift", module: "B", line: 1), + graph: graph ) let implB = makeDeclaration( kind: .functionMethodInstance, name: "run()", usr: "impl_b_run", - location: makeLocation("/tmp/b.swift", module: "B", line: 2) + location: makeLocation("/tmp/b.swift", module: "B", line: 2), + graph: graph ) implB.parent = classB - classB.declarations.insert(implB) + classB.declarations.append(implB) - graph.add(proto) - graph.add(requirement) - graph.add(classA) - graph.add(implA) - graph.add(classB) - graph.add(implB) + try graph.add(proto) + try graph.add(requirement) + try graph.add(classA) + try graph.add(implA) + try graph.add(classB) + try graph.add(implB) // Related references from conforming implementations to the protocol requirement. let relatedA = makeReference( @@ -625,7 +689,8 @@ final class DeterminismRegressionTest: XCTestCase { usr: "proto_run", name: "run()", location: implA.location, - parent: implA + parent: implA, + graph: graph ) let relatedB = makeReference( kind: .related, @@ -633,7 +698,8 @@ final class DeterminismRegressionTest: XCTestCase { usr: "proto_run", name: "run()", location: implB.location, - parent: implB + parent: implB, + graph: graph ) // Conformance references from classes to protocol. @@ -643,7 +709,8 @@ final class DeterminismRegressionTest: XCTestCase { usr: "proto_runnable", name: "Runnable", location: classA.location, - parent: classA + parent: classA, + graph: graph ) let conformsB = makeReference( kind: .related, @@ -651,7 +718,8 @@ final class DeterminismRegressionTest: XCTestCase { usr: "proto_runnable", name: "Runnable", location: classB.location, - parent: classB + parent: classB, + graph: graph ) graph.add(relatedA, from: implA) diff --git a/Tests/PeripheryTests/ObjcAccessibleRetentionTest.swift b/Tests/PeripheryTests/ObjcAccessibleRetentionTest.swift index f4b895279..8062ee70b 100644 --- a/Tests/PeripheryTests/ObjcAccessibleRetentionTest.swift +++ b/Tests/PeripheryTests/ObjcAccessibleRetentionTest.swift @@ -1,8 +1,9 @@ import SystemPackage -@testable import TestShared import XCTest #if os(macOS) + @testable import TestShared + final class ObjcAccessibleRetentionTest: FixtureSourceGraphTestCase { func testRetainsOptionalProtocolMethodImplementedInSubclass() throws { try XCTSkipIf(Self.swiftVersion.version.isVersion(lessThan: "6.3"), "Requires Swift >= 6.3") diff --git a/Tests/PeripheryTests/ObjcAnnotatedRetentionTest.swift b/Tests/PeripheryTests/ObjcAnnotatedRetentionTest.swift index a2f344343..bbf60631b 100644 --- a/Tests/PeripheryTests/ObjcAnnotatedRetentionTest.swift +++ b/Tests/PeripheryTests/ObjcAnnotatedRetentionTest.swift @@ -1,8 +1,9 @@ import SystemPackage -@testable import TestShared import XCTest #if os(macOS) + @testable import TestShared + final class ObjcAnnotatedRetentionTest: FixtureSourceGraphTestCase { func testRetainsAnnotatedExtensionDeclarations() { analyze(retainObjcAnnotated: true) { diff --git a/Tests/PeripheryTests/SourceGraphConflictTests.swift b/Tests/PeripheryTests/SourceGraphConflictTests.swift new file mode 100644 index 000000000..eb7716f3f --- /dev/null +++ b/Tests/PeripheryTests/SourceGraphConflictTests.swift @@ -0,0 +1,61 @@ +import Configuration +import Shared +@testable import SourceGraph +import SystemPackage +import XCTest + +final class SourceGraphConflictTests: XCTestCase { + func testAddThrowsOnUsrConflict() throws { + let graph = SourceGraph( + configuration: Configuration() + ) + let usr = "s:4Test3fooyyF" + + let firstDeclaration = makeDeclaration( + in: graph, + name: "foo", + usr: usr, + path: "/tmp/First.swift", + modules: ["FirstModule"] + ) + let conflictingDeclaration = makeDeclaration( + in: graph, + name: "foo", + usr: usr, + path: "/tmp/Second.swift", + modules: ["SecondModule"] + ) + + try graph.add(firstDeclaration) + + XCTAssertThrowsError(try graph.add(conflictingDeclaration)) { error in + guard case let PeripheryError.sourceGraphIntegrityError(message) = error else { + return XCTFail("Unexpected error: \(error)") + } + + XCTAssertTrue(message.contains(usr)) + XCTAssertTrue(message.contains("FirstModule")) + XCTAssertTrue(message.contains("SecondModule")) + } + + XCTAssertTrue(graph.declaration(withUsr: usr) === firstDeclaration) + } + + private func makeDeclaration( + in graph: SourceGraph, + name: String, + usr: String, + path: String, + modules: Set + ) -> Declaration { + let file = SourceFile(path: FilePath(path), modules: modules) + let usrID = graph.usrInterner.intern(usr) + return Declaration( + name: name, + kind: .functionFree, + usrs: [usr], + usrIDs: [usrID], + location: Location(file: file, line: 1, column: 1) + ) + } +} diff --git a/Tests/PeripheryTests/Syntax/UnusedParameterTest.swift b/Tests/PeripheryTests/Syntax/UnusedParameterTest.swift index 5d9ad3a84..b533d8c72 100644 --- a/Tests/PeripheryTests/Syntax/UnusedParameterTest.swift +++ b/Tests/PeripheryTests/Syntax/UnusedParameterTest.swift @@ -1,5 +1,7 @@ import Foundation @testable import SourceGraph +import SwiftParser +import SwiftSyntax @testable import SyntaxAnalysis @testable import TestShared import XCTest @@ -114,6 +116,23 @@ final class UnusedParameterTest: XCTestCase { assertUsed(label: "param", name: "param", in: "myFunc(param:)") } + func testNestedFunctionAnalyzerPath() { + let sourceFile = SourceFile(path: testFixturePath, modules: ["UnusedParameterFixtures"]) + let source = try! String(contentsOf: sourceFile.path.url, encoding: .utf8) + let syntax = Parser.parse(source: source) + let converter = SourceLocationConverter(fileName: sourceFile.path.string, tree: syntax) + let analyzer = UnusedParameterAnalyzer() + let paramsByFunction = analyzer.analyze( + file: sourceFile, + syntax: syntax, + locationConverter: converter, + parseProtocols: false + ) + + let unusedParams = paramsByFunction.first { $0.key.fullName == "innerFunc(param:)" }?.value + XCTAssertEqual(unusedParams?.map(\.name.text), ["param"]) + } + func testNestedVariable() { analyze() assertUsed(label: "param", name: "param", in: "myFunc(param:)") diff --git a/Tests/Shared/SourceGraphTestCase.swift b/Tests/Shared/SourceGraphTestCase.swift index 1177ed682..73b105f3e 100644 --- a/Tests/Shared/SourceGraphTestCase.swift +++ b/Tests/Shared/SourceGraphTestCase.swift @@ -26,7 +26,7 @@ open class SourceGraphTestCase: XCTestCase { swiftVersion = SwiftVersion(shell: shell) let configuration = Configuration() configuration.quiet = true - graph = SourceGraph(configuration: configuration, logger: logger) + graph = SourceGraph(configuration: configuration) } override open func tearDown() { @@ -56,7 +56,7 @@ open class SourceGraphTestCase: XCTestCase { ) } - graph = SourceGraph(configuration: configuration, logger: logger) + graph = SourceGraph(configuration: configuration) let pipeline = IndexPipeline( plan: newPlan, graph: SourceGraphMutex(graph: graph), @@ -66,7 +66,7 @@ open class SourceGraphTestCase: XCTestCase { ) try! pipeline.perform() - allIndexedDeclarations = graph.allDeclarations + allIndexedDeclarations = Set(graph.allDeclarations) try! SourceGraphMutatorRunner( graph: graph, logger: logger, @@ -84,7 +84,7 @@ open class SourceGraphTestCase: XCTestCase { } else { guard let declaration = materialize(description, file: file, line: line) else { return } - if !Self.graph.usedDeclarations.contains(declaration) { + if !declaration.isUsed { XCTFail("Expected declaration to be referenced: \(declaration)", file: file, line: line) } @@ -303,7 +303,7 @@ open class SourceGraphTestCase: XCTestCase { switch scope { case let .declaration(declaration): if result.contains(declaration) { - result = declaration.declarations.union(declaration.unusedParameters) + result = Set(declaration.declarations + declaration.unusedParameters) } case let .module(module): result = result.filter { $0.location.file.modules.contains(module) } @@ -333,7 +333,7 @@ private extension [ScanResult] { } } - var redundantProtocolDeclarations: [Declaration: (references: Set, inherited: Set)] { + var redundantProtocolDeclarations: [Declaration: (references: [Reference], inherited: Set)] { reduce(into: .init()) { result, scanResult in if case let .redundantProtocol(references, inherited) = scanResult.annotation { result[scanResult.declaration] = (references, inherited) diff --git a/baselines/bazel.json b/baselines/bazel.json index 835493c5d..c9939d635 100644 --- a/baselines/bazel.json +++ b/baselines/bazel.json @@ -4,6 +4,9 @@ "s:11SourceGraph0aB8DebuggerC", "s:13SystemPackage8FilePathV10ExtensionsE5chdir7closureyyyKXE_tKF", "s:14SyntaxAnalysis21UnusedParameterParserV5parse4file0F9ProtocolsSayAA8FunctionVG11SourceGraph0J4FileC_SbtKFZ", + "s:14SyntaxAnalysis21UnusedParameterParserV5parse4file6syntax17locationConverter0F9ProtocolsSayAA8FunctionVG11SourceGraph0M4FileC_05SwiftA00moA0VAO0m8LocationJ0CSbtFZ", + "s:14SyntaxAnalysis21UnusedParameterParserV5parse6syntaxSayAA8FunctionVG05SwiftA0010SourceFileA0V_tF", + "s:14SyntaxAnalysis23UnusedParameterAnalyzerC7analyze4file6syntax17locationConverter14parseProtocolsSDyAA8FunctionVShyAA0D0VGG11SourceGraph0N4FileC_05SwiftA00npA0VAR0n8LocationJ0CSbtF", "s:14SyntaxAnalysis8FunctionV8fullNameSSvp" ] } diff --git a/baselines/linux-bazel.json b/baselines/linux-bazel.json index f55df1d7e..f8df136df 100644 --- a/baselines/linux-bazel.json +++ b/baselines/linux-bazel.json @@ -7,6 +7,9 @@ "s:13SystemPackage8FilePathV10ExtensionsE4globyShyACGSSFZ", "s:13SystemPackage8FilePathV10ExtensionsE5chdir7closureyyyKXE_tKF", "s:14SyntaxAnalysis21UnusedParameterParserV5parse4file0F9ProtocolsSayAA8FunctionVG11SourceGraph0J4FileC_SbtKFZ", + "s:14SyntaxAnalysis21UnusedParameterParserV5parse4file6syntax17locationConverter0F9ProtocolsSayAA8FunctionVG11SourceGraph0M4FileC_05SwiftA00moA0VAO0m8LocationJ0CSbtFZ", + "s:14SyntaxAnalysis21UnusedParameterParserV5parse6syntaxSayAA8FunctionVG05SwiftA0010SourceFileA0V_tF", + "s:14SyntaxAnalysis23UnusedParameterAnalyzerC7analyze4file6syntax17locationConverter14parseProtocolsSDyAA8FunctionVShyAA0D0VGG11SourceGraph0N4FileC_05SwiftA00npA0VAR0n8LocationJ0CSbtF", "s:14SyntaxAnalysis8FunctionV8fullNameSSvp", "s:6Shared14SetupSelectionO", "s:6Shared17SetupGuideHelpersC6select8multipleAA0B9SelectionOSaySSG_tF", diff --git a/baselines/linux.json b/baselines/linux.json index 13418f45e..ee4ccd627 100644 --- a/baselines/linux.json +++ b/baselines/linux.json @@ -1,8 +1,6 @@ { "v1": { "usrs": [ - "import-TestShared-Tests/PeripheryTests/ObjcAccessibleRetentionTest.swift:2:1", - "import-TestShared-Tests/PeripheryTests/ObjcAnnotatedRetentionTest.swift:2:1", "s:11SourceGraph15ProjectFileKindO10extensionsSaySSGvp", "s:6Shared14SetupSelectionO", "s:6Shared17SetupGuideHelpersC6select8multipleAA0B9SelectionOSaySSG_tF",