diff --git a/.github/workflows/build-chocolatey.yml b/.github/workflows/build-chocolatey.yml deleted file mode 100644 index 9de87d8e5ad6..000000000000 --- a/.github/workflows/build-chocolatey.yml +++ /dev/null @@ -1,57 +0,0 @@ -################################################################################################### -### THIS IS A REUSABLE WORKFLOW TO BUILD SCALA WITH CHOCOLATEY ### -### HOW TO USE: ### -### ### -### NOTE: ### -### ### -################################################################################################### - - -name: Build 'scala' Chocolatey Package -run-name: Build 'scala' (${{ inputs.version }}) Chocolatey Package - -on: - workflow_call: - inputs: - version: - required: true - type : string - url: - required: true - type : string - digest: - required: true - type : string - -jobs: - build: - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - name: Replace the version placeholder - uses: richardrigutins/replace-in-files@v2 - with: - files: ./pkgs/chocolatey/scala.nuspec - search-text: '@LAUNCHER_VERSION@' - replacement-text: ${{ inputs.version }} - - name: Replace the URL placeholder - uses: richardrigutins/replace-in-files@v2 - with: - files: ./pkgs/chocolatey/tools/chocolateyInstall.ps1 - search-text: '@LAUNCHER_URL@' - replacement-text: ${{ inputs.url }} - - name: Replace the CHECKSUM placeholder - uses: richardrigutins/replace-in-files@v2 - with: - files: ./pkgs/chocolatey/tools/chocolateyInstall.ps1 - search-text: '@LAUNCHER_SHA256@' - replacement-text: ${{ inputs.digest }} - - name: Build the Chocolatey package (.nupkg) - run: choco pack ./pkgs/chocolatey/scala.nuspec --out ./pkgs/chocolatey - - name: Upload the Chocolatey package to GitHub - uses: actions/upload-artifact@v4 - with: - name: scala.nupkg - path: ./pkgs/chocolatey/scala.${{ inputs.version }}.nupkg - if-no-files-found: error - \ No newline at end of file diff --git a/.github/workflows/build-msi.yml b/.github/workflows/build-msi.yml deleted file mode 100644 index 14838c589d6a..000000000000 --- a/.github/workflows/build-msi.yml +++ /dev/null @@ -1,38 +0,0 @@ -################################################################################################### -### THIS IS A REUSABLE WORKFLOW TO BUILD SCALA MSI ### -### HOW TO USE: ### -### - THE RELEASE WORKFLOW SHOULD CALL THIS WORKFLOW ### -### - IT WILL UPLOAD TO GITHUB THE MSI FILE FOR SCALA UNDER THE 'scala.msi' NAME ### -### ### -### NOTE: ### -### - WE SHOULD BUILD SCALA USING JAVA 8 ### -################################################################################################### - -name: Build the MSI Package - -on: - workflow_call: - -env: - # Release only happends when triggering CI by pushing tag - RELEASEBUILD: ${{ startsWith(github.event.ref, 'refs/tags/') && 'yes' || 'no' }} - -jobs: - build: - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 - with: - distribution: 'adopt' - java-version: '8' - cache: 'sbt' - - name: Build MSI package - run: sbt 'dist-win-x86_64/Windows/packageBin' - env: - DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - - name: Upload MSI Artifact - uses: actions/upload-artifact@v4 - with: - name: scala.msi - path: ./dist/win-x86_64/target/windows/scala.msi diff --git a/.github/workflows/build-sdk.yml b/.github/workflows/build-sdk.yml deleted file mode 100644 index cd111df1a083..000000000000 --- a/.github/workflows/build-sdk.yml +++ /dev/null @@ -1,117 +0,0 @@ -################################################################################################### -### THIS IS A REUSABLE WORKFLOW TO BUILD THE SCALA LAUNCHERS ### -### HOW TO USE: ### -### - THSI WORKFLOW WILL PACKAGE THE ALL THE LAUNCHERS AND UPLOAD THEM TO GITHUB ARTIFACTS ### -### ### -### NOTE: ### -### - SEE THE WORFLOW FOR THE NAMES OF THE ARTIFACTS ### -################################################################################################### - - -name: Build Scala Launchers -run-name: Build Scala Launchers - -on: - workflow_call: - inputs: - java-version: - type : string - required : true - outputs: - universal-id: - description: ID of the `universal` package from GitHub Artifacts (Authentication Required) - value : ${{ jobs.build.outputs.universal-id }} - linux-x86_64-id: - description: ID of the `linux x86-64` package from GitHub Artifacts (Authentication Required) - value : ${{ jobs.build.outputs.linux-x86_64-id }} - linux-aarch64-id: - description: ID of the `linux aarch64` package from GitHub Artifacts (Authentication Required) - value : ${{ jobs.build.outputs.linux-aarch64-id }} - mac-x86_64-id: - description: ID of the `mac x86-64` package from GitHub Artifacts (Authentication Required) - value : ${{ jobs.build.outputs.mac-x86_64-id }} - mac-aarch64-id: - description: ID of the `mac aarch64` package from GitHub Artifacts (Authentication Required) - value : ${{ jobs.build.outputs.mac-aarch64-id }} - win-x86_64-id: - description: ID of the `win x86-64` package from GitHub Artifacts (Authentication Required) - value : ${{ jobs.build.outputs.win-x86_64-id }} - win-x86_64-digest: - description: The SHA256 of the uploaded artifact (`win x86-64`) - value : ${{ jobs.build.outputs.win-x86_64-digest }} - - -jobs: - build: - runs-on: ubuntu-latest - outputs: - universal-id : ${{ steps.universal.outputs.artifact-id }} - linux-x86_64-id : ${{ steps.linux-x86_64.outputs.artifact-id }} - linux-aarch64-id : ${{ steps.linux-aarch64.outputs.artifact-id }} - mac-x86_64-id : ${{ steps.mac-x86_64.outputs.artifact-id }} - mac-aarch64-id : ${{ steps.mac-aarch64.outputs.artifact-id }} - win-x86_64-id : ${{ steps.win-x86_64.outputs.artifact-id }} - win-x86_64-digest: ${{ steps.win-x86_64-digest.outputs.digest }} - env: - DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: ${{ inputs.java-version }} - cache : sbt - - uses: sbt/setup-sbt@v1 - - name: Build and pack the SDK (universal) - run : ./project/scripts/sbt dist/Universal/stage - - name: Build and pack the SDK (linux x86-64) - run : ./project/scripts/sbt dist-linux-x86_64/Universal/stage - - name: Build and pack the SDK (linux aarch64) - run : ./project/scripts/sbt dist-linux-aarch64/Universal/stage - - name: Build and pack the SDK (mac x86-64) - run : ./project/scripts/sbt dist-mac-x86_64/Universal/stage - - name: Build and pack the SDK (mac aarch64) - run : ./project/scripts/sbt dist-mac-aarch64/Universal/stage - - name: Build and pack the SDK (win x86-64) - run : ./project/scripts/sbt dist-win-x86_64/Universal/stage - - name: Upload zip archive to GitHub Artifact (universal) - uses: actions/upload-artifact@v4 - id : universal - with: - path: ./dist/target/universal/stage - name: scala3-universal - - name: Upload zip archive to GitHub Artifact (linux x86-64) - uses: actions/upload-artifact@v4 - id : linux-x86_64 - with: - path: ./dist/linux-x86_64/target/universal/stage - name: scala3-x86_64-pc-linux - - name: Upload zip archive to GitHub Artifact (linux aarch64) - uses: actions/upload-artifact@v4 - id : linux-aarch64 - with: - path: ./dist/linux-aarch64/target/universal/stage - name: scala3-aarch64-pc-linux - - name: Upload zip archive to GitHub Artifact (mac x86-64) - uses: actions/upload-artifact@v4 - id : mac-x86_64 - with: - path: ./dist/mac-x86_64/target/universal/stage - name: scala3-x86_64-apple-darwin - - name: Upload zip archive to GitHub Artifact (mac aarch64) - uses: actions/upload-artifact@v4 - id : mac-aarch64 - with: - path: ./dist/mac-aarch64/target/universal/stage - name: scala3-aarch64-apple-darwin - - name: Upload zip archive to GitHub Artifact (win x86-64) - uses: actions/upload-artifact@v4 - id : win-x86_64 - with: - path: ./dist/win-x86_64/target/universal/stage - name: scala3-x86_64-pc-win32 - - name: Compute SHA256 of the uploaded artifact (win x86-64) - id : win-x86_64-digest - run : | - curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -o artifact.zip -L https://api.github.com/repos/scala/scala3/actions/artifacts/${{ steps.win-x86_64.outputs.artifact-id }}/zip - echo "digest=$(sha256sum artifact.zip | cut -d " " -f 1)" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml deleted file mode 100644 index c8b5617fc534..000000000000 --- a/.github/workflows/ci.yaml +++ /dev/null @@ -1,928 +0,0 @@ -name: Dotty - -on: - push: - ## Be careful if you add or remove something here! Quoting from - ## : - ## - ## > If you define only tags/tags-ignore or only branches/branches-ignore, the - ## > workflow won't run for events affecting the undefined Git ref. If you - ## > define neither tags/tags-ignore or branches/branches-ignore, the workflow - ## > will run for events affecting either branches or tags. - ## - ## We want the CI to run on both branches and tags, so we should either have: - ## - both (tags or tags-ignore) and (branches or branches-ignore), - ## - or neither of them. - ## But it's important to not have only one or the other. - tags: - - '*' - branches-ignore: - - 'gh-readonly-queue/**' - - 'release-**' - - 'lts-**' - pull_request: - merge_group: - schedule: - - cron: '0 3 * * *' # Every day at 3 AM - workflow_dispatch: - -# Cancels any in-progress runs within the same group identified by workflow name and GH reference (branch or tag) -# For example it would: -# - terminate previous PR CI execution after pushing more changes to the same PR branch -# - terminate previous on-push CI run after merging new PR to main -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} - -env: - DOTTY_CI_RUN: true - DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - -# In this file, we set `--cpu-shares 4096` on every job. This might seem useless -# since it means that every container has the same weight which should be -# equivalent to doing nothing, but it turns out that OpenJDK computes -# `Runtime.getRuntime.availableProcessors` by dividing the cpu-shares value if -# it exists by 1024 (cf -# http://mail.openjdk.java.net/pipermail/hotspot-dev/2019-January/036087.html), -# so this means that we effectively run every job with 4 cores. This is much -# nicer than setting `--cpus 4` because the latter enforces CPU quotas and ends -# up slowing our jobs more than needed. It's equivalent to running the JVM with -# `-XX:ActiveProcessorCount=4`, but since our tests can spawn new JVM in many -# places, it would be very hard to ensure that this option is always passed to -# `java` (we could use the `_JAVA_OPTIONS` environment variable, but this prints -# text on stderr and so can break tests which check the output of a program). - -jobs: - test_non_bootstrapped: - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2024-10-18 - options: --cpu-shares 4096 - volumes: - - ${{ github.workspace }}/../../cache/sbt:/root/.sbt - - ${{ github.workspace }}/../../cache/ivy:/root/.ivy2/cache - - ${{ github.workspace }}/../../cache/general:/root/.cache - if: "github.event_name == 'schedule' && github.repository == 'scala/scala3' - || github.event_name == 'push' - || ( - github.event_name == 'pull_request' - && !contains(github.event.pull_request.body, '[skip ci]') - && contains(github.event.pull_request.body, '[test_non_bootstrapped]') - ) - || ( - github.event_name == 'workflow_dispatch' - && github.repository == 'scala/scala3' - )" - steps: - - name: Set JDK 17 as default - run: echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> $GITHUB_PATH - - ## Workaround for https://github.com/actions/runner/issues/2033 (See https://github.com/scala/scala3/pull/19720) - - name: Reset existing repo - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Checkout cleanup script - uses: actions/checkout@v4 - - - name: Cleanup - run: .github/workflows/cleanup.sh - - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Add SBT proxy repositories - run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - - name: Test - # DON'T add dist/Universal/stage! - # Adding dist/Universal/stage bootstraps the compiler - # which undermines the point of these tests: - # to quickly run the tests without the cost of bootstrapping - # and also to run tests when the compiler doesn't bootstrap - run: | - ./project/scripts/sbt ";compile ;test" - ./project/scripts/cmdTests - - test: - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2024-10-18 - options: --cpu-shares 4096 - volumes: - - ${{ github.workspace }}/../../cache/sbt:/root/.sbt - - ${{ github.workspace }}/../../cache/ivy:/root/.ivy2/cache - - ${{ github.workspace }}/../../cache/general:/root/.cache - if: "github.event_name == 'schedule' && github.repository == 'scala/scala3' - || github.event_name == 'push' - || github.event_name == 'merge_group' - || ( - github.event_name == 'pull_request' - && !contains(github.event.pull_request.body, '[skip ci]') - && !contains(github.event.pull_request.body, '[skip test]') - ) - || ( - github.event_name == 'workflow_dispatch' - && github.repository == 'scala/scala3' - )" - - steps: - - name: Set JDK 17 as default - run: echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> $GITHUB_PATH - - - name: Reset existing repo - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Checkout cleanup script - uses: actions/checkout@v4 - - - name: Cleanup - run: .github/workflows/cleanup.sh - - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Add SBT proxy repositories - run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - - name: Cmd Tests - run: | - ./project/scripts/buildScalaBinary - ./project/scripts/sbt ";scala3-bootstrapped/compile ;scala3-bootstrapped/test ;sbt-test/scripted scala2-compat/* ;scala3-compiler-bootstrapped/scala3CompilerCoursierTest:test" - ./project/scripts/cmdTests - ./project/scripts/bootstrappedOnlyCmdTests - - - name: Scala.js Test - run: | - ./project/scripts/sbt ";sjsSandbox/run ;sjsSandbox/test ;sjsJUnitTests/test ;set sjsJUnitTests/scalaJSLinkerConfig ~= switchToESModules ;sjsJUnitTests/test ;sjsCompilerTests/test" - - - name: Test with Scala 2 library TASTy (fast) - run: ./project/scripts/sbt ";set ThisBuild/Build.scala2Library := Build.Scala2LibraryTasty ;scala3-bootstrapped/testCompilation i5; scala3-bootstrapped/testCompilation tests/run/typelevel-peano.scala; scala3-bootstrapped/testOnly dotty.tools.backend.jvm.DottyBytecodeTests" # only test a subset of test to avoid doubling the CI execution time - - - name: Test with Scala 2 library with CC TASTy (fast) - run: ./project/scripts/sbt ";set ThisBuild/Build.scala2Library := Build.Scala2LibraryCCTasty; scala2-library-cc/compile; scala2-library-cc-tasty/compile; scala3-bootstrapped/testCompilation i3" - - test_scala2_library_tasty: - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2024-10-18 - options: --cpu-shares 4096 - volumes: - - ${{ github.workspace }}/../../cache/sbt:/root/.sbt - - ${{ github.workspace }}/../../cache/ivy:/root/.ivy2/cache - - ${{ github.workspace }}/../../cache/general:/root/.cache - if: "github.event_name == 'schedule' && github.repository == 'scala/scala3' - || ( - github.event_name == 'pull_request' - && contains(github.event.pull_request.body, '[test_scala2_library_tasty]') - ) - || ( - github.event_name == 'workflow_dispatch' - && github.repository == 'scala/scala3' - )" - - steps: - - name: Set JDK 17 as default - run: echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> $GITHUB_PATH - - - name: Reset existing repo - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Checkout cleanup script - uses: actions/checkout@v4 - - - name: Cleanup - run: .github/workflows/cleanup.sh - - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Add SBT proxy repositories - run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - - name: Test with Scala 2 library TASTy - run: ./project/scripts/sbt ";set ThisBuild/Build.scala2Library := Build.Scala2LibraryTasty ;scala3-bootstrapped/test" - - - name: Test with Scala 2 library with CC TASTy - run: ./project/scripts/sbt ";set ThisBuild/Build.scala2Library := Build.Scala2LibraryCCTasty ;scala3-bootstrapped/test" - - - test_windows_fast: - runs-on: [self-hosted, Windows] - if: "( - github.event_name == 'push' - && github.ref != 'refs/heads/main' - ) - || github.event_name == 'merge_group' - || ( - github.event_name == 'pull_request' - && !contains(github.event.pull_request.body, '[skip ci]') - && !contains(github.event.pull_request.body, '[skip test_windows_fast]') - )" - - steps: - - name: Reset existing repo - shell: cmd - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Test - run: sbt ";scala3-bootstrapped/compile; scala3-bootstrapped/testCompilation; scala3-presentation-compiler/test; scala3-language-server/test" - shell: cmd - - - name: build binary - run: sbt "dist-win-x86_64/Universal/stage" & bash -version - shell: cmd - - - name: cygwin tests - run: '"C:\Program Files\cygwin64\bin\bash" ./project/scripts/winCmdTests' - shell: cmd - - - name: msys tests - run: '"C:\Program Files\Git\bin\bash" ./project/scripts/winCmdTests' - shell: cmd - - - name: win tests - run: './project/scripts/winCmdTests.bat' - shell: cmd - - - name: Scala.js Test - run: sbt ";sjsJUnitTests/test ;set sjsJUnitTests/scalaJSLinkerConfig ~= switchToESModules ;sjsJUnitTests/test ;sjsCompilerTests/test" - shell: cmd - - test_windows_full: - runs-on: [self-hosted, Windows] - if: "github.event_name == 'schedule' && github.repository == 'scala/scala3' - || github.event_name == 'push' - || ( - github.event_name == 'pull_request' - && !contains(github.event.pull_request.body, '[skip ci]') - && contains(github.event.pull_request.body, '[test_windows_full]') - )" - - steps: - - name: Reset existing repo - shell: cmd - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Git Checkout - uses: actions/checkout@v4 - - - name: build binary - run: sbt "dist-win-x86_64/Universal/stage" - shell: cmd - - - name: Test - run: sbt ";scala3-bootstrapped/compile ;scala3-bootstrapped/test" - shell: cmd - - - name: Scala.js Test - run: sbt ";sjsJUnitTests/test ;set sjsJUnitTests/scalaJSLinkerConfig ~= switchToESModules ;sjsJUnitTests/test ;sjsCompilerTests/test" - shell: cmd - - mima: - name: MiMa - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2024-10-18 - options: --cpu-shares 4096 - volumes: - - ${{ github.workspace }}/../../cache/sbt:/root/.sbt - - ${{ github.workspace }}/../../cache/ivy:/root/.ivy2/cache - - ${{ github.workspace }}/../../cache/general:/root/.cache - if: "github.event_name == 'schedule' && github.repository == 'scala/scala3' - || github.event_name == 'push' - || github.event_name == 'merge_group' - || ( - github.event_name == 'pull_request' - && !contains(github.event.pull_request.body, '[skip ci]') - && !contains(github.event.pull_request.body, '[skip mima]') - ) - || ( - github.event_name == 'workflow_dispatch' - && github.repository == 'scala/scala3' - )" - steps: - - name: Set JDK 17 as default - run: echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> $GITHUB_PATH - - - name: Reset existing repo - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Checkout cleanup script - uses: actions/checkout@v4 - - - name: Cleanup - run: .github/workflows/cleanup.sh - - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Add SBT proxy repositories - run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - - name: MiMa - run: | - ./project/scripts/sbt ";scala3-interfaces/mimaReportBinaryIssues ;scala3-library-bootstrapped/mimaReportBinaryIssues ;scala3-library-bootstrappedJS/mimaReportBinaryIssues; tasty-core-bootstrapped/mimaReportBinaryIssues; scala2-library-bootstrapped/mimaReportBinaryIssues" - - - name: TASTy MiMa - run: | - # This script cleans the compiler and recompiles it from scratch (keep as last run) - ./project/scripts/scala2-library-tasty-mima.sh - - community_build_a: - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2024-10-18 - options: --cpu-shares 4096 - volumes: - - ${{ github.workspace }}/../../cache/sbt:/root/.sbt - - ${{ github.workspace }}/../../cache/ivy:/root/.ivy2/cache - - ${{ github.workspace }}/../../cache/general:/root/.cache - if: "github.event_name == 'schedule' && github.repository == 'scala/scala3' - || github.event_name == 'push' - || github.event_name == 'merge_group' - || ( - github.event_name == 'pull_request' - && !contains(github.event.pull_request.body, '[skip ci]') - && !contains(github.event.pull_request.body, '[skip community_build]') - && !contains(github.event.pull_request.body, '[skip community_build_a]') - ) - || ( - github.event_name == 'workflow_dispatch' - && github.repository == 'scala/scala3' - )" - - steps: - ###################################################################################### - ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. SCALA IS DISTRIBUTED USING JAVA 8. ## - ###################################################################################### - - name: Set JDK 8 as default - run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH - - name: Reset existing repo - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Checkout cleanup script - uses: actions/checkout@v4 - - - name: Cleanup - run: .github/workflows/cleanup.sh - - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Add SBT proxy repositories - run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - - name: Test - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git submodule sync - git submodule update --init --recursive --jobs 7 - ./project/scripts/sbt "community-build/testOnly dotty.communitybuild.CommunityBuildTestA" - - - name: Show dependency tracking file - if: ${{ always() }} - run: cat community-build/dotty-community-build-deps || true - - community_build_b: - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2024-10-18 - options: --cpu-shares 4096 - volumes: - - ${{ github.workspace }}/../../cache/sbt:/root/.sbt - - ${{ github.workspace }}/../../cache/ivy:/root/.ivy2/cache - - ${{ github.workspace }}/../../cache/general:/root/.cache - if: "github.event_name == 'schedule' && github.repository == 'scala/scala3' - || github.event_name == 'push' - || github.event_name == 'merge_group' - || ( - github.event_name == 'pull_request' - && !contains(github.event.pull_request.body, '[skip ci]') - && !contains(github.event.pull_request.body, '[skip community_build]') - && !contains(github.event.pull_request.body, '[skip community_build_b]') - ) - || ( - github.event_name == 'workflow_dispatch' - && github.repository == 'scala/scala3' - )" - - steps: - ###################################################################################### - ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. SCALA IS DISTRIBUTED USING JAVA 8. ## - ###################################################################################### - - name: Set JDK 8 as default - run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH - - name: Reset existing repo - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Checkout cleanup script - uses: actions/checkout@v4 - - - name: Cleanup - run: .github/workflows/cleanup.sh - - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Add SBT proxy repositories - run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - - name: Test - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git submodule sync - git submodule update --init --recursive --jobs 7 - ./project/scripts/sbt "community-build/testOnly dotty.communitybuild.CommunityBuildTestB" - - - name: Show dependency tracking file - if: ${{ always() }} - run: cat community-build/dotty-community-build-deps || true - - community_build_c: - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2024-10-18 - options: --cpu-shares 4096 - volumes: - - ${{ github.workspace }}/../../cache/sbt:/root/.sbt - - ${{ github.workspace }}/../../cache/ivy:/root/.ivy2/cache - - ${{ github.workspace }}/../../cache/general:/root/.cache - if: "github.event_name == 'schedule' && github.repository == 'scala/scala3' - || github.event_name == 'push' - || github.event_name == 'merge_group' - || ( - github.event_name == 'pull_request' - && !contains(github.event.pull_request.body, '[skip ci]') - && !contains(github.event.pull_request.body, '[skip community_build]') - && !contains(github.event.pull_request.body, '[skip community_build_c]') - ) - || ( - github.event_name == 'workflow_dispatch' - && github.repository == 'scala/scala3' - )" - - steps: - ###################################################################################### - ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. SCALA IS DISTRIBUTED USING JAVA 8. ## - ###################################################################################### - - name: Set JDK 8 as default - run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH - - name: Reset existing repo - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Checkout cleanup script - uses: actions/checkout@v4 - - - name: Cleanup - run: .github/workflows/cleanup.sh - - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Add SBT proxy repositories - run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - - name: Test - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git submodule sync - git submodule update --init --recursive --jobs 7 - ./project/scripts/sbt "community-build/testOnly dotty.communitybuild.CommunityBuildTestC" - - - name: Show dependency tracking file - if: ${{ always() }} - run: cat community-build/dotty-community-build-deps || true - - test_sbt: - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2024-10-18 - options: --cpu-shares 4096 - volumes: - - ${{ github.workspace }}/../../cache/sbt:/root/.sbt - - ${{ github.workspace }}/../../cache/ivy:/root/.ivy2/cache - - ${{ github.workspace }}/../../cache/general:/root/.cache - if: "github.event_name == 'schedule' && github.repository == 'scala/scala3' - || github.event_name == 'push' - || ( - github.event_name == 'pull_request' - && !contains(github.event.pull_request.body, '[skip ci]') - && !contains(github.event.pull_request.body, '[skip test_sbt]') - ) - || ( - github.event_name == 'workflow_dispatch' - && github.repository == 'scala/scala3' - )" - - steps: - - name: Set JDK 17 as default - run: echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> $GITHUB_PATH - - - name: Reset existing repo - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Checkout cleanup script - uses: actions/checkout@v4 - - - name: Cleanup - run: .github/workflows/cleanup.sh - - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Add SBT proxy repositories - run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - - name: Test sbt - run: ./project/scripts/sbt "sbt-test/scripted; sbt-community-build/scripted" - - test_java8: - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2024-10-18 - options: --cpu-shares 4096 - volumes: - - ${{ github.workspace }}/../../cache/sbt:/root/.sbt - - ${{ github.workspace }}/../../cache/ivy:/root/.ivy2/cache - - ${{ github.workspace }}/../../cache/general:/root/.cache - - if: "github.event_name == 'schedule' && github.repository == 'scala/scala3' - || ( - github.event_name == 'push' - && startsWith(github.event.ref, 'refs/tags/') - ) - || ( - github.event_name == 'pull_request' - && !contains(github.event.pull_request.body, '[skip ci]') - && contains(github.event.pull_request.body, '[test_java8]') - ) - || ( - github.event_name == 'workflow_dispatch' - && github.repository == 'scala/scala3' - )" - - steps: - #################################################################################### - ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. THIS TEST IS SPECIFIC FOR JAVA 8 ## - #################################################################################### - - name: Set JDK 8 as default - run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH - - - name: Reset existing repo - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Checkout cleanup script - uses: actions/checkout@v4 - - - name: Cleanup - run: .github/workflows/cleanup.sh - - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Add SBT proxy repositories - run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - - name: Test - run: | - ./project/scripts/buildScalaBinary - ./project/scripts/sbt ";scala3-bootstrapped/compile ;scala3-bootstrapped/test ;sbt-test/scripted scala2-compat/*" - ./project/scripts/cmdTests - ./project/scripts/bootstrappedOnlyCmdTests - - - name: Scala.js Test - run: | - ./project/scripts/sbt ";sjsSandbox/run ;sjsSandbox/test ;sjsJUnitTests/test ;set sjsJUnitTests/scalaJSLinkerConfig ~= switchToESModules ;sjsJUnitTests/test ;sjsCompilerTests/test" - - publish_nightly: - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2024-10-18 - options: --cpu-shares 4096 - volumes: - - ${{ github.workspace }}/../../cache/sbt:/root/.sbt - - ${{ github.workspace }}/../../cache/ivy:/root/.ivy2/cache - - ${{ github.workspace }}/../../cache/general:/root/.cache - strategy: - matrix: - series: [ - {repository: scala/scala3, branch: main}, # Scala Next nightly - {repository: scala/scala3-lts, branch: lts-3.3} # Scala LTS nightly - ] - needs: [test_non_bootstrapped, test, mima, community_build_a, community_build_b, community_build_c, test_sbt, test_java8] - if: "(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.repository == 'scala/scala3'" - env: - NIGHTLYBUILD: yes - PGP_PW: ${{ secrets.PGP_PW }} # PGP passphrase - PGP_SECRET: ${{ secrets.PGP_SECRET }} # Export your private and public PGP key to an *.asc file, take the file's contents as a string - SONATYPE_PW: ${{ secrets.SONATYPE_PW_ORGSCALALANG }} - SONATYPE_USER: ${{ secrets.SONATYPE_USER_ORGSCALALANG }} - - steps: - ###################################################################################### - ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. SCALA IS DISTRIBUTED USING JAVA 8. ## - ###################################################################################### - - name: Set JDK 8 as default - run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH - - name: Reset existing repo - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Checkout cleanup script - uses: actions/checkout@v4 - - - name: Cleanup - run: .github/workflows/cleanup.sh - - - name: Git Checkout - uses: actions/checkout@v4 - with: - repository: ${{ matrix.series.repository }} - ref: ${{ matrix.series.branch }} - - - name: Add SBT proxy repositories - run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - - name: Get version string for this build - run: | - ver=$(./project/scripts/sbt "print scala3-compiler-bootstrapped/version" | tail -n1) - echo "This build version: $ver" - echo "THISBUILD_VERSION=$ver" >> $GITHUB_ENV - - - name: Check is version matching pattern - shell: bash - run: | - if ! grep -Eo "3\.[0-9]+\.[0-9]+-RC[0-9]+-bin-[0-9]{8}-[a-zA-Z0-9]{7}-NIGHTLY" <<< "${{ env.THISBUILD_VERSION }}"; then - echo "Version used by compiler to publish nightly release does not match expected pattern" - exit 1 - fi - - - name: Check whether not yet published - id: not_yet_published - continue-on-error: true - run: | - ! ./project/scripts/is-version-published.sh "$THISBUILD_VERSION" - - - name: Publish Nightly - if: "steps.not_yet_published.outcome == 'success'" - run: | - ./project/scripts/sbtPublish ";project scala3-bootstrapped ;publishSigned ;sonaRelease" - - nightly_documentation: - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2024-10-18 - options: --cpu-shares 4096 - volumes: - - ${{ github.workspace }}/../../cache/sbt:/root/.sbt - - ${{ github.workspace }}/../../cache/ivy:/root/.ivy2/cache - - ${{ github.workspace }}/../../cache/general:/root/.cache - needs: [publish_nightly] - if: "(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.repository == 'scala/scala3'" - env: - NIGHTLYBUILD: yes - steps: - - name: Reset existing repo - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Checkout cleanup script - uses: actions/checkout@v4 - - - name: Cleanup - run: .github/workflows/cleanup.sh - - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Add SBT proxy repositories - run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - - name: Generate Website - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - ./project/scripts/genDocs -doc-snapshot - - - name: Deploy Website to https://dotty.epfl.ch - uses: peaceiris/actions-gh-pages@v4 - with: - personal_token: ${{ secrets.DOTTYBOT_TOKEN }} - publish_dir: docs/_site - external_repository: scala/dotty.epfl.ch - publish_branch: main - - publish_release: - permissions: - contents: write # for GH CLI to create a release - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2024-10-18 - options: --cpu-shares 4096 - volumes: - - ${{ github.workspace }}/../../cache/sbt:/root/.sbt - - ${{ github.workspace }}/../../cache/ivy:/root/.ivy2/cache - - ${{ github.workspace }}/../../cache/general:/root/.cache - needs: [test_non_bootstrapped, test, mima, community_build_a, community_build_b, community_build_c, test_sbt, test_java8, build-sdk-package, build-msi-package] - if: "github.event_name == 'push' - && startsWith(github.event.ref, 'refs/tags/')" - - env: - RELEASEBUILD: yes - PGP_PW: ${{ secrets.PGP_PW }} # PGP passphrase - PGP_SECRET: ${{ secrets.PGP_SECRET }} # Export your private and public PGP key to an *.asc file, take the file's contents as a string - SONATYPE_PW: ${{ secrets.SONATYPE_PW_ORGSCALALANG }} - SONATYPE_USER: ${{ secrets.SONATYPE_USER_ORGSCALALANG }} - - steps: - ###################################################################################### - ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. SCALA IS DISTRIBUTED USING JAVA 8. ## - ###################################################################################### - - name: Set JDK 8 as default - run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH - - name: Reset existing repo - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - - - name: Checkout cleanup script - uses: actions/checkout@v4 - - - name: Cleanup - run: .github/workflows/cleanup.sh - - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Add SBT proxy repositories - run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - # Extract the release tag - - name: Extract the release tag - run : echo "RELEASE_TAG=${GITHUB_REF#*refs/tags/}" >> $GITHUB_ENV - - - name: Check compiler version - shell: bash - run : | - version=$(./project/scripts/sbt "print scala3-compiler-bootstrapped/version" | tail -n1) - echo "This build version: ${version}" - if [ "${version}" != "${{ env.RELEASE_TAG }}" ]; then - echo "Compiler version for this build '${version}', does not match tag: ${{ env.RELEASE_TAG }}" - exit 1 - fi - - - name: Prepare the SDKs - shell: bash - run : | - prepareSDK() { - distroSuffix="$1" - sbtProject="$2" - distDir="$3" - - # Build binaries - ./project/scripts/sbt "all ${sbtProject}/Universal/packageBin ${sbtProject}/Universal/packageZipTarball" - - artifactName="scala3-${{ env.RELEASE_TAG }}${distroSuffix}" - - # Caluclate SHA for each of archive files - for file in "${artifactName}.zip" "${artifactName}.tar.gz"; do - mv ${distDir}/target/universal/$file $file - sha256sum "${file}" > "${file}.sha256" - done - } - prepareSDK "" "dist" "./dist/" - prepareSDK "-aarch64-pc-linux" "dist-linux-aarch64" "./dist/linux-aarch64/" - prepareSDK "-x86_64-pc-linux" "dist-linux-x86_64" "./dist/linux-x86_64/" - prepareSDK "-aarch64-apple-darwin" "dist-mac-aarch64" "./dist/mac-aarch64/" - prepareSDK "-x86_64-apple-darwin" "dist-mac-x86_64" "./dist/mac-x86_64/" - prepareSDK "-x86_64-pc-win32" "dist-win-x86_64" "./dist/win-x86_64/" - - - name: Download MSI package - uses: actions/download-artifact@v4 - with: - name: scala.msi - path: . - - name: Prepare MSI package - shell: bash - run: | - msiInstaller="scala3-${{ env.RELEASE_TAG }}.msi" - mv scala.msi "${msiInstaller}" - sha256sum "${msiInstaller}" > "${msiInstaller}.sha256" - - - name: Install GH CLI - uses: dev-hanz-ops/install-gh-cli-action@v0.2.1 - with: - gh-cli-version: 2.59.0 - - # Create the GitHub release - - name: Create GitHub Release - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token - shell: bash - run: | - # We need to config safe.directory in every step that might reference git - # It is not persisted between steps - git config --global --add safe.directory $GITHUB_WORKSPACE - gh release create \ - --draft \ - --title "${{ env.RELEASE_TAG }}" \ - --notes-file ./changelogs/${{ env.RELEASE_TAG }}.md \ - --latest=${{ !contains(env.RELEASE_TAG, '-RC') }} \ - --prerelease=${{ contains(env.RELEASE_TAG, '-RC') }} \ - --verify-tag ${{ env.RELEASE_TAG }} \ - scala3-${{ env.RELEASE_TAG }}*.zip \ - scala3-${{ env.RELEASE_TAG }}*.tar.gz \ - scala3-${{ env.RELEASE_TAG }}*.sha256 \ - scala3-${{ env.RELEASE_TAG }}.msi - - - name: Publish Release - run: ./project/scripts/sbtPublish ";project scala3-bootstrapped ;publishSigned ;sonaUpload" - - - open_issue_on_failure: - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2024-10-18 - needs: [nightly_documentation, test_windows_full] - # The `failure()` expression is true iff at least one of the dependencies - # of this job (including transitive dependencies) has failed. - if: "failure() && github.event_name == 'schedule'" - steps: - - name: Checkout issue template - uses: actions/checkout@v4 - - - name: Open an issue - uses: JasonEtco/create-an-issue@v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - WORKFLOW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - with: - filename: .github/workflows/issue_nightly_failed.md - - build-msi-package: - uses: ./.github/workflows/build-msi.yml - if : - (github.event_name == 'pull_request' && contains(github.event.pull_request.body, '[test_msi]')) || - (github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/')) - - test-msi-package: - uses: ./.github/workflows/test-msi.yml - needs: [build-msi-package] - with: - # Ensure that version starts with prefix 3. - # In the future it can be adapted to compare with git tag or version set in the project/Build.scala - version: "3." - java-version: 8 - - build-sdk-package: - uses: ./.github/workflows/build-sdk.yml - if: - (github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]')) || - (github.event_name == 'workflow_dispatch' && github.repository == 'scala/scala3') || - (github.event_name == 'schedule' && github.repository == 'scala/scala3') || - github.event_name == 'push' || - github.event_name == 'merge_group' - with: - java-version: 8 - - build-chocolatey-package: - uses: ./.github/workflows/build-chocolatey.yml - needs: [ build-sdk-package ] - with: - version: 3.6.0-SNAPSHOT # Fake version, used only for choco tests - url : https://api.github.com/repos/scala/scala3/actions/artifacts/${{ needs.build-sdk-package.outputs.win-x86_64-id }}/zip - digest : ${{ needs.build-sdk-package.outputs.win-x86_64-digest }} - - test-chocolatey-package: - uses: ./.github/workflows/test-chocolatey.yml - with: - version : 3.6.0-SNAPSHOT # Fake version, used only for choco tests - java-version: 8 - if: github.event_name == 'pull_request' && contains(github.event.pull_request.body, '[test_chocolatey]') - needs: [ build-chocolatey-package ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000000..db42ed2aa3e3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: CI +on: + push: + branches: + - plasmon + tags: + - "v*" + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: coursier/cache-action@v6.4 + - uses: coursier/setup-action@v1 + - name: Test + run: ./mill -i __.compile + + publish: + if: github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: coursier/cache-action@v6 + - uses: coursier/setup-action@v1 + - name: Publish + run: ./mill -i mill.scalalib.SonatypeCentralPublishModule/ + env: + MILL_PGP_SECRET_BASE64: ${{ secrets.PUBLISH_SECRET_KEY_BASE64 }} + MILL_PGP_PASSPHRASE: ${{ secrets.PUBLISH_SECRET_KEY_PASSWORD }} + MILL_SONATYPE_USERNAME: ${{ secrets.PUBLISH_USER }} + MILL_SONATYPE_PASSWORD: ${{ secrets.PUBLISH_PASSWORD }} diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml deleted file mode 100644 index 50b75751f49a..000000000000 --- a/.github/workflows/cla.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Scala CLA -on: - pull_request: - branches-ignore: - - 'language-reference-stable' -jobs: - check: - runs-on: ubuntu-latest - if: github.event.pull_request.user.login != 'dependabot' - steps: - - name: Verify CLA - uses: scala/cla-checker@v1 - with: - author: ${{ github.event.pull_request.user.login }} diff --git a/.github/workflows/cleanup.sh b/.github/workflows/cleanup.sh deleted file mode 100755 index fe2a9bed3e59..000000000000 --- a/.github/workflows/cleanup.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -TARGETS="$(pwd) /__w/_temp /github/home/" -for folder in $TARGETS; do - echo "Cleaning $folder" - cd $folder - for f in $(ls -a); do - if [ "$f" != "." ] && - [ "$f" != ".." ] && - [ "$f" != "_github_home" ] && # __w/_temp/_github_home is mount as - # /github/home and is cleaned separately - [ "$f" != "_github_workflow" ] # Contains workflow metadata, is needed - # for the correct workflow execution - then - rm -rf "$f" - fi - done -done diff --git a/.github/workflows/dependency-graph.yml b/.github/workflows/dependency-graph.yml deleted file mode 100644 index 6a3f8174b2d7..000000000000 --- a/.github/workflows/dependency-graph.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Update Dependency Graph -on: - push: - branches: - - main # default branch of the project -jobs: - dependency-graph: - name: Update Dependency Graph - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: sbt/setup-sbt@v1 - - uses: scalacenter/sbt-dependency-submission@v3 - env: - DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} diff --git a/.github/workflows/issue_nightly_failed.md b/.github/workflows/issue_nightly_failed.md deleted file mode 100644 index 8399513578f2..000000000000 --- a/.github/workflows/issue_nightly_failed.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Nightly {{ workflow }} workflow of {{ date | date('YYYY-MM-DD') }} failed -labels: itype:bug, prio:blocker ---- -See {{ env.WORKFLOW_RUN_URL }} diff --git a/.github/workflows/language-reference.yaml b/.github/workflows/language-reference.yaml deleted file mode 100644 index 61a2768c51da..000000000000 --- a/.github/workflows/language-reference.yaml +++ /dev/null @@ -1,72 +0,0 @@ -name: Language reference documentation - -on: - push: - branches: - - 'language-reference-stable' - pull_request: - branches: - - 'language-reference-stable' - workflow_dispatch: - -permissions: - contents: read - -jobs: - build-and-push: - name: Build reference documentation and push it - permissions: - contents: write # for Git to git push - runs-on: ubuntu-latest - steps: - - name: Get current date - id: date - run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT - - - name: Git Checkout - uses: actions/checkout@v4 - with: - path: 'dotty' - fetch-depth: 0 - ssh-key: ${{ secrets.DOCS_KEY }} - - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: 17 - cache: 'sbt' - - uses: sbt/setup-sbt@v1 - - - name: Generate reference documentation and test links - run: | - cd dotty - ./project/scripts/sbt "scaladoc/generateReferenceDocumentation --no-regenerate-expected-links" - ./project/scripts/docsLinksStability ./scaladoc/output/reference ./project/scripts/expected-links/reference-expected-links.txt - cd .. - env: - DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - - - name: Push changes to scala3-reference-docs - if: github.event_name == 'push' - uses: actions/checkout@v4 - with: - repository: lampepfl/scala3-reference-docs - fetch-depth: 0 - submodules: true - ssh-key: ${{ secrets.DOCS_DEPLOY_KEY }} - path: 'scala3-reference-docs' - - - if: github.event_name == 'push' - run: | - \cp -a dotty/scaladoc/output/reference/. scala3-reference-docs/ - cd scala3-reference-docs - git config user.name gh-actions - git config user.email actions@github.com - git add . - if ! git diff-index --quiet HEAD; then - git commit -m "UPDATE ${{ steps.date.outputs.date }}" - git push - fi - cd .. - diff --git a/.github/workflows/lts-backport.yaml b/.github/workflows/lts-backport.yaml deleted file mode 100644 index 376b8817b35e..000000000000 --- a/.github/workflows/lts-backport.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: Add to backporting project - -on: - push: - branches: - - main - -jobs: - add-to-backporting-project: - if: "!contains(github.event.push.head_commit.message, '[Next only]') && - github.repository == 'scala/scala3'" - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: coursier/cache-action@v6 - - uses: VirtusLab/scala-cli-setup@v1.8.4 - - run: scala-cli ./project/scripts/addToBackportingProject.scala -- ${{ github.sha }} - env: - GRAPHQL_API_TOKEN: ${{ secrets.GRAPHQL_API_TOKEN }} - diff --git a/.github/workflows/publish-chocolatey.yml b/.github/workflows/publish-chocolatey.yml deleted file mode 100644 index 88a8a7913188..000000000000 --- a/.github/workflows/publish-chocolatey.yml +++ /dev/null @@ -1,39 +0,0 @@ -################################################################################################### -### THIS IS A REUSABLE WORKFLOW TO PUBLISH SCALA TO CHOCOLATEY ### -### HOW TO USE: ### -### - THE RELEASE WORKFLOW SHOULD CALL THIS WORKFLOW ### -### - IT WILL PUBLISH TO CHOCOLATEY THE MSI ### -### ### -### NOTE: ### -### - WE SHOULD KEEP IN SYNC THE NAME OF THE MSI WITH THE ACTUAL BUILD ### -### - WE SHOULD KEEP IN SYNC THE URL OF THE RELEASE ### -### - IT ASSUMES THAT THE `build-chocolatey` WORKFLOW WAS EXECUTED BEFORE ### -################################################################################################### - - -name: Publish Scala to Chocolatey -run-name: Publish Scala ${{ inputs.version }} to Chocolatey - -on: - workflow_call: - inputs: - version: - required: true - type: string - secrets: - # Connect to https://community.chocolatey.org/profiles/scala - # Accessible via https://community.chocolatey.org/account - API-KEY: - required: true - -jobs: - publish: - runs-on: windows-latest - steps: - - name: Fetch the Chocolatey package from GitHub - uses: actions/download-artifact@v4 - with: - name: scala.nupkg - - name: Publish the package to Chocolatey - run: choco push scala.${{inputs.version}}.nupkg --source https://push.chocolatey.org/ --api-key ${{ secrets.API-KEY }} - \ No newline at end of file diff --git a/.github/workflows/publish-sdkman.yml b/.github/workflows/publish-sdkman.yml deleted file mode 100644 index fbbada2a1a70..000000000000 --- a/.github/workflows/publish-sdkman.yml +++ /dev/null @@ -1,67 +0,0 @@ -################################################################################################### -### THIS IS A REUSABLE WORKFLOW TO PUBLISH SCALA TO SDKMAN! ### -### HOW TO USE: ### -### - THE RELEASE WORKFLOW SHOULD CALL THIS WORKFLOW ### -### - IT WILL PUBLISH TO SDKMAN! THE BINARIES TO EACH SUPPORTED PLATFORM AND A UNIVERSAL JAR ### -### - IT CHANGES THE DEFAULT VERSION IN SDKMAN! ### -### ### -### NOTE: ### -### - WE SHOULD KEEP IN SYNC THE NAME OF THE ARCHIVES WITH THE ACTUAL BUILD ### -### - WE SHOULD KEEP IN SYNC THE URL OF THE RELEASE ### -################################################################################################### - - -name: Publish Scala to SDKMAN! -run-name: Publish Scala ${{ inputs.version }} to SDKMAN! - -on: - workflow_call: - inputs: - version: - required: true - type: string - secrets: - CONSUMER-KEY: - required: true - CONSUMER-TOKEN: - required: true - -env: - RELEASE-URL: 'https://github.com/scala/scala3/releases/download/$%7B%7B%20inputs.version%20%7D%7D' - -jobs: - publish: - runs-on: ubuntu-latest - strategy: - matrix: - include: - - platform: LINUX_64 - archive : 'scala3-${{ inputs.version }}-x86_64-pc-linux.zip' - - platform: LINUX_ARM64 - archive : 'scala3-${{ inputs.version }}-aarch64-pc-linux.zip' - - platform: MAC_OSX - archive : 'scala3-${{ inputs.version }}-x86_64-apple-darwin.zip' - - platform: MAC_ARM64 - archive : 'scala3-${{ inputs.version }}-aarch64-apple-darwin.zip' - - platform: WINDOWS_64 - archive : 'scala3-${{ inputs.version }}-x86_64-pc-win32.zip' - steps: - - uses: sdkman/sdkman-release-action@2800d4359ae097a99afea7e0370f0c6e726182a4 - with: - CONSUMER-KEY : ${{ secrets.CONSUMER-KEY }} - CONSUMER-TOKEN : ${{ secrets.CONSUMER-TOKEN }} - CANDIDATE : scala - VERSION : ${{ inputs.version }} - URL : '${{ env.RELEASE-URL }}/${{ matrix.archive }}' - PLATFORM : ${{ matrix.platform }} - - default: - runs-on: ubuntu-latest - needs: publish - steps: - - uses: sdkman/sdkman-default-action@b3f991bd109e40155af1b13a4c6fc8e8ccada65e - with: - CONSUMER-KEY : ${{ secrets.CONSUMER-KEY }} - CONSUMER-TOKEN : ${{ secrets.CONSUMER-TOKEN }} - CANDIDATE : scala - VERSION : ${{ inputs.version }} diff --git a/.github/workflows/publish-winget.yml b/.github/workflows/publish-winget.yml deleted file mode 100644 index ba5877e63ec7..000000000000 --- a/.github/workflows/publish-winget.yml +++ /dev/null @@ -1,36 +0,0 @@ -################################################################################################### -### THIS IS A REUSABLE WORKFLOW TO PUBLISH SCALA TO WINGET ### -### HOW TO USE: ### -### - THE RELEASE WORKFLOW SHOULD CALL THIS WORKFLOW ### -### - IT WILL PUBLISH THE MSI TO WINGET ### -### ### -### NOTE: ### -### - WE SHOULD KEEP IN SYNC THE https://github.com/dottybot/winget-pkgs REPOSITORY ### -################################################################################################### - - -name: Publish Scala to winget -run-name: Publish Scala ${{ inputs.version }} to winget - -on: - workflow_call: - inputs: - version: - required: true - type: string - secrets: - DOTTYBOT-TOKEN: - required: true - -jobs: - publish: - runs-on: windows-latest - steps: - - uses: vedantmgoyal9/winget-releaser@4ffc7888bffd451b357355dc214d43bb9f23917e - with: - identifier : Scala.Scala.3 - version : ${{ inputs.version }} - installers-regex: '\.msi$' - release-tag : ${{ inputs.version }} - fork-user : dottybot - token : ${{ secrets.DOTTYBOT-WINGET-TOKEN }} \ No newline at end of file diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml deleted file mode 100644 index ab921ec588d2..000000000000 --- a/.github/workflows/releases.yml +++ /dev/null @@ -1,72 +0,0 @@ -################################################################################################### -### OFFICIAL RELEASE WORKFLOW ### -### HOW TO USE: ### -### - THIS WORKFLOW WILL NEED TO BE TRIGGERED MANUALLY ### -### ### -### NOTE: ### -### - THIS WORKFLOW SHOULD ONLY BE RUN ON STABLE RELEASES ### -### - IT ASSUMES THAT THE PRE-RELEASE WORKFLOW WAS PREVIOUSLY EXECUTED ### -### ### -################################################################################################### - -name: Official release of Scala -run-name: Official release of Scala ${{ inputs.version }} - -on: - workflow_dispatch: - inputs: - version: - description: 'The version to officially release' - required: true - type: string - -jobs: - # TODO: ADD JOB TO SWITCH THE GITHUB RELEASE FROM DRAFT TO LATEST - publish-sdkman: - uses: ./.github/workflows/publish-sdkman.yml - with: - version: ${{ inputs.version }} - secrets: - CONSUMER-KEY: ${{ secrets.SDKMAN_KEY }} - CONSUMER-TOKEN: ${{ secrets.SDKMAN_TOKEN }} - - publish-winget: - uses: ./.github/workflows/publish-winget.yml - with: - version: ${{ inputs.version }} - secrets: - DOTTYBOT-TOKEN: ${{ secrets.DOTTYBOT_WINGET_TOKEN }} - - compute-digest: - runs-on: ubuntu-latest - outputs: - digest: ${{ steps.digest.outputs.digest }} - steps: - - name: Compute the SHA256 of scala3-${{ inputs.version }}-x86_64-pc-win32.zip in GitHub Release - id: digest - run: | - curl -o artifact.zip -L https://github.com/scala/scala3/releases/download/${{ inputs.version }}/scala3-${{ inputs.version }}-x86_64-pc-win32.zip - echo "digest=$(sha256sum artifact.zip | cut -d " " -f 1)" >> "$GITHUB_OUTPUT" - - build-chocolatey: - uses: ./.github/workflows/build-chocolatey.yml - needs: compute-digest - with: - version: ${{ inputs.version }} - url : 'https://github.com/scala/scala3/releases/download/$%7B%7B%20inputs.version%20%7D%7D/scala3-$%7B%7B%20inputs.version%20%7D%7D-x86_64-pc-win32.zip' - digest : ${{ needs.compute-digest.outputs.digest }} - test-chocolatey: - uses: ./.github/workflows/test-chocolatey.yml - needs: build-chocolatey - with: - version : ${{ inputs.version }} - java-version: 8 - publish-chocolatey: - uses: ./.github/workflows/publish-chocolatey.yml - needs: [ build-chocolatey, test-chocolatey ] - with: - version: ${{ inputs.version }} - secrets: - API-KEY: ${{ secrets.CHOCOLATEY_KEY }} - - # TODO: ADD RELEASE WORKFLOW TO CHOCOLATEY AND OTHER PACKAGE MANAGERS HERE \ No newline at end of file diff --git a/.github/workflows/repositories b/.github/workflows/repositories deleted file mode 100644 index 05da6df0c2bb..000000000000 --- a/.github/workflows/repositories +++ /dev/null @@ -1,4 +0,0 @@ -[repositories] -local -my-ivy-proxy-releases: https://scala-webapps.epfl.ch/artifactory/sbt-plugin-releases/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext] -my-maven-proxy-releases: https://scala-webapps.epfl.ch/artifactory/central/ diff --git a/.github/workflows/scaladoc.yaml b/.github/workflows/scaladoc.yaml deleted file mode 100644 index d2e3071e765b..000000000000 --- a/.github/workflows/scaladoc.yaml +++ /dev/null @@ -1,89 +0,0 @@ -name: scaladoc - -on: - push: - branches-ignore: - - 'language-reference-stable' - - 'gh-readonly-queue/**' - pull_request: - branches-ignore: - - 'language-reference-stable' - merge_group: -permissions: - contents: read - -jobs: - build: - env: - AZURE_STORAGE_SAS_TOKEN: ${{ secrets.AZURE_STORAGE_SAS_TOKEN }} - DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - runs-on: ubuntu-latest - if: "github.event_name == 'merge_group' - || ( github.event_name == 'pull_request' - && !contains(github.event.pull_request.body, '[skip ci]') - && !contains(github.event.pull_request.body, '[skip docs]') - ) - || contains(github.event.ref, 'scaladoc') - || contains(github.event.ref, 'main')" - - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: 17 - cache: 'sbt' - - - uses: sbt/setup-sbt@v1 - - name: Compile and test scala3doc-js - run: ./project/scripts/sbt scaladoc-js-main/test - - - name: Compile and test - run: | - ./project/scripts/sbt scaladoc/test - ./project/scripts/cmdScaladocTests - - - name: Locally publish self - run: ./project/scripts/sbt scaladoc/publishLocal - - - name: Generate self documentation - run: ./project/scripts/sbt scaladoc/generateSelfDocumentation - - - name: Generate testcases documentation - run: ./project/scripts/sbt scaladoc/generateTestcasesDocumentation - - - name: Generate reference documentation - run: ./project/scripts/sbt scaladoc/generateReferenceDocumentation - - - name: Generate Scala 3 documentation - run: ./project/scripts/sbt scaladoc/generateScalaDocumentation - - - name: Generate documentation for example project using dotty-sbt - run: ./project/scripts/sbt "sbt-test/scripted sbt-dotty/scaladoc" - - stdlib-sourcelinks-test: - runs-on: ubuntu-latest - # if false - disable flaky test - if: "false && (( github.event_name == 'pull_request' - && !contains(github.event.pull_request.body, '[skip ci]') - && !contains(github.event.pull_request.body, '[skip docs]') - ) - || contains(github.event.ref, 'scaladoc') - || contains(github.event.ref, 'scala3doc') - || contains(github.event.ref, 'main'))" - - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: 17 - - - name: Test sourcelinks to stdlib - run: true # ./project/scripts/sbt scaladoc/sourceLinksIntegrationTest:test diff --git a/.github/workflows/spec.yml b/.github/workflows/spec.yml deleted file mode 100644 index cf13174e1f8e..000000000000 --- a/.github/workflows/spec.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Specification - -on: - push: - tags: - - '*' - branches-ignore: - - 'gh-readonly-queue/**' - pull_request: - merge_group: - workflow_dispatch: - -env: - DOTTY_CI_RUN: true - -jobs: - specification: - runs-on: ubuntu-latest - if: (github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]')) || - (github.event_name == 'workflow_dispatch' && github.repository == 'scala/scala3') || - github.event_name == 'push' || - github.event_name == 'merge_group' - defaults: - run: - working-directory: ./docs/_spec - - steps: - - uses: actions/checkout@v4 - - # Keep in sync with ./docs/_spec/Dockerfile - - uses: ruby/setup-ruby@v1 - with: - ruby-version: '2.7' - - name: Install required gems - run: | - gem install "rubygems-update:<3.5" --no-document - update_rubygems - gem install sass-embedded -v 1.58.0 - gem install bundler:1.17.2 jekyll - bundle install - npm install bower - - - name: Build the specification - run: | - bundle exec jekyll build - - # Deploy - - name: Deployment - env: - USER_FOR_TEST: ${{ secrets.SPEC_DEPLOY_USER }} - if: ${{ env.USER_FOR_TEST != '' }} - uses: burnett01/rsync-deployments@7.0.2 - with: - switches: -rzv - path: docs/_spec/_site/ - remote_path: ${{ secrets.SPEC_DEPLOY_PATH }} - remote_host: ${{ secrets.SPEC_DEPLOY_HOST }} - remote_user: ${{ secrets.SPEC_DEPLOY_USER }} - remote_key: ${{ secrets.SPEC_DEPLOY_KEY }} - remote_key_pass: ${{ secrets.SPEC_DEPLOY_PASS }} diff --git a/.github/workflows/test-cc.yml b/.github/workflows/test-cc.yml deleted file mode 100644 index 69996fb6d74e..000000000000 --- a/.github/workflows/test-cc.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Scala 3 with Capture Checking - -on: - push: - branches: - - main - pull_request: - paths: - - .github/workflows/test-cc.yml - - scala2-library-cc/** - - scala2-library-cc-tasty/** - - compiler/src/dotty/tools/dotc/cc/** - ## Capture Checking Tests - - tests/pos-custom-args/captures/** - - tests/run-custom-args/captures/** - - tests/neg-custom-args/captures/** - -env: - DOTTY_CI_RUN: true - DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - -jobs: - suite-with-stdlib-cc: - name: Test Suite with the CC Standard Library - runs-on: ubuntu-latest - steps: - - name: Git Checkout - uses: actions/checkout@v4 - - uses: sbt/setup-sbt@v1 - - name: Test with Scala 2 library with CC TASTy - run: ./project/scripts/sbt ";set ThisBuild/Build.scala2Library := Build.Scala2LibraryCCTasty ;scala3-bootstrapped/test" diff --git a/.github/workflows/test-chocolatey.yml b/.github/workflows/test-chocolatey.yml deleted file mode 100644 index e302968b9129..000000000000 --- a/.github/workflows/test-chocolatey.yml +++ /dev/null @@ -1,54 +0,0 @@ -################################################################################################### -### THIS IS A REUSABLE WORKFLOW TO TEST SCALA WITH CHOCOLATEY ### -### HOW TO USE: ### -### ### -### NOTE: ### -### ### -################################################################################################### - -name: Test 'scala' Chocolatey Package -run-name: Test 'scala' (${{ inputs.version }}) Chocolatey Package - -on: - workflow_call: - inputs: - version: - required: true - type: string - java-version: - required: true - type : string - -env: - CHOCOLATEY-REPOSITORY: chocolatey-pkgs - # Controls behaviour of chocolatey{Install,Uninstall}.ps1 scripts - # During snapshot releases it uses a different layout and requires access token to GH Actions artifacts - # During stable releases it uses publically available archives - DOTTY_CI_INSTALLATION: ${{ endsWith(inputs.version, '-SNAPSHOT') && secrets.GITHUB_TOKEN || '' }} - -jobs: - test: - runs-on: windows-latest - steps: - - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: ${{ inputs.java-version }} - - name: Download the 'nupkg' from GitHub Artifacts - uses: actions/download-artifact@v4 - with: - name: scala.nupkg - path: ${{ env.CHOCOLATEY-REPOSITORY }} - - name : Install the `scala` package with Chocolatey - run : choco install scala --source "${{ env.CHOCOLATEY-REPOSITORY }}" --pre # --pre since we might be testing non-stable releases - shell: pwsh - - name : Test the `scala` command - run : scala --version - shell: pwsh - - name : Test the `scalac` command - run : scalac --version - - name : Test the `scaladoc` command - run : scaladoc --version - - name : Uninstall the `scala` package - run : choco uninstall scala - \ No newline at end of file diff --git a/.github/workflows/test-launchers.yml b/.github/workflows/test-launchers.yml deleted file mode 100644 index 25bd5a4bf42f..000000000000 --- a/.github/workflows/test-launchers.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Test CLI Launchers on all the platforms -on: - pull_request: - workflow_dispatch: - -env: - DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} - -jobs: - linux-x86_64: - name: Deploy and Test on Linux x64 architecture - runs-on: ubuntu-latest - if: (github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]') ) || - (github.event_name == 'workflow_dispatch' && github.repository == 'scala/scala3' ) - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: 'sbt' - - uses: sbt/setup-sbt@v1 - - name: Build and test launcher command - run: ./project/scripts/native-integration/bashTests - env: - LAUNCHER_EXPECTED_PROJECT: "dist-linux-x86_64" - - linux-aarch64: - name: Deploy and Test on Linux ARM64 architecture - runs-on: ubuntu-24.04-arm - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: 'sbt' - - uses: sbt/setup-sbt@v1 - - name: Build and test launcher command - run: ./project/scripts/native-integration/bashTests - env: - LAUNCHER_EXPECTED_PROJECT: "dist-linux-aarch64" - - mac-x86_64: - name: Deploy and Test on Mac x64 architecture - runs-on: macos-13 - if: (github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]') ) || - (github.event_name == 'workflow_dispatch' && github.repository == 'scala/scala3' ) - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: 'sbt' - - uses: sbt/setup-sbt@v1 - - name: Build and test launcher command - run: ./project/scripts/native-integration/bashTests - env: - LAUNCHER_EXPECTED_PROJECT: "dist-mac-x86_64" - - mac-aarch64: - name: Deploy and Test on Mac ARM64 architecture - runs-on: macos-latest - if: (github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]') ) || - (github.event_name == 'workflow_dispatch' && github.repository == 'scala/scala3' ) - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: 'sbt' - - uses: sbt/setup-sbt@v1 - - name: Build and test launcher command - run: ./project/scripts/native-integration/bashTests - env: - LAUNCHER_EXPECTED_PROJECT: "dist-mac-aarch64" - - win-x86_64: - name: Deploy and Test on Windows x64 architecture - runs-on: windows-latest - if: (github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]') ) || - (github.event_name == 'workflow_dispatch' && github.repository == 'scala/scala3' ) - steps: - - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: 'sbt' - - uses: sbt/setup-sbt@v1 - - name: Build the launcher command - run: sbt "dist-win-x86_64/Universal/stage" - - name: Run the launcher command tests - run: './project/scripts/native-integration/winTests.bat' - shell: cmd diff --git a/.github/workflows/test-msi.yml b/.github/workflows/test-msi.yml deleted file mode 100644 index 1299c3d55061..000000000000 --- a/.github/workflows/test-msi.yml +++ /dev/null @@ -1,77 +0,0 @@ -################################################################################################### -### THIS IS A REUSABLE WORKFLOW TO TEST SCALA WITH MSI RUNNER ### -### HOW TO USE: ### -### Provide optional `version` to test if installed binaries are installed with ### -### correct Scala version. ### -### NOTE: Requires `scala.msi` artifact uploaded within the same run ### -### ### -################################################################################################### - -name: Test 'scala' MSI Package -run-name: Test 'scala' (${{ inputs.version }}) MSI Package - -on: - workflow_call: - inputs: - version: - required: true - type: string - java-version: - required: true - type : string - -jobs: - test: - runs-on: windows-latest - steps: - - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: ${{ inputs.java-version }} - - name: Download MSI artifact - uses: actions/download-artifact@v4 - with: - name: scala.msi - path: . - - # Run the MSI installer - # During normal installation msiexec would modify the PATH automatically. - # However, it seems not to work in GH Actions. Append the PATH manually instead. - - name: Install Scala Runner - shell: pwsh - run: | - Start-Process 'msiexec.exe' -ArgumentList '/I "scala.msi" /L*V "install.log" /qb' -Wait - Get-Content 'install.log' - Add-Content $env:GITHUB_PATH "C:\Program Files (x86)\scala\bin" - - # Run tests to ensure the Scala Runner was installed and works - - name: Test Scala Runner - shell: pwsh - run: | - scala --version - if (-not (scala --version | Select-String "Scala version \(default\): ${{ inputs.version }}")) { - Write-Host "Invalid Scala version of MSI installed runner, expected ${{ inputs.version }}" - Exit 1 - } - - name : Test the `scalac` command - shell: pwsh - run: | - scalac --version - if (-not (scalac --version | Select-String "Scala compiler version ${{ inputs.version }}")) { - Write-Host "Invalid scalac version of MSI installed runner, expected ${{ inputs.version }}" - Exit 1 - } - - name : Test the `scaladoc` command - shell: pwsh - run: | - scaladoc --version - if (-not (scaladoc --version | Select-String "Scaladoc version ${{ inputs.version }}")) { - Write-Host "Invalid scaladoc version of MSI installed runner, expected ${{ inputs.version }}" - Exit 1 - } - - name : Uninstall the `scala` package - shell: pwsh - run: | - Start-Process 'msiexec.exe' -ArgumentList '/X "scala.msi" /L*V "uninstall.log" /qb' -Wait - Get-Content 'uninstall.log' - \ No newline at end of file diff --git a/build.mill b/build.mill new file mode 100644 index 000000000000..26e679724b6c --- /dev/null +++ b/build.mill @@ -0,0 +1,39 @@ +//| mill-version: 1.0.5 + +package build + +import mill.* +import mill.javalib.publish.* +import mill.scalalib.* +import mill.util.VcsVersion + +object `presentation-compiler` extends SbtModule with PublishModule { + def sources = Task.Sources(os.sub / "src/main") + def scalaVersion = "3.7.3" + def mvnDeps = Seq( + mvn"org.lz4:lz4-java:1.8.0", + mvn"io.get-coursier:interface:1.0.18", + mvn"io.github.plasmon-scala:mtags-interfaces:1.6.1" + .exclude(("org.eclipse.lsp4j", "org.eclipse.lsp4j")) + .exclude(("org.eclipse.lsp4j", "org.eclipse.lsp4j.jsonrpc")), + mvn"io.github.plasmon-scala:mtags_2.13.16:1.6.1", + mvn"org.eclipse.lsp4j:org.eclipse.lsp4j:0.23.1", + mvn"org.scala-lang:scala3-compiler_3:${scalaVersion()}" + ) + def scalacOptions = super.scalacOptions() ++ Seq( + "-source", "3.3", "-Yexplicit-nulls", "-Wsafe-init" + ) + + def artifactName = "scala3-presentation-compiler" + def publishVersion = VcsVersion.vcsState().format(untaggedSuffix = "-SNAPSHOT") + def pomSettings = PomSettings( + description = "scala3-presentation-compiler", + organization = "io.github.plasmon-scala", + url = s"https://github.com/plasmon-scala/scala3", + licenses = Seq(License.`Apache-2.0`), + versionControl = VersionControl.github("plasmon-scala", "scala3"), + developers = Seq( + Developer("alexarchambault", "Alex Archambault", "https://github.com/alexarchambault"), + ) + ) +} diff --git a/changelogs/3.7.3-RC1.md b/changelogs/3.7.3-RC1.md new file mode 100644 index 000000000000..25a2526d8e50 --- /dev/null +++ b/changelogs/3.7.3-RC1.md @@ -0,0 +1,224 @@ +# Release highlights + +- Standardize on `-Vprint:...` (still support `-Xprint:...` as alias) [#22828](https://github.com/scala/scala3/pull/22828) + +# Other changes and fixes + +## Desugaring + +- Fix #23224: Optimize simple tuple extraction [#23373](https://github.com/scala/scala3/pull/23373) + +## Enums + +- Make hashcode of enum items stable [#23218](https://github.com/scala/scala3/pull/23218) + +## Erasure + +- Replace erased class modifiers with Erased base traits [#23447](https://github.com/scala/scala3/pull/23447) +- Bring back part of PruneErasedDefs [#23466](https://github.com/scala/scala3/pull/23466) + +## Experimental: Capture Checking + +- Fix parsing crash for update in later phases [#23390](https://github.com/scala/scala3/pull/23390) +- Implement boxing for singleton type arguments [#23418](https://github.com/scala/scala3/pull/23418) +- Expand Capability types also in arguments of Capability classes [#23427](https://github.com/scala/scala3/pull/23427) +- Adjustments to the capability trilogy [#23428](https://github.com/scala/scala3/pull/23428) +- Set context owner to the method for `paramsToCap` [#23436](https://github.com/scala/scala3/pull/23436) +- Flatten nested capture sets in retainedElementsRaw [#23571](https://github.com/scala/scala3/pull/23571) +- Fix well-formed test for capabilities [#23393](https://github.com/scala/scala3/pull/23393) +- Add restricted capabilities `x.only[C]` [#23485](https://github.com/scala/scala3/pull/23485) +- Rely on hidden sets for use checking [#23580](https://github.com/scala/scala3/pull/23580) + +## Experimental: Seperation Checking + +- Make separation checking controlled by language import [#23560](https://github.com/scala/scala3/pull/23560) + +## Experimental: Erased Definitions + +- Refactorings and fixes to erased definition handling [#23404](https://github.com/scala/scala3/pull/23404) + +## Experimental: Explicit Nulls + +- Add quick fix to remove unnecessary .nn [#23461](https://github.com/scala/scala3/pull/23461) +- Add `stableNull` annotation to force tracking mutable fields [#23528](https://github.com/scala/scala3/pull/23528) + +## Experimental: Global Initialization + +- Rewrite resolveThis in global init checker [#23282](https://github.com/scala/scala3/pull/23282) +- Fix errors in the global initialization checker when compiling bootstrapped dotty [#23429](https://github.com/scala/scala3/pull/23429) +- Fix error in product-sequence match in global init checker [#23480](https://github.com/scala/scala3/pull/23480) + +## Experimental: Into + +- Fix isConversionTargetType test [#23401](https://github.com/scala/scala3/pull/23401) + +## Experimental: Modularity + +- Refinements to skolemizaton [#23513](https://github.com/scala/scala3/pull/23513) + +## Experimental: Unroll + +- Enable UnrollDefinitions phase in REPL frontend phases [#23433](https://github.com/scala/scala3/pull/23433) + +## Extension Methods + +- Avoid forcing extension on check of local select [#23439](https://github.com/scala/scala3/pull/23439) + +## Implicits + +- Refine implicit search fallbacks for better ClassTag handling [#23532](https://github.com/scala/scala3/pull/23532) + +## Inline + +- Fix Symbol.info remapping in TreeTypeMap [#23432](https://github.com/scala/scala3/pull/23432) +- Fail not inlined inline method calls early [#22925](https://github.com/scala/scala3/pull/22925) +- Fix inline export forwarder generation regression [#23126](https://github.com/scala/scala3/pull/23126) + +## Linting + +- Consider setter of effectively private var [#23211](https://github.com/scala/scala3/pull/23211) +- Add accessible check for import usage [#23348](https://github.com/scala/scala3/pull/23348) +- Check OrType in interpolated toString lint [#23365](https://github.com/scala/scala3/pull/23365) +- Use result of lambda type of implicit in CheckUnused [#23497](https://github.com/scala/scala3/pull/23497) + +## Match Types + +- Fix: #23261 Distinguish 0.0 and -0.0 in ConstantType match types [#23265](https://github.com/scala/scala3/pull/23265) + +## Named Tuples + +- Skip bypassing unapply for scala 2 case classes to allow for single-element named tuple in unapply [#23603](https://github.com/scala/scala3/pull/23603) + +## Parser + +- Enforce `-new-syntax` under `-language:future` [#23443](https://github.com/scala/scala3/pull/23443) +- Disallow Scala 2 implicits under `-source:future` [#23472](https://github.com/scala/scala3/pull/23472) + +## Pattern Matching + +- Fix problems in checking that a constructor is uninhabited for exhaustive match checking [#23403](https://github.com/scala/scala3/pull/23403) + +## Pickling + +- Don't force annotation unpickling when testing for SilentIntoAnnot [#23506](https://github.com/scala/scala3/pull/23506) +- Drop invalid assumption from TastyUnpickler [#23353](https://github.com/scala/scala3/pull/23353) + +## Printer + +- Print update modifier when printing update method definitions [#23392](https://github.com/scala/scala3/pull/23392) + +## Positions + +- Compare span points in pathTo to determine best span [#23581](https://github.com/scala/scala3/pull/23581) +- Add line number magic comment support [#23549](https://github.com/scala/scala3/pull/23549) + +## Presentation Compiler + +- Port Inlay hints for name parameters [#23375](https://github.com/scala/scala3/pull/23375) +- Fix: Simplify infer type for apply [#23434](https://github.com/scala/scala3/pull/23434) +- Fix: Inconsistent annotation tooltips [#23454](https://github.com/scala/scala3/pull/23454) +- Fix adjust type when already exists [#23455](https://github.com/scala/scala3/pull/23455) +- Exclude named parameters inlay hints for java defined [#23462](https://github.com/scala/scala3/pull/23462) +- Fix: StringIndexOutOfBoundsException in presentation compiler's hasColon method [#23498](https://github.com/scala/scala3/pull/23498) +- Add InferredMethodProvider for automatic method signature generation [#23563](https://github.com/scala/scala3/pull/23563) +- Fix completions for Quotes [#23619](https://github.com/scala/scala3/pull/23619) +- Handle default arguments in named parameters for inlay hints [#23641](https://github.com/scala/scala3/pull/23641) + +## Quotes + +- Skip splice level checking for `` symbols [#22782](https://github.com/scala/scala3/pull/22782) +- Fix stale top level synthetic package object being used in later runs [#23464](https://github.com/scala/scala3/pull/23464) +- Emit an error for quoted pattern type variable after `new` [#23618](https://github.com/scala/scala3/pull/23618) +- Fix issue with certain polyfunctions not properly matching in macros [#23614](https://github.com/scala/scala3/pull/23614) +- Check PCP of constructor calls on the type [#7531](https://github.com/scala/scala3/pull/7531) + +## Reflection + +- Quotes reflect: sort the typeMembers output list and filter out non-members [#22876](https://github.com/scala/scala3/pull/22876) + +## Reporting + +- Add an explainer to the DoubleDefinition error [#23470](https://github.com/scala/scala3/pull/23470) +- Suppress warnings in comprehensions with 22+ binds [#23590](https://github.com/scala/scala3/pull/23590) +- Unhelpful error message when trying to use named extraction, when not matching case class or named tuple [#23354](https://github.com/scala/scala3/pull/23354) +- Improve error message for conflicting definitions [#23453](https://github.com/scala/scala3/pull/23453) +- `-Yprofile-trace` properly report macro splicing source [#23488](https://github.com/scala/scala3/pull/23488) +- `-Yprofile-trace` profiles all inline calls [#23490](https://github.com/scala/scala3/pull/23490) + +## Rewrites + +- Patch empty implicit parens on error recovery [#22835](https://github.com/scala/scala3/pull/22835) +- Rewrite underscore with optional space [#23525](https://github.com/scala/scala3/pull/23525) + +## Scaladoc + +- Scaladoc: fixes and improvements to context bounds and extension methods [#22156](https://github.com/scala/scala3/pull/22156) +- Encode path of class [#23503](https://github.com/scala/scala3/pull/23503) + +## SemanticDB + +- Bugfix: Also save infos in semanticdb [#23587](https://github.com/scala/scala3/pull/23587) + +## Transform + +- Handle multiple type parameter lists in value class methods [#23516](https://github.com/scala/scala3/pull/23516) +- Check path of module prefix for tailrec [#23491](https://github.com/scala/scala3/pull/23491) + +## Tuples + +- Normalize tuple types in var args seq literals and classOf instances [#23465](https://github.com/scala/scala3/pull/23465) + +## Typer + +- Fix #22922: Add TypeParamRef handling in isSingletonBounded [#23501](https://github.com/scala/scala3/pull/23501) +- Fix this references everywhere in dependent function types [#23514](https://github.com/scala/scala3/pull/23514) +- Don't approximate a type using `Nothing` as prefix [#23531](https://github.com/scala/scala3/pull/23531) +- Support cleanup actions in class completers [#23515](https://github.com/scala/scala3/pull/23515) +- Fix regressions in asSeenFrom introduced in 3.7 [#23438](https://github.com/scala/scala3/pull/23438) +- Use correct owner in eta expansion [#7564](https://github.com/scala/scala3/pull/7564) +- Fix irrefutability checking in `for` with untupling [#23273](https://github.com/scala/scala3/pull/23273) +- Fix missing members reporting for var setters [#23476](https://github.com/scala/scala3/pull/23476) +- Guard against invalid prefixes in argForParam [#23508](https://github.com/scala/scala3/pull/23508) +- Add missing case to TypeComparer [#23550](https://github.com/scala/scala3/pull/23550) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.7.2..3.7.3-RC1` these are: + +``` + 80 Martin Odersky + 56 Hamza Remmal + 22 Wojciech Mazur + 20 noti0na1 + 18 Yichen Xu + 16 Som Snytt + 14 Jan Chyb + 9 Matt Bovel + 7 EnzeXing + 6 Guillaume Martres + 5 Sébastien Doeraene + 5 aherlihy + 4 Zieliński Patryk + 3 Oliver Bračevac + 3 Tomasz Godzik + 2 Alexander + 2 Mikołaj Fornal + 2 Piotr Chabelski + 2 Seyon Sivatharan + 1 Alex1005a + 1 HarrisL2 + 1 Jan + 1 Jentsch + 1 Jędrzej Rochala + 1 Katarzyna Marek + 1 Marc GRIS + 1 Martin Duhem + 1 Patryk Zieliński + 1 Przemysław Sajnóg + 1 Seth Tisue + 1 Wessel W. Bakker + 1 bingchen-li + 1 kijuky +``` diff --git a/changelogs/3.7.3-RC2.md b/changelogs/3.7.3-RC2.md new file mode 100644 index 000000000000..243d06b5348d --- /dev/null +++ b/changelogs/3.7.3-RC2.md @@ -0,0 +1,29 @@ +# Backported chnages + +- Warn if implicit default shadows given [#23559](https://github.com/scala/scala3/pull/23559) +- Bump Scala CLI to v1.8.5 (was v1.8.4) [#23702](https://github.com/scala/scala3/pull/23702) +- Fix issue with pc breaking in requiredMethod on newly overloaded valueOf [#23708](https://github.com/scala/scala3/pull/23708) +- Handle default arguments in named parameters for inlay hints [#23641](https://github.com/scala/scala3/pull/23641) +- Add suppression if nowarn differs [#23652](https://github.com/scala/scala3/pull/23652) +- Fix match type bounds checking problem [#23695](https://github.com/scala/scala3/pull/23695) +- Generalize "Don't approximate a type using Nothing as prefix" [#23628](https://github.com/scala/scala3/pull/23628) +- More careful ClassTag instantiation [#23659](https://github.com/scala/scala3/pull/23659) +- Use more context for implicit search only if no default argument [#23664](https://github.com/scala/scala3/pull/23664) +- Fix extracting refinements from intersection types in dynamic select hovers [#23640](https://github.com/scala/scala3/pull/23640) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.7.3-RC1..3.7.3-RC2` these are: + +``` + 5 Martin Odersky + 4 Som Snytt + 2 Wojciech Mazur + 1 Guillaume Martres + 1 Jan Chyb + 1 Kacper Korban + 1 Piotr Chabelski + 1 aherlihy +``` diff --git a/changelogs/3.7.3-RC3.md b/changelogs/3.7.3-RC3.md new file mode 100644 index 000000000000..59e385fa26a8 --- /dev/null +++ b/changelogs/3.7.3-RC3.md @@ -0,0 +1,14 @@ +# Backported chnages + +- Update scala-cli to 1.9.0 (was 1.8.5) [#23861](https://github.com/scala/scala3/pull/23861) + + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.7.3-RC2..3.7.3-RC3` these are: + +``` + 3 Wojciech Mazur +``` diff --git a/changelogs/3.7.3.md b/changelogs/3.7.3.md new file mode 100644 index 000000000000..c8c31a4d5743 --- /dev/null +++ b/changelogs/3.7.3.md @@ -0,0 +1,238 @@ +# Release highlights + +- Warn if implicit default shadows given [#23559](https://github.com/scala/scala3/pull/23559) +- Standardize on `-Vprint:...` (still support `-Xprint:...` as alias) [#22828](https://github.com/scala/scala3/pull/22828) + +# Other changes and fixes + +## Desugaring + +- Optimize simple tuple extraction [#23373](https://github.com/scala/scala3/pull/23373) + +## Enums + +- Make hashcode of enum items stable [#23218](https://github.com/scala/scala3/pull/23218) + +## Erasure + +- Replace erased class modifiers with Erased base traits [#23447](https://github.com/scala/scala3/pull/23447) +- Bring back part of PruneErasedDefs [#23466](https://github.com/scala/scala3/pull/23466) + +## Experimental: Capture Checking + +- Fix parsing crash for update in later phases [#23390](https://github.com/scala/scala3/pull/23390) +- Implement boxing for singleton type arguments [#23418](https://github.com/scala/scala3/pull/23418) +- Expand Capability types also in arguments of Capability classes [#23427](https://github.com/scala/scala3/pull/23427) +- Adjustments to the capability trilogy [#23428](https://github.com/scala/scala3/pull/23428) +- Set context owner to the method for `paramsToCap` [#23436](https://github.com/scala/scala3/pull/23436) +- Flatten nested capture sets in retainedElementsRaw [#23571](https://github.com/scala/scala3/pull/23571) +- Fix well-formed test for capabilities [#23393](https://github.com/scala/scala3/pull/23393) +- Add restricted capabilities `x.only[C]` [#23485](https://github.com/scala/scala3/pull/23485) +- Rely on hidden sets for use checking [#23580](https://github.com/scala/scala3/pull/23580) + +## Experimental: Seperation Checking + +- Make separation checking controlled by language import [#23560](https://github.com/scala/scala3/pull/23560) + +## Experimental: Erased Definitions + +- Refactorings and fixes to erased definition handling [#23404](https://github.com/scala/scala3/pull/23404) + +## Experimental: Explicit Nulls + +- Add quick fix to remove unnecessary .nn [#23461](https://github.com/scala/scala3/pull/23461) +- Add `stableNull` annotation to force tracking mutable fields [#23528](https://github.com/scala/scala3/pull/23528) + +## Experimental: Global Initialization + +- Rewrite resolveThis in global init checker [#23282](https://github.com/scala/scala3/pull/23282) +- Fix errors in the global initialization checker when compiling bootstrapped dotty [#23429](https://github.com/scala/scala3/pull/23429) +- Fix error in product-sequence match in global init checker [#23480](https://github.com/scala/scala3/pull/23480) + +## Experimental: Into + +- Fix isConversionTargetType test [#23401](https://github.com/scala/scala3/pull/23401) + +## Experimental: Modularity + +- Refinements to skolemizaton [#23513](https://github.com/scala/scala3/pull/23513) + +## Experimental: Unroll + +- Enable UnrollDefinitions phase in REPL frontend phases [#23433](https://github.com/scala/scala3/pull/23433) + +## Extension Methods + +- Avoid forcing extension on check of local select [#23439](https://github.com/scala/scala3/pull/23439) + +## Implicits + +- Refine implicit search fallbacks for better ClassTag handling [#23532](https://github.com/scala/scala3/pull/23532) + +## Inline + +- Fix Symbol.info remapping in TreeTypeMap [#23432](https://github.com/scala/scala3/pull/23432) +- Fail not inlined inline method calls early [#22925](https://github.com/scala/scala3/pull/22925) +- Fix inline export forwarder generation regression [#23126](https://github.com/scala/scala3/pull/23126) + +## Linting + +- Consider setter of effectively private var [#23211](https://github.com/scala/scala3/pull/23211) +- Add accessible check for import usage [#23348](https://github.com/scala/scala3/pull/23348) +- Check OrType in interpolated toString lint [#23365](https://github.com/scala/scala3/pull/23365) +- Use result of lambda type of implicit in CheckUnused [#23497](https://github.com/scala/scala3/pull/23497) +- Add suppression if nowarn differs [#23652](https://github.com/scala/scala3/pull/23652) + +## Match Types + +- Fix: #23261 Distinguish 0.0 and -0.0 in ConstantType match types [#23265](https://github.com/scala/scala3/pull/23265) + +## Named Tuples + +- Skip bypassing unapply for scala 2 case classes to allow for single-element named tuple in unapply [#23603](https://github.com/scala/scala3/pull/23603) + +## Parser + +- Enforce `-new-syntax` under `-language:future` [#23443](https://github.com/scala/scala3/pull/23443) +- Disallow Scala 2 implicits under `-source:future` [#23472](https://github.com/scala/scala3/pull/23472) + +## Pattern Matching + +- Fix problems in checking that a constructor is uninhabited for exhaustive match checking [#23403](https://github.com/scala/scala3/pull/23403) + +## Pickling + +- Don't force annotation unpickling when testing for SilentIntoAnnot [#23506](https://github.com/scala/scala3/pull/23506) +- Drop invalid assumption from TastyUnpickler [#23353](https://github.com/scala/scala3/pull/23353) + +## Printer + +- Print update modifier when printing update method definitions [#23392](https://github.com/scala/scala3/pull/23392) + +## Positions + +- Compare span points in pathTo to determine best span [#23581](https://github.com/scala/scala3/pull/23581) +- Add line number magic comment support [#23549](https://github.com/scala/scala3/pull/23549) + +## Presentation Compiler + +- Port Inlay hints for name parameters [#23375](https://github.com/scala/scala3/pull/23375) +- Fix: Simplify infer type for apply [#23434](https://github.com/scala/scala3/pull/23434) +- Fix: Inconsistent annotation tooltips [#23454](https://github.com/scala/scala3/pull/23454) +- Fix adjust type when already exists [#23455](https://github.com/scala/scala3/pull/23455) +- Exclude named parameters inlay hints for java defined [#23462](https://github.com/scala/scala3/pull/23462) +- Fix: StringIndexOutOfBoundsException in presentation compiler's hasColon method [#23498](https://github.com/scala/scala3/pull/23498) +- Add InferredMethodProvider for automatic method signature generation [#23563](https://github.com/scala/scala3/pull/23563) +- Fix completions for Quotes [#23619](https://github.com/scala/scala3/pull/23619) +- Handle default arguments in named parameters for inlay hints [#23641](https://github.com/scala/scala3/pull/23641) +- Fix issue with pc breaking in requiredMethod on newly overloaded valueOf [#23708](https://github.com/scala/scala3/pull/23708) +- Handle default arguments in named parameters for inlay hints [#23641](https://github.com/scala/scala3/pull/23641) +- Fix extracting refinements from intersection types in dynamic select hovers [#23640](https://github.com/scala/scala3/pull/23640) + +## Quotes + +- Skip splice level checking for `` symbols [#22782](https://github.com/scala/scala3/pull/22782) +- Fix stale top level synthetic package object being used in later runs [#23464](https://github.com/scala/scala3/pull/23464) +- Emit an error for quoted pattern type variable after `new` [#23618](https://github.com/scala/scala3/pull/23618) +- Fix issue with certain polyfunctions not properly matching in macros [#23614](https://github.com/scala/scala3/pull/23614) +- Check PCP of constructor calls on the type [#7531](https://github.com/scala/scala3/pull/7531) + +## Reflection + +- Quotes reflect: sort the typeMembers output list and filter out non-members [#22876](https://github.com/scala/scala3/pull/22876) + +## Reporting + +- Add an explainer to the DoubleDefinition error [#23470](https://github.com/scala/scala3/pull/23470) +- Suppress warnings in comprehensions with 22+ binds [#23590](https://github.com/scala/scala3/pull/23590) +- Unhelpful error message when trying to use named extraction, when not matching case class or named tuple [#23354](https://github.com/scala/scala3/pull/23354) +- Improve error message for conflicting definitions [#23453](https://github.com/scala/scala3/pull/23453) +- `-Yprofile-trace` properly report macro splicing source [#23488](https://github.com/scala/scala3/pull/23488) +- `-Yprofile-trace` profiles all inline calls [#23490](https://github.com/scala/scala3/pull/23490) + +## Rewrites + +- Patch empty implicit parens on error recovery [#22835](https://github.com/scala/scala3/pull/22835) +- Rewrite underscore with optional space [#23525](https://github.com/scala/scala3/pull/23525) + +## Runner + +- Bump Scala CLI to v1.9.0 (was v1.8.4) [#23702](https://github.com/scala/scala3/pull/23856) + +## Scaladoc + +- Scaladoc: fixes and improvements to context bounds and extension methods [#22156](https://github.com/scala/scala3/pull/22156) +- Encode path of class [#23503](https://github.com/scala/scala3/pull/23503) + +## SemanticDB + +- Bugfix: Also save infos in semanticdb [#23587](https://github.com/scala/scala3/pull/23587) + +## Transform + +- Handle multiple type parameter lists in value class methods [#23516](https://github.com/scala/scala3/pull/23516) +- Check path of module prefix for tailrec [#23491](https://github.com/scala/scala3/pull/23491) + +## Tuples + +- Normalize tuple types in var args seq literals and classOf instances [#23465](https://github.com/scala/scala3/pull/23465) + +## Typer + +- Fix #22922: Add TypeParamRef handling in isSingletonBounded [#23501](https://github.com/scala/scala3/pull/23501) +- Fix this references everywhere in dependent function types [#23514](https://github.com/scala/scala3/pull/23514) +- Don't approximate a type using `Nothing` as prefix [#23531](https://github.com/scala/scala3/pull/23531) +- Support cleanup actions in class completers [#23515](https://github.com/scala/scala3/pull/23515) +- Fix regressions in asSeenFrom introduced in 3.7 [#23438](https://github.com/scala/scala3/pull/23438) +- Use correct owner in eta expansion [#7564](https://github.com/scala/scala3/pull/7564) +- Fix irrefutability checking in `for` with untupling [#23273](https://github.com/scala/scala3/pull/23273) +- Fix missing members reporting for var setters [#23476](https://github.com/scala/scala3/pull/23476) +- Guard against invalid prefixes in argForParam [#23508](https://github.com/scala/scala3/pull/23508) +- Add missing case to TypeComparer [#23550](https://github.com/scala/scala3/pull/23550) +- Fix match type bounds checking problem [#23695](https://github.com/scala/scala3/pull/23695) +- Generalize "Don't approximate a type using Nothing as prefix" [#23628](https://github.com/scala/scala3/pull/23628) +- More careful ClassTag instantiation [#23659](https://github.com/scala/scala3/pull/23659) +- Use more context for implicit search only if no default argument [#23664](https://github.com/scala/scala3/pull/23664) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.7.2..3.7.3` these are: + +``` + 85 Martin Odersky + 56 Hamza Remmal + 29 Wojciech Mazur + 20 Som Snytt + 20 noti0na1 + 18 Yichen Xu + 15 Jan Chyb + 9 Matt Bovel + 7 EnzeXing + 7 Guillaume Martres + 6 aherlihy + 5 Sébastien Doeraene + 4 Zieliński Patryk + 3 Oliver Bračevac + 3 Piotr Chabelski + 3 Tomasz Godzik + 2 Alexander + 2 Mikołaj Fornal + 2 Seyon Sivatharan + 1 Alex1005a + 1 HarrisL2 + 1 Jan + 1 Jentsch + 1 Jędrzej Rochala + 1 Kacper Korban + 1 Katarzyna Marek + 1 Marc GRIS + 1 Martin Duhem + 1 Patryk Zieliński + 1 Przemysław Sajnóg + 1 Seth Tisue + 1 Wessel W. Bakker + 1 bingchen-li + 1 kijuky +``` diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index a9f0c677e17f..58ea3e03edba 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -92,14 +92,21 @@ extends ImplicitRunInfo, ConstraintRunInfo, cc.CaptureRunInfo { mySuspendedMessages.getOrElseUpdate(warning.pos.source, mutable.LinkedHashSet.empty) += warning def nowarnAction(dia: Diagnostic): Action.Warning.type | Action.Verbose.type | Action.Silent.type = - mySuppressions.getOrElse(dia.pos.source, Nil).find(_.matches(dia)) match { - case Some(s) => + mySuppressions.get(dia.pos.source) match + case Some(suppressions) => + val matching = suppressions.iterator.filter(_.matches(dia)) + if matching.hasNext then + val s = matching.next() + for other <- matching do + if !other.used then + other.markSuperseded() // superseded unless marked used later s.markUsed() - if (s.verbose) Action.Verbose + if s.verbose then Action.Verbose else Action.Silent - case _ => + else Action.Warning - } + case none => + Action.Warning def registerNowarn(annotPos: SourcePosition, range: Span)(conf: String, pos: SrcPos)(using Context): Unit = var verbose = false @@ -118,12 +125,10 @@ extends ImplicitRunInfo, ConstraintRunInfo, cc.CaptureRunInfo { .merge addSuppression: Suppression(annotPos, filters, range.start, range.end, verbose) - .tap: sup => - if filters == List(MessageFilter.None) then sup.markUsed() // invalid suppressions, don't report as unused def addSuppression(sup: Suppression): Unit = val suppressions = mySuppressions.getOrElseUpdate(sup.annotPos.source, ListBuffer.empty) - if sup.start != sup.end && suppressions.forall(x => x.start != sup.start || x.end != sup.end) then + if sup.start != sup.end then suppressions += sup def reportSuspendedMessages(source: SourceFile)(using Context): Unit = { @@ -134,7 +139,8 @@ extends ImplicitRunInfo, ConstraintRunInfo, cc.CaptureRunInfo { mySuspendedMessages.remove(source).foreach(_.foreach(ctx.reporter.issueIfNotSuppressed)) } - def runFinished(hasErrors: Boolean): Unit = + def runFinished()(using Context): Unit = + val hasErrors = ctx.reporter.hasErrors // report suspended messages (in case the run finished before typer) mySuspendedMessages.keysIterator.toList.foreach(reportSuspendedMessages) // report unused nowarns only if all all phases are done @@ -142,10 +148,16 @@ extends ImplicitRunInfo, ConstraintRunInfo, cc.CaptureRunInfo { for source <- mySuppressions.keysIterator.toList sups <- mySuppressions.remove(source) - sup <- sups.reverse - if !sup.used do - report.warning("@nowarn annotation does not suppress any warnings", sup.annotPos) + val suppressions = sups.reverse.toList + for sup <- suppressions do + if !sup.used + && !suppressions.exists(s => s.ne(sup) && s.used && s.annotPos == sup.annotPos) // duplicate + && sup.filters != List(MessageFilter.None) // invalid suppression, don't report as unused + then + val more = if sup.superseded then " but matches a diagnostic" else "" + report.warning("@nowarn annotation does not suppress any warnings"+more, sup.annotPos) + end suppressions /** The compilation units currently being compiled, this may return different * results over time. @@ -411,7 +423,7 @@ extends ImplicitRunInfo, ConstraintRunInfo, cc.CaptureRunInfo { ctx.reporter.finalizeReporting() if (!ctx.reporter.hasErrors) Rewrites.writeBack() - suppressions.runFinished(hasErrors = ctx.reporter.hasErrors) + suppressions.runFinished() while (finalizeActions.nonEmpty && canProgress()) { val action = finalizeActions.remove(0) action() diff --git a/compiler/src/dotty/tools/dotc/ast/Positioned.scala b/compiler/src/dotty/tools/dotc/ast/Positioned.scala index d8017783f47f..5b57733eaeb1 100644 --- a/compiler/src/dotty/tools/dotc/ast/Positioned.scala +++ b/compiler/src/dotty/tools/dotc/ast/Positioned.scala @@ -3,7 +3,8 @@ package dotc package ast import util.Spans.* -import util.{SourceFile, SourcePosition, SrcPos} +import util.{SourceFile, SourcePosition, SrcPos, WrappedSourceFile} +import WrappedSourceFile.MagicHeaderInfo, MagicHeaderInfo.* import core.Contexts.* import core.Decorators.* import core.NameOps.* @@ -51,7 +52,15 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Src def source: SourceFile = mySource - def sourcePos(using Context): SourcePosition = source.atSpan(span) + def sourcePos(using Context): SourcePosition = + val info = WrappedSourceFile.locateMagicHeader(source) + info match + case HasHeader(offset, originalFile) => + if span.start >= offset then // This span is in user code + originalFile.atSpan(span.shift(-offset)) + else // Otherwise, return the source position in the wrapper code + source.atSpan(span) + case _ => source.atSpan(span) /** This positioned item, widened to `SrcPos`. Used to make clear we only need the * position, typically for error reporting. diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 5cdc742fa753..a2c557ea2987 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -167,6 +167,7 @@ private sealed trait WarningSettings: private val WimplausiblePatterns = BooleanSetting(WarningSetting, "Wimplausible-patterns", "Warn if comparison with a pattern value looks like it might always fail.") private val WunstableInlineAccessors = BooleanSetting(WarningSetting, "WunstableInlineAccessors", "Warn an inline methods has references to non-stable binary APIs.") private val WtoStringInterpolated = BooleanSetting(WarningSetting, "Wtostring-interpolated", "Warn a standard interpolator used toString on a reference type.") + private val WrecurseWithDefault = BooleanSetting(WarningSetting, "Wrecurse-with-default", "Warn when a method calls itself with a default argument.") private val Wunused: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( WarningSetting, name = "Wunused", @@ -309,6 +310,7 @@ private sealed trait WarningSettings: def implausiblePatterns(using Context): Boolean = allOr(WimplausiblePatterns) def unstableInlineAccessors(using Context): Boolean = allOr(WunstableInlineAccessors) def toStringInterpolated(using Context): Boolean = allOr(WtoStringInterpolated) + def recurseWithDefault(using Context): Boolean = allOr(WrecurseWithDefault) def checkInit(using Context): Boolean = allOr(WcheckInit) /** -X "Extended" or "Advanced" settings */ @@ -444,6 +446,8 @@ private sealed trait YSettings: val YbestEffort: Setting[Boolean] = BooleanSetting(ForkSetting, "Ybest-effort", "Enable best-effort compilation attempting to produce betasty to the META-INF/best-effort directory, regardless of errors, as part of the pickler phase.") val YwithBestEffortTasty: Setting[Boolean] = BooleanSetting(ForkSetting, "Ywith-best-effort-tasty", "Allow to compile using best-effort tasty files. If such file is used, the compiler will stop after the pickler phase.") + val YmagicOffsetHeader: Setting[String] = StringSetting(ForkSetting, "Ymagic-offset-header", "header", "Specify the magic header comment that marks the start of the actual code in generated wrapper scripts. Example: -Ymagic-offset-header:SOURCE_CODE_START. Then, in the source, the magic comment `///SOURCE_CODE_START:` marks the start of user code. The comment should be suffixed by `:` to indicate the original file.", "") + // Experimental language features @deprecated(message = "This flag has no effect and will be removed in a future version.", since = "3.7.0") val YnoKindPolymorphism: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-kind-polymorphism", "Disable kind polymorphism. (This flag has no effect)", deprecation = Deprecation.removed()) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index a6f59d8b2d33..7fac8c818a1a 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -6507,7 +6507,7 @@ object Types extends TypeUtils { protected def range(lo: Type, hi: Type): Type = if variance > 0 then hi else if variance < 0 then - if (lo eq defn.NothingType) && hi.hasSimpleKind then + if (lo eq defn.NothingType) then // Approximate by Nothing & hi instead of just Nothing, in case the // approximated type is used as the prefix of another type (this would // lead to a type with a `NoDenotation` denot and a possible @@ -6518,8 +6518,14 @@ object Types extends TypeUtils { // example if Nothing is the type of a parameter being depended on in // a MethodType) // - // Test case in tests/pos/i23530.scala - AndType(lo, hi) + // Test case in tests/pos/i23530.scala (and tests/pos/i23627.scala for + // the higher-kinded case which requires eta-expansion) + hi.etaExpand match + case expandedHi: HKTypeLambda => + expandedHi.derivedLambdaType(resType = AndType(lo, expandedHi.resType)) + case _ => + // simple-kinded case + AndType(lo, hi) else lo else if lo `eq` hi then lo diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 2dedcd788ec4..103687abdbff 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -232,6 +232,9 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case UnnecessaryNN // errorNumber: 216 case ErasedNotPureID // errorNumber: 217 case IllegalErasedDefID // errorNumber: 218 + case CannotInstantiateQuotedTypeVarID // errorNumber: 219 + case DefaultShadowsGivenID // errorNumber: 220 + case RecurseWithDefaultID // errorNumber: 221 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala index 7fddfc8d6ed0..0414bdb3c58b 100644 --- a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala +++ b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala @@ -11,7 +11,7 @@ import core.Decorators.* import printing.Highlighting.{Blue, Red, Yellow} import printing.SyntaxHighlighting import Diagnostic.* -import util.{ SourcePosition, NoSourcePosition } +import util.{SourcePosition, NoSourcePosition} import util.Chars.{ LF, CR, FF, SU } import scala.annotation.switch diff --git a/compiler/src/dotty/tools/dotc/reporting/WConf.scala b/compiler/src/dotty/tools/dotc/reporting/WConf.scala index ac25f2f6cd30..cff15aa6dc38 100644 --- a/compiler/src/dotty/tools/dotc/reporting/WConf.scala +++ b/compiler/src/dotty/tools/dotc/reporting/WConf.scala @@ -10,6 +10,7 @@ import dotty.tools.dotc.interfaces.SourceFile import dotty.tools.dotc.reporting.MessageFilter.SourcePattern import java.util.regex.PatternSyntaxException +import scala.PartialFunction.cond import scala.annotation.internal.sharable import scala.util.matching.Regex @@ -136,13 +137,20 @@ object WConf: if (parseErrorss.nonEmpty) Left(parseErrorss.flatten) else Right(WConf(configs)) -class Suppression(val annotPos: SourcePosition, filters: List[MessageFilter], val start: Int, val end: Int, val verbose: Boolean): - private var _used = false - def used: Boolean = _used +class Suppression(val annotPos: SourcePosition, val filters: List[MessageFilter], val start: Int, val end: Int, val verbose: Boolean): + inline def unusedState = 0 + inline def usedState = 1 + inline def supersededState = 2 + private var _used = unusedState + def used: Boolean = _used == usedState + def superseded: Boolean = _used == supersededState def markUsed(): Unit = - _used = true + _used = usedState + def markSuperseded(): Unit = + _used = supersededState def matches(dia: Diagnostic): Boolean = val pos = dia.pos pos.exists && start <= pos.start && pos.end <= end && filters.forall(_.matches(dia)) override def toString = s"Suppress in ${annotPos.source} $start..$end [${filters.mkString(", ")}]" +end Suppression diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index eb5e692b2915..210322841158 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3435,6 +3435,18 @@ final class QuotedTypeMissing(tpe: Type)(using Context) extends StagingMessage(Q end QuotedTypeMissing +final class CannotInstantiateQuotedTypeVar(symbol: Symbol)(using patternCtx: Context) extends StagingMessage(CannotInstantiateQuotedTypeVarID): + override protected def msg(using Context): String = + i"""Quoted pattern type variable `${symbol.name}` cannot be instantiated. + |If you meant to refer to a class named `${symbol.name}`, wrap it in backticks. + |If you meant to introduce a binding, this is not allowed after `new`. You might + |want to use the lower-level `quotes.reflect` API instead. + |Read more about type variables in quoted pattern in the Scala documentation: + |https://docs.scala-lang.org/scala3/guides/macros/quotes.html#type-variables-in-quoted-patterns + """ + + override protected def explain(using Context): String = "" + final class DeprecatedAssignmentSyntax(key: Name, value: untpd.Tree)(using Context) extends SyntaxMsg(DeprecatedAssignmentSyntaxID): override protected def msg(using Context): String = i"""Deprecated syntax: since 3.7 this is interpreted as a named tuple with one element, @@ -3652,3 +3664,15 @@ final class IllegalErasedDef(sym: Symbol)(using Context) extends TypeMsg(Illegal override protected def explain(using Context): String = "Only non-lazy immutable values can be `erased`" end IllegalErasedDef + +final class DefaultShadowsGiven(name: Name)(using Context) extends TypeMsg(DefaultShadowsGivenID): + override protected def msg(using Context): String = + i"Argument for implicit parameter $name was supplied using a default argument." + override protected def explain(using Context): String = + "Usually the given in scope is intended, but you must specify it after explicit `using`." + +final class RecurseWithDefault(name: Name)(using Context) extends TypeMsg(RecurseWithDefaultID): + override protected def msg(using Context): String = + i"Recursive call used a default argument for parameter $name." + override protected def explain(using Context): String = + "It's more explicit to pass current or modified arguments in a recursion." diff --git a/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala b/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala index 3c7216625a7c..272db26bdd3c 100644 --- a/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala +++ b/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala @@ -7,7 +7,7 @@ import core.Contexts.* import collection.mutable import scala.annotation.tailrec import dotty.tools.dotc.reporting.Reporter -import dotty.tools.dotc.util.SourcePosition; +import dotty.tools.dotc.util.SourcePosition import java.io.OutputStreamWriter import java.nio.charset.StandardCharsets.UTF_8 diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index e2505144abda..8bf88a0027c4 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -267,8 +267,14 @@ object PatternMatcher { def matchArgsPatternPlan(args: List[Tree], syms: List[Symbol]): Plan = args match { case arg :: args1 => - val sym :: syms1 = syms: @unchecked - patternPlan(sym, arg, matchArgsPatternPlan(args1, syms1)) + if (args.length != syms.length) + report.error(UnapplyInvalidNumberOfArguments(tree, tree.tpe :: Nil), arg.srcPos) + // Generate a throwaway but type-correct plan. + // This plan will never execute because it'll be guarded by a `NonNullTest`. + ResultPlan(tpd.Throw(tpd.nullLiteral)) + else + val sym :: syms1 = syms: @unchecked + patternPlan(sym, arg, matchArgsPatternPlan(args1, syms1)) case Nil => assert(syms.isEmpty) onSuccess @@ -343,7 +349,13 @@ object PatternMatcher { receiver.ensureConforms(defn.NonEmptyTupleTypeRef), // If scrutinee is a named tuple, cast to underlying tuple Literal(Constant(i))) - if (isSyntheticScala2Unapply(unapp.symbol) && caseAccessors.length == args.length) + def getOfGetMatch(gm: Tree) = gm.select(nme.get, _.info.isParameterless) + // Disable Scala2Unapply optimization if the argument is a named argument for a single-element named tuple to + // enable selecting the field. See i23131.scala for test cases. + val wasUnaryNamedTupleSelectArgForNamedTuple = + args.length == 1 && args.head.removeAttachment(FirstTransform.WasNamedArg).isDefined && + isGetMatch(unappType) && getOfGetMatch(unapp).tpe.widenDealias.isNamedTupleType + if (isSyntheticScala2Unapply(unapp.symbol) && caseAccessors.length == args.length && !wasUnaryNamedTupleSelectArgForNamedTuple) def tupleSel(sym: Symbol) = // If scrutinee is a named tuple, cast to underlying tuple, so that we can // continue to select with _1, _2, ... @@ -376,7 +388,7 @@ object PatternMatcher { else { assert(isGetMatch(unappType)) val argsPlan = { - val get = ref(unappResult).select(nme.get, _.info.isParameterless) + val get = getOfGetMatch(ref(unappResult)) val arity = productArity(get.tpe.stripNamedTuple, unapp.srcPos) if (isUnapplySeq) letAbstract(get) { getResult => @@ -386,9 +398,6 @@ object PatternMatcher { } else letAbstract(get) { getResult => - def isUnaryNamedTupleSelectArg(arg: Tree) = - get.tpe.widenDealias.isNamedTupleType - && arg.removeAttachment(FirstTransform.WasNamedArg).isDefined // Special case: Normally, we pull out the argument wholesale if // there is only one. But if the argument is a named argument for // a single-element named tuple, we have to select the field instead. @@ -396,7 +405,7 @@ object PatternMatcher { // of patterns we add a WasNamedArg attachment, which is used to guide the // logic here. See i22900.scala for test cases. val selectors = args match - case arg :: Nil if !isUnaryNamedTupleSelectArg(arg) => + case arg :: Nil if !wasUnaryNamedTupleSelectArgForNamedTuple => ref(getResult) :: Nil case _ => productSelectors(getResult.info).map(ref(getResult).select(_)) diff --git a/compiler/src/dotty/tools/dotc/transform/TailRec.scala b/compiler/src/dotty/tools/dotc/transform/TailRec.scala index 08c2c6a015c0..690a180e52ca 100644 --- a/compiler/src/dotty/tools/dotc/transform/TailRec.scala +++ b/compiler/src/dotty/tools/dotc/transform/TailRec.scala @@ -4,9 +4,9 @@ package transform import ast.{TreeTypeMap, tpd} import config.Printers.tailrec import core.* -import Contexts.*, Flags.*, Symbols.*, Decorators.em +import Contexts.*, Flags.*, Symbols.*, Decorators.* import Constants.Constant -import NameKinds.{TailLabelName, TailLocalName, TailTempName} +import NameKinds.{DefaultGetterName, TailLabelName, TailLocalName, TailTempName} import StdNames.nme import reporting.* import transform.MegaPhase.MiniPhase @@ -325,7 +325,14 @@ class TailRec extends MiniPhase { method.matches(calledMethod) && enclosingClass.appliedRef.widen <:< prefix.tpe.widenDealias - if (isRecursiveCall) + if isRecursiveCall then + if ctx.settings.Whas.recurseWithDefault then + tree.args.find(_.symbol.name.is(DefaultGetterName)) match + case Some(arg) => + val DefaultGetterName(_, index) = arg.symbol.name: @unchecked + report.warning(RecurseWithDefault(calledMethod.info.firstParamNames(index)), tree.srcPos) + case _ => + if (inTailPosition) { tailrec.println("Rewriting tail recursive call: " + tree.span) rewrote = true diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 50363759c542..b7e1f349a377 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -685,7 +685,7 @@ object SpaceEngine { val refined = trace(i"refineUsingParent($tp, $sym1, $mixins)")(TypeOps.refineUsingParent(tp, sym1, mixins)) def containsUninhabitedField(tp: Type): Boolean = - tp.fields.exists { field => + !tp.typeSymbol.is(ModuleClass) && tp.fields.exists { field => !field.symbol.flags.is(Lazy) && field.info.dealias.isBottomType } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index da6b20b75c29..290e061772e4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -774,6 +774,11 @@ trait Applications extends Compatibility { methodType.isImplicitMethod && (applyKind == ApplyKind.Using || failEmptyArgs) if !defaultArg.isEmpty then + if methodType.isImplicitMethod && ctx.mode.is(Mode.ImplicitsEnabled) + && !inferImplicitArg(formal, appPos.span).tpe.isError + then + report.warning(DefaultShadowsGiven(methodType.paramNames(n)), appPos) + defaultArg.tpe.widen match case _: MethodOrPoly if testOnly => matchArgs(args1, formals1, n + 1) case _ => matchArgs(args1, addTyped(treeToArg(defaultArg)), n + 1) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index ceec2392af1a..ecbb34ea2949 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -145,22 +145,26 @@ object Checking { val checker = new TypeTraverser: def traverse(tp: Type) = tp match - case AppliedType(tycon, argTypes) - if !(tycon.typeSymbol.is(JavaDefined) && ctx.compilationUnit.isJava) - // Don't check bounds in Java units that refer to Java type constructors. - // Scala is not obliged to do Java type checking and in fact i17763 goes wrong - // if we attempt to check bounds of F-bounded mutually recursive Java interfaces. - // Do check all bounds in Scala units and those bounds in Java units that - // occur in applications of Scala type constructors. - && !isCaptureChecking || tycon.typeSymbol.is(CaptureChecked) - // Don't check bounds when capture checking type constructors that were not - // themselves capture checked. Since the type constructor could not foresee - // possible capture sets, it's better to be lenient for backwards compatibility. - => - checkAppliedType( - untpd.AppliedTypeTree(TypeTree(tycon), argTypes.map(TypeTree(_))) - .withType(tp).withSpan(tpt.span.toSynthetic), - tpt) + case tp @ AppliedType(tycon, argTypes) => + // Should the type be re-checked in the CC phase? + // Exempted are types that are not themselves capture-checked. + // Since the type constructor could not foresee possible capture sets, + // it's better to be lenient for backwards compatibility. + // Also exempted are match aliases. See tuple-ops.scala for an example that + // would fail otherwise. + def checkableUnderCC = + tycon.typeSymbol.is(CaptureChecked) && !tp.isMatchAlias + if !(tycon.typeSymbol.is(JavaDefined) && ctx.compilationUnit.isJava) + // Don't check bounds in Java units that refer to Java type constructors. + // Scala is not obliged to do Java type checking and in fact i17763 goes wrong + // if we attempt to check bounds of F-bounded mutually recursive Java interfaces. + // Do check all bounds in Scala units and those bounds in Java units that + // occur in applications of Scala type constructors. + && (!isCaptureChecking || checkableUnderCC) then + checkAppliedType( + untpd.AppliedTypeTree(TypeTree(tycon), argTypes.map(TypeTree(_))) + .withType(tp).withSpan(tpt.span.toSynthetic), + tpt) case _ => traverseChildren(tp) checker.traverse(tpt.tpe) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 4e7c4336b852..f979654e9811 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -222,7 +222,7 @@ trait QuotesAndSplices { if ctx.mode.is(Mode.InPatternAlternative) then report.error(IllegalVariableInPatternAlternative(tree.name), tree.srcPos) val typeSym = inContext(quotePatternOuterContext(ctx)) { - newSymbol(ctx.owner, tree.name.toTypeName, Case, typeSymInfo, NoSymbol, tree.span) + newSymbol(ctx.owner, tree.name.toTypeName, Synthetic | Case, typeSymInfo, NoSymbol, tree.span) } addQuotedPatternTypeVariable(typeSym) Bind(typeSym, untpd.Ident(nme.WILDCARD).withType(typeSymInfo)).withSpan(tree.span) diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 8df2fc1ed4b7..0c35d0377e51 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -20,6 +20,7 @@ import ast.tpd.* import Synthesizer.* import sbt.ExtractDependencies.* import xsbti.api.DependencyContext.* +import TypeComparer.{fullLowerBound, fullUpperBound} /** Synthesize terms for special classes */ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): @@ -38,10 +39,32 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): // bounds are usually widened during instantiation. instArg(tp.tp1) case tvar: TypeVar if ctx.typerState.constraint.contains(tvar) => + // If tvar has a lower or upper bound: + // 1. If the bound is not another type variable, use this as approximation. + // 2. Otherwise, if the type can be forced to be fully defined, use that type + // as approximation. + // 3. Otherwise leave argument uninstantiated. + // The reason for (2) is that we observed complicated constraints in i23611.scala + // that get better types if a fully defined type is computed than if several type + // variables are approximated incrementally. This is a minimization of some ZIO code. + // So in order to keep backwards compatibility (where before we _only_ did 2) we + // add that special case. + def isGroundConstr(tp: Type): Boolean = tp.dealias match + case tvar: TypeVar if ctx.typerState.constraint.contains(tvar) => false + case pref: TypeParamRef if ctx.typerState.constraint.contains(pref) => false + case tp: AndOrType => isGroundConstr(tp.tp1) && isGroundConstr(tp.tp2) + case _ => true instArg( - if tvar.hasLowerBound then tvar.instantiate(fromBelow = true) - else if tvar.hasUpperBound then tvar.instantiate(fromBelow = false) - else NoType) + if tvar.hasLowerBound then + if isGroundConstr(fullLowerBound(tvar.origin)) then tvar.instantiate(fromBelow = true) + else if isFullyDefined(tp, ForceDegree.all) then tp + else NoType + else if tvar.hasUpperBound then + if isGroundConstr(fullUpperBound(tvar.origin)) then tvar.instantiate(fromBelow = false) + else if isFullyDefined(tp, ForceDegree.all) then tp + else NoType + else + NoType) case _ => tp @@ -569,9 +592,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): resType <:< target val tparams = poly.paramRefs val variances = childClass.typeParams.map(_.paramVarianceSign) - val instanceTypes = tparams.lazyZip(variances).map((tparam, variance) => + val instanceTypes = tparams.lazyZip(variances).map: (tparam, variance) => TypeComparer.instanceType(tparam, fromBelow = variance < 0, Widen.Unions) - ) val instanceType = resType.substParams(poly, instanceTypes) // this is broken in tests/run/i13332intersection.scala, // because type parameters are not correctly inferred. diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 10a061ab8fc4..e9e3e22342bf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1237,6 +1237,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => var tpt1 = typedType(tree.tpt) val tsym = tpt1.tpe.underlyingClassRef(refinementOK = false).typeSymbol + if ctx.mode.isQuotedPattern && tpt1.tpe.typeSymbol.isAllOf(Synthetic | Case) then + val errorTp = errorType(CannotInstantiateQuotedTypeVar(tpt1.tpe.typeSymbol), tpt1.srcPos) + return cpy.New(tree)(tpt1).withType(errorTp) if tsym.is(Package) then report error(em"$tsym cannot be instantiated", tpt1.srcPos) tpt1 = tpt1.withType(ensureAccessible(tpt1.tpe, superAccess = false, tpt1.srcPos)) @@ -4359,11 +4362,17 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val arg = inferImplicitArg(formal, tree.span.endPos) + lazy val defaultArg = findDefaultArgument(argIndex) + .showing(i"default argument: for $formal, $tree, $argIndex = $result", typr) + def argHasDefault = hasDefaultParams && !defaultArg.isEmpty + def canProfitFromMoreConstraints = arg.tpe.isInstanceOf[AmbiguousImplicits] - // ambiguity could be decided by more constraints - || !isFullyDefined(formal, ForceDegree.none) - // more context might constrain type variables which could make implicit scope larger + // Ambiguity could be decided by more constraints + || !isFullyDefined(formal, ForceDegree.none) && !argHasDefault + // More context might constrain type variables which could make implicit scope larger. + // But in this case we should search with additional arguments typed only if there + // is no default argument. arg.tpe match case failed: SearchFailureType if canProfitFromMoreConstraints => @@ -4376,15 +4385,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case failed: AmbiguousImplicits => arg :: implicitArgs(formals1, argIndex + 1, pt) case failed: SearchFailureType => - lazy val defaultArg = findDefaultArgument(argIndex) - .showing(i"default argument: for $formal, $tree, $argIndex = $result", typr) - if !hasDefaultParams || defaultArg.isEmpty then - // no need to search further, the adapt fails in any case - // the reason why we continue inferring arguments in case of an AmbiguousImplicits - // is that we need to know whether there are further errors. - // If there are none, we have to propagate the ambiguity to the caller. - arg :: formals1.map(dummyArg) - else + if argHasDefault then // This is tricky. On the one hand, we need the defaultArg to // correctly type subsequent formal parameters in the same using // clause in case there are parameter dependencies. On the other hand, @@ -4395,6 +4396,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // `if propFail.exists` where we re-type the whole using clause with named // arguments for all implicits that were found. arg :: inferArgsAfter(defaultArg) + else + // no need to search further, the adapt fails in any case + // the reason why we continue inferring arguments in case of an AmbiguousImplicits + // is that we need to know whether there are further errors. + // If there are none, we have to propagate the ambiguity to the caller. + arg :: formals1.map(dummyArg) case _ => arg :: inferArgsAfter(arg) end implicitArgs diff --git a/compiler/src/dotty/tools/dotc/util/SourceFile.scala b/compiler/src/dotty/tools/dotc/util/SourceFile.scala index eb99fe99d926..9cc3dc5e731d 100644 --- a/compiler/src/dotty/tools/dotc/util/SourceFile.scala +++ b/compiler/src/dotty/tools/dotc/util/SourceFile.scala @@ -7,6 +7,7 @@ import scala.language.unsafeNulls import dotty.tools.io.* import Spans.* import core.Contexts.* +import core.Decorators.* import scala.io.Codec import Chars.* @@ -61,6 +62,36 @@ object ScriptSourceFile { } } +object WrappedSourceFile: + enum MagicHeaderInfo: + case HasHeader(offset: Int, originalFile: SourceFile) + case NoHeader + import MagicHeaderInfo.* + + private val cache: mutable.HashMap[SourceFile, MagicHeaderInfo] = mutable.HashMap.empty + + def locateMagicHeader(sourceFile: SourceFile)(using Context): MagicHeaderInfo = + def findOffset: MagicHeaderInfo = + val magicHeader = ctx.settings.YmagicOffsetHeader.value + if magicHeader.isEmpty then NoHeader + else + val text = new String(sourceFile.content) + val headerQuoted = java.util.regex.Pattern.quote("///" + magicHeader) + val regex = s"(?m)^$headerQuoted:(.+)$$".r + regex.findFirstMatchIn(text) match + case Some(m) => + val markerOffset = m.start + val sourceStartOffset = sourceFile.nextLine(markerOffset) + val file = ctx.getFile(m.group(1)) + if file.exists then + HasHeader(sourceStartOffset, ctx.getSource(file)) + else + report.warning(em"original source file not found: ${file.path}") + NoHeader + case None => NoHeader + val result = cache.getOrElseUpdate(sourceFile, findOffset) + result + class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends interfaces.SourceFile { import SourceFile.* diff --git a/mill b/mill new file mode 100755 index 000000000000..4a0cb640bd7c --- /dev/null +++ b/mill @@ -0,0 +1,333 @@ +#!/usr/bin/env sh + +# This is a wrapper script, that automatically selects or downloads Mill from Maven Central or GitHub release pages. +# +# This script determines the Mill version to use by trying these sources +# - env-variable `MILL_VERSION` +# - local file `.mill-version` +# - local file `.config/mill-version` +# - `mill-version` from YAML fronmatter of current buildfile +# - if accessible, find the latest stable version available on Maven Central (https://repo1.maven.org/maven2) +# - env-variable `DEFAULT_MILL_VERSION` +# +# If a version has the suffix '-native' a native binary will be used. +# If a version has the suffix '-jvm' an executable jar file will be used, requiring an already installed Java runtime. +# If no such suffix is found, the script will pick a default based on version and platform. +# +# Once a version was determined, it tries to use either +# - a system-installed mill, if found and it's version matches +# - an already downloaded version under ~/.cache/mill/download +# +# If no working mill version was found on the system, +# this script downloads a binary file from Maven Central or Github Pages (this is version dependent) +# into a cache location (~/.cache/mill/download). +# +# Mill Project URL: https://github.com/com-lihaoyi/mill +# Script Version: 1.0.0-M1-21-7b6fae-DIRTY892b63e8 +# +# If you want to improve this script, please also contribute your changes back! +# This script was generated from: dist/scripts/src/mill.sh +# +# Licensed under the Apache License, Version 2.0 + +set -e + +if [ "$1" = "--setup-completions" ] ; then + # Need to preserve the first position of those listed options + MILL_FIRST_ARG=$1 + shift +fi + +if [ -z "${DEFAULT_MILL_VERSION}" ] ; then + DEFAULT_MILL_VERSION="0.12.10" +fi + + +if [ -z "${GITHUB_RELEASE_CDN}" ] ; then + GITHUB_RELEASE_CDN="" +fi + + +MILL_REPO_URL="https://github.com/com-lihaoyi/mill" + +if [ -z "${CURL_CMD}" ] ; then + CURL_CMD=curl +fi + +# Explicit commandline argument takes precedence over all other methods +if [ "$1" = "--mill-version" ] ; then + echo "The --mill-version option is no longer supported." 1>&2 +fi + +MILL_BUILD_SCRIPT="" + +if [ -f "build.mill" ] ; then + MILL_BUILD_SCRIPT="build.mill" +elif [ -f "build.mill.scala" ] ; then + MILL_BUILD_SCRIPT="build.mill.scala" +elif [ -f "build.sc" ] ; then + MILL_BUILD_SCRIPT="build.sc" +fi + +# Please note, that if a MILL_VERSION is already set in the environment, +# We reuse it's value and skip searching for a value. + +# If not already set, read .mill-version file +if [ -z "${MILL_VERSION}" ] ; then + if [ -f ".mill-version" ] ; then + MILL_VERSION="$(tr '\r' '\n' < .mill-version | head -n 1 2> /dev/null)" + elif [ -f ".config/mill-version" ] ; then + MILL_VERSION="$(tr '\r' '\n' < .config/mill-version | head -n 1 2> /dev/null)" + elif [ -n "${MILL_BUILD_SCRIPT}" ] ; then + MILL_VERSION="$(cat ${MILL_BUILD_SCRIPT} | grep '//[|] *mill-version: *' | sed 's;//| *mill-version: *;;')" + fi +fi + +MILL_USER_CACHE_DIR="${XDG_CACHE_HOME:-${HOME}/.cache}/mill" + +if [ -z "${MILL_DOWNLOAD_PATH}" ] ; then + MILL_DOWNLOAD_PATH="${MILL_USER_CACHE_DIR}/download" +fi + +# If not already set, try to fetch newest from Github +if [ -z "${MILL_VERSION}" ] ; then + # TODO: try to load latest version from release page + echo "No mill version specified." 1>&2 + echo "You should provide a version via a '//| mill-version: ' comment or a '.mill-version' file." 1>&2 + + mkdir -p "${MILL_DOWNLOAD_PATH}" + LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest" 2>/dev/null || ( + # we might be on OSX or BSD which don't have -d option for touch + # but probably a -A [-][[hh]mm]SS + touch "${MILL_DOWNLOAD_PATH}/.expire_latest"; touch -A -010000 "${MILL_DOWNLOAD_PATH}/.expire_latest" + ) || ( + # in case we still failed, we retry the first touch command with the intention + # to show the (previously suppressed) error message + LANG=C touch -d '1 hour ago' "${MILL_DOWNLOAD_PATH}/.expire_latest" + ) + + # POSIX shell variant of bash's -nt operator, see https://unix.stackexchange.com/a/449744/6993 + # if [ "${MILL_DOWNLOAD_PATH}/.latest" -nt "${MILL_DOWNLOAD_PATH}/.expire_latest" ] ; then + if [ -n "$(find -L "${MILL_DOWNLOAD_PATH}/.latest" -prune -newer "${MILL_DOWNLOAD_PATH}/.expire_latest")" ]; then + # we know a current latest version + MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null) + fi + + if [ -z "${MILL_VERSION}" ] ; then + # we don't know a current latest version + echo "Retrieving latest mill version ..." 1>&2 + LANG=C ${CURL_CMD} -s -i -f -I ${MILL_REPO_URL}/releases/latest 2> /dev/null | grep --ignore-case Location: | sed s'/^.*tag\///' | tr -d '\r\n' > "${MILL_DOWNLOAD_PATH}/.latest" + MILL_VERSION=$(head -n 1 "${MILL_DOWNLOAD_PATH}"/.latest 2> /dev/null) + fi + + if [ -z "${MILL_VERSION}" ] ; then + # Last resort + MILL_VERSION="${DEFAULT_MILL_VERSION}" + echo "Falling back to hardcoded mill version ${MILL_VERSION}" 1>&2 + else + echo "Using mill version ${MILL_VERSION}" 1>&2 + fi +fi + +MILL_NATIVE_SUFFIX="-native" +MILL_JVM_SUFFIX="-jvm" +FULL_MILL_VERSION=$MILL_VERSION +ARTIFACT_SUFFIX="" +set_artifact_suffix(){ + if [ "$(expr substr $(uname -s) 1 5 2>/dev/null)" = "Linux" ]; then + if [ "$(uname -m)" = "aarch64" ]; then + ARTIFACT_SUFFIX="-native-linux-aarch64" + else + ARTIFACT_SUFFIX="-native-linux-amd64" + fi + elif [ "$(uname)" = "Darwin" ]; then + if [ "$(uname -m)" = "arm64" ]; then + ARTIFACT_SUFFIX="-native-mac-aarch64" + else + ARTIFACT_SUFFIX="-native-mac-amd64" + fi + else + echo "This native mill launcher supports only Linux and macOS." 1>&2 + exit 1 + fi +} + +case "$MILL_VERSION" in + *"$MILL_NATIVE_SUFFIX") + MILL_VERSION=${MILL_VERSION%"$MILL_NATIVE_SUFFIX"} + set_artifact_suffix + ;; + + *"$MILL_JVM_SUFFIX") + MILL_VERSION=${MILL_VERSION%"$MILL_JVM_SUFFIX"} + ;; + + *) + case "$MILL_VERSION" in + 0.1.*) ;; + 0.2.*) ;; + 0.3.*) ;; + 0.4.*) ;; + 0.5.*) ;; + 0.6.*) ;; + 0.7.*) ;; + 0.8.*) ;; + 0.9.*) ;; + 0.10.*) ;; + 0.11.*) ;; + 0.12.*) ;; + *) + set_artifact_suffix + esac + ;; +esac + +MILL="${MILL_DOWNLOAD_PATH}/$MILL_VERSION$ARTIFACT_SUFFIX" + +try_to_use_system_mill() { + if [ "$(uname)" != "Linux" ]; then + return 0 + fi + + MILL_IN_PATH="$(command -v mill || true)" + + if [ -z "${MILL_IN_PATH}" ]; then + return 0 + fi + + SYSTEM_MILL_FIRST_TWO_BYTES=$(head --bytes=2 "${MILL_IN_PATH}") + if [ "${SYSTEM_MILL_FIRST_TWO_BYTES}" = "#!" ]; then + # MILL_IN_PATH is (very likely) a shell script and not the mill + # executable, ignore it. + return 0 + fi + + SYSTEM_MILL_PATH=$(readlink -e "${MILL_IN_PATH}") + SYSTEM_MILL_SIZE=$(stat --format=%s "${SYSTEM_MILL_PATH}") + SYSTEM_MILL_MTIME=$(stat --format=%y "${SYSTEM_MILL_PATH}") + + if [ ! -d "${MILL_USER_CACHE_DIR}" ]; then + mkdir -p "${MILL_USER_CACHE_DIR}" + fi + + SYSTEM_MILL_INFO_FILE="${MILL_USER_CACHE_DIR}/system-mill-info" + if [ -f "${SYSTEM_MILL_INFO_FILE}" ]; then + parseSystemMillInfo() { + LINE_NUMBER="${1}" + # Select the line number of the SYSTEM_MILL_INFO_FILE, cut the + # variable definition in that line in two halves and return + # the value, and finally remove the quotes. + sed -n "${LINE_NUMBER}p" "${SYSTEM_MILL_INFO_FILE}" |\ + cut -d= -f2 |\ + sed 's/"\(.*\)"/\1/' + } + + CACHED_SYSTEM_MILL_PATH=$(parseSystemMillInfo 1) + CACHED_SYSTEM_MILL_VERSION=$(parseSystemMillInfo 2) + CACHED_SYSTEM_MILL_SIZE=$(parseSystemMillInfo 3) + CACHED_SYSTEM_MILL_MTIME=$(parseSystemMillInfo 4) + + if [ "${SYSTEM_MILL_PATH}" = "${CACHED_SYSTEM_MILL_PATH}" ] \ + && [ "${SYSTEM_MILL_SIZE}" = "${CACHED_SYSTEM_MILL_SIZE}" ] \ + && [ "${SYSTEM_MILL_MTIME}" = "${CACHED_SYSTEM_MILL_MTIME}" ]; then + if [ "${CACHED_SYSTEM_MILL_VERSION}" = "${MILL_VERSION}" ]; then + MILL="${SYSTEM_MILL_PATH}" + return 0 + else + return 0 + fi + fi + fi + + SYSTEM_MILL_VERSION=$(${SYSTEM_MILL_PATH} --version | head -n1 | sed -n 's/^Mill.*version \(.*\)/\1/p') + + cat < "${SYSTEM_MILL_INFO_FILE}" +CACHED_SYSTEM_MILL_PATH="${SYSTEM_MILL_PATH}" +CACHED_SYSTEM_MILL_VERSION="${SYSTEM_MILL_VERSION}" +CACHED_SYSTEM_MILL_SIZE="${SYSTEM_MILL_SIZE}" +CACHED_SYSTEM_MILL_MTIME="${SYSTEM_MILL_MTIME}" +EOF + + if [ "${SYSTEM_MILL_VERSION}" = "${MILL_VERSION}" ]; then + MILL="${SYSTEM_MILL_PATH}" + fi +} +try_to_use_system_mill + +# If not already downloaded, download it +if [ ! -s "${MILL}" ] || [ "$MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT" = "1" ] ; then + case $MILL_VERSION in + 0.0.* | 0.1.* | 0.2.* | 0.3.* | 0.4.* ) + DOWNLOAD_SUFFIX="" + DOWNLOAD_FROM_MAVEN=0 + ;; + 0.5.* | 0.6.* | 0.7.* | 0.8.* | 0.9.* | 0.10.* | 0.11.0-M* ) + DOWNLOAD_SUFFIX="-assembly" + DOWNLOAD_FROM_MAVEN=0 + ;; + *) + DOWNLOAD_SUFFIX="-assembly" + DOWNLOAD_FROM_MAVEN=1 + ;; + esac + case $MILL_VERSION in + 0.12.0 | 0.12.1 | 0.12.2 | 0.12.3 | 0.12.4 | 0.12.5 | 0.12.6 | 0.12.7 | 0.12.8 | 0.12.9 | 0.12.10 | 0.12.11 ) + DOWNLOAD_EXT="jar" + ;; + 0.12.* ) + DOWNLOAD_EXT="exe" + ;; + 0.* ) + DOWNLOAD_EXT="jar" + ;; + *) + DOWNLOAD_EXT="exe" + ;; + esac + + DOWNLOAD_FILE=$(mktemp mill.XXXXXX) + if [ "$DOWNLOAD_FROM_MAVEN" = "1" ] ; then + DOWNLOAD_URL="https://repo1.maven.org/maven2/com/lihaoyi/mill-dist$%7BARTIFACT_SUFFIX%7D/$%7BMILL_VERSION%7D/mill-dist$%7BARTIFACT_SUFFIX%7D-$%7BMILL_VERSION%7D.$%7BDOWNLOAD_EXT%7D" + else + MILL_VERSION_TAG=$(echo "$MILL_VERSION" | sed -E 's/([^-]+)(-M[0-9]+)?(-.*)?/\1\2/') + DOWNLOAD_URL="${GITHUB_RELEASE_CDN}${MILL_REPO_URL}/releases/download/${MILL_VERSION_TAG}/${MILL_VERSION}${DOWNLOAD_SUFFIX}" + unset MILL_VERSION_TAG + fi + + if [ "$MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT" = "1" ] ; then + echo $DOWNLOAD_URL + echo $MILL + exit 0 + fi + # TODO: handle command not found + echo "Downloading mill ${MILL_VERSION} from ${DOWNLOAD_URL} ..." 1>&2 + ${CURL_CMD} -f -L -o "${DOWNLOAD_FILE}" "${DOWNLOAD_URL}" + chmod +x "${DOWNLOAD_FILE}" + mkdir -p "${MILL_DOWNLOAD_PATH}" + mv "${DOWNLOAD_FILE}" "${MILL}" + + unset DOWNLOAD_FILE + unset DOWNLOAD_SUFFIX +fi + +if [ -z "$MILL_MAIN_CLI" ] ; then + MILL_MAIN_CLI="${0}" +fi + +MILL_FIRST_ARG="" +if [ "$1" = "--bsp" ] || [ "${1#"-i"}" != "$1" ] || [ "$1" = "--interactive" ] || [ "$1" = "--no-server" ] || [ "$1" = "--no-daemon" ] || [ "$1" = "--repl" ] || [ "$1" = "--help" ] ; then + # Need to preserve the first position of those listed options + MILL_FIRST_ARG=$1 + shift +fi + +unset MILL_DOWNLOAD_PATH +unset MILL_OLD_DOWNLOAD_PATH +unset OLD_MILL +unset MILL_VERSION +unset MILL_REPO_URL + +# -D mill.main.cli is for compatibility with Mill 0.10.9 - 0.13.0-M2 +# We don't quote MILL_FIRST_ARG on purpose, so we can expand the empty value without quotes +# shellcheck disable=SC2086 +exec "${MILL}" $MILL_FIRST_ARG -D "mill.main.cli=${MILL_MAIN_CLI}" "$@" diff --git a/mill.bat b/mill.bat new file mode 100644 index 000000000000..f36d81229704 --- /dev/null +++ b/mill.bat @@ -0,0 +1,299 @@ +@echo off + +rem This is a wrapper script, that automatically selects or downloads Mill from Maven Central or GitHub release pages. +rem +rem This script determines the Mill version to use by trying these sources +rem - env-variable `MILL_VERSION` +rem - local file `.mill-version` +rem - local file `.config/mill-version` +rem - `mill-version` from YAML fronmatter of current buildfile +rem - if accessible, find the latest stable version available on Maven Central (https://repo1.maven.org/maven2) +rem - env-variable `DEFAULT_MILL_VERSION` +rem +rem If a version has the suffix '-native' a native binary will be used. +rem If a version has the suffix '-jvm' an executable jar file will be used, requiring an already installed Java runtime. +rem If no such suffix is found, the script will pick a default based on version and platform. +rem +rem Once a version was determined, it tries to use either +rem - a system-installed mill, if found and it's version matches +rem - an already downloaded version under %USERPROFILE%\.mill\download +rem +rem If no working mill version was found on the system, +rem this script downloads a binary file from Maven Central or Github Pages (this is version dependent) +rem into a cache location (%USERPROFILE%\.mill\download). +rem +rem Mill Project URL: https://github.com/com-lihaoyi/mill +rem Script Version: 1.0.0-M1-21-7b6fae-DIRTY892b63e8 +rem +rem If you want to improve this script, please also contribute your changes back! +rem This script was generated from: dist/scripts/src/mill.bat +rem +rem Licensed under the Apache License, Version 2.0 + +rem setlocal seems to be unavailable on Windows 95/98/ME +rem but I don't think we need to support them in 2019 +setlocal enabledelayedexpansion + +if [!DEFAULT_MILL_VERSION!]==[] ( set "DEFAULT_MILL_VERSION=0.12.10" ) + +if [!MILL_GITHUB_RELEASE_CDN!]==[] ( set "MILL_GITHUB_RELEASE_CDN=" ) + +if [!MILL_MAIN_CLI!]==[] ( set "MILL_MAIN_CLI=%~f0" ) + +set "MILL_REPO_URL=https://github.com/com-lihaoyi/mill" + +SET MILL_BUILD_SCRIPT= + +if exist "build.mill" ( + set MILL_BUILD_SCRIPT=build.mill +) else ( + if exist "build.mill.scala" ( + set MILL_BUILD_SCRIPT=build.mill.scala + ) else ( + if exist "build.sc" ( + set MILL_BUILD_SCRIPT=build.sc + ) else ( + rem no-op + ) + ) +) + +if [!MILL_VERSION!]==[] ( + if exist .mill-version ( + set /p MILL_VERSION=<.mill-version + ) else ( + if exist .config\mill-version ( + set /p MILL_VERSION=<.config\mill-version + ) else ( + if not "%MILL_BUILD_SCRIPT%"=="" ( + for /f "tokens=1-2*" %%a in ('findstr /C:"//| mill-version:" %MILL_BUILD_SCRIPT%') do ( + set "MILL_VERSION=%%c" + ) + ) else ( + rem no-op + ) + ) + ) +) + +if [!MILL_VERSION!]==[] set MILL_VERSION=%DEFAULT_MILL_VERSION% + +if [!MILL_DOWNLOAD_PATH!]==[] set MILL_DOWNLOAD_PATH=%USERPROFILE%\.mill\download + +rem without bat file extension, cmd doesn't seem to be able to run it + +set "MILL_NATIVE_SUFFIX=-native" +set "MILL_JVM_SUFFIX=-jvm" +set "FULL_MILL_VERSION=%MILL_VERSION%" +set "MILL_EXT=.bat" +set "ARTIFACT_SUFFIX=" +REM Check if MILL_VERSION contains MILL_NATIVE_SUFFIX +echo !MILL_VERSION! | findstr /C:"%MILL_NATIVE_SUFFIX%" >nul +if !errorlevel! equ 0 ( + set "MILL_VERSION=%MILL_VERSION:-native=%" + REM -native images compiled with graal do not support windows-arm + REM https://github.com/oracle/graal/issues/9215 + IF /I NOT "%PROCESSOR_ARCHITECTURE%"=="ARM64" ( + set "ARTIFACT_SUFFIX=-native-windows-amd64" + set "MILL_EXT=.exe" + ) else ( + rem no-op + ) +) else ( + echo !MILL_VERSION! | findstr /C:"%MILL_JVM_SUFFIX%" >nul + if !errorlevel! equ 0 ( + set "MILL_VERSION=%MILL_VERSION:-jvm=%" + ) else ( + set "SKIP_VERSION=false" + set "MILL_PREFIX=%MILL_VERSION:~0,4%" + if "!MILL_PREFIX!"=="0.1." set "SKIP_VERSION=true" + if "!MILL_PREFIX!"=="0.2." set "SKIP_VERSION=true" + if "!MILL_PREFIX!"=="0.3." set "SKIP_VERSION=true" + if "!MILL_PREFIX!"=="0.4." set "SKIP_VERSION=true" + if "!MILL_PREFIX!"=="0.5." set "SKIP_VERSION=true" + if "!MILL_PREFIX!"=="0.6." set "SKIP_VERSION=true" + if "!MILL_PREFIX!"=="0.7." set "SKIP_VERSION=true" + if "!MILL_PREFIX!"=="0.8." set "SKIP_VERSION=true" + if "!MILL_PREFIX!"=="0.9." set "SKIP_VERSION=true" + set "MILL_PREFIX=%MILL_VERSION:~0,5%" + if "!MILL_PREFIX!"=="0.10." set "SKIP_VERSION=true" + if "!MILL_PREFIX!"=="0.11." set "SKIP_VERSION=true" + if "!MILL_PREFIX!"=="0.12." set "SKIP_VERSION=true" + + if "!SKIP_VERSION!"=="false" ( + IF /I NOT "%PROCESSOR_ARCHITECTURE%"=="ARM64" ( + set "ARTIFACT_SUFFIX=-native-windows-amd64" + set "MILL_EXT=.exe" + ) + ) else ( + rem no-op + ) + ) +) + +set MILL=%MILL_DOWNLOAD_PATH%\!FULL_MILL_VERSION!!MILL_EXT! + +set MILL_RESOLVE_DOWNLOAD= + +if not exist "%MILL%" ( + set MILL_RESOLVE_DOWNLOAD=true +) else ( + if defined MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT ( + set MILL_RESOLVE_DOWNLOAD=true + ) else ( + rem no-op + ) +) + + +if [!MILL_RESOLVE_DOWNLOAD!]==[true] ( + set MILL_VERSION_PREFIX=%MILL_VERSION:~0,4% + set MILL_SHORT_VERSION_PREFIX=%MILL_VERSION:~0,2% + rem Since 0.5.0 + set MILL_DOWNLOAD_SUFFIX=-assembly + rem Since 0.11.0 + set MILL_DOWNLOAD_FROM_MAVEN=1 + if [!MILL_VERSION_PREFIX!]==[0.0.] ( + set MILL_DOWNLOAD_SUFFIX= + set MILL_DOWNLOAD_FROM_MAVEN=0 + ) + if [!MILL_VERSION_PREFIX!]==[0.1.] ( + set MILL_DOWNLOAD_SUFFIX= + set MILL_DOWNLOAD_FROM_MAVEN=0 + ) + if [!MILL_VERSION_PREFIX!]==[0.2.] ( + set MILL_DOWNLOAD_SUFFIX= + set MILL_DOWNLOAD_FROM_MAVEN=0 + ) + if [!MILL_VERSION_PREFIX!]==[0.3.] ( + set MILL_DOWNLOAD_SUFFIX= + set MILL_DOWNLOAD_FROM_MAVEN=0 + ) + if [!MILL_VERSION_PREFIX!]==[0.4.] ( + set MILL_DOWNLOAD_SUFFIX= + set MILL_DOWNLOAD_FROM_MAVEN=0 + ) + if [!MILL_VERSION_PREFIX!]==[0.5.] set MILL_DOWNLOAD_FROM_MAVEN=0 + if [!MILL_VERSION_PREFIX!]==[0.6.] set MILL_DOWNLOAD_FROM_MAVEN=0 + if [!MILL_VERSION_PREFIX!]==[0.7.] set MILL_DOWNLOAD_FROM_MAVEN=0 + if [!MILL_VERSION_PREFIX!]==[0.8.] set MILL_DOWNLOAD_FROM_MAVEN=0 + if [!MILL_VERSION_PREFIX!]==[0.9.] set MILL_DOWNLOAD_FROM_MAVEN=0 + + set MILL_VERSION_PREFIX=%MILL_VERSION:~0,5% + if [!MILL_VERSION_PREFIX!]==[0.10.] set MILL_DOWNLOAD_FROM_MAVEN=0 + + set MILL_VERSION_PREFIX=%MILL_VERSION:~0,8% + if [!MILL_VERSION_PREFIX!]==[0.11.0-M] set MILL_DOWNLOAD_FROM_MAVEN=0 + + set MILL_VERSION_PREFIX=%MILL_VERSION:~0,5% + set DOWNLOAD_EXT=exe + if [!MILL_SHORT_VERSION_PREFIX!]==[0.] set DOWNLOAD_EXT=jar + if [!MILL_VERSION_PREFIX!]==[0.12.] set DOWNLOAD_EXT=exe + if [!MILL_VERSION!]==[0.12.0] set DOWNLOAD_EXT=jar + if [!MILL_VERSION!]==[0.12.1] set DOWNLOAD_EXT=jar + if [!MILL_VERSION!]==[0.12.2] set DOWNLOAD_EXT=jar + if [!MILL_VERSION!]==[0.12.3] set DOWNLOAD_EXT=jar + if [!MILL_VERSION!]==[0.12.4] set DOWNLOAD_EXT=jar + if [!MILL_VERSION!]==[0.12.5] set DOWNLOAD_EXT=jar + if [!MILL_VERSION!]==[0.12.6] set DOWNLOAD_EXT=jar + if [!MILL_VERSION!]==[0.12.7] set DOWNLOAD_EXT=jar + if [!MILL_VERSION!]==[0.12.8] set DOWNLOAD_EXT=jar + if [!MILL_VERSION!]==[0.12.9] set DOWNLOAD_EXT=jar + if [!MILL_VERSION!]==[0.12.10] set DOWNLOAD_EXT=jar + if [!MILL_VERSION!]==[0.12.11] set DOWNLOAD_EXT=jar + + set MILL_VERSION_PREFIX= + set MILL_SHORT_VERSION_PREFIX= + + for /F "delims=- tokens=1" %%A in ("!MILL_VERSION!") do set MILL_VERSION_BASE=%%A + set MILL_VERSION_MILESTONE= + for /F "delims=- tokens=2" %%A in ("!MILL_VERSION!") do set MILL_VERSION_MILESTONE=%%A + set MILL_VERSION_MILESTONE_START=!MILL_VERSION_MILESTONE:~0,1! + if [!MILL_VERSION_MILESTONE_START!]==[M] ( + set MILL_VERSION_TAG=!MILL_VERSION_BASE!-!MILL_VERSION_MILESTONE! + ) else ( + set MILL_VERSION_TAG=!MILL_VERSION_BASE! + ) + if [!MILL_DOWNLOAD_FROM_MAVEN!]==[1] ( + set MILL_DOWNLOAD_URL=https://repo1.maven.org/maven2/com/lihaoyi/mill-dist!ARTIFACT_SUFFIX!/!MILL_VERSION!/mill-dist!ARTIFACT_SUFFIX!-!MILL_VERSION!.!DOWNLOAD_EXT! + ) else ( + set MILL_DOWNLOAD_URL=!MILL_GITHUB_RELEASE_CDN!%MILL_REPO_URL%/releases/download/!MILL_VERSION_TAG!/!MILL_VERSION!!MILL_DOWNLOAD_SUFFIX! + ) + + if defined MILL_TEST_DRY_RUN_LAUNCHER_SCRIPT ( + echo !MILL_DOWNLOAD_URL! + echo !MILL! + exit /b 0 + ) + + rem there seems to be no way to generate a unique temporary file path (on native Windows) + set MILL_DOWNLOAD_FILE=%MILL%.tmp + + echo Downloading mill !MILL_VERSION! from !MILL_DOWNLOAD_URL! ... 1>&2 + + if not exist "%MILL_DOWNLOAD_PATH%" mkdir "%MILL_DOWNLOAD_PATH%" + rem curl is bundled with recent Windows 10 + rem but I don't think we can expect all the users to have it in 2019 + where /Q curl + if !ERRORLEVEL! EQU 0 ( + curl -f -L "!MILL_DOWNLOAD_URL!" -o "!MILL_DOWNLOAD_FILE!" + ) else ( + rem bitsadmin seems to be available on Windows 7 + rem without /dynamic, github returns 403 + rem bitsadmin is sometimes needlessly slow but it looks better with /priority foreground + bitsadmin /transfer millDownloadJob /dynamic /priority foreground "!MILL_DOWNLOAD_URL!" "!MILL_DOWNLOAD_FILE!" + ) + if not exist "!MILL_DOWNLOAD_FILE!" ( + echo Could not download mill !MILL_VERSION! 1>&2 + exit /b 1 + ) + + move /y "!MILL_DOWNLOAD_FILE!" "%MILL%" + + set MILL_DOWNLOAD_FILE= + set MILL_DOWNLOAD_SUFFIX= +) + +set MILL_DOWNLOAD_PATH= +set MILL_VERSION= +set MILL_REPO_URL= + +rem Need to preserve the first position of those listed options +set MILL_FIRST_ARG= +if [%~1%]==[--bsp] ( + set MILL_FIRST_ARG=%1% +) else ( + if [%~1%]==[-i] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--interactive] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--no-server] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--no-daemon] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--repl] ( + set MILL_FIRST_ARG=%1% + ) else ( + if [%~1%]==[--help] ( + set MILL_FIRST_ARG=%1% + ) + ) + ) + ) + ) + ) +) +set "MILL_PARAMS=%*%" + +if not [!MILL_FIRST_ARG!]==[] ( + for /f "tokens=1*" %%a in ("%*") do ( + set "MILL_PARAMS=%%b" + ) +) + +rem -D mill.main.cli is for compatibility with Mill 0.10.9 - 0.13.0-M2 +"%MILL%" %MILL_FIRST_ARG% -D "mill.main.cli=%MILL_MAIN_CLI%" %MILL_PARAMS% diff --git a/presentation-compiler/src/main/dotty/tools/dotc/classpath/metals/Entries.scala b/presentation-compiler/src/main/dotty/tools/dotc/classpath/metals/Entries.scala new file mode 100644 index 000000000000..e5346a4319b7 --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/dotc/classpath/metals/Entries.scala @@ -0,0 +1,12 @@ +package dotty.tools.dotc.classpath +package metals + +import dotty.tools.io.AbstractFile + +object Entries: + def packageEntry(name: String): PackageEntry = + PackageEntryImpl(name) + def classFileEntry(file: AbstractFile): ClassFileEntry = + ClassFileEntry(file) + def sourceFileEntry(file: AbstractFile): SourceFileEntry = + SourceFileEntry(file) \ No newline at end of file diff --git a/presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala index 97ec396abcf1..93578057b1bd 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala @@ -4,9 +4,10 @@ import java.nio.file.Paths import scala.collection.mutable import scala.jdk.CollectionConverters.* -import scala.meta.pc.reports.ReportContext +import scala.meta.internal.mtags.SourcePath import scala.meta.internal.pc.AutoImportsResultImpl import scala.meta.pc.* +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Symbols.* @@ -75,7 +76,11 @@ final class AutoImportsProvider( val visitor = new CompilerSearchVisitor(visit) if isExtension then search.searchMethods(name, buildTargetIdentifier, visitor) - else search.search(name, buildTargetIdentifier, visitor) + else { + SourcePath.withContext { ctx => + search.search(name, buildTargetIdentifier, visitor, ctx.iface) + } + } val results = symbols.result.filter(isExactMatch(_, name)) if results.nonEmpty then diff --git a/presentation-compiler/src/main/dotty/tools/pc/CachingDriver.scala b/presentation-compiler/src/main/dotty/tools/pc/CachingDriver.scala index f5715c2780a9..54a3800d40a5 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/CachingDriver.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/CachingDriver.scala @@ -1,11 +1,30 @@ package dotty.tools.pc +import scala.meta.internal.pc.* + import java.net.URI import java.util as ju import dotty.tools.dotc.interactive.InteractiveDriver import dotty.tools.dotc.reporting.Diagnostic import dotty.tools.dotc.util.SourceFile +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Contexts.ContextBase +import dotty.tools.dotc.config.Platform +import dotty.tools.io.ClassPath +import dotty.tools.dotc.classpath.AggregateClassPath +import java.io.File +import java.nio.file.Paths +import java.nio.file.Path +import java.net.URL +import dotty.tools.io.AbstractFile +import dotty.tools.dotc.classpath.PackageName +import dotty.tools.dotc.classpath.ClassPathEntries +import dotty.tools.dotc.classpath.ClassFileEntry +import dotty.tools.dotc.classpath.SourceFileEntry +import dotty.tools.dotc.classpath.PackageEntry +import dotty.tools.io.FileZipArchive +import java.util.concurrent.ConcurrentHashMap import scala.compiletime.uninitialized @@ -27,10 +46,174 @@ import scala.compiletime.uninitialized * To avoid the complexity related to currentCtx, * we decided to cache only when the target URI only if the same as the previous run. */ -class CachingDriver(override val settings: List[String]) extends InteractiveDriver(settings): +class CachingDriver( + override val settings: List[String], + javaHome: Path +) extends InteractiveDriver(settings): @volatile private var lastCompiledURI: URI = uninitialized + private var printedCp = false + + override protected def initCtx: Context = { + val baseCtx: ContextBase = new ContextBase { baseCtx0 => + override protected def newPlatform(using Context): Platform = { + if (baseCtx0.settings.scalajs.value) super.newPlatform + else + new dotty.tools.dotc.config.JavaPlatform { + override def classPath(using Context): ClassPath = { + + val modFiles = baseCtx0.settings.classpath.value + .split(File.pathSeparator) + .filter(_.endsWith(".jmod")) + .map(Paths.get(_)) + + lazy val modCp: ClassPath = new ClassPath { + import java.nio.file._ + import CachingDriver.fza + + lazy val srcZip: Path = { + val candidates = List(javaHome.resolve("src.zip"), javaHome.resolve("lib/src.zip")) + candidates + .iterator + .filter(Files.isRegularFile(_)) + .take(1) + .find(_ => true) + .getOrElse { + sys.error(s"No src.zip found in $candidates") + } + } + + def cpIterator(): Iterator[Path] = + modFiles.iterator + + def asClassPathStrings: Seq[String] = + cpIterator() + .map(_.toString) + .toVector + def asSourcePathString: String = + srcZip.toString + def asURLs: Seq[URL] = + cpIterator() + .map(_.toUri.toURL) + .toVector + + def findClassFile(className: String): Option[AbstractFile] = { + val entryName = "classes/" + className.replace(".", "/") + ".class" + val idx = entryName.lastIndexOf('/') + val dirName = entryName.substring(0, idx + 1) + val fileName = entryName.substring(idx + 1) + cpIterator() + .flatMap { f => + fza(f) + .allDirs + .get(dirName) + .iterator + .flatMap(_.entries.get(fileName).iterator) + } + .take(1) + .toList + .headOption + } + + def hasPackage(pkg: PackageName): Boolean = { + val dirName = ("classes" +: pkg.dottedString.split('.').filter(_.nonEmpty)).mkString("", "/", "/") + cpIterator().exists(f => Option(fza(f).allDirs.get(dirName)).nonEmpty) + } + + def list(inPackage: PackageName): ClassPathEntries = + ClassPathEntries(packages(inPackage), classes(inPackage) ++ sources(inPackage)) + + def packages(inPackage: PackageName): Seq[PackageEntry] = { + val dirName = ("classes" +: inPackage.dottedString.split('.').filter(_.nonEmpty)).mkString("", "/", "/") + val prefix = if (inPackage.dottedString.isEmpty) "" else inPackage.dottedString + "." + cpIterator() + .flatMap { f => + fza(f) + .allDirs + .get(dirName) + .iterator + .flatMap { dirEnt => + dirEnt + .entries + .valuesIterator + .filter(_.isDirectory) + .map(e => dotty.tools.dotc.classpath.metals.Entries.packageEntry(prefix + e.name)) + } + } + .toVector + .distinct + } + def classes(inPackage: PackageName): Seq[ClassFileEntry] = { + val dirName = ("classes" +: inPackage.dottedString.split('.').filter(_.nonEmpty)).mkString("", "/", "/") + cpIterator() + .flatMap { f => + fza(f) + .allDirs + .get(dirName) + .iterator + .flatMap { dirEnt => + dirEnt + .entries + .valuesIterator + .filter(!_.isDirectory) + .filter(_.name.endsWith(".class")) + .map(e => dotty.tools.dotc.classpath.metals.Entries.classFileEntry(e)) + } + } + .toVector + .distinct + } + def sources(inPackage: PackageName): Seq[SourceFileEntry] = { + val dirName = ("classes" +: inPackage.dottedString.split('.').filter(_.nonEmpty)).mkString("", "/", "/") + fza(srcZip) + .allDirs + .get(dirName) + .iterator + .flatMap { dirEnt => + dirEnt + .entries + .valuesIterator + .filter(e => e.name.endsWith(".scala") || e.name.endsWith(".java")) + .map(e => dotty.tools.dotc.classpath.metals.Entries.sourceFileEntry(e)) + } + .toVector + .distinct + } + } + + def process(cp: ClassPath): Option[ClassPath] = + cp match { + case agg: AggregateClassPath => + val res = agg.aggregates.flatMap(cp0 => process(cp0).toSeq) + if (res.isEmpty) None + else Some(AggregateClassPath(res)) + case _: dotty.tools.dotc.classpath.JrtClassPath => + None + case other => + Some(other) + } + + val cp = super.classPath + val processedCp = process(cp).getOrElse(AggregateClassPath(Nil)) + if (!printedCp) { + System.err.println("cp = " + pprint.apply(cp)) + System.err.println("processedCp = " + pprint.apply(processedCp)) + printedCp = true + } + processedCp match { + case agg: AggregateClassPath => + AggregateClassPath(modCp +: agg.aggregates) + case other => + AggregateClassPath(Seq(modCp, other)) + } + } + } + } + } + baseCtx.initialCtx + } + private def alreadyCompiled(uri: URI, content: Array[Char]): Boolean = compilationUnits.get(uri) match case Some(unit) @@ -54,3 +237,17 @@ class CachingDriver(override val settings: List[String]) extends InteractiveDriv diags end CachingDriver + +object CachingDriver: + private val fzaCache = new ConcurrentHashMap[Path, FileZipArchive] + private def fza(path: Path): FileZipArchive = { + val valueOrNull = fzaCache.get(path) + if (valueOrNull == null) { + val fza0 = new FileZipArchive(path, None) + val previousOrNull = fzaCache.putIfAbsent(path, fza0) + if (previousOrNull == null) fza0 + else previousOrNull + } + else + valueOrNull + } diff --git a/presentation-compiler/src/main/dotty/tools/pc/CompileProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/CompileProvider.scala new file mode 100644 index 000000000000..f0a9a266bfe3 --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/pc/CompileProvider.scala @@ -0,0 +1,47 @@ +package dotty.tools.pc + +import java.util.{List as JList} + +import scala.jdk.CollectionConverters.* +import scala.meta.internal.mtags.GlobalSymbolIndex +import scala.meta.pc.CompileResult +import scala.meta.pc.VirtualFileParams +import scala.meta.pc.reports.ReportContext + +import dotty.tools.dotc.interactive.InteractiveDriver +import dotty.tools.dotc.util.SourceFile +import dotty.tools.dotc.core.Contexts.Context + +object CompileProvider: + + def compile( + params: VirtualFileParams, + driver: InteractiveDriver + )(implicit reportContext: ReportContext): CompileProvider.Result = + val uri = params.uri().nn + val sourceFile = SourceFile.virtual(uri, params.text().nn) + driver.run(uri, sourceFile) + val unit = driver.compilationUnits.get(uri) + + given ctx: Context = + val ctx = driver.currentCtx + unit.map(ctx.fresh.setCompilationUnit).getOrElse(ctx) + + CompileProvider.Result( + Nil, + unit + .map(u => List(u.tpdTree)) + .getOrElse(driver.openedTrees(uri).map(_.tree)) + .map(_.showIndented(2)) + .mkString("\n- ") + ) + end compile + + final case class Result( + scalaDiagnostics: List[String], + fullTree: String + ) extends CompileResult { + def diagnostics(): JList[String] = scalaDiagnostics.asJava + } + +end CompileProvider diff --git a/presentation-compiler/src/main/dotty/tools/pc/CompilerSearchVisitor.scala b/presentation-compiler/src/main/dotty/tools/pc/CompilerSearchVisitor.scala index aed8bf418490..24c0dc696556 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/CompilerSearchVisitor.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/CompilerSearchVisitor.scala @@ -4,8 +4,8 @@ import java.util.logging.Level import java.util.logging.Logger import scala.meta.internal.metals.Report -import scala.meta.pc.reports.ReportContext import scala.meta.pc.* +import scala.meta.pc.reports.ReportContext import scala.util.control.NonFatal import dotty.tools.dotc.core.Contexts.* @@ -68,7 +68,7 @@ class CompilerSearchVisitor( loop(List(pkgSym), parts) end toSymbols - def visitClassfile(pkgPath: String, filename: String): Int = + def visitClassfile(pkgPath: String, filename: String, ctx: SourcePathContext): Int = val pkg = normalizePackage(pkgPath) val innerPath = filename diff --git a/presentation-compiler/src/main/dotty/tools/pc/CompletionItemResolver.scala b/presentation-compiler/src/main/dotty/tools/pc/CompletionItemResolver.scala index f7fdb1c36e6d..95a51fba6256 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/CompletionItemResolver.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/CompletionItemResolver.scala @@ -1,5 +1,6 @@ package dotty.tools.pc +import scala.meta.internal.mtags.GlobalSymbolIndex import scala.meta.internal.pc.ItemResolver import scala.meta.pc.PresentationCompilerConfig import scala.meta.pc.SymbolDocumentation @@ -18,6 +19,7 @@ object CompletionItemResolver extends ItemResolver: override def adjustIndexOfJavaParams = 0 def resolve( + module: GlobalSymbolIndex.Module, item: CompletionItem, msym: String, search: SymbolSearch, @@ -26,16 +28,16 @@ object CompletionItemResolver extends ItemResolver: SemanticdbSymbols.inverseSemanticdbSymbol(msym) match case gsym :: _ if gsym != NoSymbol => search - .symbolDocumentation(gsym) + .symbolDocumentation(module, gsym) .orElse( - search.symbolDocumentation(gsym.companion) + search.symbolDocumentation(module, gsym.companion) ) match case Some(info) if item.getDetail() != null => enrichDocs( item, info, metalsConfig, - fullDocstring(gsym, search), + fullDocstring(module, gsym, search), gsym.is(JavaDefined) ) case _ => @@ -46,11 +48,11 @@ object CompletionItemResolver extends ItemResolver: end match end resolve - private def fullDocstring(gsym: Symbol, search: SymbolSearch)(using + private def fullDocstring(module: GlobalSymbolIndex.Module, gsym: Symbol, search: SymbolSearch)(using Context ): String = def docs(gsym: Symbol): String = - search.symbolDocumentation(gsym).fold("")(_.docstring().nn) + search.symbolDocumentation(module, gsym).fold("")(_.docstring().nn) val gsymDoc = docs(gsym) def keyword(gsym: Symbol): String = if gsym.isClass then "class" @@ -62,15 +64,15 @@ object CompletionItemResolver extends ItemResolver: if companion == NoSymbol || gsym.is(JavaDefined) then if gsymDoc.isEmpty() then if gsym.isAliasType then - fullDocstring(gsym.info.deepDealiasAndSimplify.typeSymbol, search) + fullDocstring(module, gsym.info.deepDealiasAndSimplify.typeSymbol, search) else if gsym.is(Method) then gsym.info.finalResultType match case tr @ TermRef(_, sym) => - fullDocstring(tr.symbol, search) + fullDocstring(module, tr.symbol, search) case _ => "" else if gsym.isTerm && gsym.info.typeSymbol.is(Module) then - fullDocstring(gsym.info.typeSymbol.companion, search) + fullDocstring(module, gsym.info.typeSymbol.companion, search) else "" else gsymDoc else diff --git a/presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala index bd44878aa11a..76abb719c818 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala @@ -2,11 +2,11 @@ package dotty.tools.pc import java.nio.file.Paths -import scala.meta.pc.reports.ReportContext import scala.meta.internal.pc.ExtractMethodUtils import scala.meta.pc.OffsetParams import scala.meta.pc.RangeParams import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import scala.meta as m import dotty.tools.dotc.ast.Trees.* diff --git a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala index c55a8a0210be..f12a196b8f81 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala @@ -3,16 +3,18 @@ package dotty.tools.pc import java.util as ju import scala.meta.internal.metals.Report -import scala.meta.pc.reports.ReportContext +import scala.meta.internal.mtags.GlobalSymbolIndex import scala.meta.internal.pc.ScalaHover import scala.meta.pc.ContentType import scala.meta.pc.HoverSignature import scala.meta.pc.OffsetParams import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Constants.* import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Decorators.* import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Names.* import dotty.tools.dotc.core.StdNames.* @@ -31,10 +33,12 @@ import dotty.tools.dotc.core.StdNames object HoverProvider: def hover( + module: GlobalSymbolIndex.Module, params: OffsetParams, driver: InteractiveDriver, search: SymbolSearch, - contentType: ContentType + contentType: ContentType, + userLogger: java.util.function.Consumer[String] )(implicit reportContext: ReportContext): ju.Optional[HoverSignature] = val uri = params.uri().nn val text = params.text().nn @@ -115,7 +119,7 @@ object HoverProvider: tpw match // https://github.com/scala/scala3/issues/8891 case tpw: ImportType => - printer.hoverSymbol(symbol, symbol.paramRef) + printer.hoverSymbol(module, symbol, symbol.paramRef) case _ => val (innerTpe, sym) = if symbol.isType then (symbol.typeRef, symbol) @@ -126,12 +130,12 @@ object HoverProvider: else if innerTpe != NoType then innerTpe else tpw - printer.hoverSymbol(sym, finalTpe.deepDealiasAndSimplify) + printer.hoverSymbol(module, sym, finalTpe.deepDealiasAndSimplify) end match end hoverString val docString = symbolTpes - .flatMap(symTpe => search.symbolDocumentation(symTpe._1, contentType)) + .flatMap(symTpe => search.symbolDocumentation(module, symTpe._1, contentType)) .map(_.docstring()) .mkString("\n") @@ -221,12 +225,21 @@ object HoverProvider: findRefinement(parent) case _ => None - val refTpe = sel.typeOpt.widen.deepDealiasAndSimplify match - case r: RefinedType => Some(r) - case t: (TermRef | TypeProxy) => Some(t.termSymbol.info.deepDealiasAndSimplify) - case _ => None + def extractRefinements(t: Type): List[Type] = t match + case r: RefinedType => List(r) + case t: (TypeRef | AppliedType) => + // deepDealiasAndSimplify can succeed with no progress, so we have to avoid infinite loops + val t1 = t.deepDealiasAndSimplify + if t1 == t then Nil + else extractRefinements(t1) + case t: TermRef => extractRefinements(t.widen) + case t: TypeProxy => List(t.termSymbol.info.deepDealiasAndSimplify) + case AndType(l , r) => List(extractRefinements(l), extractRefinements(r)).flatten + case _ => Nil + + val refTpe: List[Type] = extractRefinements(sel.typeOpt) - refTpe.flatMap(findRefinement).asJava + refTpe.flatMap(findRefinement).headOption.asJava case _ => ju.Optional.empty().nn diff --git a/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala b/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala index 8640f518c0f1..690b6e8f2456 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala @@ -17,9 +17,9 @@ import dotty.tools.pc.printer.ShortenedTypePrinter import dotty.tools.pc.printer.ShortenedTypePrinter.IncludeDefaultParam import dotty.tools.pc.utils.InteractiveEnrichments.* -import scala.meta.pc.reports.ReportContext import scala.meta.pc.OffsetParams import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext class InferExpectedType( search: SymbolSearch, diff --git a/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala index af5a0e409d1a..77511ac1389e 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala @@ -3,10 +3,10 @@ package dotty.tools.pc import java.nio.file.Paths import scala.annotation.tailrec -import scala.meta.pc.reports.ReportContext import scala.meta.pc.OffsetParams import scala.meta.pc.PresentationCompilerConfig import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import scala.meta as m import dotty.tools.dotc.ast.Trees.* diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala index ca5a36cefad0..a01a2d43d7a8 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala @@ -5,6 +5,7 @@ import java.nio.file.Paths import java.util.ArrayList import scala.jdk.CollectionConverters.* +import scala.meta.internal.mtags.GlobalSymbolIndex import scala.meta.internal.pc.DefinitionResultImpl import scala.meta.pc.DefinitionResult import scala.meta.pc.OffsetParams @@ -31,13 +32,13 @@ class PcDefinitionProvider( search: SymbolSearch ): - def definitions(): DefinitionResult = - definitions(findTypeDef = false) + def definitions(module: GlobalSymbolIndex.Module): DefinitionResult = + definitions(module, findTypeDef = false) - def typeDefinitions(): DefinitionResult = - definitions(findTypeDef = true) + def typeDefinitions(module: GlobalSymbolIndex.Module): DefinitionResult = + definitions(module, findTypeDef = true) - private def definitions(findTypeDef: Boolean): DefinitionResult = + private def definitions(module: GlobalSymbolIndex.Module, findTypeDef: Boolean): DefinitionResult = val uri = params.uri().nn val text = params.text().nn val filePath = Paths.get(uri) @@ -53,10 +54,10 @@ class PcDefinitionProvider( given ctx: Context = driver.localContext(params) val indexedContext = IndexedContext(pos)(using ctx) val result = - if findTypeDef then findTypeDefinitions(path, pos, indexedContext, uri) - else findDefinitions(path, pos, indexedContext, uri) + if findTypeDef then findTypeDefinitions(module, path, pos, indexedContext, uri) + else findDefinitions(module, path, pos, indexedContext, uri) - if result.locations().nn.isEmpty() then fallbackToUntyped(pos, uri)(using ctx) + if result.locations().nn.isEmpty() then fallbackToUntyped(module, pos, uri)(using ctx) else result end definitions @@ -72,17 +73,18 @@ class PcDefinitionProvider( * @param pos cursor position * @return definition result */ - private def fallbackToUntyped(pos: SourcePosition, uri: URI)( + private def fallbackToUntyped(module: GlobalSymbolIndex.Module, pos: SourcePosition, uri: URI)( using ctx: Context ) = lazy val untpdPath = NavigateAST .untypedPath(pos.span) .collect { case t: untpd.Tree => t } - definitionsForSymbols(untpdPath.headOption.map(_.symbol).toList, uri, pos) + definitionsForSymbols(module, untpdPath.headOption.map(_.symbol).toList, uri, pos) end fallbackToUntyped private def findDefinitions( + module: GlobalSymbolIndex.Module, path: List[Tree], pos: SourcePosition, indexed: IndexedContext, @@ -90,6 +92,7 @@ class PcDefinitionProvider( ): DefinitionResult = import indexed.ctx definitionsForSymbols( + module, MetalsInteractive.enclosingSymbols(path, pos, indexed), uri, pos @@ -97,6 +100,7 @@ class PcDefinitionProvider( end findDefinitions private def findTypeDefinitions( + module: GlobalSymbolIndex.Module, path: List[Tree], pos: SourcePosition, indexed: IndexedContext, @@ -113,13 +117,14 @@ class PcDefinitionProvider( case Nil => path.headOption match case Some(value: Literal) => - definitionsForSymbols(List(value.typeOpt.widen.typeSymbol), uri, pos) + definitionsForSymbols(module, List(value.typeOpt.widen.typeSymbol), uri, pos) case _ => DefinitionResultImpl.empty case _ => - definitionsForSymbols(typeSymbols, uri, pos) + definitionsForSymbols(module, typeSymbols, uri, pos) end findTypeDefinitions private def definitionsForSymbols( + module: GlobalSymbolIndex.Module, symbols: List[Symbol], uri: URI, pos: SourcePosition @@ -129,10 +134,11 @@ class PcDefinitionProvider( case syms @ ((_, headSym) :: tail) => val locations = syms.flatMap: case (sym, semanticdbSymbol) => - locationsForSymbol(sym, semanticdbSymbol, uri, pos) + locationsForSymbol(module, sym, semanticdbSymbol, uri, pos) DefinitionResultImpl(headSym, locations.asJava) private def locationsForSymbol( + module: GlobalSymbolIndex.Module, symbol: Symbol, semanticdbSymbol: String, uri: URI, @@ -149,7 +155,7 @@ class PcDefinitionProvider( case srcTree if srcTree.namePos.exists => new Location(params.uri().toString(), srcTree.namePos.toLsp) .toList - else search.definition(semanticdbSymbol, uri).asScala.toList + else search.definition(module.asString, semanticdbSymbol, uri).asScala.toList def semanticSymbolsSorted( syms: List[Symbol] diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala index b22b3ae8b7f4..5bba2c3f54c2 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala @@ -5,7 +5,6 @@ import java.nio.file.Paths import scala.annotation.tailrec -import scala.meta.pc.reports.ReportContext import dotty.tools.pc.utils.InteractiveEnrichments.* import dotty.tools.pc.printer.ShortenedTypePrinter import scala.meta.internal.pc.InlayHints @@ -13,12 +12,14 @@ import scala.meta.internal.pc.LabelPart import scala.meta.internal.pc.LabelPart.* import scala.meta.pc.InlayHintsParams import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.NameOps.fieldName import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.NameKinds.DefaultGetterName import dotty.tools.dotc.core.StdNames.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* @@ -457,6 +458,13 @@ object Parameters: case TypeApply(fun, _) => getUnderlyingFun(fun) case t => t + @tailrec + def isDefaultArg(arg: Tree): Boolean = arg match + case Ident(name) => name.is(DefaultGetterName) + case Select(_, name) => name.is(DefaultGetterName) + case Apply(fun, _) => isDefaultArg(fun) + case _ => false + if (params.namedParameters() || params.byNameParameters()) then tree match case Apply(fun, args) if isRealApply(fun) => @@ -467,15 +475,16 @@ object Parameters: val funTp = fun.typeOpt.widenTermRefExpr val paramNames = funTp.paramNamess.flatten val paramInfos = funTp.paramInfoss.flatten + Some( - // Check if the function is an infix function or the underlying function is an infix function isInfixFun(fun, args) || underlyingFun.isInfix, ( args .zip(paramNames) .zip(paramInfos) .collect { - case ((arg, paramName), paramInfo) if !arg.span.isZeroExtent => (paramName.fieldName, arg.sourcePos, paramInfo.isByName) + case ((arg, paramName), paramInfo) if !arg.span.isZeroExtent && !isDefaultArg(arg) => + (paramName.fieldName, arg.sourcePos, paramInfo.isByName) } ) ) diff --git a/presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerAccess.scala b/presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerAccess.scala index f6fc48e5ae67..2affc9a7506b 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerAccess.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerAccess.scala @@ -3,9 +3,9 @@ package dotty.tools.pc import java.util.concurrent.ScheduledExecutorService import scala.concurrent.ExecutionContextExecutor -import scala.meta.pc.reports.ReportContext import scala.meta.internal.pc.CompilerAccess import scala.meta.pc.PresentationCompilerConfig +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.reporting.StoreReporter import dotty.tools.dotc.interactive.InteractiveDriver @@ -13,7 +13,8 @@ import dotty.tools.dotc.interactive.InteractiveDriver class Scala3CompilerAccess( config: PresentationCompilerConfig, sh: Option[ScheduledExecutorService], - newCompiler: () => Scala3CompilerWrapper + newCompiler: () => Scala3CompilerWrapper, + userLogger: java.util.function.Consumer[String] )(using ec: ExecutionContextExecutor, rc: ReportContext) extends CompilerAccess[StoreReporter, InteractiveDriver]( config, @@ -22,7 +23,8 @@ class Scala3CompilerAccess( /* If running inside the executor, we need to reset the job queue * Otherwise it will block indefinetely in case of infinite loops. */ - shouldResetJobQueue = true + shouldResetJobQueue = true, + userLogger ): def newReporter = new StoreReporter(null) diff --git a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala index 18311d1b7853..93cf68c8d1fa 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala @@ -15,17 +15,19 @@ import scala.jdk.CollectionConverters._ import scala.language.unsafeNulls import scala.meta.internal.metals.CompilerVirtualFileParams import scala.meta.internal.metals.EmptyCancelToken -import scala.meta.pc.reports.EmptyReportContext import scala.meta.internal.metals.PcQueryContext -import scala.meta.pc.reports.ReportContext import scala.meta.internal.metals.ReportLevel import scala.meta.internal.mtags.CommonMtagsEnrichments.* +import scala.meta.internal.mtags.GlobalSymbolIndex +import scala.meta.internal.mtags.SourcePath import scala.meta.internal.pc.CompilerAccess import scala.meta.internal.pc.DefinitionResultImpl import scala.meta.internal.pc.EmptyCompletionList import scala.meta.internal.pc.EmptySymbolSearch import scala.meta.internal.pc.PresentationCompilerConfigImpl import scala.meta.pc.* +import scala.meta.pc.reports.EmptyReportContext +import scala.meta.pc.reports.ReportContext import scala.meta.pc.{PcSymbolInformation as IPcSymbolInformation} import dotty.tools.dotc.reporting.StoreReporter @@ -39,22 +41,26 @@ import dotty.tools.dotc.interactive.InteractiveDriver import org.eclipse.lsp4j.DocumentHighlight import org.eclipse.lsp4j.TextEdit import org.eclipse.lsp4j as l - - -case class ScalaPresentationCompiler( - buildTargetIdentifier: String = "", - buildTargetName: Option[String] = None, - classpath: Seq[Path] = Nil, - options: List[String] = Nil, - search: SymbolSearch = EmptySymbolSearch, - ec: ExecutionContextExecutor = ExecutionContext.global, - sh: Option[ScheduledExecutorService] = None, - config: PresentationCompilerConfig = PresentationCompilerConfigImpl(), - folderPath: Option[Path] = None, - reportsLevel: ReportLevel = ReportLevel.Info, - completionItemPriority: CompletionItemPriority = (_: String) => 0, - reportContext: ReportContext = EmptyReportContext() -) extends PresentationCompiler: +import scala.meta.internal.pc.HasCompilerAccess +import dotty.tools.dotc.config.Properties + + +class ScalaPresentationCompiler( + javaHome: Path, + userLoggerSupplier: java.util.function.Supplier[java.util.function.Consumer[String]], + var module: GlobalSymbolIndex.Module, + var buildTargetName: Option[String] = None, + var classpath: Seq[Path] = Nil, + var options: List[String] = Nil, + var search: SymbolSearch = EmptySymbolSearch, + var ec: ExecutionContextExecutor = ExecutionContext.global, + var sh: Option[ScheduledExecutorService] = None, + var config: PresentationCompilerConfig = PresentationCompilerConfigImpl(), + var folderPath: Option[Path] = None, + var reportsLevel: ReportLevel = ReportLevel.Info, + var completionItemPriority: CompletionItemPriority = (_: String) => 0, + var reportContext: ReportContext = EmptyReportContext() +) extends PresentationCompiler with HasCompilerAccess: given ReportContext = reportContext @@ -68,7 +74,7 @@ case class ScalaPresentationCompiler( PcConvertToNamedLambdaParameters.codeActionId ).asJava - def this() = this("", None, Nil, Nil) + private val userLogger = userLoggerSupplier.get() val scalaVersion = BuildInfo.scalaVersion @@ -116,23 +122,44 @@ case class ScalaPresentationCompiler( override def withCompletionItemPriority( priority: CompletionItemPriority - ): PresentationCompiler = - copy(completionItemPriority = priority) + ): this.type = { + this.completionItemPriority = priority + this + } - override def withBuildTargetName(buildTargetName: String) = - copy(buildTargetName = Some(buildTargetName)) + override def withBuildTargetName(buildTargetName: String): this.type = { + this.buildTargetName = Some(buildTargetName) + this + } - override def withReportsLoggerLevel(level: String): PresentationCompiler = - copy(reportsLevel = ReportLevel.fromString(level)) + override def withReportsLoggerLevel(level: String): this.type = { + this.reportsLevel = ReportLevel.fromString(level) + this + } val compilerAccess: CompilerAccess[StoreReporter, InteractiveDriver] = Scala3CompilerAccess( config, sh, - () => new Scala3CompilerWrapper(CachingDriver(driverSettings)) + () => new Scala3CompilerWrapper(newDriver), + userLogger )(using ec) - val driverSettings = + def newDriver: InteractiveDriver = { + userLogger.accept(s"Creating new Scala Presentation Compiler for ${Properties.versionString}") + userLogger.accept("Settings:") + for (elem <- driverSettings) + userLogger.accept(s" $elem") + userLogger.accept("") + userLogger.accept(s"Java home: $javaHome") + userLogger.accept("Class path:") + for (elem <- classpath) + userLogger.accept(s" $elem") + userLogger.accept("") + CachingDriver(driverSettings, javaHome) + } + + def driverSettings = val implicitSuggestionTimeout = List("-Ximport-suggestion-timeout", "0") val defaultFlags = List("-color:never") val filteredOptions = removeDoubleOptions(options.filterNot(forbiddenOptions)) @@ -140,6 +167,9 @@ case class ScalaPresentationCompiler( filteredOptions ::: defaultFlags ::: implicitSuggestionTimeout ::: "-classpath" :: classpath .mkString(File.pathSeparator) :: Nil + def latestException(): Option[(Option[String], Throwable)] = + None // TODO + private def removeDoubleOptions(options: List[String]): List[String] = options match case head :: _ :: tail if forbiddenDoubleOptions(head) => @@ -152,7 +182,9 @@ case class ScalaPresentationCompiler( ): CompletableFuture[ju.List[Node]] = compilerAccess.withInterruptableCompiler( new ju.ArrayList[Node](), - params.token() + params.token(), + "semanticTokens", + params.uri.toASCIIString ) { access => val driver = access.compiler() new PcSemanticTokensProvider(driver, params).provide().asJava @@ -164,6 +196,8 @@ case class ScalaPresentationCompiler( compilerAccess.withInterruptableCompiler( new ju.ArrayList[l.InlayHint](), params.token(), + "syntheticDecorations", + params.uri.toASCIIString ) { access => val driver = access.compiler() new PcInlayHintsProvider(driver, params, search) @@ -182,28 +216,35 @@ case class ScalaPresentationCompiler( def complete(params: OffsetParams): CompletableFuture[l.CompletionList] = compilerAccess.withInterruptableCompiler( EmptyCompletionList(), - params.token() + params.token(), + "complete", + params.uri.toASCIIString ) { access => val driver = access.compiler() - new CompletionProvider( + val provider = new CompletionProvider( search, driver, - () => InteractiveDriver(driverSettings), + () => newDriver, params, config, - buildTargetIdentifier, + module.asString, folderPath, completionItemPriority - ).completions() + ) + SourcePath.withContext { ctx => + provider.completions(module)(using ctx.iface) + } }(params.toQueryContext) def definition(params: OffsetParams): CompletableFuture[DefinitionResult] = compilerAccess.withInterruptableCompiler( DefinitionResultImpl.empty, - params.token() + params.token(), + "definition", + params.uri.toASCIIString ) { access => val driver = access.compiler() - PcDefinitionProvider(driver, params, search).definitions() + PcDefinitionProvider(driver, params, search).definitions(module) }(params.toQueryContext) override def typeDefinition( @@ -211,10 +252,12 @@ case class ScalaPresentationCompiler( ): CompletableFuture[DefinitionResult] = compilerAccess.withInterruptableCompiler( DefinitionResultImpl.empty, - params.token() + params.token(), + "typeDefinition", + params.uri.toASCIIString ) { access => val driver = access.compiler() - PcDefinitionProvider(driver, params, search).typeDefinitions() + PcDefinitionProvider(driver, params, search).typeDefinitions(module) }(params.toQueryContext) def documentHighlight( @@ -222,7 +265,9 @@ case class ScalaPresentationCompiler( ): CompletableFuture[ju.List[DocumentHighlight]] = compilerAccess.withInterruptableCompiler( List.empty[DocumentHighlight].asJava, - params.token() + params.token(), + "documentHighlight", + params.uri.toASCIIString ) { access => val driver = access.compiler() PcDocumentHighlightProvider(driver, params).highlights.asJava @@ -234,6 +279,8 @@ case class ScalaPresentationCompiler( compilerAccess.withNonInterruptableCompiler( List.empty[ReferencesResult].asJava, params.file().token, + "references", + params.file.uri.toASCIIString ) { access => val driver = access.compiler() PcReferencesProvider(driver, params) @@ -245,6 +292,8 @@ case class ScalaPresentationCompiler( compilerAccess.withInterruptableCompiler( Optional.empty(), params.token, + "inferExpectedType", + params.uri.toASCIIString ) { access => val driver = access.compiler() new InferExpectedType(search, driver, params).infer().asJava @@ -265,6 +314,8 @@ case class ScalaPresentationCompiler( compilerAccess.withNonInterruptableCompiler[Optional[IPcSymbolInformation]]( Optional.empty(), EmptyCancelToken, + "info", + "" ) { access => SymbolInformationProvider(using access.compiler().currentCtx) .info(symbol) @@ -277,9 +328,12 @@ case class ScalaPresentationCompiler( code: String ): CompletableFuture[Array[Byte]] = val virtualFile = CompilerVirtualFileParams(filename, code) + userLogger.accept(s"Computing semanticdb for $filename") compilerAccess.withNonInterruptableCompiler( Array.empty[Byte], - EmptyCancelToken + EmptyCancelToken, + "semanticdb", + filename.toASCIIString ) { access => val driver = access.compiler() val provider = SemanticdbTextDocumentProvider(driver, folderPath) @@ -292,10 +346,12 @@ case class ScalaPresentationCompiler( ): CompletableFuture[l.CompletionItem] = compilerAccess.withNonInterruptableCompiler( item, - EmptyCancelToken + EmptyCancelToken, + "completionItem", + "" ) { access => val driver = access.compiler() - CompletionItemResolver.resolve(item, symbol, search, config)(using + CompletionItemResolver.resolve(module, item, symbol, search, config)(using driver.currentCtx ) }(emptyQueryContext) @@ -309,7 +365,9 @@ case class ScalaPresentationCompiler( ] = compilerAccess.withNonInterruptableCompiler( List.empty[scala.meta.pc.AutoImportsResult].asJava, - params.token() + params.token(), + "autoImports", + params.uri.toASCIIString ) { access => val driver = access.compiler() new AutoImportsProvider( @@ -318,7 +376,7 @@ case class ScalaPresentationCompiler( name, params, config, - buildTargetIdentifier + module.asString ) .autoImports(isExtension) .asJava @@ -330,10 +388,13 @@ case class ScalaPresentationCompiler( val empty: ju.List[l.TextEdit] = new ju.ArrayList[l.TextEdit]() compilerAccess.withNonInterruptableCompiler( empty, - params.token() + params.token(), + "implementAbstractMembers", + params.uri.toASCIIString ) { pc => val driver = pc.compiler() OverrideCompletions.implementAllAt( + module, params, driver, search, @@ -348,7 +409,9 @@ case class ScalaPresentationCompiler( val empty: ju.List[l.TextEdit] = new ju.ArrayList[l.TextEdit]() compilerAccess.withNonInterruptableCompiler( empty, - params.token() + params.token(), + "inferredType", + params.uri.toASCIIString ) { pc => new InferredTypeProvider(params, pc.compiler(), config, search) .inferredTypeEdits() @@ -373,7 +436,12 @@ case class ScalaPresentationCompiler( ): CompletableFuture[ju.List[l.TextEdit]] = val empty: Either[String, List[l.TextEdit]] = Right(List()) (compilerAccess - .withInterruptableCompiler(empty, params.token()) { pc => + .withInterruptableCompiler( + empty, + params.token(), + "inlineValue", + params.uri.toASCIIString + ) { pc => new PcInlineValueProvider(pc.compiler(), params) .getInlineTextEdits() }(params.toQueryContext)) @@ -388,7 +456,12 @@ case class ScalaPresentationCompiler( extractionPos: OffsetParams ): CompletableFuture[ju.List[l.TextEdit]] = val empty: ju.List[l.TextEdit] = new ju.ArrayList[l.TextEdit]() - compilerAccess.withInterruptableCompiler(empty, range.token()) { + compilerAccess.withInterruptableCompiler( + empty, + range.token(), + "extractMethod", + range.uri.toASCIIString + ) { pc => new ExtractMethodProvider( range, @@ -414,7 +487,12 @@ case class ScalaPresentationCompiler( ): CompletableFuture[ju.List[l.TextEdit]] = val empty: Either[String, List[l.TextEdit]] = Right(List()) (compilerAccess - .withNonInterruptableCompiler(empty, params.token()) { pc => + .withNonInterruptableCompiler( + empty, + params.token(), + "convertToNamedArguments", + params.uri.toASCIIString + ) { pc => new ConvertToNamedArgumentsProvider( pc.compiler(), params, @@ -431,7 +509,8 @@ case class ScalaPresentationCompiler( ): CompletableFuture[ju.List[l.SelectionRange]] = CompletableFuture.completedFuture { compilerAccess.withSharedCompiler( - List.empty[l.SelectionRange].asJava + List.empty[l.SelectionRange].asJava, + params.asScala.headOption.map(_.uri().toASCIIString()).getOrElse("") ) { pc => new SelectionRangeProvider( pc.compiler(), @@ -441,15 +520,31 @@ case class ScalaPresentationCompiler( } end selectionRange + def compile( + params: VirtualFileParams + ): CompletableFuture[CompileResult] = + compilerAccess.withNonInterruptableCompiler( + CompileProvider.Result(Nil, ""), + params.token(), + "compile", + params.uri.toASCIIString + ) { access => + val driver = access.compiler() + CompileProvider.compile(params, driver) + }(params.toQueryContext) + end compile + def hover( params: OffsetParams ): CompletableFuture[ju.Optional[HoverSignature]] = compilerAccess.withNonInterruptableCompiler( ju.Optional.empty[HoverSignature](), - params.token() + params.token(), + "hover", + params.uri.toASCIIString ) { access => val driver = access.compiler() - HoverProvider.hover(params, driver, search, config.hoverContentType()) + HoverProvider.hover(module, params, driver, search, config.hoverContentType(), userLogger) }(params.toQueryContext) end hover @@ -458,7 +553,9 @@ case class ScalaPresentationCompiler( ): CompletableFuture[ju.Optional[l.Range]] = compilerAccess.withNonInterruptableCompiler( Optional.empty[l.Range](), - params.token() + params.token(), + "prepareRename", + params.uri.toASCIIString ) { access => val driver = access.compiler() Optional.ofNullable( @@ -472,30 +569,34 @@ case class ScalaPresentationCompiler( ): CompletableFuture[ju.List[l.TextEdit]] = compilerAccess.withNonInterruptableCompiler( List[l.TextEdit]().asJava, - params.token() + params.token(), + "rename", + params.uri.toASCIIString ) { access => val driver = access.compiler() PcRenameProvider(driver, params, Some(name)).rename().asJava }(params.toQueryContext) def newInstance( - buildTargetIdentifier: String, + moduleString: String, classpath: ju.List[Path], options: ju.List[String] - ): PresentationCompiler = - copy( - buildTargetIdentifier = buildTargetIdentifier, - classpath = classpath.asScala.toSeq, - options = options.asScala.toList - ) + ): this.type = { + this.module = GlobalSymbolIndex.Module.fromString(moduleString) + this.classpath = classpath.asScala.toSeq + this.options = options.asScala.toList + this + } def signatureHelp(params: OffsetParams): CompletableFuture[l.SignatureHelp] = compilerAccess.withNonInterruptableCompiler( new l.SignatureHelp(), - params.token() + params.token(), + "signatureHelp", + params.uri.toASCIIString ) { access => val driver = access.compiler() - SignatureHelpProvider.signatureHelp(driver, params, search) + SignatureHelpProvider.signatureHelp(module, driver, params, search) }(params.toQueryContext) override def didChange( @@ -506,32 +607,46 @@ case class ScalaPresentationCompiler( override def didClose(uri: URI): Unit = compilerAccess.withNonInterruptableCompiler( (), - EmptyCancelToken + EmptyCancelToken, + "close", + uri.toASCIIString ) { access => access.compiler().close(uri) }(emptyQueryContext) override def withExecutorService( executorService: ExecutorService - ): PresentationCompiler = - copy(ec = ExecutionContext.fromExecutorService(executorService)) + ): this.type = { + this.ec = ExecutionContext.fromExecutorService(executorService) + this + } override def withConfiguration( config: PresentationCompilerConfig - ): PresentationCompiler = - copy(config = config) + ): this.type = { + this.config = config + this + } override def withScheduledExecutorService( sh: ScheduledExecutorService - ): PresentationCompiler = - copy(sh = Some(sh)) - - def withSearch(search: SymbolSearch): PresentationCompiler = - copy(search = search) - - override def withReportContext(reportContext: ReportContext): PresentationCompiler = - copy(reportContext = reportContext) - - def withWorkspace(workspace: Path): PresentationCompiler = - copy(folderPath = Some(workspace)) + ): this.type = { + this.sh = Some(sh) + this + } + + def withSearch(search: SymbolSearch): this.type = { + this.search = search + this + } + + override def withReportContext(reportContext: ReportContext): this.type = { + this.reportContext = reportContext + this + } + + def withWorkspace(workspace: Path): this.type = { + this.folderPath = Some(workspace) + this + } override def isLoaded() = compilerAccess.isLoaded() diff --git a/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala index 4fab29159bfb..86b84da0b5d5 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala @@ -13,14 +13,16 @@ import dotty.tools.pc.utils.InteractiveEnrichments.* import org.eclipse.lsp4j as l import scala.jdk.CollectionConverters.* -import scala.meta.pc.reports.ReportContext +import scala.meta.internal.mtags.GlobalSymbolIndex import scala.meta.pc.OffsetParams import scala.meta.pc.SymbolDocumentation import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext object SignatureHelpProvider: def signatureHelp( + module: GlobalSymbolIndex.Module, driver: InteractiveDriver, params: OffsetParams, search: SymbolSearch @@ -49,7 +51,7 @@ object SignatureHelpProvider: signature.denot.map(signature -> _) val signatureInfos = infos.map { case (signature, denot) => - search.symbolDocumentation(denot.symbol) match + search.symbolDocumentation(module, denot.symbol) match case Some(doc) => withDocumentation( doc, diff --git a/presentation-compiler/src/main/dotty/tools/pc/buildinfo/BuildInfo.scala b/presentation-compiler/src/main/dotty/tools/pc/buildinfo/BuildInfo.scala new file mode 100644 index 000000000000..0857318bcf30 --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/pc/buildinfo/BuildInfo.scala @@ -0,0 +1,5 @@ +package dotty.tools.pc.buildinfo + +object BuildInfo { + def scalaVersion = "3.7.3" +} diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala index d3509466040d..278c3c6c90c9 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala @@ -4,10 +4,12 @@ package completions import java.nio.file.Path import scala.jdk.CollectionConverters._ -import scala.meta.pc.reports.ReportContext +import scala.meta.internal.mtags.GlobalSymbolIndex import scala.meta.pc.OffsetParams import scala.meta.pc.PresentationCompilerConfig +import scala.meta.pc.SourcePathContext import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.tpd.* @@ -50,11 +52,11 @@ class CompletionProvider( freshDriver: () => InteractiveDriver, params: OffsetParams, config: PresentationCompilerConfig, - buildTargetIdentifier: String, + moduleString: String, folderPath: Option[Path], referenceCounter: CompletionItemPriority )(using reports: ReportContext): - def completions(): CompletionList = + def completions(module: GlobalSymbolIndex.Module)(using SourcePathContext): CompletionList = val uri = params.uri().nn val text = params.text().nn @@ -125,7 +127,7 @@ class CompletionProvider( text, locatedCtx, search, - buildTargetIdentifier, + moduleString, completionPos, indexedCtx, tpdPath, @@ -136,10 +138,11 @@ class CompletionProvider( unit.comments, driver.settings, referenceCounter - ).completions() + ).completions(module) val items = completions.zipWithIndex.map { case (item, idx) => completionItems( + module, item, idx, autoImportsGen, @@ -200,6 +203,7 @@ class CompletionProvider( end applyCompletionCursor private def completionItems( + module: GlobalSymbolIndex.Module, completion: CompletionValue, idx: Int, autoImports: AutoImportsGenerator, @@ -218,9 +222,9 @@ class CompletionProvider( // to recalculate the description // related issue https://github.com/lampepfl/scala3/issues/11941 lazy val kind: CompletionItemKind = underlyingCompletion.completionItemKind - val description = underlyingCompletion.description(printer) + val description = underlyingCompletion.description(module.asString, printer) val label = - if config.isDetailIncludedInLabel then completion.labelWithDescription(printer) + if config.isDetailIncludedInLabel then completion.labelWithDescription(module.asString, printer) else completion.label val ident = underlyingCompletion.insertText.getOrElse(underlyingCompletion.label) lazy val isInStringInterpolation = @@ -268,7 +272,7 @@ class CompletionProvider( item.setAdditionalTextEdits((underlyingCompletion.additionalEdits ++ additionalEdits).asJava) underlyingCompletion.insertMode.foreach(item.setInsertTextMode) - val data = underlyingCompletion.completionData(buildTargetIdentifier) + val data = underlyingCompletion.completionData(moduleString) item.setData(data.toJson) item.setTags(underlyingCompletion.lspTags.asJava) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala index 05d97972d76e..93a07995f630 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala @@ -1,6 +1,7 @@ package dotty.tools.pc package completions +import scala.meta.internal.mtags.GlobalSymbolIndex import scala.meta.internal.pc.CompletionItemData import dotty.tools.dotc.core.Contexts.Context @@ -46,17 +47,17 @@ sealed trait CompletionValue: def filterText: Option[String] = None def completionItemKind(using Context): CompletionItemKind def completionItemDataKind: Integer = CompletionItemData.None - def description(printer: ShortenedTypePrinter)(using Context): String = "" + def description(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = "" def insertMode: Option[InsertTextMode] = None - def completionData(buildTargetIdentifier: String)( + def completionData(moduleString: String)( using Context - ): CompletionItemData = CompletionItemData("", buildTargetIdentifier, kind = completionItemDataKind) + ): CompletionItemData = CompletionItemData("", moduleString, kind = completionItemDataKind) def command: Option[String] = None /** * Label with potentially attached description. */ - def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = + def labelWithDescription(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = label def lspTags(using Context): List[CompletionItemTag] = Nil end CompletionValue @@ -70,11 +71,11 @@ object CompletionValue: def isExtensionMethod: Boolean = false override def completionData( - buildTargetIdentifier: String + moduleString: String )(using Context): CompletionItemData = CompletionItemData( SemanticdbSymbols.symbolName(symbol), - buildTargetIdentifier, + GlobalSymbolIndex.Module.fromString(moduleString).targetId, kind = completionItemDataKind ) def importSymbol: Symbol = symbol @@ -97,17 +98,18 @@ object CompletionValue: if symbol.isDeprecated then List(CompletionItemTag.Deprecated) else Nil override def labelWithDescription( + moduleString: String, printer: ShortenedTypePrinter )(using Context): String = - if symbol.isConstructor then s"${snippetAffix.toPrefix}${label}${description(printer)}" - else if symbol.is(Method) then s"${label}${description(printer)}" - else if symbol.is(Mutable) then s"$label${description(printer)}" + if symbol.isConstructor then s"${snippetAffix.toPrefix}${label}${description(moduleString, printer)}" + else if symbol.is(Method) then s"${label}${description(moduleString, printer)}" + else if symbol.is(Mutable) then s"$label${description(moduleString, printer)}" else if symbol.is(Package) || symbol.is(Module) || symbol.isClass then - s"${labelWithSuffix(printer)}${description(printer)}" + s"${labelWithSuffix(printer)}${description(moduleString, printer)}" else if symbol.isType then labelWithSuffix(printer) else if symbol.isTerm && symbol.info.typeSymbol.is(Module) then - s"${label}${description(printer)}" - else s"$label${description(printer)}" + s"${label}${description(moduleString, printer)}" + else s"$label${description(moduleString, printer)}" protected def labelWithSuffix(printer: ShortenedTypePrinter)(using Context): String = if snippetAffix.addLabelSnippet @@ -118,11 +120,11 @@ object CompletionValue: s"${label}${printedParams.mkString("[", ",", "]")}" else label - override def description(printer: ShortenedTypePrinter)(using Context): String = + override def description(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = def info = denotation.info.widenTermRefExpr val isVal = !(symbol.is(Module) || symbol.is(Method) || symbol.isType || info.typeSymbol.is(Module)) val prefix = if isVal then ": " else "" - prefix ++ printer.completionSymbol(denotation) + prefix ++ printer.completionSymbol(GlobalSymbolIndex.Module.fromString(moduleString), denotation) end Symbolic @@ -148,10 +150,10 @@ object CompletionValue: ) extends Symbolic: override def additionalEdits: List[TextEdit] = extraMethod.additionalEdits override def command: Option[String] = extraMethod.command - override def completionData(buildTargetIdentifier: String)(using Context): CompletionItemData = extraMethod.completionData((buildTargetIdentifier)) + override def completionData(moduleString: String)(using Context): CompletionItemData = extraMethod.completionData((moduleString)) override def completionItemKind(using Context): CompletionItemKind = extraMethod.completionItemKind - override def description(printer: ShortenedTypePrinter)(using Context): String = extraMethod.description(printer) - override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = extraMethod.labelWithDescription(printer) + override def description(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = extraMethod.description(moduleString, printer) + override def labelWithDescription(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = extraMethod.labelWithDescription(moduleString, printer) override def range: Option[Range] = extraMethod.range override def denotation: Denotation = extraMethod.denotation override def label: String = extraMethod.label @@ -180,15 +182,15 @@ object CompletionValue: ) extends Symbolic: override def completionItemDataKind: Integer = CompletionSource.WorkspaceKind.ordinal - override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = + override def labelWithDescription(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = def isMethodOrValue = !(symbol.isType || symbol.is(Module)) if symbol.isConstructor || symbol.name == nme.apply then - s"${snippetAffix.toPrefix}${label}${description(printer)} - ${printer.fullNameString(importSymbol.effectiveOwner)}" + s"${snippetAffix.toPrefix}${label}${description(moduleString, printer)} - ${printer.fullNameString(importSymbol.effectiveOwner)}" else if isMethodOrValue then s"${labelWithSuffix(printer)} - ${printer.fullNameString(symbol.effectiveOwner)}" else if symbol.is(Package) || symbol.is(Module) || symbol.isClass then - s"${labelWithSuffix(printer)} -${description(printer)}" - else super.labelWithDescription(printer) + s"${labelWithSuffix(printer)} -${description(moduleString, printer)}" + else super.labelWithDescription(moduleString, printer) /** * CompletionValue for old implicit classes methods via SymbolSearch @@ -202,8 +204,8 @@ object CompletionValue: override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Method override def completionItemDataKind: Integer = CompletionSource.ImplicitClassKind.ordinal - override def description(printer: ShortenedTypePrinter)(using Context): String = - s"${super.description(printer)} (implicit)" + override def description(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = + s"${super.description(moduleString, printer)} (implicit)" /** * CompletionValue for extension methods via SymbolSearch @@ -217,8 +219,8 @@ object CompletionValue: CompletionItemKind.Method override def completionItemDataKind: Integer = CompletionSource.ExtensionKind.ordinal override def isExtensionMethod: Boolean = true - override def description(printer: ShortenedTypePrinter)(using Context): String = - s"${printer.completionSymbol(denotation)} (extension)" + override def description(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = + s"${printer.completionSymbol(GlobalSymbolIndex.Module.fromString(moduleString), denotation)} (extension)" /** * @param shortenedNames shortened type names by `Printer`. This field should be used for autoImports @@ -240,7 +242,7 @@ object CompletionValue: override def completionItemDataKind: Integer = CompletionSource.OverrideKind.ordinal override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Method - override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = + override def labelWithDescription(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = label end Override @@ -253,10 +255,10 @@ object CompletionValue: override def completionItemDataKind: Integer = CompletionSource.OverrideKind.ordinal override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Field - override def description(printer: ShortenedTypePrinter)(using Context): String = + override def description(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = ": " + printer.tpe(tpe) - override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = + override def labelWithDescription(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = label end NamedArg @@ -310,10 +312,11 @@ object CompletionValue: ) extends Symbolic: override def completionItemDataKind: Integer = CompletionSource.InterpolatorKind.ordinal override def description( + moduleString: String, printer: ShortenedTypePrinter )(using Context): String = - if isExtension then s"${printer.completionSymbol(symbol)} (extension)" - else super.description(printer) + if isExtension then s"${printer.completionSymbol(GlobalSymbolIndex.Module.fromString(moduleString), symbol)} (extension)" + else super.description(moduleString, printer) override def isExtensionMethod: Boolean = isExtension end Interpolator @@ -326,7 +329,7 @@ object CompletionValue: override def completionItemDataKind: Integer = CompletionSource.MatchCompletionKind.ordinal override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Enum - override def description(printer: ShortenedTypePrinter)(using Context): String = + override def description(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = desc case class CaseKeyword( @@ -341,11 +344,11 @@ object CompletionValue: override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Method - override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = + override def labelWithDescription(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = label - override def description(printer: ShortenedTypePrinter)(using Context): String = - printer.completionSymbol(denotation) + override def description(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = + printer.completionSymbol(GlobalSymbolIndex.Module.fromString(moduleString), denotation) end CaseKeyword case class Document(label: String, doc: String, description: String) @@ -357,14 +360,14 @@ object CompletionValue: override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Snippet - override def description(printer: ShortenedTypePrinter)(using Context): String = + override def description(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = description override def insertMode: Option[InsertTextMode] = Some(InsertTextMode.AsIs) case class SingletonValue(label: String, info: Type, override val range: Option[Range]) extends CompletionValue: override def insertText: Option[String] = Some(label) - override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = + override def labelWithDescription(moduleString: String, printer: ShortenedTypePrinter)(using Context): String = s"$label: ${printer.tpe(info)}" override def completionItemKind(using Context): CompletionItemKind = diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala index e7902ef8aa44..889d947235d8 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -5,10 +5,11 @@ import java.nio.file.Path import java.nio.file.Paths import scala.collection.mutable -import scala.meta.pc.reports.ReportContext import scala.meta.internal.mtags.CoursierComplete +import scala.meta.internal.mtags.GlobalSymbolIndex import scala.meta.internal.pc.{IdentifierComparator, MemberOrdering, CompletionFuzzy} import scala.meta.pc.* +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.ast.untpd @@ -37,7 +38,7 @@ class Completions( text: String, ctx: Context, search: SymbolSearch, - buildTargetIdentifier: String, + moduleString: String, completionPos: CompletionPos, indexedContext: IndexedContext, path: List[Tree], @@ -109,7 +110,7 @@ class Completions( if completionMode.is(Mode.Member) then CompletionFuzzy.matchesSubCharacters(completionPos.query, name.toString) else CompletionFuzzy.matches(completionPos.query, name.toString) - def enrichedCompilerCompletions(qualType: Type): (List[CompletionValue], SymbolSearch.Result) = + def enrichedCompilerCompletions(qualType: Type)(using SourcePathContext): (List[CompletionValue], SymbolSearch.Result) = val compilerCompletions = Completion .rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath, Some(fuzzyMatcher)) @@ -118,8 +119,8 @@ class Completions( .flatMap(toCompletionValues) .filterInteresting(qualType) - def completions(): (List[CompletionValue], SymbolSearch.Result) = - val (advanced, exclusive) = advancedCompletions(path, completionPos) + def completions(module: GlobalSymbolIndex.Module)(using SourcePathContext): (List[CompletionValue], SymbolSearch.Result) = + val (advanced, exclusive) = advancedCompletions(module, path, completionPos) val (all, result) = if exclusive then (advanced, SymbolSearch.Result.COMPLETE) else @@ -305,9 +306,10 @@ class Completions( * Metals should provide advanced completions only. */ private def advancedCompletions( + module: GlobalSymbolIndex.Module, path: List[Tree], completionPos: CompletionPos - ): (List[CompletionValue], Boolean) = + )(using SourcePathContext): (List[CompletionValue], Boolean) = val pos = completionPos.originalCursorPosition lazy val rawPath = Paths .get(pos.source.path).nn @@ -339,6 +341,7 @@ class Completions( case MatchCaseExtractor.MatchExtractor(selector) => ( CaseKeywordCompletion.matchContribute( + module, selector, completionPos, indexedContext, @@ -357,6 +360,7 @@ class Completions( ) => ( CaseKeywordCompletion.contribute( + module, selector, completionPos, indexedContext, @@ -377,6 +381,7 @@ class Completions( ) => ( CaseKeywordCompletion.contribute( + module, selector, completionPos, indexedContext, @@ -396,6 +401,7 @@ class Completions( ) => ( CaseKeywordCompletion.contribute( + module, selector, completionPos, indexedContext, @@ -412,6 +418,7 @@ class Completions( case Ident(name) :: (unapp : UnApply) :: _ => ( CaseKeywordCompletion.contribute( + module, EmptyTree, // no selector completionPos, indexedContext, @@ -426,6 +433,7 @@ class Completions( case Select(_, name) :: (unapp : UnApply) :: _ => ( CaseKeywordCompletion.contribute( + module, EmptyTree, // no selector completionPos, indexedContext, @@ -443,6 +451,7 @@ class Completions( case OverrideExtractor(td, completing, start, exhaustive, fallbackName) => ( OverrideCompletions.contribute( + module, td, completing, start, @@ -475,7 +484,7 @@ class Completions( config.isCompletionSnippetsEnabled(), search, config, - buildTargetIdentifier + moduleString ) .filterInteresting(enrich = false) ._1 @@ -560,7 +569,7 @@ class Completions( private def enrichWithSymbolSearch( visit: CompletionValue => Boolean, qualType: Type = ctx.definitions.AnyType - ): Option[SymbolSearch.Result] = + )(using SourcePathContext): Option[SymbolSearch.Result] = val query = completionPos.query if completionMode.is(Mode.Scope) && query.nonEmpty then val visitor = new CompilerSearchVisitor(sym => @@ -587,7 +596,7 @@ class Completions( ).map(visit).forall(_ == true) else false, ) - Some(search.search(query, buildTargetIdentifier, visitor).nn) + Some(search.search(query, moduleString, visitor, implicitly[SourcePathContext]).nn) else if completionMode.is(Mode.Member) && query.nonEmpty then val visitor = new CompilerSearchVisitor(sym => def isExtensionMethod = sym.is(ExtensionMethod) && @@ -626,7 +635,7 @@ class Completions( ).map(visit).forall(_ == true) else false, ) - Some(search.searchMethods(query, buildTargetIdentifier, visitor).nn) + Some(search.searchMethods(query, moduleString, visitor).nn) else Some(SymbolSearch.Result.INCOMPLETE) end enrichWithSymbolSearch @@ -676,7 +685,7 @@ class Completions( def filterInteresting( qualType: Type = ctx.definitions.AnyType, enrich: Boolean = true - ): (List[CompletionValue], SymbolSearch.Result) = + )(using SourcePathContext): (List[CompletionValue], SymbolSearch.Result) = val alreadySeen = mutable.Set.empty[String] val buf = List.newBuilder[CompletionValue] def visit(head: CompletionValue): Boolean = @@ -734,15 +743,18 @@ class Completions( defn.Object_notifyAll, defn.Object_notify, defn.Predef_undefined, - defn.ObjectClass.info.member(nme.wait_).symbol, // NOTE(olafur) IntelliJ does not complete the root package and without this filter // then `_root_` would appear as a completion result in the code `foobar(_)` defn.RootPackage, // NOTE(gabro) valueOf was added as a Predef member in 2.13. We filter it out since is a niche // use case and it would appear upon typing 'val' - defn.ValueOfClass.info.member(nme.valueOf).symbol, - defn.ScalaPredefModule.requiredMethod(nme.valueOf) - ).flatMap(_.alternatives.map(_.symbol)).toSet + defn.ValueOfClass + ) ++ ( + Set( + defn.ObjectClass.info.member(nme.wait_), + defn.ScalaPredefModule.info.member(nme.valueOf) + ).flatMap(_.alternatives.map(_.symbol)).toSet + ) private def isNotLocalForwardReference(sym: Symbol)(using Context): Boolean = !sym.isLocalToBlock || diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala index 9cceff7310c6..d949bfdec27f 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala @@ -1,11 +1,12 @@ package dotty.tools.pc.completions import scala.collection.mutable.ListBuffer -import scala.meta.pc.reports.ReportContext import scala.meta.internal.pc.CompletionFuzzy import scala.meta.internal.pc.InterpolationSplice import scala.meta.pc.PresentationCompilerConfig +import scala.meta.pc.SourcePathContext import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Contexts.Context @@ -32,7 +33,7 @@ object InterpolatorCompletions: search: SymbolSearch, config: PresentationCompilerConfig, buildTargetIdentifier: String - )(using Context, ReportContext) = + )(using Context, ReportContext, SourcePathContext) = InterpolationSplice(completionPos.queryEnd, text.toCharArray().nn, text) match case Some(interpolator) => InterpolatorCompletions.contributeScope( @@ -222,7 +223,7 @@ object InterpolatorCompletions: hasStringInterpolator: Boolean, search: SymbolSearch, buildTargetIdentifier: String - )(using ctx: Context, reportsContext: ReportContext): List[CompletionValue] = + )(using ctx: Context, reportsContext: ReportContext, sourcePathCtx: SourcePathContext): List[CompletionValue] = val litStartPos = lit.span.start val litEndPos = lit.span.end - (if completionPos.withCURSOR then Cursor.value.length else 0) val position = completionPos.originalCursorPosition @@ -274,7 +275,7 @@ object InterpolatorCompletions: true, ) if interpolator.name.nonEmpty then - search.search(interpolator.name, buildTargetIdentifier, visitor) + search.search(interpolator.name, buildTargetIdentifier, visitor, implicitly[SourcePathContext]) def collectCompletions( isWorkspace: Boolean diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala index 2e89b4e5bb99..7656584adde4 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala @@ -6,10 +6,11 @@ import java.net.URI import scala.collection.mutable import scala.collection.mutable.ListBuffer import scala.jdk.CollectionConverters._ -import scala.meta.pc.reports.ReportContext +import scala.meta.internal.mtags.GlobalSymbolIndex import scala.meta.internal.pc.CompletionFuzzy import scala.meta.pc.PresentationCompilerConfig import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import dotty.tools.toOption import dotty.tools.dotc.ast.tpd.* @@ -57,6 +58,7 @@ object CaseKeywordCompletion: * @param hasBind `true` when `case _: @@ =>`, if hasBind we don't need unapply completions */ def contribute( + module: GlobalSymbolIndex.Module, selector: Tree, completionPos: CompletionPos, indexedContext: IndexedContext, @@ -213,6 +215,7 @@ object CaseKeywordCompletion: val sealedMembers0 = res.filter((si, _) => sealedDescs.contains(si.sym)) sortSubclasses( + module, selectorSym.info, sealedMembers0, completionPos.sourceUri, @@ -261,6 +264,7 @@ object CaseKeywordCompletion: * @param typedtree typed tree of the file, used for generating auto imports */ def matchContribute( + module: GlobalSymbolIndex.Module, selector: Tree, completionPos: CompletionPos, indexedContext: IndexedContext, @@ -291,7 +295,7 @@ object CaseKeywordCompletion: .flatMap(si => completionGenerator.labelForCaseMember(si.sym, si.name).map((si, _)) ) - sortSubclasses(tpe, subclasses, completionPos.sourceUri, search) + sortSubclasses(module, tpe, subclasses, completionPos.sourceUri, search) val (labels, imports) = sortedSubclasses.map((si, label) => (label, si.importSel)).unzip @@ -331,6 +335,7 @@ object CaseKeywordCompletion: end matchContribute private def sortSubclasses[A]( + module: GlobalSymbolIndex.Module, tpe: Type, syms: List[(SymbolImport, String)], uri: URI, @@ -340,7 +345,7 @@ object CaseKeywordCompletion: syms.sortBy(_._1.sym.sourcePos.point) else val defnSymbols = search - .definitionSourceToplevels(SemanticdbSymbols.symbolName(tpe.typeSymbol), uri).nn + .definitionSourceToplevels(module.asString, SemanticdbSymbols.symbolName(tpe.typeSymbol), uri).nn .asScala .zipWithIndex .toMap diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala index f01a1e9b8cd8..db9bcf6c021d 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala @@ -4,11 +4,12 @@ package completions import java.util as ju import scala.jdk.CollectionConverters._ -import scala.meta.pc.reports.ReportContext +import scala.meta.internal.mtags.GlobalSymbolIndex import scala.meta.pc.OffsetParams import scala.meta.pc.PresentationCompilerConfig import scala.meta.pc.PresentationCompilerConfig.OverrideDefFormat import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.ast.tpd.Tree @@ -46,6 +47,7 @@ object OverrideCompletions: * `*override def f|` (where `|` represents the cursor position). */ def contribute( + module: GlobalSymbolIndex.Module, td: TypeDef, completing: Option[Symbol], start: Int, @@ -115,6 +117,7 @@ object OverrideCompletions: overridables .map(sym => toCompletionValue( + module, sym.denot, start, td, @@ -131,6 +134,7 @@ object OverrideCompletions: end contribute def implementAllAt( + module: GlobalSymbolIndex.Module, params: OffsetParams, driver: InteractiveDriver, search: SymbolSearch, @@ -205,6 +209,7 @@ object OverrideCompletions: config ) lazy val implementAll = implementAllFor( + module, indexedContext, text, search, @@ -222,6 +227,7 @@ object OverrideCompletions: end implementAllAt private def implementAllFor( + module: GlobalSymbolIndex.Module, indexedContext: IndexedContext, text: String, search: SymbolSearch, @@ -297,6 +303,7 @@ object OverrideCompletions: val completionValues = overridables .map(sym => toCompletionValue( + module, sym.denot, 0, // we don't care the position of each completion value from ImplementAll defn, @@ -393,6 +400,7 @@ object OverrideCompletions: ) private def toCompletionValue( + module: GlobalSymbolIndex.Module, sym: SymDenotation, start: Int, defn: TargetDef, @@ -432,6 +440,7 @@ object OverrideCompletions: if sym.is(Method) then printer.defaultMethodSignature( + module, sym.symbol, seenFrom, additionalMods = diff --git a/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala b/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala index 5dee96c6133c..5da6ae5e6c72 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala @@ -2,10 +2,11 @@ package dotty.tools.pc.printer import scala.collection.mutable import scala.meta.internal.jdk.CollectionConverters.* -import scala.meta.pc.reports.ReportContext +import scala.meta.internal.mtags.GlobalSymbolIndex import scala.meta.internal.mtags.KeywordWrapper import scala.meta.pc.SymbolDocumentation import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Denotations.Denotation @@ -217,7 +218,7 @@ class ShortenedTypePrinter( val dealiased = if (tpe.isNamedTupleType) tpe.deepDealiasAndSimplify else tpe toText(dealiased).mkString(defaultWidth, false) - def hoverSymbol(sym: Symbol, info: Type)(using Context): String = + def hoverSymbol(module: GlobalSymbolIndex.Module, sym: Symbol, info: Type)(using Context): String = val typeSymbol = info.typeSymbol def shortTypeString: String = tpe(info) @@ -241,7 +242,7 @@ class ShortenedTypePrinter( case o if typeSymbol.is(Flags.Module) => // enum s"${keyString(o)} $name: $ownerTypeString" case m if m.is(Flags.Method) => - defaultMethodSignature(m, info) + defaultMethodSignature(module, m, info) case _ => val implicitKeyword = if sym.is(Flags.Implicit) then List("implicit") else Nil @@ -258,7 +259,7 @@ class ShortenedTypePrinter( lazy val effectiveOwner = sym.effectiveOwner sym.isType && (effectiveOwner == defn.ScalaPackageClass || effectiveOwner == defn.ScalaPredefModuleClass) - def completionSymbol(denotation: Denotation): String = + def completionSymbol(module: GlobalSymbolIndex.Module, denotation: Denotation): String = val info = denotation.info.widenTermRefExpr val typeSymbol = info.typeSymbol val sym = denotation.symbol @@ -271,7 +272,7 @@ class ShortenedTypePrinter( else if sym.is(Flags.Package) || sym.isClass then " " + fullNameString(sym.effectiveOwner) else if sym.is(Flags.Module) || typeSymbol.is(Flags.Module) then typeEffectiveOwner else if sym.is(Flags.Method) then - defaultMethodSignature(sym, info, onlyMethodParams = true) + defaultMethodSignature(module, sym, info, onlyMethodParams = true) else if sym.isType then info match case TypeAlias(t) => " = " + tpe(t.resultType) @@ -288,6 +289,7 @@ class ShortenedTypePrinter( * ": collection.mutable.Map[A, B]" for no-arg method */ def defaultMethodSignature( + module: GlobalSymbolIndex.Module, gsym: Symbol, gtpe: Type, onlyMethodParams: Boolean = false, @@ -313,7 +315,7 @@ class ShortenedTypePrinter( ) lazy val paramsDocs = - symbolSearch.symbolDocumentation(gsym) match + symbolSearch.symbolDocumentation(module, gsym) match case Some(info) => (info.typeParameters().nn.asScala ++ info.parameters().nn.asScala).toSeq case _ => diff --git a/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala b/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala index 15715143b217..beef31a4cb28 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/utils/InteractiveEnrichments.scala @@ -5,6 +5,7 @@ import java.util.Optional import scala.annotation.tailrec import scala.meta.internal.jdk.CollectionConverters.* import scala.meta.internal.mtags.CommonMtagsEnrichments +import scala.meta.internal.mtags.GlobalSymbolIndex import scala.meta.internal.mtags.KeywordWrapper import scala.meta.pc.ContentType import scala.meta.pc.OffsetParams @@ -260,7 +261,7 @@ object InteractiveEnrichments extends CommonMtagsEnrichments: } extension (search: SymbolSearch) - def symbolDocumentation(symbol: Symbol, contentType: ContentType = ContentType.MARKDOWN)(using + def symbolDocumentation(module: GlobalSymbolIndex.Module, symbol: Symbol, contentType: ContentType = ContentType.MARKDOWN)(using Context ): Option[SymbolDocumentation] = def toSemanticdbSymbol(symbol: Symbol) = @@ -281,9 +282,11 @@ object InteractiveEnrichments extends CommonMtagsEnrichments: if symbol.isLocal then Optional.empty else search.documentation( + module.asString, sym, () => parentSymbols.iterator.map(toSemanticdbSymbol).toList.asJava, contentType, + null // meh ) documentation.nn.toScala end symbolDocumentation diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala index 05037cffca94..277a579ba4ce 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -129,9 +129,6 @@ class CompletionSuite extends BaseCompletionSuite: |isInstanceOf[X0]: Boolean |synchronized[X0](x$0: X0): X0 |toString(): String - |wait(): Unit - |wait(x$0: Long): Unit - |wait(x$0: Long, x$1: Int): Unit |""".stripMargin ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala index c483dc289b0e..60827f1e3590 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala @@ -851,3 +851,78 @@ class HoverTermSuite extends BaseHoverSuite: |""".stripMargin, "val thisIsAVeryLongName: Int".hover ) + + @Test def `intersection_of_selectable-1` = + check( + """|class Record extends Selectable: + | def selectDynamic(name: String): Any = ??? + | + |type A = Record { val aa: Int } + |type B = Record { val bb: String } + |type AB = A & B + | + |val ab: AB = Record().asInstanceOf[AB] + |val ab_a = ab.a@@a + |""".stripMargin, + "val aa: Int".hover + ) + + @Test def `intersection_of_selectable-2` = + check( + """|class Record extends Selectable: + | def selectDynamic(name: String): Any = ??? + | + |type A = Record { val aa: Int } + |type B = Record { val aa: String } + |type AB = A & B + | + |val ab: AB = Record().asInstanceOf[AB] + |val ab_a = ab.a@@a + |""".stripMargin, + "val aa: Int & String".hover + ) + + @Test def `intersection_of_selectable-3` = + check( + """|class Record extends Selectable: + | def selectDynamic(name: String): Any = ??? + | + |type A = Record { val aa: Int } + |type B = Record { val bb: String } + |type AB = A & B + | + |val ab: AB = Record().asInstanceOf[AB] + |val ab_a = ab.b@@b + |""".stripMargin, + "val bb: String".hover + ) + + @Test def `intersection_of_selectable-4` = + check( + """|class Record extends Selectable: + | def selectDynamic(name: String): Any = ??? + | + |type A = Record { val aa: Int } + |type B = Record { val bb: String } + |type C = Record { val cc: Float } + |type AB = A & B + |type ABC = AB & C + | + |val abc: ABC = Record().asInstanceOf[ABC] + |val abc_a = abc.a@@a + |""".stripMargin, + "val aa: Int".hover + ) + + @Test def `intersection_of_selectable-5` = + check( + """|class Record extends Selectable: + | def selectDynamic(name: String): Any = ??? + | + |type AL = List[Int] & Record { val aa: Int } + | + |val al: AL = ???.asInstanceOf[ABC] + |val al_a = al.a@@a + |""".stripMargin, + "val aa: Int".hover + ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala index bb79c19f5823..d9c10080581f 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala @@ -1277,4 +1277,61 @@ class InlayHintsSuite extends BaseInlayHintsSuite { |} |""".stripMargin ) + + @Test def `default-parameter` = + check( + """|object Main { + | def foo(a: Int, b: Int = 2) = a + b + | val x = foo(1) + |} + |""".stripMargin, + """|object Main { + | def foo(a: Int, b: Int = 2)/*: Int<>*/ = a + b + | val x/*: Int<>*/ = foo(/*a = */1) + |} + |""".stripMargin + ) + + @Test def `default-parameter-2` = + check( + """|object Main { + | def foo(a: Int = 10, b: Int = 2) = a + b + | val x = foo(b = 1) + |} + |""".stripMargin, + """|object Main { + | def foo(a: Int = 10, b: Int = 2)/*: Int<>*/ = a + b + | val x/*: Int<>*/ = foo(b = 1) + |} + |""".stripMargin + ) + + @Test def `default-parameter-3` = + check( + """|object Main { + | def foo(a: Int, b: Int = 2, c: Int) = a + b + c + | val x = foo(a = 1, c = 2) + |} + |""".stripMargin, + """|object Main { + | def foo(a: Int, b: Int = 2, c: Int)/*: Int<>*/ = a + b + c + | val x/*: Int<>*/ = foo(a = 1, c = 2) + |} + |""".stripMargin + ) + + @Test def `default-parameter-4` = + check( + """|object Main { + | def foo(a: Int, b: Int = 2, c: Int) = a + b + c + | val x = foo(1, 2, 3) + |} + |""".stripMargin, + """|object Main { + | def foo(a: Int, b: Int = 2, c: Int)/*: Int<>*/ = a + b + c + | val x/*: Int<>*/ = foo(/*a = */1, /*b = */2, /*c = */3) + |} + |""".stripMargin + ) + } diff --git a/project/Build.scala b/project/Build.scala index 08433784c4ae..cd45bdf4ceda 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -52,7 +52,7 @@ object Build { * * Warning: Change of this variable needs to be consulted with `expectedTastyVersion` */ - val referenceVersion = "3.7.2-RC2" + val referenceVersion = "3.7.2" /** Version of the Scala compiler targeted in the current release cycle * Contains a version without RC/SNAPSHOT/NIGHTLY specific suffixes @@ -72,7 +72,7 @@ object Build { * During release candidate cycle incremented by the release officer before publishing a subsequent RC version; * During final, stable release is set exactly to `developedVersion`. */ - val baseVersion = s"$developedVersion-RC1" + val baseVersion = developedVersion /** The version of TASTY that should be emitted, checked in runtime test * For defails on how TASTY version should be set see related discussions: @@ -90,7 +90,7 @@ object Build { * - in release candidate branch is experimental if {patch == 0} * - in stable release is always non-experimetnal */ - val expectedTastyVersion = "28.8-experimental-1" + val expectedTastyVersion = "28.7" checkReleasedTastyVersion() /** Final version of Scala compiler, controlled by environment variables. */ @@ -137,7 +137,7 @@ object Build { val mimaPreviousLTSDottyVersion = "3.3.0" /** Version of Scala CLI to download */ - val scalaCliLauncherVersion = "1.8.4" + val scalaCliLauncherVersion = "1.9.0" /** Version of Coursier to download for initializing the local maven repo of Scala command */ val coursierJarVersion = "2.1.24" diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala index 30a5ac22be0d..24473c874c96 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala @@ -1,13 +1,13 @@ package dotty.tools.scaladoc package tasty -import scala.jdk.CollectionConverters._ - -import scala.quoted._ +import scala.annotation.* +import scala.jdk.CollectionConverters.* +import scala.quoted.* import scala.util.control.NonFatal -import NameNormalizer._ -import SyntheticsSupport._ +import NameNormalizer.* +import SyntheticsSupport.* trait TypesSupport: self: TastyParser => @@ -155,24 +155,25 @@ trait TypesSupport: .reduceLeftOption((acc: SSignature, elem: SSignature) => acc ++ plain(", ").l ++ elem).getOrElse(List()) ++ plain(")").l - def parseRefinedElem(name: String, info: TypeRepr, polyTyped: SSignature = Nil): SSignature = ( info match { + def parseRefinedElem(name: String, info: TypeRepr, polyTyped: SSignature = Nil): SSignature = + val ssig = info match case m: MethodType => { val paramList = getParamList(m) keyword("def ").l ++ plain(name).l ++ polyTyped ++ paramList ++ plain(": ").l ++ inner(m.resType, skipThisTypePrefix) } - case t: PolyType => { + case t: PolyType => val paramBounds = getParamBounds(t) - val parsedMethod = parseRefinedElem(name, t.resType) - if (!paramBounds.isEmpty){ + if !paramBounds.isEmpty then parseRefinedElem(name, t.resType, plain("[").l ++ paramBounds ++ plain("]").l) - } else parseRefinedElem(name, t.resType) - } + else + parseRefinedElem(name, t.resType, polyTyped = Nil) case ByNameType(tp) => keyword("def ").l ++ plain(s"$name: ").l ++ inner(tp, skipThisTypePrefix) case t: TypeBounds => keyword("type ").l ++ plain(name).l ++ inner(t, skipThisTypePrefix) case t: TypeRef => keyword("val ").l ++ plain(s"$name: ").l ++ inner(t, skipThisTypePrefix) case t: TermRef => keyword("val ").l ++ plain(s"$name: ").l ++ inner(t, skipThisTypePrefix) case other => noSupported(s"Not supported type in refinement $info") - } ) ++ plain("; ").l + + ssig ++ plain("; ").l def parsePolyFunction(info: TypeRepr): SSignature = info match { case t: PolyType => @@ -253,6 +254,7 @@ trait TypesSupport: }) ++ plain("]").l case tp @ TypeRef(qual, typeName) => + inline def wrapping = shouldWrapInParens(inner = qual, outer = tp, isLeft = true) qual match { case r: RecursiveThis => tpe(s"this.$typeName").l case ThisType(tr) => @@ -269,23 +271,28 @@ trait TypesSupport: if skipPrefix(qual, elideThis, originalOwner, skipThisTypePrefix) then tpe(tp.typeSymbol) else - val sig = inParens(inner(qual, skipThisTypePrefix)(using skipTypeSuffix = true), shouldWrapInParens(qual, tp, true)) - sig ++ plain(".").l ++ tpe(tp.typeSymbol) + val sig = inParens( + inner(qual, skipThisTypePrefix)(using indent = indent, skipTypeSuffix = true), wrapping) + sig + ++ plain(".").l + ++ tpe(tp.typeSymbol) case t if skipPrefix(t, elideThis, originalOwner, skipThisTypePrefix) => tpe(tp.typeSymbol) case _: TermRef | _: ParamRef => val suffix = if tp.typeSymbol == Symbol.noSymbol then tpe(typeName).l else tpe(tp.typeSymbol) - inner(qual, skipThisTypePrefix)(using skipTypeSuffix = true) ++ plain(".").l ++ suffix + inner(qual, skipThisTypePrefix)(using indent = indent, skipTypeSuffix = true) + ++ plain(".").l + ++ suffix case _ => - val sig = inParens(inner(qual, skipThisTypePrefix), shouldWrapInParens(qual, tp, true)) + val sig = inParens(inner(qual, skipThisTypePrefix), wrapping) sig ++ keyword("#").l ++ tpe(tp.typeSymbol) } case tr @ TermRef(qual, typeName) => val prefix = qual match case t if skipPrefix(t, elideThis, originalOwner, skipThisTypePrefix) => Nil - case tp => inner(tp, skipThisTypePrefix)(using skipTypeSuffix = true) ++ plain(".").l + case tp => inner(tp, skipThisTypePrefix)(using indent = indent, skipTypeSuffix = true) ++ plain(".").l val suffix = if skipTypeSuffix then Nil else List(plain("."), keyword("type")) val typeSig = tr.termSymbol.tree match case vd: ValDef if tr.termSymbol.flags.is(Flags.Module) => @@ -304,9 +311,17 @@ trait TypesSupport: val spaces = " " * (indent) val casesTexts = cases.flatMap { case MatchCase(from, to) => - keyword(caseSpaces + "case ").l ++ inner(from, skipThisTypePrefix) ++ keyword(" => ").l ++ inner(to, skipThisTypePrefix)(using indent = indent + 2) ++ plain("\n").l + keyword(caseSpaces + "case ").l + ++ inner(from, skipThisTypePrefix) + ++ keyword(" => ").l + ++ inner(to, skipThisTypePrefix)(using indent = indent + 2, skipTypeSuffix = skipTypeSuffix) + ++ plain("\n").l case TypeLambda(_, _, MatchCase(from, to)) => - keyword(caseSpaces + "case ").l ++ inner(from, skipThisTypePrefix) ++ keyword(" => ").l ++ inner(to, skipThisTypePrefix)(using indent = indent + 2) ++ plain("\n").l + keyword(caseSpaces + "case ").l + ++ inner(from, skipThisTypePrefix) + ++ keyword(" => ").l + ++ inner(to, skipThisTypePrefix)(using indent = indent + 2, skipTypeSuffix = skipTypeSuffix) + ++ plain("\n").l } inner(sc, skipThisTypePrefix) ++ keyword(" match ").l ++ plain("{\n").l ++ casesTexts ++ plain(spaces + "}").l diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 37e3a3acfdab..4c1453243450 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -324,7 +324,7 @@ object TastyFormat { * compatibility, but remains backwards compatible, with all * preceding `MinorVersion`. */ - final val MinorVersion: Int = 8 + final val MinorVersion: Int = 7 /** Natural Number. The `ExperimentalVersion` allows for * experimentation with changes to TASTy without committing @@ -340,7 +340,7 @@ object TastyFormat { * is able to read final TASTy documents if the file's * `MinorVersion` is strictly less than the current value. */ - final val ExperimentalVersion: Int = 1 + final val ExperimentalVersion: Int = 0 /**This method implements a binary relation (`<:<`) between two TASTy versions. * diff --git a/tests/neg-macros/i22616.check b/tests/neg-macros/i22616.check new file mode 100644 index 000000000000..d830d0c3fe00 --- /dev/null +++ b/tests/neg-macros/i22616.check @@ -0,0 +1,16 @@ +-- [E219] Staging Issue Error: tests/neg-macros/i22616.scala:13:22 ----------------------------------------------------- +13 | case '{ new caseName(${ Expr(name) }) } => Some(caseName(name)) // error // error + | ^^^^^^^^ + | Quoted pattern type variable `caseName` cannot be instantiated. + | If you meant to refer to a class named `caseName`, wrap it in backticks. + | If you meant to introduce a binding, this is not allowed after `new`. You might + | want to use the lower-level `quotes.reflect` API instead. + | Read more about type variables in quoted pattern in the Scala documentation: + | https://docs.scala-lang.org/scala3/guides/macros/quotes.html#type-variables-in-quoted-patterns + | +-- [E006] Not Found Error: tests/neg-macros/i22616.scala:13:67 --------------------------------------------------------- +13 | case '{ new caseName(${ Expr(name) }) } => Some(caseName(name)) // error // error + | ^^^^ + | Not found: name + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-macros/i22616.scala b/tests/neg-macros/i22616.scala new file mode 100644 index 000000000000..bd86716bc30c --- /dev/null +++ b/tests/neg-macros/i22616.scala @@ -0,0 +1,18 @@ +import scala.quoted.* + +final case class caseName(name: String) extends scala.annotation.Annotation +object caseName { + + given FromExpr[caseName] = + new FromExpr[caseName] { + override def unapply(x: Expr[caseName])(using Quotes): Option[caseName] = + val y: Int = 42 + x match { + case '{ caseName(${ Expr(name) }) } => Some(caseName(name)) + // with/without the following line... + case '{ new caseName(${ Expr(name) }) } => Some(caseName(name)) // error // error + case _ => println(x.show); None + } + } + +} diff --git a/tests/neg-macros/i22616b.check b/tests/neg-macros/i22616b.check new file mode 100644 index 000000000000..3c6007276cef --- /dev/null +++ b/tests/neg-macros/i22616b.check @@ -0,0 +1,13 @@ +-- [E007] Type Mismatch Error: tests/neg-macros/i22616b.scala:17:18 ---------------------------------------------------- +17 | case '{ Foo($y: t) } => // error + | ^^^^^ + | Found: t + | Required: String + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg-macros/i22616b.scala:18:19 -------------------------------------------------------- +18 | '{type S = t; ()} // error + | ^ + | Not found: type t + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-macros/i22616b.scala b/tests/neg-macros/i22616b.scala new file mode 100644 index 000000000000..45926f4e0d37 --- /dev/null +++ b/tests/neg-macros/i22616b.scala @@ -0,0 +1,22 @@ +// This test illustrates a current limitation of quoted pattern type variables, +// which has been discussed in https://github.com/scala/scala3/issues/22616#issuecomment-3012534064: +// These type variables do not have bound in general (see `typedQuotedTypeVar`), +// so they might not conform to the expected type. Here, `t` does not conform +// to `String`. + +import scala.quoted.{FromExpr, Expr, Quotes} + +case class Foo(x: String) + +object Macro: + inline def myMacro(): Unit = + ${ myMacroImpl('{Foo("hello")}) } + + def myMacroImpl(x: Expr[Foo])(using Quotes): Expr[Unit] = + x match + case '{ Foo($y: t) } => // error + '{type S = t; ()} // error + case _ => + println("not a foo") + + '{()} diff --git a/tests/neg/19414-desugared.check b/tests/neg/i19414-desugared.check similarity index 91% rename from tests/neg/19414-desugared.check rename to tests/neg/i19414-desugared.check index cc51ee471553..72a3a5eabd37 100644 --- a/tests/neg/19414-desugared.check +++ b/tests/neg/i19414-desugared.check @@ -1,4 +1,4 @@ --- [E172] Type Error: tests/neg/19414-desugared.scala:22:34 ------------------------------------------------------------ +-- [E172] Type Error: tests/neg/i19414-desugared.scala:22:34 ----------------------------------------------------------- 22 | summon[BodySerializer[JsObject]] // error: Ambiguous given instances | ^ |No best given instance of type BodySerializer[JsObject] was found for parameter x of method summon in object Predef. diff --git a/tests/neg/19414-desugared.scala b/tests/neg/i19414-desugared.scala similarity index 100% rename from tests/neg/19414-desugared.scala rename to tests/neg/i19414-desugared.scala diff --git a/tests/neg/19414.check b/tests/neg/i19414.check similarity index 91% rename from tests/neg/19414.check rename to tests/neg/i19414.check index 016e3942c825..10bc939494c6 100644 --- a/tests/neg/19414.check +++ b/tests/neg/i19414.check @@ -1,4 +1,4 @@ --- [E172] Type Error: tests/neg/19414.scala:15:34 ---------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i19414.scala:15:34 --------------------------------------------------------------------- 15 | summon[BodySerializer[JsObject]] // error: Ambiguous given instances | ^ |No best given instance of type BodySerializer[JsObject] was found for parameter x of method summon in object Predef. diff --git a/tests/neg/19414.scala b/tests/neg/i19414.scala similarity index 100% rename from tests/neg/19414.scala rename to tests/neg/i19414.scala diff --git a/tests/neg/i23155a.scala b/tests/neg/i23155a.scala new file mode 100644 index 000000000000..ac48bbf298e0 --- /dev/null +++ b/tests/neg/i23155a.scala @@ -0,0 +1,7 @@ +import scala.NamedTuple +object Unpack_NT { + (1, 2) match { + case Unpack_NT(first, _) => first // error + } + def unapply(e: (Int, Int)): Some[NamedTuple.NamedTuple["x" *: "y" *: EmptyTuple, Int *: Int *: EmptyTuple]] = ??? +} diff --git a/tests/neg/i23155b.scala b/tests/neg/i23155b.scala new file mode 100644 index 000000000000..ef35f4631983 --- /dev/null +++ b/tests/neg/i23155b.scala @@ -0,0 +1,6 @@ +object Unpack_T { + (1, 2) match { + case Unpack_T(first, _) => first // error + } + def unapply(e: (Int, Int)): Some[Int *: Int *: EmptyTuple] = ??? +} diff --git a/tests/neg/magic-offset-header-a.scala b/tests/neg/magic-offset-header-a.scala new file mode 100644 index 000000000000..48267a7853f0 --- /dev/null +++ b/tests/neg/magic-offset-header-a.scala @@ -0,0 +1,2 @@ + +def test1(): Int = "无穷" // error diff --git a/tests/neg/magic-offset-header-a_wrapper.check b/tests/neg/magic-offset-header-a_wrapper.check new file mode 100644 index 000000000000..0ab253c12804 --- /dev/null +++ b/tests/neg/magic-offset-header-a_wrapper.check @@ -0,0 +1,7 @@ +-- [E007] Type Mismatch Error: tests/neg/magic-offset-header-a.scala:2:19 ---------------------------------------------- +2 |def test1(): Int = "无穷" // error + | ^^^^ + | Found: ("无穷" : String) + | Required: Int + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/magic-offset-header-a_wrapper.scala b/tests/neg/magic-offset-header-a_wrapper.scala new file mode 100644 index 000000000000..af4f2b8bf8dd --- /dev/null +++ b/tests/neg/magic-offset-header-a_wrapper.scala @@ -0,0 +1,7 @@ +//> using options -Ymagic-offset-header:TEST_MARKER +val t1 = 1 +val t2 = 2 +val t3 = 3 +///TEST_MARKER:tests/neg/magic-offset-header-a.scala + +def test1(): Int = "无穷" // anypos-error diff --git a/tests/neg/magic-offset-header-b.scala b/tests/neg/magic-offset-header-b.scala new file mode 100644 index 000000000000..aeb569272523 --- /dev/null +++ b/tests/neg/magic-offset-header-b.scala @@ -0,0 +1,2 @@ + +def y: Int = false // error diff --git a/tests/neg/magic-offset-header-b_wrapper.check b/tests/neg/magic-offset-header-b_wrapper.check new file mode 100644 index 000000000000..5d862e5a6b10 --- /dev/null +++ b/tests/neg/magic-offset-header-b_wrapper.check @@ -0,0 +1,14 @@ +-- [E007] Type Mismatch Error: tests/neg/magic-offset-header-b_wrapper.scala:3:13 -------------------------------------- +3 |def x: Int = true // error + | ^^^^ + | Found: (true : Boolean) + | Required: Int + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/magic-offset-header-b.scala:2:13 ---------------------------------------------- +2 |def y: Int = false // error + | ^^^^^ + | Found: (false : Boolean) + | Required: Int + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/magic-offset-header-b_wrapper.scala b/tests/neg/magic-offset-header-b_wrapper.scala new file mode 100644 index 000000000000..ef0e552948d3 --- /dev/null +++ b/tests/neg/magic-offset-header-b_wrapper.scala @@ -0,0 +1,7 @@ +//> using options -Ymagic-offset-header:TEST_MARKER + +def x: Int = true // error + +///TEST_MARKER:tests/neg/magic-offset-header-b.scala + +def y: Int = false // anypos-error diff --git a/tests/neg/magic-offset-header-c.scala b/tests/neg/magic-offset-header-c.scala new file mode 100644 index 000000000000..be3cb333abff --- /dev/null +++ b/tests/neg/magic-offset-header-c.scala @@ -0,0 +1,3 @@ + +def userCode = + val x: String = 0 // error diff --git a/tests/neg/magic-offset-header-c_wrapper.check b/tests/neg/magic-offset-header-c_wrapper.check new file mode 100644 index 000000000000..0c33f5ea0338 --- /dev/null +++ b/tests/neg/magic-offset-header-c_wrapper.check @@ -0,0 +1,7 @@ +-- [E007] Type Mismatch Error: tests/neg/magic-offset-header-c.scala:3:18 ---------------------------------------------- +3 | val x: String = 0 // error + | ^ + | Found: (0 : Int) + | Required: String + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/magic-offset-header-c_wrapper.scala b/tests/neg/magic-offset-header-c_wrapper.scala new file mode 100644 index 000000000000..51804a647fbe --- /dev/null +++ b/tests/neg/magic-offset-header-c_wrapper.scala @@ -0,0 +1,8 @@ +//> using options -Ymagic-offset-header:SOURCE_CODE_START_MARKER + +val generatedCode = 123 + +///SOURCE_CODE_START_MARKER:tests/neg/magic-offset-header-c.scala + +def userCode = + val x: String = 0 // anypos-error diff --git a/tests/neg/magic-offset-header-d_wrapper.check b/tests/neg/magic-offset-header-d_wrapper.check new file mode 100644 index 000000000000..36421e857a1c --- /dev/null +++ b/tests/neg/magic-offset-header-d_wrapper.check @@ -0,0 +1,15 @@ +original source file not found: something_nonexist.scala +-- [E007] Type Mismatch Error: tests/neg/magic-offset-header-d_wrapper.scala:3:20 -------------------------------------- +3 |def test1: String = 0 // error + | ^ + | Found: (0 : Int) + | Required: String + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/magic-offset-header-d_wrapper.scala:5:17 -------------------------------------- +5 |def test2: Int = "0" // error + | ^^^ + | Found: ("0" : String) + | Required: Int + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/magic-offset-header-d_wrapper.scala b/tests/neg/magic-offset-header-d_wrapper.scala new file mode 100644 index 000000000000..85840e84b702 --- /dev/null +++ b/tests/neg/magic-offset-header-d_wrapper.scala @@ -0,0 +1,5 @@ +//> using options -Ymagic-offset-header:SOURCE_CODE_START_MARKER + +def test1: String = 0 // error +///SOURCE_CODE_START_MARKER:something_nonexist.scala +def test2: Int = "0" // error diff --git a/tests/neg/nowarn.check b/tests/neg/nowarn.check deleted file mode 100644 index ff01de1788bd..000000000000 --- a/tests/neg/nowarn.check +++ /dev/null @@ -1,110 +0,0 @@ --- [E002] Syntax Warning: tests/neg/nowarn.scala:11:10 ----------------------------------------------------------------- -11 |def t1a = try 1 // warning (parser) - | ^^^^^ - | A try without catch or finally is equivalent to putting - | its body in a block; no exceptions are handled. - | - | longer explanation available when compiling with `-explain` --- [E002] Syntax Warning: tests/neg/nowarn.scala:25:25 ----------------------------------------------------------------- -25 |@nowarn(o.inl) def t2d = try 1 // two warnings (`inl` is not a compile-time constant) - | ^^^^^ - | A try without catch or finally is equivalent to putting - | its body in a block; no exceptions are handled. - | - | longer explanation available when compiling with `-explain` --- [E002] Syntax Warning: tests/neg/nowarn.scala:33:26 ----------------------------------------------------------------- -33 |@nowarn("id=1") def t4d = try 1 // error and warning (unused nowarn, wrong id) - | ^^^^^ - | A try without catch or finally is equivalent to putting - | its body in a block; no exceptions are handled. - | - | longer explanation available when compiling with `-explain` --- [E002] Syntax Warning: tests/neg/nowarn.scala:35:28 ----------------------------------------------------------------- -35 |@nowarn("verbose") def t5 = try 1 // warning with details - | ^^^^^ - | A try without catch or finally is equivalent to putting - | its body in a block; no exceptions are handled. - |Matching filters for @nowarn or -Wconf: - | - id=E2 - | - name=EmptyCatchAndFinallyBlock - | - | longer explanation available when compiling with `-explain` --- [E129] Potential Issue Warning: tests/neg/nowarn.scala:15:11 -------------------------------------------------------- -15 |def t2 = { 1; 2 } // warning (the invalid nowarn doesn't silence anything) - | ^ - | A pure expression does nothing in statement position - | - | longer explanation available when compiling with `-explain` --- Warning: tests/neg/nowarn.scala:14:8 -------------------------------------------------------------------------------- -14 |@nowarn("wat?") // warning (typer, invalid filter) - | ^^^^^^ - | Invalid message filter - | unknown filter: wat? --- [E129] Potential Issue Warning: tests/neg/nowarn.scala:18:12 -------------------------------------------------------- -18 |def t2a = { 1; 2 } // warning (invalid nowarn doesn't silence) - | ^ - | A pure expression does nothing in statement position - | - | longer explanation available when compiling with `-explain` --- Warning: tests/neg/nowarn.scala:17:8 -------------------------------------------------------------------------------- -17 |@nowarn(t1a.toString) // warning (typer, argument not a compile-time constant) - | ^^^^^^^^^^^^ - | filter needs to be a compile-time constant string --- Warning: tests/neg/nowarn.scala:25:10 ------------------------------------------------------------------------------- -25 |@nowarn(o.inl) def t2d = try 1 // two warnings (`inl` is not a compile-time constant) - | ^^^^^ - | filter needs to be a compile-time constant string --- Deprecation Warning: tests/neg/nowarn.scala:39:10 ------------------------------------------------------------------- -39 |def t6a = f // warning (refchecks, deprecation) - | ^ - | method f is deprecated --- Deprecation Warning: tests/neg/nowarn.scala:42:30 ------------------------------------------------------------------- -42 |@nowarn("msg=fish") def t6d = f // error (unused nowarn), warning (deprecation) - | ^ - | method f is deprecated --- Deprecation Warning: tests/neg/nowarn.scala:49:10 ------------------------------------------------------------------- -49 |def t7c = f // warning (deprecation) - | ^ - | method f is deprecated --- [E092] Pattern Match Unchecked Warning: tests/neg/nowarn.scala:55:7 ------------------------------------------------- -55 | case _: List[Int] => 0 // warning (patmat, unchecked) - | ^ - |the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from Any - | - | longer explanation available when compiling with `-explain` --- Error: tests/neg/nowarn.scala:33:1 ---------------------------------------------------------------------------------- -33 |@nowarn("id=1") def t4d = try 1 // error and warning (unused nowarn, wrong id) - |^^^^^^^^^^^^^^^ - |@nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:42:1 ---------------------------------------------------------------------------------- -42 |@nowarn("msg=fish") def t6d = f // error (unused nowarn), warning (deprecation) - |^^^^^^^^^^^^^^^^^^^ - |@nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:50:5 ---------------------------------------------------------------------------------- -50 | : @nowarn("msg=fish") // error (unused nowarn) - | ^^^^^^^^^^^^^^^^^^^ - | @nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:62:0 ---------------------------------------------------------------------------------- -62 |@nowarn def t9a = { 1: @nowarn; 2 } // error (outer @nowarn is unused) - |^^^^^^^ - |@nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:63:27 --------------------------------------------------------------------------------- -63 |@nowarn def t9b = { 1: Int @nowarn; 2 } // error (inner @nowarn is unused, it covers the type, not the expression) - | ^^^^^^^ - | @nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:68:0 ---------------------------------------------------------------------------------- -68 |@nowarn @ann(f) def t10b = 0 // error (unused nowarn) - |^^^^^^^ - |@nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:69:8 ---------------------------------------------------------------------------------- -69 |@ann(f: @nowarn) def t10c = 0 // error (unused nowarn), should be silent - | ^^^^^^^ - | @nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:72:0 ---------------------------------------------------------------------------------- -72 |@nowarn class I1a { // error (unused nowarn) - |^^^^^^^ - |@nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:77:0 ---------------------------------------------------------------------------------- -77 |@nowarn class I1b { // error (unused nowarn) - |^^^^^^^ - |@nowarn annotation does not suppress any warnings diff --git a/tests/neg/nowarn.scala b/tests/neg/nowarn.scala deleted file mode 100644 index 5b18ab5ccc51..000000000000 --- a/tests/neg/nowarn.scala +++ /dev/null @@ -1,89 +0,0 @@ -//> using options -deprecation -Wunused:nowarn "-Wconf:msg=@nowarn annotation does not suppress any warnings:e" - -import scala.annotation.{ nowarn, Annotation } - -// This test doesn't run with `-Werror`, because once there's an error, later phases are skipped and we would not see -// their warnings. -// Instead, this test runs with `-Wunused:nowarn -Wconf:msg=@nowarn annotation does not suppress any warnings:e`. -// Only "unused nowarn" warnings are reported as errors. Since these warnings are reported at the very end, all other -// phases of the compiler run normally. - -def t1a = try 1 // warning (parser) -@nowarn("msg=try without catch") def t1b = try 1 - -@nowarn("wat?") // warning (typer, invalid filter) -def t2 = { 1; 2 } // warning (the invalid nowarn doesn't silence anything) - -@nowarn(t1a.toString) // warning (typer, argument not a compile-time constant) -def t2a = { 1; 2 } // warning (invalid nowarn doesn't silence) - -object o: - final val const = "msg=try" - inline def inl = "msg=try" - -@nowarn(o.const) def t2c = try 1 // no warning -@nowarn(o.inl) def t2d = try 1 // two warnings (`inl` is not a compile-time constant) - -@nowarn("id=E129") def t3a = { 1; 2 } -@nowarn("name=PureExpressionInStatementPosition") def t3b = { 1; 2 } - -@nowarn("id=E002") def t4a = try 1 -@nowarn("id=E2") def t4b = try 1 -@nowarn("id=2") def t4c = try 1 -@nowarn("id=1") def t4d = try 1 // error and warning (unused nowarn, wrong id) - -@nowarn("verbose") def t5 = try 1 // warning with details - -@deprecated def f = 0 - -def t6a = f // warning (refchecks, deprecation) -@nowarn("cat=deprecation") def t6b = f -@nowarn("msg=deprecated") def t6c = f -@nowarn("msg=fish") def t6d = f // error (unused nowarn), warning (deprecation) -@nowarn("") def t6e = f -@nowarn def t6f = f - -def t7a = f: @nowarn("cat=deprecation") -def t7b = f - : @nowarn("msg=deprecated") -def t7c = f // warning (deprecation) - : @nowarn("msg=fish") // error (unused nowarn) -def t7d = f: @nowarn("") -def t7e = f: @nowarn - -def t8a(x: Any) = x match - case _: List[Int] => 0 // warning (patmat, unchecked) - case _ => 1 - -@nowarn("cat=unchecked") def t8(x: Any) = x match - case _: List[Int] => 0 - case _ => 1 - -@nowarn def t9a = { 1: @nowarn; 2 } // error (outer @nowarn is unused) -@nowarn def t9b = { 1: Int @nowarn; 2 } // error (inner @nowarn is unused, it covers the type, not the expression) - -class ann(a: Any) extends Annotation - -@ann(f) def t10a = 0 // should be a deprecation warning, but currently isn't -@nowarn @ann(f) def t10b = 0 // error (unused nowarn) -@ann(f: @nowarn) def t10c = 0 // error (unused nowarn), should be silent - -def forceCompletionOfI1a = (new I1a).m -@nowarn class I1a { // error (unused nowarn) - @nowarn def m = { 1; 2 } -} - -// completion during type checking -@nowarn class I1b { // error (unused nowarn) - @nowarn def m = { 1; 2 } -} - -@nowarn class I1c { - def m = { 1; 2 } -} - -trait T { - @nowarn val t1 = { 0; 1 } -} - -class K extends T diff --git a/tests/pos-custom-args/captures/tuple-ops.scala b/tests/pos-custom-args/captures/tuple-ops.scala new file mode 100644 index 000000000000..6259328dd9ab --- /dev/null +++ b/tests/pos-custom-args/captures/tuple-ops.scala @@ -0,0 +1,13 @@ +sealed trait Tupp + +case object EmptyTupp extends Tupp +type EmptyTupp = EmptyTupp.type +infix case class `*::`[H, T <: Tupp](h: H, t: T) extends Tupp + +type Union[T <: Tupp] = T match + case EmptyTupp => Nothing + case h *:: t => h | Union[t] + +type Map[T <: Tupp, F[_ <: Union[T]]] <: Tupp = T match + case EmptyTupp => EmptyTupp + case h *:: t => F[h] *:: Map[t, F] \ No newline at end of file diff --git a/tests/pos/i23611.scala b/tests/pos/i23611.scala new file mode 100644 index 000000000000..0fef178b9c32 --- /dev/null +++ b/tests/pos/i23611.scala @@ -0,0 +1,26 @@ +import java.io.{File, IOException} +import java.net.URI +import java.nio.file.{Path, Paths} +import scala.reflect.ClassTag + +trait FileConnectors { + def listPath(path: => Path): ZStream[Any, IOException, Path] + + final def listFile(file: => File): ZStream[Any, IOException, File] = + for { + path <- null.asInstanceOf[ZStream[Any, IOException, Path]] + r <- listPath(path).mapZIO(a => ZIO.attempt(a.toFile).refineToOrDie) + } yield r +} + +sealed trait ZIO[-R, +E, +A] +extension [R, E <: Throwable, A](self: ZIO[R, E, A]) + def refineToOrDie[E1 <: E: ClassTag]: ZIO[R, E1, A] = ??? + +object ZIO: + def attempt[A](code: => A): ZIO[Any, Throwable, A] = ??? + +sealed trait ZStream[-R, +E, +A]: + def map[B](f: A => B): ZStream[R, E, B] = ??? + def flatMap[R1 <: R, E1 >: E, B](f: A => ZStream[R1, E1, B]): ZStream[R1, E1, B] + def mapZIO[R1 <: R, E1 >: E, A1](f: A => ZIO[R1, E1, A1]): ZStream[R1, E1, A1] \ No newline at end of file diff --git a/tests/pos/i23611a.scala b/tests/pos/i23611a.scala new file mode 100644 index 000000000000..fbaf709e2f0e --- /dev/null +++ b/tests/pos/i23611a.scala @@ -0,0 +1,30 @@ +import java.io.{File, IOException} +import java.net.URI +import java.nio.file.{Path, Paths} +import scala.reflect.ClassTag + +trait FileConnectors { + def listPath(path: => Path): ZStream[Any, IOException, Path] + + final def listFile(file: => File): ZStream[Any, IOException, File] = + for { + path <- null.asInstanceOf[ZStream[Any, IOException, Path]] + r <- listPath(path).mapZIO(a => ZIO.attempt(a.toFile).refineToOrDie) + } yield r +} + +sealed abstract class CanFail[-E] +object CanFail: + given [E]: CanFail[E] = ??? + +sealed trait ZIO[-R, +E, +A] +extension [R, E <: Throwable, A](self: ZIO[R, E, A]) + def refineToOrDie[E1 <: E: ClassTag](using CanFail[E]): ZIO[R, E1, A] = ??? + +object ZIO: + def attempt[A](code: => A): ZIO[Any, Throwable, A] = ??? + +sealed trait ZStream[-R, +E, +A]: + def map[B](f: A => B): ZStream[R, E, B] = ??? + def flatMap[R1 <: R, E1 >: E, B](f: A => ZStream[R1, E1, B]): ZStream[R1, E1, B] + def mapZIO[R1 <: R, E1 >: E, A1](f: A => ZIO[R1, E1, A1]): ZStream[R1, E1, A1] \ No newline at end of file diff --git a/tests/pos/i23627.scala b/tests/pos/i23627.scala new file mode 100644 index 000000000000..1480a80c9c00 --- /dev/null +++ b/tests/pos/i23627.scala @@ -0,0 +1,18 @@ +trait TestContainer: + trait TestPath[T]: + type AbsMember + + extension (path: TestPath[?]) + infix def ext(color: path.AbsMember): Unit = ??? + infix def ext(other: Int): Unit = ??? + +object Repro: + val dc2: TestContainer = ??? + import dc2.TestPath + + def transition(path: TestPath[?])(using DummyImplicit): TestPath[?] = ??? + + def test: Unit = + val di: TestPath[?] = ??? + // error + val z1 = transition(di).ext(1) diff --git a/tests/run-macros/i22616c.check b/tests/run-macros/i22616c.check new file mode 100644 index 000000000000..d1918272a8f7 --- /dev/null +++ b/tests/run-macros/i22616c.check @@ -0,0 +1 @@ +_B_ diff --git a/tests/run-macros/i22616c/Macro_4.scala b/tests/run-macros/i22616c/Macro_4.scala new file mode 100644 index 000000000000..02e677db3428 --- /dev/null +++ b/tests/run-macros/i22616c/Macro_4.scala @@ -0,0 +1,11 @@ +import scala.quoted.* + +object Macro: + inline def myMacro[T](): String = + ${ myMacroImpl[T]() } + + def myMacroImpl[T: Type]()(using Quotes): Expr[String] = + import quotes.reflect.* + val myTypeRepr = MyTypeRepr(TypeRepr.of[T]) + val `caseName`(name) = myTypeRepr.requiredAnnotationValue[caseName] + Expr(name) diff --git a/tests/run-macros/i22616c/MyTypeRepr_3.scala b/tests/run-macros/i22616c/MyTypeRepr_3.scala new file mode 100644 index 000000000000..1a35889d403d --- /dev/null +++ b/tests/run-macros/i22616c/MyTypeRepr_3.scala @@ -0,0 +1,33 @@ +import scala.quoted.* + +final class MyTypeRepr(using val quotes: Quotes)(val unwrap: quotes.reflect.TypeRepr) { + import quotes.reflect.* + + def getAnnotation(annotTpe: quotes.reflect.Symbol): Option[quotes.reflect.Term] = + unwrap.typeSymbol.getAnnotation(annotTpe) + + def optionalAnnotation[Annot: Type]: Option[Expr[Annot]] = { + val annotTpe = TypeRepr.of[Annot] + val annotFlags = annotTpe.typeSymbol.flags + + if (annotFlags.is(Flags.Abstract) || annotFlags.is(Flags.Trait)) + report.errorAndAbort(s"Bad annotation type ${annotTpe.show} is abstract") + + this.getAnnotation(annotTpe.typeSymbol) match + case Some(tree) if tree.tpe <:< annotTpe => Some(tree.asExprOf[Annot]) + case _ => None + } + + def requiredAnnotation[Annot: Type]: Expr[Annot] = + optionalAnnotation[Annot].getOrElse(report.errorAndAbort(s"Missing required annotation `${TypeRepr.of[Annot].show}` for `$this`")) + + def optionalAnnotationValue[Annot: {Type, FromExpr}]: Option[Annot] = + optionalAnnotation[Annot].map { expr => + expr.value.getOrElse(report.errorAndAbort(s"Found annotation `${TypeRepr.of[Annot].show}` for `$this`, but are unable to extract Expr.value\n${expr.show}")) + } + + def requiredAnnotationValue[Annot: {Type, FromExpr}]: Annot = { + val expr = requiredAnnotation[Annot] + expr.value.getOrElse(report.errorAndAbort(s"Found annotation `${TypeRepr.of[Annot].show}` for `$this`, but are unable to extract Expr.value\n${expr.show}")) + } +} diff --git a/tests/run-macros/i22616c/SealedTrait3_2.scala b/tests/run-macros/i22616c/SealedTrait3_2.scala new file mode 100644 index 000000000000..9141a9ebd08b --- /dev/null +++ b/tests/run-macros/i22616c/SealedTrait3_2.scala @@ -0,0 +1,8 @@ +sealed trait SealedTrait3[+A, +B] +object SealedTrait3 { + final case class AB1[+B, +A](a: B, b: A) extends SealedTrait3[B, A] + final case class AB2[+C, +D](a: C, b: D) extends SealedTrait3[D, C] + final case class A[+T](a: T) extends SealedTrait3[T, Nothing] + @caseName("_B_") final case class B[+T](b: T) extends SealedTrait3[Nothing, T] + case object Neither extends SealedTrait3[Nothing, Nothing] +} diff --git a/tests/run-macros/i22616c/Test_5.scala b/tests/run-macros/i22616c/Test_5.scala new file mode 100644 index 000000000000..c8d177a5ae1b --- /dev/null +++ b/tests/run-macros/i22616c/Test_5.scala @@ -0,0 +1,2 @@ +@main def Test = + println(Macro.myMacro[SealedTrait3.B[Any]]()) diff --git a/tests/run-macros/i22616c/caseName_1.scala b/tests/run-macros/i22616c/caseName_1.scala new file mode 100644 index 000000000000..c8fee116e2e9 --- /dev/null +++ b/tests/run-macros/i22616c/caseName_1.scala @@ -0,0 +1,14 @@ +import scala.quoted.* + +final case class caseName(name: String) extends scala.annotation.Annotation +object caseName { + // This demonstrates a workaround for issue #22616. + given FromExpr[caseName] = + new FromExpr[caseName] { + override def unapply(x: Expr[caseName])(using Quotes): Option[caseName] = + x match { + case '{ new `caseName`(${ Expr(name) }) } => Some(caseName(name)) + case _ => println(x.show); None + } + } +} diff --git a/tests/run/i23131.scala b/tests/run/i23131.scala new file mode 100644 index 000000000000..2fdee0d9a618 --- /dev/null +++ b/tests/run/i23131.scala @@ -0,0 +1,6 @@ +import scala.NamedTuple +@main +def Test = + Some((name = "Bob")) match { + case Some(name = a) => println(a) + } \ No newline at end of file diff --git a/tests/warn/i23541.check b/tests/warn/i23541.check new file mode 100644 index 000000000000..64ffbd9808d2 --- /dev/null +++ b/tests/warn/i23541.check @@ -0,0 +1,18 @@ +-- [E220] Type Warning: tests/warn/i23541.scala:29:13 ------------------------------------------------------------------ +29 | println(f(using s = "ab")) // warn uses default instead of given // prints "ab" + | ^^^^^^^^^^^^^^^^^ + | Argument for implicit parameter i was supplied using a default argument. + | + | longer explanation available when compiling with `-explain` +-- [E221] Type Warning: tests/warn/i23541.scala:5:17 ------------------------------------------------------------------- +5 | else fun(x - 1)(using p = p + x) // warn recurse uses default (instead of given passed down the stack) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Recursive call used a default argument for parameter q. + | + | longer explanation available when compiling with `-explain` +-- [E221] Type Warning: tests/warn/i23541.scala:9:17 ------------------------------------------------------------------- +9 | else gun(x - 1)(p = p + x) // warn recurse uses default (value not passed down the stack) + | ^^^^^^^^^^^^^^^^^^^^^ + | Recursive call used a default argument for parameter q. + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i23541.scala b/tests/warn/i23541.scala new file mode 100644 index 000000000000..7f374b421ba3 --- /dev/null +++ b/tests/warn/i23541.scala @@ -0,0 +1,30 @@ +//> using options -Wrecurse-with-default + +def fun(x: Int)(using p: Int, q: Int = 0): Int = + if x <= 0 then p * q + else fun(x - 1)(using p = p + x) // warn recurse uses default (instead of given passed down the stack) + +def gun(x: Int)(p: Int, q: Int = 0): Int = + if x <= 0 then p * q + else gun(x - 1)(p = p + x) // warn recurse uses default (value not passed down the stack) + +def nested(using x: Int, y: Int = 42): Int = + def f: Int = nested(using x) // nowarn only self-recursive tailrec is eligible for warning + f + +def f(using s: String, i: Int = 1): String = s * i +def g(using s: String)(using i: Int = 1): String = s * i + +@main def Test = + println(fun(3)(using p = 0, q = 1)) + locally: + given String = "ab" + println(f) // prints "ab" + println(g) // prints "ab" + locally: + println(f(using s = "ab")) // prints "ab" + println(g(using s = "ab")) // prints "ab" + locally: + given Int = 2 + println(f(using s = "ab")) // warn uses default instead of given // prints "ab" + println(g(using s = "ab")) // prints "abab" diff --git a/tests/warn/i23651.scala b/tests/warn/i23651.scala new file mode 100644 index 000000000000..c81ddb4249f8 --- /dev/null +++ b/tests/warn/i23651.scala @@ -0,0 +1,27 @@ +//> using options -deprecation -Wunused:nowarn + +import scala.annotation.nowarn + +@deprecated +class A + +@deprecated +class B + +@nowarn("msg=trait C is deprecated") // warn + // @nowarn annotation does not suppress any warnings +@nowarn("msg=class A is deprecated") +@nowarn("cat=deprecation&msg=class A is deprecated") // warn + // @nowarn annotation does not suppress any warnings but matches a diagnostic +@nowarn("cat=deprecation&msg=class B is deprecated") +trait C1: + def a: A + def b: B + +@nowarn("cat=deprecation&msg=class B is deprecated") +@nowarn("cat=deprecation&msg=class B is deprecated") // warn + // @nowarn annotation does not suppress any warnings but matches a diagnostic +@nowarn("cat=deprecation&msg=class A is deprecated") +trait C2: + def a: A + def b: B diff --git a/tests/warn/nowarn.check b/tests/warn/nowarn.check new file mode 100644 index 000000000000..89ff8d51161b --- /dev/null +++ b/tests/warn/nowarn.check @@ -0,0 +1,110 @@ +-- [E002] Syntax Warning: tests/warn/nowarn.scala:5:10 ----------------------------------------------------------------- +5 |def t1a = try 1 // warn (parser) + | ^^^^^ + | A try without catch or finally is equivalent to putting + | its body in a block; no exceptions are handled. + | + | longer explanation available when compiling with `-explain` +-- [E002] Syntax Warning: tests/warn/nowarn.scala:19:25 ---------------------------------------------------------------- +19 |@nowarn(o.inl) def t2d = try 1 // warn // warn (`inl` is not a compile-time constant) + | ^^^^^ + | A try without catch or finally is equivalent to putting + | its body in a block; no exceptions are handled. + | + | longer explanation available when compiling with `-explain` +-- [E002] Syntax Warning: tests/warn/nowarn.scala:27:26 ---------------------------------------------------------------- +27 |@nowarn("id=1") def t4d = try 1 // warn // warn (unused nowarn, wrong id) + | ^^^^^ + | A try without catch or finally is equivalent to putting + | its body in a block; no exceptions are handled. + | + | longer explanation available when compiling with `-explain` +-- [E002] Syntax Warning: tests/warn/nowarn.scala:29:28 ---------------------------------------------------------------- +29 |@nowarn("verbose") def t5 = try 1 // warn with details + | ^^^^^ + | A try without catch or finally is equivalent to putting + | its body in a block; no exceptions are handled. + |Matching filters for @nowarn or -Wconf: + | - id=E2 + | - name=EmptyCatchAndFinallyBlock + | + | longer explanation available when compiling with `-explain` +-- [E129] Potential Issue Warning: tests/warn/nowarn.scala:9:11 -------------------------------------------------------- +9 |def t2 = { 1; 2 } // warn (the invalid nowarn doesn't silence anything) + | ^ + | A pure expression does nothing in statement position + | + | longer explanation available when compiling with `-explain` +-- Warning: tests/warn/nowarn.scala:8:8 -------------------------------------------------------------------------------- +8 |@nowarn("wat?") // warn (typer, invalid filter) + | ^^^^^^ + | Invalid message filter + | unknown filter: wat? +-- [E129] Potential Issue Warning: tests/warn/nowarn.scala:12:12 ------------------------------------------------------- +12 |def t2a = { 1; 2 } // warn (invalid nowarn doesn't silence) + | ^ + | A pure expression does nothing in statement position + | + | longer explanation available when compiling with `-explain` +-- Warning: tests/warn/nowarn.scala:11:8 ------------------------------------------------------------------------------- +11 |@nowarn(t1a.toString) // warn (typer, argument not a compile-time constant) + | ^^^^^^^^^^^^ + | filter needs to be a compile-time constant string +-- Warning: tests/warn/nowarn.scala:19:10 ------------------------------------------------------------------------------ +19 |@nowarn(o.inl) def t2d = try 1 // warn // warn (`inl` is not a compile-time constant) + | ^^^^^ + | filter needs to be a compile-time constant string +-- Deprecation Warning: tests/warn/nowarn.scala:33:10 ------------------------------------------------------------------ +33 |def t6a = f // warn (refchecks, deprecation) + | ^ + | method f is deprecated +-- Deprecation Warning: tests/warn/nowarn.scala:36:30 ------------------------------------------------------------------ +36 |@nowarn("msg=fish") def t6d = f // warn (unused nowarn) // warn (deprecation) + | ^ + | method f is deprecated +-- Deprecation Warning: tests/warn/nowarn.scala:43:10 ------------------------------------------------------------------ +43 |def t7c = f // warn (deprecation) + | ^ + | method f is deprecated +-- [E092] Pattern Match Unchecked Warning: tests/warn/nowarn.scala:49:7 ------------------------------------------------ +49 | case _: List[Int] => 0 // warn (patmat, unchecked) + | ^ + |the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from Any + | + | longer explanation available when compiling with `-explain` +-- Warning: tests/warn/nowarn.scala:27:1 ------------------------------------------------------------------------------- +27 |@nowarn("id=1") def t4d = try 1 // warn // warn (unused nowarn, wrong id) + |^^^^^^^^^^^^^^^ + |@nowarn annotation does not suppress any warnings +-- Warning: tests/warn/nowarn.scala:36:1 ------------------------------------------------------------------------------- +36 |@nowarn("msg=fish") def t6d = f // warn (unused nowarn) // warn (deprecation) + |^^^^^^^^^^^^^^^^^^^ + |@nowarn annotation does not suppress any warnings +-- Warning: tests/warn/nowarn.scala:44:5 ------------------------------------------------------------------------------- +44 | : @nowarn("msg=fish") // warn (unused nowarn) + | ^^^^^^^^^^^^^^^^^^^ + | @nowarn annotation does not suppress any warnings +-- Warning: tests/warn/nowarn.scala:56:0 ------------------------------------------------------------------------------- +56 |@nowarn def t9a = { 1: @nowarn; 2 } // warn (outer @nowarn is unused) + |^^^^^^^ + |@nowarn annotation does not suppress any warnings but matches a diagnostic +-- Warning: tests/warn/nowarn.scala:57:27 ------------------------------------------------------------------------------ +57 |@nowarn def t9b = { 1: Int @nowarn; 2 } // warn (inner @nowarn is unused, it covers the type, not the expression) + | ^^^^^^^ + | @nowarn annotation does not suppress any warnings +-- Warning: tests/warn/nowarn.scala:62:0 ------------------------------------------------------------------------------- +62 |@nowarn @ann(f) def t10b = 0 // warn (unused nowarn) + |^^^^^^^ + |@nowarn annotation does not suppress any warnings +-- Warning: tests/warn/nowarn.scala:63:8 ------------------------------------------------------------------------------- +63 |@ann(f: @nowarn) def t10c = 0 // warn (unused nowarn), should be silent + | ^^^^^^^ + | @nowarn annotation does not suppress any warnings +-- Warning: tests/warn/nowarn.scala:66:0 ------------------------------------------------------------------------------- +66 |@nowarn class I1a { // warn (unused nowarn) + |^^^^^^^ + |@nowarn annotation does not suppress any warnings but matches a diagnostic +-- Warning: tests/warn/nowarn.scala:71:0 ------------------------------------------------------------------------------- +71 |@nowarn class I1b { // warn (unused nowarn) + |^^^^^^^ + |@nowarn annotation does not suppress any warnings but matches a diagnostic diff --git a/tests/warn/nowarn.scala b/tests/warn/nowarn.scala new file mode 100644 index 000000000000..f6cacdd677d0 --- /dev/null +++ b/tests/warn/nowarn.scala @@ -0,0 +1,83 @@ +//> using options -deprecation -Wunused:nowarn + +import scala.annotation.{nowarn, Annotation} + +def t1a = try 1 // warn (parser) +@nowarn("msg=try without catch") def t1b = try 1 + +@nowarn("wat?") // warn (typer, invalid filter) +def t2 = { 1; 2 } // warn (the invalid nowarn doesn't silence anything) + +@nowarn(t1a.toString) // warn (typer, argument not a compile-time constant) +def t2a = { 1; 2 } // warn (invalid nowarn doesn't silence) + +object o: + final val const = "msg=try" + inline def inl = "msg=try" + +@nowarn(o.const) def t2c = try 1 // no warn +@nowarn(o.inl) def t2d = try 1 // warn // warn (`inl` is not a compile-time constant) + +@nowarn("id=E129") def t3a = { 1; 2 } +@nowarn("name=PureExpressionInStatementPosition") def t3b = { 1; 2 } + +@nowarn("id=E002") def t4a = try 1 +@nowarn("id=E2") def t4b = try 1 +@nowarn("id=2") def t4c = try 1 +@nowarn("id=1") def t4d = try 1 // warn // warn (unused nowarn, wrong id) + +@nowarn("verbose") def t5 = try 1 // warn with details + +@deprecated def f = 0 + +def t6a = f // warn (refchecks, deprecation) +@nowarn("cat=deprecation") def t6b = f +@nowarn("msg=deprecated") def t6c = f +@nowarn("msg=fish") def t6d = f // warn (unused nowarn) // warn (deprecation) +@nowarn("") def t6e = f +@nowarn def t6f = f + +def t7a = f: @nowarn("cat=deprecation") +def t7b = f + : @nowarn("msg=deprecated") +def t7c = f // warn (deprecation) + : @nowarn("msg=fish") // warn (unused nowarn) +def t7d = f: @nowarn("") +def t7e = f: @nowarn + +def t8a(x: Any) = x match + case _: List[Int] => 0 // warn (patmat, unchecked) + case _ => 1 + +@nowarn("cat=unchecked") def t8(x: Any) = x match + case _: List[Int] => 0 + case _ => 1 + +@nowarn def t9a = { 1: @nowarn; 2 } // warn (outer @nowarn is unused) +@nowarn def t9b = { 1: Int @nowarn; 2 } // warn (inner @nowarn is unused, it covers the type, not the expression) + +class ann(a: Any) extends Annotation + +@ann(f) def t10a = 0 // should be a deprecation warning, but currently isn't +@nowarn @ann(f) def t10b = 0 // warn (unused nowarn) +@ann(f: @nowarn) def t10c = 0 // warn (unused nowarn), should be silent + +def forceCompletionOfI1a = (new I1a).m +@nowarn class I1a { // warn (unused nowarn) + @nowarn def m = { 1; 2 } +} + +// completion during type checking +@nowarn class I1b { // warn (unused nowarn) + @nowarn def m = { 1; 2 } +} + +@nowarn class I1c { + def m = { 1; 2 } +} + +trait T { + @nowarn val t1 = { 0; 1 } +} + +class K extends T