From c3ab6b7303f21f1b34105506be6cf8215648cce3 Mon Sep 17 00:00:00 2001 From: Jaden Peterson Date: Mon, 6 Jan 2025 13:59:07 -0500 Subject: [PATCH 1/7] Added a Scala 3 toolchain for use in rules_scala --- scala/BUILD | 1 + scala_config.bzl | 14 +++++++++++++- test/shell/test_scala_config.sh | 8 +++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/scala/BUILD b/scala/BUILD index 7663b980a..94f81a12b 100644 --- a/scala/BUILD +++ b/scala/BUILD @@ -34,6 +34,7 @@ scala_toolchain( [ toolchain( name = tc, + target_settings = ["@io_bazel_rules_scala_config//:scala_version" + version_suffix(SCALA_VERSION)], toolchain = tc + "_impl", toolchain_type = "//scala:toolchain_type", visibility = ["//visibility:public"], diff --git a/scala_config.bzl b/scala_config.bzl index 70cf132c3..d8ffeaf4c 100644 --- a/scala_config.bzl +++ b/scala_config.bzl @@ -29,12 +29,24 @@ def _store_config(repository_ctx): ) # All versions supported - scala_versions = repository_ctx.attr.scala_versions + if "SCALA_VERSIONS" in repository_ctx.os.environ: + scala_versions = repository_ctx.os.environ["SCALA_VERSIONS"].split(",") + else: + scala_versions = repository_ctx.attr.scala_versions + if not scala_versions: scala_versions = [scala_version] elif scala_version not in scala_versions: fail("You have to include the default Scala version (%s) in the `scala_versions` list." % scala_version) + # dottyijar requires Scala v3.6.2, but we don't want to force the caller to always provide 3.6.2. Therefore, we + # append it if it hasn't been provided. + # + # Once we move to Bzlmod, this shouldn't be a problem, since we can register a toolchain for Scala v3.6.2 without + # requiring users of this ruleset to do so. + if "3.6.2" not in scala_versions: + scala_versions = scala_versions + ["3.6.2"] + enable_compiler_dependency_tracking = repository_ctx.os.environ.get( "ENABLE_COMPILER_DEPENDENCY_TRACKING", str(repository_ctx.attr.enable_compiler_dependency_tracking), diff --git a/test/shell/test_scala_config.sh b/test/shell/test_scala_config.sh index ab17f661d..6c53fdc0e 100755 --- a/test/shell/test_scala_config.sh +++ b/test/shell/test_scala_config.sh @@ -7,17 +7,23 @@ runner=$(get_test_runner "${1:-local}") test_classpath_contains_2_12() { bazel aquery 'mnemonic("Javac", //src/java/io/bazel/rulesscala/scalac:scalac)' \ --repo_env=SCALA_VERSION=2.12.20 \ + --repo_env=SCALA_VERSIONS=2.12.20 \ | grep scala-library-2.12 } test_classpath_contains_2_13() { bazel aquery 'mnemonic("Javac", //src/java/io/bazel/rulesscala/scalac:scalac)' \ --repo_env=SCALA_VERSION=2.13.15 \ + --repo_env=SCALA_VERSIONS=2.13.15 \ | grep scala-library-2.13 } test_scala_config_content() { - bazel build --repo_env=SCALA_VERSION=0.0.0 @io_bazel_rules_scala_config//:all 2> /dev/null + bazel build \ + --repo_env=SCALA_VERSION=0.0.0 \ + --repo_env=SCALA_VERSIONS=0.0.0 \ + @io_bazel_rules_scala_config//:all 2> /dev/null + grep "SCALA_MAJOR_VERSION='0.0'" \ "$(bazel info output_base)"/external/*io_bazel_rules_scala_config/config.bzl } From 3d18e86535c079315971a1d1dfe93ed6d76e7856 Mon Sep 17 00:00:00 2001 From: Jaden Peterson Date: Mon, 6 Jan 2025 16:09:39 -0500 Subject: [PATCH 2/7] Vary the specs2 version by the Scala version Different versions of specs2 support different Scala versions, so it's necessary to use a different version, depending on which Scala version we're on. v4.10.6 is the last to support Scala 2.11, v4.20.9 is the last to support Scala 2, and the latest version as of the time this commit was written, v5.5.8, only supports Scala 3. --- .../testing/multi_frameworks_toolchain/BUILD | 2 + scala/private/macros/scala_repositories.bzl | 14 ++++- scripts/create_repository.py | 50 +++++++++-------- specs2/specs2_junit.bzl | 2 + .../specs2/Specs2RunnerBuilder.scala | 2 +- third_party/repositories/scala_2_11.bzl | 52 +++++++++++++---- third_party/repositories/scala_2_12.bzl | 56 ++++++++++++++----- third_party/repositories/scala_2_13.bzl | 56 ++++++++++++++----- third_party/repositories/scala_3_1.bzl | 22 ++++++-- third_party/repositories/scala_3_2.bzl | 22 ++++++-- third_party/repositories/scala_3_3.bzl | 22 ++++++-- third_party/repositories/scala_3_4.bzl | 22 ++++++-- third_party/repositories/scala_3_5.bzl | 22 ++++++-- third_party/repositories/scala_3_6.bzl | 22 ++++++-- 14 files changed, 279 insertions(+), 87 deletions(-) diff --git a/examples/testing/multi_frameworks_toolchain/BUILD b/examples/testing/multi_frameworks_toolchain/BUILD index 27eedcf7e..53baf7211 100644 --- a/examples/testing/multi_frameworks_toolchain/BUILD +++ b/examples/testing/multi_frameworks_toolchain/BUILD @@ -31,5 +31,7 @@ setup_scala_testing_toolchain( ], specs2_junit_classpath = [ "@io_bazel_rules_scala_org_specs2_specs2_junit", + "@org_portable_scala_portable_scala_reflect", + "@org_scala_sbt_test_interface", ], ) diff --git a/scala/private/macros/scala_repositories.bzl b/scala/private/macros/scala_repositories.bzl index 81b6c9cbf..39c19bac8 100644 --- a/scala/private/macros/scala_repositories.bzl +++ b/scala/private/macros/scala_repositories.bzl @@ -37,6 +37,12 @@ _COMPILER_SOURCES_ENTRY_TEMPLATE = """ "@io_bazel_rules_scala_config//:scala_version{scala_version_suffix}": "@scala_compiler_source{scala_version_suffix}//:src",""" +_JUNIT_DEPS = ["io_bazel_rules_scala_org_hamcrest_hamcrest_core"] +_SPECS2_DEPS = [ + "org_portable_scala_portable_scala_reflect", + "org_scala_sbt_test_interface", +] + def _compiler_sources_repo_impl(rctx): sources = [ _COMPILER_SOURCES_ENTRY_TEMPLATE.format( @@ -144,12 +150,18 @@ def rules_scala_setup(scala_compiler_srcjar = None): def _artifact_ids(scala_version): result = [ + "io_bazel_rules_scala_junit_junit", + "io_bazel_rules_scala_org_specs2_specs2_common", + "io_bazel_rules_scala_org_specs2_specs2_core", + "io_bazel_rules_scala_org_specs2_specs2_fp", + "io_bazel_rules_scala_org_specs2_specs2_junit", + "io_bazel_rules_scala_org_specs2_specs2_matcher", "io_bazel_rules_scala_scala_compiler", "io_bazel_rules_scala_scala_library", "io_bazel_rules_scala_scala_parser_combinators", "io_bazel_rules_scala_scala_xml", "org_scala_lang_modules_scala_collection_compat", - ] + ] + _JUNIT_DEPS + _SPECS2_DEPS if scala_version.startswith("2."): result.extend([ diff --git a/scripts/create_repository.py b/scripts/create_repository.py index 9ade78254..7d4b6e23e 100755 --- a/scripts/create_repository.py +++ b/scripts/create_repository.py @@ -76,12 +76,12 @@ def select_root_artifacts(scala_version, scala_major, is_scala_3) -> List[str]: scala_2_version = scala_version scala_2_major = scala_major - scalatest_major = scala_major + scala_3_major = scala_major if is_scala_3: scala_2_version = max_scala_2_version scala_2_major = max_scala_2_major - scalatest_major = '3' + scala_3_major = '3' scalafmt_version = SCALAFMT_VERSION scalapb_version = SCALAPB_VERSION @@ -92,29 +92,34 @@ def select_root_artifacts(scala_version, scala_major, is_scala_3) -> List[str]: scalapb_version = '0.9.8' protoc_bridge_version = '0.7.14' + if is_scala_3: + specs2_version = '4.20.9' + elif scala_major == '2.11': + specs2_version = '4.10.6' + else: + specs2_version = '4.20.9' + root_artifacts = [ - 'com.google.api.grpc:proto-google-common-protos:' + - GRPC_COMMON_PROTOS_VERSION, + f'com.google.api.grpc:proto-google-common-protos:{GRPC_COMMON_PROTOS_VERSION}', f'com.google.guava:guava:{GUAVA_VERSION}', f'com.google.protobuf:protobuf-java:{PROTOBUF_JAVA_VERSION}', - f'com.thesamet.scalapb:compilerplugin_{scala_2_major}:' + - scalapb_version, - f'com.thesamet.scalapb:protoc-bridge_{scala_2_major}:' + - protoc_bridge_version, - f'com.thesamet.scalapb:scalapb-runtime_{scala_2_major}:' + - scalapb_version, - f'com.thesamet.scalapb:scalapb-runtime-grpc_{scala_2_major}:' + - scalapb_version, - f'org.scala-lang.modules:scala-parser-combinators_{scala_2_major}:' + - PARSER_COMBINATORS_VERSION, + f'com.thesamet.scalapb:compilerplugin_{scala_2_major}:{scalapb_version}', + f'com.thesamet.scalapb:protoc-bridge_{scala_2_major}:{protoc_bridge_version}', + f'com.thesamet.scalapb:scalapb-runtime-grpc_{scala_2_major}:{scalapb_version}', + f'com.thesamet.scalapb:scalapb-runtime_{scala_2_major}:{scalapb_version}', + f'org.scala-lang.modules:scala-parser-combinators_{scala_2_major}:{PARSER_COMBINATORS_VERSION}', f'org.scala-lang:scala-compiler:{scala_2_version}', f'org.scala-lang:scala-library:{scala_2_version}', f'org.scala-lang:scala-reflect:{scala_2_version}', f'org.scala-lang:scalap:{scala_2_version}', f'org.scalameta:scalafmt-core_{scala_2_major}:{scalafmt_version}', - f'org.scalatest:scalatest_{scalatest_major}:{SCALATEST_VERSION}', - f'org.typelevel:kind-projector_{scala_2_version}:' + - KIND_PROJECTOR_VERSION, + f'org.scalatest:scalatest_{scala_3_major}:{SCALATEST_VERSION}', + f'org.specs2:specs2-common_{scala_3_major}:{specs2_version}', + f'org.specs2:specs2-core_{scala_3_major}:{specs2_version}', + f'org.specs2:specs2-fp_{scala_3_major}:{specs2_version}', + f'org.specs2:specs2-junit_{scala_3_major}:{specs2_version}', + f'org.specs2:specs2-matcher_{scala_3_major}:{specs2_version}', + f'org.typelevel:kind-projector_{scala_2_version}:{KIND_PROJECTOR_VERSION}', ] + [f'io.grpc:grpc-{lib}:{GRPC_VERSION}' for lib in GRPC_LIBS] if scala_version == max_scala_2_version or is_scala_3: @@ -123,16 +128,15 @@ def select_root_artifacts(scala_version, scala_major, is_scala_3) -> List[str]: if is_scala_3: root_artifacts.extend([ - f'org.scala-lang:scala3-library_3:{scala_version}', + f'org.jline:jline-reader:{JLINE_VERSION}', + f'org.jline:jline-terminal:{JLINE_VERSION}', + f'org.jline:jline-terminal-jna:{JLINE_VERSION}', f'org.scala-lang:scala3-compiler_3:{scala_version}', + f'org.scala-lang:scala3-library_3:{scala_version}', f'org.scala-lang:scala3-interfaces:{scala_version}', f'org.scala-lang:tasty-core_3:{scala_version}', - 'org.scala-sbt:compiler-interface:' + - SBT_COMPILER_INTERFACE_VERSION, + f'org.scala-sbt:compiler-interface:{SBT_COMPILER_INTERFACE_VERSION}', f'org.scala-sbt:util-interface:{SBT_UTIL_INTERFACE_VERSION}', - f'org.jline:jline-reader:{JLINE_VERSION}', - f'org.jline:jline-terminal:{JLINE_VERSION}', - f'org.jline:jline-terminal-jna:{JLINE_VERSION}', ]) else: diff --git a/specs2/specs2_junit.bzl b/specs2/specs2_junit.bzl index d6fbf53da..359ac2d1e 100644 --- a/specs2/specs2_junit.bzl +++ b/specs2/specs2_junit.bzl @@ -13,6 +13,8 @@ load("//third_party/repositories:repositories.bzl", "repositories") def specs2_junit_artifact_ids(): return [ "io_bazel_rules_scala_org_specs2_specs2_junit", + "org_portable_scala_portable_scala_reflect", + "org_scala_sbt_test_interface", ] def specs2_junit_repositories( diff --git a/src/java/io/bazel/rulesscala/specs2/Specs2RunnerBuilder.scala b/src/java/io/bazel/rulesscala/specs2/Specs2RunnerBuilder.scala index 0acdf8454..52ad0118f 100644 --- a/src/java/io/bazel/rulesscala/specs2/Specs2RunnerBuilder.scala +++ b/src/java/io/bazel/rulesscala/specs2/Specs2RunnerBuilder.scala @@ -179,6 +179,6 @@ class FilteredSpecs2ClassRunner(parentRunner: org.specs2.runner.JUnitRunner, tes private implicit class `Collection Regex Extensions`(coll: List[String]) { def toRegexAlternation: Option[String] = if (coll.isEmpty) None - else Some(coll.map(_.toQuotedRegex).mkString("(", "|", ")")) + else Some(coll.map(_.toQuotedRegex).mkString("^(", "|", ")$")) } } diff --git a/third_party/repositories/scala_2_11.bzl b/third_party/repositories/scala_2_11.bzl index e4c882cc6..0815bfa3e 100644 --- a/third_party/repositories/scala_2_11.bzl +++ b/third_party/repositories/scala_2_11.bzl @@ -130,8 +130,11 @@ artifacts = { "sha256": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b", }, "io_bazel_rules_scala_junit_junit": { - "artifact": "junit:junit:4.12", - "sha256": "59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a", + "artifact": "junit:junit:4.13.1", + "sha256": "c30719db974d6452793fe191b3638a5777005485bae145924044530ffa5f6122", + "deps": [ + "@io_bazel_rules_scala_org_hamcrest_hamcrest_core", + ], }, "io_bazel_rules_scala_mustache": { "artifact": "com.github.spullara.mustache.java:compiler:0.8.18", @@ -166,36 +169,53 @@ artifacts = { "sha256": "0df97574914aee92fd349d0cb4e00f3345d45b2c239e0bb50f0a90ead47888e0", }, "io_bazel_rules_scala_org_specs2_specs2_common": { - "artifact": "org.specs2:specs2-common_2.11:4.4.1", - "sha256": "52d7c0da58725606e98c6e8c81d2efe632053520a25da9140116d04a4abf9d2c", + "artifact": "org.specs2:specs2-common_2.11:4.10.6", + "sha256": "823bdd776b3c4759506b527e0e58d31f9eebbba7b16858ad0fef4b29559dcb5d", "deps": [ "@io_bazel_rules_scala_org_specs2_specs2_fp", + "@io_bazel_rules_scala_scala_library", + "@io_bazel_rules_scala_scala_parser_combinators", + "@io_bazel_rules_scala_scala_reflect", + "@io_bazel_rules_scala_scala_xml", + "@org_portable_scala_portable_scala_reflect", + "@org_scala_sbt_test_interface", ], }, "io_bazel_rules_scala_org_specs2_specs2_core": { - "artifact": "org.specs2:specs2-core_2.11:4.4.1", - "sha256": "8e95cb7e347e7a87e7a80466cbd88419ece1aaacb35c32e8bd7d299a623b31b9", + "artifact": "org.specs2:specs2-core_2.11:4.10.6", + "sha256": "dbd85edf0b399f98a4494c7e5a2ff1868a5c2c6f06e34e56d0e2e2b5b9d9431e", "deps": [ "@io_bazel_rules_scala_org_specs2_specs2_common", "@io_bazel_rules_scala_org_specs2_specs2_matcher", + "@io_bazel_rules_scala_scala_library", + "@org_portable_scala_portable_scala_reflect", + "@org_scala_sbt_test_interface", ], }, "io_bazel_rules_scala_org_specs2_specs2_fp": { - "artifact": "org.specs2:specs2-fp_2.11:4.4.1", - "sha256": "e43006fdd0726ffcd1e04c6c4d795176f5f765cc787cc09baebe1fcb009e4462", + "artifact": "org.specs2:specs2-fp_2.11:4.10.6", + "sha256": "afe794801e0adb93c353bf1c8b4a10b44d5f8ead52449ee17b3284613eac0f5e", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], }, "io_bazel_rules_scala_org_specs2_specs2_junit": { - "artifact": "org.specs2:specs2-junit_2.11:4.4.1", - "sha256": "a8549d52e87896624200fe35ef7b841c1c698a8fb5d97d29bf082762aea9bb72", + "artifact": "org.specs2:specs2-junit_2.11:4.10.6", + "sha256": "b09bb5324b339b022ccf23a669c815084249299e5014097195a0f671d4b89eb3", "deps": [ + "@io_bazel_rules_scala_junit_junit", "@io_bazel_rules_scala_org_specs2_specs2_core", + "@io_bazel_rules_scala_scala_library", + "@org_portable_scala_portable_scala_reflect", + "@org_scala_sbt_test_interface", ], }, "io_bazel_rules_scala_org_specs2_specs2_matcher": { - "artifact": "org.specs2:specs2-matcher_2.11:4.4.1", - "sha256": "448e5ab89d4d650d23030fdbee66a010a07dcac5e4c3e73ef5fe39ca1aace1cd", + "artifact": "org.specs2:specs2-matcher_2.11:4.10.6", + "sha256": "c788968cfef1377bc9025f96a4ff86fc2a44c5fd36762683a6c7d597b14692ef", "deps": [ "@io_bazel_rules_scala_org_specs2_specs2_common", + "@io_bazel_rules_scala_scala_library", ], }, "io_bazel_rules_scala_scala_compiler": { @@ -424,6 +444,14 @@ artifacts = { "artifact": "org.codehaus.mojo:animal-sniffer-annotations:1.24", "sha256": "c720e6e5bcbe6b2f48ded75a47bccdb763eede79d14330102e0d352e3d89ed92", }, + "org_portable_scala_portable_scala_reflect": { + "artifact": "org.portable-scala:portable-scala-reflect_2.11:1.0.0", + "sha256": "c26ed70888b620232b2dbea98449f1169b5074224c63202e82ff3f4e0f6030f4", + "deps": [ + "@io_bazel_rules_scala_scala_library", + "@io_bazel_rules_scala_scala_reflect", + ], + }, "org_scala_lang_modules_scala_collection_compat": { "artifact": "org.scala-lang.modules:scala-collection-compat_2.11:2.1.2", "sha256": "e9667b8b7276aeb42599f536fe4d7caab06eabc55e9995572267ad60c7a11c8b", diff --git a/third_party/repositories/scala_2_12.bzl b/third_party/repositories/scala_2_12.bzl index b25066a41..27fe77709 100644 --- a/third_party/repositories/scala_2_12.bzl +++ b/third_party/repositories/scala_2_12.bzl @@ -140,8 +140,11 @@ artifacts = { "sha256": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b", }, "io_bazel_rules_scala_junit_junit": { - "artifact": "junit:junit:4.12", - "sha256": "59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a", + "artifact": "junit:junit:4.13.2", + "sha256": "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", + "deps": [ + "@io_bazel_rules_scala_org_hamcrest_hamcrest_core", + ], }, "io_bazel_rules_scala_mustache": { "artifact": "com.github.spullara.mustache.java:compiler:0.8.18", @@ -176,36 +179,52 @@ artifacts = { "sha256": "0df97574914aee92fd349d0cb4e00f3345d45b2c239e0bb50f0a90ead47888e0", }, "io_bazel_rules_scala_org_specs2_specs2_common": { - "artifact": "org.specs2:specs2-common_2.12:4.4.1", - "sha256": "7b7d2497bfe10ad552f5ab3780537c7db9961d0ae841098d5ebd91c78d09438a", + "artifact": "org.specs2:specs2-common_2.12:4.20.9", + "sha256": "cb4c1354b70824df6583fbc22f1565679f04e350f026fe3f243b190661afce82", "deps": [ "@io_bazel_rules_scala_org_specs2_specs2_fp", + "@io_bazel_rules_scala_scala_library", + "@io_bazel_rules_scala_scala_parser_combinators", + "@io_bazel_rules_scala_scala_reflect", + "@org_portable_scala_portable_scala_reflect", + "@org_scala_sbt_test_interface", ], }, "io_bazel_rules_scala_org_specs2_specs2_core": { - "artifact": "org.specs2:specs2-core_2.12:4.4.1", - "sha256": "f92c3c83844aac13250acec4eb247a2a26a2b3f04e79ef1bf42c56de4e0bb2e7", + "artifact": "org.specs2:specs2-core_2.12:4.20.9", + "sha256": "1a4c428cb57d501ebd184307e3c9dbdfce44c227e201b21bed9ba4a00cd003ec", "deps": [ - "@io_bazel_rules_scala_org_specs2_specs2_common", "@io_bazel_rules_scala_org_specs2_specs2_matcher", + "@io_bazel_rules_scala_scala_library", + "@org_portable_scala_portable_scala_reflect", + "@org_scala_sbt_test_interface", ], }, "io_bazel_rules_scala_org_specs2_specs2_fp": { - "artifact": "org.specs2:specs2-fp_2.12:4.4.1", - "sha256": "834a145b28dbf57ba6d96f02a3862522e693b5aeec44d4cb2f305ef5617dc73f", + "artifact": "org.specs2:specs2-fp_2.12:4.20.9", + "sha256": "42afcd59093a2f416c62b00c6b95a37ccac09cc55bf4429dbe214c76a4c94db5", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], }, "io_bazel_rules_scala_org_specs2_specs2_junit": { - "artifact": "org.specs2:specs2-junit_2.12:4.4.1", - "sha256": "c867824801da5cccf75354da6d12d406009c435865ecd08a881b799790e9ffec", + "artifact": "org.specs2:specs2-junit_2.12:4.20.9", + "sha256": "148cf6e4bda1f9ad707fea73d9cb4f0b7cbe50d90424b087b029e91c41b6572f", "deps": [ + "@io_bazel_rules_scala_junit_junit", "@io_bazel_rules_scala_org_specs2_specs2_core", + "@io_bazel_rules_scala_scala_library", + "@io_bazel_rules_scala_scala_xml", + "@org_portable_scala_portable_scala_reflect", + "@org_scala_sbt_test_interface", ], }, "io_bazel_rules_scala_org_specs2_specs2_matcher": { - "artifact": "org.specs2:specs2-matcher_2.12:4.4.1", - "sha256": "78c699001c307dcc5dcbec8a80cd9f14e9bdaa047579c3d1010ee4bea66805fe", + "artifact": "org.specs2:specs2-matcher_2.12:4.20.9", + "sha256": "ce3da49a66f1d50b0b622d1d980088869115778f06a582542ed2f508e8d9cd3e", "deps": [ "@io_bazel_rules_scala_org_specs2_specs2_common", + "@io_bazel_rules_scala_scala_library", ], }, "io_bazel_rules_scala_scala_compiler": { @@ -436,6 +455,13 @@ artifacts = { "artifact": "org.codehaus.mojo:animal-sniffer-annotations:1.24", "sha256": "c720e6e5bcbe6b2f48ded75a47bccdb763eede79d14330102e0d352e3d89ed92", }, + "org_portable_scala_portable_scala_reflect": { + "artifact": "org.portable-scala:portable-scala-reflect_2.12:1.1.3", + "sha256": "647cb184209f821296b48acbed4836e9b1dbde2ac2c921a400e9da2576edb497", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "org_scala_lang_modules_scala_collection_compat": { "artifact": "org.scala-lang.modules:scala-collection-compat_2.12:2.12.0", "sha256": "1619c5e4399e1e4793667970aae232652db0549e795c90abf91e44c55ec37cb3", @@ -450,6 +476,10 @@ artifacts = { "@io_bazel_rules_scala_scala_compiler", ], }, + "org_scala_sbt_test_interface": { + "artifact": "org.scala-sbt:test-interface:1.0", + "sha256": "15f70b38bb95f3002fec9aea54030f19bb4ecfbad64c67424b5e5fea09cd749e", + }, "org_scalameta_common": { "artifact": "org.scalameta:common_2.12:4.9.9", "sha256": "8b85032d1fd8cb33c091cf560362b5a9ce5cb507ab38e6968d04f7978d18f600", diff --git a/third_party/repositories/scala_2_13.bzl b/third_party/repositories/scala_2_13.bzl index 891fc654d..d96e9c5a1 100644 --- a/third_party/repositories/scala_2_13.bzl +++ b/third_party/repositories/scala_2_13.bzl @@ -144,8 +144,11 @@ artifacts = { "sha256": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b", }, "io_bazel_rules_scala_junit_junit": { - "artifact": "junit:junit:4.12", - "sha256": "59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a", + "artifact": "junit:junit:4.13.2", + "sha256": "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", + "deps": [ + "@io_bazel_rules_scala_org_hamcrest_hamcrest_core", + ], }, "io_bazel_rules_scala_mustache": { "artifact": "com.github.spullara.mustache.java:compiler:0.8.18", @@ -180,36 +183,52 @@ artifacts = { "sha256": "0df97574914aee92fd349d0cb4e00f3345d45b2c239e0bb50f0a90ead47888e0", }, "io_bazel_rules_scala_org_specs2_specs2_common": { - "artifact": "org.specs2:specs2-common_2.13:4.10.3", - "sha256": "51636fb6a904b3c807de0673f283a971379c9886e03aedbecbf5d787b22346b0", + "artifact": "org.specs2:specs2-common_2.13:4.20.9", + "sha256": "d6ef04e754ed9c86cef53e215815dbe14fa9462d6d242619661d353ed9fb9e1a", "deps": [ "@io_bazel_rules_scala_org_specs2_specs2_fp", + "@io_bazel_rules_scala_scala_library", + "@io_bazel_rules_scala_scala_parser_combinators", + "@io_bazel_rules_scala_scala_reflect", + "@org_portable_scala_portable_scala_reflect", + "@org_scala_sbt_test_interface", ], }, "io_bazel_rules_scala_org_specs2_specs2_core": { - "artifact": "org.specs2:specs2-core_2.13:4.10.3", - "sha256": "9cc55eb11781c9b77689cf8175795fad34b060718b04a225fffb0613a181256b", + "artifact": "org.specs2:specs2-core_2.13:4.20.9", + "sha256": "43a5aa9958c260403914e5127af55f5108ab107854d4a1828d2c92ccfdac8193", "deps": [ - "@io_bazel_rules_scala_org_specs2_specs2_common", "@io_bazel_rules_scala_org_specs2_specs2_matcher", + "@io_bazel_rules_scala_scala_library", + "@org_portable_scala_portable_scala_reflect", + "@org_scala_sbt_test_interface", ], }, "io_bazel_rules_scala_org_specs2_specs2_fp": { - "artifact": "org.specs2:specs2-fp_2.13:4.10.3", - "sha256": "48a908b345c93a3387ddd157ab338686513f450c7dd8afe0f32b6edc7ff15239", + "artifact": "org.specs2:specs2-fp_2.13:4.20.9", + "sha256": "76104d161fc7fe7a685448fb2662eeadc039fff6bf5ed40943f2d03316ed72b7", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], }, "io_bazel_rules_scala_org_specs2_specs2_junit": { - "artifact": "org.specs2:specs2-junit_2.13:4.10.3", - "sha256": "49c4e7cf5483aada90852314983fc046f72092da1a4e7900ace6574444f581ea", + "artifact": "org.specs2:specs2-junit_2.13:4.20.9", + "sha256": "a5476802cfce4c2ff2d09661a7629d0cdfa29af2c41949bc07c40a60f629809e", "deps": [ + "@io_bazel_rules_scala_junit_junit", "@io_bazel_rules_scala_org_specs2_specs2_core", + "@io_bazel_rules_scala_scala_library", + "@io_bazel_rules_scala_scala_xml", + "@org_portable_scala_portable_scala_reflect", + "@org_scala_sbt_test_interface", ], }, "io_bazel_rules_scala_org_specs2_specs2_matcher": { - "artifact": "org.specs2:specs2-matcher_2.13:4.10.3", - "sha256": "754465f58dad8f59b3bb299d5dc127027bf0c0c9ad25250260fc95abd705363b", + "artifact": "org.specs2:specs2-matcher_2.13:4.20.9", + "sha256": "9dd9b7ee0a895c2d3231cf4b820380f419ec6c06afac6c3b31caf1ad3b362dd7", "deps": [ "@io_bazel_rules_scala_org_specs2_specs2_common", + "@io_bazel_rules_scala_scala_library", ], }, "io_bazel_rules_scala_scala_compiler": { @@ -458,6 +477,13 @@ artifacts = { "artifact": "org.jline:jline:3.27.1", "sha256": "72fcbc58da05092067739ded62ed6b1ba9075ecd0fee1caa634c2cbf1a16fe7a", }, + "org_portable_scala_portable_scala_reflect": { + "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.3", + "sha256": "920f62979293069cf721865f931e42f9f7b0b2720ee9f6a9ddff76a19ecf8d4e", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "org_scala_lang_modules_scala_collection_compat": { "artifact": "org.scala-lang.modules:scala-collection-compat_2.13:2.12.0", "sha256": "befff482233cd7f9a7ca1e1f5a36ede421c018e6ce82358978c475d45532755f", @@ -472,6 +498,10 @@ artifacts = { "@io_bazel_rules_scala_scala_compiler", ], }, + "org_scala_sbt_test_interface": { + "artifact": "org.scala-sbt:test-interface:1.0", + "sha256": "15f70b38bb95f3002fec9aea54030f19bb4ecfbad64c67424b5e5fea09cd749e", + }, "org_scalameta_common": { "artifact": "org.scalameta:common_2.13:4.9.9", "sha256": "be66ba789863c65abfc9c1e448339ce915f2bc778daf348d884a967e8eb473ee", diff --git a/third_party/repositories/scala_3_1.bzl b/third_party/repositories/scala_3_1.bzl index e8bda4e22..767b370e5 100644 --- a/third_party/repositories/scala_3_1.bzl +++ b/third_party/repositories/scala_3_1.bzl @@ -148,8 +148,11 @@ artifacts = { "sha256": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b", }, "io_bazel_rules_scala_junit_junit": { - "artifact": "junit:junit:4.12", - "sha256": "59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a", + "artifact": "junit:junit:4.13.2", + "sha256": "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", + "deps": [ + "@io_bazel_rules_scala_org_hamcrest_hamcrest_core", + ], }, "io_bazel_rules_scala_mustache": { "artifact": "com.github.spullara.mustache.java:compiler:0.8.18", @@ -272,8 +275,8 @@ artifacts = { "sha256": "0a183e880575bcc97a2047761880241784734e7ee094dc6fafd6a8f2109ff1da", }, "io_bazel_rules_scala_scala_xml": { - "artifact": "org.scala-lang.modules:scala-xml_3:2.1.0", - "sha256": "48f22343575f4b1d6550eecc42d4b7f0a0d30223c72f015d8d893feab4cbeecd", + "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", + "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -495,6 +498,13 @@ artifacts = { "@org_jline_jline_terminal", ], }, + "org_portable_scala_portable_scala_reflect": { + "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.3", + "sha256": "920f62979293069cf721865f931e42f9f7b0b2720ee9f6a9ddff76a19ecf8d4e", + "deps": [ + "@io_bazel_rules_scala_scala_library_2", + ], + }, "org_scala_lang_modules_scala_collection_compat": { "artifact": "org.scala-lang.modules:scala-collection-compat_2.13:2.12.0", "sha256": "befff482233cd7f9a7ca1e1f5a36ede421c018e6ce82358978c475d45532755f", @@ -516,6 +526,10 @@ artifacts = { "@org_scala_sbt_util_interface", ], }, + "org_scala_sbt_test_interface": { + "artifact": "org.scala-sbt:test-interface:1.0", + "sha256": "15f70b38bb95f3002fec9aea54030f19bb4ecfbad64c67424b5e5fea09cd749e", + }, "org_scala_sbt_util_interface": { "artifact": "org.scala-sbt:util-interface:1.10.5", "sha256": "a2f3eb27a3312acda18dc57ea617d443048e816a9803bde723643254f0b66e5a", diff --git a/third_party/repositories/scala_3_2.bzl b/third_party/repositories/scala_3_2.bzl index 7d16e7242..0429be002 100644 --- a/third_party/repositories/scala_3_2.bzl +++ b/third_party/repositories/scala_3_2.bzl @@ -148,8 +148,11 @@ artifacts = { "sha256": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b", }, "io_bazel_rules_scala_junit_junit": { - "artifact": "junit:junit:4.12", - "sha256": "59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a", + "artifact": "junit:junit:4.13.2", + "sha256": "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", + "deps": [ + "@io_bazel_rules_scala_org_hamcrest_hamcrest_core", + ], }, "io_bazel_rules_scala_mustache": { "artifact": "com.github.spullara.mustache.java:compiler:0.8.18", @@ -272,8 +275,8 @@ artifacts = { "sha256": "df0690721532323a3c533385a06a4f532231d012d38f65bd75864718cfabace4", }, "io_bazel_rules_scala_scala_xml": { - "artifact": "org.scala-lang.modules:scala-xml_3:2.1.0", - "sha256": "48f22343575f4b1d6550eecc42d4b7f0a0d30223c72f015d8d893feab4cbeecd", + "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", + "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -495,6 +498,13 @@ artifacts = { "@org_jline_jline_terminal", ], }, + "org_portable_scala_portable_scala_reflect": { + "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.3", + "sha256": "920f62979293069cf721865f931e42f9f7b0b2720ee9f6a9ddff76a19ecf8d4e", + "deps": [ + "@io_bazel_rules_scala_scala_library_2", + ], + }, "org_scala_lang_modules_scala_collection_compat": { "artifact": "org.scala-lang.modules:scala-collection-compat_2.13:2.12.0", "sha256": "befff482233cd7f9a7ca1e1f5a36ede421c018e6ce82358978c475d45532755f", @@ -516,6 +526,10 @@ artifacts = { "@org_scala_sbt_util_interface", ], }, + "org_scala_sbt_test_interface": { + "artifact": "org.scala-sbt:test-interface:1.0", + "sha256": "15f70b38bb95f3002fec9aea54030f19bb4ecfbad64c67424b5e5fea09cd749e", + }, "org_scala_sbt_util_interface": { "artifact": "org.scala-sbt:util-interface:1.10.5", "sha256": "a2f3eb27a3312acda18dc57ea617d443048e816a9803bde723643254f0b66e5a", diff --git a/third_party/repositories/scala_3_3.bzl b/third_party/repositories/scala_3_3.bzl index 62079504c..424eb1a67 100644 --- a/third_party/repositories/scala_3_3.bzl +++ b/third_party/repositories/scala_3_3.bzl @@ -148,8 +148,11 @@ artifacts = { "sha256": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b", }, "io_bazel_rules_scala_junit_junit": { - "artifact": "junit:junit:4.12", - "sha256": "59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a", + "artifact": "junit:junit:4.13.2", + "sha256": "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", + "deps": [ + "@io_bazel_rules_scala_org_hamcrest_hamcrest_core", + ], }, "io_bazel_rules_scala_mustache": { "artifact": "com.github.spullara.mustache.java:compiler:0.8.18", @@ -273,8 +276,8 @@ artifacts = { "sha256": "13d44693d6f2d38e0595954d11234c0373d6d0e689a8151b06878c5b631d57d5", }, "io_bazel_rules_scala_scala_xml": { - "artifact": "org.scala-lang.modules:scala-xml_3:2.1.0", - "sha256": "48f22343575f4b1d6550eecc42d4b7f0a0d30223c72f015d8d893feab4cbeecd", + "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", + "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -496,6 +499,13 @@ artifacts = { "@org_jline_jline_terminal", ], }, + "org_portable_scala_portable_scala_reflect": { + "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.3", + "sha256": "920f62979293069cf721865f931e42f9f7b0b2720ee9f6a9ddff76a19ecf8d4e", + "deps": [ + "@io_bazel_rules_scala_scala_library_2", + ], + }, "org_scala_lang_modules_scala_collection_compat": { "artifact": "org.scala-lang.modules:scala-collection-compat_2.13:2.12.0", "sha256": "befff482233cd7f9a7ca1e1f5a36ede421c018e6ce82358978c475d45532755f", @@ -517,6 +527,10 @@ artifacts = { "@org_scala_sbt_util_interface", ], }, + "org_scala_sbt_test_interface": { + "artifact": "org.scala-sbt:test-interface:1.0", + "sha256": "15f70b38bb95f3002fec9aea54030f19bb4ecfbad64c67424b5e5fea09cd749e", + }, "org_scala_sbt_util_interface": { "artifact": "org.scala-sbt:util-interface:1.10.5", "sha256": "a2f3eb27a3312acda18dc57ea617d443048e816a9803bde723643254f0b66e5a", diff --git a/third_party/repositories/scala_3_4.bzl b/third_party/repositories/scala_3_4.bzl index 7d7fe65bd..59ad195b3 100644 --- a/third_party/repositories/scala_3_4.bzl +++ b/third_party/repositories/scala_3_4.bzl @@ -148,8 +148,11 @@ artifacts = { "sha256": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b", }, "io_bazel_rules_scala_junit_junit": { - "artifact": "junit:junit:4.12", - "sha256": "59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a", + "artifact": "junit:junit:4.13.2", + "sha256": "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", + "deps": [ + "@io_bazel_rules_scala_org_hamcrest_hamcrest_core", + ], }, "io_bazel_rules_scala_mustache": { "artifact": "com.github.spullara.mustache.java:compiler:0.8.18", @@ -273,8 +276,8 @@ artifacts = { "sha256": "e3b5bdb3bbb3038e290d85e6e4f528c9d7fe1c7b1274695e3140ec6b86a84097", }, "io_bazel_rules_scala_scala_xml": { - "artifact": "org.scala-lang.modules:scala-xml_3:2.1.0", - "sha256": "48f22343575f4b1d6550eecc42d4b7f0a0d30223c72f015d8d893feab4cbeecd", + "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", + "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -496,6 +499,13 @@ artifacts = { "@org_jline_jline_terminal", ], }, + "org_portable_scala_portable_scala_reflect": { + "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.3", + "sha256": "920f62979293069cf721865f931e42f9f7b0b2720ee9f6a9ddff76a19ecf8d4e", + "deps": [ + "@io_bazel_rules_scala_scala_library_2", + ], + }, "org_scala_lang_modules_scala_collection_compat": { "artifact": "org.scala-lang.modules:scala-collection-compat_2.13:2.12.0", "sha256": "befff482233cd7f9a7ca1e1f5a36ede421c018e6ce82358978c475d45532755f", @@ -517,6 +527,10 @@ artifacts = { "@org_scala_sbt_util_interface", ], }, + "org_scala_sbt_test_interface": { + "artifact": "org.scala-sbt:test-interface:1.0", + "sha256": "15f70b38bb95f3002fec9aea54030f19bb4ecfbad64c67424b5e5fea09cd749e", + }, "org_scala_sbt_util_interface": { "artifact": "org.scala-sbt:util-interface:1.10.5", "sha256": "a2f3eb27a3312acda18dc57ea617d443048e816a9803bde723643254f0b66e5a", diff --git a/third_party/repositories/scala_3_5.bzl b/third_party/repositories/scala_3_5.bzl index 061f8c20f..bc584e6a5 100644 --- a/third_party/repositories/scala_3_5.bzl +++ b/third_party/repositories/scala_3_5.bzl @@ -148,8 +148,11 @@ artifacts = { "sha256": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b", }, "io_bazel_rules_scala_junit_junit": { - "artifact": "junit:junit:4.12", - "sha256": "59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a", + "artifact": "junit:junit:4.13.2", + "sha256": "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", + "deps": [ + "@io_bazel_rules_scala_org_hamcrest_hamcrest_core", + ], }, "io_bazel_rules_scala_mustache": { "artifact": "com.github.spullara.mustache.java:compiler:0.8.18", @@ -273,8 +276,8 @@ artifacts = { "sha256": "b380158748e147f4e44654ad16003c89599ddd456eac29f9686cb6d5515067f3", }, "io_bazel_rules_scala_scala_xml": { - "artifact": "org.scala-lang.modules:scala-xml_3:2.1.0", - "sha256": "48f22343575f4b1d6550eecc42d4b7f0a0d30223c72f015d8d893feab4cbeecd", + "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", + "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -496,6 +499,13 @@ artifacts = { "@org_jline_jline_terminal", ], }, + "org_portable_scala_portable_scala_reflect": { + "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.3", + "sha256": "920f62979293069cf721865f931e42f9f7b0b2720ee9f6a9ddff76a19ecf8d4e", + "deps": [ + "@io_bazel_rules_scala_scala_library_2", + ], + }, "org_scala_lang_modules_scala_collection_compat": { "artifact": "org.scala-lang.modules:scala-collection-compat_2.13:2.12.0", "sha256": "befff482233cd7f9a7ca1e1f5a36ede421c018e6ce82358978c475d45532755f", @@ -517,6 +527,10 @@ artifacts = { "@org_scala_sbt_util_interface", ], }, + "org_scala_sbt_test_interface": { + "artifact": "org.scala-sbt:test-interface:1.0", + "sha256": "15f70b38bb95f3002fec9aea54030f19bb4ecfbad64c67424b5e5fea09cd749e", + }, "org_scala_sbt_util_interface": { "artifact": "org.scala-sbt:util-interface:1.10.5", "sha256": "a2f3eb27a3312acda18dc57ea617d443048e816a9803bde723643254f0b66e5a", diff --git a/third_party/repositories/scala_3_6.bzl b/third_party/repositories/scala_3_6.bzl index 6e91ba7ed..79ae1e825 100644 --- a/third_party/repositories/scala_3_6.bzl +++ b/third_party/repositories/scala_3_6.bzl @@ -148,8 +148,11 @@ artifacts = { "sha256": "e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b", }, "io_bazel_rules_scala_junit_junit": { - "artifact": "junit:junit:4.12", - "sha256": "59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a", + "artifact": "junit:junit:4.13.2", + "sha256": "8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3", + "deps": [ + "@io_bazel_rules_scala_org_hamcrest_hamcrest_core", + ], }, "io_bazel_rules_scala_mustache": { "artifact": "com.github.spullara.mustache.java:compiler:0.8.18", @@ -282,8 +285,8 @@ artifacts = { ], }, "io_bazel_rules_scala_scala_xml": { - "artifact": "org.scala-lang.modules:scala-xml_3:2.1.0", - "sha256": "48f22343575f4b1d6550eecc42d4b7f0a0d30223c72f015d8d893feab4cbeecd", + "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", + "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -505,6 +508,13 @@ artifacts = { "@org_jline_jline_terminal", ], }, + "org_portable_scala_portable_scala_reflect": { + "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.3", + "sha256": "920f62979293069cf721865f931e42f9f7b0b2720ee9f6a9ddff76a19ecf8d4e", + "deps": [ + "@io_bazel_rules_scala_scala_library_2", + ], + }, "org_scala_lang_modules_scala_collection_compat": { "artifact": "org.scala-lang.modules:scala-collection-compat_2.13:2.12.0", "sha256": "befff482233cd7f9a7ca1e1f5a36ede421c018e6ce82358978c475d45532755f", @@ -526,6 +536,10 @@ artifacts = { "@org_scala_sbt_util_interface", ], }, + "org_scala_sbt_test_interface": { + "artifact": "org.scala-sbt:test-interface:1.0", + "sha256": "15f70b38bb95f3002fec9aea54030f19bb4ecfbad64c67424b5e5fea09cd749e", + }, "org_scala_sbt_util_interface": { "artifact": "org.scala-sbt:util-interface:1.10.5", "sha256": "a2f3eb27a3312acda18dc57ea617d443048e816a9803bde723643254f0b66e5a", From 42c8958d69e2cc3f0e464d9208571a7c76a8e189 Mon Sep 17 00:00:00 2001 From: Jaden Peterson Date: Mon, 6 Jan 2025 13:59:50 -0500 Subject: [PATCH 3/7] Wrote a TASTy parser --- scala/private/macros/scala_repositories.bzl | 6 + scripts/create_repository.py | 3 + .../bazel/rules_scala/dottyijar/tasty/BUILD | 46 + .../rules_scala/dottyijar/tasty/Tasty.scala | 2767 +++++++++++++++++ .../dottyijar/tasty/Tasty.spec.scala | 124 + .../rules_scala/dottyijar/tasty/format/BUILD | 15 + .../dottyijar/tasty/format/Marker.scala | 11 + .../dottyijar/tasty/format/TastyFormat.scala | 274 ++ .../dottyijar/tasty/format/TastyReader.scala | 180 ++ .../tasty/format/TastyReferencable.scala | 15 + .../tasty/format/TastyReference.scala | 35 + .../dottyijar/tasty/format/TastyWriter.scala | 170 + .../dottyijar/tasty/format/package.scala | 15 + third_party/repositories/scala_2_11.bzl | 4 + third_party/repositories/scala_2_12.bzl | 4 + third_party/repositories/scala_2_13.bzl | 4 + third_party/repositories/scala_3_1.bzl | 30 +- third_party/repositories/scala_3_2.bzl | 30 +- third_party/repositories/scala_3_3.bzl | 26 + third_party/repositories/scala_3_4.bzl | 26 + third_party/repositories/scala_3_5.bzl | 26 + third_party/repositories/scala_3_6.bzl | 26 + 22 files changed, 3833 insertions(+), 4 deletions(-) create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/BUILD create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.spec.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/format/BUILD create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/format/Marker.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyFormat.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReader.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReferencable.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReference.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyWriter.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/format/package.scala diff --git a/scala/private/macros/scala_repositories.bzl b/scala/private/macros/scala_repositories.bzl index 39c19bac8..cd06bb27d 100644 --- a/scala/private/macros/scala_repositories.bzl +++ b/scala/private/macros/scala_repositories.bzl @@ -37,6 +37,7 @@ _COMPILER_SOURCES_ENTRY_TEMPLATE = """ "@io_bazel_rules_scala_config//:scala_version{scala_version_suffix}": "@scala_compiler_source{scala_version_suffix}//:src",""" +_IZUMI_REFLECT_DEPS = ["dev_zio_izumi_reflect_thirdparty_boopickle_shaded"] _JUNIT_DEPS = ["io_bazel_rules_scala_org_hamcrest_hamcrest_core"] _SPECS2_DEPS = [ "org_portable_scala_portable_scala_reflect", @@ -150,6 +151,7 @@ def rules_scala_setup(scala_compiler_srcjar = None): def _artifact_ids(scala_version): result = [ + "commons_io_commons_io", "io_bazel_rules_scala_junit_junit", "io_bazel_rules_scala_org_specs2_specs2_common", "io_bazel_rules_scala_org_specs2_specs2_core", @@ -179,6 +181,8 @@ def _artifact_ids(scala_version): if scala_version.startswith("3."): result.extend([ + "com_softwaremill_common_tagging", + "dev_zio_izumi_reflect", "io_bazel_rules_scala_scala_asm", "io_bazel_rules_scala_scala_compiler_2", "io_bazel_rules_scala_scala_interfaces", @@ -193,6 +197,8 @@ def _artifact_ids(scala_version): "org_scala_sbt_util_interface", ]) + result.extend(_IZUMI_REFLECT_DEPS) + return result def rules_scala_toolchain_deps_repositories( diff --git a/scripts/create_repository.py b/scripts/create_repository.py index 7d4b6e23e..319976de3 100755 --- a/scripts/create_repository.py +++ b/scripts/create_repository.py @@ -107,6 +107,7 @@ def select_root_artifacts(scala_version, scala_major, is_scala_3) -> List[str]: f'com.thesamet.scalapb:protoc-bridge_{scala_2_major}:{protoc_bridge_version}', f'com.thesamet.scalapb:scalapb-runtime-grpc_{scala_2_major}:{scalapb_version}', f'com.thesamet.scalapb:scalapb-runtime_{scala_2_major}:{scalapb_version}', + 'commons-io:commons-io:2.18.0', f'org.scala-lang.modules:scala-parser-combinators_{scala_2_major}:{PARSER_COMBINATORS_VERSION}', f'org.scala-lang:scala-compiler:{scala_2_version}', f'org.scala-lang:scala-library:{scala_2_version}', @@ -128,6 +129,8 @@ def select_root_artifacts(scala_version, scala_major, is_scala_3) -> List[str]: if is_scala_3: root_artifacts.extend([ + 'com.softwaremill.common:tagging_3:2.3.5', + 'dev.zio:izumi-reflect_3:2.3.10', f'org.jline:jline-reader:{JLINE_VERSION}', f'org.jline:jline-terminal:{JLINE_VERSION}', f'org.jline:jline-terminal-jna:{JLINE_VERSION}', diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/BUILD b/src/scala/io/bazel/rules_scala/dottyijar/tasty/BUILD new file mode 100644 index 000000000..3e65d34d5 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/BUILD @@ -0,0 +1,46 @@ +load("//scala:scala.bzl", "scala_library_for_plugin_bootstrapping", "scala_specs2_junit_test") +load("@bazel_skylib//rules:copy_file.bzl", "copy_file") + +scala_library_for_plugin_bootstrapping( + name = "tasty", + srcs = glob( + ["*.scala"], + exclude = ["*.spec.scala"], + ), + scala_version = "3.6.2", + visibility = ["//visibility:public"], + deps = [ + "//src/scala/io/bazel/rules_scala/dottyijar/tasty/format", + "@com_softwaremill_common_tagging_3_6_2", + "@dev_zio_izumi_reflect_3_6_2", + "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded_3_6_2", + "@io_bazel_rules_scala_scala_compiler_3_6_2", + "@io_bazel_rules_scala_scala_tasty_core_3_6_2", + ], +) + +copy_file( + name = "scala3-compiler-jar", + src = "@io_bazel_rules_scala_scala_compiler_3_6_2//:scala3-compiler_3-3.6.2.jar", + out = "scala3-compiler.jar", +) + +scala_specs2_junit_test( + name = "specs", + srcs = glob(["*.spec.scala"]), + jvm_flags = ["-DDEBUG_TASTYFORMAT=true"], + resource_strip_prefix = package_name(), + resources = [":scala3-compiler-jar"], + scala_version = "3.6.2", + suffixes = ["Spec"], + deps = [ + ":tasty", + "//src/scala/io/bazel/rules_scala/dottyijar/tasty/format", + "@commons_io_commons_io_3_6_2", + "@io_bazel_rules_scala_org_specs2_specs2_common_3_6_2", + "@io_bazel_rules_scala_org_specs2_specs2_core_3_6_2", + "@io_bazel_rules_scala_org_specs2_specs2_junit", + "@io_bazel_rules_scala_org_specs2_specs2_matcher_3_6_2", + "@io_bazel_rules_scala_scala_compiler_3_6_2", + ], +) diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.scala new file mode 100644 index 000000000..788f041bc --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.scala @@ -0,0 +1,2767 @@ +package io.bazel.rules_scala.dottyijar.tasty + +import com.softwaremill.tagging.* +import dotty.tools.dotc.util.Spans.Span +import dotty.tools.tasty.TastyFormat as DottyTastyFormat +import io.bazel.rules_scala.dottyijar.tasty.format.* +import java.util.UUID +import scala.annotation.nowarn +import scala.util.control.NonFatal + +case class Tasty( + majorVersion: UnsignedInt, + minorVersion: UnsignedInt, + experimentalVersion: UnsignedInt, + versionString: String, + uuid: UUID, + nameTable: TastyNameTable, + astsSection: Option[TastySection[TastySectionPayload.Asts]], + positionsSection: Option[TastySection[TastySectionPayload.Positions]], + commentsSection: Option[TastySection[TastySectionPayload.Comments]], + attributesSection: Option[TastySection[TastySectionPayload.Attributes]], +) { + def write: Array[Byte] = { + val writer = TastyWriter.empty + + summon[TastyFormat[Tasty]].write(writer, this) + + writer.fillInReferences() + writer.toArray + } +} + +object Tasty { + def apply( + majorVersion: UnsignedInt, + minorVersion: UnsignedInt, + experimentalVersion: UnsignedInt, + versionString: String, + uuid: UUID, + nameTable: TastyNameTable, + sections: List[TastySection[? <: TastySectionPayload]], + ): Tasty = { + val astsSection: Option[TastySection[TastySectionPayload.Asts]] = sections.collectFirst { + case TastySection(name, payload: TastySectionPayload.Asts) => TastySection(name, payload) + } + + val positionsSection: Option[TastySection[TastySectionPayload.Positions]] = sections.collectFirst { + case TastySection(name, payload: TastySectionPayload.Positions) => TastySection(name, payload) + } + + val commentsSection: Option[TastySection[TastySectionPayload.Comments]] = sections.collectFirst { + case TastySection(name, payload: TastySectionPayload.Comments) => TastySection(name, payload) + } + + val attributesSection: Option[TastySection[TastySectionPayload.Attributes]] = sections.collectFirst { + case TastySection(name, payload: TastySectionPayload.Attributes) => TastySection(name, payload) + } + + Tasty( + majorVersion, + minorVersion, + experimentalVersion, + versionString, + uuid, + nameTable, + astsSection, + positionsSection, + commentsSection, + attributesSection, + ) + } + + def read(encoded: Array[Byte]): Tasty = { + val reader = TastyReader(encoded) + val result = summon[TastyFormat[Tasty]].read(reader) + + reader.linkReferences() + + result + } + + given TastyFormat[Tasty] = TastyFormat( + reader => { + reader.readMagicNumber() + + val majorVersion = summon[TastyFormat[UnsignedInt]].read(reader) + val minorVersion = summon[TastyFormat[UnsignedInt]].read(reader) + val experimentalVersion = summon[TastyFormat[UnsignedInt]].read(reader) + val versionString = summon[TastyFormat[String]].read(reader) + val uuid = summon[TastyFormat[UUID]].read(reader) + val nameTable = summon[TastyFormat[TastyNameTable]].read(reader) + val sectionTastyFormat = TastySection.tastyFormat(nameTable) + val sections = reader.readUntilEnd(sectionTastyFormat.read(reader)) + + Tasty(majorVersion, minorVersion, experimentalVersion, versionString, uuid, nameTable, sections) + }, + (writer, tasty) => { + writer.writeMagicNumber() + + summon[TastyFormat[UnsignedInt]].write(writer, tasty.majorVersion) + summon[TastyFormat[UnsignedInt]].write(writer, tasty.minorVersion) + summon[TastyFormat[UnsignedInt]].write(writer, tasty.experimentalVersion) + summon[TastyFormat[String]].write(writer, tasty.versionString) + summon[TastyFormat[UUID]].write(writer, tasty.uuid) + summon[TastyFormat[TastyNameTable]].write(writer, tasty.nameTable) + + val sectionTastyFormat = TastySection.tastyFormat(tasty.nameTable) + + tasty.astsSection.foreach(sectionTastyFormat.write(writer, _)) + tasty.positionsSection.foreach(sectionTastyFormat.write(writer, _)) + tasty.commentsSection.foreach(sectionTastyFormat.write(writer, _)) + tasty.attributesSection.foreach(sectionTastyFormat.write(writer, _)) + }, + ) +} + +case class TastyCaseDefinition( + pattern: TastyTerm, + rightHandSide: TastyTerm, + override var information: TastyReferencableInformation, + guard: Option[TastyTerm], +) extends TastyReferencable + +object TastyCaseDefinition { + private given TastyFormat[(TastyTerm, TastyTerm, TastyReferencableInformation, Option[TastyTerm])] = + TastyFormat.forOptional[(TastyTerm, TastyTerm, TastyReferencableInformation), TastyTerm] + + given TastySumType[TastyCaseDefinition] = + TastySumType.withSingleVariant(DottyTastyFormat.CASEDEF, TastyFormat.forProduct[TastyCaseDefinition]) + + given TastyFormat[TastyCaseDefinition] = TastyFormat.forSumType +} + +sealed trait TastyConstant extends TastyPath with TastyReferencable + +object TastyConstant { + given TastySumType[TastyConstant] = new TastySumType( + TastySumType.Variant[TastyUnitConstant](DottyTastyFormat.UNITconst), + TastySumType.Variant[TastyFalseConstant](DottyTastyFormat.FALSEconst), + TastySumType.Variant[TastyTrueConstant](DottyTastyFormat.TRUEconst), + TastySumType.Variant[TastyByteConstant](DottyTastyFormat.BYTEconst), + TastySumType.Variant[TastyShortConstant](DottyTastyFormat.SHORTconst), + TastySumType.Variant[TastyCharConstant](DottyTastyFormat.CHARconst), + TastySumType.Variant[TastyIntConstant](DottyTastyFormat.INTconst), + TastySumType.Variant[TastyLongConstant](DottyTastyFormat.LONGconst), + TastySumType.Variant[TastyFloatConstant](DottyTastyFormat.FLOATconst), + TastySumType.Variant[TastyDoubleConstant](DottyTastyFormat.DOUBLEconst), + TastySumType.Variant[TastyStringConstant](DottyTastyFormat.STRINGconst), + TastySumType.Variant[TastyNullConstant](DottyTastyFormat.NULLconst), + TastySumType.Variant[TastyClassConstant](DottyTastyFormat.CLASSconst), + ) + + given TastyFormat[TastyConstant] = TastyFormat.forSumType +} + +case class TastyUnitConstant(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyConstant + +object TastyUnitConstant { + given TastyFormat[TastyUnitConstant] = TastyFormat.forProduct +} + +case class TastyFalseConstant(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyConstant + +object TastyFalseConstant { + given TastyFormat[TastyFalseConstant] = TastyFormat.forProduct +} + +case class TastyTrueConstant(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyConstant + +object TastyTrueConstant { + given TastyFormat[TastyTrueConstant] = TastyFormat.forProduct +} + +case class TastyByteConstant( + value: SignedInt, + override var information: TastyReferencableInformation = TastyReferencableInformation(), +) extends TastyConstant + +object TastyByteConstant { + given TastyFormat[TastyByteConstant] = TastyFormat.forProduct +} + +case class TastyShortConstant( + value: SignedInt, + override var information: TastyReferencableInformation = TastyReferencableInformation(), +) extends TastyConstant + +object TastyShortConstant { + given TastyFormat[TastyShortConstant] = TastyFormat.forProduct +} + +case class TastyCharConstant( + value: UnsignedInt, + override var information: TastyReferencableInformation = TastyReferencableInformation(), +) extends TastyConstant + +object TastyCharConstant { + given TastyFormat[TastyCharConstant] = TastyFormat.forProduct +} + +case class TastyIntConstant( + value: SignedInt, + override var information: TastyReferencableInformation = TastyReferencableInformation(), +) extends TastyConstant + +object TastyIntConstant { + given TastyFormat[TastyIntConstant] = TastyFormat.forProduct +} + +case class TastyLongConstant( + value: SignedLong, + override var information: TastyReferencableInformation = TastyReferencableInformation(), +) extends TastyConstant + +object TastyLongConstant { + given TastyFormat[TastyLongConstant] = TastyFormat.forProduct +} + +case class TastyFloatConstant( + value: SignedInt, + override var information: TastyReferencableInformation = TastyReferencableInformation(), +) extends TastyConstant + +object TastyFloatConstant { + given TastyFormat[TastyFloatConstant] = TastyFormat.forProduct +} + +case class TastyDoubleConstant( + value: SignedLong, + override var information: TastyReferencableInformation = TastyReferencableInformation(), +) extends TastyConstant + +object TastyDoubleConstant { + given TastyFormat[TastyDoubleConstant] = TastyFormat.forProduct +} + +case class TastyStringConstant( + value: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), +) extends TastyConstant + +object TastyStringConstant { + given TastyFormat[TastyStringConstant] = TastyFormat.forProduct +} + +case class TastyNullConstant(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyConstant + +object TastyNullConstant { + given TastyFormat[TastyNullConstant] = TastyFormat.forProduct +} + +case class TastyClassConstant( + typeArgument: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), +) extends TastyConstant + +object TastyClassConstant { + given TastyFormat[TastyClassConstant] = TastyFormat.forProduct +} + +case class TastyImplicitArgument( + argument: TastyTerm, + override var information: TastyReferencableInformation = TastyReferencableInformation(), +) extends TastyReferencable + +object TastyImplicitArgument { + given TastySumType[TastyImplicitArgument] = + TastySumType.withSingleVariant(DottyTastyFormat.IMPLICITarg, TastyFormat.forProduct[TastyImplicitArgument]) + + given TastyFormat[TastyImplicitArgument] = TastyFormat.forSumType +} + +sealed trait TastyModifier extends TastyReferencable + +object TastyModifier { + case class Private(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Private { + given TastyFormat[Private] = TastyFormat.forProduct + } + + case class Protected(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Protected { + given TastyFormat[Protected] = TastyFormat.forProduct + } + + case class PrivateQualified( + qualifier: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyModifier + + object PrivateQualified { + given TastyFormat[PrivateQualified] = TastyFormat.forProduct + } + + case class ProtectedQualified( + qualifier: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyModifier + + object ProtectedQualified { + given TastyFormat[ProtectedQualified] = TastyFormat.forProduct + } + + case class Abstract(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Abstract { + given TastyFormat[Abstract] = TastyFormat.forProduct + } + + case class Final(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Final { + given TastyFormat[Final] = TastyFormat.forProduct + } + + case class Sealed(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Sealed { + given TastyFormat[Sealed] = TastyFormat.forProduct + } + + case class Case(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Case { + given TastyFormat[Case] = TastyFormat.forProduct + } + + case class Implicit(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Implicit { + given TastyFormat[Implicit] = TastyFormat.forProduct + } + + case class Given(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Given { + given TastyFormat[Given] = TastyFormat.forProduct + } + + case class Erased(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Erased { + given TastyFormat[Erased] = TastyFormat.forProduct + } + + case class Lazy(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Lazy { + given TastyFormat[Lazy] = TastyFormat.forProduct + } + + case class Override(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Override { + given TastyFormat[Override] = TastyFormat.forProduct + } + + case class Opaque(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Opaque { + given TastyFormat[Opaque] = TastyFormat.forProduct + } + + case class Inline(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Inline { + given TastyFormat[Inline] = TastyFormat.forProduct + } + + case class Macro(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Macro { + given TastyFormat[Macro] = TastyFormat.forProduct + } + + case class InlineProxy(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object InlineProxy { + given TastyFormat[InlineProxy] = TastyFormat.forProduct + } + + case class Static(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Static { + given TastyFormat[Static] = TastyFormat.forProduct + } + + case class Object(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Object { + given TastyFormat[Object] = TastyFormat.forProduct + } + + case class Trait(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Trait { + given TastyFormat[Trait] = TastyFormat.forProduct + } + + case class Enum(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Enum { + given TastyFormat[Enum] = TastyFormat.forProduct + } + + case class Local(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Local { + given TastyFormat[Local] = TastyFormat.forProduct + } + + case class Synthetic(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Synthetic { + given TastyFormat[Synthetic] = TastyFormat.forProduct + } + + case class Artifact(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Artifact { + given TastyFormat[Artifact] = TastyFormat.forProduct + } + + case class Mutable(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Mutable { + given TastyFormat[Mutable] = TastyFormat.forProduct + } + + case class FieldAccessor(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object FieldAccessor { + given TastyFormat[FieldAccessor] = TastyFormat.forProduct + } + + case class CaseAccessor(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object CaseAccessor { + given TastyFormat[CaseAccessor] = TastyFormat.forProduct + } + + case class Covariant(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Covariant { + given TastyFormat[Covariant] = TastyFormat.forProduct + } + + case class Contravariant(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Contravariant { + given TastyFormat[Contravariant] = TastyFormat.forProduct + } + + case class HasDefault(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object HasDefault { + given TastyFormat[HasDefault] = TastyFormat.forProduct + } + + case class Stable(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Stable { + given TastyFormat[Stable] = TastyFormat.forProduct + } + + case class Extension(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Extension { + given TastyFormat[Extension] = TastyFormat.forProduct + } + + case class ParameterSetter(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object ParameterSetter { + given TastyFormat[ParameterSetter] = TastyFormat.forProduct + } + + case class ParameterAlias(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object ParameterAlias { + given TastyFormat[ParameterAlias] = TastyFormat.forProduct + } + + case class Exported(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Exported { + given TastyFormat[Exported] = TastyFormat.forProduct + } + + case class Open(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Open { + given TastyFormat[Open] = TastyFormat.forProduct + } + + case class Invisible(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Invisible { + given TastyFormat[Invisible] = TastyFormat.forProduct + } + + case class Tracked(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Tracked { + given TastyFormat[Tracked] = TastyFormat.forProduct + } + + case class Annotation( + `type`: TastyType, + value: TastyTerm, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyModifier + + object Annotation { + given TastyFormat[Annotation] = TastyFormat.forProduct[Annotation].withLengthPrefixed + } + + /** + * This isn't formally documented in the TASTy grammar as a modifier, but you can find it here: + * [[https://github.com/scala/scala3/blob/4d3f7576ccae724e6f83d2f3d68bd4c4e1dd5a14/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala#L757]] + */ + case class Transparent(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Transparent { + given TastyFormat[Transparent] = TastyFormat.forProduct + } + + /** + * This isn't formally documented in the TASTy grammar as a modifier, but you can find it here: + * [[https://github.com/scala/scala3/blob/4d3f7576ccae724e6f83d2f3d68bd4c4e1dd5a14/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala#L758]] + */ + case class Infix(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyModifier + + object Infix { + given TastyFormat[Infix] = TastyFormat.forProduct + } + + given TastySumType[TastyModifier] = new TastySumType( + TastySumType.Variant[Private](DottyTastyFormat.PRIVATE), + TastySumType.Variant[Protected](DottyTastyFormat.PROTECTED), + TastySumType.Variant[PrivateQualified](DottyTastyFormat.PRIVATEqualified), + TastySumType.Variant[ProtectedQualified](DottyTastyFormat.PROTECTEDqualified), + TastySumType.Variant[Abstract](DottyTastyFormat.ABSTRACT), + TastySumType.Variant[Final](DottyTastyFormat.FINAL), + TastySumType.Variant[Sealed](DottyTastyFormat.SEALED), + TastySumType.Variant[Case](DottyTastyFormat.CASE), + TastySumType.Variant[Implicit](DottyTastyFormat.IMPLICIT), + TastySumType.Variant[Given](DottyTastyFormat.GIVEN), + TastySumType.Variant[Erased](DottyTastyFormat.ERASED), + TastySumType.Variant[Lazy](DottyTastyFormat.LAZY), + TastySumType.Variant[Override](DottyTastyFormat.OVERRIDE), + TastySumType.Variant[Opaque](DottyTastyFormat.OPAQUE), + TastySumType.Variant[Inline](DottyTastyFormat.INLINE), + TastySumType.Variant[Macro](DottyTastyFormat.MACRO), + TastySumType.Variant[InlineProxy](DottyTastyFormat.INLINEPROXY), + TastySumType.Variant[Static](DottyTastyFormat.STATIC), + TastySumType.Variant[Object](DottyTastyFormat.OBJECT), + TastySumType.Variant[Trait](DottyTastyFormat.TRAIT), + TastySumType.Variant[Enum](DottyTastyFormat.ENUM), + TastySumType.Variant[Local](DottyTastyFormat.LOCAL), + TastySumType.Variant[Synthetic](DottyTastyFormat.SYNTHETIC), + TastySumType.Variant[Artifact](DottyTastyFormat.ARTIFACT), + TastySumType.Variant[Mutable](DottyTastyFormat.MUTABLE), + TastySumType.Variant[FieldAccessor](DottyTastyFormat.FIELDaccessor), + TastySumType.Variant[CaseAccessor](DottyTastyFormat.CASEaccessor), + TastySumType.Variant[Covariant](DottyTastyFormat.COVARIANT), + TastySumType.Variant[Contravariant](DottyTastyFormat.CONTRAVARIANT), + TastySumType.Variant[HasDefault](DottyTastyFormat.HASDEFAULT), + TastySumType.Variant[Stable](DottyTastyFormat.STABLE), + TastySumType.Variant[Extension](DottyTastyFormat.EXTENSION), + TastySumType.Variant[ParameterSetter](DottyTastyFormat.PARAMsetter), + TastySumType.Variant[ParameterAlias](DottyTastyFormat.PARAMalias), + TastySumType.Variant[Exported](DottyTastyFormat.EXPORTED), + TastySumType.Variant[Open](DottyTastyFormat.OPEN), + TastySumType.Variant[Invisible](DottyTastyFormat.INVISIBLE), + TastySumType.Variant[Tracked](DottyTastyFormat.TRACKED), + TastySumType.Variant[Annotation](DottyTastyFormat.ANNOTATION), + TastySumType.Variant[Transparent](DottyTastyFormat.TRANSPARENT), + TastySumType.Variant[Infix](DottyTastyFormat.INFIX), + ) + + given TastyFormat[TastyModifier] = TastyFormat.forSumType +} + +sealed trait TastyName extends TastyReferencable + +object TastyName { + case class Simple( + name: String, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyName + + object Simple { + given TastyFormat[Simple] = TastyFormat.forProduct + } + + case class Qualified( + qualified: TastyNameReference, + selector: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyName + + object Qualified { + given TastyFormat[Qualified] = TastyFormat.forProduct[Qualified].withLengthPrefixed + } + + case class Expanded( + qualified: TastyNameReference, + selector: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyName + + object Expanded { + given TastyFormat[Expanded] = TastyFormat.forProduct[Expanded].withLengthPrefixed + } + + case class ExpandPrefix( + qualified: TastyNameReference, + selector: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyName + + object ExpandPrefix { + given TastyFormat[ExpandPrefix] = TastyFormat.forProduct[ExpandPrefix].withLengthPrefixed + } + + case class Unique( + separator: TastyNameReference, + uniqueId: UnsignedInt, + override var information: TastyReferencableInformation, + underlying: Option[TastyNameReference], + ) extends TastyName + + object Unique { + private given TastyFormat[ + (TastyNameReference, UnsignedInt, TastyReferencableInformation, Option[TastyNameReference]), + ] = TastyFormat.forOptional[(TastyNameReference, UnsignedInt, TastyReferencableInformation), TastyNameReference] + + given TastyFormat[Unique] = TastyFormat.forProduct + } + + case class DefaultGetter( + underlying: TastyNameReference, + index: UnsignedInt, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyName + + object DefaultGetter { + given TastyFormat[DefaultGetter] = TastyFormat.forProduct[DefaultGetter].withLengthPrefixed + } + + case class SuperAccessor( + underlying: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyName + + object SuperAccessor { + given TastyFormat[SuperAccessor] = TastyFormat.forProduct[SuperAccessor].withLengthPrefixed + } + + case class InlineAccessor( + underlying: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyName + + object InlineAccessor { + given TastyFormat[InlineAccessor] = TastyFormat.forProduct[InlineAccessor].withLengthPrefixed + } + + case class ObjectClass( + underlying: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyName + + object ObjectClass { + given TastyFormat[ObjectClass] = TastyFormat.forProduct[ObjectClass].withLengthPrefixed + } + + case class BodyRetainer( + underlying: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyName + + object BodyRetainer { + given TastyFormat[BodyRetainer] = TastyFormat.forProduct[BodyRetainer].withLengthPrefixed + } + + case class Signed( + original: TastyNameReference, + resultSignature: TastyNameReference, + override var information: TastyReferencableInformation, + parameterSignatures: List[TastyParameterSignature], + ) extends TastyName + + object Signed { + given TastyFormat[ + (TastyNameReference, TastyNameReference, TastyReferencableInformation, List[TastyParameterSignature]), + ] = TastyFormat.forVariadic[ + (TastyNameReference, TastyNameReference, TastyReferencableInformation), + TastyParameterSignature, + List[TastyParameterSignature], + ] + + given TastyFormat[Signed] = TastyFormat.forProduct + } + + case class TargetSigned( + original: TastyNameReference, + target: TastyNameReference, + resultSignature: TastyNameReference, + override var information: TastyReferencableInformation, + parameterSignatures: List[TastyParameterSignature], + ) extends TastyName + + object TargetSigned { + given TastyFormat[ + ( + TastyNameReference, + TastyNameReference, + TastyNameReference, + TastyReferencableInformation, + List[TastyParameterSignature], + ), + ] = TastyFormat.forVariadic[ + (TastyNameReference, TastyNameReference, TastyNameReference, TastyReferencableInformation), + TastyParameterSignature, + List[TastyParameterSignature], + ] + + given TastyFormat[TargetSigned] = TastyFormat.forProduct + } + + given TastySumType[TastyName] = new TastySumType( + TastySumType.Variant[Simple](DottyTastyFormat.NameTags.UTF8), + TastySumType.Variant[Qualified](DottyTastyFormat.NameTags.QUALIFIED), + TastySumType.Variant[Expanded](DottyTastyFormat.NameTags.EXPANDED), + TastySumType.Variant[ExpandPrefix](DottyTastyFormat.NameTags.EXPANDPREFIX), + TastySumType.Variant[Unique](DottyTastyFormat.NameTags.UNIQUE), + TastySumType.Variant[DefaultGetter](DottyTastyFormat.NameTags.DEFAULTGETTER), + TastySumType.Variant[SuperAccessor](DottyTastyFormat.NameTags.SUPERACCESSOR), + TastySumType.Variant[InlineAccessor](DottyTastyFormat.NameTags.INLINEACCESSOR), + TastySumType.Variant[ObjectClass](DottyTastyFormat.NameTags.OBJECTCLASS), + TastySumType.Variant[BodyRetainer](DottyTastyFormat.NameTags.BODYRETAINER), + TastySumType.Variant[Signed](DottyTastyFormat.NameTags.SIGNED), + TastySumType.Variant[TargetSigned](DottyTastyFormat.NameTags.TARGETSIGNED), + ) + + given TastyFormat[TastyName] = TastyFormat.forSumType +} + +case class TastyNameReference( + i: UnsignedInt, + override var information: TastyReferencableInformation = TastyReferencableInformation(), +) extends TastyReferencable + +object TastyNameReference { + given TastyFormat[TastyNameReference] = TastyFormat.forProduct +} + +sealed trait TastyParameter extends TastySymbol with TastyReferencable + +object TastyParameter { + case class EmptyClause(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyParameter + + object EmptyClause { + given TastyFormat[EmptyClause] = TastyFormat.forProduct + } + + case class SplitClause(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends TastyParameter + + object SplitClause { + given TastyFormat[SplitClause] = TastyFormat.forProduct + } + + given TastySumType[TastyParameter] = summon[TastySumType[TastyTypeParameter]] + .or(summon[TastySumType[TastyTermParameter]]) + .or( + new TastySumType( + TastySumType.Variant[EmptyClause](DottyTastyFormat.EMPTYCLAUSE), + TastySumType.Variant[SplitClause](DottyTastyFormat.SPLITCLAUSE), + ), + ) + + given TastyFormat[TastyParameter] = TastyFormat.forSumType +} + +case class TastyTypeParameter( + name: TastyNameReference, + `type`: TastyTypeTree, + override var information: TastyReferencableInformation, + modifiers: List[TastyModifier], +) extends TastyParameter + +object TastyTypeParameter { + private given TastyFormat[(TastyNameReference, TastyTypeTree, TastyReferencableInformation, List[TastyModifier])] = + TastyFormat.forVariadic[ + (TastyNameReference, TastyTypeTree, TastyReferencableInformation), + TastyModifier, + List[TastyModifier], + ] + + given TastySumType[TastyTypeParameter] = + TastySumType.withSingleVariant(DottyTastyFormat.TYPEPARAM, TastyFormat.forProduct[TastyTypeParameter]) + + given TastyFormat[TastyTypeParameter] = TastyFormat.forSumType +} + +case class TastyTermParameter( + name: TastyNameReference, + `type`: TastyTypeTree, + override var information: TastyReferencableInformation, + modifiers: List[TastyModifier], +) extends TastyParameter + +object TastyTermParameter { + private given TastyFormat[(TastyNameReference, TastyTypeTree, TastyReferencableInformation, List[TastyModifier])] = + TastyFormat.forVariadic[ + (TastyNameReference, TastyTypeTree, TastyReferencableInformation), + TastyModifier, + List[TastyModifier], + ] + + given TastySumType[TastyTermParameter] = + TastySumType.withSingleVariant(DottyTastyFormat.PARAM, TastyFormat.forProduct[TastyTermParameter]) + + given TastyFormat[TastyTermParameter] = TastyFormat.forSumType +} + +sealed trait TastyParameterSignature extends TastyReferencable + +object TastyParameterSignature { + case class TypeParameterSectionLength( + length: Int, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyParameterSignature + + case class TermParameter( + name: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyParameterSignature + + given TastyFormat[TastyParameterSignature] = TastyFormat( + reader => { + val value = reader.readSignedInt() + + if (value < 0) { + TypeParameterSectionLength(-value) + } else { + TermParameter(TastyNameReference(value.taggedWith[Unsigned])) + } + }, + (writer, value) => + value match { + case TypeParameterSectionLength(length, _) => writer.writeSignedInt((-length).taggedWith[Signed]) + case TermParameter(name, _) => writer.writeSignedInt(name.i.taggedWith[Signed]) + }, + ) +} + +sealed trait TastyPath extends TastyTerm with TastyType with TastyReferencable + +object TastyPath { + case class LocalReference( + reference: TastyAstReference[TastySymbol], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyPath + + object LocalReference { + given TastyFormat[LocalReference] = TastyFormat.forProduct + } + + case class PrefixedLocalReference( + reference: TastyAstReference[TastySymbol], + qualified: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyPath + + object PrefixedLocalReference { + given TastyFormat[PrefixedLocalReference] = TastyFormat.forProduct + } + + case class PackageReference( + fullyQualifiedName: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyPath + + object PackageReference { + given TastyFormat[PackageReference] = TastyFormat.forProduct + } + + case class NonLocalReference( + possiblySignedName: TastyNameReference, + qualified: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyPath + + object NonLocalReference { + given TastyFormat[NonLocalReference] = TastyFormat.forProduct + } + + case class NonLocalReferenceIn( + possiblySignedName: TastyNameReference, + qualified: TastyType, + owner: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyPath + + object NonLocalReferenceIn { + given TastyFormat[NonLocalReferenceIn] = TastyFormat.forProduct + } + + case class This( + classReference: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyPath + + object This { + given TastyFormat[This] = TastyFormat.forProduct + } + + case class RecursivelyRefinedThis( + recursivelyRefinedType: TastyAstReference[TastyType], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyPath + + object RecursivelyRefinedThis { + given TastyFormat[RecursivelyRefinedThis] = TastyFormat.forProduct + } + + case class Shared( + path: TastyAstReference[TastyPath], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyPath + + object Shared { + given TastyFormat[Shared] = TastyFormat.forProduct + } + + given TastySumType[TastyPath] = summon[TastySumType[TastyConstant]].or( + new TastySumType( + TastySumType.Variant[LocalReference](DottyTastyFormat.TERMREFdirect), + TastySumType.Variant[PrefixedLocalReference](DottyTastyFormat.TERMREFsymbol), + TastySumType.Variant[PackageReference](DottyTastyFormat.TERMREFpkg), + TastySumType.Variant[NonLocalReference](DottyTastyFormat.TERMREF), + TastySumType.Variant[NonLocalReferenceIn](DottyTastyFormat.TERMREFin), + TastySumType.Variant[This](DottyTastyFormat.THIS), + TastySumType.Variant[RecursivelyRefinedThis](DottyTastyFormat.RECthis), + TastySumType.Variant[Shared](DottyTastyFormat.SHAREDtype), + ), + ) + + given TastyFormat[TastyPath] = TastyFormat.forSumType +} + +case class TastyNameTable(names: Vector[TastyName]) { + override def toString: String = + s"""TastyNameTable( + |${names.zipWithIndex.map { case (name, i) => s" $i: $name" }.mkString("\n")} + |)""".stripMargin + + def apply(reference: TastyNameReference): TastyName = names(reference.i) +} + +object TastyNameTable { + given TastyFormat[TastyNameTable] = + TastyFormat.forIterableWithLengthPrefixed[TastyName, Vector[TastyName]].bimap(TastyNameTable(_), _.names) +} + +case class TastySection[A <: TastySectionPayload](name: TastyNameReference, payload: A) + +object TastySection { + def tastyFormat(nameTable: TastyNameTable): TastyFormat[TastySection[? <: TastySectionPayload]] = TastyFormat( + reader => { + val nameReference = summon[TastyFormat[TastyNameReference]].read(reader) + val name = nameTable(nameReference) + val payload = reader.readWithLength(reader.readUnsignedInt()) { reader => + name match { + case TastyName.Simple("ASTs", _) => summon[TastyFormat[TastySectionPayload.Asts]].read(reader) + case TastyName.Simple("Positions", _) => summon[TastyFormat[TastySectionPayload.Positions]].read(reader) + case TastyName.Simple("Comments", _) => summon[TastyFormat[TastySectionPayload.Comments]].read(reader) + case TastyName.Simple("Attributes", _) => summon[TastyFormat[TastySectionPayload.Attributes]].read(reader) + case TastyName.Simple(name, _) => throw new Exception(s"Unrecognized section name: $name") + case other => + throw new Exception(s"Expected a simple string when following a section name reference, but got $other") + } + } + + TastySection(nameReference, payload) + }, + (writer, section) => { + summon[TastyFormat[TastyNameReference]].write(writer, section.name) + + writer.writeWithLengthPrefixed { writer => + section.payload match { + case asts: TastySectionPayload.Asts => + summon[TastyFormat[TastySectionPayload.Asts]].write(writer, asts) + + case positions: TastySectionPayload.Positions => + summon[TastyFormat[TastySectionPayload.Positions]].write(writer, positions) + + case comments: TastySectionPayload.Comments => + summon[TastyFormat[TastySectionPayload.Comments]].write(writer, comments) + + case attributes: TastySectionPayload.Attributes => + summon[TastyFormat[TastySectionPayload.Attributes]].write(writer, attributes) + } + } + }, + ) +} + +sealed trait TastySectionPayload + +object TastySectionPayload { + case class Asts(topLevelStatements: List[TastyTopLevelStatement]) extends TastySectionPayload + + object Asts { + given TastyFormat[Asts] = TastyFormat + .forIterableWithoutLengthPrefixed[TastyTopLevelStatement, List[TastyTopLevelStatement]] + .bimap(Asts(_), _.topLevelStatements) + .marked(MarkerType.AstSection) + } + + case class Positions(lineSizes: Positions.LineSizes, deltas: List[Positions.Delta | Positions.Source]) + extends TastySectionPayload + + object Positions { + case class LineSizes(sizes: List[UnsignedInt]) + + object LineSizes { + given TastyFormat[LineSizes] = TastyFormat( + reader => { + val length = reader.readUnsignedInt() + + LineSizes(Range(0, length).map(_ => reader.readUnsignedInt()).toList) + }, + (writer, lineSizes) => { + writer.writeUnsignedInt(lineSizes.sizes.length.taggedWith[Unsigned]) + + lineSizes.sizes.foreach(writer.writeUnsignedInt) + }, + ) + } + + case class Delta(addressDelta: Int, start: Option[SignedInt], end: Option[SignedInt], point: Option[SignedInt]) + + object Delta { + given TastyFormat[Delta] = TastyFormat( + reader => { + val header = reader.readUnsignedInt() + val addressDelta = header >> 3 + val start = Option.when(((header >>> 2) & 0x1) == 1)(reader.readSignedInt()) + val end = Option.when(((header >>> 1) & 0x1) == 1)(reader.readSignedInt()) + val point = Option.when((header & 0x1) == 1)(reader.readSignedInt()) + + Delta(addressDelta, start, end, point) + }, + (writer, delta) => { + val header = ( + (delta.addressDelta << 3) | + ((if (delta.start.isDefined) 1 else 0) << 2) | + ((if (delta.end.isDefined) 1 else 0) << 1) | + (if (delta.point.isDefined) 1 else 0) + ).taggedWith[Unsigned] + + writer.writeUnsignedInt(header) + + delta.start.foreach(writer.writeSignedInt) + delta.end.foreach(writer.writeSignedInt) + delta.point.foreach(writer.writeSignedInt) + }, + ) + } + + case class Source(path: TastyNameReference) + + object Source { + given TastyFormat[Source] = TastyFormat.forProduct + } + + private given TastyFormat[Delta | Source] = TastyFormat( + reader => + if (reader.peek(_.readUnsignedInt()) == DottyTastyFormat.SOURCE) { + reader.readUnsignedInt() + + summon[TastyFormat[Source]].read(reader) + } else { + summon[TastyFormat[Delta]].read(reader) + }, + (writer, value) => + value match { + case delta: Delta => summon[TastyFormat[Delta]].write(writer, delta) + case source: Source => + writer.writeUnsignedInt(DottyTastyFormat.SOURCE.taggedWith[Unsigned]) + + summon[TastyFormat[Source]].write(writer, source) + }, + ) + + private given TastyFormat[List[Delta | Source]] = TastyFormat.forIterableWithoutLengthPrefixed + + given TastyFormat[Positions] = TastyFormat.forProduct + } + + /** + * The TASTy grammar doesn't mention this, but each comment is prefixed with the address of the symbol to which it + * belongs: + * [[https://github.com/scala/scala3/blob/4d3f7576ccae724e6f83d2f3d68bd4c4e1dd5a14/compiler/src/dotty/tools/dotc/core/tasty/CommentUnpickler.scala#L21]] + */ + case class Comments(comments: List[(TastyAstReference[TastySymbol], Comments.Comment)]) extends TastySectionPayload + + object Comments { + case class Comment(content: String, coordinates: Span) + + object Comment { + given TastyFormat[Comment] = TastyFormat.forProduct + } + + given TastyFormat[Comments] = TastyFormat + .forIterableWithoutLengthPrefixed[ + (TastyAstReference[TastySymbol], Comment), + List[(TastyAstReference[TastySymbol], Comment)], + ] + .bimap(Comments(_), _.comments) + } + + case class Attributes(attributes: List[Attributes.Attribute]) extends TastySectionPayload + + object Attributes { + sealed trait Attribute extends TastyReferencable + + object Attribute { + case class Scala2StandardLibrary( + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends Attribute + + object Scala2StandardLibrary { + given TastyFormat[Scala2StandardLibrary] = TastyFormat.forProduct + } + + case class ExplicitNulls(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends Attribute + + object ExplicitNulls { + given TastyFormat[ExplicitNulls] = TastyFormat.forProduct + } + + case class CaptureChecked(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends Attribute + + object CaptureChecked { + given TastyFormat[CaptureChecked] = TastyFormat.forProduct + } + + case class WithPureFunctions( + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends Attribute + + object WithPureFunctions { + given TastyFormat[WithPureFunctions] = TastyFormat.forProduct + } + + case class Java(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends Attribute + + object Java { + given TastyFormat[Java] = TastyFormat.forProduct + } + + case class Outline(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends Attribute + + object Outline { + given TastyFormat[Outline] = TastyFormat.forProduct + } + + case class SourceFile( + path: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends Attribute + + object SourceFile { + given TastyFormat[SourceFile] = TastyFormat.forProduct + } + + given TastySumType[Attribute] = new TastySumType( + TastySumType.Variant[Scala2StandardLibrary](DottyTastyFormat.SCALA2STANDARDLIBRARYattr), + TastySumType.Variant[ExplicitNulls](DottyTastyFormat.EXPLICITNULLSattr), + TastySumType.Variant[CaptureChecked](DottyTastyFormat.CAPTURECHECKEDattr), + TastySumType.Variant[WithPureFunctions](DottyTastyFormat.WITHPUREFUNSattr), + TastySumType.Variant[Java](DottyTastyFormat.JAVAattr), + TastySumType.Variant[Outline](DottyTastyFormat.OUTLINEattr), + TastySumType.Variant[SourceFile](DottyTastyFormat.SOURCEFILEattr), + ) + + given TastyFormat[Attribute] = TastyFormat.forSumType + } + + given TastyFormat[Attributes] = + TastyFormat.forIterableWithoutLengthPrefixed[Attribute, List[Attribute]].bimap(Attributes(_), _.attributes) + } +} + +sealed trait TastySelector extends TastyReferencable + +object TastySelector { + case class Imported( + name: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastySelector + + object Imported { + given TastyFormat[Imported] = TastyFormat.forProduct + } + + case class Renamed( + to: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastySelector + + object Renamed { + given TastyFormat[Renamed] = TastyFormat.forProduct + } + + case class Bounded( + `type`: TastyTypeTree, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastySelector + + object Bounded { + given TastyFormat[Bounded] = TastyFormat.forProduct + } + + given TastySumType[TastySelector] = new TastySumType( + TastySumType.Variant[Imported](DottyTastyFormat.IMPORTED), + TastySumType.Variant[Renamed](DottyTastyFormat.RENAMED), + TastySumType.Variant[Bounded](DottyTastyFormat.BOUNDED), + ) + + given TastyFormat[TastySelector] = TastyFormat.forSumType +} + +case class TastySelf( + name: TastyNameReference, + `type`: TastyTypeTree, + override var information: TastyReferencableInformation = TastyReferencableInformation(), +) extends TastyReferencable + +object TastySelf { + given TastySumType[TastySelf] = + TastySumType.withSingleVariant(DottyTastyFormat.SELFDEF, TastyFormat.forProduct[TastySelf]) + + given TastyFormat[TastySelf] = TastyFormat.forSumType +} + +sealed trait TastyStatement extends TastyTopLevelStatement with TastyReferencable + +object TastyStatement { + given TastySumType[TastyStatement] = summon[TastySumType[TastyTerm]] + .or(summon[TastySumType[TastyValOrDefDefinition]]) + .or(summon[TastySumType[TastyTypeDefinition]]) + .or( + new TastySumType( + TastySumType.Variant[TastyImportStatement](DottyTastyFormat.IMPORT), + TastySumType.Variant[TastyExportStatement](DottyTastyFormat.EXPORT), + ), + ) + + given TastyFormat[TastyStatement] = TastyFormat.forSumType +} + +case class TastyImportStatement( + qualifier: TastyTerm, + override var information: TastyReferencableInformation, + selectors: List[TastySelector], +) extends TastyStatement + with TastyReferencable + +object TastyImportStatement { + private given TastyFormat[(TastyTerm, TastyReferencableInformation, List[TastySelector])] = + TastyFormat.forVariadic[(TastyTerm, TastyReferencableInformation), TastySelector, List[TastySelector]] + + given TastyFormat[TastyImportStatement] = TastyFormat.forProduct +} + +case class TastyExportStatement( + qualifier: TastyTerm, + override var information: TastyReferencableInformation, + selectors: List[TastySelector], +) extends TastyStatement + with TastyReferencable + +object TastyExportStatement { + private given TastyFormat[(TastyTerm, TastyReferencableInformation, List[TastySelector])] = + TastyFormat.forVariadic[(TastyTerm, TastyReferencableInformation), TastySelector, List[TastySelector]] + + given TastyFormat[TastyExportStatement] = TastyFormat.forProduct +} + +/** + * A symbol is ill-defined in the TASTy grammar, but according to Dotty's TASTy unpickler, it can be a definition + * (`val`, `def`, or `type`), type parameter, parameter, bind pattern, or template: + * [[https://github.com/scala/scala3/blob/4d3f7576ccae724e6f83d2f3d68bd4c4e1dd5a14/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala#L592]] + */ +sealed trait TastySymbol extends TastyReferencable + +object TastySymbol { + given TastySumType[TastySymbol] = summon[TastySumType[TastyValOrDefDefinition]] + .or(summon[TastySumType[TastyTypeDefinition]]) + .or(summon[TastySumType[TastyParameter]]) + .or(summon[TastySumType[TastyTerm.Pattern.Bind]]) + .or(summon[TastySumType[TastyTemplate]]) + /** + * The compiler is incapable of proving that the union of every subclass of [[TastySymbol]] is equivalent to + * [[TastySymbol]] itself. + */ + .or(new TastySumType()) + + given TastyFormat[TastySymbol] = TastyFormat.forSumType +} + +case class TastyTemplate( + typeParameters: List[TastyTypeParameter], + parameters: List[TastyTermParameter], + parents: List[TastyTerm | TastyTypeTree], + self: Option[TastySelf], + statements: List[TastyStatement], + override var information: TastyReferencableInformation = TastyReferencableInformation(), +) extends TastySymbol + +object TastyTemplate { + private val underlyingTastyFormat = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val typeParameters = reader.readWhile( + !reader.isAtEnd && summon[TastySumType[TastyTypeParameter]].peekIsVariant(reader), + )(summon[TastyFormat[TastyTypeParameter]].read(reader)) + + val parameters = reader.readWhile( + !reader.isAtEnd && summon[TastySumType[TastyTermParameter]].peekIsVariant(reader), + )(summon[TastyFormat[TastyTermParameter]].read(reader)) + + val parents = reader.readWhile( + !reader.isAtEnd && summon[TastySumType[TastyTerm | TastyTypeTree]].peekIsVariant(reader), + )(summon[TastyFormat[TastyTerm | TastyTypeTree]].read(reader)) + + val self = Option.when(!reader.isAtEnd && summon[TastySumType[TastySelf]].peekIsVariant(reader))( + summon[TastyFormat[TastySelf]].read(reader), + ) + + // Why does the specification allow for a `SPLITCLAUSE` here? + if (reader.peek(_.readByte()) == DottyTastyFormat.SPLITCLAUSE.toByte) { + reader.readByte() + } + + val statements = reader.readUntilEnd(summon[TastyFormat[TastyStatement]].read(reader)) + + TastyTemplate(typeParameters, parameters, parents, self, statements) + }, + (writer, template) => + writer.writeWithLengthPrefixed { writer => + template.typeParameters.foreach(summon[TastyFormat[TastyTypeParameter]].write(writer, _)) + template.parameters.foreach(summon[TastyFormat[TastyTermParameter]].write(writer, _)) + template.parents.foreach(summon[TastyFormat[TastyTerm | TastyTypeTree]].write(writer, _)) + template.self.foreach(summon[TastyFormat[TastySelf]].write(writer, _)) + template.statements.foreach(summon[TastyFormat[TastyStatement]].write(writer, _)) + }, + ) + + private given TastySumType[TastyTerm | TastyTypeTree] = + summon[TastySumType[TastyTerm]].or(summon[TastySumType[TastyTypeTree]]) + + private given TastyFormat[TastyTerm | TastyTypeTree] = TastyFormat.forSumType + + given TastySumType[TastyTemplate] = + TastySumType.withSingleVariant(DottyTastyFormat.TEMPLATE, underlyingTastyFormat) + + given TastyFormat[TastyTemplate] = TastyFormat.forSumType +} + +sealed trait TastyTerm extends TastyStatement with TastyReferencable + +object TastyTerm { + case class Identifier( + name: TastyNameReference, + `type`: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object Identifier { + given TastyFormat[Identifier] = TastyFormat.forProduct + } + + case class Select( + possiblySignedName: TastyNameReference, + qualified: TastyTerm, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object Select { + given TastyFormat[Select] = TastyFormat.forProduct + } + + case class SelectIn( + possiblySignedName: TastyNameReference, + qualified: TastyTerm, + owner: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object SelectIn { + given TastyFormat[SelectIn] = TastyFormat.forProduct[SelectIn].withLengthPrefixed + } + + case class QualifiedThis( + qualifier: TastyTypeTree, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object QualifiedThis { + given TastyFormat[QualifiedThis] = TastyFormat.forProduct + } + + case class New( + classType: TastyTypeTree, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object New { + given TastyFormat[New] = TastyFormat.forProduct + } + + case class Elided( + expressionType: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object Elided { + given TastyFormat[Elided] = TastyFormat.forProduct + } + + case class Throw( + throwable: TastyTerm, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object Throw { + given TastyFormat[Throw] = TastyFormat.forProduct + } + + case class NamedArgument( + parameterName: TastyNameReference, + argument: TastyTerm, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object NamedArgument { + given TastyFormat[NamedArgument] = TastyFormat.forProduct + } + + case class Apply( + function: TastyTerm, + override var information: TastyReferencableInformation, + arguments: List[TastyTerm], + ) extends TastyTerm + + object Apply { + private given TastyFormat[(TastyTerm, TastyReferencableInformation, List[TastyTerm])] = + TastyFormat.forVariadic[(TastyTerm, TastyReferencableInformation), TastyTerm, List[TastyTerm]] + + given TastyFormat[Apply] = TastyFormat.forProduct + } + + case class ApplySignaturePolymorphic( + function: TastyTerm, + methodType: TastyType, + override var information: TastyReferencableInformation, + arguments: List[TastyTerm], + ) extends TastyTerm + + object ApplySignaturePolymorphic { + private given TastyFormat[(TastyTerm, TastyType, TastyReferencableInformation, List[TastyTerm])] = + TastyFormat.forVariadic[(TastyTerm, TastyType, TastyReferencableInformation), TastyTerm, List[TastyTerm]] + + given TastyFormat[ApplySignaturePolymorphic] = TastyFormat.forProduct + } + + case class TypeApply( + function: TastyTerm, + override var information: TastyReferencableInformation, + typeArguments: List[TastyTypeTree], + ) extends TastyTerm + + object TypeApply { + private given TastyFormat[(TastyTerm, TastyReferencableInformation, List[TastyTypeTree])] = + TastyFormat.forVariadic[(TastyTerm, TastyReferencableInformation), TastyTypeTree, List[TastyTypeTree]] + + given TastyFormat[TypeApply] = TastyFormat.forProduct + } + + case class Super( + `this`: TastyTerm, + override var information: TastyReferencableInformation, + typeArgument: Option[TastyTypeTree], + ) extends TastyTerm + + object Super { + private given TastyFormat[(TastyTerm, TastyReferencableInformation, Option[TastyTypeTree])] = + TastyFormat.forOptional[(TastyTerm, TastyReferencableInformation), TastyTypeTree] + + given TastyFormat[Super] = TastyFormat.forProduct + } + + case class TypeAscribed( + expression: TastyTerm, + ascribedType: TastyTypeTree, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object TypeAscribed { + given TastyFormat[TypeAscribed] = TastyFormat.forProduct[TypeAscribed].withLengthPrefixed + } + + case class Assignment( + leftHandSide: TastyTerm, + rightHandSide: TastyTerm, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object Assignment { + given TastyFormat[Assignment] = TastyFormat.forProduct[Assignment].withLengthPrefixed + } + + case class Block( + expression: TastyTerm, + override var information: TastyReferencableInformation, + statements: List[TastyStatement], + ) extends TastyTerm + + object Block { + private given TastyFormat[(TastyTerm, TastyReferencableInformation, List[TastyStatement])] = + TastyFormat.forVariadic[(TastyTerm, TastyReferencableInformation), TastyStatement, List[TastyStatement]] + + given TastyFormat[Block] = TastyFormat.forProduct + } + + case class Inlined( + expression: TastyTerm, + call: Option[TastyTypeTree], + definitions: List[TastyValOrDefDefinition], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object Inlined { + given TastyFormat[Inlined] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val expression = summon[TastyFormat[TastyTerm]].read(reader) + val call = Option.when(!reader.isAtEnd && summon[TastySumType[TastyTypeTree]].peekIsVariant(reader))( + summon[TastyFormat[TastyTypeTree]].read(reader), + ) + + val definitions = reader.readUntilEnd(summon[TastyFormat[TastyValOrDefDefinition]].read(reader)) + + Inlined(expression, call, definitions) + }, + (writer, inlined) => + writer.writeWithLengthPrefixed { writer => + summon[TastyFormat[TastyTerm]].write(writer, inlined.expression) + + inlined.call.foreach(summon[TastyFormat[TastyTypeTree]].write(writer, _)) + inlined.definitions.foreach(summon[TastyFormat[TastyValOrDefDefinition]].write(writer, _)) + }, + ) + } + + case class Lambda( + method: TastyTerm, + override var information: TastyReferencableInformation, + targetType: Option[TastyTypeTree], + ) extends TastyTerm + + object Lambda { + private given TastyFormat[(TastyTerm, TastyReferencableInformation, Option[TastyTypeTree])] = + TastyFormat.forOptional[(TastyTerm, TastyReferencableInformation), TastyTypeTree] + + given TastyFormat[Lambda] = TastyFormat.forProduct + } + + case class If( + inlined: Boolean, + condition: TastyTerm, + thenValue: TastyTerm, + elseValue: TastyTerm, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object If { + given TastyFormat[If] = TastyFormat( + reader => { + reader.readUnsignedInt() + + val inlined = reader.peek(_.readByte()).toInt == DottyTastyFormat.INLINE.toByte + + if (inlined) { + reader.readByte() + } + + val condition = summon[TastyFormat[TastyTerm]].read(reader) + val thenValue = summon[TastyFormat[TastyTerm]].read(reader) + val elseValue = summon[TastyFormat[TastyTerm]].read(reader) + + If(inlined, condition, thenValue, elseValue) + }, + (writer, value) => + writer.writeWithLengthPrefixed { writer => + if (value.inlined) { + writer.writeByte(DottyTastyFormat.INLINE.toByte) + } + + summon[TastyFormat[TastyTerm]].write(writer, value.condition) + summon[TastyFormat[TastyTerm]].write(writer, value.thenValue) + summon[TastyFormat[TastyTerm]].write(writer, value.elseValue) + }, + ) + } + + case class Match( + inline: Boolean, + scrutinee: Option[TastyTerm], + cases: List[TastyCaseDefinition], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object Match { + given TastyFormat[Match] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val implicitTag = DottyTastyFormat.IMPLICIT.toByte + val inlineTag = DottyTastyFormat.INLINE.toByte + val (inline, scrutinee) = reader.peek(_.readByte()) match { + case `implicitTag` => + reader.readByte() + + (true, None) + + case `inlineTag` => + reader.readByte() + + (true, Some(summon[TastyFormat[TastyTerm]].read(reader))) + + case _ => (false, Some(summon[TastyFormat[TastyTerm]].read(reader))) + } + + val cases = reader.readUntilEnd(summon[TastyFormat[TastyCaseDefinition]].read(reader)) + + Match(inline, scrutinee, cases) + }, + (writer, value) => + writer.writeWithLengthPrefixed { writer => + (value.inline, value.scrutinee) match { + case (true, None) => writer.writeByte(DottyTastyFormat.IMPLICIT.toByte) + case (true, Some(_)) => writer.writeByte(DottyTastyFormat.INLINE.toByte) + case _ => + } + + value.scrutinee.foreach(summon[TastyFormat[TastyTerm]].write(writer, _)) + value.cases.foreach(summon[TastyFormat[TastyCaseDefinition]].write(writer, _)) + }, + ) + } + + object Pattern { + + /** + * [[Bind]] refers to a bound pattern in an ordinary `match` expression or a type `match` (referred to here as an + * "ordinary bind" and "type bind", respectively). The TASTy grammar differentiates ordinary binds and type binds, + * but we've merged them into a single class for a couple of reasons: + * - It's possible for the pattern of a type bind to be a [[TastyTerm.Identifier]], when it should be a + * [[TastyTypeTree.Identifier]]: + * [[https://github.com/scalacenter/tasty-query/blob/275ea2d125b16ac74d47d56babfa91bea144f2ad/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala#L1645]] + * Expressing this in the definition of a type [[Bind]] bind class would complicate it. + * - Both ordinary binds and type binds can be [[TastySymbol]]s. Because they begin with the same tag + * ([[DottyTastyFormat.BIND]]), differentiating them would be extremely difficult and messy. It's much simpler + * to combine them into a single class. + * + * @param modifiers + * The TASTy grammar doesn't mention this, but [[Bind]] can have modifiers: + * [[https://github.com/scala/scala3/blob/4d3f7576ccae724e6f83d2f3d68bd4c4e1dd5a14/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala#L1591]] + */ + case class Bind( + boundName: TastyNameReference, + patternType: TastyType, + pattern: TastyTerm | TastyTypeTree, + override var information: TastyReferencableInformation, + modifiers: List[TastyModifier], + ) extends TastyTerm + with TastyType + with TastySymbol + + object Bind { + private given TastySumType[TastyTerm | TastyTypeTree] = + summon[TastySumType[TastyTerm]].or(summon[TastySumType[TastyTypeTree]]) + + private given TastyFormat[TastyTerm | TastyTypeTree] = TastyFormat.forSumType + private given TastyFormat[ + (TastyNameReference, TastyType, TastyTerm | TastyTypeTree, TastyReferencableInformation, List[TastyModifier]), + ] = TastyFormat.forVariadic[ + (TastyNameReference, TastyType, TastyTerm | TastyTypeTree, TastyReferencableInformation), + TastyModifier, + List[TastyModifier], + ] + + /** + * [[Bind]] gets its own [[TastySumType]] because it's both a [[TastyTerm]] and [[TastySymbol]]. Therefore, its + * [[TastySumType]] needs to be passed to [[TastySumType.or]]. + */ + given TastySumType[Bind] = TastySumType.withSingleVariant(DottyTastyFormat.BIND, TastyFormat.forProduct[Bind]) + given TastyFormat[Bind] = TastyFormat.forSumType + } + + case class Alternative( + override var information: TastyReferencableInformation, + alternatives: List[TastyTerm], + ) extends TastyTerm + + object Alternative { + given TastyFormat[(TastyReferencableInformation, List[TastyTerm])] = + TastyFormat.forVariadic[Tuple1[TastyReferencableInformation], TastyTerm, List[TastyTerm]] + + given TastyFormat[Alternative] = TastyFormat.forProduct + } + + case class Unapply( + function: TastyTerm, + implicitArguments: List[TastyImplicitArgument], + patternType: TastyType, + patterns: List[TastyTerm], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object Unapply { + given TastyFormat[Unapply] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val function = summon[TastyFormat[TastyTerm]].read(reader) + val implicitArguments = reader.readWhile( + !reader.isAtEnd && summon[TastySumType[TastyImplicitArgument]].peekIsVariant(reader), + )(summon[TastyFormat[TastyImplicitArgument]].read(reader)) + + val patternType = summon[TastyFormat[TastyType]].read(reader) + val patterns = reader.readUntilEnd(summon[TastyFormat[TastyTerm]].read(reader)) + + Unapply(function, implicitArguments, patternType, patterns) + }, + (writer, unapply) => + writer.writeWithLengthPrefixed { writer => + summon[TastyFormat[TastyTerm]].write(writer, unapply.function) + + unapply.implicitArguments.foreach(summon[TastyFormat[TastyImplicitArgument]].write(writer, _)) + + summon[TastyFormat[TastyType]].write(writer, unapply.patternType) + + unapply.patterns.foreach(summon[TastyFormat[TastyTerm]].write(writer, _)) + }, + ) + } + + case class QuotePattern( + body: TastyTerm, + quotes: TastyTerm, + patternType: TastyType, + override var information: TastyReferencableInformation, + bindings: List[TastyTerm], + ) extends TastyTerm + + object QuotePattern { + private given TastyFormat[(TastyTerm, TastyTerm, TastyType, TastyReferencableInformation, List[TastyTerm])] = + TastyFormat + .forVariadic[(TastyTerm, TastyTerm, TastyType, TastyReferencableInformation), TastyTerm, List[TastyTerm]] + + given TastyFormat[QuotePattern] = TastyFormat.forProduct + } + } + + object PickledQuoteTree { + case class Explicit( + typeTree: TastyTypeTree, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object Explicit { + given TastyFormat[Explicit] = TastyFormat.forProduct + } + + case class Hole( + index: UnsignedInt, + `type`: TastyType, + override var information: TastyReferencableInformation, + arguments: List[TastyTerm], + ) extends TastyTerm + + object Hole { + private given TastyFormat[(UnsignedInt, TastyType, TastyReferencableInformation, List[TastyTerm])] = + TastyFormat.forVariadic[(UnsignedInt, TastyType, TastyReferencableInformation), TastyTerm, List[TastyTerm]] + + given TastyFormat[Hole] = TastyFormat.forProduct + } + } + + case class Try( + expression: TastyTerm, + cases: List[TastyCaseDefinition], + finalizer: Option[TastyTerm], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object Try { + given TastyFormat[Try] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val expression = summon[TastyFormat[TastyTerm]].read(reader) + val cases = reader.readWhile( + !reader.isAtEnd && summon[TastySumType[TastyCaseDefinition]].peekIsVariant(reader), + )(summon[TastyFormat[TastyCaseDefinition]].read(reader)) + + val finalizer = Option.unless(reader.isAtEnd)(summon[TastyFormat[TastyTerm]].read(reader)) + + Try(expression, cases, finalizer) + }, + (writer, value) => + writer.writeWithLengthPrefixed { writer => + summon[TastyFormat[TastyTerm]].write(writer, value.expression) + + value.cases.foreach(summon[TastyFormat[TastyCaseDefinition]].write(writer, _)) + value.finalizer.foreach(summon[TastyFormat[TastyTerm]].write(writer, _)) + }, + ) + } + + case class Return( + method: TastyAstReference[TastyValOrDefDefinition.Def], + override var information: TastyReferencableInformation, + expression: Option[TastyTerm], + ) extends TastyTerm + + object Return { + private given TastyFormat[ + (TastyAstReference[TastyValOrDefDefinition.Def], TastyReferencableInformation, Option[TastyTerm]), + ] = + TastyFormat.forOptional[(TastyAstReference[TastyValOrDefDefinition.Def], TastyReferencableInformation), TastyTerm] + + given TastyFormat[Return] = TastyFormat.forProduct + } + + case class While( + condition: TastyTerm, + body: TastyTerm, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object While { + given TastyFormat[While] = TastyFormat.forProduct[While].withLengthPrefixed + } + + case class RepeatedArgument( + elementType: TastyTypeTree, + override var information: TastyReferencableInformation, + elements: List[TastyTerm], + ) extends TastyTerm + + object RepeatedArgument { + private given TastyFormat[(TastyTypeTree, TastyReferencableInformation, List[TastyTerm])] = + TastyFormat.forVariadic[(TastyTypeTree, TastyReferencableInformation), TastyTerm, List[TastyTerm]] + + given TastyFormat[RepeatedArgument] = TastyFormat.forProduct + } + + case class SelectOuter( + levels: UnsignedInt, + qualified: TastyTerm, + `type`: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object SelectOuter { + given TastyFormat[SelectOuter] = TastyFormat.forProduct[SelectOuter].withLengthPrefixed + } + + case class Quoted( + body: TastyTerm, + bodyType: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object Quoted { + given TastyFormat[Quoted] = TastyFormat.forProduct[Quoted].withLengthPrefixed + } + + case class Spliced( + expression: TastyTerm, + expressionType: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object Spliced { + given TastyFormat[Spliced] = TastyFormat.forProduct[Spliced].withLengthPrefixed + } + + case class SplicedPattern( + pattern: TastyTerm, + patternType: TastyType, + typeArguments: List[TastyTypeTree], + arguments: List[TastyTerm], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object SplicedPattern { + given TastyFormat[SplicedPattern] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val pattern = summon[TastyFormat[TastyTerm]].read(reader) + val patternType = summon[TastyFormat[TastyType]].read(reader) + val typeArguments = reader.readWhile( + !reader.isAtEnd && summon[TastySumType[TastyTypeTree]].peekIsVariant(reader), + )(summon[TastyFormat[TastyTypeTree]].read(reader)) + + val arguments = reader.readUntilEnd(summon[TastyFormat[TastyTerm]].read(reader)) + + SplicedPattern(pattern, patternType, typeArguments, arguments) + }, + (writer, splicedPattern) => + writer.writeWithLengthPrefixed { writer => + summon[TastyFormat[TastyTerm]].write(writer, splicedPattern.pattern) + summon[TastyFormat[TastyType]].write(writer, splicedPattern.patternType) + + splicedPattern.typeArguments.foreach(summon[TastyFormat[TastyTypeTree]].write(writer, _)) + splicedPattern.arguments.foreach(summon[TastyFormat[TastyTerm]].write(writer, _)) + }, + ) + } + + case class Shared( + term: TastyAstReference[TastyTerm], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTerm + + object Shared { + given TastyFormat[Shared] = TastyFormat.forProduct + } + + given TastySumType[TastyTerm] = summon[TastySumType[TastyPath]] + .or(summon[TastySumType[Pattern.Bind]]) + .or( + new TastySumType( + TastySumType.Variant[Identifier](DottyTastyFormat.IDENT), + TastySumType.Variant[Select](DottyTastyFormat.SELECT), + TastySumType.Variant[SelectIn](DottyTastyFormat.SELECTin), + TastySumType.Variant[QualifiedThis](DottyTastyFormat.QUALTHIS), + TastySumType.Variant[New](DottyTastyFormat.NEW), + TastySumType.Variant[Elided](DottyTastyFormat.ELIDED), + TastySumType.Variant[Throw](DottyTastyFormat.THROW), + TastySumType.Variant[NamedArgument](DottyTastyFormat.NAMEDARG), + TastySumType.Variant[Apply](DottyTastyFormat.APPLY), + TastySumType.Variant[ApplySignaturePolymorphic](DottyTastyFormat.APPLYsigpoly), + TastySumType.Variant[TypeApply](DottyTastyFormat.TYPEAPPLY), + TastySumType.Variant[Super](DottyTastyFormat.SUPER), + TastySumType.Variant[TypeAscribed](DottyTastyFormat.TYPED), + TastySumType.Variant[Assignment](DottyTastyFormat.ASSIGN), + TastySumType.Variant[Block](DottyTastyFormat.BLOCK), + TastySumType.Variant[Inlined](DottyTastyFormat.INLINED), + TastySumType.Variant[Lambda](DottyTastyFormat.LAMBDA), + TastySumType.Variant[If](DottyTastyFormat.IF), + TastySumType.Variant[Match](DottyTastyFormat.MATCH), + TastySumType.Variant[Pattern.Alternative](DottyTastyFormat.ALTERNATIVE), + TastySumType.Variant[Pattern.Unapply](DottyTastyFormat.UNAPPLY), + TastySumType.Variant[Pattern.QuotePattern](DottyTastyFormat.QUOTE), + TastySumType.Variant[PickledQuoteTree.Explicit](DottyTastyFormat.EXPLICITtpt), + TastySumType.Variant[PickledQuoteTree.Hole](DottyTastyFormat.HOLE), + TastySumType.Variant[Try](DottyTastyFormat.TRY), + TastySumType.Variant[Return](DottyTastyFormat.RETURN), + TastySumType.Variant[While](DottyTastyFormat.WHILE), + TastySumType.Variant[RepeatedArgument](DottyTastyFormat.REPEATED), + TastySumType.Variant[SelectOuter](DottyTastyFormat.SELECTouter), + TastySumType.Variant[Quoted](DottyTastyFormat.QUOTE), + TastySumType.Variant[Spliced](DottyTastyFormat.SPLICE), + TastySumType.Variant[SplicedPattern](DottyTastyFormat.SPLICEPATTERN), + TastySumType.Variant[Shared](DottyTastyFormat.SHAREDterm), + ), + ) + + given TastyFormat[TastyTerm] = TastyFormat.forSumType +} + +sealed trait TastyTopLevelStatement extends TastyReferencable + +object TastyTopLevelStatement { + given TastySumType[TastyTopLevelStatement] = + new TastySumType(TastySumType.Variant[TastyPackageStatement](DottyTastyFormat.PACKAGE)) + .or(summon[TastySumType[TastyStatement]]) + + given TastyFormat[TastyTopLevelStatement] = TastyFormat.forSumType +} + +case class TastyPackageStatement( + path: TastyPath, + override var information: TastyReferencableInformation, + topLevelStatements: List[TastyTopLevelStatement], +) extends TastyTopLevelStatement + +object TastyPackageStatement { + private given TastyFormat[(TastyPath, TastyReferencableInformation, List[TastyTopLevelStatement])] = TastyFormat + .forVariadic[(TastyPath, TastyReferencableInformation), TastyTopLevelStatement, List[TastyTopLevelStatement]] + + given TastyFormat[TastyPackageStatement] = TastyFormat.forProduct +} + +sealed trait TastyType extends TastyTypeTree with TastyReferencable + +object TastyType { + case class LocalReference( + reference: TastyAstReference[TastySymbol], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object LocalReference { + given TastyFormat[LocalReference] = TastyFormat.forProduct + } + + case class PrefixedLocalReference( + reference: TastyAstReference[TastySymbol], + qualified: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object PrefixedLocalReference { + given TastyFormat[PrefixedLocalReference] = TastyFormat.forProduct + } + + case class PackageReference( + fullyQualifiedName: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object PackageReference { + given TastyFormat[PackageReference] = TastyFormat.forProduct + } + + case class NonLocalReference( + name: TastyNameReference, + qualified: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object NonLocalReference { + given TastyFormat[NonLocalReference] = TastyFormat.forProduct + } + + case class NonLocalReferenceIn( + name: TastyNameReference, + qualified: TastyType, + namespace: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object NonLocalReferenceIn { + given TastyFormat[NonLocalReferenceIn] = TastyFormat.forProduct[NonLocalReferenceIn].withLengthPrefixed + } + + case class RecursivelyRefined( + underlying: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object RecursivelyRefined { + given TastyFormat[RecursivelyRefined] = TastyFormat.forProduct + } + + case class Super( + thisType: TastyType, + underlying: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object Super { + given TastyFormat[Super] = TastyFormat.forProduct[Super].withLengthPrefixed + } + + case class Refined( + refinementName: TastyNameReference, + underlying: TastyType, + info: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object Refined { + given TastyFormat[Refined] = TastyFormat.forProduct[Refined].withLengthPrefixed + } + + case class Applied( + typeConstructor: TastyType, + override var information: TastyReferencableInformation, + arguments: List[TastyType], + ) extends TastyType + + object Applied { + private given TastyFormat[(TastyType, TastyReferencableInformation, List[TastyType])] = + TastyFormat.forVariadic[(TastyType, TastyReferencableInformation), TastyType, List[TastyType]] + + given TastyFormat[Applied] = TastyFormat.forProduct + } + + case class TypeBounds( + low: TastyType, + high: Option[TastyType], + variances: List[Variance], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object TypeBounds { + given TastyFormat[TypeBounds] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val low = summon[TastyFormat[TastyType]].read(reader) + val high = Option.when(!reader.isAtEnd && summon[TastySumType[TastyType]].peekIsVariant(reader))( + summon[TastyFormat[TastyType]].read(reader), + ) + + val variances = reader.readUntilEnd(summon[TastyFormat[Variance]].read(reader)) + + TypeBounds(low, high, variances) + }, + (writer, typeBounds) => + writer.writeWithLengthPrefixed { writer => + summon[TastyFormat[TastyType]].write(writer, typeBounds.low) + + typeBounds.high.foreach(summon[TastyFormat[TastyType]].write(writer, _)) + typeBounds.variances.foreach(summon[TastyFormat[Variance]].write(writer, _)) + }, + ) + } + + sealed trait Variance extends TastyReferencable + + object Variance { + case class Invariant(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends Variance + + object Invariant { + given TastyFormat[Invariant] = TastyFormat.forProduct + } + + case class Covariant(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends Variance + + object Covariant { + given TastyFormat[Covariant] = TastyFormat.forProduct + } + + case class Contravariant(override var information: TastyReferencableInformation = TastyReferencableInformation()) + extends Variance + + object Contravariant { + given TastyFormat[Contravariant] = TastyFormat.forProduct + } + + given TastySumType[Variance] = new TastySumType( + TastySumType.Variant[Invariant](DottyTastyFormat.STABLE), + TastySumType.Variant[Covariant](DottyTastyFormat.COVARIANT), + TastySumType.Variant[Contravariant](DottyTastyFormat.CONTRAVARIANT), + ) + + given TastyFormat[Variance] = TastyFormat.forSumType + } + + case class Annotated( + underlying: TastyType, + annotation: TastyTerm, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object Annotated { + given TastyFormat[Annotated] = TastyFormat.forProduct[Annotated].withLengthPrefixed + } + + case class And( + left: TastyType, + right: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object And { + given TastyFormat[And] = TastyFormat.forProduct[And].withLengthPrefixed + } + + case class Or( + left: TastyType, + right: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object Or { + given TastyFormat[Or] = TastyFormat.forProduct[Or].withLengthPrefixed + } + + case class Match( + upperBound: TastyType, + scrutinee: TastyType, + override var information: TastyReferencableInformation, + cases: List[TastyType], + ) extends TastyType + + object Match { + private given TastyFormat[(TastyType, TastyType, TastyReferencableInformation, List[TastyType])] = + TastyFormat.forVariadic[(TastyType, TastyType, TastyReferencableInformation), TastyType, List[TastyType]] + + given TastyFormat[Match] = TastyFormat.forProduct + } + + case class MatchCase( + pattern: TastyType, + rightHandSide: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object MatchCase { + given TastyFormat[MatchCase] = TastyFormat.forProduct[MatchCase].withLengthPrefixed + } + + case class Flexible( + underlying: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object Flexible { + given TastyFormat[Flexible] = TastyFormat.forProduct[Flexible].withLengthPrefixed + } + + case class ByName( + underlying: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object ByName { + given TastyFormat[ByName] = TastyFormat.forProduct + } + + /** + * @param typeOrBounds + * According to the TASTy grammar, this should be an AST reference to a type, but it's read in tasty-query as an + * actual type: + * [[https://github.com/scalacenter/tasty-query/blob/275ea2d125b16ac74d47d56babfa91bea144f2ad/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala#L438]] + */ + case class TypeName( + typeOrBounds: TastyType, + name: TastyNameReference, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyReferencable + + object TypeName { + given TastyFormat[TypeName] = TastyFormat.forProduct + } + + case class ParameterReference( + binder: TastyAstReference[TastyType], + parameterNumber: UnsignedInt, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object ParameterReference { + given TastyFormat[ParameterReference] = TastyFormat.forProduct[ParameterReference].withLengthPrefixed + } + + case class PolymorphicMethod( + result: TastyType, + override var information: TastyReferencableInformation, + parameters: List[TypeName], + ) extends TastyType + + object PolymorphicMethod { + private given TastyFormat[(TastyType, TastyReferencableInformation, List[TypeName])] = + TastyFormat.forVariadic[(TastyType, TastyReferencableInformation), TypeName, List[TypeName]] + + given TastyFormat[PolymorphicMethod] = TastyFormat.forProduct + } + + case class Method( + result: TastyType, + parameters: List[TypeName], + modifiers: List[TastyModifier], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object Method { + given TastyFormat[Method] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val result = summon[TastyFormat[TastyType]].read(reader) + val parameters = reader.readWhile( + !reader.isAtEnd && !summon[TastySumType[TastyModifier]].peekIsVariant(reader), + )(summon[TastyFormat[TypeName]].read(reader)) + + val modifiers = reader.readUntilEnd(summon[TastyFormat[TastyModifier]].read(reader)) + + Method(result, parameters, modifiers) + }, + (writer, method) => + writer.writeWithLengthPrefixed { writer => + summon[TastyFormat[TastyType]].write(writer, method.result) + + method.parameters.foreach(summon[TastyFormat[TypeName]].write(writer, _)) + method.modifiers.foreach(summon[TastyFormat[TastyModifier]].write(writer, _)) + }, + ) + } + + case class TypeLambda( + result: TastyType, + override var information: TastyReferencableInformation, + parameters: List[TypeName], + ) extends TastyType + + object TypeLambda { + private given TastyFormat[(TastyType, TastyReferencableInformation, List[TypeName])] = + TastyFormat.forVariadic[(TastyType, TastyReferencableInformation), TypeName, List[TypeName]] + + given TastyFormat[TypeLambda] = TastyFormat.forProduct + } + + case class Shared( + `type`: TastyAstReference[TastyType], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object Shared { + given TastyFormat[Shared] = TastyFormat.forProduct + } + + /** + * This isn't formally documented in the TASTy grammar as a type (nor is it clear from reading Dotty's TASTy parser + * that it's a valid type), but you can find it here: + * [[https://github.com/scalacenter/tasty-query/blob/275ea2d125b16ac74d47d56babfa91bea144f2ad/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala#L980]] + */ + case class QualifiedThis( + qualifier: TastyTypeTree, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyType + + object QualifiedThis { + given TastyFormat[QualifiedThis] = TastyFormat.forProduct + } + + given TastySumType[TastyType] = summon[TastySumType[TastyPath]] + .or(summon[TastySumType[TastyTerm.Pattern.Bind]]) + .or( + new TastySumType( + TastySumType.Variant[LocalReference](DottyTastyFormat.TYPEREFdirect), + TastySumType.Variant[PrefixedLocalReference](DottyTastyFormat.TYPEREFsymbol), + TastySumType.Variant[PackageReference](DottyTastyFormat.TYPEREFpkg), + TastySumType.Variant[NonLocalReference](DottyTastyFormat.TYPEREF), + TastySumType.Variant[NonLocalReferenceIn](DottyTastyFormat.TYPEREFin), + TastySumType.Variant[RecursivelyRefined](DottyTastyFormat.RECtype), + TastySumType.Variant[Super](DottyTastyFormat.SUPERtype), + TastySumType.Variant[Refined](DottyTastyFormat.REFINEDtype), + TastySumType.Variant[Applied](DottyTastyFormat.APPLIEDtype), + TastySumType.Variant[TypeBounds](DottyTastyFormat.TYPEBOUNDS), + TastySumType.Variant[Annotated](DottyTastyFormat.ANNOTATEDtype), + TastySumType.Variant[And](DottyTastyFormat.ANDtype), + TastySumType.Variant[Or](DottyTastyFormat.ORtype), + TastySumType.Variant[Match](DottyTastyFormat.MATCHtype), + TastySumType.Variant[MatchCase](DottyTastyFormat.MATCHCASEtype), + TastySumType.Variant[Flexible](DottyTastyFormat.FLEXIBLEtype), + TastySumType.Variant[ByName](DottyTastyFormat.BYNAMEtype), + TastySumType.Variant[ParameterReference](DottyTastyFormat.PARAMtype), + TastySumType.Variant[PolymorphicMethod](DottyTastyFormat.POLYtype), + TastySumType.Variant[Method](DottyTastyFormat.METHODtype), + TastySumType.Variant[TypeLambda](DottyTastyFormat.TYPELAMBDAtype), + TastySumType.Variant[Shared](DottyTastyFormat.SHAREDtype), + TastySumType.Variant[QualifiedThis](DottyTastyFormat.QUALTHIS), + ), + ) + + given TastyFormat[TastyType] = TastyFormat.forSumType +} + +case class TastyTypeDefinition( + name: TastyNameReference, + value: TastyTypeTree | TastyTemplate, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + modifiers: List[TastyModifier], +) extends TastyStatement + with TastySymbol + +object TastyTypeDefinition { + private given TastySumType[TastyTypeTree | TastyTemplate] = + summon[TastySumType[TastyTypeTree]].or(summon[TastySumType[TastyTemplate]]) + + private given TastyFormat[TastyTypeTree | TastyTemplate] = TastyFormat.forSumType + private given TastyFormat[ + (TastyNameReference, TastyTypeTree | TastyTemplate, TastyReferencableInformation, List[TastyModifier]), + ] = TastyFormat.forVariadic[ + (TastyNameReference, TastyTypeTree | TastyTemplate, TastyReferencableInformation), + TastyModifier, + List[TastyModifier], + ] + + given TastySumType[TastyTypeDefinition] = + TastySumType.withSingleVariant(DottyTastyFormat.TYPEDEF, TastyFormat.forProduct[TastyTypeDefinition]) + + given TastyFormat[TastyTypeDefinition] = TastyFormat.forSumType +} + +sealed trait TastyTypeTree extends TastyReferencable + +object TastyTypeTree { + case class Identifier( + name: TastyNameReference, + `type`: TastyType, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTypeTree + + object Identifier { + given TastyFormat[Identifier] = TastyFormat.forProduct + } + + /** + * This isn't formally documented in the TASTy grammar as a type tree, but you can find it here: + * [[https://github.com/scala/scala3/blob/4d3f7576ccae724e6f83d2f3d68bd4c4e1dd5a14/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala#L1319]] + * + * The difference between this and [[SelectFromType]] is that this selects a type from a term, whereas + * [[SelectFromType]] selects a field from a type. + */ + case class SelectFromTerm( + possiblySignedName: TastyNameReference, + qualified: TastyTerm, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTypeTree + + object SelectFromTerm { + given TastyFormat[SelectFromTerm] = TastyFormat.forProduct + } + + case class SelectFromType( + possiblySignedName: TastyNameReference, + qualified: TastyTypeTree, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTypeTree + + object SelectFromType { + given TastyFormat[SelectFromType] = TastyFormat.forProduct + } + + case class Singleton( + reference: TastyTerm, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTypeTree + + object Singleton { + given TastyFormat[Singleton] = TastyFormat.forProduct + } + + case class Refined( + underlying: TastyTypeTree, + override var information: TastyReferencableInformation, + refinements: List[TastyStatement], + ) extends TastyTypeTree + + object Refined { + private given TastyFormat[(TastyTypeTree, TastyReferencableInformation, List[TastyStatement])] = + TastyFormat.forVariadic[(TastyTypeTree, TastyReferencableInformation), TastyStatement, List[TastyStatement]] + + given TastyFormat[Refined] = TastyFormat.forProduct + } + + case class Applied( + typeConstructor: TastyTypeTree, + override var information: TastyReferencableInformation, + typeArguments: List[TastyTypeTree], + ) extends TastyTypeTree + + object Applied { + private given TastyFormat[(TastyTypeTree, TastyReferencableInformation, List[TastyTypeTree])] = + TastyFormat.forVariadic[(TastyTypeTree, TastyReferencableInformation), TastyTypeTree, List[TastyTypeTree]] + + given TastyFormat[Applied] = TastyFormat.forProduct + } + + case class Lambda( + typeParameters: List[TastyTypeParameter], + body: TastyTypeTree, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTypeTree + + object Lambda { + given TastyFormat[Lambda] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val typeParameters = reader.readWhile( + !reader.isAtEnd && summon[TastySumType[TastyTypeParameter]].peekIsVariant(reader), + )(summon[TastyFormat[TastyTypeParameter]].read(reader)) + + val body = summon[TastyFormat[TastyTypeTree]].read(reader) + + Lambda(typeParameters, body) + }, + (writer, lambda) => + writer.writeWithLengthPrefixed { writer => + lambda.typeParameters.foreach(summon[TastyFormat[TastyTypeParameter]].write(writer, _)) + + summon[TastyFormat[TastyTypeTree]].write(writer, lambda.body) + }, + ) + } + + /** + * @param alias + * The TASTy grammar doesn't mention this, but [[TypeBounds]] can have a third field: + * [[https://github.com/scala/scala3/blob/4d3f7576ccae724e6f83d2f3d68bd4c4e1dd5a14/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala#L1654]] + */ + case class TypeBounds( + low: TastyTypeTree, + high: Option[TastyTypeTree], + alias: Option[TastyTypeTree], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTypeTree + + object TypeBounds { + given TastyFormat[TypeBounds] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val low = summon[TastyFormat[TastyTypeTree]].read(reader) + val high = Option.unless(reader.isAtEnd)(summon[TastyFormat[TastyTypeTree]].read(reader)) + val alias = Option.unless(reader.isAtEnd)(summon[TastyFormat[TastyTypeTree]].read(reader)) + + TypeBounds(low, high, alias) + }, + (writer, typeBounds) => + writer.writeWithLengthPrefixed { writer => + summon[TastyFormat[TastyTypeTree]].write(writer, typeBounds.low) + + typeBounds.high.foreach(summon[TastyFormat[TastyTypeTree]].write(writer, _)) + typeBounds.alias.foreach(summon[TastyFormat[TastyTypeTree]].write(writer, _)) + }, + ) + } + + case class Annotated( + underlying: TastyTypeTree, + annotation: TastyTerm, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTypeTree + + object Annotated { + given TastyFormat[Annotated] = TastyFormat.forProduct[Annotated].withLengthPrefixed + } + + case class Match( + bound: Option[TastyTypeTree], + scrutinee: TastyTypeTree, + cases: List[TastyCaseDefinition], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTypeTree + + object Match { + given TastyFormat[Match] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val boundOrScrutinee = summon[TastyFormat[TastyTypeTree]].read(reader) + val (bound, scrutinee) = if (!reader.isAtEnd && summon[TastySumType[TastyTypeTree]].peekIsVariant(reader)) { + (Some(boundOrScrutinee), summon[TastyFormat[TastyTypeTree]].read(reader)) + } else { + (None, boundOrScrutinee) + } + + val cases = reader.readUntilEnd(summon[TastyFormat[TastyCaseDefinition]].read(reader)) + + Match(bound, scrutinee, cases) + }, + (writer, value) => + writer.writeWithLengthPrefixed { writer => + value.bound.foreach(summon[TastyFormat[TastyTypeTree]].write(writer, _)) + + summon[TastyFormat[TastyTypeTree]].write(writer, value.scrutinee) + + value.cases.foreach(summon[TastyFormat[TastyCaseDefinition]].write(writer, _)) + }, + ) + } + + case class ByName( + underlying: TastyTypeTree, + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTypeTree + + object ByName { + given TastyFormat[ByName] = TastyFormat.forProduct + } + + /** + * This isn't formally documented in the TASTy grammar as a type tree, but you can find it here: + * [[https://github.com/scala/scala3/blob/4d3f7576ccae724e6f83d2f3d68bd4c4e1dd5a14/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala#L1691]] + * + * It's used to refer to a type tree that's defined elsewhere without duplicating it. + */ + case class Shared( + term: TastyAstReference[TastyTypeTree], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyTypeTree + + object Shared { + given TastyFormat[Shared] = TastyFormat.forProduct + } + + given TastySumType[TastyTypeTree] = new TastySumType( + TastySumType.Variant[Identifier](DottyTastyFormat.IDENTtpt), + TastySumType.Variant[SelectFromTerm](DottyTastyFormat.SELECT), + TastySumType.Variant[SelectFromType](DottyTastyFormat.SELECTtpt), + TastySumType.Variant[Singleton](DottyTastyFormat.SINGLETONtpt), + TastySumType.Variant[Refined](DottyTastyFormat.REFINEDtpt), + TastySumType.Variant[Applied](DottyTastyFormat.APPLIEDtpt), + TastySumType.Variant[Lambda](DottyTastyFormat.LAMBDAtpt), + TastySumType.Variant[TypeBounds](DottyTastyFormat.TYPEBOUNDStpt), + TastySumType.Variant[Annotated](DottyTastyFormat.ANNOTATEDtpt), + TastySumType.Variant[Match](DottyTastyFormat.MATCHtpt), + TastySumType.Variant[ByName](DottyTastyFormat.BYNAMEtpt), + TastySumType.Variant[Shared](DottyTastyFormat.SHAREDterm), + ).or(summon[TastySumType[TastyType]]) + + given TastyFormat[TastyTypeTree] = TastyFormat.forSumType +} + +sealed trait TastyValOrDefDefinition extends TastyStatement with TastySymbol + +object TastyValOrDefDefinition { + case class Val( + name: TastyNameReference, + `type`: TastyTypeTree, + value: Option[TastyTerm], + modifiers: List[TastyModifier], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyValOrDefDefinition + + object Val { + given TastyFormat[Val] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val name = summon[TastyFormat[TastyNameReference]].read(reader) + val `type` = summon[TastyFormat[TastyTypeTree]].read(reader) + val value = Option.when(!reader.isAtEnd && summon[TastySumType[TastyTerm]].peekIsVariant(reader))( + summon[TastyFormat[TastyTerm]].read(reader), + ) + + val modifiers = reader.readUntilEnd(summon[TastyFormat[TastyModifier]].read(reader)) + + Val(name, `type`, value, modifiers) + }, + (writer, definition) => + writer.writeWithLengthPrefixed { writer => + summon[TastyFormat[TastyNameReference]].write(writer, definition.name) + summon[TastyFormat[TastyTypeTree]].write(writer, definition.`type`) + + definition.value.foreach(summon[TastyFormat[TastyTerm]].write(writer, _)) + definition.modifiers.foreach(summon[TastyFormat[TastyModifier]].write(writer, _)) + }, + ) + } + + case class Def( + name: TastyNameReference, + parameters: List[TastyParameter], + `type`: TastyTypeTree, + value: Option[TastyTerm], + modifiers: List[TastyModifier], + override var information: TastyReferencableInformation = TastyReferencableInformation(), + ) extends TastyValOrDefDefinition + + object Def { + private val underlyingTastyFormat: TastyFormat[Def] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val name = summon[TastyFormat[TastyNameReference]].read(reader) + val parameters = reader.readWhile(summon[TastySumType[TastyParameter]].peekIsVariant(reader))( + summon[TastyFormat[TastyParameter]].read(reader), + ) + + val `type` = summon[TastyFormat[TastyTypeTree]].read(reader) + val value = Option.when(!reader.isAtEnd && summon[TastySumType[TastyTerm]].peekIsVariant(reader))( + summon[TastyFormat[TastyTerm]].read(reader), + ) + + val modifiers = reader.readUntilEnd(summon[TastyFormat[TastyModifier]].read(reader)) + + Def(name, parameters, `type`, value, modifiers) + }, + (writer, definition) => + writer.writeWithLengthPrefixed { writer => + summon[TastyFormat[TastyNameReference]].write(writer, definition.name) + + definition.parameters.foreach(summon[TastyFormat[TastyParameter]].write(writer, _)) + + summon[TastyFormat[TastyTypeTree]].write(writer, definition.`type`) + + definition.value.foreach(summon[TastyFormat[TastyTerm]].write(writer, _)) + definition.modifiers.foreach(summon[TastyFormat[TastyModifier]].write(writer, _)) + }, + ) + + /** + * [[Def]] gets its own [[TastySumType]] because it's used in [[TastyTerm.Return]] and its tag needs to be included + * when we're reading or writing a [[TastyTerm.Return]], not just when we're reading or writing a + * [[TastyValOrDefDefinition]]. + */ + given TastySumType[Def] = TastySumType.withSingleVariant(DottyTastyFormat.DEFDEF, underlyingTastyFormat) + given TastyFormat[Def] = TastyFormat.forSumType + } + + given TastySumType[TastyValOrDefDefinition] = + summon[TastySumType[Def]].or(new TastySumType(TastySumType.Variant[Val](DottyTastyFormat.VALDEF))) + + given TastyFormat[TastyValOrDefDefinition] = TastyFormat.forSumType +} diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.spec.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.spec.scala new file mode 100644 index 000000000..611f072a5 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.spec.scala @@ -0,0 +1,124 @@ +package io.bazel.rules_scala.dottyijar.tasty + +import io.bazel.rules_scala.dottyijar.tasty.format.DebuggingTastyFormat +import org.apache.commons.io.IOUtils +import java.io.{File, FileOutputStream, InputStream} +import java.nio.file.Files +import java.util.zip.ZipFile +import org.specs2.execute.Result +import org.specs2.mutable.SpecificationWithJUnit +import scala.jdk.CollectionConverters.* +import scala.util.control.NonFatal + +class TastySpec extends SpecificationWithJUnit { + + /** + * [[io.bazel.rules_scala.dottyijar.tasty.format.DebuggingTastyFormat]] uses global state to track the structure of + * the TASTy file being read or written, which means that only one file can be read at a time. If we weren't using + * `DebuggingTastyFormat` (which should be the case everywhere else besides this test), multiple TASTy files could be + * read from and written to in parallel. + */ + sequential + + "Tasty" should { + def withTestCases[A](callback: List[TestCase] => A): A = { + val scala3CompilerJar = File.createTempFile("scala3-compiler", ".jar") + val inputStream = getClass.getClassLoader.getResourceAsStream("scala3-compiler.jar") + val outputStream = new FileOutputStream(scala3CompilerJar) + + try { + try { + IOUtils.copy(inputStream, outputStream) + } finally { + inputStream.close() + outputStream.close() + } + + val scala3CompilerZipFile = new ZipFile(scala3CompilerJar) + + callback( + scala3CompilerZipFile + .entries() + .asScala + .filter(_.getName.endsWith(".tasty")) + .map(entry => TestCase(entry.getName, scala3CompilerZipFile.getInputStream(entry))) + .toList, + ) + } finally { + Files.delete(scala3CompilerJar.toPath) + } + } + + "Accurately model every TASTy file for the Scala 3 compiler" in { + withTestCases { + Result.foreach(_) { testCase => + println(s"Testing ${testCase.path}") + + val content = testCase.inputStream.readAllBytes() + + def handleTastyFormatFailure(writtenContent: Option[Array[Byte]]): Unit = { + val i = testCase.path.lastIndexOf("/") + val j = testCase.path.lastIndexOf(".") + val baseName = testCase.path.slice(if (i == -1) 0 else i + 1, if (j == -1) testCase.path.length else j) + + /** + * [[File.createTempFile]] requires the temporary file prefix to be at least three characters long. + */ + val prefix = if (baseName.length < 3) baseName + "_" * (3 - baseName.length) else baseName + val extractedTastyFilePath = File.createTempFile(prefix, ".tasty").toPath + + Files.write(extractedTastyFilePath, content) + + println(s"Extracted the incorrectly read TASTy file here: $extractedTastyFilePath") + + writtenContent.foreach { writtenContent => + val path = File.createTempFile(baseName, ".tasty").toPath + + Files.write(path, writtenContent) + + println(s"Extracted the incorrectly written TASTy file here: $path") + } + + println("TastyFormat logs:") + println(DebuggingTastyFormat.logs) + } + + def tryTastyFormatOperation[A](result: => A, writtenContent: Option[Array[Byte]] = None): A = try { + result + } catch { + case NonFatal(exception) => + handleTastyFormatFailure(writtenContent) + + throw exception + } + + try { + val readTasty = tryTastyFormatOperation(Tasty.read(content)) + val writtenContent = tryTastyFormatOperation(readTasty.write) + val writtenTasty = tryTastyFormatOperation(Tasty.read(writtenContent), Some(writtenContent)) + + /** + * See the documentation for [[io.bazel.rules_scala.dottyijar.tasty.format.TastyReference.equals]] to + * understand why we don't just compare [[content]] and [[writtenContent]] directly. + */ + if (readTasty != writtenTasty) { + handleTastyFormatFailure(Some(writtenContent)) + + /** + * `writtenTasty must ===(readTasty)` uses way too much memory, so we only call it if [[readTasty]] and + * [[writtenTasty]] are unequal + */ + writtenTasty must ===(readTasty) + } + + success + } finally { + DebuggingTastyFormat.clearLogs() + } + } + } + } + } +} + +private case class TestCase(path: String, inputStream: InputStream) diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/BUILD b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/BUILD new file mode 100644 index 000000000..5344714f8 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/BUILD @@ -0,0 +1,15 @@ +load("//scala:scala.bzl", "scala_library_for_plugin_bootstrapping") + +scala_library_for_plugin_bootstrapping( + name = "format", + srcs = glob(["*.scala"]), + scala_version = "3.6.2", + visibility = ["//visibility:public"], + deps = [ + "@com_softwaremill_common_tagging_3_6_2", + "@dev_zio_izumi_reflect_3_6_2", + "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded_3_6_2", + "@io_bazel_rules_scala_scala_compiler_3_6_2", + "@io_bazel_rules_scala_scala_tasty_core_3_6_2", + ], +) diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/Marker.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/Marker.scala new file mode 100644 index 000000000..861aec495 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/Marker.scala @@ -0,0 +1,11 @@ +package io.bazel.rules_scala.dottyijar.tasty.format + +private case class Marker(position: Int, markerType: MarkerType) + +sealed abstract class MarkerType + +object MarkerType { + case object AstSection extends MarkerType +} + +class MarkerNotSetException(markerType: MarkerType) extends Exception(s"A marker of type $markerType hasn't been set.") diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyFormat.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyFormat.scala new file mode 100644 index 000000000..b9234511d --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyFormat.scala @@ -0,0 +1,274 @@ +package io.bazel.rules_scala.dottyijar.tasty.format + +import com.softwaremill.tagging.* +import dotty.tools.dotc.util.Spans.Span +import izumi.reflect.Tag +import java.util.UUID +import scala.collection.{mutable, Factory} +import scala.deriving.Mirror +import scala.reflect.ClassTag + +trait TastyFormat[A] private[format] { + def read(reader: TastyReader): A + def write(writer: TastyWriter, value: A): Unit + + final def bimap[B: Tag](to: A => B, from: B => A): TastyFormat[B] = + TastyFormat(reader => to(read(reader)), (writer, value) => write(writer, from(value))) + + final def marked(markerType: MarkerType): TastyFormat[A] = new TastyFormat[A] { + override def read(reader: TastyReader): A = { + reader.setMarker(markerType) + + TastyFormat.this.read(reader) + } + + override def write(writer: TastyWriter, value: A): Unit = { + writer.setMarker(markerType) + + TastyFormat.this.write(writer, value) + } + } + + final def withLengthPrefixed: TastyFormat[A] = new TastyFormat[A] { + override def read(reader: TastyReader): A = reader.readWithLength(reader.readUnsignedInt())(TastyFormat.this.read) + override def write(writer: TastyWriter, value: A): Unit = + writer.writeWithLengthPrefixed(TastyFormat.this.write(_, value)) + } +} + +object TastyFormat { + def apply[A: Tag](read: TastyReader => A, write: (TastyWriter, A) => Unit): TastyFormat[A] = { + val _read = read + val _write = write + + if (isDebuggingEnabled) { + new DebuggingTastyFormat[A] { + override protected def readUnderlying(reader: TastyReader): A = _read(reader) + override protected def writeUnderlying(writer: TastyWriter, value: A): Unit = _write(writer, value) + } + } else { + new TastyFormat[A] { + override def read(reader: TastyReader): A = _read(reader) + override def write(writer: TastyWriter, value: A): Unit = _write(writer, value) + } + } + } + + inline def forIterableWithLengthPrefixed[Element: TastyFormat, SeqLike <: Seq[Element]](using + factory: Factory[Element, SeqLike], + ): TastyFormat[SeqLike] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + factory.fromSpecific(reader.readUntilEnd(summon[TastyFormat[Element]].read(reader))) + }, + (writer, value) => + writer.writeWithLengthPrefixed(writer => value.foreach(summon[TastyFormat[Element]].write(writer, _))), + ) + + inline def forIterableWithoutLengthPrefixed[Element: TastyFormat, SeqLike <: Seq[Element]](using + factory: Factory[Element, SeqLike], + ): TastyFormat[SeqLike] = TastyFormat( + reader => factory.fromSpecific(reader.readUntilEnd(summon[TastyFormat[Element]].read(reader))), + (writer, value) => value.foreach(summon[TastyFormat[Element]].write(writer, _)), + ) + + inline def forOptional[Init <: Tuple: TastyFormat, Last: TastyFormat](using + evidence1: Tuple.Init[Tuple.Append[Init, Option[Last]]] =:= Init, + evidence2: Tuple.Last[Tuple.Append[Init, Option[Last]]] =:= Option[Last], + ): TastyFormat[Tuple.Append[Init, Option[Last]]] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val init = summon[TastyFormat[Init]].read(reader) + + init :* Option.unless(reader.isAtEnd)(summon[TastyFormat[Last]].read(reader)) + }, + (writer, value) => + writer.writeWithLengthPrefixed { writer => + summon[TastyFormat[Init]].write(writer, value.init) + + value.last.foreach(summon[TastyFormat[Last]].write(writer, _)) + }, + ) + + inline def forProduct[A <: Product](using + mirror: Mirror.ProductOf[A], + )(using underlying: TastyFormat[mirror.MirroredElemTypes]): TastyFormat[A] = TastyFormat( + reader => mirror.fromTuple(underlying.read(reader)), + (writer, record) => underlying.write(writer, Tuple.fromProductTyped(record)), + ) + + inline def forSumType[A <: TastyReferencable: TastySumType]: TastyFormat[A] = + TastyFormat( + reader => + reader.readReferencable { + val tag = reader.readByte() + + summon[TastySumType[A]].variantsByTag + .getOrElse( + tag, + throw new Exception( + s"Unknown tag: $tag. Expected one of ${summon[TastySumType[A]].variants.map(_.tag).mkString(", ")}", + ), + ) + .read(reader) + }, + (writer, value) => { + + /** + * Previously, we declared a `TastyReferencableFormat` type that wrapped an underlying [[TastyFormat]] value by + * recording the position of the value to be written (which is necessary to write references to that value). We + * ended up scrapping this and instead made all sum types "referencable" because it wasn't guaranteed that the + * positions of sum type values would be recorded. This is due to two reasons: + * + * 1. [[TastySumType]]s can be composed, so even if the [[TastyFormat]] for a type `A` was referencable, it + * may have been passed to [[TastySumType.or]] to create a [[TastySumType]] for a supertype of `A`. Unless + * that supertype's [[TastyFormat]] was wrapped in a `TastyReferencableFormat`, it wouldn't be + * "referencable", meaning that we couldn't write references to values of type `A` unless those values were + * written as "`A`"s, and not some supertype of `A`. + * 1. [[TastySumType]] composition also means that if a value of type `A` were written using a more specific + * [[TastyFormat]] for one of its subclasses that wasn't wrapped in a `TastyReferencableFormat`, the + * position for that value wouldn't have been recorded. + */ + writer.writeReferencable(value)( + summon[TastySumType[A]].variants + .find(_.maybeWrite(writer, value)) + .getOrElse(throw new Exception(s"Couldn't find the variant of $value")), + ) + }, + ) + + def forValue[A: ValueOf: Tag]: TastyFormat[A] = TastyFormat(_ => summon[ValueOf[A]].value, (_, _) => {}) + + inline def forVariadic[Init <: Tuple: TastyFormat, LastElement: TastyFormat, Last <: Iterable[LastElement]](using + factory: Factory[LastElement, Last], + evidence1: Tuple.Init[Tuple.Append[Init, Last]] =:= Init, + evidence2: Tuple.Last[Tuple.Append[Init, Last]] =:= Last, + ): TastyFormat[Tuple.Append[Init, Last]] = TastyFormat( + reader => + reader.readWithLength(reader.readUnsignedInt()) { reader => + val init = summon[TastyFormat[Init]].read(reader) + + init :* summon[Factory[LastElement, Last]] + .fromSpecific(reader.readUntilEnd(summon[TastyFormat[LastElement]].read(reader))) + }, + (writer, value) => + writer.writeWithLengthPrefixed { writer => + summon[TastyFormat[Init]].write(writer, value.init) + + value.last.foreach(summon[TastyFormat[LastElement]].write(writer, _)) + }, + ) + + given TastyFormat[SignedInt] = TastyFormat(_.readSignedInt(), (writer, value) => writer.writeSignedInt(value)) + given TastyFormat[SignedLong] = TastyFormat(_.readSignedLong(), (writer, value) => writer.writeSignedLong(value)) + given TastyFormat[Span] = summon[TastyFormat[SignedLong]].bimap(new Span(_), _.coords.taggedWith[Signed]) + given TastyFormat[String] = TastyFormat(_.readUtf8String(), (writer, value) => writer.writeUtf8String(value)) + given TastyFormat[UnsignedInt] = TastyFormat(_.readUnsignedInt(), (writer, value) => writer.writeUnsignedInt(value)) + given TastyFormat[UnsignedLong] = + TastyFormat(_.readUnsignedLong(), (writer, value) => writer.writeUnsignedLong(value)) + + given TastyFormat[UUID] = TastyFormat(_.readUuid(), (writer, value) => writer.writeUuid(value)) + + inline given [A: TastyFormat]: TastyFormat[Tuple1[A]] with { + override def read(reader: TastyReader): Tuple1[A] = Tuple1(summon[TastyFormat[A]].read(reader)) + override def write(writer: TastyWriter, value: Tuple1[A]): Unit = summon[TastyFormat[A]].write(writer, value._1) + } + + inline given [Head: TastyFormat, Tail <: Tuple: TastyFormat]: TastyFormat[Head *: Tail] with { + override def read(reader: TastyReader): Head *: Tail = + summon[TastyFormat[Head]].read(reader) *: summon[TastyFormat[Tail]].read(reader) + + override def write(writer: TastyWriter, value: Head *: Tail): Unit = { + summon[TastyFormat[Head]].write(writer, value.head) + summon[TastyFormat[Tail]].write(writer, value.tail) + } + } +} + +private abstract class DebuggingTastyFormat[A: Tag] extends TastyFormat[A] { + protected def readUnderlying(reader: TastyReader): A + protected def writeUnderlying(writer: TastyWriter, value: A): Unit + + private val typeName = summon[Tag[A]].tag.shortName + + override def read(reader: TastyReader): A = { + DebuggingTastyFormat.depth += 1 + + try { + DebuggingTastyFormat.logBuffer ++= + s"${" " * DebuggingTastyFormat.depth}TastyFormat: Reading $typeName at ${reader.start}\n" + + val position = reader.start + val result = readUnderlying(reader) + + DebuggingTastyFormat.logBuffer ++= + s"${" " * DebuggingTastyFormat.depth}TastyFormat: Read $result at $position\n" + + result + } finally { + DebuggingTastyFormat.depth -= 1 + } + } + + override def write(writer: TastyWriter, value: A): Unit = { + DebuggingTastyFormat.depth += 1 + + try { + DebuggingTastyFormat.logBuffer ++= + s"${" " * DebuggingTastyFormat.depth}TastyFormat: Writing $value at ${writer.start}\n" + + val position = writer.start + + writeUnderlying(writer, value) + + DebuggingTastyFormat.logBuffer ++= + s"${" " * DebuggingTastyFormat.depth}TastyFormat: Wrote $typeName at $position\n" + } finally { + DebuggingTastyFormat.depth -= 1 + } + } +} + +private[tasty] object DebuggingTastyFormat { + private var depth = -1 + private val logBuffer = new mutable.StringBuilder() + + def clearLogs(): Unit = logBuffer.clear() + def logs: String = logBuffer.toString +} + +class TastySumType[A](private[format] val variants: TastySumType.Variant[? <: A]*) { + private[format] val variantsByTag: Map[Byte, TastySumType.Variant[? <: A]] = + variants.map(variant => variant.tag -> variant).toMap + + def peekIsVariant(reader: TastyReader): Boolean = variantsByTag.contains(reader.peek(_.readByte())) + def or[B](other: TastySumType[B]): TastySumType[A | B] = + new TastySumType(variants ++ other.variants*) +} + +object TastySumType { + case class Variant[A: ClassTag](tag: Byte)(using TastyFormat[A]) { + private[format] def maybeWrite(writer: TastyWriter, value: Any): Boolean = value match { + case value: A => + writer.writeByte(tag) + + summon[TastyFormat[A]].write(writer, value) + + true + + case _ => false + } + + private[format] def read(reader: TastyReader): A = summon[TastyFormat[A]].read(reader) + } + + object Variant { + def apply[A: ClassTag](tag: Int)(using TastyFormat[A]): Variant[A] = new Variant(tag.toByte) + } + + def withSingleVariant[A: ClassTag](tag: Byte, format: TastyFormat[A]): TastySumType[A] = + new TastySumType(Variant(tag)(using summon, format)) + + def withSingleVariant[A: ClassTag](tag: Int, format: TastyFormat[A]): TastySumType[A] = + withSingleVariant(tag.toByte, format) +} diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReader.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReader.scala new file mode 100644 index 000000000..a076d518b --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReader.scala @@ -0,0 +1,180 @@ +package io.bazel.rules_scala.dottyijar.tasty.format + +import com.softwaremill.tagging.* +import dotty.tools.tasty.TastyFormat as DottyTastyFormat +import java.nio.charset.StandardCharsets +import java.util.UUID +import scala.annotation.tailrec +import scala.collection.mutable + +case class TastyReader private ( + input: Array[Byte], + private[format] var start: Int, + end: Int, + markers: mutable.Map[MarkerType, Marker], + private var nextReferencableId: Int, + referencesByTargetPosition: mutable.LongMap[TastyReference[? <: MarkerType, ?]], + referencablesByPosition: mutable.LongMap[TastyReferencable], +) { + private def readNBytes(length: Int): Array[Byte] = { + val result = input.slice(start, start + length) + + start += result.length + + result + } + + private def readUncompressedLong(): Long = Range(0, 8).foldLeft(0L) { case (current, _) => + (current << 8) | (readByte() & 0xff /* This prevents sign extension */ ) + } + + def isAtEnd: Boolean = start >= end + def linkReferences(): Unit = referencesByTargetPosition.foreach { case (position, reference) => + val referencable = + referencablesByPosition.getOrElse(position, throw new Exception(s"No referencable value read at $position")) + + referencable.information = TastyReferencableInformation(Some(reference.referencableId)) + } + + def peek[A](read: TastyReader => A): A = { + + /** + * We have [[reader]] and [[this]] share [[nextReferencableId]], [[referencesByTargetPosition]], and + * [[referencablesByPosition]] values to increase efficiency and reduce allocations. Although the value peeked will + * eventually be re-read, if it's a [[TastyReference]], it should be re-read with the same ID because we cache + * references in [[referencesByTargetPosition]]. + */ + val reader = copy() + val result = read(reader) + + nextReferencableId = reader.nextReferencableId + + result + } + + def readByte(): Byte = { + val result = input(start) + + start += 1 + + result + } + + def readMagicNumber(): Unit = { + val actual = readNBytes(4) + val expected = DottyTastyFormat.header.map(_.toByte) + + if (!java.util.Arrays.equals(actual, expected)) { + throw new Exception( + s"Expected a magic number of ${expected.toList}, but got ${actual.toList}", + ) + } + } + + def readReferencable[A <: TastyReferencable](read: => A): A = { + val position = start + val result = read + + referencablesByPosition(position) = result + + result + } + + def readReference[RelativeTo <: MarkerType, Value](relativeTo: RelativeTo): TastyReference[RelativeTo, Value] = { + val marker = markers.getOrElse(relativeTo, throw new MarkerNotSetException(relativeTo)) + val position = marker.position + readUnsignedInt() + + referencesByTargetPosition + .getOrElseUpdate( + position, { + val result = TastyReference(relativeTo, nextReferencableId) + + nextReferencableId += 1 + + result + }, + ) + .asInstanceOf[TastyReference[RelativeTo, Value]] + } + + def readSignedInt(): SignedInt = readSignedLong().toInt.taggedWith[Signed] + def readSignedLong(): SignedLong = { + var currentByte = readByte() + var result: Long = (currentByte << 1).toByte >> 1 // Sign extend the first byte, using bit 6 as the sign + + while ((currentByte & 0x80) == 0) { + currentByte = readByte() + result = (result << 7) | (currentByte & 0x7f) + } + + result.taggedWith[Signed] + } + + def readUnsignedInt(): UnsignedInt = readUnsignedLong().toInt.taggedWith[Unsigned] + def readUnsignedLong(): UnsignedLong = { + var currentByte = readByte() + var result = 0L + + while { + result = (result << 7) | (currentByte & 0x7f) + + (currentByte & 0x80) == 0 + } do { + currentByte = readByte() + } + + result.taggedWith[Unsigned] + } + + def readUntilEnd[A](read: => A): List[A] = { + val result = readWhile(!isAtEnd)(read) + + assert(start == end, s"Expected to read until $end, but stopped at $start") + + result + } + + def readUtf8String(): String = new String(readNBytes(readUnsignedInt()), StandardCharsets.UTF_8) + def readUuid(): UUID = new UUID(readUncompressedLong(), readUncompressedLong()) + def readWithLength[A](length: Int)(read: TastyReader => A): A = { + val reader = copy(end = start + length) + val result = read(reader) + + assert( + reader.start == reader.end, + s"Given a length of $length, expected to read until $end, but stopped at ${reader.start}", + ) + + start = reader.start + nextReferencableId = reader.nextReferencableId + + result + } + + def readWhile[A](condition: => Boolean)(read: => A): List[A] = { + @tailrec + def withExisting(existing: List[A])(condition: => Boolean)(read: => A): List[A] = { + if (condition) { + withExisting(read +: existing)(condition)(read) + } else { + existing + } + } + + withExisting(List.empty)(condition)(read).reverse + } + + def setMarker(markerType: MarkerType): Unit = markers(markerType) = Marker(start, markerType) +} + +object TastyReader { + def apply(input: Array[Byte]): TastyReader = new TastyReader( + input, + start = 0, + end = input.length, + markers = mutable.Map.empty, + nextReferencableId = 0, + referencesByTargetPosition = mutable.LongMap.empty, + referencablesByPosition = mutable.LongMap.empty, + ) +} diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReferencable.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReferencable.scala new file mode 100644 index 000000000..439f8031c --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReferencable.scala @@ -0,0 +1,15 @@ +package io.bazel.rules_scala.dottyijar.tasty.format + +trait TastyReferencable { + def information: TastyReferencableInformation + def information_=(newInformation: TastyReferencableInformation): Unit +} + +case class TastyReferencableInformation(id: Option[Int] = None) + +object TastyReferencableInformation { + given TastyFormat[TastyReferencableInformation] = new TastyFormat[TastyReferencableInformation] { + def read(reader: TastyReader): TastyReferencableInformation = TastyReferencableInformation() + def write(writer: TastyWriter, value: TastyReferencableInformation): Unit = {} + } +} diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReference.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReference.scala new file mode 100644 index 000000000..6c3c4493e --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReference.scala @@ -0,0 +1,35 @@ +package io.bazel.rules_scala.dottyijar.tasty.format + +import izumi.reflect.Tag + +/** + * References another value in the TASTy file. In their serialized form, [[TastyReference]]s are represented as an + * offset from some marker in the TASTy file. In reality, the only marker is the AST section payload, but I wanted to + * make [[TastyReference]] indifferent to the marker type to enforce a separation between the infrastructure for reading + * to and writing from TASTy files ([[TastyReference]], [[TastyFormat]], [[TastyReader]], and [[TastyWriter]]) and the + * TASTy types. + * + * In their deserialized form, [[TastyReference]]s are assigned IDs that uniquely identify the value they're + * referencing. These values should extend [[TastyReferencable]], which has an `information` field containing this ID. + * We opted to represent references in this way because it makes it far easier to update the values they reference. If, + * instead, we stored the address to which they point, we'd have to update those addresses whenever they change, which + * is very difficult to do in automated fashion. + */ +case class TastyReference[RelativeTo <: MarkerType, Value](relativeTo: RelativeTo, referencableId: Int) + +object TastyReference { + + /** + * [[Value]] must have a [[TastySumType]] because values that have [[TastySumType]]s are the only values whose + * positions we record during writing. See the comment in the implementation of [[TastyFormat.forSumType]] to + * understand why. + */ + given [RelativeTo <: MarkerType: ValueOf: Tag, Value: Tag](using + => TastySumType[Value], + ): TastyFormat[TastyReference[RelativeTo, Value]] = TastyFormat( + reader => reader.readReference(summon[ValueOf[RelativeTo]].value), + (writer, reference) => writer.writeReference(reference), + ) +} + +type TastyAstReference[A] = TastyReference[MarkerType.AstSection.type, A] diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyWriter.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyWriter.scala new file mode 100644 index 000000000..a68f6fb3f --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyWriter.scala @@ -0,0 +1,170 @@ +package io.bazel.rules_scala.dottyijar.tasty.format + +import com.softwaremill.tagging.* +import dotty.tools.tasty.TastyFormat as DottyTastyFormat +import java.nio.charset.StandardCharsets +import java.util.UUID +import scala.collection.mutable + +case class TastyWriter private ( + output: mutable.ArrayBuffer[Byte], + private[format] var start: Int, + private val markers: mutable.Map[MarkerType, Marker], + private val references: mutable.ArrayBuffer[Reference], + private val referencablePositionsById: mutable.LongMap[Int], +) { + private def writeUncompressedLong(long: Long): Unit = + Range(56, -8, -8).foreach(i => writeByte((long >>> i).toByte)) + + private def writeUnsignedLongWithWidth(long: Long, width: Int): Unit = { + Range(width * 7, 7, -7).foreach(remainingBits => writeByte(((long >>> (remainingBits - 7)) & 0x7f).toByte)) + + writeByte(((long & 0x7f) | 0x80).toByte) + } + + def fillInReferences(): Unit = { + references.foreach { reference => + val markerType = reference.reference.relativeTo + val marker = markers.getOrElse(markerType, throw new MarkerNotSetException(markerType)) + val relativePosition = referencablePositionsById(reference.reference.referencableId) - marker.position + + copy(start = reference.position).writeUnsignedLongWithWidth(relativePosition, TastyWriter.referenceWidth) + } + + references.clear() + } + + def setMarker(markerType: MarkerType): Unit = markers(markerType) = Marker(start, markerType) + def toArray: Array[Byte] = output.toArray + def writeByte(byte: Byte): Unit = { + if (start == output.length) { + output += byte + } else { + output(start) = byte + } + + start += 1 + } + + def writeBytes(bytes: Array[Byte]): Unit = { + output ++= bytes + start += bytes.length + } + + def writeMagicNumber(): Unit = writeBytes(DottyTastyFormat.header.map(_.toByte)) + def writeReferencable(value: TastyReferencable)(write: => Unit): Unit = { + value.information.id.foreach(referencablePositionsById(_) = start) + + write + } + + def writeReference(reference: TastyReference[? <: MarkerType, ?]): Unit = { + references += Reference(reference, start) + + writeUnsignedLongWithWidth(0, TastyWriter.referenceWidth) + } + + def writeSignedInt(int: SignedInt): Unit = writeSignedLong(int.toLong.taggedWith[Signed]) + + /** + * This method is copied from this one in Dotty: + * [[https://github.com/scala/scala3/blob/4d3f7576ccae724e6f83d2f3d68bd4c4e1dd5a14/tasty/src/dotty/tools/tasty/TastyBuffer.scala]] + * + * I can't, for the life of me, understand how it works. + */ + def writeSignedLong(long: SignedLong): Unit = { + def writePrefix(long: Long): Unit = { + val prefix = long >> 7 + + if (prefix != 0L - ((long >> 6) & 1)) { + writePrefix(prefix) + } + + writeByte((long & 0x7f).toByte) + } + + val prefix = long >> 7 + + if (prefix != 0L - ((long >> 6) & 1)) { + writePrefix(prefix) + } + + writeByte(((long & 0x7f) | 0x80).toByte) + } + + def writeUnsignedInt(int: UnsignedInt): Unit = writeUnsignedLong(int.toLong.taggedWith[Unsigned]) + def writeUnsignedLong(long: UnsignedLong): Unit = { + def writePrefix(long: Long): Unit = { + val prefix = long >> 7 + + if (prefix != 0) { + writePrefix(prefix) + } + + writeByte((long & 0x7f).toByte) + } + + val prefix = long >> 7 + + if (prefix != 0) { + writePrefix(prefix) + } + + writeByte(((long & 0x7f) | 0x80).toByte) + } + + def writeUtf8String(string: String): Unit = { + val bytes = string.getBytes(StandardCharsets.UTF_8) + + writeUnsignedInt(bytes.length.taggedWith[Unsigned]) + writeBytes(bytes) + } + + def writeUuid(uuid: UUID): Unit = { + writeUncompressedLong(uuid.getMostSignificantBits) + writeUncompressedLong(uuid.getLeastSignificantBits) + } + + def writeWithLengthPrefixed(write: TastyWriter => Unit): Unit = { + val bufferWriter = copy( + output = mutable.ArrayBuffer.empty, + start = 0, + markers = mutable.Map.empty, + references = mutable.ArrayBuffer.empty, + referencablePositionsById = mutable.LongMap.empty, + ) + + write(bufferWriter) + + val buffer = bufferWriter.toArray + + writeUnsignedInt(buffer.length.taggedWith[Unsigned]) + + markers ++= bufferWriter.markers.view.map { case (markerType, marker) => + (markerType, marker.copy(position = start + marker.position)) + } + + references ++= + bufferWriter.references.view.map(reference => reference.copy(position = start + reference.position)) + + referencablePositionsById ++= + bufferWriter.referencablePositionsById.view.map { case (id, position) => (id, start + position) } + + writeBytes(buffer) + } +} + +object TastyWriter { + private val referenceWidth = 4 + + def empty: TastyWriter = new TastyWriter( + output = mutable.ArrayBuffer.empty, + start = 0, + markers = mutable.Map.empty, + references = mutable.ArrayBuffer.empty, + referencablePositionsById = mutable.LongMap.empty, + ) +} + +private case class ReferencedValue(relativeTo: MarkerType, value: Any) +private case class Reference(reference: TastyReference[? <: MarkerType, ?], position: Int) diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/package.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/package.scala new file mode 100644 index 000000000..ebdea1dcd --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/package.scala @@ -0,0 +1,15 @@ +package io.bazel.rules_scala.dottyijar.tasty + +import com.softwaremill.tagging.* + +package object format { + private val isDebuggingEnabled: Boolean = Option(System.getProperty("DEBUG_TASTYFORMAT")).contains("true") + + trait Signed + trait Unsigned + + type SignedInt = Int @@ Signed + type SignedLong = Long @@ Signed + type UnsignedInt = Int @@ Unsigned + type UnsignedLong = Long @@ Unsigned +} diff --git a/third_party/repositories/scala_2_11.bzl b/third_party/repositories/scala_2_11.bzl index 0815bfa3e..e346eaf59 100644 --- a/third_party/repositories/scala_2_11.bzl +++ b/third_party/repositories/scala_2_11.bzl @@ -110,6 +110,10 @@ artifacts = { "artifact": "com.typesafe:config:1.2.1", "sha256": "c160fbd78f51a0c2375a794e435ce2112524a6871f64d0331895e9e26ee8b9ee", }, + "commons_io_commons_io": { + "artifact": "commons-io:commons-io:2.18.0", + "sha256": "f3ca0f8d63c40e23a56d54101c60d5edee136b42d84bfb85bc7963093109cf8b", + }, "io_bazel_rules_scala_failureaccess": { "artifact": "com.google.guava:failureaccess:1.0.2", "sha256": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064", diff --git a/third_party/repositories/scala_2_12.bzl b/third_party/repositories/scala_2_12.bzl index 27fe77709..86566751c 100644 --- a/third_party/repositories/scala_2_12.bzl +++ b/third_party/repositories/scala_2_12.bzl @@ -116,6 +116,10 @@ artifacts = { "artifact": "com.typesafe:config:1.4.1", "sha256": "4c0aa7e223c75c8840c41fc183d4cd3118140a1ee503e3e08ce66ed2794c948f", }, + "commons_io_commons_io": { + "artifact": "commons-io:commons-io:2.18.0", + "sha256": "f3ca0f8d63c40e23a56d54101c60d5edee136b42d84bfb85bc7963093109cf8b", + }, "dev_dirs_directories": { "artifact": "dev.dirs:directories:26", "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", diff --git a/third_party/repositories/scala_2_13.bzl b/third_party/repositories/scala_2_13.bzl index d96e9c5a1..9e888ed25 100644 --- a/third_party/repositories/scala_2_13.bzl +++ b/third_party/repositories/scala_2_13.bzl @@ -120,6 +120,10 @@ artifacts = { "artifact": "com.typesafe:config:1.4.1", "sha256": "4c0aa7e223c75c8840c41fc183d4cd3118140a1ee503e3e08ce66ed2794c948f", }, + "commons_io_commons_io": { + "artifact": "commons-io:commons-io:2.18.0", + "sha256": "f3ca0f8d63c40e23a56d54101c60d5edee136b42d84bfb85bc7963093109cf8b", + }, "dev_dirs_directories": { "artifact": "dev.dirs:directories:26", "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", diff --git a/third_party/repositories/scala_3_1.bzl b/third_party/repositories/scala_3_1.bzl index 767b370e5..1adfe737f 100644 --- a/third_party/repositories/scala_3_1.bzl +++ b/third_party/repositories/scala_3_1.bzl @@ -115,6 +115,13 @@ artifacts = { "@io_bazel_rules_scala_scala_library_2", ], }, + "com_softwaremill_common_tagging": { + "artifact": "com.softwaremill.common:tagging_3:2.3.5", + "sha256": "06d2b7578fcd272791a20dbe8e2f279162733dcd9293b38a87060dc5080d0382", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "com_twitter__scalding_date": { "testonly": True, "artifact": "com.twitter:scalding-date_2.13:0.17.0", @@ -124,10 +131,29 @@ artifacts = { "artifact": "com.typesafe:config:1.4.1", "sha256": "4c0aa7e223c75c8840c41fc183d4cd3118140a1ee503e3e08ce66ed2794c948f", }, + "commons_io_commons_io": { + "artifact": "commons-io:commons-io:2.18.0", + "sha256": "f3ca0f8d63c40e23a56d54101c60d5edee136b42d84bfb85bc7963093109cf8b", + }, "dev_dirs_directories": { "artifact": "dev.dirs:directories:26", "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", }, + "dev_zio_izumi_reflect": { + "artifact": "dev.zio:izumi-reflect_3:2.3.10", + "sha256": "9f4e23968f00eaea2132337b94faa9e52cc7f159cce319c2f112c36d2c29d55d", + "deps": [ + "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded", + "@io_bazel_rules_scala_scala_library", + ], + }, + "dev_zio_izumi_reflect_thirdparty_boopickle_shaded": { + "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.3.10", + "sha256": "3c36e0e0c6bc18f5c1a0ffbf820c3783b472690afc56105e9c7c8b81357b5661", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "io_bazel_rules_scala_failureaccess": { "artifact": "com.google.guava:failureaccess:1.0.2", "sha256": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064", @@ -239,8 +265,8 @@ artifacts = { "sha256": "dca9bcd395deffca77c1d3919b4cc998234025059a892b10c3674c9a37d6dc9f", }, "io_bazel_rules_scala_scala_library": { - "artifact": "org.scala-lang:scala3-library_3:3.1.3", - "sha256": "1ac79970d94a1762ce6af4208820b4fa4c70093409decaad85c69d8b5f46e422", + "artifact": "org.scala-lang:scala3-library_3:3.3.3", + "sha256": "16fe064f1373ed6f098d3d9f812a398ed5075db4bf2721c04e630502cb352816", "deps": [ "@io_bazel_rules_scala_scala_library_2", ], diff --git a/third_party/repositories/scala_3_2.bzl b/third_party/repositories/scala_3_2.bzl index 0429be002..12cbac39c 100644 --- a/third_party/repositories/scala_3_2.bzl +++ b/third_party/repositories/scala_3_2.bzl @@ -115,6 +115,13 @@ artifacts = { "@io_bazel_rules_scala_scala_library_2", ], }, + "com_softwaremill_common_tagging": { + "artifact": "com.softwaremill.common:tagging_3:2.3.5", + "sha256": "06d2b7578fcd272791a20dbe8e2f279162733dcd9293b38a87060dc5080d0382", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "com_twitter__scalding_date": { "testonly": True, "artifact": "com.twitter:scalding-date_2.13:0.17.0", @@ -124,10 +131,29 @@ artifacts = { "artifact": "com.typesafe:config:1.4.1", "sha256": "4c0aa7e223c75c8840c41fc183d4cd3118140a1ee503e3e08ce66ed2794c948f", }, + "commons_io_commons_io": { + "artifact": "commons-io:commons-io:2.18.0", + "sha256": "f3ca0f8d63c40e23a56d54101c60d5edee136b42d84bfb85bc7963093109cf8b", + }, "dev_dirs_directories": { "artifact": "dev.dirs:directories:26", "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", }, + "dev_zio_izumi_reflect": { + "artifact": "dev.zio:izumi-reflect_3:2.3.10", + "sha256": "9f4e23968f00eaea2132337b94faa9e52cc7f159cce319c2f112c36d2c29d55d", + "deps": [ + "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded", + "@io_bazel_rules_scala_scala_library", + ], + }, + "dev_zio_izumi_reflect_thirdparty_boopickle_shaded": { + "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.3.10", + "sha256": "3c36e0e0c6bc18f5c1a0ffbf820c3783b472690afc56105e9c7c8b81357b5661", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "io_bazel_rules_scala_failureaccess": { "artifact": "com.google.guava:failureaccess:1.0.2", "sha256": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064", @@ -239,8 +265,8 @@ artifacts = { "sha256": "f07bab6250d718613f0f8250cc61cc23217c4fd84c410c3af43b8098fff31f69", }, "io_bazel_rules_scala_scala_library": { - "artifact": "org.scala-lang:scala3-library_3:3.2.2", - "sha256": "f96317c57a5beae2cb16607d2b99cba7b136a96416e736966e5955e6608d868b", + "artifact": "org.scala-lang:scala3-library_3:3.3.3", + "sha256": "16fe064f1373ed6f098d3d9f812a398ed5075db4bf2721c04e630502cb352816", "deps": [ "@io_bazel_rules_scala_scala_library_2", ], diff --git a/third_party/repositories/scala_3_3.bzl b/third_party/repositories/scala_3_3.bzl index 424eb1a67..2d67af6e3 100644 --- a/third_party/repositories/scala_3_3.bzl +++ b/third_party/repositories/scala_3_3.bzl @@ -115,6 +115,13 @@ artifacts = { "@io_bazel_rules_scala_scala_library_2", ], }, + "com_softwaremill_common_tagging": { + "artifact": "com.softwaremill.common:tagging_3:2.3.5", + "sha256": "06d2b7578fcd272791a20dbe8e2f279162733dcd9293b38a87060dc5080d0382", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "com_twitter__scalding_date": { "testonly": True, "artifact": "com.twitter:scalding-date_2.13:0.17.0", @@ -124,10 +131,29 @@ artifacts = { "artifact": "com.typesafe:config:1.4.1", "sha256": "4c0aa7e223c75c8840c41fc183d4cd3118140a1ee503e3e08ce66ed2794c948f", }, + "commons_io_commons_io": { + "artifact": "commons-io:commons-io:2.18.0", + "sha256": "f3ca0f8d63c40e23a56d54101c60d5edee136b42d84bfb85bc7963093109cf8b", + }, "dev_dirs_directories": { "artifact": "dev.dirs:directories:26", "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", }, + "dev_zio_izumi_reflect": { + "artifact": "dev.zio:izumi-reflect_3:2.3.10", + "sha256": "9f4e23968f00eaea2132337b94faa9e52cc7f159cce319c2f112c36d2c29d55d", + "deps": [ + "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded", + "@io_bazel_rules_scala_scala_library", + ], + }, + "dev_zio_izumi_reflect_thirdparty_boopickle_shaded": { + "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.3.10", + "sha256": "3c36e0e0c6bc18f5c1a0ffbf820c3783b472690afc56105e9c7c8b81357b5661", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "io_bazel_rules_scala_failureaccess": { "artifact": "com.google.guava:failureaccess:1.0.2", "sha256": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064", diff --git a/third_party/repositories/scala_3_4.bzl b/third_party/repositories/scala_3_4.bzl index 59ad195b3..2d1f75bef 100644 --- a/third_party/repositories/scala_3_4.bzl +++ b/third_party/repositories/scala_3_4.bzl @@ -115,6 +115,13 @@ artifacts = { "@io_bazel_rules_scala_scala_library_2", ], }, + "com_softwaremill_common_tagging": { + "artifact": "com.softwaremill.common:tagging_3:2.3.5", + "sha256": "06d2b7578fcd272791a20dbe8e2f279162733dcd9293b38a87060dc5080d0382", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "com_twitter__scalding_date": { "testonly": True, "artifact": "com.twitter:scalding-date_2.13:0.17.0", @@ -124,10 +131,29 @@ artifacts = { "artifact": "com.typesafe:config:1.4.1", "sha256": "4c0aa7e223c75c8840c41fc183d4cd3118140a1ee503e3e08ce66ed2794c948f", }, + "commons_io_commons_io": { + "artifact": "commons-io:commons-io:2.18.0", + "sha256": "f3ca0f8d63c40e23a56d54101c60d5edee136b42d84bfb85bc7963093109cf8b", + }, "dev_dirs_directories": { "artifact": "dev.dirs:directories:26", "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", }, + "dev_zio_izumi_reflect": { + "artifact": "dev.zio:izumi-reflect_3:2.3.10", + "sha256": "9f4e23968f00eaea2132337b94faa9e52cc7f159cce319c2f112c36d2c29d55d", + "deps": [ + "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded", + "@io_bazel_rules_scala_scala_library", + ], + }, + "dev_zio_izumi_reflect_thirdparty_boopickle_shaded": { + "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.3.10", + "sha256": "3c36e0e0c6bc18f5c1a0ffbf820c3783b472690afc56105e9c7c8b81357b5661", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "io_bazel_rules_scala_failureaccess": { "artifact": "com.google.guava:failureaccess:1.0.2", "sha256": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064", diff --git a/third_party/repositories/scala_3_5.bzl b/third_party/repositories/scala_3_5.bzl index bc584e6a5..df626abfc 100644 --- a/third_party/repositories/scala_3_5.bzl +++ b/third_party/repositories/scala_3_5.bzl @@ -115,6 +115,13 @@ artifacts = { "@io_bazel_rules_scala_scala_library_2", ], }, + "com_softwaremill_common_tagging": { + "artifact": "com.softwaremill.common:tagging_3:2.3.5", + "sha256": "06d2b7578fcd272791a20dbe8e2f279162733dcd9293b38a87060dc5080d0382", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "com_twitter__scalding_date": { "testonly": True, "artifact": "com.twitter:scalding-date_2.13:0.17.0", @@ -124,10 +131,29 @@ artifacts = { "artifact": "com.typesafe:config:1.4.1", "sha256": "4c0aa7e223c75c8840c41fc183d4cd3118140a1ee503e3e08ce66ed2794c948f", }, + "commons_io_commons_io": { + "artifact": "commons-io:commons-io:2.18.0", + "sha256": "f3ca0f8d63c40e23a56d54101c60d5edee136b42d84bfb85bc7963093109cf8b", + }, "dev_dirs_directories": { "artifact": "dev.dirs:directories:26", "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", }, + "dev_zio_izumi_reflect": { + "artifact": "dev.zio:izumi-reflect_3:2.3.10", + "sha256": "9f4e23968f00eaea2132337b94faa9e52cc7f159cce319c2f112c36d2c29d55d", + "deps": [ + "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded", + "@io_bazel_rules_scala_scala_library", + ], + }, + "dev_zio_izumi_reflect_thirdparty_boopickle_shaded": { + "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.3.10", + "sha256": "3c36e0e0c6bc18f5c1a0ffbf820c3783b472690afc56105e9c7c8b81357b5661", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "io_bazel_rules_scala_failureaccess": { "artifact": "com.google.guava:failureaccess:1.0.2", "sha256": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064", diff --git a/third_party/repositories/scala_3_6.bzl b/third_party/repositories/scala_3_6.bzl index 79ae1e825..42d8175c2 100644 --- a/third_party/repositories/scala_3_6.bzl +++ b/third_party/repositories/scala_3_6.bzl @@ -115,6 +115,13 @@ artifacts = { "@io_bazel_rules_scala_scala_library_2", ], }, + "com_softwaremill_common_tagging": { + "artifact": "com.softwaremill.common:tagging_3:2.3.5", + "sha256": "06d2b7578fcd272791a20dbe8e2f279162733dcd9293b38a87060dc5080d0382", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "com_twitter__scalding_date": { "testonly": True, "artifact": "com.twitter:scalding-date_2.13:0.17.0", @@ -124,10 +131,29 @@ artifacts = { "artifact": "com.typesafe:config:1.4.1", "sha256": "4c0aa7e223c75c8840c41fc183d4cd3118140a1ee503e3e08ce66ed2794c948f", }, + "commons_io_commons_io": { + "artifact": "commons-io:commons-io:2.18.0", + "sha256": "f3ca0f8d63c40e23a56d54101c60d5edee136b42d84bfb85bc7963093109cf8b", + }, "dev_dirs_directories": { "artifact": "dev.dirs:directories:26", "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", }, + "dev_zio_izumi_reflect": { + "artifact": "dev.zio:izumi-reflect_3:2.3.10", + "sha256": "9f4e23968f00eaea2132337b94faa9e52cc7f159cce319c2f112c36d2c29d55d", + "deps": [ + "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded", + "@io_bazel_rules_scala_scala_library", + ], + }, + "dev_zio_izumi_reflect_thirdparty_boopickle_shaded": { + "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.3.10", + "sha256": "3c36e0e0c6bc18f5c1a0ffbf820c3783b472690afc56105e9c7c8b81357b5661", + "deps": [ + "@io_bazel_rules_scala_scala_library", + ], + }, "io_bazel_rules_scala_failureaccess": { "artifact": "com.google.guava:failureaccess:1.0.2", "sha256": "8a8f81cf9b359e3f6dfa691a1e776985c061ef2f223c9b2c80753e1b458e8064", From 4e90f0360382f5d4a15d5da2f1328c9d1884e06f Mon Sep 17 00:00:00 2001 From: Jaden Peterson Date: Mon, 6 Jan 2025 14:43:25 -0500 Subject: [PATCH 4/7] Wrote an early version of dottyijar Currently, it just strips private `val`s and `def`s and definition values, but I plan on making it more aggresive in the future. --- scala/private/macros/scala_repositories.bzl | 1 + scripts/create_repository.py | 1 + .../io/bazel/rules_scala/dottyijar/BUILD | 42 ++ .../rules_scala/dottyijar/DottyIjar.scala | 119 +++++ .../rules_scala/dottyijar/TastyUpdater.scala | 254 ++++++++++ .../dottyijar/TastyUpdater.spec.scala | 41 ++ .../bazel/rules_scala/dottyijar/tasty/BUILD | 29 +- .../dottyijar/tasty/Tasty.spec.scala | 38 +- .../dottyijar/tasty/TastyDereferencer.scala | 25 + .../dottyijar/tasty/TastyElement.scala | 450 ++++++++++++++++++ .../tasty/TastySpecification.test.scala | 40 ++ .../dottyijar/tastyinspector/BUILD | 24 + .../tastyinspector/TastyInspector.scala | 106 +++++ third_party/repositories/scala_3_1.bzl | 7 + third_party/repositories/scala_3_2.bzl | 7 + third_party/repositories/scala_3_3.bzl | 7 + third_party/repositories/scala_3_4.bzl | 7 + third_party/repositories/scala_3_5.bzl | 7 + third_party/repositories/scala_3_6.bzl | 7 + 19 files changed, 1171 insertions(+), 41 deletions(-) create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/BUILD create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/DottyIjar.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/TastyUpdater.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/TastyUpdater.spec.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/TastyDereferencer.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/TastyElement.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/TastySpecification.test.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tastyinspector/BUILD create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tastyinspector/TastyInspector.scala diff --git a/scala/private/macros/scala_repositories.bzl b/scala/private/macros/scala_repositories.bzl index cd06bb27d..89302665e 100644 --- a/scala/private/macros/scala_repositories.bzl +++ b/scala/private/macros/scala_repositories.bzl @@ -189,6 +189,7 @@ def _artifact_ids(scala_version): "io_bazel_rules_scala_scala_library_2", "io_bazel_rules_scala_scala_reflect_2", "io_bazel_rules_scala_scala_tasty_core", + "io_bazel_rules_scala_scala_tasty_inspector", "org_jline_jline_native", "org_jline_jline_reader", "org_jline_jline_terminal", diff --git a/scripts/create_repository.py b/scripts/create_repository.py index 319976de3..21748119c 100755 --- a/scripts/create_repository.py +++ b/scripts/create_repository.py @@ -137,6 +137,7 @@ def select_root_artifacts(scala_version, scala_major, is_scala_3) -> List[str]: f'org.scala-lang:scala3-compiler_3:{scala_version}', f'org.scala-lang:scala3-library_3:{scala_version}', f'org.scala-lang:scala3-interfaces:{scala_version}', + f'org.scala-lang:scala3-tasty-inspector_3:{scala_version}', f'org.scala-lang:tasty-core_3:{scala_version}', f'org.scala-sbt:compiler-interface:{SBT_COMPILER_INTERFACE_VERSION}', f'org.scala-sbt:util-interface:{SBT_UTIL_INTERFACE_VERSION}', diff --git a/src/scala/io/bazel/rules_scala/dottyijar/BUILD b/src/scala/io/bazel/rules_scala/dottyijar/BUILD new file mode 100644 index 000000000..9b6d92d45 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/BUILD @@ -0,0 +1,42 @@ +load("//scala:scala.bzl", "scala_library_for_plugin_bootstrapping", "scala_specs2_junit_test") +load("@rules_java//java:defs.bzl", "java_binary") + +scala_library_for_plugin_bootstrapping( + name = "dottyijar-library", + srcs = glob( + ["*.scala"], + exclude = ["*.spec.scala"], + ), + scala_version = "3.6.2", + visibility = ["//visibility:public"], + deps = [ + "//src/scala/io/bazel/rules_scala/dottyijar/tasty", + "//src/scala/io/bazel/rules_scala/dottyijar/tasty/format", + "@com_softwaremill_common_tagging_3_6_2", + "@dev_zio_izumi_reflect_3_6_2", + ], +) + +# TODO: Eventually, we should make a bootstrapping toolchain so we don't have to create a `*_for_plugin_bootstrapping` equivalent for each Scala rule. +java_binary( + name = "dottyijar", + main_class = "io.bazel.rules_scala.dottyijar.DottyIjar", + visibility = ["//visibility:public"], + runtime_deps = [":dottyijar-library"], +) + +scala_specs2_junit_test( + name = "specs", + srcs = glob(["*.spec.scala"]), + scala_version = "3.6.2", + suffixes = ["Spec"], + deps = [ + ":dottyijar-library", + "//src/scala/io/bazel/rules_scala/dottyijar/tasty:test", + "@io_bazel_rules_scala_org_specs2_specs2_common_3_6_2", + "@io_bazel_rules_scala_org_specs2_specs2_core_3_6_2", + "@io_bazel_rules_scala_org_specs2_specs2_junit", + "@io_bazel_rules_scala_org_specs2_specs2_matcher_3_6_2", + "@io_bazel_rules_scala_scala_tasty_inspector_3_6_2", + ], +) diff --git a/src/scala/io/bazel/rules_scala/dottyijar/DottyIjar.scala b/src/scala/io/bazel/rules_scala/dottyijar/DottyIjar.scala new file mode 100644 index 000000000..3d0b721a9 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/DottyIjar.scala @@ -0,0 +1,119 @@ +package io.bazel.rules_scala.dottyijar + +import io.bazel.rules_scala.dottyijar.tasty.Tasty +import io.bazel.rules_scala.dottyijar.tasty.format.{TastyFormat, TastyReader, TastyWriter} +import java.io.FileOutputStream +import java.nio.file.{Path, Paths} +import java.nio.file.attribute.FileTime +import java.util.zip.{ZipEntry, ZipFile, ZipOutputStream} +import scala.jdk.CollectionConverters.* + +object DottyIjar { + private def writeInterfaceJar(inputJar: ZipFile, outputStream: ZipOutputStream): Unit = { + def copyEntryWithContent(entry: ZipEntry, content: Array[Byte]): Unit = { + val newEntry = new ZipEntry(entry.getName) + + newEntry.setCreationTime(FileTime.fromMillis(0)) + newEntry.setLastAccessTime(FileTime.fromMillis(0)) + newEntry.setLastModifiedTime(FileTime.fromMillis(0)) + + outputStream.putNextEntry(newEntry) + outputStream.write(content, 0, content.length) + } + + def copyEntry(entry: ZipEntry): Unit = copyEntryWithContent(entry, inputJar.getInputStream(entry).readAllBytes()) + + outputStream.setComment(inputJar.getComment) + outputStream.setLevel(0) + + val entryNames = inputJar.entries.asScala.map(_.getName).toSet + + inputJar.entries.asScala.foreach { + case entry if entry.getName.startsWith("META-INF/") => copyEntry(entry) + case entry if entry.getName.endsWith(".class") => + val i = entry.getName.lastIndexOf('/') + val directory = entry.getName.slice(0, i) + val filename = entry.getName.slice(i + 1, entry.getName.length) + val j = filename.indexOf("$") + val tastyFileBaseName = if (j == -1) filename.stripSuffix(".class") else filename.slice(0, j) + + if (!entryNames(s"$directory/$tastyFileBaseName.tasty")) { + copyEntry(entry) + } + + case entry if entry.getName.endsWith(".tasty") => + val content = inputJar.getInputStream(entry).readAllBytes() + val updatedContent = TastyUpdater.updateTastyFile(content) + + copyEntryWithContent(entry, updatedContent) + + case entry => copyEntry(entry) + } + } + + def main(arguments: Array[String]): Unit = Arguments + .parseArguments(arguments) + .fold( + println, + arguments => { + val inputJar = new ZipFile(arguments.inputPath.toFile) + + try { + val outputStream = new ZipOutputStream(new FileOutputStream(arguments.outputPath.toFile)) + + try { + writeInterfaceJar(inputJar, outputStream) + } finally { + outputStream.close() + } + } finally { + inputJar.close() + } + }, + ) +} + +private case class Arguments(inputPath: Path, outputPath: Path) + +object Arguments { + def parseArguments(arguments: Array[String]): Either[String, Arguments] = arguments + .foldLeft[Either[String, UnvalidatedArguments]](Right(UnvalidatedArguments())) { + case (unvalidatedArguments, argument) => + unvalidatedArguments.flatMap { unvalidatedArguments => + argument match { + case "-h" | "--help" => + Left( + """dottyijar removes information from Scala 3 JARs that aren't needed for compilation. + | + |Usage: + | dottyijar + | dottyijar -h | --help + | + |Options: + | -h --help Show this screen.""".stripMargin, + ) + + case _ => + lazy val path = Paths.get(argument) + + if (unvalidatedArguments.inputPath.isEmpty) { + Right(unvalidatedArguments.copy(inputPath = Some(path))) + } else if (unvalidatedArguments.outputPath.isEmpty) { + Right(unvalidatedArguments.copy(outputPath = Some(path))) + } else { + Left(s"Unexpected argument: $argument") + } + } + } + } + .flatMap(_.validate) +} + +private case class UnvalidatedArguments(inputPath: Option[Path] = None, outputPath: Option[Path] = None) { + def validate: Either[String, Arguments] = ( + for { + inputPath <- inputPath + outputPath <- outputPath + } yield Arguments(inputPath, outputPath) + ).toRight("Please provide paths to the input and output JARs.") +} diff --git a/src/scala/io/bazel/rules_scala/dottyijar/TastyUpdater.scala b/src/scala/io/bazel/rules_scala/dottyijar/TastyUpdater.scala new file mode 100644 index 000000000..251c48dc6 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/TastyUpdater.scala @@ -0,0 +1,254 @@ +package io.bazel.rules_scala.dottyijar + +import com.softwaremill.tagging.* +import java.util.UUID +import io.bazel.rules_scala.dottyijar.tasty.* +import io.bazel.rules_scala.dottyijar.tasty.format.{MarkerType, TastyFormat, TastyReader, TastyReference, TastyWriter, Unsigned} +import scala.collection.mutable + +/** + * This class is the meat of dottyijar. It's responsible for transforming the read TASTy file into the one written by + * dottyijar. Currently all it does is: + * - Clear the TASTy UUID, positions section, and comments section + * - Remove all `val` and `def` definitions that are private and not inline + * - Replace the values of all `val` and `def` definitions and class parent arguments with `???` + * - Remove unused names from the name table + */ +private class ContextualTastyUpdater(oldTasty: Tasty) { + private var nextNameIndex = oldTasty.nameTable.names.length + private val addedNames = mutable.ArrayBuffer.empty[TastyName] + private def getOrCreateNameReference(name: TastyName): TastyNameReference = oldTasty.nameTable.names.zipWithIndex + .collectFirst { case (`name`, i) => TastyNameReference(i.taggedWith[Unsigned]) } + .getOrElse( + TastyNameReference( + { + val i = nextNameIndex + + nextNameIndex += 1 + addedNames += name + + i + }.taggedWith[Unsigned], + ), + ) + + private lazy val scalaNameReference = getOrCreateNameReference(TastyName.Simple("scala")) + private lazy val predefNameReference = getOrCreateNameReference(TastyName.Simple("Predef")) + private lazy val `???nameReference` = getOrCreateNameReference(TastyName.Simple("???")) + private lazy val `???identifier` = TastyTerm.Identifier( + `???nameReference`, + TastyType.NonLocalReference(predefNameReference, TastyType.PackageReference(scalaNameReference)), + ) + + private def updateAstsSection( + section: TastySection[TastySectionPayload.Asts], + ): TastySection[TastySectionPayload.Asts] = + section.copy(payload = updateAstsSectionPayload(section.payload)) + + private def updateAstsSectionPayload(payload: TastySectionPayload.Asts): TastySectionPayload.Asts = + TastySectionPayload.Asts(payload.topLevelStatements.flatMap(updateTopLevelStatement)) + + private def updateStatement(statement: TastyStatement): Option[TastyStatement] = + statement match { + case _: TastyTerm => None + case definition: TastyValOrDefDefinition => updateValOrDefDefinition(definition) + case definition: TastyTypeDefinition => Some(updateTypeDefinition(definition)) + case _ => Some(statement) + } + + private def updateTemplate(template: TastyTemplate): TastyTemplate = + template.copy( + parents = template.parents.map { + case apply: TastyTerm.Apply => apply.copy(arguments = apply.arguments.map(_ => `???identifier`)) + case block: TastyTerm.Block => block.copy(statements = block.statements.flatMap(updateStatement)) + case parent => parent + }, + statements = template.statements.flatMap(updateStatement), + ) + + private def updateTopLevelStatement(statement: TastyTopLevelStatement): Option[TastyTopLevelStatement] = + statement match { + case statement: TastyPackageStatement => + Some(statement.copy(topLevelStatements = statement.topLevelStatements.flatMap(updateTopLevelStatement))) + + case statement: TastyStatement => updateStatement(statement) + } + + private def updateTypeDefinition(definition: TastyTypeDefinition): TastyTypeDefinition = definition match { + case TastyTypeDefinition(name, template: TastyTemplate, information, modifiers) => + TastyTypeDefinition(name, updateTemplate(template), information, modifiers) + + case _ => definition + } + + private def updateValOrDefDefinition(definition: TastyValOrDefDefinition): Option[TastyValOrDefDefinition] = { + val modifiers = definition match { + case definition: TastyValOrDefDefinition.Def => definition.modifiers + case definition: TastyValOrDefDefinition.Val => definition.modifiers + } + + val isObject = modifiers.exists { + case _: TastyModifier.Object => true + case _ => false + } + + lazy val isPrivate = modifiers.exists { + case _: TastyModifier.Private => true + case _ => false + } + + lazy val isInline = modifiers.exists { + case _: TastyModifier.Inline => true + case _ => false + } + + if (!isObject && isPrivate && !isInline) { + None + } else { + definition match { + case TastyValOrDefDefinition.Val(name, tastyType, Some(_), modifiers, information) => + Some(TastyValOrDefDefinition.Val(name, tastyType, Some(`???identifier`), modifiers, information)) + + case TastyValOrDefDefinition.Def(name, parameters, tastyType, Some(_), modifiers, information) => + Some(TastyValOrDefDefinition.Def(name, parameters, tastyType, Some(`???identifier`), modifiers, information)) + + case _ => Some(definition) + } + } + } + + lazy val updatedTasty: Tasty = { + val updatedAstsSection = oldTasty.astsSection.map(updateAstsSection) + val updatedNameTable = TastyNameTable(oldTasty.nameTable.names ++ addedNames) + + oldTasty.copy( + uuid = new UUID(0, 0), + nameTable = updatedNameTable, + astsSection = updatedAstsSection, + positionsSection = None, + commentsSection = None, + ) + } +} + +private object TastyUpdater { + private def getUsedNameIndices(tasty: Tasty): Set[Int] = { + val usedInSections = tasty.astsSection.map(getUsedNameIndicesInSection).getOrElse(Iterable.empty) ++ + tasty.positionsSection.map(getUsedNameIndicesInSection).getOrElse(Iterable.empty) ++ + tasty.attributesSection.map(getUsedNameIndicesInSection).getOrElse(Iterable.empty) + + // I'm pretty sure the graph of name dependencies is acyclic, so a standard depth-first search should work + val result = mutable.Set.empty[Int] + val stack = mutable.ArrayBuffer.from(usedInSections) + + while (stack.nonEmpty) { + val i = stack.remove(stack.length - 1) + + if (!result(i)) { + result += i + + TastyElement.collect(tasty.nameTable.names(i)) { case TastyNameReference(j, _) => stack += j }.foreach { _ => } + } + } + + result.toSet + } + + private def getUsedNameIndicesInSection[A <: TastySectionPayload]( + section: TastySection[A], + )(using TastyElement[TastySection[A]]): Iterable[Int] = + TastyElement.collect(section) { case TastyNameReference(i, _) => i } + + private def removeDanglingSharedValues[A: TastyElement]( + node: A, + handledReferences: mutable.Set[TastyReference[? <: MarkerType, ?]], + oldTastyDereferencer: TastyDereferencer, + updatedTastyDeferencer: TastyDereferencer, + ): A = { + val sharedReference = node match { + case TastyPath.Shared(reference, _) => Some(reference) + case TastyTerm.Shared(reference, _) => Some(reference) + case TastyType.Shared(reference, _) => Some(reference) + case TastyTypeTree.Shared(reference, _) => Some(reference) + case _ => None + } + + sharedReference + .map { reference => + if (handledReferences(reference) || updatedTastyDeferencer.isValidReference(reference)) { + node + } else { + handledReferences += reference + + /** + * None of the AST nodes reference types more specific than [[TastyPath]], [[TastyTerm]], [[TastyType]], or + * [[TastyTypeTree]], but the compiler doesn't know that, so we cast the updated value to [[A]]. + */ + val dereferenced = + oldTastyDereferencer.dereference(reference.asInstanceOf[TastyReference[? <: MarkerType, A]]) + + removeDanglingSharedValues(dereferenced, handledReferences, oldTastyDereferencer, updatedTastyDeferencer) + } + } + .getOrElse( + TastyElement.map(node)( + [B] => + child => removeDanglingSharedValues(child, handledReferences, oldTastyDereferencer, updatedTastyDeferencer), + ), + ) + } + + /** + * Remove shared paths, terms, types, and type trees whose referenced values don't exist in a given [[Tasty]]. I + * believe the compiler shares these nodes to save space, even when the shared values aren't linked in any way. + * However, since dottyijar removes private methods and method implementations, it's possible that some shared values' + * referenced values could be deleted, rendering the references dangling. In this situation, we "inline" one of the + * shared values so that the others can reference it. + */ + private def removeDanglingSharedValuesFromTasty(oldTasty: Tasty, updatedTasty: Tasty): Tasty = updatedTasty.copy( + astsSection = updatedTasty.astsSection + .map { section => + section.copy( + payload = removeDanglingSharedValues( + section.payload, + handledReferences = mutable.Set.empty, + oldTastyDereferencer = TastyDereferencer(oldTasty), + updatedTastyDeferencer = TastyDereferencer(updatedTasty), + ), + ) + }, + ) + + private def removeUnusedNames(tasty: Tasty): Tasty = { + val usedNameIndices = getUsedNameIndices(tasty) + val nameIndexUpdates = usedNameIndices.toList.sorted.view.zipWithIndex.toMap + + def updateNameReferences[A: TastyElement](element: A): A = TastyElement.map(element) { + [B] => + child => + child match { + case TastyNameReference(i, information) => + TastyNameReference(nameIndexUpdates(i).taggedWith[Unsigned], information).asInstanceOf[B] + + case _ => updateNameReferences(child) + } + } + + updateNameReferences( + tasty.copy( + nameTable = TastyNameTable( + tasty.nameTable.names.zipWithIndex.collect { case (name, i) if usedNameIndices(i) => name }, + ), + ), + ) + } + + def updateTastyFile(content: Array[Byte]): Array[Byte] = { + val oldTasty = Tasty.read(content) + val updatedTasty = new ContextualTastyUpdater(oldTasty).updatedTasty + val withoutSharedValues = removeDanglingSharedValuesFromTasty(oldTasty, updatedTasty) + val withoutUnusedNames = removeUnusedNames(withoutSharedValues) + + withoutUnusedNames.write + } +} diff --git a/src/scala/io/bazel/rules_scala/dottyijar/TastyUpdater.spec.scala b/src/scala/io/bazel/rules_scala/dottyijar/TastyUpdater.spec.scala new file mode 100644 index 000000000..918643987 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/TastyUpdater.spec.scala @@ -0,0 +1,41 @@ +package io.bazel.rules_scala.dottyijar + +import io.bazel.rules_scala.dottyijar.tasty.TastySpecification +import java.io.File +import java.nio.file.Files +import org.specs2.execute.Result +import scala.quoted.Quotes +import scala.tasty.inspector.{Inspector, TastyInspector} + +class TastyUpdaterSpec extends TastySpecification { + "TastyUpdater" should { + "Work on every TASTy file for the Scala 3 compiler" in { + withTestCases { + Result.foreach(_) { testCase => + println(s"Testing ${testCase.path}") + + val content = testCase.inputStream.readAllBytes() + val updatedContent = TastyUpdater.updateTastyFile(content) + val i = testCase.path.lastIndexOf("/") + val j = testCase.path.lastIndexOf(".") + val baseName = testCase.path.slice(if (i == -1) 0 else i + 1, if (j == -1) testCase.path.length else j) + + /** + * [[File.createTempFile]] requires the temporary file prefix to be at least three characters long. + */ + val prefix = if (baseName.length < 3) baseName + "_" * (3 - baseName.length) else baseName + val updatedTastyFilePath = File.createTempFile(prefix, ".tasty").toPath + + Files.write(updatedTastyFilePath, updatedContent) + + TastyInspector.inspectTastyFiles(List(updatedTastyFilePath.toString))(DummyInspector) must + not(throwAn[Exception]) + } + } + } + } +} + +private object DummyInspector extends Inspector { + override def inspect(using quotes: Quotes)(tastys: List[scala.tasty.inspector.Tasty[quotes.type]]): Unit = {} +} diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/BUILD b/src/scala/io/bazel/rules_scala/dottyijar/tasty/BUILD index 3e65d34d5..ed8ac95aa 100644 --- a/src/scala/io/bazel/rules_scala/dottyijar/tasty/BUILD +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/BUILD @@ -1,11 +1,14 @@ -load("//scala:scala.bzl", "scala_library_for_plugin_bootstrapping", "scala_specs2_junit_test") +load("//scala:scala.bzl", "scala_library", "scala_library_for_plugin_bootstrapping", "scala_specs2_junit_test") load("@bazel_skylib//rules:copy_file.bzl", "copy_file") scala_library_for_plugin_bootstrapping( name = "tasty", srcs = glob( ["*.scala"], - exclude = ["*.spec.scala"], + exclude = [ + "*.spec.scala", + "*.test.scala", + ], ), scala_version = "3.6.2", visibility = ["//visibility:public"], @@ -25,18 +28,34 @@ copy_file( out = "scala3-compiler.jar", ) +scala_library( + name = "test", + testonly = True, + srcs = glob(["*.test.scala"]), + resource_strip_prefix = package_name(), + resources = [":scala3-compiler-jar"], + scala_version = "3.6.2", + visibility = ["//visibility:public"], + deps = [ + "@commons_io_commons_io_3_6_2", + "@io_bazel_rules_scala_junit_junit", + "@io_bazel_rules_scala_org_specs2_specs2_common_3_6_2", + "@io_bazel_rules_scala_org_specs2_specs2_core_3_6_2", + "@io_bazel_rules_scala_org_specs2_specs2_junit", + "@io_bazel_rules_scala_org_specs2_specs2_matcher_3_6_2", + ], +) + scala_specs2_junit_test( name = "specs", srcs = glob(["*.spec.scala"]), jvm_flags = ["-DDEBUG_TASTYFORMAT=true"], - resource_strip_prefix = package_name(), - resources = [":scala3-compiler-jar"], scala_version = "3.6.2", suffixes = ["Spec"], deps = [ ":tasty", + ":test", "//src/scala/io/bazel/rules_scala/dottyijar/tasty/format", - "@commons_io_commons_io_3_6_2", "@io_bazel_rules_scala_org_specs2_specs2_common_3_6_2", "@io_bazel_rules_scala_org_specs2_specs2_core_3_6_2", "@io_bazel_rules_scala_org_specs2_specs2_junit", diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.spec.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.spec.scala index 611f072a5..d2428497d 100644 --- a/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.spec.scala +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.spec.scala @@ -1,16 +1,12 @@ package io.bazel.rules_scala.dottyijar.tasty import io.bazel.rules_scala.dottyijar.tasty.format.DebuggingTastyFormat -import org.apache.commons.io.IOUtils -import java.io.{File, FileOutputStream, InputStream} +import java.io.File import java.nio.file.Files -import java.util.zip.ZipFile import org.specs2.execute.Result -import org.specs2.mutable.SpecificationWithJUnit -import scala.jdk.CollectionConverters.* import scala.util.control.NonFatal -class TastySpec extends SpecificationWithJUnit { +class TastySpec extends TastySpecification { /** * [[io.bazel.rules_scala.dottyijar.tasty.format.DebuggingTastyFormat]] uses global state to track the structure of @@ -21,34 +17,6 @@ class TastySpec extends SpecificationWithJUnit { sequential "Tasty" should { - def withTestCases[A](callback: List[TestCase] => A): A = { - val scala3CompilerJar = File.createTempFile("scala3-compiler", ".jar") - val inputStream = getClass.getClassLoader.getResourceAsStream("scala3-compiler.jar") - val outputStream = new FileOutputStream(scala3CompilerJar) - - try { - try { - IOUtils.copy(inputStream, outputStream) - } finally { - inputStream.close() - outputStream.close() - } - - val scala3CompilerZipFile = new ZipFile(scala3CompilerJar) - - callback( - scala3CompilerZipFile - .entries() - .asScala - .filter(_.getName.endsWith(".tasty")) - .map(entry => TestCase(entry.getName, scala3CompilerZipFile.getInputStream(entry))) - .toList, - ) - } finally { - Files.delete(scala3CompilerJar.toPath) - } - } - "Accurately model every TASTy file for the Scala 3 compiler" in { withTestCases { Result.foreach(_) { testCase => @@ -120,5 +88,3 @@ class TastySpec extends SpecificationWithJUnit { } } } - -private case class TestCase(path: String, inputStream: InputStream) diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/TastyDereferencer.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/TastyDereferencer.scala new file mode 100644 index 000000000..4f13e0dd2 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/TastyDereferencer.scala @@ -0,0 +1,25 @@ +package io.bazel.rules_scala.dottyijar.tasty + +import io.bazel.rules_scala.dottyijar.tasty.format.{MarkerType, TastyReferencable, TastyReference} +import scala.collection.mutable + +class TastyDereferencer(tasty: Tasty) { + private lazy val referencablesById = tasty.astsSection + .map { section => + mutable.LongMap( + TastyElement + .collect(section.payload) { case element: TastyReferencable => + element.information.id.map(_.toLong -> element) + } + .flatten + .toSeq*, + ) + } + .getOrElse(mutable.LongMap.empty) + + def dereference[A](reference: TastyReference[? <: MarkerType, A]): A = + referencablesById(reference.referencableId).asInstanceOf[A] + + def isValidReference(reference: TastyReference[? <: MarkerType, ?]): Boolean = + referencablesById.contains(reference.referencableId) +} diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/TastyElement.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/TastyElement.scala new file mode 100644 index 000000000..268e970c0 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/TastyElement.scala @@ -0,0 +1,450 @@ +package io.bazel.rules_scala.dottyijar.tasty + +import dotty.tools.dotc.util.Spans.Span +import io.bazel.rules_scala.dottyijar.tasty.format.{MarkerType, SignedInt, SignedLong, TastyReferencableInformation, TastyReference, UnsignedInt} +import java.util.UUID +import scala.annotation.nowarn +import scala.collection.Factory +import scala.compiletime.summonAll +import scala.deriving.Mirror + +trait TastyElement[A] { + + /** + * Whether the given value is a node in the TASTy AST, and not an iterable, primitive, or + * [[TastyReferenceInformation]]. I tried making this `private[tasty]`, but for some reason, Scala inlines it and a + * [[NoSuchMethodError]] is thrown at runtime in [[TastyElement.forSum]]. + */ + def isNode: Boolean + + def collect[B](value: A)(collector: PartialFunction[Any, B]): Iterable[B] + def map(value: A)(mapper: [B] => B => TastyElement[B] ?=> B): A +} + +object TastyElement + extends TastyElementIterableGivens + with TastyElementAlgebraicGivens + with TastyElementIdentityGivens + with TastyElementTastyGivens { + protected def collectChild[A: TastyElement, B](element: A)(collector: PartialFunction[Any, B]): Iterable[B] = { + val collected = if (summon[TastyElement[A]].isNode && collector.isDefinedAt(element)) { + Iterable(collector(element)) + } else { + Iterable.empty + } + + collected ++ collect(element)(collector) + } + + protected def mapChild[A: TastyElement](element: A)(mapper: [B] => B => TastyElement[B] ?=> B): A = + if (summon[TastyElement[A]].isNode) mapper(element) else map(element)(mapper) + + def collect[A: TastyElement, B](value: A)(collector: PartialFunction[Any, B]): Iterable[B] = + summon[TastyElement[A]].collect(value)(collector) + + def map[A: TastyElement](value: A)(mapper: [B] => B => TastyElement[B] ?=> B): A = + summon[TastyElement[A]].map(value)(mapper) + + def identity[A](isNode: Boolean): TastyElement[A] = { + val _isNode = isNode + + new TastyElement[A] { + override val isNode: Boolean = _isNode + override def collect[B](value: A)(collector: PartialFunction[Any, B]): Iterable[B] = Iterable.empty + override def map(value: A)(mapper: [B] => B => TastyElement[B] ?=> B): A = value + } + } +} + +transparent trait TastyElementIterableGivens { self: TastyElement.type => + inline given [A: TastyElement]: TastyElement[Option[A]] with { + override val isNode: Boolean = false + override def collect[B](value: Option[A])(collector: PartialFunction[Any, B]): Iterable[B] = + value.map(collectChild(_)(collector)).getOrElse(Iterable.empty) + + override def map(value: Option[A])(mapper: [B] => B => TastyElement[B] ?=> B): Option[A] = + value.map(mapChild(_)(mapper)) + } + + inline given [Element: TastyElement, IterableLike[A] <: Iterable[A]](using + Factory[Element, IterableLike[Element]], + ): TastyElement[IterableLike[Element]] with { + override val isNode: Boolean = false + override def collect[A](value: IterableLike[Element])(collector: PartialFunction[Any, A]): Iterable[A] = + value.collect(collectChild(_)(collector)).flatten + + override def map( + value: IterableLike[Element], + )(mapper: [B] => B => TastyElement[B] ?=> B): IterableLike[Element] = + summon[Factory[Element, IterableLike[Element]]].fromSpecific(value.map(mapChild(_)(mapper))) + } +} + +transparent trait TastyElementAlgebraicGivens { self: TastyElement.type => + given TastyElement[EmptyTuple] = identity(isNode = false) + + inline given tastyReferencableInformationTupleTastyElement[ + A <: Tuple: TastyElement, + ]: TastyElement[TastyReferencableInformation *: A] with { + override val isNode: Boolean = false + override def collect[B](value: TastyReferencableInformation *: A)(collector: PartialFunction[Any, B]): Iterable[B] = + TastyElement.collect(value.tail)(collector) + + override def map( + value: TastyReferencableInformation *: A, + )(mapper: [B] => B => TastyElement[B] ?=> B): TastyReferencableInformation *: A = + value.head *: TastyElement.map(value.tail)(mapper) + } + + inline given tupleTastyElement[ + Head: TastyElement, + Tail <: Tuple: TastyElement, + ]: TastyElement[Head *: Tail] with { + override val isNode: Boolean = false + override def collect[A](value: Head *: Tail)(collector: PartialFunction[Any, A]): Iterable[A] = + collectChild(value.head)(collector) ++ TastyElement.collect(value.tail)(collector) + + override def map(value: Head *: Tail)(mapper: [A] => A => TastyElement[A] ?=> A): Head *: Tail = + mapChild(value.head)(mapper) *: TastyElement.map(value.tail)(mapper) + } + + inline def forProduct[A <: Product](using + mirror: Mirror.ProductOf[A], + )(using TastyElement[mirror.MirroredElemTypes]): TastyElement[A] = new TastyElement[A] { + override val isNode: Boolean = true + override def collect[B](value: A)(collector: PartialFunction[Any, B]): Iterable[B] = + TastyElement.collect(Tuple.fromProductTyped(value))(collector) + + override def map(value: A)(mapper: [B] => B => TastyElement[B] ?=> B): A = + mirror.fromTuple(TastyElement.map(Tuple.fromProductTyped(value))(mapper)) + }: @nowarn("msg=New anonymous class definition will be duplicated at each inline site") + + inline def forSum[Value, Elements <: Tuple](using + mirror: Mirror.SumOf[Value] { type MirroredElemTypes = Elements }, + ): TastyElement[Value] = new TastyElement[Value] { + private lazy val tastyElements = summonAll[Tuple.Map[Elements, TastyElement]] + private def getTastyElement(value: Value): TastyElement[Value] = + tastyElements.productElement(mirror.ordinal(value)).asInstanceOf[TastyElement[Value]] + + override val isNode: Boolean = true + override def collect[A](value: Value)(collector: PartialFunction[Any, A]): Iterable[A] = + getTastyElement(value).collect(value)(collector) + + override def map(value: Value)(mapper: [A] => A => TastyElement[A] ?=> A): Value = + getTastyElement(value).map(value)(mapper) + }: @nowarn("msg=New anonymous class definition will be duplicated at each inline site") +} + +transparent trait TastyElementIdentityGivens { self: TastyElement.type => + given TastyElement[Boolean] = identity(isNode = false) + given TastyElement[Int] = identity(isNode = false) + given TastyElement[SignedInt] = identity(isNode = false) + given TastyElement[SignedLong] = identity(isNode = false) + given TastyElement[Span] = identity(isNode = false) + given TastyElement[String] = identity(isNode = false) + given TastyElement[UnsignedInt] = identity(isNode = false) + given TastyElement[UUID] = identity(isNode = false) +} + +transparent trait TastyElementTastyGivens { self: TastyElement.type => + given [RelativeTo <: MarkerType, Value]: TastyElement[TastyReference[RelativeTo, Value]] = identity(isNode = true) + + given TastyElement[Tasty] = forProduct + + given TastyElement[TastyCaseDefinition] = forProduct + + given TastyElement[TastyUnitConstant] = forProduct + given TastyElement[TastyFalseConstant] = forProduct + given TastyElement[TastyTrueConstant] = forProduct + given TastyElement[TastyByteConstant] = forProduct + given TastyElement[TastyShortConstant] = forProduct + given TastyElement[TastyCharConstant] = forProduct + given TastyElement[TastyIntConstant] = forProduct + given TastyElement[TastyLongConstant] = forProduct + given TastyElement[TastyFloatConstant] = forProduct + given TastyElement[TastyDoubleConstant] = forProduct + given TastyElement[TastyStringConstant] = forProduct + given TastyElement[TastyNullConstant] = forProduct + given TastyElement[TastyClassConstant] = forProduct + given TastyElement[TastyConstant] = forSum + + given TastyElement[TastyImplicitArgument] = forProduct + + given TastyElement[TastyModifier.Private] = forProduct + given TastyElement[TastyModifier.Protected] = forProduct + given TastyElement[TastyModifier.PrivateQualified] = forProduct + given TastyElement[TastyModifier.ProtectedQualified] = forProduct + given TastyElement[TastyModifier.Abstract] = forProduct + given TastyElement[TastyModifier.Final] = forProduct + given TastyElement[TastyModifier.Sealed] = forProduct + given TastyElement[TastyModifier.Case] = forProduct + given TastyElement[TastyModifier.Implicit] = forProduct + given TastyElement[TastyModifier.Given] = forProduct + given TastyElement[TastyModifier.Erased] = forProduct + given TastyElement[TastyModifier.Lazy] = forProduct + given TastyElement[TastyModifier.Override] = forProduct + given TastyElement[TastyModifier.Opaque] = forProduct + given TastyElement[TastyModifier.Inline] = forProduct + given TastyElement[TastyModifier.Macro] = forProduct + given TastyElement[TastyModifier.InlineProxy] = forProduct + given TastyElement[TastyModifier.Static] = forProduct + given TastyElement[TastyModifier.Object] = forProduct + given TastyElement[TastyModifier.Trait] = forProduct + given TastyElement[TastyModifier.Enum] = forProduct + given TastyElement[TastyModifier.Local] = forProduct + given TastyElement[TastyModifier.Synthetic] = forProduct + given TastyElement[TastyModifier.Artifact] = forProduct + given TastyElement[TastyModifier.Mutable] = forProduct + given TastyElement[TastyModifier.FieldAccessor] = forProduct + given TastyElement[TastyModifier.CaseAccessor] = forProduct + given tastyModifierCovariantTastyElement: TastyElement[TastyModifier.Covariant] = forProduct + given tastyModifierContravariantTastyElement: TastyElement[TastyModifier.Contravariant] = forProduct + given TastyElement[TastyModifier.HasDefault] = forProduct + given TastyElement[TastyModifier.Stable] = forProduct + given TastyElement[TastyModifier.Extension] = forProduct + given TastyElement[TastyModifier.ParameterSetter] = forProduct + given TastyElement[TastyModifier.ParameterAlias] = forProduct + given TastyElement[TastyModifier.Exported] = forProduct + given TastyElement[TastyModifier.Open] = forProduct + given TastyElement[TastyModifier.Invisible] = forProduct + given TastyElement[TastyModifier.Tracked] = forProduct + given TastyElement[TastyModifier.Annotation] = forProduct + given TastyElement[TastyModifier.Transparent] = forProduct + given TastyElement[TastyModifier.Infix] = forProduct + given TastyElement[TastyModifier] = forSum + + given TastyElement[TastyName.Simple] = forProduct + given TastyElement[TastyName.Qualified] = forProduct + given TastyElement[TastyName.Expanded] = forProduct + given TastyElement[TastyName.ExpandPrefix] = forProduct + given TastyElement[TastyName.Unique] = forProduct + given TastyElement[TastyName.DefaultGetter] = forProduct + given TastyElement[TastyName.SuperAccessor] = forProduct + given TastyElement[TastyName.InlineAccessor] = forProduct + given TastyElement[TastyName.ObjectClass] = forProduct + given TastyElement[TastyName.BodyRetainer] = forProduct + given TastyElement[TastyName.Signed] = forProduct + given TastyElement[TastyName.TargetSigned] = forProduct + given TastyElement[TastyName] = forSum + + given TastyElement[TastyNameReference] = forProduct + + given TastyElement[TastyNameTable] = forProduct + + given TastyElement[TastyTypeParameter] = forProduct + given TastyElement[TastyTermParameter] = forProduct + given TastyElement[TastyParameter.EmptyClause] = forProduct + given TastyElement[TastyParameter.SplitClause] = forProduct + given TastyElement[TastyParameter] = forSum + + given TastyElement[TastyParameterSignature.TypeParameterSectionLength] = forProduct + given TastyElement[TastyParameterSignature.TermParameter] = forProduct + given TastyElement[TastyParameterSignature] = forSum + + given tastyPathLocalReferenceTastyElement: TastyElement[TastyPath.LocalReference] = forProduct + given tastyPathPrefixedLocalReferenceTastyElement: TastyElement[TastyPath.PrefixedLocalReference] = forProduct + given tastyPathPackageReferenceTastyElement: TastyElement[TastyPath.PackageReference] = forProduct + given tastyPathNonLocalReferenceTastyElement: TastyElement[TastyPath.NonLocalReference] = forProduct + given tastyPathNonLocalReferenceInTastyElement: TastyElement[TastyPath.NonLocalReferenceIn] = forProduct + given TastyElement[TastyPath.This] = forProduct + given TastyElement[TastyPath.RecursivelyRefinedThis] = forProduct + given tastyPathSharedTastyElement: TastyElement[TastyPath.Shared] = forProduct + given TastyElement[TastyPath] = forSum + + given [A <: TastySectionPayload: TastyElement]: TastyElement[TastySection[A]] = forProduct + + given TastyElement[TastySectionPayload.Asts] = forProduct + given TastyElement[TastySectionPayload.Positions.Delta] = forProduct + given TastyElement[TastySectionPayload.Positions.Source] = forProduct + given TastyElement[TastySectionPayload.Positions.LineSizes] = forProduct + given TastyElement[TastySectionPayload.Positions] = { + given TastyElement[TastySectionPayload.Positions.Delta | TastySectionPayload.Positions.Source] with { + override val isNode: Boolean = true + override def collect[A]( + value: TastySectionPayload.Positions.Delta | TastySectionPayload.Positions.Source, + )(collector: PartialFunction[Any, A]): Iterable[A] = value match { + case delta: TastySectionPayload.Positions.Delta => TastyElement.collect(delta)(collector) + case source: TastySectionPayload.Positions.Source => TastyElement.collect(source)(collector) + } + + override def map(value: TastySectionPayload.Positions.Delta | TastySectionPayload.Positions.Source)( + mapper: [A] => A => TastyElement[A] ?=> A, + ): TastySectionPayload.Positions.Delta | TastySectionPayload.Positions.Source = + value match { + case delta: TastySectionPayload.Positions.Delta => TastyElement.map(delta)(mapper) + case source: TastySectionPayload.Positions.Source => TastyElement.map(source)(mapper) + } + } + + forProduct + } + + given TastyElement[TastySectionPayload.Comments.Comment] = forProduct + given TastyElement[TastySectionPayload.Comments] = forProduct + given TastyElement[TastySectionPayload.Attributes.Attribute.Scala2StandardLibrary] = forProduct + given TastyElement[TastySectionPayload.Attributes.Attribute.ExplicitNulls] = forProduct + given TastyElement[TastySectionPayload.Attributes.Attribute.CaptureChecked] = forProduct + given TastyElement[TastySectionPayload.Attributes.Attribute.WithPureFunctions] = forProduct + given TastyElement[TastySectionPayload.Attributes.Attribute.Java] = forProduct + given TastyElement[TastySectionPayload.Attributes.Attribute.Outline] = forProduct + given TastyElement[TastySectionPayload.Attributes.Attribute.SourceFile] = forProduct + given TastyElement[TastySectionPayload.Attributes.Attribute] = forSum + given TastyElement[TastySectionPayload.Attributes] = forProduct + + given TastyElement[TastySelector.Imported] = forProduct + given TastyElement[TastySelector.Renamed] = forProduct + given TastyElement[TastySelector.Bounded] = forProduct + given TastyElement[TastySelector] = forSum + + given TastyElement[TastySelf] = forProduct + + given TastyElement[TastyImportStatement] = forProduct + given TastyElement[TastyExportStatement] = forProduct + given TastyElement[TastyStatement] = forSum + + given TastyElement[TastyTemplate] = { + given TastyElement[TastyTerm | TastyTypeTree] with { + override val isNode: Boolean = true + override def collect[A](value: TastyTerm | TastyTypeTree)(collector: PartialFunction[Any, A]): Iterable[A] = + value match { + case term: TastyTerm => TastyElement.collect(term)(collector) + case typeTree: TastyTypeTree => TastyElement.collect(typeTree)(collector) + } + + override def map( + value: TastyTerm | TastyTypeTree, + )(mapper: [A] => A => TastyElement[A] ?=> A): TastyTerm | TastyTypeTree = value match { + case term: TastyTerm => TastyElement.map(term)(mapper) + case typeTree: TastyTypeTree => TastyElement.map(typeTree)(mapper) + } + } + + forProduct + } + + given tastyTermIdentifierTastyElement: TastyElement[TastyTerm.Identifier] = forProduct + given TastyElement[TastyTerm.Select] = forProduct + given TastyElement[TastyTerm.SelectIn] = forProduct + given tastyTermQualifiedThisTastyElement: TastyElement[TastyTerm.QualifiedThis] = forProduct + given TastyElement[TastyTerm.New] = forProduct + given TastyElement[TastyTerm.Elided] = forProduct + given TastyElement[TastyTerm.Throw] = forProduct + given TastyElement[TastyTerm.NamedArgument] = forProduct + given TastyElement[TastyTerm.Apply] = forProduct + given TastyElement[TastyTerm.ApplySignaturePolymorphic] = forProduct + given TastyElement[TastyTerm.TypeApply] = forProduct + given tastyTermSuperTastyElement: TastyElement[TastyTerm.Super] = forProduct + given TastyElement[TastyTerm.TypeAscribed] = forProduct + given TastyElement[TastyTerm.Assignment] = forProduct + given TastyElement[TastyTerm.Block] = forProduct + given TastyElement[TastyTerm.Inlined] = forProduct + given tastyTypeLambdaTastyElement: TastyElement[TastyTerm.Lambda] = forProduct + given TastyElement[TastyTerm.If] = forProduct + given tastyTermMatchTastyElement: TastyElement[TastyTerm.Match] = forProduct + given TastyElement[TastyTerm.Pattern.Bind] = { + given TastyElement[TastyTerm | TastyTypeTree] with { + override val isNode: Boolean = true + override def collect[A](value: TastyTerm | TastyTypeTree)(collector: PartialFunction[Any, A]): Iterable[A] = + value match { + case term: TastyTerm => TastyElement.collect(term)(collector) + case typeTree: TastyTypeTree => TastyElement.collect(typeTree)(collector) + } + + override def map( + value: TastyTerm | TastyTypeTree, + )(mapper: [A] => A => TastyElement[A] ?=> A): TastyTerm | TastyTypeTree = value match { + case term: TastyTerm => TastyElement.map(term)(mapper) + case typeTree: TastyTypeTree => TastyElement.map(typeTree)(mapper) + } + } + + forProduct + } + + given TastyElement[TastyTerm.Pattern.Alternative] = forProduct + given TastyElement[TastyTerm.Pattern.Unapply] = forProduct + given TastyElement[TastyTerm.Pattern.QuotePattern] = forProduct + given TastyElement[TastyTerm.PickledQuoteTree.Explicit] = forProduct + given TastyElement[TastyTerm.PickledQuoteTree.Hole] = forProduct + given TastyElement[TastyTerm.Try] = forProduct + given TastyElement[TastyTerm.Return] = forProduct + given TastyElement[TastyTerm.While] = forProduct + given TastyElement[TastyTerm.RepeatedArgument] = forProduct + given TastyElement[TastyTerm.SelectOuter] = forProduct + given TastyElement[TastyTerm.Quoted] = forProduct + given TastyElement[TastyTerm.Spliced] = forProduct + given TastyElement[TastyTerm.SplicedPattern] = forProduct + given tastyTermSharedTastyElement: TastyElement[TastyTerm.Shared] = forProduct + given TastyElement[TastyTerm] = forSum + + given TastyElement[TastyPackageStatement] = forProduct + given TastyElement[TastyTopLevelStatement] = forSum + + given tastyTypeLocalReferenceTastyElement: TastyElement[TastyType.LocalReference] = forProduct + given tastyTypePrefixedLocalReferenceTastyElement: TastyElement[TastyType.PrefixedLocalReference] = forProduct + given tastyTypePackageReferenceTastyElement: TastyElement[TastyType.PackageReference] = forProduct + given tastyTypeNonLocalReferenceTastyElement: TastyElement[TastyType.NonLocalReference] = forProduct + given tastyTypeNonLocalReferenceInTastyElement: TastyElement[TastyType.NonLocalReferenceIn] = forProduct + given TastyElement[TastyType.RecursivelyRefined] = forProduct + given tastyTypeSuperTastyElement: TastyElement[TastyType.Super] = forProduct + given tastyTypeRefinedTastyElement: TastyElement[TastyType.Refined] = forProduct + given tastyTypeAppliedTastyElement: TastyElement[TastyType.Applied] = forProduct + given tastyTypeTypeBoundsTastyElement: TastyElement[TastyType.TypeBounds] = forProduct + given TastyElement[TastyType.Variance.Invariant] = forProduct + given tastyTypeVarianceCovariantTastyElement: TastyElement[TastyType.Variance.Covariant] = forProduct + given tastyTypeVarianceContravariantTastyElement: TastyElement[TastyType.Variance.Contravariant] = forProduct + given TastyElement[TastyType.Variance] = forSum + given tastyTypeAnnotatedTastyElement: TastyElement[TastyType.Annotated] = forProduct + given TastyElement[TastyType.And] = forProduct + given TastyElement[TastyType.Or] = forProduct + given tastyTypeMatchTastyElement: TastyElement[TastyType.Match] = forProduct + given TastyElement[TastyType.MatchCase] = forProduct + given TastyElement[TastyType.Flexible] = forProduct + given tastyTypeByNameTastyElement: TastyElement[TastyType.ByName] = forProduct + given TastyElement[TastyType.TypeName] = forProduct + given TastyElement[TastyType.ParameterReference] = forProduct + given TastyElement[TastyType.PolymorphicMethod] = forProduct + given TastyElement[TastyType.Method] = forProduct + given TastyElement[TastyType.TypeLambda] = forProduct + given tastyTypeSharedTastyElement: TastyElement[TastyType.Shared] = forProduct + given tastyTypeQualifiedThisTastyElement: TastyElement[TastyType.QualifiedThis] = forProduct + given TastyElement[TastyType] = forSum + + given TastyElement[TastyTypeDefinition] = { + given TastyElement[TastyTypeTree | TastyTemplate] with { + override val isNode: Boolean = true + override def collect[A](value: TastyTypeTree | TastyTemplate)(collector: PartialFunction[Any, A]): Iterable[A] = + value match { + case typeTree: TastyTypeTree => TastyElement.collect(typeTree)(collector) + case template: TastyTemplate => TastyElement.collect(template)(collector) + } + + override def map( + value: TastyTypeTree | TastyTemplate, + )(mapper: [A] => A => TastyElement[A] ?=> A): TastyTypeTree | TastyTemplate = value match { + case typeTree: TastyTypeTree => TastyElement.map(typeTree)(mapper) + case template: TastyTemplate => TastyElement.map(template)(mapper) + } + } + + forProduct + } + + given tastyTypeTreeIdentifierTastyElement: TastyElement[TastyTypeTree.Identifier] = forProduct + given TastyElement[TastyTypeTree.SelectFromTerm] = forProduct + given TastyElement[TastyTypeTree.SelectFromType] = forProduct + given TastyElement[TastyTypeTree.Singleton] = forProduct + given tastyTypeTreeRefinedTastyElement: TastyElement[TastyTypeTree.Refined] = forProduct + given tastyTypeTreeAppliedTastyElement: TastyElement[TastyTypeTree.Applied] = forProduct + given tastyTypeTreeLambdaTastyElement: TastyElement[TastyTypeTree.Lambda] = forProduct + given tastyTypeTreeTypeBoundsTastyElement: TastyElement[TastyTypeTree.TypeBounds] = forProduct + given tastyTypeTreeAnnotatedTastyElement: TastyElement[TastyTypeTree.Annotated] = forProduct + given tastyTypeTreeMatchTastyElement: TastyElement[TastyTypeTree.Match] = forProduct + given tastyTypeTreeByNameTastyElement: TastyElement[TastyTypeTree.ByName] = forProduct + given tastyTypeTreeSharedTastyElement: TastyElement[TastyTypeTree.Shared] = forProduct + given TastyElement[TastyTypeTree] = forSum + + given TastyElement[TastyValOrDefDefinition.Val] = forProduct + given TastyElement[TastyValOrDefDefinition.Def] = forProduct + given TastyElement[TastyValOrDefDefinition] = forSum +} diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/TastySpecification.test.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/TastySpecification.test.scala new file mode 100644 index 000000000..990229eda --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/TastySpecification.test.scala @@ -0,0 +1,40 @@ +package io.bazel.rules_scala.dottyijar.tasty + +import java.io.{File, FileOutputStream, InputStream} +import java.nio.file.Files +import java.util.zip.ZipFile +import org.apache.commons.io.IOUtils +import org.specs2.mutable.SpecificationWithJUnit +import scala.jdk.CollectionConverters.* + +class TastySpecification extends SpecificationWithJUnit { + protected def withTestCases[A](callback: List[TestCase] => A): A = { + val scala3CompilerJar = File.createTempFile("scala3-compiler", ".jar") + val inputStream = getClass.getClassLoader.getResourceAsStream("scala3-compiler.jar") + val outputStream = new FileOutputStream(scala3CompilerJar) + + try { + try { + IOUtils.copy(inputStream, outputStream) + } finally { + inputStream.close() + outputStream.close() + } + + val scala3CompilerZipFile = new ZipFile(scala3CompilerJar) + + callback( + scala3CompilerZipFile + .entries() + .asScala + .filter(_.getName.endsWith(".tasty")) + .map(entry => TestCase(entry.getName, scala3CompilerZipFile.getInputStream(entry))) + .toList, + ) + } finally { + Files.delete(scala3CompilerJar.toPath) + } + } +} + +case class TestCase(path: String, inputStream: InputStream) diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tastyinspector/BUILD b/src/scala/io/bazel/rules_scala/dottyijar/tastyinspector/BUILD new file mode 100644 index 000000000..f2de05f27 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tastyinspector/BUILD @@ -0,0 +1,24 @@ +load("@rules_java//java:defs.bzl", "java_binary") +load("//scala:scala.bzl", "scala_binary") + +java_binary( + name = "compiler", + main_class = "dotty.tools.dotc.Main", + runtime_deps = [ + "@io_bazel_rules_scala_scala_compiler_3_6_2", + "@io_bazel_rules_scala_scala_library_3_6_2", + ], +) + +scala_binary( + name = "tastyinspector", + srcs = glob(["*.scala"]), + data = [":compiler"], + main_class = "io.bazel.rules_scala.dottyijar.tastyinspector.TastyInspector", + scala_version = "3.6.2", + deps = [ + "//src/scala/io/bazel/rules_scala/dottyijar/tasty", + "//src/scala/io/bazel/rules_scala/dottyijar/tasty/format", + "@dev_zio_izumi_reflect_3_6_2", + ], +) diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tastyinspector/TastyInspector.scala b/src/scala/io/bazel/rules_scala/dottyijar/tastyinspector/TastyInspector.scala new file mode 100644 index 000000000..67eb46f2f --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tastyinspector/TastyInspector.scala @@ -0,0 +1,106 @@ +package io.bazel.rules_scala.dottyijar.tastyinspector + +import io.bazel.rules_scala.dottyijar.tasty.format.{TastyFormat, TastyReader, TastyReferencableInformation} +import io.bazel.rules_scala.dottyijar.tasty.{Tasty, TastyNameTable} +import java.nio.file.{Files, Paths} +import scala.collection.mutable +import scala.jdk.CollectionConverters.* +import scala.sys.process.* +import scala.util.Try + +object TastyInspector { + private val maximumLineLength = 120 + private val prettyPrintCache = mutable.Map[(Any, Int), String]() + private def getIndentation(depth: Int): String = " " * depth + private def prettyPrintTasty(tasty: Tasty): String = prettyPrintTastyValue(tasty, depth = 0) + private def prettyPrintTastyProductLike(prefix: String, elements: => Iterator[Any], depth: Int): String = { + val indentation = getIndentation(depth) + val singleLineJoinedElements = elements.map(prettyPrintTastyValue(_, depth = 0)).mkString(", ") + val singleLine = s"$indentation$prefix($singleLineJoinedElements)" + + if (singleLine.length <= maximumLineLength) { + singleLine + } else { + val lines = elements.map(element => s"${prettyPrintTastyValue(element, depth = depth + 1)},\n").mkString + + s"$indentation$prefix(\n$lines$indentation)" + } + } + + private def prettyPrintTastyValue(value: Any, depth: Int): String = prettyPrintCache.getOrElseUpdate( + (value, depth), + value match { + case iterable: Iterable[?] => + prettyPrintTastyProductLike( + iterable match { + case _: List[?] => "List" + case _: Vector[?] => "Vector" + case _ => iterable.getClass.getSimpleName + }, + iterable.iterator, + depth, + ) + + /** + * [[TastyNameTable]] is the only type that overrides `toString`. + */ + case product: Product if !product.isInstanceOf[TastyNameTable] => + if (product.productArity == 0) { + s"${getIndentation(depth)}${product.productPrefix}" + } else { + prettyPrintTastyProductLike(product.productPrefix, product.productIterator, depth) + } + + case _ => value.toString.split('\n').view.map(line => s"${getIndentation(depth)}$line").mkString("\n") + }, + ) + + def main(arguments: Array[String]): Unit = ( + for { + argument <- arguments.headOption.toRight("Please provide a path to a `.scala` file.\n") + tastyPath <- + if (argument.endsWith(".tasty")) { + Right(Paths.get(argument)) + } else { + val outputDirectory = Files.createTempDirectory("tasty-inspector") + val compilerReturnCode = List( + "src/scala/io/bazel/rules_scala/dottyijar/tastyinspector/compiler", + "-d", + outputDirectory.toString, + "-usejavacp", + argument, + ).! + + if (compilerReturnCode == 0) { + val outputtedFileStream = Files.walk(outputDirectory) + + try { + val outputtedFiles = outputtedFileStream.iterator.asScala + .filter(path => path.toFile.isFile && path.toString.endsWith(".tasty")) + .toList + + outputtedFiles match { + case List(path) => Right(path) + case Nil => Left("The compiler didn't produce any `.tasty` files.\n") + case _ => Left(s"The compiler produced multiple `.tasty` files: ${outputtedFiles.mkString(", ")}\n") + } + } finally { + outputtedFileStream.close() + } + } else { + Left("") + } + } + } yield tastyPath + ) match { + case Left(errorMessage) => + println(errorMessage) + + sys.exit(1) + + case Right(tastyPath) => + val tasty = Tasty.read(Files.readAllBytes(tastyPath)) + + println(prettyPrintTasty(tasty)) + } +} diff --git a/third_party/repositories/scala_3_1.bzl b/third_party/repositories/scala_3_1.bzl index 1adfe737f..15b979d1f 100644 --- a/third_party/repositories/scala_3_1.bzl +++ b/third_party/repositories/scala_3_1.bzl @@ -300,6 +300,13 @@ artifacts = { "artifact": "org.scala-lang:tasty-core_3:3.1.3", "sha256": "0a183e880575bcc97a2047761880241784734e7ee094dc6fafd6a8f2109ff1da", }, + "io_bazel_rules_scala_scala_tasty_inspector": { + "artifact": "org.scala-lang:scala3-tasty-inspector_3:3.1.3", + "sha256": "b9c6e3c196cb0ea15cff650c8fd430a81bfb149336443921b3bcbdcb50243138", + "deps": [ + "@io_bazel_rules_scala_scala_compiler", + ], + }, "io_bazel_rules_scala_scala_xml": { "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", diff --git a/third_party/repositories/scala_3_2.bzl b/third_party/repositories/scala_3_2.bzl index 12cbac39c..7e3cc7cb7 100644 --- a/third_party/repositories/scala_3_2.bzl +++ b/third_party/repositories/scala_3_2.bzl @@ -300,6 +300,13 @@ artifacts = { "artifact": "org.scala-lang:tasty-core_3:3.2.2", "sha256": "df0690721532323a3c533385a06a4f532231d012d38f65bd75864718cfabace4", }, + "io_bazel_rules_scala_scala_tasty_inspector": { + "artifact": "org.scala-lang:scala3-tasty-inspector_3:3.2.2", + "sha256": "7ccaba0ad2551f840cf2779a6624b93656eb91205f21067b424d749f7b421824", + "deps": [ + "@io_bazel_rules_scala_scala_compiler", + ], + }, "io_bazel_rules_scala_scala_xml": { "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", diff --git a/third_party/repositories/scala_3_3.bzl b/third_party/repositories/scala_3_3.bzl index 2d67af6e3..9fae41295 100644 --- a/third_party/repositories/scala_3_3.bzl +++ b/third_party/repositories/scala_3_3.bzl @@ -301,6 +301,13 @@ artifacts = { "artifact": "org.scala-lang:tasty-core_3:3.3.4", "sha256": "13d44693d6f2d38e0595954d11234c0373d6d0e689a8151b06878c5b631d57d5", }, + "io_bazel_rules_scala_scala_tasty_inspector": { + "artifact": "org.scala-lang:scala3-tasty-inspector_3:3.3.4", + "sha256": "8c289ef4e155edf28245ac44b6652bd498566643160a31713a6bfd0777272fd0", + "deps": [ + "@io_bazel_rules_scala_scala_compiler", + ], + }, "io_bazel_rules_scala_scala_xml": { "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", diff --git a/third_party/repositories/scala_3_4.bzl b/third_party/repositories/scala_3_4.bzl index 2d1f75bef..41006d5b3 100644 --- a/third_party/repositories/scala_3_4.bzl +++ b/third_party/repositories/scala_3_4.bzl @@ -301,6 +301,13 @@ artifacts = { "artifact": "org.scala-lang:tasty-core_3:3.4.3", "sha256": "e3b5bdb3bbb3038e290d85e6e4f528c9d7fe1c7b1274695e3140ec6b86a84097", }, + "io_bazel_rules_scala_scala_tasty_inspector": { + "artifact": "org.scala-lang:scala3-tasty-inspector_3:3.4.3", + "sha256": "45188bd5b2b985aaf6cab56aa7484fbcdc488c7b0ce39bba39f3c80f43b5c58c", + "deps": [ + "@io_bazel_rules_scala_scala_compiler", + ], + }, "io_bazel_rules_scala_scala_xml": { "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", diff --git a/third_party/repositories/scala_3_5.bzl b/third_party/repositories/scala_3_5.bzl index df626abfc..529b65cb7 100644 --- a/third_party/repositories/scala_3_5.bzl +++ b/third_party/repositories/scala_3_5.bzl @@ -301,6 +301,13 @@ artifacts = { "artifact": "org.scala-lang:tasty-core_3:3.5.2", "sha256": "b380158748e147f4e44654ad16003c89599ddd456eac29f9686cb6d5515067f3", }, + "io_bazel_rules_scala_scala_tasty_inspector": { + "artifact": "org.scala-lang:scala3-tasty-inspector_3:3.5.2", + "sha256": "8bfe15d8804f6e2d72cdb4a3bc284ec6cb974291f327270db37e7af5128d1660", + "deps": [ + "@io_bazel_rules_scala_scala_compiler", + ], + }, "io_bazel_rules_scala_scala_xml": { "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", diff --git a/third_party/repositories/scala_3_6.bzl b/third_party/repositories/scala_3_6.bzl index 42d8175c2..b50fa1ccf 100644 --- a/third_party/repositories/scala_3_6.bzl +++ b/third_party/repositories/scala_3_6.bzl @@ -310,6 +310,13 @@ artifacts = { "@io_bazel_rules_scala_scala_library", ], }, + "io_bazel_rules_scala_scala_tasty_inspector": { + "artifact": "org.scala-lang:scala3-tasty-inspector_3:3.6.2", + "sha256": "0075e4bb5289facf54004060ea0f3aa60f60a03ec9dcb83c2fc01b06ffced6e7", + "deps": [ + "@io_bazel_rules_scala_scala_compiler", + ], + }, "io_bazel_rules_scala_scala_xml": { "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", From 8be90155426ff1ebe2c355b5cd519210dba713e8 Mon Sep 17 00:00:00 2001 From: Jaden Peterson Date: Wed, 8 Jan 2025 21:37:54 -0500 Subject: [PATCH 5/7] Integrated dottyijar with the ruleset and wrote tests for it --- scala/private/common_attributes.bzl | 5 +++ scala/private/phases/phase_compile.bzl | 39 ++++++++++++++----- test/dottyijar/BUILD | 33 ++++++++++++++++ test/dottyijar/DefinitionValues1.scala | 5 +++ test/dottyijar/DefinitionValues2.scala | 5 +++ .../dottyijar/DefinitionValuesDependent.scala | 5 +++ test/dottyijar/PrivateMembers1.scala | 13 +++++++ test/dottyijar/PrivateMembers2.scala | 8 ++++ test/dottyijar/PrivateMembersDependent.scala | 5 +++ test/shell/test_dottyijar.sh | 32 +++++++++++++++ 10 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 test/dottyijar/BUILD create mode 100644 test/dottyijar/DefinitionValues1.scala create mode 100644 test/dottyijar/DefinitionValues2.scala create mode 100644 test/dottyijar/DefinitionValuesDependent.scala create mode 100644 test/dottyijar/PrivateMembers1.scala create mode 100644 test/dottyijar/PrivateMembers2.scala create mode 100644 test/dottyijar/PrivateMembersDependent.scala create mode 100755 test/shell/test_dottyijar.sh diff --git a/scala/private/common_attributes.bzl b/scala/private/common_attributes.bzl index a71080296..a33c396ed 100644 --- a/scala/private/common_attributes.bzl +++ b/scala/private/common_attributes.bzl @@ -78,6 +78,11 @@ common_attrs.update({ executable = True, cfg = "exec", ), + "_dottyijar": attr.label( + cfg = "exec", + default = "//src/scala/io/bazel/rules_scala/dottyijar", + executable = True, + ), }) implicit_deps = { diff --git a/scala/private/phases/phase_compile.bzl b/scala/private/phases/phase_compile.bzl index d1534aabb..7b78abefa 100644 --- a/scala/private/phases/phase_compile.bzl +++ b/scala/private/phases/phase_compile.bzl @@ -41,8 +41,10 @@ def phase_compile_library(ctx, p): return _phase_compile_default(ctx, p, args) def phase_compile_library_for_plugin_bootstrapping(ctx, p): + is_scala_2 = ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scala_version.startswith("2.") + args = struct( - buildijar = ctx.attr.build_ijar, + buildijar = ctx.attr.build_ijar and is_scala_2, ) return _phase_compile_default(ctx, p, args) @@ -102,13 +104,11 @@ def phase_compile_common(ctx, p): return _phase_compile_default(ctx, p) def _phase_compile_default(ctx, p, _args = struct()): - buildijar_default_value = True if ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scala_version.startswith("2.") else False - return _phase_compile( ctx, p, _args.srcjars if hasattr(_args, "srcjars") else depset(), - _args.buildijar if hasattr(_args, "buildijar") else buildijar_default_value, + not hasattr(_args, "buildijar") or _args.buildijar, _args.implicit_junit_deps_needed_for_java_compilation if hasattr(_args, "implicit_junit_deps_needed_for_java_compilation") else [], unused_dependency_checker_ignored_targets = _args.unused_dependency_checker_ignored_targets if hasattr(_args, "unused_dependency_checker_ignored_targets") else [], ) @@ -228,12 +228,7 @@ def _compile_or_empty( # build ijar if needed if buildijar: - ijar = java_common.run_ijar( - ctx.actions, - jar = ctx.outputs.jar, - target_label = ctx.label, - java_toolchain = specified_java_compile_toolchain(ctx), - ) + ijar = _build_ijar(ctx) else: # macro code needs to be available at compile-time, # so set ijar == jar @@ -266,6 +261,30 @@ def _compile_or_empty( merged_provider = merged_provider, ) +def _build_ijar(ctx): + if ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scala_version.startswith("2."): + return java_common.run_ijar( + ctx.actions, + jar = ctx.outputs.jar, + target_label = ctx.label, + java_toolchain = specified_java_compile_toolchain(ctx), + ) + + output = ctx.actions.declare_file("{}-ijar.jar".format(ctx.label.name)) + arguments = ctx.actions.args() + arguments.add(ctx.outputs.jar) + arguments.add(output) + + ctx.actions.run( + arguments = [arguments], + executable = ctx.executable._dottyijar, + inputs = [ctx.outputs.jar], + mnemonic = "DottyIjar", + outputs = [output], + ) + + return output + def _build_nosrc_jar(ctx): resources = [s + ":" + t for t, s in _resource_paths(ctx.files.resources, ctx.attr.resource_strip_prefix)] diff --git a/test/dottyijar/BUILD b/test/dottyijar/BUILD new file mode 100644 index 000000000..97386a83e --- /dev/null +++ b/test/dottyijar/BUILD @@ -0,0 +1,33 @@ +load("//scala:scala.bzl", "scala_binary", "scala_library") + +scala_library( + name = "private-members", + srcs = ["PrivateMembers.scala"], + scala_version = "3.6.2", + tags = ["manual"], +) + +scala_binary( + name = "private-members-dependent", + srcs = ["PrivateMembersDependent.scala"], + main_class = "dottyijar.Main", + scala_version = "3.6.2", + tags = ["manual"], + deps = [":private-members"], +) + +scala_library( + name = "definition-values", + srcs = ["DefinitionValues.scala"], + scala_version = "3.6.2", + tags = ["manual"], +) + +scala_library( + name = "definition-values-dependent", + srcs = ["DefinitionValuesDependent.scala"], + main_class = "dottyijar.Main", + scala_version = "3.6.2", + tags = ["manual"], + deps = [":definition-values"], +) diff --git a/test/dottyijar/DefinitionValues1.scala b/test/dottyijar/DefinitionValues1.scala new file mode 100644 index 000000000..93be7e48c --- /dev/null +++ b/test/dottyijar/DefinitionValues1.scala @@ -0,0 +1,5 @@ +package dottyijar + +object DefinitionValues { + val greeting: String = s"Hello, Bob!" +} diff --git a/test/dottyijar/DefinitionValues2.scala b/test/dottyijar/DefinitionValues2.scala new file mode 100644 index 000000000..e56b012c1 --- /dev/null +++ b/test/dottyijar/DefinitionValues2.scala @@ -0,0 +1,5 @@ +package dottyijar + +object DefinitionValues { + val greeting: String = s"Hello, Bob!".replace("Bob", "Alice") +} diff --git a/test/dottyijar/DefinitionValuesDependent.scala b/test/dottyijar/DefinitionValuesDependent.scala new file mode 100644 index 000000000..6ce24b32a --- /dev/null +++ b/test/dottyijar/DefinitionValuesDependent.scala @@ -0,0 +1,5 @@ +package dottyijar + +object Main { + def main(arguments: Array[String]): Unit = println(DefinitionValues.greeting) +} diff --git a/test/dottyijar/PrivateMembers1.scala b/test/dottyijar/PrivateMembers1.scala new file mode 100644 index 000000000..ba62873e4 --- /dev/null +++ b/test/dottyijar/PrivateMembers1.scala @@ -0,0 +1,13 @@ +package dottyijar + +object PrivateMembers { + private val privateField = () + private lazy val privateLazyField = () + + val publicField = () + lazy val publicLazyField = () + + private def privateMethod: Unit = {} + + def publicMethod: Unit = {} +} diff --git a/test/dottyijar/PrivateMembers2.scala b/test/dottyijar/PrivateMembers2.scala new file mode 100644 index 000000000..bcb70e6fd --- /dev/null +++ b/test/dottyijar/PrivateMembers2.scala @@ -0,0 +1,8 @@ +package dottyijar + +object PrivateMembers { + val publicField = () + lazy val publicLazyField = () + + def publicMethod: Unit = {} +} diff --git a/test/dottyijar/PrivateMembersDependent.scala b/test/dottyijar/PrivateMembersDependent.scala new file mode 100644 index 000000000..f5af8138a --- /dev/null +++ b/test/dottyijar/PrivateMembersDependent.scala @@ -0,0 +1,5 @@ +package dottyijar + +object Main { + def main(arguments: Array[String]): Unit = println(PrivateMembers.publicField) +} \ No newline at end of file diff --git a/test/shell/test_dottyijar.sh b/test/shell/test_dottyijar.sh new file mode 100755 index 000000000..7cb67b19d --- /dev/null +++ b/test/shell/test_dottyijar.sh @@ -0,0 +1,32 @@ +# shellcheck source=./test_runner.sh +dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +. "${dir}"/test_runner.sh +. "${dir}"/test_helper.sh +runner=$(get_test_runner "${1:-local}") + +ijar_invariant() { + cp "test/dottyijar/$3" "test/dottyijar/$2" + trap "rm 'test/dottyijar/$2'" EXIT + + bazel build "//test/dottyijar:$1-dependent" + + ijar1_path="$(mktemp)" + + cp "bazel-bin/test/dottyijar/$1-ijar.jar" "$ijar1_path" + + cp "test/dottyijar/$4" "test/dottyijar/$2" + bazel build "//test/dottyijar:$1-dependent" + + diff "$ijar1_path" "bazel-bin/test/dottyijar/$1-ijar.jar" +} + +ijar_invariant_to_private_members() { + ijar_invariant private-members PrivateMembers.scala PrivateMembers1.scala PrivateMembers2.scala +} + +ijar_invariant_to_definition_values() { + ijar_invariant definition-values DefinitionValues.scala DefinitionValues1.scala DefinitionValues2.scala +} + +$runner ijar_invariant_to_private_members +$runner ijar_invariant_to_definition_values From 4af41de751277cce22d975054c90f88be1cd0c33 Mon Sep 17 00:00:00 2001 From: Jaden Peterson Date: Wed, 22 Jan 2025 11:13:41 -0500 Subject: [PATCH 6/7] Don't use com.softwaremill.tagging We'd like to minimize the number of required dependencies. --- scala/private/macros/scala_repositories.bzl | 1 - scripts/create_repository.py | 1 - .../io/bazel/rules_scala/dottyijar/BUILD | 2 +- .../rules_scala/dottyijar/TastyUpdater.scala | 18 ++++--- .../bazel/rules_scala/dottyijar/tasty/BUILD | 3 +- .../rules_scala/dottyijar/tasty/Tasty.scala | 54 +++++++++---------- .../dottyijar/tasty/TastyElement.scala | 3 +- .../rules_scala/dottyijar/tasty/format/BUILD | 2 +- .../dottyijar/tasty/format/TastyFormat.scala | 18 ++++--- .../dottyijar/tasty/format/TastyReader.scala | 14 ++--- .../dottyijar/tasty/format/TastyWriter.scala | 20 +++---- .../dottyijar/tasty/format/package.scala | 15 ------ .../rules_scala/dottyijar/tasty/numeric/BUILD | 8 +++ .../dottyijar/tasty/numeric/SignedInt.scala | 3 ++ .../dottyijar/tasty/numeric/SignedLong.scala | 3 ++ .../dottyijar/tasty/numeric/UnsignedInt.scala | 3 ++ .../tasty/numeric/UnsignedLong.scala | 3 ++ third_party/repositories/scala_3_1.bzl | 7 --- third_party/repositories/scala_3_2.bzl | 7 --- third_party/repositories/scala_3_3.bzl | 7 --- third_party/repositories/scala_3_4.bzl | 7 --- third_party/repositories/scala_3_5.bzl | 7 --- third_party/repositories/scala_3_6.bzl | 7 --- 23 files changed, 92 insertions(+), 121 deletions(-) delete mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/format/package.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/BUILD create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/SignedInt.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/SignedLong.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/UnsignedInt.scala create mode 100644 src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/UnsignedLong.scala diff --git a/scala/private/macros/scala_repositories.bzl b/scala/private/macros/scala_repositories.bzl index 89302665e..f7f462510 100644 --- a/scala/private/macros/scala_repositories.bzl +++ b/scala/private/macros/scala_repositories.bzl @@ -181,7 +181,6 @@ def _artifact_ids(scala_version): if scala_version.startswith("3."): result.extend([ - "com_softwaremill_common_tagging", "dev_zio_izumi_reflect", "io_bazel_rules_scala_scala_asm", "io_bazel_rules_scala_scala_compiler_2", diff --git a/scripts/create_repository.py b/scripts/create_repository.py index 21748119c..2c6463886 100755 --- a/scripts/create_repository.py +++ b/scripts/create_repository.py @@ -129,7 +129,6 @@ def select_root_artifacts(scala_version, scala_major, is_scala_3) -> List[str]: if is_scala_3: root_artifacts.extend([ - 'com.softwaremill.common:tagging_3:2.3.5', 'dev.zio:izumi-reflect_3:2.3.10', f'org.jline:jline-reader:{JLINE_VERSION}', f'org.jline:jline-terminal:{JLINE_VERSION}', diff --git a/src/scala/io/bazel/rules_scala/dottyijar/BUILD b/src/scala/io/bazel/rules_scala/dottyijar/BUILD index 9b6d92d45..e8dd594e7 100644 --- a/src/scala/io/bazel/rules_scala/dottyijar/BUILD +++ b/src/scala/io/bazel/rules_scala/dottyijar/BUILD @@ -12,7 +12,7 @@ scala_library_for_plugin_bootstrapping( deps = [ "//src/scala/io/bazel/rules_scala/dottyijar/tasty", "//src/scala/io/bazel/rules_scala/dottyijar/tasty/format", - "@com_softwaremill_common_tagging_3_6_2", + "//src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric", "@dev_zio_izumi_reflect_3_6_2", ], ) diff --git a/src/scala/io/bazel/rules_scala/dottyijar/TastyUpdater.scala b/src/scala/io/bazel/rules_scala/dottyijar/TastyUpdater.scala index 251c48dc6..4c194a293 100644 --- a/src/scala/io/bazel/rules_scala/dottyijar/TastyUpdater.scala +++ b/src/scala/io/bazel/rules_scala/dottyijar/TastyUpdater.scala @@ -1,9 +1,9 @@ package io.bazel.rules_scala.dottyijar -import com.softwaremill.tagging.* import java.util.UUID import io.bazel.rules_scala.dottyijar.tasty.* -import io.bazel.rules_scala.dottyijar.tasty.format.{MarkerType, TastyFormat, TastyReader, TastyReference, TastyWriter, Unsigned} +import io.bazel.rules_scala.dottyijar.tasty.format.{MarkerType, TastyFormat, TastyReader, TastyReference, TastyWriter} +import io.bazel.rules_scala.dottyijar.tasty.numeric.UnsignedInt import scala.collection.mutable /** @@ -18,17 +18,17 @@ private class ContextualTastyUpdater(oldTasty: Tasty) { private var nextNameIndex = oldTasty.nameTable.names.length private val addedNames = mutable.ArrayBuffer.empty[TastyName] private def getOrCreateNameReference(name: TastyName): TastyNameReference = oldTasty.nameTable.names.zipWithIndex - .collectFirst { case (`name`, i) => TastyNameReference(i.taggedWith[Unsigned]) } + .collectFirst { case (`name`, i) => TastyNameReference(UnsignedInt(i)) } .getOrElse( TastyNameReference( - { + UnsignedInt({ val i = nextNameIndex nextNameIndex += 1 addedNames += name i - }.taggedWith[Unsigned], + }), ), ) @@ -147,7 +147,9 @@ private object TastyUpdater { if (!result(i)) { result += i - TastyElement.collect(tasty.nameTable.names(i)) { case TastyNameReference(j, _) => stack += j }.foreach { _ => } + TastyElement + .collect(tasty.nameTable.names(i)) { case TastyNameReference(j, _) => stack += j.value } + .foreach { _ => } } } @@ -157,7 +159,7 @@ private object TastyUpdater { private def getUsedNameIndicesInSection[A <: TastySectionPayload]( section: TastySection[A], )(using TastyElement[TastySection[A]]): Iterable[Int] = - TastyElement.collect(section) { case TastyNameReference(i, _) => i } + TastyElement.collect(section) { case TastyNameReference(i, _) => i.value } private def removeDanglingSharedValues[A: TastyElement]( node: A, @@ -228,7 +230,7 @@ private object TastyUpdater { child => child match { case TastyNameReference(i, information) => - TastyNameReference(nameIndexUpdates(i).taggedWith[Unsigned], information).asInstanceOf[B] + TastyNameReference(UnsignedInt(nameIndexUpdates(i.value)), information).asInstanceOf[B] case _ => updateNameReferences(child) } diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/BUILD b/src/scala/io/bazel/rules_scala/dottyijar/tasty/BUILD index ed8ac95aa..c722c6750 100644 --- a/src/scala/io/bazel/rules_scala/dottyijar/tasty/BUILD +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/BUILD @@ -14,7 +14,7 @@ scala_library_for_plugin_bootstrapping( visibility = ["//visibility:public"], deps = [ "//src/scala/io/bazel/rules_scala/dottyijar/tasty/format", - "@com_softwaremill_common_tagging_3_6_2", + "//src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric", "@dev_zio_izumi_reflect_3_6_2", "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded_3_6_2", "@io_bazel_rules_scala_scala_compiler_3_6_2", @@ -56,6 +56,7 @@ scala_specs2_junit_test( ":tasty", ":test", "//src/scala/io/bazel/rules_scala/dottyijar/tasty/format", + "//src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric", "@io_bazel_rules_scala_org_specs2_specs2_common_3_6_2", "@io_bazel_rules_scala_org_specs2_specs2_core_3_6_2", "@io_bazel_rules_scala_org_specs2_specs2_junit", diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.scala index 788f041bc..41fa5cd8d 100644 --- a/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.scala +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/Tasty.scala @@ -1,9 +1,9 @@ package io.bazel.rules_scala.dottyijar.tasty -import com.softwaremill.tagging.* import dotty.tools.dotc.util.Spans.Span import dotty.tools.tasty.TastyFormat as DottyTastyFormat import io.bazel.rules_scala.dottyijar.tasty.format.* +import io.bazel.rules_scala.dottyijar.tasty.numeric.{SignedInt, SignedLong, UnsignedInt} import java.util.UUID import scala.annotation.nowarn import scala.util.control.NonFatal @@ -887,18 +887,18 @@ object TastyParameterSignature { given TastyFormat[TastyParameterSignature] = TastyFormat( reader => { - val value = reader.readSignedInt() + val value = reader.readSignedInt().value if (value < 0) { TypeParameterSectionLength(-value) } else { - TermParameter(TastyNameReference(value.taggedWith[Unsigned])) + TermParameter(TastyNameReference(UnsignedInt(value))) } }, (writer, value) => value match { - case TypeParameterSectionLength(length, _) => writer.writeSignedInt((-length).taggedWith[Signed]) - case TermParameter(name, _) => writer.writeSignedInt(name.i.taggedWith[Signed]) + case TypeParameterSectionLength(length, _) => writer.writeSignedInt(SignedInt(-length)) + case TermParameter(name, _) => writer.writeSignedInt(SignedInt(name.i.value)) }, ) } @@ -1004,7 +1004,7 @@ case class TastyNameTable(names: Vector[TastyName]) { |${names.zipWithIndex.map { case (name, i) => s" $i: $name" }.mkString("\n")} |)""".stripMargin - def apply(reference: TastyNameReference): TastyName = names(reference.i) + def apply(reference: TastyNameReference): TastyName = names(reference.i.value) } object TastyNameTable { @@ -1019,7 +1019,7 @@ object TastySection { reader => { val nameReference = summon[TastyFormat[TastyNameReference]].read(reader) val name = nameTable(nameReference) - val payload = reader.readWithLength(reader.readUnsignedInt()) { reader => + val payload = reader.readWithLength(reader.readUnsignedInt().value) { reader => name match { case TastyName.Simple("ASTs", _) => summon[TastyFormat[TastySectionPayload.Asts]].read(reader) case TastyName.Simple("Positions", _) => summon[TastyFormat[TastySectionPayload.Positions]].read(reader) @@ -1076,12 +1076,12 @@ object TastySectionPayload { object LineSizes { given TastyFormat[LineSizes] = TastyFormat( reader => { - val length = reader.readUnsignedInt() + val length = reader.readUnsignedInt().value LineSizes(Range(0, length).map(_ => reader.readUnsignedInt()).toList) }, (writer, lineSizes) => { - writer.writeUnsignedInt(lineSizes.sizes.length.taggedWith[Unsigned]) + writer.writeUnsignedInt(UnsignedInt(lineSizes.sizes.length)) lineSizes.sizes.foreach(writer.writeUnsignedInt) }, @@ -1093,7 +1093,7 @@ object TastySectionPayload { object Delta { given TastyFormat[Delta] = TastyFormat( reader => { - val header = reader.readUnsignedInt() + val header = reader.readUnsignedInt().value val addressDelta = header >> 3 val start = Option.when(((header >>> 2) & 0x1) == 1)(reader.readSignedInt()) val end = Option.when(((header >>> 1) & 0x1) == 1)(reader.readSignedInt()) @@ -1102,12 +1102,12 @@ object TastySectionPayload { Delta(addressDelta, start, end, point) }, (writer, delta) => { - val header = ( + val header = UnsignedInt( (delta.addressDelta << 3) | ((if (delta.start.isDefined) 1 else 0) << 2) | ((if (delta.end.isDefined) 1 else 0) << 1) | (if (delta.point.isDefined) 1 else 0) - ).taggedWith[Unsigned] + ) writer.writeUnsignedInt(header) @@ -1126,7 +1126,7 @@ object TastySectionPayload { private given TastyFormat[Delta | Source] = TastyFormat( reader => - if (reader.peek(_.readUnsignedInt()) == DottyTastyFormat.SOURCE) { + if (reader.peek(_.readUnsignedInt().value) == DottyTastyFormat.SOURCE) { reader.readUnsignedInt() summon[TastyFormat[Source]].read(reader) @@ -1137,7 +1137,7 @@ object TastySectionPayload { value match { case delta: Delta => summon[TastyFormat[Delta]].write(writer, delta) case source: Source => - writer.writeUnsignedInt(DottyTastyFormat.SOURCE.taggedWith[Unsigned]) + writer.writeUnsignedInt(UnsignedInt(DottyTastyFormat.SOURCE)) summon[TastyFormat[Source]].write(writer, source) }, @@ -1377,7 +1377,7 @@ case class TastyTemplate( object TastyTemplate { private val underlyingTastyFormat = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val typeParameters = reader.readWhile( !reader.isAtEnd && summon[TastySumType[TastyTypeParameter]].peekIsVariant(reader), )(summon[TastyFormat[TastyTypeParameter]].read(reader)) @@ -1600,7 +1600,7 @@ object TastyTerm { object Inlined { given TastyFormat[Inlined] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val expression = summon[TastyFormat[TastyTerm]].read(reader) val call = Option.when(!reader.isAtEnd && summon[TastySumType[TastyTypeTree]].peekIsVariant(reader))( summon[TastyFormat[TastyTypeTree]].read(reader), @@ -1681,7 +1681,7 @@ object TastyTerm { object Match { given TastyFormat[Match] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val implicitTag = DottyTastyFormat.IMPLICIT.toByte val inlineTag = DottyTastyFormat.INLINE.toByte val (inline, scrutinee) = reader.peek(_.readByte()) match { @@ -1788,7 +1788,7 @@ object TastyTerm { object Unapply { given TastyFormat[Unapply] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val function = summon[TastyFormat[TastyTerm]].read(reader) val implicitArguments = reader.readWhile( !reader.isAtEnd && summon[TastySumType[TastyImplicitArgument]].peekIsVariant(reader), @@ -1864,7 +1864,7 @@ object TastyTerm { object Try { given TastyFormat[Try] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val expression = summon[TastyFormat[TastyTerm]].read(reader) val cases = reader.readWhile( !reader.isAtEnd && summon[TastySumType[TastyCaseDefinition]].peekIsVariant(reader), @@ -1964,7 +1964,7 @@ object TastyTerm { object SplicedPattern { given TastyFormat[SplicedPattern] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val pattern = summon[TastyFormat[TastyTerm]].read(reader) val patternType = summon[TastyFormat[TastyType]].read(reader) val typeArguments = reader.readWhile( @@ -2166,7 +2166,7 @@ object TastyType { object TypeBounds { given TastyFormat[TypeBounds] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val low = summon[TastyFormat[TastyType]].read(reader) val high = Option.when(!reader.isAtEnd && summon[TastySumType[TastyType]].peekIsVariant(reader))( summon[TastyFormat[TastyType]].read(reader), @@ -2340,7 +2340,7 @@ object TastyType { object Method { given TastyFormat[Method] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val result = summon[TastyFormat[TastyType]].read(reader) val parameters = reader.readWhile( !reader.isAtEnd && !summon[TastySumType[TastyModifier]].peekIsVariant(reader), @@ -2540,7 +2540,7 @@ object TastyTypeTree { object Lambda { given TastyFormat[Lambda] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val typeParameters = reader.readWhile( !reader.isAtEnd && summon[TastySumType[TastyTypeParameter]].peekIsVariant(reader), )(summon[TastyFormat[TastyTypeParameter]].read(reader)) @@ -2573,7 +2573,7 @@ object TastyTypeTree { object TypeBounds { given TastyFormat[TypeBounds] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val low = summon[TastyFormat[TastyTypeTree]].read(reader) val high = Option.unless(reader.isAtEnd)(summon[TastyFormat[TastyTypeTree]].read(reader)) val alias = Option.unless(reader.isAtEnd)(summon[TastyFormat[TastyTypeTree]].read(reader)) @@ -2610,7 +2610,7 @@ object TastyTypeTree { object Match { given TastyFormat[Match] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val boundOrScrutinee = summon[TastyFormat[TastyTypeTree]].read(reader) val (bound, scrutinee) = if (!reader.isAtEnd && summon[TastySumType[TastyTypeTree]].peekIsVariant(reader)) { (Some(boundOrScrutinee), summon[TastyFormat[TastyTypeTree]].read(reader)) @@ -2689,7 +2689,7 @@ object TastyValOrDefDefinition { object Val { given TastyFormat[Val] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val name = summon[TastyFormat[TastyNameReference]].read(reader) val `type` = summon[TastyFormat[TastyTypeTree]].read(reader) val value = Option.when(!reader.isAtEnd && summon[TastySumType[TastyTerm]].peekIsVariant(reader))( @@ -2723,7 +2723,7 @@ object TastyValOrDefDefinition { object Def { private val underlyingTastyFormat: TastyFormat[Def] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val name = summon[TastyFormat[TastyNameReference]].read(reader) val parameters = reader.readWhile(summon[TastySumType[TastyParameter]].peekIsVariant(reader))( summon[TastyFormat[TastyParameter]].read(reader), diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/TastyElement.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/TastyElement.scala index 268e970c0..6a37a9dbe 100644 --- a/src/scala/io/bazel/rules_scala/dottyijar/tasty/TastyElement.scala +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/TastyElement.scala @@ -1,7 +1,8 @@ package io.bazel.rules_scala.dottyijar.tasty import dotty.tools.dotc.util.Spans.Span -import io.bazel.rules_scala.dottyijar.tasty.format.{MarkerType, SignedInt, SignedLong, TastyReferencableInformation, TastyReference, UnsignedInt} +import io.bazel.rules_scala.dottyijar.tasty.format.{MarkerType, TastyReferencableInformation, TastyReference} +import io.bazel.rules_scala.dottyijar.tasty.numeric.{SignedInt, SignedLong, UnsignedInt} import java.util.UUID import scala.annotation.nowarn import scala.collection.Factory diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/BUILD b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/BUILD index 5344714f8..de882f625 100644 --- a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/BUILD +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/BUILD @@ -6,7 +6,7 @@ scala_library_for_plugin_bootstrapping( scala_version = "3.6.2", visibility = ["//visibility:public"], deps = [ - "@com_softwaremill_common_tagging_3_6_2", + "//src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric", "@dev_zio_izumi_reflect_3_6_2", "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded_3_6_2", "@io_bazel_rules_scala_scala_compiler_3_6_2", diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyFormat.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyFormat.scala index b9234511d..cc37a6d3a 100644 --- a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyFormat.scala +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyFormat.scala @@ -1,6 +1,6 @@ package io.bazel.rules_scala.dottyijar.tasty.format -import com.softwaremill.tagging.* +import io.bazel.rules_scala.dottyijar.tasty.numeric.{SignedInt, SignedLong, UnsignedInt, UnsignedLong} import dotty.tools.dotc.util.Spans.Span import izumi.reflect.Tag import java.util.UUID @@ -30,13 +30,17 @@ trait TastyFormat[A] private[format] { } final def withLengthPrefixed: TastyFormat[A] = new TastyFormat[A] { - override def read(reader: TastyReader): A = reader.readWithLength(reader.readUnsignedInt())(TastyFormat.this.read) + override def read(reader: TastyReader): A = + reader.readWithLength(reader.readUnsignedInt().value)(TastyFormat.this.read) + override def write(writer: TastyWriter, value: A): Unit = writer.writeWithLengthPrefixed(TastyFormat.this.write(_, value)) } } object TastyFormat { + private val isDebuggingEnabled: Boolean = Option(System.getProperty("DEBUG_TASTYFORMAT")).contains("true") + def apply[A: Tag](read: TastyReader => A, write: (TastyWriter, A) => Unit): TastyFormat[A] = { val _read = read val _write = write @@ -58,7 +62,7 @@ object TastyFormat { factory: Factory[Element, SeqLike], ): TastyFormat[SeqLike] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => factory.fromSpecific(reader.readUntilEnd(summon[TastyFormat[Element]].read(reader))) }, (writer, value) => @@ -77,7 +81,7 @@ object TastyFormat { evidence2: Tuple.Last[Tuple.Append[Init, Option[Last]]] =:= Option[Last], ): TastyFormat[Tuple.Append[Init, Option[Last]]] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val init = summon[TastyFormat[Init]].read(reader) init :* Option.unless(reader.isAtEnd)(summon[TastyFormat[Last]].read(reader)) @@ -145,7 +149,7 @@ object TastyFormat { evidence2: Tuple.Last[Tuple.Append[Init, Last]] =:= Last, ): TastyFormat[Tuple.Append[Init, Last]] = TastyFormat( reader => - reader.readWithLength(reader.readUnsignedInt()) { reader => + reader.readWithLength(reader.readUnsignedInt().value) { reader => val init = summon[TastyFormat[Init]].read(reader) init :* summon[Factory[LastElement, Last]] @@ -161,7 +165,9 @@ object TastyFormat { given TastyFormat[SignedInt] = TastyFormat(_.readSignedInt(), (writer, value) => writer.writeSignedInt(value)) given TastyFormat[SignedLong] = TastyFormat(_.readSignedLong(), (writer, value) => writer.writeSignedLong(value)) - given TastyFormat[Span] = summon[TastyFormat[SignedLong]].bimap(new Span(_), _.coords.taggedWith[Signed]) + given TastyFormat[Span] = + summon[TastyFormat[SignedLong]].bimap(long => new Span(long.value), span => SignedLong(span.coords)) + given TastyFormat[String] = TastyFormat(_.readUtf8String(), (writer, value) => writer.writeUtf8String(value)) given TastyFormat[UnsignedInt] = TastyFormat(_.readUnsignedInt(), (writer, value) => writer.writeUnsignedInt(value)) given TastyFormat[UnsignedLong] = diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReader.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReader.scala index a076d518b..0a973079b 100644 --- a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReader.scala +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyReader.scala @@ -1,7 +1,7 @@ package io.bazel.rules_scala.dottyijar.tasty.format -import com.softwaremill.tagging.* import dotty.tools.tasty.TastyFormat as DottyTastyFormat +import io.bazel.rules_scala.dottyijar.tasty.numeric.{SignedInt, SignedLong, UnsignedInt, UnsignedLong} import java.nio.charset.StandardCharsets import java.util.UUID import scala.annotation.tailrec @@ -82,7 +82,7 @@ case class TastyReader private ( def readReference[RelativeTo <: MarkerType, Value](relativeTo: RelativeTo): TastyReference[RelativeTo, Value] = { val marker = markers.getOrElse(relativeTo, throw new MarkerNotSetException(relativeTo)) - val position = marker.position + readUnsignedInt() + val position = marker.position + readUnsignedInt().value referencesByTargetPosition .getOrElseUpdate( @@ -97,7 +97,7 @@ case class TastyReader private ( .asInstanceOf[TastyReference[RelativeTo, Value]] } - def readSignedInt(): SignedInt = readSignedLong().toInt.taggedWith[Signed] + def readSignedInt(): SignedInt = SignedInt(readSignedLong().value.toInt) def readSignedLong(): SignedLong = { var currentByte = readByte() var result: Long = (currentByte << 1).toByte >> 1 // Sign extend the first byte, using bit 6 as the sign @@ -107,10 +107,10 @@ case class TastyReader private ( result = (result << 7) | (currentByte & 0x7f) } - result.taggedWith[Signed] + SignedLong(result) } - def readUnsignedInt(): UnsignedInt = readUnsignedLong().toInt.taggedWith[Unsigned] + def readUnsignedInt(): UnsignedInt = UnsignedInt(readUnsignedLong().value.toInt) def readUnsignedLong(): UnsignedLong = { var currentByte = readByte() var result = 0L @@ -123,7 +123,7 @@ case class TastyReader private ( currentByte = readByte() } - result.taggedWith[Unsigned] + UnsignedLong(result) } def readUntilEnd[A](read: => A): List[A] = { @@ -134,7 +134,7 @@ case class TastyReader private ( result } - def readUtf8String(): String = new String(readNBytes(readUnsignedInt()), StandardCharsets.UTF_8) + def readUtf8String(): String = new String(readNBytes(readUnsignedInt().value), StandardCharsets.UTF_8) def readUuid(): UUID = new UUID(readUncompressedLong(), readUncompressedLong()) def readWithLength[A](length: Int)(read: TastyReader => A): A = { val reader = copy(end = start + length) diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyWriter.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyWriter.scala index a68f6fb3f..c579a2c6a 100644 --- a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyWriter.scala +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/TastyWriter.scala @@ -1,7 +1,7 @@ package io.bazel.rules_scala.dottyijar.tasty.format -import com.softwaremill.tagging.* import dotty.tools.tasty.TastyFormat as DottyTastyFormat +import io.bazel.rules_scala.dottyijar.tasty.numeric.{SignedInt, SignedLong, UnsignedInt, UnsignedLong} import java.nio.charset.StandardCharsets import java.util.UUID import scala.collection.mutable @@ -64,7 +64,7 @@ case class TastyWriter private ( writeUnsignedLongWithWidth(0, TastyWriter.referenceWidth) } - def writeSignedInt(int: SignedInt): Unit = writeSignedLong(int.toLong.taggedWith[Signed]) + def writeSignedInt(int: SignedInt): Unit = writeSignedLong(SignedLong(int.value.toLong)) /** * This method is copied from this one in Dotty: @@ -83,16 +83,16 @@ case class TastyWriter private ( writeByte((long & 0x7f).toByte) } - val prefix = long >> 7 + val prefix = long.value >> 7 - if (prefix != 0L - ((long >> 6) & 1)) { + if (prefix != 0L - ((long.value >> 6) & 1)) { writePrefix(prefix) } - writeByte(((long & 0x7f) | 0x80).toByte) + writeByte(((long.value & 0x7f) | 0x80).toByte) } - def writeUnsignedInt(int: UnsignedInt): Unit = writeUnsignedLong(int.toLong.taggedWith[Unsigned]) + def writeUnsignedInt(int: UnsignedInt): Unit = writeUnsignedLong(UnsignedLong(int.value.toLong)) def writeUnsignedLong(long: UnsignedLong): Unit = { def writePrefix(long: Long): Unit = { val prefix = long >> 7 @@ -104,19 +104,19 @@ case class TastyWriter private ( writeByte((long & 0x7f).toByte) } - val prefix = long >> 7 + val prefix = long.value >> 7 if (prefix != 0) { writePrefix(prefix) } - writeByte(((long & 0x7f) | 0x80).toByte) + writeByte(((long.value & 0x7f) | 0x80).toByte) } def writeUtf8String(string: String): Unit = { val bytes = string.getBytes(StandardCharsets.UTF_8) - writeUnsignedInt(bytes.length.taggedWith[Unsigned]) + writeUnsignedInt(UnsignedInt(bytes.length)) writeBytes(bytes) } @@ -138,7 +138,7 @@ case class TastyWriter private ( val buffer = bufferWriter.toArray - writeUnsignedInt(buffer.length.taggedWith[Unsigned]) + writeUnsignedInt(UnsignedInt(buffer.length)) markers ++= bufferWriter.markers.view.map { case (markerType, marker) => (markerType, marker.copy(position = start + marker.position)) diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/package.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/package.scala deleted file mode 100644 index ebdea1dcd..000000000 --- a/src/scala/io/bazel/rules_scala/dottyijar/tasty/format/package.scala +++ /dev/null @@ -1,15 +0,0 @@ -package io.bazel.rules_scala.dottyijar.tasty - -import com.softwaremill.tagging.* - -package object format { - private val isDebuggingEnabled: Boolean = Option(System.getProperty("DEBUG_TASTYFORMAT")).contains("true") - - trait Signed - trait Unsigned - - type SignedInt = Int @@ Signed - type SignedLong = Long @@ Signed - type UnsignedInt = Int @@ Unsigned - type UnsignedLong = Long @@ Unsigned -} diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/BUILD b/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/BUILD new file mode 100644 index 000000000..6328ffb26 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/BUILD @@ -0,0 +1,8 @@ +load("//scala:scala.bzl", "scala_library_for_plugin_bootstrapping") + +scala_library_for_plugin_bootstrapping( + name = "numeric", + srcs = glob(["*.scala"]), + scala_version = "3.6.2", + visibility = ["//visibility:public"], +) diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/SignedInt.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/SignedInt.scala new file mode 100644 index 000000000..c3405fa7e --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/SignedInt.scala @@ -0,0 +1,3 @@ +package io.bazel.rules_scala.dottyijar.tasty.numeric + +case class SignedInt(value: Int) extends AnyVal diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/SignedLong.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/SignedLong.scala new file mode 100644 index 000000000..32a80c967 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/SignedLong.scala @@ -0,0 +1,3 @@ +package io.bazel.rules_scala.dottyijar.tasty.numeric + +case class SignedLong(value: Long) extends AnyVal diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/UnsignedInt.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/UnsignedInt.scala new file mode 100644 index 000000000..de7d91e49 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/UnsignedInt.scala @@ -0,0 +1,3 @@ +package io.bazel.rules_scala.dottyijar.tasty.numeric + +case class UnsignedInt(value: Int) extends AnyVal diff --git a/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/UnsignedLong.scala b/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/UnsignedLong.scala new file mode 100644 index 000000000..36ef7abc0 --- /dev/null +++ b/src/scala/io/bazel/rules_scala/dottyijar/tasty/numeric/UnsignedLong.scala @@ -0,0 +1,3 @@ +package io.bazel.rules_scala.dottyijar.tasty.numeric + +case class UnsignedLong(value: Long) extends AnyVal diff --git a/third_party/repositories/scala_3_1.bzl b/third_party/repositories/scala_3_1.bzl index 15b979d1f..8c25710aa 100644 --- a/third_party/repositories/scala_3_1.bzl +++ b/third_party/repositories/scala_3_1.bzl @@ -115,13 +115,6 @@ artifacts = { "@io_bazel_rules_scala_scala_library_2", ], }, - "com_softwaremill_common_tagging": { - "artifact": "com.softwaremill.common:tagging_3:2.3.5", - "sha256": "06d2b7578fcd272791a20dbe8e2f279162733dcd9293b38a87060dc5080d0382", - "deps": [ - "@io_bazel_rules_scala_scala_library", - ], - }, "com_twitter__scalding_date": { "testonly": True, "artifact": "com.twitter:scalding-date_2.13:0.17.0", diff --git a/third_party/repositories/scala_3_2.bzl b/third_party/repositories/scala_3_2.bzl index 7e3cc7cb7..6b09e8b36 100644 --- a/third_party/repositories/scala_3_2.bzl +++ b/third_party/repositories/scala_3_2.bzl @@ -115,13 +115,6 @@ artifacts = { "@io_bazel_rules_scala_scala_library_2", ], }, - "com_softwaremill_common_tagging": { - "artifact": "com.softwaremill.common:tagging_3:2.3.5", - "sha256": "06d2b7578fcd272791a20dbe8e2f279162733dcd9293b38a87060dc5080d0382", - "deps": [ - "@io_bazel_rules_scala_scala_library", - ], - }, "com_twitter__scalding_date": { "testonly": True, "artifact": "com.twitter:scalding-date_2.13:0.17.0", diff --git a/third_party/repositories/scala_3_3.bzl b/third_party/repositories/scala_3_3.bzl index 9fae41295..bd8f61258 100644 --- a/third_party/repositories/scala_3_3.bzl +++ b/third_party/repositories/scala_3_3.bzl @@ -115,13 +115,6 @@ artifacts = { "@io_bazel_rules_scala_scala_library_2", ], }, - "com_softwaremill_common_tagging": { - "artifact": "com.softwaremill.common:tagging_3:2.3.5", - "sha256": "06d2b7578fcd272791a20dbe8e2f279162733dcd9293b38a87060dc5080d0382", - "deps": [ - "@io_bazel_rules_scala_scala_library", - ], - }, "com_twitter__scalding_date": { "testonly": True, "artifact": "com.twitter:scalding-date_2.13:0.17.0", diff --git a/third_party/repositories/scala_3_4.bzl b/third_party/repositories/scala_3_4.bzl index 41006d5b3..659852c94 100644 --- a/third_party/repositories/scala_3_4.bzl +++ b/third_party/repositories/scala_3_4.bzl @@ -115,13 +115,6 @@ artifacts = { "@io_bazel_rules_scala_scala_library_2", ], }, - "com_softwaremill_common_tagging": { - "artifact": "com.softwaremill.common:tagging_3:2.3.5", - "sha256": "06d2b7578fcd272791a20dbe8e2f279162733dcd9293b38a87060dc5080d0382", - "deps": [ - "@io_bazel_rules_scala_scala_library", - ], - }, "com_twitter__scalding_date": { "testonly": True, "artifact": "com.twitter:scalding-date_2.13:0.17.0", diff --git a/third_party/repositories/scala_3_5.bzl b/third_party/repositories/scala_3_5.bzl index 529b65cb7..d693476ee 100644 --- a/third_party/repositories/scala_3_5.bzl +++ b/third_party/repositories/scala_3_5.bzl @@ -115,13 +115,6 @@ artifacts = { "@io_bazel_rules_scala_scala_library_2", ], }, - "com_softwaremill_common_tagging": { - "artifact": "com.softwaremill.common:tagging_3:2.3.5", - "sha256": "06d2b7578fcd272791a20dbe8e2f279162733dcd9293b38a87060dc5080d0382", - "deps": [ - "@io_bazel_rules_scala_scala_library", - ], - }, "com_twitter__scalding_date": { "testonly": True, "artifact": "com.twitter:scalding-date_2.13:0.17.0", diff --git a/third_party/repositories/scala_3_6.bzl b/third_party/repositories/scala_3_6.bzl index b50fa1ccf..b2b2ab086 100644 --- a/third_party/repositories/scala_3_6.bzl +++ b/third_party/repositories/scala_3_6.bzl @@ -115,13 +115,6 @@ artifacts = { "@io_bazel_rules_scala_scala_library_2", ], }, - "com_softwaremill_common_tagging": { - "artifact": "com.softwaremill.common:tagging_3:2.3.5", - "sha256": "06d2b7578fcd272791a20dbe8e2f279162733dcd9293b38a87060dc5080d0382", - "deps": [ - "@io_bazel_rules_scala_scala_library", - ], - }, "com_twitter__scalding_date": { "testonly": True, "artifact": "com.twitter:scalding-date_2.13:0.17.0", From 4915b4bf6637b0f34943f44884c71ea41587a982 Mon Sep 17 00:00:00 2001 From: Jaden Peterson Date: Wed, 22 Jan 2025 14:55:46 -0500 Subject: [PATCH 7/7] Support versions of Scala 3 prior to v3.4.0 --- scala/private/phases/phase_compile.bzl | 13 ++++++++++++- scripts/create_repository.py | 9 +++++++-- third_party/repositories/scala_3_1.bzl | 20 ++++++++++---------- third_party/repositories/scala_3_2.bzl | 20 ++++++++++---------- third_party/repositories/scala_3_3.bzl | 16 ++++++++-------- third_party/repositories/scala_3_4.bzl | 16 ++++++++-------- third_party/repositories/scala_3_5.bzl | 16 ++++++++-------- third_party/repositories/scala_3_6.bzl | 16 ++++++++-------- 8 files changed, 71 insertions(+), 55 deletions(-) diff --git a/scala/private/phases/phase_compile.bzl b/scala/private/phases/phase_compile.bzl index 7b78abefa..c1df0ada9 100644 --- a/scala/private/phases/phase_compile.bzl +++ b/scala/private/phases/phase_compile.bzl @@ -262,7 +262,10 @@ def _compile_or_empty( ) def _build_ijar(ctx): - if ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scala_version.startswith("2."): + scala_version = ctx.toolchains["@io_bazel_rules_scala//scala:toolchain_type"].scala_version + is_scala_2 = scala_version.startswith("2.") + + if is_scala_2: return java_common.run_ijar( ctx.actions, jar = ctx.outputs.jar, @@ -270,6 +273,14 @@ def _build_ijar(ctx): java_toolchain = specified_java_compile_toolchain(ctx), ) + is_scala_3_3_or_lower = scala_version.startswith("3.") and int(scala_version.split(".")[1]) < 4 + + # Prior to Scala v3.4.0, TASTy files couldn't be read directly without a `.class` file present and its + # "TASTY" attributes preserved: + # https://github.com/scala/scala3/pull/17594 + if is_scala_3_3_or_lower: + return ctx.outputs.jar + output = ctx.actions.declare_file("{}-ijar.jar".format(ctx.label.name)) arguments = ctx.actions.args() arguments.add(ctx.outputs.jar) diff --git a/scripts/create_repository.py b/scripts/create_repository.py index 2c6463886..8931096c1 100755 --- a/scripts/create_repository.py +++ b/scripts/create_repository.py @@ -93,7 +93,9 @@ def select_root_artifacts(scala_version, scala_major, is_scala_3) -> List[str]: protoc_bridge_version = '0.7.14' if is_scala_3: - specs2_version = '4.20.9' + # Versions greater than v4.20.0 depend on a version of the Scala standard library greater than v3.1.3. This is a + # problem because Scala v3.1.3, which we support, needs to use a matching version of the Scala standard library. + specs2_version = '4.20.0' elif scala_major == '2.11': specs2_version = '4.10.6' else: @@ -129,7 +131,10 @@ def select_root_artifacts(scala_version, scala_major, is_scala_3) -> List[str]: if is_scala_3: root_artifacts.extend([ - 'dev.zio:izumi-reflect_3:2.3.10', + # Versions of izumi-reflect greater than v2.2.1 depend on a version of the Scala standard library greater + # than v3.1.3. This is a problem because Scala v3.1.3, which we support, needs to use a matching version of + # the Scala standard library. + 'dev.zio:izumi-reflect_3:2.2.1', f'org.jline:jline-reader:{JLINE_VERSION}', f'org.jline:jline-terminal:{JLINE_VERSION}', f'org.jline:jline-terminal-jna:{JLINE_VERSION}', diff --git a/third_party/repositories/scala_3_1.bzl b/third_party/repositories/scala_3_1.bzl index 8c25710aa..a538969f9 100644 --- a/third_party/repositories/scala_3_1.bzl +++ b/third_party/repositories/scala_3_1.bzl @@ -133,16 +133,16 @@ artifacts = { "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", }, "dev_zio_izumi_reflect": { - "artifact": "dev.zio:izumi-reflect_3:2.3.10", - "sha256": "9f4e23968f00eaea2132337b94faa9e52cc7f159cce319c2f112c36d2c29d55d", + "artifact": "dev.zio:izumi-reflect_3:2.2.1", + "sha256": "1228bff91fa585b227427b3b8b93bffaf64b22727ea6a2c160476f00434d3a10", "deps": [ "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded", "@io_bazel_rules_scala_scala_library", ], }, "dev_zio_izumi_reflect_thirdparty_boopickle_shaded": { - "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.3.10", - "sha256": "3c36e0e0c6bc18f5c1a0ffbf820c3783b472690afc56105e9c7c8b81357b5661", + "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.2.1", + "sha256": "34c34cbf4c18f725ba3860bc9f09dfeaee13430cccc72711cbac711bb379b290", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -258,8 +258,8 @@ artifacts = { "sha256": "dca9bcd395deffca77c1d3919b4cc998234025059a892b10c3674c9a37d6dc9f", }, "io_bazel_rules_scala_scala_library": { - "artifact": "org.scala-lang:scala3-library_3:3.3.3", - "sha256": "16fe064f1373ed6f098d3d9f812a398ed5075db4bf2721c04e630502cb352816", + "artifact": "org.scala-lang:scala3-library_3:3.1.3", + "sha256": "1ac79970d94a1762ce6af4208820b4fa4c70093409decaad85c69d8b5f46e422", "deps": [ "@io_bazel_rules_scala_scala_library_2", ], @@ -301,8 +301,8 @@ artifacts = { ], }, "io_bazel_rules_scala_scala_xml": { - "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", - "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", + "artifact": "org.scala-lang.modules:scala-xml_3:2.1.0", + "sha256": "48f22343575f4b1d6550eecc42d4b7f0a0d30223c72f015d8d893feab4cbeecd", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -525,8 +525,8 @@ artifacts = { ], }, "org_portable_scala_portable_scala_reflect": { - "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.3", - "sha256": "920f62979293069cf721865f931e42f9f7b0b2720ee9f6a9ddff76a19ecf8d4e", + "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.1", + "sha256": "11f2f59d0c228912811095025b36ce58a025a8397851d773295c8ad7862d8488", "deps": [ "@io_bazel_rules_scala_scala_library_2", ], diff --git a/third_party/repositories/scala_3_2.bzl b/third_party/repositories/scala_3_2.bzl index 6b09e8b36..6b3a3796f 100644 --- a/third_party/repositories/scala_3_2.bzl +++ b/third_party/repositories/scala_3_2.bzl @@ -133,16 +133,16 @@ artifacts = { "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", }, "dev_zio_izumi_reflect": { - "artifact": "dev.zio:izumi-reflect_3:2.3.10", - "sha256": "9f4e23968f00eaea2132337b94faa9e52cc7f159cce319c2f112c36d2c29d55d", + "artifact": "dev.zio:izumi-reflect_3:2.2.1", + "sha256": "1228bff91fa585b227427b3b8b93bffaf64b22727ea6a2c160476f00434d3a10", "deps": [ "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded", "@io_bazel_rules_scala_scala_library", ], }, "dev_zio_izumi_reflect_thirdparty_boopickle_shaded": { - "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.3.10", - "sha256": "3c36e0e0c6bc18f5c1a0ffbf820c3783b472690afc56105e9c7c8b81357b5661", + "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.2.1", + "sha256": "34c34cbf4c18f725ba3860bc9f09dfeaee13430cccc72711cbac711bb379b290", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -258,8 +258,8 @@ artifacts = { "sha256": "f07bab6250d718613f0f8250cc61cc23217c4fd84c410c3af43b8098fff31f69", }, "io_bazel_rules_scala_scala_library": { - "artifact": "org.scala-lang:scala3-library_3:3.3.3", - "sha256": "16fe064f1373ed6f098d3d9f812a398ed5075db4bf2721c04e630502cb352816", + "artifact": "org.scala-lang:scala3-library_3:3.2.2", + "sha256": "f96317c57a5beae2cb16607d2b99cba7b136a96416e736966e5955e6608d868b", "deps": [ "@io_bazel_rules_scala_scala_library_2", ], @@ -301,8 +301,8 @@ artifacts = { ], }, "io_bazel_rules_scala_scala_xml": { - "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", - "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", + "artifact": "org.scala-lang.modules:scala-xml_3:2.1.0", + "sha256": "48f22343575f4b1d6550eecc42d4b7f0a0d30223c72f015d8d893feab4cbeecd", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -525,8 +525,8 @@ artifacts = { ], }, "org_portable_scala_portable_scala_reflect": { - "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.3", - "sha256": "920f62979293069cf721865f931e42f9f7b0b2720ee9f6a9ddff76a19ecf8d4e", + "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.1", + "sha256": "11f2f59d0c228912811095025b36ce58a025a8397851d773295c8ad7862d8488", "deps": [ "@io_bazel_rules_scala_scala_library_2", ], diff --git a/third_party/repositories/scala_3_3.bzl b/third_party/repositories/scala_3_3.bzl index bd8f61258..693016775 100644 --- a/third_party/repositories/scala_3_3.bzl +++ b/third_party/repositories/scala_3_3.bzl @@ -133,16 +133,16 @@ artifacts = { "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", }, "dev_zio_izumi_reflect": { - "artifact": "dev.zio:izumi-reflect_3:2.3.10", - "sha256": "9f4e23968f00eaea2132337b94faa9e52cc7f159cce319c2f112c36d2c29d55d", + "artifact": "dev.zio:izumi-reflect_3:2.2.1", + "sha256": "1228bff91fa585b227427b3b8b93bffaf64b22727ea6a2c160476f00434d3a10", "deps": [ "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded", "@io_bazel_rules_scala_scala_library", ], }, "dev_zio_izumi_reflect_thirdparty_boopickle_shaded": { - "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.3.10", - "sha256": "3c36e0e0c6bc18f5c1a0ffbf820c3783b472690afc56105e9c7c8b81357b5661", + "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.2.1", + "sha256": "34c34cbf4c18f725ba3860bc9f09dfeaee13430cccc72711cbac711bb379b290", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -302,8 +302,8 @@ artifacts = { ], }, "io_bazel_rules_scala_scala_xml": { - "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", - "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", + "artifact": "org.scala-lang.modules:scala-xml_3:2.1.0", + "sha256": "48f22343575f4b1d6550eecc42d4b7f0a0d30223c72f015d8d893feab4cbeecd", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -526,8 +526,8 @@ artifacts = { ], }, "org_portable_scala_portable_scala_reflect": { - "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.3", - "sha256": "920f62979293069cf721865f931e42f9f7b0b2720ee9f6a9ddff76a19ecf8d4e", + "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.1", + "sha256": "11f2f59d0c228912811095025b36ce58a025a8397851d773295c8ad7862d8488", "deps": [ "@io_bazel_rules_scala_scala_library_2", ], diff --git a/third_party/repositories/scala_3_4.bzl b/third_party/repositories/scala_3_4.bzl index 659852c94..8ab050088 100644 --- a/third_party/repositories/scala_3_4.bzl +++ b/third_party/repositories/scala_3_4.bzl @@ -133,16 +133,16 @@ artifacts = { "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", }, "dev_zio_izumi_reflect": { - "artifact": "dev.zio:izumi-reflect_3:2.3.10", - "sha256": "9f4e23968f00eaea2132337b94faa9e52cc7f159cce319c2f112c36d2c29d55d", + "artifact": "dev.zio:izumi-reflect_3:2.2.1", + "sha256": "1228bff91fa585b227427b3b8b93bffaf64b22727ea6a2c160476f00434d3a10", "deps": [ "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded", "@io_bazel_rules_scala_scala_library", ], }, "dev_zio_izumi_reflect_thirdparty_boopickle_shaded": { - "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.3.10", - "sha256": "3c36e0e0c6bc18f5c1a0ffbf820c3783b472690afc56105e9c7c8b81357b5661", + "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.2.1", + "sha256": "34c34cbf4c18f725ba3860bc9f09dfeaee13430cccc72711cbac711bb379b290", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -302,8 +302,8 @@ artifacts = { ], }, "io_bazel_rules_scala_scala_xml": { - "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", - "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", + "artifact": "org.scala-lang.modules:scala-xml_3:2.1.0", + "sha256": "48f22343575f4b1d6550eecc42d4b7f0a0d30223c72f015d8d893feab4cbeecd", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -526,8 +526,8 @@ artifacts = { ], }, "org_portable_scala_portable_scala_reflect": { - "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.3", - "sha256": "920f62979293069cf721865f931e42f9f7b0b2720ee9f6a9ddff76a19ecf8d4e", + "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.1", + "sha256": "11f2f59d0c228912811095025b36ce58a025a8397851d773295c8ad7862d8488", "deps": [ "@io_bazel_rules_scala_scala_library_2", ], diff --git a/third_party/repositories/scala_3_5.bzl b/third_party/repositories/scala_3_5.bzl index d693476ee..28a69ce24 100644 --- a/third_party/repositories/scala_3_5.bzl +++ b/third_party/repositories/scala_3_5.bzl @@ -133,16 +133,16 @@ artifacts = { "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", }, "dev_zio_izumi_reflect": { - "artifact": "dev.zio:izumi-reflect_3:2.3.10", - "sha256": "9f4e23968f00eaea2132337b94faa9e52cc7f159cce319c2f112c36d2c29d55d", + "artifact": "dev.zio:izumi-reflect_3:2.2.1", + "sha256": "1228bff91fa585b227427b3b8b93bffaf64b22727ea6a2c160476f00434d3a10", "deps": [ "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded", "@io_bazel_rules_scala_scala_library", ], }, "dev_zio_izumi_reflect_thirdparty_boopickle_shaded": { - "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.3.10", - "sha256": "3c36e0e0c6bc18f5c1a0ffbf820c3783b472690afc56105e9c7c8b81357b5661", + "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.2.1", + "sha256": "34c34cbf4c18f725ba3860bc9f09dfeaee13430cccc72711cbac711bb379b290", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -302,8 +302,8 @@ artifacts = { ], }, "io_bazel_rules_scala_scala_xml": { - "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", - "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", + "artifact": "org.scala-lang.modules:scala-xml_3:2.1.0", + "sha256": "48f22343575f4b1d6550eecc42d4b7f0a0d30223c72f015d8d893feab4cbeecd", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -526,8 +526,8 @@ artifacts = { ], }, "org_portable_scala_portable_scala_reflect": { - "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.3", - "sha256": "920f62979293069cf721865f931e42f9f7b0b2720ee9f6a9ddff76a19ecf8d4e", + "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.1", + "sha256": "11f2f59d0c228912811095025b36ce58a025a8397851d773295c8ad7862d8488", "deps": [ "@io_bazel_rules_scala_scala_library_2", ], diff --git a/third_party/repositories/scala_3_6.bzl b/third_party/repositories/scala_3_6.bzl index b2b2ab086..d6456808b 100644 --- a/third_party/repositories/scala_3_6.bzl +++ b/third_party/repositories/scala_3_6.bzl @@ -133,16 +133,16 @@ artifacts = { "sha256": "6d18fe25aa30b7e08b908cd21151d8f96e22965c640acd7751add9bbfe6137d4", }, "dev_zio_izumi_reflect": { - "artifact": "dev.zio:izumi-reflect_3:2.3.10", - "sha256": "9f4e23968f00eaea2132337b94faa9e52cc7f159cce319c2f112c36d2c29d55d", + "artifact": "dev.zio:izumi-reflect_3:2.2.1", + "sha256": "1228bff91fa585b227427b3b8b93bffaf64b22727ea6a2c160476f00434d3a10", "deps": [ "@dev_zio_izumi_reflect_thirdparty_boopickle_shaded", "@io_bazel_rules_scala_scala_library", ], }, "dev_zio_izumi_reflect_thirdparty_boopickle_shaded": { - "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.3.10", - "sha256": "3c36e0e0c6bc18f5c1a0ffbf820c3783b472690afc56105e9c7c8b81357b5661", + "artifact": "dev.zio:izumi-reflect-thirdparty-boopickle-shaded_3:2.2.1", + "sha256": "34c34cbf4c18f725ba3860bc9f09dfeaee13430cccc72711cbac711bb379b290", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -311,8 +311,8 @@ artifacts = { ], }, "io_bazel_rules_scala_scala_xml": { - "artifact": "org.scala-lang.modules:scala-xml_3:2.3.0", - "sha256": "3220723238102107ab83182468e5dbe351b081a0601386710ef46c81a95d38d0", + "artifact": "org.scala-lang.modules:scala-xml_3:2.1.0", + "sha256": "48f22343575f4b1d6550eecc42d4b7f0a0d30223c72f015d8d893feab4cbeecd", "deps": [ "@io_bazel_rules_scala_scala_library", ], @@ -535,8 +535,8 @@ artifacts = { ], }, "org_portable_scala_portable_scala_reflect": { - "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.3", - "sha256": "920f62979293069cf721865f931e42f9f7b0b2720ee9f6a9ddff76a19ecf8d4e", + "artifact": "org.portable-scala:portable-scala-reflect_2.13:1.1.1", + "sha256": "11f2f59d0c228912811095025b36ce58a025a8397851d773295c8ad7862d8488", "deps": [ "@io_bazel_rules_scala_scala_library_2", ],