diff --git a/.github/workflows/build-msi.yml b/.github/workflows/build-msi.yml index 14838c589d6a..ca6100179776 100644 --- a/.github/workflows/build-msi.yml +++ b/.github/workflows/build-msi.yml @@ -21,8 +21,8 @@ jobs: build: runs-on: windows-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: distribution: 'adopt' java-version: '8' diff --git a/.github/workflows/build-sdk.yml b/.github/workflows/build-sdk.yml index cd111df1a083..8248f10ae824 100644 --- a/.github/workflows/build-sdk.yml +++ b/.github/workflows/build-sdk.yml @@ -55,8 +55,8 @@ jobs: env: DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: distribution: temurin java-version: ${{ inputs.java-version }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c8b5617fc534..c6d6fe539e62 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -824,7 +824,7 @@ jobs: prepareSDK "-x86_64-pc-win32" "dist-win-x86_64" "./dist/win-x86_64/" - name: Download MSI package - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: scala.msi path: . diff --git a/.github/workflows/language-reference.yaml b/.github/workflows/language-reference.yaml index 61a2768c51da..6d6a5fd904db 100644 --- a/.github/workflows/language-reference.yaml +++ b/.github/workflows/language-reference.yaml @@ -31,7 +31,7 @@ jobs: ssh-key: ${{ secrets.DOCS_KEY }} - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 diff --git a/.github/workflows/lts-backport.yaml b/.github/workflows/lts-backport.yaml index 6c8353435b50..9f5bb992440b 100644 --- a/.github/workflows/lts-backport.yaml +++ b/.github/workflows/lts-backport.yaml @@ -16,7 +16,7 @@ jobs: with: fetch-depth: 0 - uses: coursier/cache-action@v6 - - uses: VirtusLab/scala-cli-setup@v1.9.0 + - uses: VirtusLab/scala-cli-setup@v1.9.1 - 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 index 88a8a7913188..62f0fb864c21 100644 --- a/.github/workflows/publish-chocolatey.yml +++ b/.github/workflows/publish-chocolatey.yml @@ -31,7 +31,7 @@ jobs: runs-on: windows-latest steps: - name: Fetch the Chocolatey package from GitHub - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: scala.nupkg - name: Publish the package to Chocolatey diff --git a/.github/workflows/publish-sdkman.yml b/.github/workflows/publish-sdkman.yml index fbbada2a1a70..a3643aa59331 100644 --- a/.github/workflows/publish-sdkman.yml +++ b/.github/workflows/publish-sdkman.yml @@ -46,7 +46,7 @@ jobs: - platform: WINDOWS_64 archive : 'scala3-${{ inputs.version }}-x86_64-pc-win32.zip' steps: - - uses: sdkman/sdkman-release-action@2800d4359ae097a99afea7e0370f0c6e726182a4 + - uses: sdkman/sdkman-release-action@c70225d437d17182d19476702b671513dc8bf048 with: CONSUMER-KEY : ${{ secrets.CONSUMER-KEY }} CONSUMER-TOKEN : ${{ secrets.CONSUMER-TOKEN }} diff --git a/.github/workflows/scaladoc.yaml b/.github/workflows/scaladoc.yaml index d2e3071e765b..6b79e1037e47 100644 --- a/.github/workflows/scaladoc.yaml +++ b/.github/workflows/scaladoc.yaml @@ -31,7 +31,7 @@ jobs: uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -80,7 +80,7 @@ jobs: uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 diff --git a/.github/workflows/stdlib.yaml b/.github/workflows/stdlib.yaml index 47984f8d15ad..8a6c4fba2383 100644 --- a/.github/workflows/stdlib.yaml +++ b/.github/workflows/stdlib.yaml @@ -14,10 +14,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -32,10 +32,10 @@ jobs: ##needs: [scala-library-nonbootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -50,10 +50,10 @@ jobs: needs : [scala3-compiler-nonbootstrapped, scala3-sbt-bridge-nonbootstrapped, scala-library-nonbootstrapped, scala3-library-nonbootstrapped] steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -68,10 +68,10 @@ jobs: ##needs: [scala-library-bootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -86,10 +86,10 @@ jobs: ##needs: [scala3-library-nonbootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -103,10 +103,10 @@ jobs: ##needs: [tasty-core-nonbootstrapped, scala3-library-nonbootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -120,10 +120,10 @@ jobs: ##needs: [scala3-compiler-nonbootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -137,10 +137,10 @@ jobs: ##needs: [scala3-library-bootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -154,10 +154,10 @@ jobs: ##needs: [tasty-core-bootstrapped, scala3-library-bootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -171,10 +171,10 @@ jobs: ##needs: [scala3-compiler-bootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -188,10 +188,10 @@ jobs: ##needs: [scala3-compiler-bootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -205,10 +205,10 @@ jobs: ##needs: [scala3-compiler-bootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -228,10 +228,10 @@ jobs: ##needs: [scala3-sbt-bridge-nonbootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -245,10 +245,10 @@ jobs: ##needs: [scala3-sbt-bridge-bootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -262,10 +262,10 @@ jobs: ##needs: [tasty-core-nonbootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -279,10 +279,10 @@ jobs: ##needs: [tasty-core-bootstrapped] Add when we add support for caching here steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 diff --git a/.github/workflows/test-chocolatey.yml b/.github/workflows/test-chocolatey.yml index e302968b9129..212af8dae50d 100644 --- a/.github/workflows/test-chocolatey.yml +++ b/.github/workflows/test-chocolatey.yml @@ -30,12 +30,12 @@ jobs: test: runs-on: windows-latest steps: - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: temurin java-version: ${{ inputs.java-version }} - name: Download the 'nupkg' from GitHub Artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: scala.nupkg path: ${{ env.CHOCOLATEY-REPOSITORY }} diff --git a/.github/workflows/test-launchers.yml b/.github/workflows/test-launchers.yml index 25bd5a4bf42f..e15f4da3bdfc 100644 --- a/.github/workflows/test-launchers.yml +++ b/.github/workflows/test-launchers.yml @@ -15,7 +15,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' @@ -32,7 +32,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' @@ -51,7 +51,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' @@ -70,7 +70,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' @@ -89,7 +89,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' diff --git a/.github/workflows/test-msi.yml b/.github/workflows/test-msi.yml index 1299c3d55061..4ced29a61cf0 100644 --- a/.github/workflows/test-msi.yml +++ b/.github/workflows/test-msi.yml @@ -24,12 +24,12 @@ jobs: test: runs-on: windows-latest steps: - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: temurin java-version: ${{ inputs.java-version }} - name: Download MSI artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: scala.msi path: . diff --git a/.jvmopts b/.jvmopts index 4df4f826d1db..d8606eba733e 100644 --- a/.jvmopts +++ b/.jvmopts @@ -1,4 +1,4 @@ --Xss1m +-Xss2m -Xms1024m -Xmx8192m -XX:MaxInlineLevel=35 diff --git a/changelogs/3.7.4-RC1.md b/changelogs/3.7.4-RC1.md new file mode 100644 index 000000000000..518addfa3400 --- /dev/null +++ b/changelogs/3.7.4-RC1.md @@ -0,0 +1,128 @@ +# Highlights of the release + +- Bump Scala CLI to v1.9.1 (was v1.9.0) [#23962](https://github.com/scala/scala3/pull/23962) +- Make coverage more similar to the one in Scala 2 [#23722](https://github.com/scala/scala3/pull/23722) + +# Other changes and fixes + +## Context Functions + +- Explain no expansion of ContextFunction0 [#23844](https://github.com/scala/scala3/pull/23844) + +## Experimental: Capture Checking + +- Fix #23737: Update superCallContext to include dummy capture parameters in scope [#23740](https://github.com/scala/scala3/pull/23740) +- Fix separation checking for function results [#23927](https://github.com/scala/scala3/pull/23927) +- Simple enhancement for pattern matching with capturing types [#23524](https://github.com/scala/scala3/pull/23524) +- Don't check bounds in match type cases at CC [#23738](https://github.com/scala/scala3/pull/23738) + +## Experimental: Explicit Nulls + +- Add warnings for inferred flexible types in public methods and fields [#23880](https://github.com/scala/scala3/pull/23880) + +## Exports + +- Refine isEffectivelyFinal to avoid no-owner crash [#23675](https://github.com/scala/scala3/pull/23675) + +## Implicits + +- Fix LiftToAnchors for higher-kinded type applications [#23672](https://github.com/scala/scala3/pull/23672) +- Fix implicit scope liftToAnchors for parameter lower bounds [#23679](https://github.com/scala/scala3/pull/23679) + +## Linting + +- Invent given pattern name in for comprehension [#23121](https://github.com/scala/scala3/pull/23121) +- Unused var message mentions unread or unset [#23719](https://github.com/scala/scala3/pull/23719) +- Lint function arrow intended context function [#23847](https://github.com/scala/scala3/pull/23847) + +## Match Types + +- Fix `derivesFrom` false negative in `provablyDisjointClasses` [#23834](https://github.com/scala/scala3/pull/23834) + + +## Parser + +- Improve message for nested package missing braces [#23816](https://github.com/scala/scala3/pull/23816) +- Fix: allow postfix setters under language.postfixOps [#23775](https://github.com/scala/scala3/pull/23775) + +## Pattern Matching + +- Fix: do not transform `Ident` to `This` in PostTyper anymore [#23899](https://github.com/scala/scala3/pull/23899) +- Call inhabited for AppliedType recursively [#23964](https://github.com/scala/scala3/pull/23964) +- Fix false unreachable case warning [#23800](https://github.com/scala/scala3/pull/23800) +- Add subtype-based fallback in inferPrefixMap and recalculate constraints after application [#23771](https://github.com/scala/scala3/pull/23771) + +## Presentation Compiler + +- Additional completions for using clause [#23647](https://github.com/scala/scala3/pull/23647) +- Completions - do not add `[]` for `... derives TC@@` [#23811](https://github.com/scala/scala3/pull/23811) +- Improve symbol order in completions provided by the presentation compiler [#23888](https://github.com/scala/scala3/pull/23888) +- Porting XRayModeHints [#23891](https://github.com/scala/scala3/pull/23891) +- Go to definition and hover for named args in pattern match [#23956](https://github.com/scala/scala3/pull/23956) + +## Reporting + +- Do not discard amended format when f-interpolator warns [#23697](https://github.com/scala/scala3/pull/23697) +- Mention named givens in double def explainer [#23833](https://github.com/scala/scala3/pull/23833) +- Compute the right span for abstract error messages [#23853](https://github.com/scala/scala3/pull/23853) +- Add quick fix to add .nn [#23598](https://github.com/scala/scala3/pull/23598) +- Add addendum to `private val` parameter variance error message [#23876](https://github.com/scala/scala3/pull/23876) + +## Scaladoc + +- Indicate optional parameters with `= ...` [#23676](https://github.com/scala/scala3/pull/23676) +- Scaladoc Support for Capture & Separation Checking [#23607](https://github.com/scala/scala3/pull/23607) +- Capture Calcuclus: don't eagerly drop caps on parameters [#23759](https://github.com/scala/scala3/pull/23759) + +## SemanticDB + +- Add context parameters to SemanticDB synthetics [#23381](https://github.com/scala/scala3/pull/23381) +- Include synthetic apply in semanticdb [#23629](https://github.com/scala/scala3/pull/23629) + +## Tuples + +- Fix: make vals created in desugaring of n-ary lambdas non-synthetic [#23896](https://github.com/scala/scala3/pull/23896) + +## Typer + +- Prevent crash in SAM conversion with mismatched arity [#23877](https://github.com/scala/scala3/pull/23877) +- Handle assertion error in TyperState [#23665](https://github.com/scala/scala3/pull/23665) +- Correctly require a `ClassTag` when building a multidimensional `Array` [#23902](https://github.com/scala/scala3/pull/23902) +- Make isExactlyNothing and isExactlyAny work for And/OrTypes [#24016](https://github.com/scala/scala3/pull/24016) + + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.7.3..3.7.4-RC1` these are: + +``` + 12 Som Snytt + 11 noti0na1 + 11 Wojciech Mazur + 6 Martin Odersky + 5 Eugene Flesselle + 4 Hamza Remmal + 4 Natsu Kagami + 4 Seyon Sivatharan + 3 Oliver Bračevac + 3 Yoonjae Jeon + 3 dependabot[bot] + 2 Jan Chyb + 2 Katarzyna Marek + 2 Matt Bovel + 1 HarrisL2 + 1 Kacper Korban + 1 Martin Duhem + 1 Paweł Perłakowski + 1 Piotr Chabelski + 1 Tomasz Godzik + 1 Vadim Chelyshov + 1 Yichen Xu + 1 Zieliński Patryk + 1 aherlihy + 1 katrinafyi + 1 vder + 1 zielinsky +``` \ No newline at end of file diff --git a/changelogs/3.7.4-RC2.md b/changelogs/3.7.4-RC2.md new file mode 100644 index 000000000000..9627af49dd99 --- /dev/null +++ b/changelogs/3.7.4-RC2.md @@ -0,0 +1,28 @@ +# Backported chnages + +- Always traverse Inlined.call in linter [#24043](https://github.com/scala/scala3/pull/24043) +- Deduplicate patches before applying them to sources [#24215](https://github.com/scala/scala3/pull/24215) +- Fix compiler crash with `-Ymagic-offset-header` [#24124](https://github.com/scala/scala3/pull/24124) +- Fix completions for named tuples [#24169](https://github.com/scala/scala3/pull/24169) +- Fix java record varargs field accessor [#24172](https://github.com/scala/scala3/pull/24172) +- Fix parameter untupling for named tuples (#23440) [#24152](https://github.com/scala/scala3/pull/24152) +- Fix possible SuspendException thrown when using macros [#24174](https://github.com/scala/scala3/pull/24174) +- Fix rendering of function-type aliases [#24042](https://github.com/scala/scala3/pull/24042) +- Ignore warnings when compiletime.testing is imported [#24036](https://github.com/scala/scala3/pull/24036) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.7.4-RC1..3.7.4-RC2` these are: + +``` + 5 Wojciech Mazur + 2 Li Haoyi + 2 Som Snytt + 2 Tomasz Godzik + 1 Florian3k + 1 Kacper Korban + 1 Oliver Bračevac + 1 aherlihy +``` diff --git a/changelogs/3.7.4-RC3.md b/changelogs/3.7.4-RC3.md new file mode 100644 index 000000000000..c7f0c1654d51 --- /dev/null +++ b/changelogs/3.7.4-RC3.md @@ -0,0 +1,19 @@ +# Backported chnages + +- Lint avoids revisiting Inlined.call [#24277](https://github.com/scala/scala3/pull/24277) +- Register no elements for lint after inlining [#24279](https://github.com/scala/scala3/pull/24279) +- Use enclosing enclosingInlineds for empty call [#24281](https://github.com/scala/scala3/pull/24281) +- Exclude synthetic opaque proxy from lint [#24264](https://github.com/scala/scala3/pull/24264) +- Deprecate `scala_legacy`/`MainGenericRunner`/`scalac -run`/`scalac -repl` for removal [#24267](https://github.com/scala/scala3/pull/24267) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.7.4-RC2..3.7.4-RC4` these are: + +``` + 4 Wojciech Mazur + 2 Piotr Chabelski + 2 Som Snytt +``` diff --git a/changelogs/3.7.4.md b/changelogs/3.7.4.md new file mode 100644 index 000000000000..1a72af451008 --- /dev/null +++ b/changelogs/3.7.4.md @@ -0,0 +1,149 @@ +# Highlights of the release + +- Bump Scala CLI to v1.9.1 (was v1.9.0) [#23962](https://github.com/scala/scala3/pull/23962) +- Make coverage more similar to the one in Scala 2 [#23722](https://github.com/scala/scala3/pull/23722) + +# Deprecations for removal +- Deprecate `scala_legacy`/`MainGenericRunner`/`scalac -run`/`scalac -repl` for removal [#24267](https://github.com/scala/scala3/pull/24267) + +# Other changes and fixes + +## Context Functions + +- Explain no expansion of ContextFunction0 [#23844](https://github.com/scala/scala3/pull/23844) + +## Experimental: Capture Checking + +- Fix #23737: Update superCallContext to include dummy capture parameters in scope [#23740](https://github.com/scala/scala3/pull/23740) +- Fix separation checking for function results [#23927](https://github.com/scala/scala3/pull/23927) +- Simple enhancement for pattern matching with capturing types [#23524](https://github.com/scala/scala3/pull/23524) +- Don't check bounds in match type cases at CC [#23738](https://github.com/scala/scala3/pull/23738) + +## Experimental: Explicit Nulls + +- Add warnings for inferred flexible types in public methods and fields [#23880](https://github.com/scala/scala3/pull/23880) + +## Exports + +- Refine isEffectivelyFinal to avoid no-owner crash [#23675](https://github.com/scala/scala3/pull/23675) + +## Implicits + +- Fix LiftToAnchors for higher-kinded type applications [#23672](https://github.com/scala/scala3/pull/23672) +- Fix implicit scope liftToAnchors for parameter lower bounds [#23679](https://github.com/scala/scala3/pull/23679) + +## Linting + +- Invent given pattern name in for comprehension [#23121](https://github.com/scala/scala3/pull/23121) +- Unused var message mentions unread or unset [#23719](https://github.com/scala/scala3/pull/23719) +- Lint function arrow intended context function [#23847](https://github.com/scala/scala3/pull/23847) +- Always traverse Inlined.call in linter [#24043](https://github.com/scala/scala3/pull/24043) +- Ignore warnings when compiletime.testing is imported [#24036](https://github.com/scala/scala3/pull/24036) +- Lint avoids revisiting Inlined.call [#24277](https://github.com/scala/scala3/pull/24277) +- Register no elements for lint after inlining [#24279](https://github.com/scala/scala3/pull/24279) +- Use enclosing enclosingInlineds for empty call [#24281](https://github.com/scala/scala3/pull/24281) +- Exclude synthetic opaque proxy from lint [#24264](https://github.com/scala/scala3/pull/24264) + +## Match Types + +- Fix `derivesFrom` false negative in `provablyDisjointClasses` [#23834](https://github.com/scala/scala3/pull/23834) + + +## Parser + +- Improve message for nested package missing braces [#23816](https://github.com/scala/scala3/pull/23816) +- Fix: Allow postfix setters under language.postfixOps [#23775](https://github.com/scala/scala3/pull/23775) +- Fix Java record varargs field accessor [#24172](https://github.com/scala/scala3/pull/24172) + +## Pattern Matching + +- Fix: do not transform `Ident` to `This` in PostTyper anymore [#23899](https://github.com/scala/scala3/pull/23899) +- Call inhabited for AppliedType recursively [#23964](https://github.com/scala/scala3/pull/23964) +- Fix false unreachable case warning [#23800](https://github.com/scala/scala3/pull/23800) +- Add subtype-based fallback in inferPrefixMap and recalculate constraints after application [#23771](https://github.com/scala/scala3/pull/23771) + +## Presentation Compiler + +- Additional completions for using clause [#23647](https://github.com/scala/scala3/pull/23647) +- Completions - do not add `[]` for `... derives TC@@` [#23811](https://github.com/scala/scala3/pull/23811) +- Improve symbol order in completions provided by the presentation compiler [#23888](https://github.com/scala/scala3/pull/23888) +- Porting XRayModeHints [#23891](https://github.com/scala/scala3/pull/23891) +- Go to definition and hover for named args in pattern match [#23956](https://github.com/scala/scala3/pull/23956) +- Fix parameter untupling for named tuples (#23440) [#24152](https://github.com/scala/scala3/pull/24152) +- Fix possible SuspendException thrown when using macros [#24174](https://github.com/scala/scala3/pull/24174) +- Fix completions for named tuples [#24169](https://github.com/scala/scala3/pull/24169) + +## Reporting + +- Do not discard amended format when f-interpolator warns [#23697](https://github.com/scala/scala3/pull/23697) +- Mention named givens in double def explainer [#23833](https://github.com/scala/scala3/pull/23833) +- Compute the right span for abstract error messages [#23853](https://github.com/scala/scala3/pull/23853) +- Add quick fix to add .nn [#23598](https://github.com/scala/scala3/pull/23598) +- Add addendum to `private val` parameter variance error message [#23876](https://github.com/scala/scala3/pull/23876) +- Fix compiler crash with `-Ymagic-offset-header` [#24124](https://github.com/scala/scala3/pull/24124) + +## Rewrites + +- Deduplicate patches before applying them to sources [#24215](https://github.com/scala/scala3/pull/24215) + +## Scaladoc + +- Indicate optional parameters with `= ...` [#23676](https://github.com/scala/scala3/pull/23676) +- Scaladoc Support for Capture & Separation Checking [#23607](https://github.com/scala/scala3/pull/23607) +- Capture Calcuclus: don't eagerly drop caps on parameters [#23759](https://github.com/scala/scala3/pull/23759) +- Fix rendering of function-type aliases [#24042](https://github.com/scala/scala3/pull/24042) + +## SemanticDB + +- Add context parameters to SemanticDB synthetics [#23381](https://github.com/scala/scala3/pull/23381) +- Include synthetic apply in semanticdb [#23629](https://github.com/scala/scala3/pull/23629) + +## Tuples + +- Fix: make vals created in desugaring of n-ary lambdas non-synthetic [#23896](https://github.com/scala/scala3/pull/23896) + +## Typer + +- Prevent crash in SAM conversion with mismatched arity [#23877](https://github.com/scala/scala3/pull/23877) +- Handle assertion error in TyperState [#23665](https://github.com/scala/scala3/pull/23665) +- Correctly require a `ClassTag` when building a multidimensional `Array` [#23902](https://github.com/scala/scala3/pull/23902) +- Make isExactlyNothing and isExactlyAny work for And/OrTypes [#24016](https://github.com/scala/scala3/pull/24016) + + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.7.3..3.7.4` these are: + +``` + 23 Wojciech Mazur + 16 Som Snytt + 11 noti0na1 + 6 Martin Odersky + 5 Eugene Flesselle + 4 Hamza Remmal + 4 Natsu Kagami + 4 Oliver Bračevac + 4 Seyon Sivatharan + 3 Piotr Chabelski + 3 Tomasz Godzik + 3 Yoonjae Jeon + 3 dependabot[bot] + 2 Jan Chyb + 2 Kacper Korban + 2 Katarzyna Marek + 2 Li Haoyi + 2 Matt Bovel + 2 aherlihy + 1 Florian3k + 1 HarrisL2 + 1 Martin Duhem + 1 Paweł Perłakowski + 1 Vadim Chelyshov + 1 Yichen Xu + 1 Zieliński Patryk + 1 katrinafyi + 1 vder + 1 zielinsky +``` \ No newline at end of file diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index b575bd2eadf8..df0793b6fb2a 100644 --- a/community-build/src/scala/dotty/communitybuild/projects.scala +++ b/community-build/src/scala/dotty/communitybuild/projects.scala @@ -128,7 +128,7 @@ final case class SbtCommunityProject( case Some(ivyHome) => List(s"-Dsbt.ivy.home=$ivyHome") case _ => Nil extraSbtArgs ++ sbtProps ++ List( - "-sbt-version", "1.10.7", + "-sbt-version", "1.11.5", "-Dsbt.supershell=false", s"-Ddotty.communitybuild.dir=$communitybuildDir", s"--addPluginSbtFile=$sbtPluginFilePath" diff --git a/compiler/src/dotty/tools/MainGenericCompiler.scala b/compiler/src/dotty/tools/MainGenericCompiler.scala index 2c3f6f97e79e..c84052df75de 100644 --- a/compiler/src/dotty/tools/MainGenericCompiler.scala +++ b/compiler/src/dotty/tools/MainGenericCompiler.scala @@ -85,7 +85,7 @@ case class CompileSettings( object MainGenericCompiler { - val classpathSeparator = File.pathSeparator + val classpathSeparator: String = File.pathSeparator @sharable val javaOption = raw"""-J(.*)""".r @sharable val javaPropOption = raw"""-D(.+?)=(.?)""".r @@ -100,6 +100,8 @@ object MainGenericCompiler { case ("-q" | "-quiet") :: tail => process(tail, settings.withQuiet) case "-repl" :: tail => + Console.err.println(s"[warning] The -repl command line option has been deprecated removal in Scala 3.8.0.") + Console.err.println(s"[warning] Please use the 'scala repl' command (Scala CLI) to run the REPL.") process(tail, settings.withCompileMode(CompileMode.Repl)) case "-script" :: targetScript :: tail => process(Nil, settings @@ -114,6 +116,8 @@ object MainGenericCompiler { case "-print-tasty" :: tail => process(tail, settings.withCompileMode(CompileMode.PrintTasty)) case "-run" :: tail => + Console.err.println(s"[warning] The -run command line option has been deprecated removal in Scala 3.8.0.") + Console.err.println(s"[warning] Please use the 'scala' command (Scala CLI) instead.") process(tail, settings.withCompileMode(CompileMode.Run)) case "-colors" :: tail => process(tail, settings.withColors) diff --git a/compiler/src/dotty/tools/MainGenericRunner.scala b/compiler/src/dotty/tools/MainGenericRunner.scala index b32630a5d63b..fcb69c59d840 100644 --- a/compiler/src/dotty/tools/MainGenericRunner.scala +++ b/compiler/src/dotty/tools/MainGenericRunner.scala @@ -96,7 +96,7 @@ case class Settings( object MainGenericRunner { - val classpathSeparator = File.pathSeparator + val classpathSeparator: String = File.pathSeparator def processClasspath(cp: String, tail: List[String]): (List[String], List[String]) = val cpEntries = cp.split(classpathSeparator).toList @@ -273,12 +273,11 @@ object MainGenericRunner { val silenced = sys.props.get("scala.use_legacy_launcher") == Some("true") if !silenced then - Console.err.println(s"[warning] MainGenericRunner class is deprecated since Scala 3.5.0, and Scala CLI features will not work.") - Console.err.println(s"[warning] Please be sure to update to the Scala CLI launcher to use the new features.") + Console.err.println(s"[warning] The MainGenericRunner class and 'legacy_scala' command have been deprecated for removal in Scala 3.8.0.") + Console.err.println(s"[warning] Please be sure to migrate to the scala command (Scala CLI).") if ranByCoursierBootstrap then Console.err.println(s"[warning] It appears that your Coursier-based Scala installation is misconfigured.") Console.err.println(s"[warning] To update to the new Scala CLI runner, please update (coursier, cs) commands first before re-installing scala.") - Console.err.println(s"[warning] Check the Scala 3.5.0 release notes to troubleshoot your installation.") run(settings) match diff --git a/compiler/src/dotty/tools/backend/jvm/CodeGen.scala b/compiler/src/dotty/tools/backend/jvm/CodeGen.scala index be86f704fa41..093d4f997aa7 100644 --- a/compiler/src/dotty/tools/backend/jvm/CodeGen.scala +++ b/compiler/src/dotty/tools/backend/jvm/CodeGen.scala @@ -85,8 +85,8 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)( case ex: InterruptedException => throw ex case ex: CompilationUnit.SuspendException => throw ex case ex: Throwable => - ex.printStackTrace() - report.error(s"Error while emitting ${unit.source}\n${ex.getMessage}", NoSourcePosition) + if !ex.isInstanceOf[TypeError] then ex.printStackTrace() + report.error(s"Error while emitting ${unit.source}\n${ex.getMessage}", cd.sourcePos) def genTastyAndSetAttributes(claszSymbol: Symbol, store: ClassNode): Unit = @@ -152,7 +152,7 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)( new interfaces.AbstractFile { override def name = absfile.name override def path = absfile.path - override def jfile = Optional.ofNullable(absfile.file) + override def jfile: Optional[java.io.File] = Optional.ofNullable(absfile.file) } private def genClass(cd: TypeDef, unit: CompilationUnit): ClassNode = { diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index f82f7956b34b..66044dd9462d 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -171,7 +171,7 @@ class Compiler { val rctx = if ctx.settings.Xsemanticdb.value then ctx.addMode(Mode.ReadPositions) - else if ctx.settings.YcheckInitGlobal.value then + else if ctx.settings.YsafeInitGlobal.value then ctx.addMode(Mode.ReadPositions) else ctx diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 8184f18a8733..2ed3415ebe70 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -67,6 +67,8 @@ object desugar { */ val TrailingForMap: Property.Key[Unit] = Property.StickyKey() + val WasTypedInfix: Property.Key[Unit] = Property.StickyKey() + /** What static check should be applied to a Match? */ enum MatchCheck { case None, Exhaustive, IrrefutablePatDef, IrrefutableGenFrom @@ -1345,7 +1347,7 @@ object desugar { )).withSpan(tree.span) end makePolyFunctionType - /** Invent a name for an anonympus given of type or template `impl`. */ + /** Invent a name for an anonymous given of type or template `impl`. */ def inventGivenName(impl: Tree)(using Context): SimpleName = val str = impl match case impl: Template => @@ -1720,10 +1722,12 @@ object desugar { case _ => Apply(sel, arg :: Nil) - if op.name.isRightAssocOperatorName then + val apply = if op.name.isRightAssocOperatorName then makeOp(right, left, Span(op.span.start, right.span.end)) else makeOp(left, right, Span(left.span.start, op.span.end, op.span.start)) + apply.pushAttachment(WasTypedInfix, ()) + return apply } /** Translate throws type `A throws E1 | ... | En` to @@ -1964,7 +1968,6 @@ object desugar { ValDef(param.name, param.tpt, selector(idx)) .withSpan(param.span) .withAttachment(UntupledParam, ()) - .withFlags(Synthetic) } Function(param :: Nil, Block(vdefs, body)) } @@ -2132,18 +2135,19 @@ object desugar { * that refers to the bound variable for the pattern. Wildcard Binds are * also replaced by Binds with fresh names. */ - def makeIdPat(pat: Tree): (Tree, Ident) = pat match { - case bind @ Bind(name, pat1) => - if name == nme.WILDCARD then - val name = UniqueName.fresh() - (cpy.Bind(pat)(name, pat1).withMods(bind.mods), Ident(name)) - else (pat, Ident(name)) + def makeIdPat(pat: Tree): (Tree, Ident) = pat match + case pat @ Bind(nme.WILDCARD, body) => + val name = + body match + case Typed(Ident(nme.WILDCARD), tpt) if pat.mods.is(Given) => inventGivenName(tpt) + case _ => UniqueName.fresh() + (cpy.Bind(pat)(name, body).withMods(pat.mods), Ident(name)) + case Bind(name, _) => (pat, Ident(name)) case id: Ident if isVarPattern(id) && id.name != nme.WILDCARD => (id, id) case Typed(id: Ident, _) if isVarPattern(id) && id.name != nme.WILDCARD => (pat, id) case _ => val name = UniqueName.fresh() (Bind(name, pat), Ident(name)) - } /** Make a pattern filter: * rhs.withFilter { case pat => true case _ => false } diff --git a/compiler/src/dotty/tools/dotc/ast/Positioned.scala b/compiler/src/dotty/tools/dotc/ast/Positioned.scala index 5b57733eaeb1..1396ee928e7f 100644 --- a/compiler/src/dotty/tools/dotc/ast/Positioned.scala +++ b/compiler/src/dotty/tools/dotc/ast/Positioned.scala @@ -55,11 +55,11 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Src 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 + // This span is in user code + case HasHeader(offset, originalFile) + if span.exists && span.start >= offset => originalFile.atSpan(span.shift(-offset)) - else // Otherwise, return the source position in the wrapper code - source.atSpan(span) + // Otherwise, return the source position in the wrapper code case _ => source.atSpan(span) /** This positioned item, widened to `SrcPos`. Used to make clear we only need the diff --git a/compiler/src/dotty/tools/dotc/cc/SepCheck.scala b/compiler/src/dotty/tools/dotc/cc/SepCheck.scala index be71fe82dc72..6989ef21f081 100644 --- a/compiler/src/dotty/tools/dotc/cc/SepCheck.scala +++ b/compiler/src/dotty/tools/dotc/cc/SepCheck.scala @@ -457,14 +457,16 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: * Also check separation via checkType within individual arguments widened to their * formal paramater types. * - * @param fn the applied function - * @param args the flattened argument lists - * @param app the entire application tree - * @param deps cross argument dependencies: maps argument trees to - * those other arguments that where mentioned by coorresponding - * formal parameters. + * @param fn the applied function + * @param args the flattened argument lists + * @param app the entire application tree + * @param deps cross argument dependencies: maps argument trees to + * those other arguments that where mentioned by coorresponding + * formal parameters. + * @param resultPeaks peaks in the result type that could interfere with the + * hidden sets of formal parameters */ - private def checkApply(fn: Tree, args: List[Tree], app: Tree, deps: collection.Map[Tree, List[Tree]])(using Context): Unit = + private def checkApply(fn: Tree, args: List[Tree], app: Tree, deps: collection.Map[Tree, List[Tree]], resultPeaks: Refs)(using Context): Unit = val (qual, fnCaptures) = methPart(fn) match case Select(qual, _) => (qual, qual.nuType.captureSet) case _ => (fn, CaptureSet.empty) @@ -475,6 +477,7 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: i"""check separate $fn($args), fnCaptures = $fnCaptures, | formalCaptures = ${args.map(arg => CaptureSet(formalCaptures(arg)))}, | actualCaptures = ${args.map(arg => CaptureSet(captures(arg)))}, + | resultPeaks = ${resultPeaks}, | deps = ${deps.toList}""") val parts = qual :: args var reported: SimpleIdentitySet[Tree] = SimpleIdentitySet.empty @@ -519,26 +522,10 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: currentPeaks.hidden ++ argPeaks.hidden) end for - def collectRefs(args: List[Type], res: Type) = - args.foldLeft(argCaptures(res)): (refs, arg) => - refs ++ arg.deepCaptureSet.elems - - /** The deep capture sets of all parameters of this type (if it is a function type) */ - def argCaptures(tpe: Type): Refs = tpe match - case defn.FunctionOf(args, resultType, isContextual) => - collectRefs(args, resultType) - case defn.RefinedFunctionOf(mt) => - collectRefs(mt.paramInfos, mt.resType) - case CapturingType(parent, _) => - argCaptures(parent) - case _ => - emptyRefs - - if !deps(app).isEmpty then - lazy val appPeaks = argCaptures(app.nuType).peaks + if !resultPeaks.isEmpty then lazy val partPeaks = partsWithPeaks.toMap - for arg <- deps(app) do - if arg.needsSepCheck && !partPeaks(arg).hidden.sharedWith(appPeaks).isEmpty then + for arg <- args do + if arg.needsSepCheck && !partPeaks(arg).hidden.sharedWith(resultPeaks).isEmpty then sepApplyError(fn, parts, arg, app) end checkApply @@ -816,10 +803,15 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: * then the dependencies of an application `f(a, b, c)` of type C^{y} is the map * * [ b -> [a] - * , c -> [a, b] - * , f(a, b, c) -> [b]] + * , c -> [a, b] ] + * + * It also returns the interfering peaks of the result of the application. They are the + * peaks of argument captures and deep captures of the result function type, minus the + * those dependent on parameters. For instance, + * if `f` has the type (x: A, y: B, c: C) -> (op: () ->{b} Unit) -> List[() ->{x, y, a} Unit], its interfering + * peaks will be the peaks of `a` and `b`. */ - private def dependencies(fn: Tree, argss: List[List[Tree]], app: Tree)(using Context): collection.Map[Tree, List[Tree]] = + private def dependencies(fn: Tree, argss: List[List[Tree]], app: Tree)(using Context): (collection.Map[Tree, List[Tree]], Refs) = def isFunApply(sym: Symbol) = sym.name == nme.apply && defn.isFunctionClass(sym.owner) val mtpe = @@ -831,23 +823,47 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: val argMap = mtpsWithArgs.toMap val deps = mutable.HashMap[Tree, List[Tree]]().withDefaultValue(Nil) + def argOfDep(dep: Capability): Option[Tree] = + dep.stripReach match + case dep: TermParamRef => + Some(argMap(dep.binder)(dep.paramNum)) + case dep: ThisType if dep.cls == fn.symbol.owner => + val Select(qual, _) = fn: @unchecked // TODO can we use fn instead? + Some(qual) + case _ => + None + def recordDeps(formal: Type, actual: Tree) = - for dep <- formal.captureSet.elems.toList do - val referred = dep.stripReach match - case dep: TermParamRef => - argMap(dep.binder)(dep.paramNum) :: Nil - case dep: ThisType if dep.cls == fn.symbol.owner => - val Select(qual, _) = fn: @unchecked // TODO can we use fn instead? - qual :: Nil - case _ => - Nil + def captures = formal.captureSet + for dep <- captures.elems.toList do + val referred = argOfDep(dep) deps(actual) ++= referred + inline def isLocalRef(x: Capability): Boolean = x.isInstanceOf[TermParamRef] + + def resultArgCaptures(tpe: Type): Refs = + def collectRefs(args: List[Type], res: Type) = + args.foldLeft(resultArgCaptures(res)): (refs, arg) => + refs ++ arg.captureSet.elems + tpe match + case defn.FunctionOf(args, resultType, isContextual) => + collectRefs(args, resultType) + case defn.RefinedFunctionOf(mt) => + collectRefs(mt.paramInfos, mt.resType) + case CapturingType(parent, refs) => + resultArgCaptures(parent) ++ tpe.boxedCaptureSet.elems + case _ => + emptyRefs + for (mt, args) <- mtpsWithArgs; (formal, arg) <- mt.paramInfos.zip(args) do recordDeps(formal, arg) - recordDeps(mtpe.finalResultType, app) + + val resultType = mtpe.finalResultType + val resultCaptures = + (resultArgCaptures(resultType) ++ resultType.deepCaptureSet.elems).filter(!isLocalRef(_)) + val resultPeaks = resultCaptures.peaks capt.println(i"deps for $app = ${deps.toList}") - deps + (deps, resultPeaks) /** Decompose an application into a function prefix and a list of argument lists. @@ -860,7 +876,8 @@ class SepCheck(checker: CheckCaptures.CheckerAPI) extends tpd.TreeTraverser: case TypeApply(fn, args) => recur(fn, argss) // skip type arguments case _ => if argss.nestedExists(_.needsSepCheck) then - checkApply(tree, argss.flatten, app, dependencies(tree, argss, app)) + val (deps, resultPeaks) = dependencies(tree, argss, app) + checkApply(tree, argss.flatten, app, deps, resultPeaks) recur(app, Nil) /** Is `tree` an application of `caps.unsafe.unsafeAssumeSeparate`? */ diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index a2c557ea2987..2d048befe171 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -159,7 +159,7 @@ private sealed trait WarningSettings: self: SettingGroup => val Whelp: Setting[Boolean] = BooleanSetting(WarningSetting, "W", "Print a synopsis of warning options.") - val XfatalWarnings: Setting[Boolean] = BooleanSetting(WarningSetting, "Werror", "Fail the compilation if there are any warnings.", aliases = List("-Xfatal-warnings")) + val Werror: Setting[Boolean] = BooleanSetting(WarningSetting, "Werror", "Fail the compilation if there are any warnings.", aliases = List("-Xfatal-warnings")) val Wall: Setting[Boolean] = BooleanSetting(WarningSetting, "Wall", "Enable all warning settings.") private val WvalueDiscard: Setting[Boolean] = BooleanSetting(WarningSetting, "Wvalue-discard", "Warn when non-Unit expression results are unused.") private val WNonUnitStatement = BooleanSetting(WarningSetting, "Wnonunit-statement", "Warn when block statements are non-Unit expressions.") @@ -168,6 +168,7 @@ private sealed trait WarningSettings: 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 WwrongArrow = BooleanSetting(WarningSetting, "Wwrong-arrow", "Warn if function arrow was used instead of context literal ?=>.") private val Wunused: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( WarningSetting, name = "Wunused", @@ -299,7 +300,7 @@ private sealed trait WarningSettings: def typeParameterShadow(using Context) = allOr("type-parameter-shadow") - val WcheckInit: Setting[Boolean] = BooleanSetting(WarningSetting, "Wsafe-init", "Ensure safe initialization of objects.") + val WsafeInit: Setting[Boolean] = BooleanSetting(WarningSetting, "Wsafe-init", "Ensure safe initialization of objects.") object Whas: def allOr(s: Setting[Boolean])(using Context): Boolean = @@ -311,7 +312,8 @@ private sealed trait WarningSettings: 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) + def wrongArrow(using Context): Boolean = allOr(WwrongArrow) + def safeInit(using Context): Boolean = allOr(WsafeInit) /** -X "Extended" or "Advanced" settings */ private sealed trait XSettings: @@ -453,7 +455,7 @@ private sealed trait YSettings: val YnoKindPolymorphism: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-kind-polymorphism", "Disable kind polymorphism. (This flag has no effect)", deprecation = Deprecation.removed()) val YexplicitNulls: Setting[Boolean] = BooleanSetting(ForkSetting, "Yexplicit-nulls", "Make reference types non-nullable. Nullable types can be expressed with unions: e.g. String|Null.") val YnoFlexibleTypes: Setting[Boolean] = BooleanSetting(ForkSetting, "Yno-flexible-types", "Disable turning nullable Java return types and parameter types into flexible types, which behave like abstract types with a nullable lower bound and non-nullable upper bound.") - val YcheckInitGlobal: Setting[Boolean] = BooleanSetting(ForkSetting, "Ysafe-init-global", "Check safe initialization of global objects.") + val YsafeInitGlobal: Setting[Boolean] = BooleanSetting(ForkSetting, "Ysafe-init-global", "Check safe initialization of global objects.") val YrequireTargetName: Setting[Boolean] = BooleanSetting(ForkSetting, "Yrequire-targetName", "Warn if an operator is defined without a @targetName annotation.") val YrecheckTest: Setting[Boolean] = BooleanSetting(ForkSetting, "Yrecheck-test", "Run basic rechecking (internal test only).") val YccDebug: Setting[Boolean] = BooleanSetting(ForkSetting, "Ycc-debug", "Used in conjunction with captureChecking language import, debug info for captured references.") diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 9de714be8c37..5a0e03330ef2 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -402,8 +402,8 @@ object Contexts { * * - as owner: The primary constructor of the class * - as outer context: The context enclosing the class context - * - as scope: type parameters, the parameter accessors, and - * the context bound companions in the class context, + * - as scope: type parameters, the parameter accessors, + * the dummy capture parameters and the context bound companions in the class context, * * The reasons for this peculiar choice of attributes are as follows: * @@ -420,7 +420,7 @@ object Contexts { def superCallContext: Context = val locals = owner.typeParams ++ owner.asClass.unforcedDecls.filter: sym => - sym.is(ParamAccessor) || sym.isContextBoundCompanion + sym.is(ParamAccessor) || sym.isContextBoundCompanion || sym.isDummyCaptureParam superOrThisCallContext(owner.primaryConstructor, newScopeWith(locals*)) /** The context for the arguments of a this(...) constructor call. diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 8566ad2a6799..6700959eee0e 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -865,9 +865,23 @@ object SymDenotations { * and is the denoting symbol also different from `Null` or `Nothing`? * @note erroneous classes are assumed to derive from all other classes * and all classes derive from them. + * @note may return a false negative when `this.info.isInstanceOf[TempClassInfo]`. */ def derivesFrom(base: Symbol)(using Context): Boolean = false + /** Could `this` derive from `base` now or in the future. + * For concistency with derivesFrom, the info is only forced when this is a ClassDenotation. + * If the info is a TempClassInfo then the baseClassSet may be temporarily approximated as empty. + * This is problematic when stability of `!derivesFrom(base)` is assumed for soundness, + * e.g., in `TypeComparer#provablyDisjointClasses`. + * @note may return a false positive when `this.info.isInstanceOf[TempClassInfo]`. + */ + final def mayDeriveFrom(base: Symbol)(using Context): Boolean = + this.isInstanceOf[ClassDenotation] && (info.isInstanceOf[TempClassInfo] || derivesFrom(base)) + + final def derivesFrom(base: Symbol, defaultIfUnknown: Boolean)(using Context): Boolean = + if defaultIfUnknown/*== true*/ then mayDeriveFrom(base) else derivesFrom(base) + /** Is this a Scala or Java annotation ? */ def isAnnotation(using Context): Boolean = isClass && (derivesFrom(defn.AnnotationClass) || is(JavaAnnotation)) @@ -1220,7 +1234,7 @@ object SymDenotations { || is(Inline, butNot = Deferred) || is(JavaDefinedVal, butNot = Method) || isConstructor - || !owner.isExtensibleClass && !is(Deferred) + || exists && !owner.isExtensibleClass && !is(Deferred) // Deferred symbols can arise through parent refinements under x.modularity. // For them, the overriding relationship reverses anyway, so // being in a final class does not mean the symbol cannot be @@ -2947,7 +2961,7 @@ object SymDenotations { dependent = null } - protected def addDependent(dep: InheritedCache) = { + protected def addDependent(dep: InheritedCache): Unit = { if (dependent == null) dependent = new WeakHashMap dependent.nn.put(dep, ()) } diff --git a/compiler/src/dotty/tools/dotc/core/SymUtils.scala b/compiler/src/dotty/tools/dotc/core/SymUtils.scala index 34908a2df6d6..f22002495bb3 100644 --- a/compiler/src/dotty/tools/dotc/core/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/SymUtils.scala @@ -91,7 +91,7 @@ class SymUtils: self.is(Synthetic) && self.infoOrCompleter.typeSymbol == defn.CBCompanion def isDummyCaptureParam(using Context): Boolean = - self.isAllOf(CaptureParam) && !(self.isClass || self.is(Method)) + self.is(PhantomSymbol) && self.infoOrCompleter.typeSymbol != defn.CBCompanion /** Is this a case class for which a product mirror is generated? * Excluded are value classes, abstract classes and case classes with more than one diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index c8ede8bfdec2..d86c50cbe2e3 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -84,8 +84,8 @@ object Symbols extends SymUtils { ctx.settings.YretainTrees.value || denot.owner.isTerm || // no risk of leaking memory after a run for these denot.isOneOf(InlineOrProxy) || // need to keep inline info - ctx.settings.Whas.checkInit || // initialization check - ctx.settings.YcheckInitGlobal.value + ctx.settings.Whas.safeInit || // initialization check + ctx.settings.YsafeInitGlobal.value /** The last denotation of this symbol */ private var lastDenot: SymDenotation = uninitialized diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 7f8f8a34c171..d420590d0c40 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3131,9 +3131,9 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling * unique value derives from the class. */ case (tp1: SingletonType, tp2) => - !tp1.derivesFrom(tp2.classSymbol) + !tp1.derivesFrom(tp2.classSymbol, defaultIfUnknown = true) case (tp1, tp2: SingletonType) => - !tp2.derivesFrom(tp1.classSymbol) + !tp2.derivesFrom(tp1.classSymbol, defaultIfUnknown = true) /* Now both sides are possibly-parameterized class types `p.C[Ts]` and `q.D[Us]`. * @@ -3189,7 +3189,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling val cls2BaseClassSet = SymDenotations.BaseClassSet(cls2.classDenot.baseClasses) val commonBaseClasses = cls1.classDenot.baseClasses.filter(cls2BaseClassSet.contains(_)) def isAncestorOfOtherBaseClass(cls: ClassSymbol): Boolean = - commonBaseClasses.exists(other => (other ne cls) && other.derivesFrom(cls)) + commonBaseClasses.exists(other => (other ne cls) && other.mayDeriveFrom(cls)) val result = commonBaseClasses.exists { baseClass => !isAncestorOfOtherBaseClass(baseClass) && isBaseTypeWithDisjointArguments(baseClass, innerPending) } @@ -3230,7 +3230,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling .filter(child => child.exists && child != cls) def eitherDerivesFromOther(cls1: Symbol, cls2: Symbol): Boolean = - cls1.derivesFrom(cls2) || cls2.derivesFrom(cls1) + cls1.mayDeriveFrom(cls2) || cls2.mayDeriveFrom(cls1) def smallestNonTraitBase(cls: Symbol): Symbol = val classes = if cls.isClass then cls.asClass.baseClasses else cls.info.classSymbols diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 2e6fa7d94d43..6eafae70c4ee 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -386,6 +386,12 @@ object TypeErasure { case _ => false } + /** Is `tp` of the form `Array^N[T]` where T is generic? */ + def isGenericArrayArg(tp: Type)(using Context): Boolean = tp.dealias match + case defn.ArrayOf(elem) => isGenericArrayArg(elem) + case _ => isGeneric(tp) + end isGenericArrayArg + /** The erased least upper bound of two erased types is computed as follows * - if both argument are arrays of objects, an array of the erased lub of the element types * - if both arguments are arrays of same primitives, an array of this primitive diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index cf03273b4805..f1936ff8cd01 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -805,9 +805,13 @@ object TypeOps: prefixTVar.uncheckedNN case ThisType(tref) if !tref.symbol.isStaticOwner => val symbol = tref.symbol + val compatibleSingleton = singletons.valuesIterator.find(_.underlying.derivesFrom(symbol)) if singletons.contains(symbol) then prefixTVar = singletons(symbol) // e.g. tests/pos/i16785.scala, keep Outer.this prefixTVar.uncheckedNN + else if compatibleSingleton.isDefined then + prefixTVar = compatibleSingleton.get + prefixTVar.uncheckedNN else if symbol.is(Module) then TermRef(this(tref.prefix), symbol.sourceModule) else if (prefixTVar != null) @@ -905,10 +909,11 @@ object TypeOps: } val inferThisMap = new InferPrefixMap - val tvars = tp1.etaExpand match + val prefixInferredTp = inferThisMap(tp1) + val tvars = prefixInferredTp.etaExpand match case eta: TypeLambda => constrained(eta) case _ => Nil - val protoTp1 = inferThisMap.apply(tp1).appliedTo(tvars) + val protoTp1 = prefixInferredTp.appliedTo(tvars) if gadtSyms.nonEmpty then ctx.gadtState.addToConstraint(gadtSyms) diff --git a/compiler/src/dotty/tools/dotc/core/TyperState.scala b/compiler/src/dotty/tools/dotc/core/TyperState.scala index d4345916ba77..c0db3301b8b5 100644 --- a/compiler/src/dotty/tools/dotc/core/TyperState.scala +++ b/compiler/src/dotty/tools/dotc/core/TyperState.scala @@ -28,6 +28,8 @@ object TyperState { opaque type Snapshot = (Constraint, TypeVars, LevelMap) + class BadTyperStateAssertion(msg: String) extends AssertionError(msg) + extension (ts: TyperState) def snapshot()(using Context): Snapshot = (ts.constraint, ts.ownedVars, ts.upLevels) @@ -43,7 +45,7 @@ object TyperState { } class TyperState() { - import TyperState.LevelMap + import TyperState.{LevelMap, BadTyperStateAssertion} private var myId: Int = uninitialized def id: Int = myId @@ -269,8 +271,10 @@ class TyperState() { */ private def includeVar(tvar: TypeVar)(using Context): Unit = val oldState = tvar.owningState.nn.get - assert(oldState == null || !oldState.isCommittable, - i"$this attempted to take ownership of $tvar which is already owned by committable $oldState") + + if oldState != null && oldState.isCommittable then + throw BadTyperStateAssertion( + i"$this attempted to take ownership of $tvar which is already owned by committable $oldState") tvar.owningState = new WeakReference(this) ownedVars += tvar diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 7fac8c818a1a..a094bc77a854 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -244,6 +244,10 @@ object Types extends TypeUtils { def isExactlyNothing(using Context): Boolean = this match { case tp: TypeRef => tp.name == tpnme.Nothing && (tp.symbol eq defn.NothingClass) + case AndType(tp1, tp2) => + tp1.isExactlyNothing || tp2.isExactlyNothing + case OrType(tp1, tp2) => + tp1.isExactlyNothing && tp2.isExactlyNothing case _ => false } @@ -251,6 +255,10 @@ object Types extends TypeUtils { def isExactlyAny(using Context): Boolean = this match { case tp: TypeRef => tp.name == tpnme.Any && (tp.symbol eq defn.AnyClass) + case AndType(tp1, tp2) => + tp1.isExactlyAny && tp2.isExactlyAny + case OrType(tp1, tp2) => + tp1.isExactlyAny || tp2.isExactlyAny case _ => false } @@ -270,7 +278,7 @@ object Types extends TypeUtils { /** True if this type is an instance of the given `cls` or an instance of * a non-bottom subclass of `cls`. */ - final def derivesFrom(cls: Symbol)(using Context): Boolean = { + final def derivesFrom(cls: Symbol, defaultIfUnknown: Boolean = false)(using Context): Boolean = { def isLowerBottomType(tp: Type) = tp.isBottomType && (tp.hasClassSymbol(defn.NothingClass) @@ -278,7 +286,7 @@ object Types extends TypeUtils { def loop(tp: Type): Boolean = try tp match case tp: TypeRef => val sym = tp.symbol - if (sym.isClass) sym.derivesFrom(cls) else loop(tp.superType) + if (sym.isClass) sym.derivesFrom(cls, defaultIfUnknown) else loop(tp.superType) case tp: AppliedType => tp.superType.derivesFrom(cls) case tp: MatchType => diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index 3d1c595877df..7b2c5977fdbd 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -35,6 +35,8 @@ import dotty.tools.dotc.core.Constants import dotty.tools.dotc.core.TypeOps import dotty.tools.dotc.core.StdNames +import java.util.logging.Logger + /** * One of the results of a completion query. * @@ -48,6 +50,8 @@ case class Completion(label: String, description: String, symbols: List[Symbol]) object Completion: + private val logger = Logger.getLogger(this.getClass.getName) + def scopeContext(pos: SourcePosition)(using Context): CompletionResult = val tpdPath = Interactive.pathTo(ctx.compilationUnit.tpdTree, pos.span) val completionContext = Interactive.contextOfPath(tpdPath).withPhase(Phases.typerPhase) @@ -187,6 +191,15 @@ object Completion: Some(qual) case _ => None + private object NamedTupleSelection: + def unapply(path: List[tpd.Tree])(using Context): Option[tpd.Tree] = + path match + case (tpd.Apply(tpd.Apply(tpd.TypeApply(fun, _), List(qual)), _)) :: _ + if fun.symbol.exists && fun.symbol.name == nme.apply && + fun.symbol.owner.exists && fun.symbol.owner == defn.NamedTupleModule.moduleClass => + Some(qual) + case _ => None + /** Inspect `path` to determine the offset where the completion result should be inserted. */ def completionOffset(untpdPath: List[untpd.Tree]): Int = @@ -195,7 +208,7 @@ object Completion: case _ => 0 /** Handle case when cursor position is inside extension method construct. - * The extension method construct is then desugared into methods, and consturct parameters + * The extension method construct is then desugared into methods, and construct parameters * are no longer a part of a typed tree, but instead are prepended to method parameters. * * @param untpdPath The typed or untyped path to the tree that is being completed @@ -242,6 +255,7 @@ object Completion: completer.scopeCompletions.names ++ completer.selectionCompletions(qual) case tpd.Select(qual, _) :: _ => completer.selectionCompletions(qual) case (tree: tpd.ImportOrExport) :: _ => completer.directMemberCompletions(tree.expr) + case NamedTupleSelection(qual) => completer.selectionCompletions(qual) case _ => completer.scopeCompletions.names interactiv.println(i"""completion info with pos = $pos, @@ -591,12 +605,19 @@ object Completion: case _: MethodOrPoly => tpe case _ => ExprType(tpe) + // Try added due to https://github.com/scalameta/metals/issues/7872 def tryApplyingReceiverToExtension(termRef: TermRef): Option[SingleDenotation] = - ctx.typer.tryApplyingExtensionMethod(termRef, qual) - .map { tree => - val tpe = asDefLikeType(tree.typeOpt.dealias) - termRef.denot.asSingleDenotation.mapInfo(_ => tpe) - } + try + ctx.typer.tryApplyingExtensionMethod(termRef, qual) + .map { tree => + val tpe = asDefLikeType(tree.typeOpt.dealias) + termRef.denot.asSingleDenotation.mapInfo(_ => tpe) + } + catch case NonFatal(ex) => + logger.warning( + s"Exception when trying to apply extension method:\n ${ex.getMessage()}\n${ex.getStackTrace().mkString("\n")}" + ) + None def extractMemberExtensionMethods(types: Seq[Type]): Seq[(TermRef, TermName)] = object DenotWithMatchingName: @@ -694,13 +715,17 @@ object Completion: * @param qual The argument to which the implicit conversion should be applied. * @return The set of types after `qual` implicit conversion. */ - private def implicitConversionTargets(qual: tpd.Tree)(using Context): Set[SearchSuccess] = { + private def implicitConversionTargets(qual: tpd.Tree)(using Context): Set[SearchSuccess] = try { val typer = ctx.typer val conversions = new typer.ImplicitSearch(defn.AnyType, qual, pos.span, Set.empty).allImplicits interactiv.println(i"implicit conversion targets considered: ${conversions.toList}%, %") conversions - } + } catch case NonFatal(ex) => + logger.warning( + s"Exception when searching for implicit conversions:\n ${ex.getMessage()}\n${ex.getStackTrace().mkString("\n")}" + ) + Set.empty /** Filter for names that should appear when looking for completions. */ private object completionsFilter extends NameFilter: diff --git a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala index 673874ae2769..40c6667fdd24 100644 --- a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala +++ b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala @@ -234,7 +234,7 @@ class InteractiveDriver(val settings: List[String]) extends Driver { private def classesFromDir(dir: Path, buffer: mutable.ListBuffer[TypeName]): Unit = try Files.walkFileTree(dir, new SimpleFileVisitor[Path] { - override def visitFile(path: Path, attrs: BasicFileAttributes) = { + override def visitFile(path: Path, attrs: BasicFileAttributes): FileVisitResult = { if (!attrs.isDirectory) { val name = path.getFileName.toString if name.endsWith(tastySuffix) then diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index d1164d4742af..ef20eafe367f 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -879,9 +879,14 @@ object JavaParsers { fieldsByName -= name end for + // accessor for record's vararg field (T...) returns array type (T[]) + def adaptVarargsType(tpt: Tree) = tpt match + case PostfixOp(tpt2, Ident(tpnme.raw.STAR)) => arrayOf(tpt2) + case _ => tpt + val accessors = (for (name, (tpt, annots)) <- fieldsByName yield - DefDef(name, List(Nil), tpt, unimplementedExpr) + DefDef(name, List(Nil), adaptVarargsType(tpt), unimplementedExpr) .withMods(Modifiers(Flags.JavaDefined | Flags.Method | Flags.Synthetic)) ).toList diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index f6dd3c2396d4..c9a3f04371a2 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2370,6 +2370,7 @@ object Parsers { * | ForExpr * | [SimpleExpr `.'] id `=' Expr * | PrefixOperator SimpleExpr `=' Expr + * | InfixExpr id [nl] `=' Expr -- only if language.postfixOps is enabled * | SimpleExpr1 ArgumentExprs `=' Expr * | PostfixExpr [Ascription] * | ‘inline’ InfixExpr MatchClause @@ -2525,7 +2526,7 @@ object Parsers { def expr1Rest(t: Tree, location: Location): Tree = if in.token == EQUALS then t match - case Ident(_) | Select(_, _) | Apply(_, _) | PrefixOp(_, _) => + case Ident(_) | Select(_, _) | Apply(_, _) | PrefixOp(_, _) | PostfixOp(_, _) => atSpan(startOffset(t), in.skipToken()) { val loc = if location.inArgs then location else Location.ElseWhere Assign(t, subPart(() => expr(loc))) diff --git a/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala index 3dc73983056a..3604b5b6ebf9 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala @@ -23,9 +23,9 @@ class ConsoleReporter( super.doReport(dia) if ctx.settings.Xprompt.value then dia match - case _: Error => Reporter.displayPrompt(reader, writer) - case _: Warning if ctx.settings.XfatalWarnings.value => Reporter.displayPrompt(reader, writer) - case _ => + case _: Error => Reporter.displayPrompt(reader, writer) + case _: Warning => if ctx.settings.Werror.value then Reporter.displayPrompt(reader, writer) + case _ => } } diff --git a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala index aadac68b37e1..af92d0e0efdf 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala @@ -224,7 +224,7 @@ abstract class Reporter extends interfaces.ReporterResult { incompleteHandler(dia, ctx) def finalizeReporting()(using Context) = - if (hasWarnings && ctx.settings.XfatalWarnings.value) + if (hasWarnings && ctx.settings.Werror.value) report(new Error("No warnings can be incurred under -Werror (or -Xfatal-warnings)", NoSourcePosition)) /** Summary of warnings and errors */ diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 210322841158..86d1ce65c032 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -14,6 +14,7 @@ import printing.Highlighting.* import printing.Formatting import ErrorMessageID.* import ast.Trees +import ast.desugar import config.{Feature, MigrationVersion, ScalaVersion} import transform.patmat.Space import transform.patmat.SpaceEngine @@ -300,6 +301,11 @@ extends NotFoundMsg(MissingIdentID) { class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tree], addenda: => String*)(using Context) extends TypeMismatchMsg(found, expected)(TypeMismatchID): + private val shouldSuggestNN = + if ctx.mode.is(Mode.SafeNulls) && expected.isValueType then + found frozen_<:< OrNull(expected) + else false + def msg(using Context) = // replace constrained TypeParamRefs and their typevars by their bounds where possible // and the bounds are not f-bounds. @@ -360,6 +366,26 @@ class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tre val treeStr = inTree.map(x => s"\nTree:\n\n${x.show}\n").getOrElse("") treeStr + "\n" + super.explain + override def actions(using Context) = + inTree match { + case Some(tree) if shouldSuggestNN => + val content = tree.source.content().slice(tree.srcPos.startPos.start, tree.srcPos.endPos.end).mkString + val replacement = tree match + case a @ Apply(_, _) if !a.hasAttachment(desugar.WasTypedInfix) => + content + ".nn" + case _ @ (Select(_, _) | Ident(_)) => content + ".nn" + case _ => "(" + content + ").nn" + List( + CodeAction(title = """Add .nn""", + description = None, + patches = List( + ActionPatch(tree.srcPos.sourcePos, replacement) + ) + ) + ) + case _ => + List() + } end TypeMismatch class NotAMember(site: Type, val name: Name, selected: String, proto: Type, addendum: => String = "")(using Context) @@ -2099,8 +2125,27 @@ extends NamingMsg(AlreadyDefinedID): i" in ${conflicting.associatedFile}" else if conflicting.owner == owner then "" else i" in ${conflicting.owner}" + def print(tpe: Type): String = + def addParams(tpe: Type): List[String] = tpe match + case tpe: MethodType => + val s = if tpe.isContextualMethod then i"(${tpe.paramInfos}%, %) =>" else "" + s :: addParams(tpe.resType) + case tpe: PolyType => + i"[${tpe.paramNames}%, %] =>" :: addParams(tpe.resType) + case tpe => + i"$tpe" :: Nil + addParams(tpe).mkString(" ") def note = - if owner.is(Method) || conflicting.is(Method) then + if conflicting.is(Given) && name.startsWith("given_") then + i"""| + | + |Provide an explicit, unique name to given definitions, + |since the names assigned to anonymous givens may clash. For example: + | + | given myGiven: ${print(atPhase(typerPhase)(conflicting.info))} // define an instance + | given myGiven @ ${print(atPhase(typerPhase)(conflicting.info))} // as a pattern variable + |""" + else if owner.is(Method) || conflicting.is(Method) then "\n\nNote that overloaded methods must all be defined in the same group of toplevel definitions" else "" if conflicting.isTerm != name.isTermName then @@ -2338,7 +2383,7 @@ class SymbolIsNotAValue(symbol: Symbol)(using Context) extends TypeMsg(SymbolIsN } class DoubleDefinition(decl: Symbol, previousDecl: Symbol, base: Symbol)(using Context) -extends NamingMsg(DoubleDefinitionID) { +extends NamingMsg(DoubleDefinitionID): import Signature.MatchDegree.* private def erasedType: Type = @@ -2400,6 +2445,25 @@ extends NamingMsg(DoubleDefinitionID) { } + details } def explain(using Context) = + def givenAddendum = + def isGivenName(sym: Symbol) = sym.name.startsWith("given_") // Desugar.inventGivenName + def print(tpe: Type): String = + def addParams(tpe: Type): List[String] = tpe match + case tpe: MethodType => + val s = if tpe.isContextualMethod then i"(${tpe.paramInfos}%, %) =>" else "" + s :: addParams(tpe.resType) + case tpe: PolyType => + i"[${tpe.paramNames}%, %] =>" :: addParams(tpe.resType) + case tpe => + i"$tpe" :: Nil + addParams(tpe).mkString(" ") + if decl.is(Given) && previousDecl.is(Given) && isGivenName(decl) && isGivenName(previousDecl) then + i"""| Provide an explicit, unique name to given definitions, + | since the names assigned to anonymous givens may clash. For example: + | + | given myGiven: ${print(atPhase(typerPhase)(decl.info))} + |""" + else "" decl.signature.matchDegree(previousDecl.signature) match case FullMatch => i""" @@ -2413,8 +2477,8 @@ extends NamingMsg(DoubleDefinitionID) { | |In your code the two declarations | - | ${previousDecl.showDcl} - | ${decl.showDcl} + | ${atPhase(typerPhase)(previousDecl.showDcl)} + | ${atPhase(typerPhase)(decl.showDcl)} | |erase to the identical signature | @@ -2422,21 +2486,20 @@ extends NamingMsg(DoubleDefinitionID) { | |so the compiler cannot keep both: the generated bytecode symbols would collide. | - |To fix this error, you need to disambiguate the two definitions. You can either: + |To fix this error, you must disambiguate the two definitions by doing one of the following: | - |1. Rename one of the definitions, or + |1. Rename one of the definitions.$givenAddendum |2. Keep the same names in source but give one definition a distinct - | bytecode-level name via `@targetName` for example: + | bytecode-level name via `@targetName`; for example: | | @targetName("${decl.name.show}_2") - | ${decl.showDcl} + | ${atPhase(typerPhase)(decl.showDcl)} | |Choose the `@targetName` argument carefully: it is the name that will be used |when calling the method externally, so it should be unique and descriptive. - """ + |""" case _ => "" - -} +end DoubleDefinition class ImportedTwice(sel: Name)(using Context) extends SyntaxMsg(ImportedTwiceID) { def msg(using Context) = s"${sel.show} is imported twice on the same import line." @@ -3381,11 +3444,13 @@ extends Message(UnusedSymbolID): object UnusedSymbol: def imports(actions: List[CodeAction])(using Context): UnusedSymbol = UnusedSymbol(i"unused import", actions) def localDefs(using Context): UnusedSymbol = UnusedSymbol(i"unused local definition") + def localVars(using Context): UnusedSymbol = UnusedSymbol(i"local variable was mutated but not read") def explicitParams(sym: Symbol)(using Context): UnusedSymbol = UnusedSymbol(i"unused explicit parameter${paramAddendum(sym)}") def implicitParams(sym: Symbol)(using Context): UnusedSymbol = UnusedSymbol(i"unused implicit parameter${paramAddendum(sym)}") def privateMembers(using Context): UnusedSymbol = UnusedSymbol(i"unused private member") + def privateVars(using Context): UnusedSymbol = UnusedSymbol(i"private variable was mutated but not read") def patVars(using Context): UnusedSymbol = UnusedSymbol(i"unused pattern variable") def unsetLocals(using Context): UnusedSymbol = UnusedSymbol(i"unset local variable, consider using an immutable val instead") diff --git a/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala b/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala index 272db26bdd3c..da6788901f0d 100644 --- a/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala +++ b/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala @@ -43,8 +43,8 @@ object Rewrites { pbuf.filterInPlace(x => !p(x.span)) def apply(cs: Array[Char]): Array[Char] = { - val delta = pbuf.map(_.delta).sum - val patches = pbuf.toList.sortBy(_.span.start) + val patches = pbuf.toList.distinct.sortBy(_.span.start) + val delta = patches.map(_.delta).sum if (patches.nonEmpty) patches.reduceLeft {(p1, p2) => assert(p1.span.end <= p2.span.start, s"overlapping patches in $source: $p1 and $p2") diff --git a/compiler/src/dotty/tools/dotc/sbt/APIUtils.scala b/compiler/src/dotty/tools/dotc/sbt/APIUtils.scala index 4ee1fd0f6b68..42843a89c0ab 100644 --- a/compiler/src/dotty/tools/dotc/sbt/APIUtils.scala +++ b/compiler/src/dotty/tools/dotc/sbt/APIUtils.scala @@ -17,10 +17,10 @@ import xsbti.api.SafeLazy.strict */ object APIUtils { private object Constants { - val PublicAccess = api.Public.create() - val EmptyModifiers = new api.Modifiers(false, false, false, false, false, false, false, false) - val EmptyStructure = api.Structure.of(strict(Array.empty), strict(Array.empty), strict(Array.empty)) - val EmptyType = api.EmptyType.of() + val PublicAccess: api.Public = api.Public.create() + val EmptyModifiers: api.Modifiers = new api.Modifiers(false, false, false, false, false, false, false, false) + val EmptyStructure: api.Structure = api.Structure.of(strict(Array.empty), strict(Array.empty), strict(Array.empty)) + val EmptyType: api.EmptyType = api.EmptyType.of() } import Constants.* diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index 4d915b57df1b..5f3eeff91ff3 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -232,13 +232,13 @@ private class ExtractAPICollector(nonLocalClassSymbols: mutable.HashSet[Symbol]) private object Constants { val emptyStringArray = Array[String]() - val local = api.ThisQualifier.create() - val public = api.Public.create() - val privateLocal = api.Private.create(local) - val protectedLocal = api.Protected.create(local) - val unqualified = api.Unqualified.create() - val thisPath = api.This.create() - val emptyType = api.EmptyType.create() + val local: api.ThisQualifier = api.ThisQualifier.create() + val public: api.Public = api.Public.create() + val privateLocal: api.Private = api.Private.create(local) + val protectedLocal: api.Protected = api.Protected.create(local) + val unqualified: api.Unqualified = api.Unqualified.create() + val thisPath: api.This = api.This.create() + val emptyType: api.EmptyType = api.EmptyType.create() val emptyModifiers = new api.Modifiers(false, false, false, false, false,false, false, false) } diff --git a/compiler/src/dotty/tools/dotc/semanticdb/SyntheticsExtractor.scala b/compiler/src/dotty/tools/dotc/semanticdb/SyntheticsExtractor.scala index af38315a857e..c6cf834c6959 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/SyntheticsExtractor.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/SyntheticsExtractor.scala @@ -72,11 +72,14 @@ class SyntheticsExtractor: range(tree.span, tree.source), s.ApplyTree( tree.fun.toSemanticOriginal, - tree.args.map(_.toSemanticTree) + tree.args.map(_.toSemanticTree), + SymbolInformation.Property.GIVEN.value ) ).toOpt - case tree: Apply if tree.fun.symbol.is(Implicit) => + case tree: Apply + if tree.fun.symbol.is(Implicit) || + (tree.fun.symbol.name == nme.apply && tree.fun.span.isSynthetic) => val pos = range(tree.span, tree.source) s.Synthetic( pos, diff --git a/compiler/src/dotty/tools/dotc/semanticdb/generated/Tree.scala b/compiler/src/dotty/tools/dotc/semanticdb/generated/Tree.scala index 310e9c010826..550b839b67c4 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/generated/Tree.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/generated/Tree.scala @@ -318,10 +318,14 @@ object TreeMessage extends SemanticdbGeneratedMessageCompanion[dotty.tools.dotc // @@protoc_insertion_point(GeneratedMessageCompanion[dotty.tools.dotc.semanticdb.Tree]) } +/** @param properties + * bitmask of SymbolInformation.Property + */ @SerialVersionUID(0L) final case class ApplyTree( function: dotty.tools.dotc.semanticdb.Tree = dotty.tools.dotc.semanticdb.ApplyTree._typemapper_function.toCustom(dotty.tools.dotc.semanticdb.TreeMessage.defaultInstance), - arguments: _root_.scala.Seq[dotty.tools.dotc.semanticdb.Tree] = _root_.scala.Seq.empty + arguments: _root_.scala.Seq[dotty.tools.dotc.semanticdb.Tree] = _root_.scala.Seq.empty, + properties: _root_.scala.Int = 0 ) extends dotty.tools.dotc.semanticdb.Tree.NonEmpty with SemanticdbGeneratedMessage derives CanEqual { @transient @sharable private var __serializedSizeMemoized: _root_.scala.Int = 0 @@ -338,6 +342,13 @@ final case class ApplyTree( val __value = dotty.tools.dotc.semanticdb.ApplyTree._typemapper_arguments.toBase(__item) __size += 1 + SemanticdbOutputStream.computeUInt32SizeNoTag(__value.serializedSize) + __value.serializedSize } + + { + val __value = properties + if (__value != 0) { + __size += SemanticdbOutputStream.computeInt32Size(3, __value) + } + }; __size } override def serializedSize: _root_.scala.Int = { @@ -364,12 +375,19 @@ final case class ApplyTree( _output__.writeUInt32NoTag(__m.serializedSize) __m.writeTo(_output__) }; + { + val __v = properties + if (__v != 0) { + _output__.writeInt32(3, __v) + } + }; } def withFunction(__v: dotty.tools.dotc.semanticdb.Tree): ApplyTree = copy(function = __v) def clearArguments = copy(arguments = _root_.scala.Seq.empty) def addArguments(__vs: dotty.tools.dotc.semanticdb.Tree *): ApplyTree = addAllArguments(__vs) def addAllArguments(__vs: Iterable[dotty.tools.dotc.semanticdb.Tree]): ApplyTree = copy(arguments = arguments ++ __vs) def withArguments(__v: _root_.scala.Seq[dotty.tools.dotc.semanticdb.Tree]): ApplyTree = copy(arguments = __v) + def withProperties(__v: _root_.scala.Int): ApplyTree = copy(properties = __v) @@ -382,6 +400,7 @@ object ApplyTree extends SemanticdbGeneratedMessageCompanion[dotty.tools.dotc.s def parseFrom(`_input__`: SemanticdbInputStream): dotty.tools.dotc.semanticdb.ApplyTree = { var __function: _root_.scala.Option[dotty.tools.dotc.semanticdb.TreeMessage] = _root_.scala.None val __arguments: _root_.scala.collection.immutable.VectorBuilder[dotty.tools.dotc.semanticdb.Tree] = new _root_.scala.collection.immutable.VectorBuilder[dotty.tools.dotc.semanticdb.Tree] + var __properties: _root_.scala.Int = 0 var _done__ = false while (!_done__) { val _tag__ = _input__.readTag() @@ -391,12 +410,15 @@ object ApplyTree extends SemanticdbGeneratedMessageCompanion[dotty.tools.dotc.s __function = _root_.scala.Some(__function.fold(LiteParser.readMessage[dotty.tools.dotc.semanticdb.TreeMessage](_input__))(LiteParser.readMessage(_input__, _))) case 18 => __arguments += dotty.tools.dotc.semanticdb.ApplyTree._typemapper_arguments.toCustom(LiteParser.readMessage[dotty.tools.dotc.semanticdb.TreeMessage](_input__)) + case 24 => + __properties = _input__.readInt32() case tag => _input__.skipField(tag) } } dotty.tools.dotc.semanticdb.ApplyTree( function = dotty.tools.dotc.semanticdb.ApplyTree._typemapper_function.toCustom(__function.getOrElse(dotty.tools.dotc.semanticdb.TreeMessage.defaultInstance)), - arguments = __arguments.result() + arguments = __arguments.result(), + properties = __properties ) } @@ -407,20 +429,24 @@ object ApplyTree extends SemanticdbGeneratedMessageCompanion[dotty.tools.dotc.s lazy val defaultInstance = dotty.tools.dotc.semanticdb.ApplyTree( function = dotty.tools.dotc.semanticdb.ApplyTree._typemapper_function.toCustom(dotty.tools.dotc.semanticdb.TreeMessage.defaultInstance), - arguments = _root_.scala.Seq.empty + arguments = _root_.scala.Seq.empty, + properties = 0 ) final val FUNCTION_FIELD_NUMBER = 1 final val ARGUMENTS_FIELD_NUMBER = 2 + final val PROPERTIES_FIELD_NUMBER = 3 @transient @sharable private[semanticdb] val _typemapper_function: SemanticdbTypeMapper[dotty.tools.dotc.semanticdb.TreeMessage, dotty.tools.dotc.semanticdb.Tree] = implicitly[SemanticdbTypeMapper[dotty.tools.dotc.semanticdb.TreeMessage, dotty.tools.dotc.semanticdb.Tree]] @transient @sharable private[semanticdb] val _typemapper_arguments: SemanticdbTypeMapper[dotty.tools.dotc.semanticdb.TreeMessage, dotty.tools.dotc.semanticdb.Tree] = implicitly[SemanticdbTypeMapper[dotty.tools.dotc.semanticdb.TreeMessage, dotty.tools.dotc.semanticdb.Tree]] def of( function: dotty.tools.dotc.semanticdb.Tree, - arguments: _root_.scala.Seq[dotty.tools.dotc.semanticdb.Tree] + arguments: _root_.scala.Seq[dotty.tools.dotc.semanticdb.Tree], + properties: _root_.scala.Int ): _root_.dotty.tools.dotc.semanticdb.ApplyTree = _root_.dotty.tools.dotc.semanticdb.ApplyTree( function, - arguments + arguments, + properties ) // @@protoc_insertion_point(GeneratedMessageCompanion[dotty.tools.dotc.semanticdb.ApplyTree]) } diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index a37dbce5bc2e..b4098ef24221 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -1,27 +1,26 @@ -package dotty.tools.dotc.transform - -import dotty.tools.dotc.ast.desugar.{ForArtifact, PatternVar} -import dotty.tools.dotc.ast.tpd.* -import dotty.tools.dotc.ast.untpd, untpd.ImportSelector -import dotty.tools.dotc.config.ScalaSettings -import dotty.tools.dotc.core.Contexts.* -import dotty.tools.dotc.core.Flags.* -import dotty.tools.dotc.core.Names.{Name, SimpleName, DerivedName, TermName, termName} -import dotty.tools.dotc.core.NameOps.{isAnonymousFunctionName, isReplWrapperName, setterName} -import dotty.tools.dotc.core.NameKinds.{ - BodyRetainerName, ContextBoundParamName, ContextFunctionParamName, DefaultGetterName, WildcardParamName} -import dotty.tools.dotc.core.StdNames.nme -import dotty.tools.dotc.core.Symbols.{ClassSymbol, NoSymbol, Symbol, defn, isDeprecated, requiredClass, requiredModule} -import dotty.tools.dotc.core.Types.* -import dotty.tools.dotc.report -import dotty.tools.dotc.reporting.{CodeAction, UnusedSymbol} -import dotty.tools.dotc.rewrites.Rewrites -import dotty.tools.dotc.transform.MegaPhase.MiniPhase -import dotty.tools.dotc.typer.{ImportInfo, Typer} -import dotty.tools.dotc.typer.Deriving.OriginalTypeClass -import dotty.tools.dotc.util.{Property, Spans, SrcPos}, Spans.Span -import dotty.tools.dotc.util.Chars.{isLineBreakChar, isWhitespace} -import dotty.tools.dotc.util.chaining.* +package dotty.tools.dotc +package transform + +import ast.*, desugar.{ForArtifact, PatternVar}, tpd.*, untpd.ImportSelector +import config.ScalaSettings +import core.*, Contexts.*, Decorators.*, Flags.* +import Names.{Name, SimpleName, DerivedName, TermName, termName} +import NameKinds.{BodyRetainerName, ContextBoundParamName, ContextFunctionParamName, DefaultGetterName, WildcardParamName} +import NameOps.{isAnonymousFunctionName, isReplWrapperName, setterName} +import Scopes.newScope +import StdNames.nme +import Symbols.{ClassSymbol, NoSymbol, Symbol, defn, isDeprecated, requiredClass, requiredModule} +import Types.* +import reporting.{CodeAction, UnusedSymbol} +import rewrites.Rewrites + +import MegaPhase.MiniPhase +import typer.{ImportInfo, Typer} +import typer.Deriving.OriginalTypeClass +import typer.Implicits.{ContextualImplicits, RenamedImplicitRef} +import util.{Property, Spans, SrcPos}, Spans.Span +import util.Chars.{isLineBreakChar, isWhitespace} +import util.chaining.* import java.util.IdentityHashMap @@ -56,17 +55,16 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha if tree.symbol.exists then // if in an inline expansion, resolve at summonInline (synthetic pos) or in an enclosing call site val resolving = - refInfos.inlined.isEmpty - || tree.srcPos.isZeroExtentSynthetic - || refInfos.inlined.exists(_.sourcePos.contains(tree.srcPos.sourcePos)) - if resolving && !ignoreTree(tree) then + tree.srcPos.isUserCode(using if tree.hasAttachment(InlinedParameter) then ctx.outer else ctx) + || tree.srcPos.isZeroExtentSynthetic // take as summonInline + if !ignoreTree(tree) then def loopOverPrefixes(prefix: Type, depth: Int): Unit = if depth < 10 && prefix.exists && !prefix.classSymbol.isEffectiveRoot then - resolveUsage(prefix.classSymbol, nme.NO_NAME, NoPrefix) + resolveUsage(prefix.classSymbol, nme.NO_NAME, NoPrefix, imports = resolving) loopOverPrefixes(prefix.normalizedPrefix, depth + 1) if tree.srcPos.isZeroExtentSynthetic then loopOverPrefixes(tree.typeOpt.normalizedPrefix, depth = 0) - resolveUsage(tree.symbol, tree.name, tree.typeOpt.importPrefix.skipPackageObject) + resolveUsage(tree.symbol, tree.name, tree.typeOpt.importPrefix.skipPackageObject, imports = resolving) else if tree.hasType then resolveUsage(tree.tpe.classSymbol, tree.name, tree.tpe.importPrefix.skipPackageObject) refInfos.isAssignment = false @@ -143,17 +141,18 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha tree override def prepareForInlined(tree: Inlined)(using Context): Context = - refInfos.inlined.push(tree.call.srcPos) + if tree.inlinedFromOuterScope then + tree.expansion.putAttachment(InlinedParameter, ()) ctx override def transformInlined(tree: Inlined)(using Context): tree.type = - //transformAllDeep(tree.expansion) // traverse expansion with nonempty inlined stack to avoid registering defs - val _ = refInfos.inlined.pop() - if !tree.call.isEmpty && phaseMode.eq(PhaseMode.Aggregate) then - transformAllDeep(tree.call) + if !tree.call.isEmpty then + if !refInfos.calls.containsKey(tree.call) then + refInfos.calls.put(tree.call, ()) + transformAllDeep(tree.call) tree override def prepareForBind(tree: Bind)(using Context): Context = - refInfos.register(tree) + register(tree) ctx /* cf QuotePattern override def transformBind(tree: Bind)(using Context): tree.type = @@ -171,7 +170,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha override def prepareForValDef(tree: ValDef)(using Context): Context = if !tree.symbol.is(Deferred) && tree.rhs.symbol != defn.Predef_undefined then - refInfos.register(tree) + register(tree) relax(tree.rhs, tree.tpt.tpe) ctx override def transformValDef(tree: ValDef)(using Context): tree.type = @@ -195,7 +194,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha if tree.symbol.is(Inline) then refInfos.inliners += 1 else if !tree.symbol.is(Deferred) && tree.rhs.symbol != defn.Predef_undefined then - refInfos.register(tree) + register(tree) relax(tree.rhs, tree.tpt.tpe) ctx override def transformDefDef(tree: DefDef)(using Context): tree.type = @@ -209,14 +208,13 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha override def transformTypeDef(tree: TypeDef)(using Context): tree.type = traverseAnnotations(tree.symbol) if !tree.symbol.is(Param) then // type parameter to do? - refInfos.register(tree) + register(tree) tree override def transformOther(tree: Tree)(using Context): tree.type = tree match case imp: Import => - if phaseMode eq PhaseMode.Aggregate then - refInfos.register(imp) + register(imp) transformAllDeep(imp.expr) for selector <- imp.selectors do if selector.isGiven then @@ -297,8 +295,11 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha * e.g., in `scala.Int`, `scala` is in scope for typer, but here we reverse-engineer the attribution. * For Select, lint does not look up `.scala` (so top-level syms look like magic) but records `scala.Int`. * For Ident, look-up finds the root import as usual. A competing import is OK because higher precedence. + * + * The `imports` flag is whether an identifier can mark an import as used: the flag is false + * for inlined code, except for `summonInline` (and related constructs) which are resolved at inlining. */ - def resolveUsage(sym0: Symbol, name: Name, prefix: Type)(using Context): Unit = + def resolveUsage(sym0: Symbol, name: Name, prefix: Type, imports: Boolean = true)(using Context): Unit = import PrecedenceLevels.* val sym = sym0.userSymbol @@ -355,7 +356,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha while !done && ctxs.hasNext do val cur = ctxs.next() if cur.owner.userSymbol == sym && !sym.is(Package) then - enclosed = true // found enclosing definition, don't register the reference + enclosed = true // found enclosing definition, don't record the reference if isLocal then if cur.owner eq sym.owner then done = true // for local def, just checking that it is not enclosing @@ -392,9 +393,15 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha // record usage and possibly an import if !enclosed then refInfos.addRef(sym) - if candidate != NoContext && candidate.isImportContext && importer != null then + if imports && candidate != NoContext && candidate.isImportContext && importer != null then refInfos.sels.put(importer, ()) end resolveUsage + + /** Register new element for warnings only at typer */ + def register(tree: Tree)(using Context): Unit = + if phaseMode eq PhaseMode.Aggregate then + refInfos.register(tree) + end CheckUnused object CheckUnused: @@ -419,6 +426,9 @@ object CheckUnused: /** Tree is LHS of Assign. */ val AssignmentTarget = Property.StickyKey[Unit] + /** Tree is an inlined parameter. */ + val InlinedParameter = Property.StickyKey[Unit] + class PostTyper extends CheckUnused(PhaseMode.Aggregate, "PostTyper") class PostInlining extends CheckUnused(PhaseMode.Report, "PostInlining") @@ -430,14 +440,16 @@ object CheckUnused: val asss = mutable.Set.empty[Symbol] // targets of assignment val skip = mutable.Set.empty[Symbol] // methods to skip (don't warn about their params) val nowarn = mutable.Set.empty[Symbol] // marked @nowarn + val calls = new IdentityHashMap[Tree, Unit] // inlined call already seen val imps = new IdentityHashMap[Import, Unit] // imports val sels = new IdentityHashMap[ImportSelector, Unit] // matched selectors - def register(tree: Tree)(using Context): Unit = if inlined.isEmpty then + def register(tree: Tree)(using Context): Unit = if tree.srcPos.isUserCode then tree match case imp: Import => if inliners == 0 && languageImport(imp.expr).isEmpty && !imp.isGeneratedByEnum + && !imp.isCompiletimeTesting && !ctx.owner.name.isReplWrapperName then imps.put(imp, ()) @@ -461,7 +473,6 @@ object CheckUnused: if tree.symbol ne NoSymbol then defs.addOne((tree.symbol, tree.srcPos)) // TODO is this a code path - val inlined = Stack.empty[SrcPos] // enclosing call.srcPos of inlined code (expansions) var inliners = 0 // depth of inline def (not inlined yet) // instead of refs.addOne, use addRef to distinguish a read from a write to var @@ -498,8 +509,11 @@ object CheckUnused: val warnings = ArrayBuilder.make[MessageInfo] def warnAt(pos: SrcPos)(msg: UnusedSymbol, origin: String = ""): Unit = warnings.addOne((msg, pos, origin)) val infos = refInfos - //println(infos.defs.mkString("DEFS\n", "\n", "\n---")) - //println(infos.refs.mkString("REFS\n", "\n", "\n---")) + + // non-local sym was target of assignment or has a sibling setter that was referenced + def isMutated(sym: Symbol): Boolean = + infos.asss(sym) + || infos.refs(sym.owner.info.member(sym.name.asTermName.setterName).symbol) def checkUnassigned(sym: Symbol, pos: SrcPos) = if sym.isLocalToBlock then @@ -509,8 +523,7 @@ object CheckUnused: && sym.is(Mutable) && (sym.is(Private) || sym.isEffectivelyPrivate) && !sym.isSetter // tracks sym.underlyingSymbol sibling getter, check setter below - && !infos.asss(sym) - && !infos.refs(sym.owner.info.member(sym.name.asTermName.setterName).symbol) + && !isMutated(sym) then warnAt(pos)(UnusedSymbol.unsetPrivates) @@ -526,7 +539,10 @@ object CheckUnused: ) && !infos.nowarn(sym) then - warnAt(pos)(UnusedSymbol.privateMembers) + if sym.is(Mutable) && isMutated(sym) then + warnAt(pos)(UnusedSymbol.privateVars) + else + warnAt(pos)(UnusedSymbol.privateMembers) def checkParam(sym: Symbol, pos: SrcPos) = val m = sym.owner @@ -626,10 +642,13 @@ object CheckUnused: def checkLocal(sym: Symbol, pos: SrcPos) = if ctx.settings.WunusedHas.locals - && !sym.is(InlineProxy) + && !sym.isOneOf(InlineProxy | Synthetic) && !sym.isCanEqual then - warnAt(pos)(UnusedSymbol.localDefs) + if sym.is(Mutable) && infos.asss(sym) then + warnAt(pos)(UnusedSymbol.localVars) + else + warnAt(pos)(UnusedSymbol.localDefs) def checkPatvars() = // convert the one non-synthetic span so all are comparable; filter NoSpan below @@ -988,9 +1007,17 @@ object CheckUnused: then imp.expr.tpe.allMembers.exists(_.symbol.isCanEqual) else imp.expr.tpe.member(sel.name.toTermName).hasAltWith(_.symbol.isCanEqual) + /** No mechanism for detection yet. */ + def isCompiletimeTesting: Boolean = + imp.expr.symbol == defn.CompiletimeTestingPackage//.moduleClass + extension (pos: SrcPos) def isZeroExtentSynthetic: Boolean = pos.span.isSynthetic && pos.span.isZeroExtent def isSynthetic: Boolean = pos.span.isSynthetic && pos.span.exists + def isUserCode(using Context): Boolean = + val inlineds = enclosingInlineds // per current context + inlineds.isEmpty + || inlineds.exists(_.srcPos.sourcePos.contains(pos.sourcePos)) // include intermediate inlinings or quotes extension [A <: AnyRef](arr: Array[A]) // returns `until` if not satisfied diff --git a/compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala b/compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala index 491f3d3d2572..449402f17fce 100644 --- a/compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala +++ b/compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala @@ -145,6 +145,31 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: val span = pos.span.toSynthetic invokeCall(statementId, span) + private def transformApplyArgs(trees: List[Tree])(using Context): List[Tree] = + if allConstArgs(trees) then trees else transform(trees) + + private def transformInnerApply(tree: Tree)(using Context): Tree = tree match + case a: Apply if a.fun.symbol == defn.StringContextModule_apply => + a + case a: Apply => + cpy.Apply(a)( + transformInnerApply(a.fun), + transformApplyArgs(a.args) + ) + case a: TypeApply => + cpy.TypeApply(a)( + transformInnerApply(a.fun), + transformApplyArgs(a.args) + ) + case s: Select => + cpy.Select(s)(transformInnerApply(s.qualifier), s.name) + case i: (Ident | This) => i + case t: Typed => + cpy.Typed(t)(transformInnerApply(t.expr), t.tpt) + case other => transform(other) + + private def allConstArgs(args: List[Tree]) = + args.forall(arg => arg.isInstanceOf[Literal] || arg.isInstanceOf[Ident]) /** * Tries to instrument an `Apply`. * These "tryInstrument" methods are useful to tweak the generation of coverage instrumentation, @@ -158,10 +183,12 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: // Create a call to Invoker.invoked(coverageDirectory, newStatementId) val coverageCall = createInvokeCall(tree, tree.sourcePos) - if needsLift(tree) then - // Transform args and fun, i.e. instrument them if needed (and if possible) - val app = cpy.Apply(tree)(transform(tree.fun), tree.args.map(transform)) + // Transform args and fun, i.e. instrument them if needed (and if possible) + val app = + if tree.fun.symbol eq defn.throwMethod then tree + else cpy.Apply(tree)(transformInnerApply(tree.fun), transformApplyArgs(tree.args)) + if needsLift(tree) then // Lifts the arguments. Note that if only one argument needs to be lifted, we lift them all. // Also, tree.fun can be lifted too. // See LiftCoverage for the internal working of this lifting. @@ -171,11 +198,10 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: InstrumentedParts(liftedDefs.toList, coverageCall, liftedApp) else // Instrument without lifting - val transformed = cpy.Apply(tree)(transform(tree.fun), transform(tree.args)) - InstrumentedParts.singleExpr(coverageCall, transformed) + InstrumentedParts.singleExpr(coverageCall, app) else // Transform recursively but don't instrument the tree itself - val transformed = cpy.Apply(tree)(transform(tree.fun), transform(tree.args)) + val transformed = cpy.Apply(tree)(transformInnerApply(tree.fun), transform(tree.args)) InstrumentedParts.notCovered(transformed) private def tryInstrument(tree: Ident)(using Context): InstrumentedParts = @@ -187,9 +213,14 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: else InstrumentedParts.notCovered(tree) + private def tryInstrument(tree: Literal)(using Context): InstrumentedParts = + val coverageCall = createInvokeCall(tree, tree.sourcePos) + InstrumentedParts.singleExpr(coverageCall, tree) + private def tryInstrument(tree: Select)(using Context): InstrumentedParts = val sym = tree.symbol - val transformed = cpy.Select(tree)(transform(tree.qualifier), tree.name) + val qual = transform(tree.qualifier).ensureConforms(tree.qualifier.tpe) + val transformed = cpy.Select(tree)(qual, tree.name) if canInstrumentParameterless(sym) then // call to a parameterless method val coverageCall = createInvokeCall(tree, tree.sourcePos) @@ -202,6 +233,7 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: tree match case t: Apply => tryInstrument(t) case t: Ident => tryInstrument(t) + case t: Literal => tryInstrument(t) case t: Select => tryInstrument(t) case _ => InstrumentedParts.notCovered(transform(tree)) @@ -223,10 +255,14 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: inContext(transformCtx(tree)) { // necessary to position inlined code properly tree match // simple cases - case tree: (Import | Export | Literal | This | Super | New) => tree + case tree: (Import | Export | This | Super | New) => tree case tree if tree.isEmpty || tree.isType => tree // empty Thicket, Ident (referring to a type), TypeTree, ... case tree if !tree.span.exists || tree.span.isZeroExtent => tree // no meaningful position + case tree: Literal => + val rest = tryInstrument(tree).toTree + rest + // identifier case tree: Ident => tryInstrument(tree).toTree @@ -280,6 +316,9 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: case tree: CaseDef => transformCaseDef(tree) + case tree: ValDef if tree.symbol.is(Inline) => + tree // transforming inline vals will result in `inline value must be pure` errors + case tree: ValDef => // only transform the rhs val rhs = transform(tree.rhs) @@ -323,13 +362,13 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: ) case tree: Inlined => - // Ideally, tree.call would provide precise information about the inlined call, - // and we would use this information for the coverage report. - // But PostTyper simplifies tree.call, so we can't report the actual method that was inlined. - // In any case, the subtrees need to be repositioned right now, otherwise the - // coverage statement will point to a potentially unreachable source file. - val dropped = Inlines.dropInlined(tree) // drop and reposition - transform(dropped) // transform the content of the Inlined + // Inlined code contents might come from another file (or project), + // which means that we cannot clearly designate which part of the inlined code + // was run using the API we are given. + // At best, we can show that the Inlined tree itself was reached. + // Additionally, Scala 2's coverage ignores macro calls entirely, + // so let's do that here too, also for regular inlined calls. + tree // For everything else just recurse and transform case _ => @@ -559,15 +598,14 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: private def isCompilerIntrinsicMethod(sym: Symbol)(using Context): Boolean = val owner = sym.maybeOwner owner.exists && ( - owner.eq(defn.AnyClass) || - owner.isPrimitiveValueClass || + (owner.eq(defn.AnyClass) && (sym == defn.Any_asInstanceOf || sym == defn.Any_isInstanceOf)) || owner.maybeOwner == defn.CompiletimePackageClass ) object InstrumentCoverage: val name: String = "instrumentCoverage" val description: String = "instrument code for coverage checking" - val ExcludeMethodFlags: FlagSet = Synthetic | Artifact | Erased + val ExcludeMethodFlags: FlagSet = Artifact | Erased /** * An instrumented Tree, in 3 parts. diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 8bf88a0027c4..10aeea40b13b 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -14,9 +14,11 @@ import Flags.*, Constants.* import Decorators.* import NameKinds.{PatMatStdBinderName, PatMatAltsName, PatMatResultName} import config.Printers.patmatch +import config.Feature import reporting.* import ast.* import util.Property.* +import cc.{CapturingType, Capabilities} import scala.annotation.tailrec import scala.collection.mutable @@ -427,8 +429,11 @@ object PatternMatcher { && !hasExplicitTypeArgs(extractor) case _ => false } + val castTp = if Feature.ccEnabled + then CapturingType(tpt.tpe, scrutinee.termRef.singletonCaptureSet) + else tpt.tpe TestPlan(TypeTest(tpt, isTrusted), scrutinee, tree.span, - letAbstract(ref(scrutinee).cast(tpt.tpe)) { casted => + letAbstract(ref(scrutinee).cast(castTp)) { casted => nonNull += casted patternPlan(casted, pat, onSuccess) }) @@ -473,7 +478,13 @@ object PatternMatcher { onSuccess ) } - case WildcardPattern() => + // When match against a `this.type` (say case a: this.type => ???), + // the typer will transform the pattern to a `Bind(..., Typed(Ident(a), ThisType(...)))`, + // then post typer will change all the `Ident` with a `ThisType` to a `This`. + // Therefore, after pattern matching, we will have the following tree `Bind(..., Typed(This(...), ThisType(...)))`. + // We handle now here the case were the pattern was transformed to a `This`, relying on the fact that the logic for + // `Typed` above will create the correct type test. + case WildcardPattern() | This(_) => onSuccess case SeqLiteral(pats, _) => matchElemsPlan(scrutinee, pats, exact = true, onSuccess) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Checker.scala b/compiler/src/dotty/tools/dotc/transform/init/Checker.scala index 4d5c467cf4fe..d34f95bedef1 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Checker.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Checker.scala @@ -29,7 +29,7 @@ class Checker extends Phase: override val runsAfter = Set(Pickler.name) override def isEnabled(using Context): Boolean = - super.isEnabled && (ctx.settings.Whas.checkInit || ctx.settings.YcheckInitGlobal.value) + super.isEnabled && (ctx.settings.Whas.safeInit || ctx.settings.YsafeInitGlobal.value) def traverse(traverser: InitTreeTraverser)(using Context): Boolean = monitor(phaseName): val unit = ctx.compilationUnit @@ -50,10 +50,10 @@ class Checker extends Phase: cancellable { val classes = traverser.getClasses() - if ctx.settings.Whas.checkInit then + if ctx.settings.Whas.safeInit then Semantic.checkClasses(classes)(using checkCtx) - if ctx.settings.YcheckInitGlobal.value then + if ctx.settings.YsafeInitGlobal.value then val obj = new Objects obj.checkClasses(classes)(using checkCtx) } diff --git a/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala b/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala index 00daefba3547..83ec7fb8399e 100644 --- a/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala @@ -116,7 +116,7 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List case Nil => loop(parts, n = 0) - if reported then (Nil, Nil) + if reported then (Nil, Nil) // on error, Transform.checked will revert to unamended inputs else assert(argc == actuals.size, s"Expected ${argc} args but got ${actuals.size} for [${parts.mkString(", ")}]") (amended.toList, actuals.toList) @@ -320,5 +320,4 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List .tap(_ => reported = true) def partWarning(message: String, index: Int, offset: Int, end: Int): Unit = r.warning(BadFormatInterpolation(message), partPosAt(index, offset, end)) - .tap(_ => reported = true) end TypedFormatChecker diff --git a/compiler/src/dotty/tools/dotc/transform/localopt/FormatInterpolatorTransform.scala b/compiler/src/dotty/tools/dotc/transform/localopt/FormatInterpolatorTransform.scala deleted file mode 100644 index 79d94c26c692..000000000000 --- a/compiler/src/dotty/tools/dotc/transform/localopt/FormatInterpolatorTransform.scala +++ /dev/null @@ -1,39 +0,0 @@ -package dotty.tools.dotc -package transform.localopt - -import dotty.tools.dotc.ast.tpd.* -import dotty.tools.dotc.core.Constants.Constant -import dotty.tools.dotc.core.Contexts.* - -object FormatInterpolatorTransform: - - /** For f"${arg}%xpart", check format conversions and return (format, args) - * suitable for String.format(format, args). - */ - def checked(fun: Tree, args0: Tree)(using Context): (Tree, Tree) = - val (partsExpr, parts) = fun match - case TypeApply(Select(Apply(_, (parts: SeqLiteral) :: Nil), _), _) => - (parts.elems, parts.elems.map { case Literal(Constant(s: String)) => s }) - case _ => - report.error("Expected statically known StringContext", fun.srcPos) - (Nil, Nil) - val (args, elemtpt) = args0 match - case seqlit: SeqLiteral => (seqlit.elems, seqlit.elemtpt) - case _ => - report.error("Expected statically known argument list", args0.srcPos) - (Nil, EmptyTree) - - def literally(s: String) = Literal(Constant(s)) - if parts.lengthIs != args.length + 1 then - val badParts = - if parts.isEmpty then "there are no parts" - else s"too ${if parts.lengthIs > args.length + 1 then "few" else "many"} arguments for interpolated string" - report.error(badParts, fun.srcPos) - (literally(""), args0) - else - val checker = TypedFormatChecker(partsExpr, parts, args) - val (format, formatArgs) = checker.checked - if format.isEmpty then (literally(parts.mkString), args0) - else (literally(format.mkString), SeqLiteral(formatArgs.toList, elemtpt)) - end checked -end FormatInterpolatorTransform diff --git a/compiler/src/dotty/tools/dotc/transform/localopt/StringInterpolatorOpt.scala b/compiler/src/dotty/tools/dotc/transform/localopt/StringInterpolatorOpt.scala index 804150eafc4e..db3a0c6c71f2 100644 --- a/compiler/src/dotty/tools/dotc/transform/localopt/StringInterpolatorOpt.scala +++ b/compiler/src/dotty/tools/dotc/transform/localopt/StringInterpolatorOpt.scala @@ -10,6 +10,8 @@ import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.StdNames.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* +import dotty.tools.dotc.printing.Formatting.* +import dotty.tools.dotc.reporting.BadFormatInterpolation import dotty.tools.dotc.transform.MegaPhase.MiniPhase import dotty.tools.dotc.typer.ConstFold @@ -22,8 +24,9 @@ import dotty.tools.dotc.typer.ConstFold */ class StringInterpolatorOpt extends MiniPhase: import tpd.* + import StringInterpolatorOpt.* - override def phaseName: String = StringInterpolatorOpt.name + override def phaseName: String = name override def description: String = StringInterpolatorOpt.description @@ -31,7 +34,7 @@ class StringInterpolatorOpt extends MiniPhase: tree match case tree: RefTree => val sym = tree.symbol - assert(!StringInterpolatorOpt.isCompilerIntrinsic(sym), + assert(!isCompilerIntrinsic(sym), i"$tree in ${ctx.owner.showLocated} should have been rewritten by phase $phaseName") case _ => @@ -117,10 +120,10 @@ class StringInterpolatorOpt extends MiniPhase: !(tp =:= defn.StringType) && { tp =:= defn.UnitType - && { report.warning("interpolated Unit value", t.srcPos); true } + && { report.warning(bfi"interpolated Unit value", t.srcPos); true } || !tp.isPrimitiveValueType - && { report.warning("interpolation uses toString", t.srcPos); true } + && { report.warning(bfi"interpolation uses toString", t.srcPos); true } } if ctx.settings.Whas.toStringInterpolated then checkIsStringify(t.tpe): Unit @@ -134,10 +137,38 @@ class StringInterpolatorOpt extends MiniPhase: case _ => false // Perform format checking and normalization, then make it StringOps(fmt).format(args1) with tweaked args def transformF(fun: Tree, args: Tree): Tree = - val (fmt, args1) = FormatInterpolatorTransform.checked(fun, args) + // For f"${arg}%xpart", check format conversions and return (format, args) for String.format(format, args). + def checked(args0: Tree)(using Context): (Tree, Tree) = + val (partsExpr, parts) = fun match + case TypeApply(Select(Apply(_, (parts: SeqLiteral) :: Nil), _), _) => + (parts.elems, parts.elems.map { case Literal(Constant(s: String)) => s }) + case _ => + report.error("Expected statically known StringContext", fun.srcPos) + (Nil, Nil) + val (args, elemtpt) = args0 match + case seqlit: SeqLiteral => (seqlit.elems, seqlit.elemtpt) + case _ => + report.error("Expected statically known argument list", args0.srcPos) + (Nil, EmptyTree) + + def literally(s: String) = Literal(Constant(s)) + if parts.lengthIs != args.length + 1 then + val badParts = + if parts.isEmpty then "there are no parts" + else s"too ${if parts.lengthIs > args.length + 1 then "few" else "many"} arguments for interpolated string" + report.error(badParts, fun.srcPos) + (literally(""), args0) + else + val checker = TypedFormatChecker(partsExpr, parts, args) + val (format, formatArgs) = checker.checked + if format.isEmpty then (literally(parts.mkString), args0) // on error just use unchecked inputs + else (literally(format.mkString), SeqLiteral(formatArgs.toList, elemtpt)) + end checked + val (fmt, args1) = checked(args) resolveConstructor(defn.StringOps.typeRef, List(fmt)) .select(nme.format) .appliedTo(args1) + end transformF // Starting with Scala 2.13, s and raw are macros in the standard // library, so we need to expand them manually. // sc.s(args) --> standardInterpolator(processEscapes, args, sc.parts) @@ -186,3 +217,7 @@ object StringInterpolatorOpt: sym == defn.StringContext_s || sym == defn.StringContext_f || sym == defn.StringContext_raw + + extension (sc: StringContext) + def bfi(args: Shown*)(using Context): BadFormatInterpolation = + BadFormatInterpolation(i(sc)(args*)) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index b7e1f349a377..4379002d2210 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -171,6 +171,14 @@ object SpaceEngine { /** Is `a` a subspace of `b`? Equivalent to `simplify(simplify(a) - simplify(b)) == Empty`, but faster */ def computeIsSubspace(a: Space, b: Space)(using Context): Boolean = trace(i"isSubspace($a, $b)") { + /** Is decomposition allowed on the right-hand side of a pattern? */ + /** We only allow decomposition on the right-hand side of a pattern if the type is not a type parameter, a type parameter reference, or a deferred type reference */ + /** This is because decomposition on the right-hand side of a pattern can lead to false positive warnings */ + inline def rhsDecompositionAllowed(tp: Type): Boolean = tp.dealias match + case _: TypeParamRef => false + case tr: TypeRef if tr.symbol.is(TypeParam) || (tr.symbol.is(Deferred) && !tr.symbol.isClass) => false + case _ => true + val a2 = simplify(a) val b2 = simplify(b) if (a ne a2) || (b ne b2) then isSubspace(a2, b2) @@ -185,7 +193,7 @@ object SpaceEngine { case (a @ Typ(tp1, _), b @ Typ(tp2, _)) => isSubType(tp1, tp2) || canDecompose(a) && isSubspace(Or(decompose(a)), b) - || canDecompose(b) && isSubspace(a, Or(decompose(b))) + || (canDecompose(b) && rhsDecompositionAllowed(tp2)) && isSubspace(a, Or(decompose(b))) case (Prod(tp1, _, _), Typ(tp2, _)) => isSubType(tp1, tp2) case (a @ Typ(tp1, _), Prod(tp2, fun, ss)) => @@ -694,6 +702,7 @@ object SpaceEngine { case OrType(tp1, tp2) => inhabited(tp1) || inhabited(tp2) case tp: RefinedType => inhabited(tp.parent) case tp: TypeRef => !containsUninhabitedField(tp) && inhabited(tp.prefix) + case tp: AppliedType => !containsUninhabitedField(tp) && inhabited(tp.tycon) case _ => !containsUninhabitedField(tp) if inhabited(refined) then refined diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 290e061772e4..d8320ba1e389 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -140,16 +140,18 @@ object Applications { def tupleComponentTypes(tp: Type)(using Context): List[Type] = tp.widenExpr.dealias.normalized match - case tp: AppliedType => - if defn.isTupleClass(tp.tycon.typeSymbol) then - tp.args - else if tp.tycon.derivesFrom(defn.PairClass) then - val List(head, tail) = tp.args - head :: tupleComponentTypes(tail) - else + case defn.NamedTuple(_, vals) => + tupleComponentTypes(vals) + case tp: AppliedType => + if defn.isTupleClass(tp.tycon.typeSymbol) then + tp.args + else if tp.tycon.derivesFrom(defn.PairClass) then + val List(head, tail) = tp.args + head :: tupleComponentTypes(tail) + else + Nil + case _ => Nil - case _ => - Nil def productArity(tp: Type, errorPos: SrcPos = NoSourcePosition)(using Context): Int = if (defn.isProductSubType(tp)) productSelectorTypes(tp, errorPos).size else -1 @@ -1427,14 +1429,11 @@ trait Applications extends Compatibility { def convertNewGenericArray(tree: Tree)(using Context): Tree = tree match { case Apply(TypeApply(tycon, targs@(targ :: Nil)), args) if tycon.symbol == defn.ArrayConstructor => fullyDefinedType(tree.tpe, "array", tree.srcPos) - - def newGenericArrayCall = + if TypeErasure.isGenericArrayArg(targ.tpe) then ref(defn.DottyArraysModule) - .select(defn.newGenericArrayMethod).withSpan(tree.span) - .appliedToTypeTrees(targs).appliedToTermArgs(args) - - if (TypeErasure.isGeneric(targ.tpe)) - newGenericArrayCall + .select(defn.newGenericArrayMethod).withSpan(tree.span) + .appliedToTypeTrees(targs) + .appliedToTermArgs(args) else tree case _ => tree @@ -2561,7 +2560,7 @@ trait Applications extends Compatibility { /** Is `formal` a product type which is elementwise compatible with `params`? */ def ptIsCorrectProduct(formal: Type, params: List[untpd.ValDef])(using Context): Boolean = isFullyDefined(formal, ForceDegree.flipBottom) - && defn.isProductSubType(formal) + && (defn.isProductSubType(formal) || formal.isNamedTupleType) && tupleComponentTypes(formal).corresponds(params): (argType, param) => param.tpt.isEmpty || argType.widenExpr <:< typedAheadType(param.tpt).tpe diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index ecbb34ea2949..990eb01b999e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -144,29 +144,30 @@ object Checking { def checkAppliedTypesIn(tpt: TypeTree)(using Context): Unit = val checker = new TypeTraverser: def traverse(tp: Type) = - tp match + tp.normalized match 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) + if !(isCaptureChecking && defn.MatchCase.isInstance(tp)) then + // Don't check match type cases under cc. For soundness it's enough + // to check bounds in reduced match types. + // See tuple-ops.scala and tuple-ops-2.scala for examples that would fail otherwise. + 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)) + // When capture checking, types that are not themselves capture-checked + // are exempted. Since the type constructor could not foresee possible + // capture sets, it's better to be lenient for backwards compatibility. + then + checkAppliedType( + untpd.AppliedTypeTree(TypeTree(tycon), argTypes.map(TypeTree(_))) + .withType(tp).withSpan(tpt.span.toSynthetic), + tpt) + traverseChildren(tp) case _ => - traverseChildren(tp) + traverseChildren(tp) checker.traverse(tpt.tpe) def checkNoWildcard(tree: Tree)(using Context): Tree = tree.tpe match { diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index fa5b1cbfe19e..e1bd045ff3a5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -832,7 +832,7 @@ trait ImplicitRunInfo: WildcardType else seen += t - t.superType match + t.underlying match case TypeBounds(lo, hi) => if lo.isBottomTypeAfterErasure then apply(hi) else AndType.make(apply(lo), apply(hi)) @@ -844,6 +844,10 @@ trait ImplicitRunInfo: case t: TypeVar => apply(t.underlying) case t: ParamRef => applyToUnderlying(t) case t: ConstantType => apply(t.underlying) + case t @ AppliedType(tycon, args) if !tycon.typeSymbol.isClass => + // To prevent arguments to be reduced away when re-applying the tycon bounds, + // we collect all parts as elements of a tuple. See i21951.scala for a test case. + apply(defn.tupleType(tycon :: args)) case t => mapOver(t) end liftToAnchors val liftedTp = liftToAnchors(tp) diff --git a/compiler/src/dotty/tools/dotc/typer/Migrations.scala b/compiler/src/dotty/tools/dotc/typer/Migrations.scala index d811987383c6..31bb29e0e6c5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Migrations.scala +++ b/compiler/src/dotty/tools/dotc/typer/Migrations.scala @@ -162,6 +162,7 @@ trait Migrations: private def checkParentheses(tree: Tree, pt: FunProto)(using Context): Boolean = val ptSpan = pt.args.head.span ptSpan.exists + && tree.span.exists && ctx.source.content .slice(tree.span.end, ptSpan.start) .exists(_ == '(') diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index a79408b756ee..ba472512a953 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -329,6 +329,15 @@ object RefChecks { val mixinOverrideErrors = new mutable.ListBuffer[MixinOverrideError]() + /** Returns a `SourcePosition` containing the full span (with the correct + * end) of the class name. */ + def clazzNamePos = + if clazz.name == tpnme.ANON_CLASS then + clazz.srcPos + else + val clazzNameEnd = clazz.srcPos.span.start + clazz.name.stripModuleClassSuffix.lastPart.length + clazz.srcPos.sourcePos.copy(span = clazz.srcPos.span.withEnd(clazzNameEnd)) + def printMixinOverrideErrors(): Unit = mixinOverrideErrors.toList match { case Nil => @@ -914,7 +923,7 @@ object RefChecks { checkNoAbstractDecls(clazz) if (abstractErrors.nonEmpty) - report.error(abstractErrorMessage, clazz.srcPos) + report.error(abstractErrorMessage, clazzNamePos) checkMemberTypesOK() checkCaseClassInheritanceInvariant() @@ -1185,6 +1194,18 @@ object RefChecks { report.warning(ExtensionNullifiedByMember(sym, target), sym.srcPos) end checkExtensionMethods + /** Check that public (and protected) methods/fields do not expose flexible types. */ + def checkPublicFlexibleTypes(sym: Symbol)(using Context): Unit = + if ctx.explicitNulls && !ctx.isJava + && sym.exists && !sym.is(Private) && sym.owner.isClass + && !sym.isOneOf(Synthetic | InlineProxy | Param | Exported) then + val resTp = sym.info.finalResultType + if resTp.existsPart(_.isInstanceOf[FlexibleType], StopAt.Static) then + report.warning( + em"${sym.show} exposes a flexible type in its inferred result type ${resTp}. Consider annotating the type explicitly", + sym.srcPos + ) + /** Verify that references in the user-defined `@implicitNotFound` message are valid. * (i.e. they refer to a type variable that really occurs in the signature of the annotated symbol.) */ @@ -1321,6 +1342,7 @@ class RefChecks extends MiniPhase { thisPhase => val sym = tree.symbol checkNoPrivateOverrides(sym) checkVolatile(sym) + checkPublicFlexibleTypes(sym) if (sym.exists && sym.owner.isTerm) { tree.rhs match { case Ident(nme.WILDCARD) => report.error(UnboundPlaceholderParameter(), sym.srcPos) @@ -1336,6 +1358,7 @@ class RefChecks extends MiniPhase { thisPhase => checkImplicitNotFoundAnnotation.defDef(sym.denot) checkUnaryMethods(sym) checkExtensionMethods(sym) + checkPublicFlexibleTypes(sym) tree } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index e9e3e22342bf..da37367c0f3a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1683,11 +1683,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val restpe = mt.resultType match case mt: MethodType => mt.toFunctionType(isJava = samParent.classSymbol.is(JavaDefined)) case tp => tp - (formals, - if (mt.isResultDependent) - untpd.InLambdaTypeTree(isResult = true, (_, syms) => restpe.substParams(mt, syms.map(_.termRef))) - else - typeTree(restpe)) + val tree = + if (mt.isResultDependent) { + if (formals.length != defaultArity) + typeTree(WildcardType) + else + untpd.InLambdaTypeTree(isResult = true, (_, syms) => restpe.substParams(mt, syms.map(_.termRef))) + } else + typeTree(restpe) + (formals, tree) case _ => (List.tabulate(defaultArity)(alwaysWildcardType), untpd.TypeTree()) } @@ -1929,8 +1933,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val firstFormal = protoFormals.head.loBound if ptIsCorrectProduct(firstFormal, params) then val isGenericTuple = - firstFormal.derivesFrom(defn.TupleClass) - && !defn.isTupleClass(firstFormal.typeSymbol) + firstFormal.isNamedTupleType + || (firstFormal.derivesFrom(defn.TupleClass) + && !defn.isTupleClass(firstFormal.typeSymbol)) desugared = desugar.makeTupledFunction(params, fnBody, isGenericTuple) else if protoFormals.length > 1 && params.length == 1 then def isParamRef(scrut: untpd.Tree): Boolean = scrut match @@ -3745,12 +3750,18 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val ifpt = defn.asContextFunctionType(pt) val result = if ifpt.exists - && defn.functionArity(ifpt) > 0 // ContextFunction0 is only used after ElimByName + && !ctx.isAfterTyper + && { + // ContextFunction0 is only used after ElimByName + val arity = defn.functionArity(ifpt) + if arity == 0 then + report.error(em"context function types require at least one parameter", xtree.srcPos) + arity > 0 + } && xtree.isTerm && !untpd.isContextualClosure(xtree) && !ctx.mode.is(Mode.Pattern) && !xtree.isInstanceOf[SplicePattern] - && !ctx.isAfterTyper && !ctx.isInlineContext then makeContextualFunction(xtree, ifpt) @@ -3820,6 +3831,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val ifun = desugar.makeContextualFunction(paramTypes, paramNamesOrNil, tree, erasedParams) typr.println(i"make contextual function $tree / $pt ---> $ifun") typedFunctionValue(ifun, pt) + .tap: + case tree @ Block((m1: DefDef) :: _, _: Closure) if ctx.settings.Whas.wrongArrow => + m1.rhs match + case Block((m2: DefDef) :: _, _: Closure) if m1.paramss.lengthCompare(m2.paramss) == 0 => + val p1s = m1.symbol.info.asInstanceOf[MethodType].paramInfos + val p2s = m2.symbol.info.asInstanceOf[MethodType].paramInfos + if p1s.corresponds(p2s)(_ =:= _) then + report.warning(em"Context function adapts a lambda with the same parameter types, possibly ?=> was intended.", tree.srcPos) + case _ => + case _ => } /** Typecheck and adapt tree, returning a typed tree. Parameters as for `typedUnadapted` */ @@ -4374,11 +4395,20 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // But in this case we should search with additional arguments typed only if there // is no default argument. + // Try to constrain the result using `pt1`, but back out if a BadTyperStateAssertion + // is thrown. TODO Find out why the bad typer state arises and prevent it. The try-catch + // is a temporary hack to keep projects compiling that would fail otherwise due to + // searching more arguments to instantiate implicits (PR #23532). A failing project + // is described in issue #23609. + def tryConstrainResult(pt: Type): Boolean = + try constrainResult(tree.symbol, wtp, pt) + catch case ex: TyperState.BadTyperStateAssertion => false + arg.tpe match case failed: SearchFailureType if canProfitFromMoreConstraints => val pt1 = pt.deepenProtoTrans - if (pt1 `ne` pt) && (pt1 ne sharpenedPt) && constrainResult(tree.symbol, wtp, pt1) - then return implicitArgs(formals, argIndex, pt1) + if (pt1 `ne` pt) && (pt1 ne sharpenedPt) && tryConstrainResult(pt1) then + return implicitArgs(formals, argIndex, pt1) case _ => arg.tpe match diff --git a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala index 0c2929283ee3..354f09382d82 100644 --- a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -178,7 +178,20 @@ class VarianceChecker(using Context) { i"\n${hl("enum case")} ${towner.name} requires explicit declaration of $tvar to resolve this issue.\n$example" else "" - em"${varianceLabel(tvar.flags)} $tvar occurs in ${varianceLabel(required)} position in type ${sym.info} of $sym$enumAddendum" + val privateParamAddendum = + if sym.flags.is(ParamAccessor) && sym.flags.is(Private) then + val varOrVal = if sym.is(Mutable) then "var" else "val" + val varFieldInstead = if sym.is(Mutable) then " and add\na field inside the class instead" else "" + s""" + | + |Implementation limitation: ${hl(f"private $varOrVal")} parameters cannot be inferred to be local + |and therefore are always variance-checked. + | + |Potential fix: remove the ${hl(f"private $varOrVal")} modifiers on the parameter ${sym.name}$varFieldInstead. + """.stripMargin + else + "" + em"${varianceLabel(tvar.flags)} $tvar occurs in ${varianceLabel(required)} position in type ${sym.info} of $sym$enumAddendum$privateParamAddendum" if (migrateTo3 && (sym.owner.isConstructor || sym.ownersIterator.exists(_.isAllOf(ProtectedLocal)))) report.migrationWarning( diff --git a/compiler/src/dotty/tools/io/Path.scala b/compiler/src/dotty/tools/io/Path.scala index 39665395c289..64137d691898 100644 --- a/compiler/src/dotty/tools/io/Path.scala +++ b/compiler/src/dotty/tools/io/Path.scala @@ -245,12 +245,12 @@ class Path private[io] (val jpath: JPath) { if (!exists) false else { Files.walkFileTree(jpath, new SimpleFileVisitor[JPath]() { - override def visitFile(file: JPath, attrs: BasicFileAttributes) = { + override def visitFile(file: JPath, attrs: BasicFileAttributes): FileVisitResult = { Files.delete(file) FileVisitResult.CONTINUE } - override def postVisitDirectory(dir: JPath, exc: IOException) = { + override def postVisitDirectory(dir: JPath, exc: IOException): FileVisitResult = { Files.delete(dir) FileVisitResult.CONTINUE } diff --git a/compiler/src/dotty/tools/repl/AbstractFileClassLoader.scala b/compiler/src/dotty/tools/repl/AbstractFileClassLoader.scala index 7a457a1d7546..1796a7dc68b5 100644 --- a/compiler/src/dotty/tools/repl/AbstractFileClassLoader.scala +++ b/compiler/src/dotty/tools/repl/AbstractFileClassLoader.scala @@ -26,7 +26,7 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) exten // on JDK 20 the URL constructor we're using is deprecated, // but the recommended replacement, URL.of, doesn't exist on JDK 8 @annotation.nowarn("cat=deprecation") - override protected def findResource(name: String) = + override protected def findResource(name: String): URL | Null = findAbstractFile(name) match case null => null case file => new URL(null, s"memory:${file.path}", new URLStreamHandler { @@ -35,13 +35,13 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) exten override def getInputStream = file.input } }) - override protected def findResources(name: String) = + override protected def findResources(name: String): java.util.Enumeration[URL] = findResource(name) match case null => Collections.enumeration(Collections.emptyList[URL]) //Collections.emptyEnumeration[URL] case url => Collections.enumeration(Collections.singleton(url)) override def findClass(name: String): Class[?] = { - var file: AbstractFile = root + var file: AbstractFile | Null = root val pathParts = name.split("[./]").toList for (dirPart <- pathParts.init) { file = file.lookupName(dirPart, true) diff --git a/compiler/src/dotty/tools/repl/JLineTerminal.scala b/compiler/src/dotty/tools/repl/JLineTerminal.scala index e4ac1626525e..2680704d326e 100644 --- a/compiler/src/dotty/tools/repl/JLineTerminal.scala +++ b/compiler/src/dotty/tools/repl/JLineTerminal.scala @@ -106,7 +106,7 @@ class JLineTerminal extends java.io.Closeable { ) extends reader.ParsedLine { // Using dummy values, not sure what they are used for def wordIndex = -1 - def words = java.util.Collections.emptyList[String] + def words: java.util.List[String] = java.util.Collections.emptyList[String] } def parse(input: String, cursor: Int, context: ParseContext): reader.ParsedLine = { diff --git a/compiler/src/dotty/tools/repl/ScriptEngine.scala b/compiler/src/dotty/tools/repl/ScriptEngine.scala index 7d385daa43e4..cce16000577f 100644 --- a/compiler/src/dotty/tools/repl/ScriptEngine.scala +++ b/compiler/src/dotty/tools/repl/ScriptEngine.scala @@ -64,20 +64,21 @@ class ScriptEngine extends AbstractScriptEngine { object ScriptEngine { import java.util.Arrays + import java.util.List import scala.util.Properties class Factory extends ScriptEngineFactory { def getEngineName = "Scala REPL" def getEngineVersion = "3.0" - def getExtensions = Arrays.asList("scala") + def getExtensions: List[String] = Arrays.asList("scala") def getLanguageName = "Scala" def getLanguageVersion = Properties.versionString - def getMimeTypes = Arrays.asList("application/x-scala") - def getNames = Arrays.asList("scala") + def getMimeTypes: List[String] = Arrays.asList("application/x-scala") + def getNames: List[String] = Arrays.asList("scala") - def getMethodCallSyntax(obj: String, m: String, args: String*) = s"$obj.$m(${args.mkString(", ")})" + def getMethodCallSyntax(obj: String, m: String, args: String*): String = s"$obj.$m(${args.mkString(", ")})" - def getOutputStatement(toDisplay: String) = s"""print("$toDisplay")""" + def getOutputStatement(toDisplay: String): String = s"""print("$toDisplay")""" def getParameter(key: String): Object = key match { case JScriptEngine.ENGINE => getEngineName @@ -88,7 +89,7 @@ object ScriptEngine { case _ => null } - def getProgram(statements: String*) = statements.mkString("; ") + def getProgram(statements: String*): String = statements.mkString("; ") def getScriptEngine: JScriptEngine = new ScriptEngine } diff --git a/compiler/src/dotty/tools/tasty/besteffort/BestEffortTastyFormat.scala b/compiler/src/dotty/tools/tasty/besteffort/BestEffortTastyFormat.scala index 99a24ce5f346..41876016b4ec 100644 --- a/compiler/src/dotty/tools/tasty/besteffort/BestEffortTastyFormat.scala +++ b/compiler/src/dotty/tools/tasty/besteffort/BestEffortTastyFormat.scala @@ -38,7 +38,7 @@ object BestEffortTastyFormat { // added AST tag - Best Effort TASTy only final val ERRORtype = 50 - def astTagToString(tag: Int) = tag match { + def astTagToString(tag: Int): String = tag match { case ERRORtype => "ERRORtype" case _ => TastyFormat.astTagToString(tag) } diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index c49efceff73f..cb8768a1cb81 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -62,7 +62,7 @@ class CompilationTests { aggregateTests( compileFile("tests/rewrites/rewrites.scala", defaultOptions.and("-source", "3.0-migration").and("-rewrite", "-indent")), compileFile("tests/rewrites/rewrites3x.scala", defaultOptions.and("-rewrite", "-source", "future-migration")), - compileFile("tests/rewrites/rewrites3x-fatal-warnings.scala", defaultOptions.and("-rewrite", "-source", "future-migration", "-Xfatal-warnings")), + compileFile("tests/rewrites/rewrites3x-fatal-warnings.scala", defaultOptions.and("-rewrite", "-source", "future-migration", "-Werror")), compileFile("tests/rewrites/i21394.scala", defaultOptions.and("-rewrite", "-source", "future-migration")), compileFile("tests/rewrites/uninitialized-var.scala", defaultOptions.and("-rewrite", "-source", "future-migration")), compileFile("tests/rewrites/with-type-operator.scala", defaultOptions.and("-rewrite", "-source", "future-migration")), @@ -89,6 +89,7 @@ class CompilationTests { compileFile("tests/rewrites/implicit-to-given.scala", defaultOptions.and("-rewrite", "-Yimplicit-to-given")), compileFile("tests/rewrites/i22792.scala", defaultOptions.and("-rewrite")), compileFile("tests/rewrites/i23449.scala", defaultOptions.and("-rewrite", "-source:3.4-migration")), + compileFile("tests/rewrites/i24213.scala", defaultOptions.and("-rewrite", "-source:3.4-migration")), ).checkRewrites() } @@ -151,7 +152,7 @@ class CompilationTests { compileFilesInDir("tests/neg-deep-subtype", allowDeepSubtypes), compileFilesInDir("tests/neg-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking", "-language:experimental.separationChecking")), compileFile("tests/neg-custom-args/sourcepath/outer/nested/Test1.scala", defaultOptions.and("-sourcepath", "tests/neg-custom-args/sourcepath")), - compileDir("tests/neg-custom-args/sourcepath2/hi", defaultOptions.and("-sourcepath", "tests/neg-custom-args/sourcepath2", "-Xfatal-warnings")), + compileDir("tests/neg-custom-args/sourcepath2/hi", defaultOptions.and("-sourcepath", "tests/neg-custom-args/sourcepath2", "-Werror")), compileList("duplicate source", List( "tests/neg-custom-args/toplevel-samesource/S.scala", "tests/neg-custom-args/toplevel-samesource/nested/S.scala"), @@ -237,21 +238,21 @@ class CompilationTests { @Test def checkInitGlobal: Unit = { implicit val testGroup: TestGroup = TestGroup("checkInitGlobal") compileFilesInDir("tests/init-global/warn", defaultOptions.and("-Ysafe-init-global"), FileFilter.exclude(TestSources.negInitGlobalScala2LibraryTastyExcludelisted)).checkWarnings() - compileFilesInDir("tests/init-global/pos", defaultOptions.and("-Ysafe-init-global", "-Xfatal-warnings"), FileFilter.exclude(TestSources.posInitGlobalScala2LibraryTastyExcludelisted)).checkCompile() + compileFilesInDir("tests/init-global/pos", defaultOptions.and("-Ysafe-init-global", "-Werror"), FileFilter.exclude(TestSources.posInitGlobalScala2LibraryTastyExcludelisted)).checkCompile() if Properties.usingScalaLibraryTasty && !Properties.usingScalaLibraryCCTasty then compileFilesInDir("tests/init-global/warn-tasty", defaultOptions.and("-Ysafe-init-global"), FileFilter.exclude(TestSources.negInitGlobalScala2LibraryTastyExcludelisted)).checkWarnings() - compileFilesInDir("tests/init-global/pos-tasty", defaultOptions.and("-Ysafe-init-global", "-Xfatal-warnings"), FileFilter.exclude(TestSources.posInitGlobalScala2LibraryTastyExcludelisted)).checkCompile() + compileFilesInDir("tests/init-global/pos-tasty", defaultOptions.and("-Ysafe-init-global", "-Werror"), FileFilter.exclude(TestSources.posInitGlobalScala2LibraryTastyExcludelisted)).checkCompile() end if } // initialization tests - @Test def checkInit: Unit = { - implicit val testGroup: TestGroup = TestGroup("checkInit") - val options = defaultOptions.and("-Wsafe-init", "-Xfatal-warnings") + @Test def safeInit: Unit = { + given TestGroup = TestGroup("safeInit") + val options = defaultOptions.and("-Wsafe-init", "-Werror") compileFilesInDir("tests/init/neg", options).checkExpectedErrors() compileFilesInDir("tests/init/warn", defaultOptions.and("-Wsafe-init")).checkWarnings() compileFilesInDir("tests/init/pos", options).checkCompile() - compileFilesInDir("tests/init/crash", options.without("-Xfatal-warnings")).checkCompile() + compileFilesInDir("tests/init/crash", options.without("-Werror")).checkCompile() // The regression test for i12128 has some atypical classpath requirements. // The test consists of three files: (a) Reflect_1 (b) Macro_2 (c) Test_3 // which must be compiled separately. In addition: @@ -260,7 +261,7 @@ class CompilationTests { // - the output from (a) _must not_ be on the classpath while compiling (c) locally { val i12128Group = TestGroup("checkInit/i12128") - val i12128Options = options.without("-Xfatal-warnings") + val i12128Options = options.without("-Werror") val outDir1 = defaultOutputDir + i12128Group + "/Reflect_1/i12128/Reflect_1" val outDir2 = defaultOutputDir + i12128Group + "/Macro_2/i12128/Macro_2" @@ -279,7 +280,7 @@ class CompilationTests { * an error when reading the files' TASTy trees. */ locally { val tastyErrorGroup = TestGroup("checkInit/tasty-error/val-or-defdef") - val tastyErrorOptions = options.without("-Xfatal-warnings") + val tastyErrorOptions = options.without("-Werror") val classA0 = defaultOutputDir + tastyErrorGroup + "/A/v0/A" val classA1 = defaultOutputDir + tastyErrorGroup + "/A/v1/A" @@ -302,7 +303,7 @@ class CompilationTests { * an error when reading the files' TASTy trees. This fact is demonstrated by the compilation of Main. */ locally { val tastyErrorGroup = TestGroup("checkInit/tasty-error/typedef") - val tastyErrorOptions = options.without("-Xfatal-warnings").without("-Ycheck:all") + val tastyErrorOptions = options.without("-Werror").without("-Ycheck:all") val classC = defaultOutputDir + tastyErrorGroup + "/C/typedef/C" val classA0 = defaultOutputDir + tastyErrorGroup + "/A/v0/A" diff --git a/compiler/test/dotty/tools/dotc/TupleShowTests.scala b/compiler/test/dotty/tools/dotc/TupleShowTests.scala index 88e0587d7d71..3aa9d0d84b42 100644 --- a/compiler/test/dotty/tools/dotc/TupleShowTests.scala +++ b/compiler/test/dotty/tools/dotc/TupleShowTests.scala @@ -54,24 +54,28 @@ class TupleShowTests extends DottyTest: @Test def tup3_show10 = chkEq("(Int,\n Long,\n Short)".normEOL, tup3.toText(ctx.printer).mkString(10, false)) - val res21 = """|(Int, Int, Int, Int, Int, Long, Long, Long, Long, Long, Int, Int, Int, Int, - | Int, Long, Long, Long, Long, Long, Int)""".stripMargin.normEOL + val res21: String = + """|(Int, Int, Int, Int, Int, Long, Long, Long, Long, Long, Int, Int, Int, Int, + | Int, Long, Long, Long, Long, Long, Int)""".stripMargin.normEOL - val res22 = """|(Int, Int, Int, Int, Int, Long, Long, Long, Long, Long, Int, Int, Int, Int, - | Int, Long, Long, Long, Long, Long, Int, Long)""".stripMargin.normEOL + val res22: String = + """|(Int, Int, Int, Int, Int, Long, Long, Long, Long, Long, Int, Int, Int, Int, + | Int, Long, Long, Long, Long, Long, Int, Long)""".stripMargin.normEOL - val res23 = """|(Int, Int, Int, Int, Int, Long, Long, Long, Long, Long, Int, Int, Int, Int, - | Int, Long, Long, Long, Long, Long, Int, Long, Short)""".stripMargin.normEOL + val res23: String = + """|(Int, Int, Int, Int, Int, Long, Long, Long, Long, Long, Int, Int, Int, Int, + | Int, Long, Long, Long, Long, Long, Int, Long, Short)""".stripMargin.normEOL - val res24 = """|(Int, Int, Int, Int, Int, Long, Long, Long, Long, Long, Int, Int, Int, Int, - | Int, Long, Long, Long, Long, Long, Int, Long, Short, Short)""".stripMargin.normEOL + val res24: String = + """|(Int, Int, Int, Int, Int, Long, Long, Long, Long, Long, Int, Int, Int, Int, + | Int, Long, Long, Long, Long, Long, Int, Long, Short, Short)""".stripMargin.normEOL def chkEq[A](expected: A, obtained: A) = assert(expected == obtained, diff(s"$expected", s"$obtained")) /** On Windows the string literal in this test source file will be read with `\n` (b/c of "-encoding UTF8") * but the compiler will correctly emit \r\n as the line separator. * So we align the expected result to faithfully compare test results. */ - extension (str: String) def normEOL = if EOL == "\n" then str else str.replace("\n", EOL) + extension (str: String) def normEOL: String = if EOL == "\n" then str else str.replace("\n", EOL) def diff(exp: String, obt: String) = val min = math.min(exp.length, obt.length) diff --git a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala index c74be4901137..c7b030f0805c 100644 --- a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala @@ -111,7 +111,7 @@ class ScalaSettingsTests: // createTestCase(settings.YjavaTasty , settings.XjavaTasty), // createTestCase(settings.YearlyTastyOutput , settings.XearlyTastyOutput, ":./"), // createTestCase(settings.YallowOutlineFromTasty, settings.XallowOutlineFromTasty), - createTestCase(settings.YcheckInit , settings.WcheckInit), + createTestCase(settings.YcheckInit , settings.WsafeInit), // createTestCase(settings.Xlint , settings.Wshadow, ":all"), // this setting is not going to be mapped to replacement. Read more in the commit message ).map: (deprecatedArgument, newSetting) => val args = List(deprecatedArgument) @@ -140,7 +140,7 @@ class ScalaSettingsTests: // createTestCase(settings.YjavaTasty , settings.XjavaTasty), // createTestCase(settings.YearlyTastyOutput , settings.XearlyTastyOutput), // createTestCase(settings.YallowOutlineFromTasty, settings.XallowOutlineFromTasty), - createTestCase(settings.YcheckInit , settings.WcheckInit), + createTestCase(settings.YcheckInit , settings.WsafeInit), createTestCase(settings.Xlint , settings.Wshadow), ).map: (deprecatedArgument, newSetting) => val args = List(deprecatedArgument) @@ -181,7 +181,7 @@ class ScalaSettingsTests: // createTestCase(settings.YjavaTasty , settings.XjavaTasty), // createTestCase(settings.YearlyTastyOutput , settings.XearlyTastyOutput, ":./"), // createTestCase(settings.YallowOutlineFromTasty, settings.XallowOutlineFromTasty), - createTestCase(settings.YcheckInit , settings.WcheckInit), + createTestCase(settings.YcheckInit , settings.WsafeInit), // createTestCase(settings.Xlint , settings.Wshadow, ":all"), // this setting is not going to be mapped to replacement. Read more in the commit message ).flatten.map: (deprecatedArgument, newSetting) => val args = List(deprecatedArgument) diff --git a/compiler/test/dotty/tools/dotc/reporting/CodeActionTest.scala b/compiler/test/dotty/tools/dotc/reporting/CodeActionTest.scala index 91074110389e..91ff958a7889 100644 --- a/compiler/test/dotty/tools/dotc/reporting/CodeActionTest.scala +++ b/compiler/test/dotty/tools/dotc/reporting/CodeActionTest.scala @@ -179,6 +179,130 @@ class CodeActionTest extends DottyTest: ctxx = ctxx ) + @Test def addNN1 = + val ctxx = newContext + ctxx.setSetting(ctxx.settings.YexplicitNulls, true) + checkCodeAction( + code = + """val s: String|Null = ??? + | val t: String = s""".stripMargin, + title = "Add .nn", + expected = + """val s: String|Null = ??? + | val t: String = s.nn""".stripMargin, + ctxx = ctxx + ) + + @Test def addNN2 = + val ctxx = newContext + ctxx.setSetting(ctxx.settings.YexplicitNulls, true) + checkCodeAction( + code = + """implicit class infixOpTest(val s1: String) extends AnyVal { + | def q(s2: String): String | Null = null + |} + | val s: String = ??? + | val t: String = s q s""".stripMargin, + title = "Add .nn", + expected = + """implicit class infixOpTest(val s1: String) extends AnyVal { + | def q(s2: String): String | Null = null + |} + | val s: String = ??? + | val t: String = (s q s).nn""".stripMargin, + ctxx = ctxx + ) + + @Test def addNN3 = + val ctxx = newContext + ctxx.setSetting(ctxx.settings.YexplicitNulls, true) + checkCodeAction( + code = + """implicit class infixOpTest(val s1: String) extends AnyVal { + | def q(s2: String, s3: String): String | Null = null + |} + | val s: String = ??? + | val t: String = s q (s, s)""".stripMargin, + title = "Add .nn", + expected = + """implicit class infixOpTest(val s1: String) extends AnyVal { + | def q(s2: String, s3: String): String | Null = null + |} + | val s: String = ??? + | val t: String = (s q (s, s)).nn""".stripMargin, + ctxx = ctxx + ) + + @Test def addNN4 = + val ctxx = newContext + ctxx.setSetting(ctxx.settings.YexplicitNulls, true) + checkCodeAction( + code = + """implicit class infixOpTest(val s1: String) extends AnyVal { + | def q(s2: String, s3: String): String | Null = null + |} + | val s: String = ??? + | val t: String = s.q(s, s)""".stripMargin, + title = "Add .nn", + expected = + """implicit class infixOpTest(val s1: String) extends AnyVal { + | def q(s2: String, s3: String): String | Null = null + |} + | val s: String = ??? + | val t: String = s.q(s, s).nn""".stripMargin, + ctxx = ctxx + ) + + @Test def addNN5 = + val ctxx = newContext + ctxx.setSetting(ctxx.settings.YexplicitNulls, true) + checkCodeAction( + code = + """val s: String | Null = ??? + |val t: String = s match { + | case _: String => "foo" + | case _ => s + |}""".stripMargin, + title = "Add .nn", + expected = + """val s: String | Null = ??? + |val t: String = s match { + | case _: String => "foo" + | case _ => s.nn + |}""".stripMargin, + ctxx = ctxx + ) + + @Test def addNN6 = + val ctxx = newContext + ctxx.setSetting(ctxx.settings.YexplicitNulls, true) + checkCodeAction( + code = + """val s: String | Null = ??? + |val t: String = if (s != null) "foo" else s""".stripMargin, + title = "Add .nn", + expected = + """val s: String | Null = ??? + |val t: String = if (s != null) "foo" else s.nn""".stripMargin, + ctxx = ctxx + ) + + @Test def addNN7 = + val ctxx = newContext + ctxx.setSetting(ctxx.settings.YexplicitNulls, true) + checkCodeAction( + code = + """given ctx: String | Null = null + |def f(using c: String): String = c + |val s: String = f(using ctx)""".stripMargin, + title = "Add .nn", + expected = + """given ctx: String | Null = null + |def f(using c: String): String = c + |val s: String = f(using ctx.nn)""".stripMargin, + ctxx = ctxx + ) + // Make sure we're not using the default reporter, which is the ConsoleReporter, // meaning they will get reported in the test run and that's it. private def newContext = diff --git a/compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala b/compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala index 827a997af14b..6df830905603 100644 --- a/compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala +++ b/compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala @@ -33,7 +33,7 @@ import dotty.tools.dotc.util.SourceFile * only 1 semanticdb file should be present * @param source the single source file producing the semanticdb */ -@main def metac(root: String, source: String) = +@main def metac(root: String, source: String): Unit = val rootSrc = Paths.get(root) val sourceSrc = Paths.get(source) val semanticFile = FileSystems.getDefault.getPathMatcher("glob:**.semanticdb") @@ -53,13 +53,13 @@ import dotty.tools.dotc.util.SourceFile @Category(Array(classOf[BootstrappedOnlyTests])) class SemanticdbTests: - val javaFile = FileSystems.getDefault.getPathMatcher("glob:**.java") - val scalaFile = FileSystems.getDefault.getPathMatcher("glob:**.scala") - val expectFile = FileSystems.getDefault.getPathMatcher("glob:**.expect.scala") - val rootSrc = Paths.get(System.getProperty("dotty.tools.dotc.semanticdb.test")) - val expectSrc = rootSrc.resolve("expect") - val javaRoot = rootSrc.resolve("javacp") - val metacExpectFile = rootSrc.resolve("metac.expect") + val javaFile: PathMatcher = FileSystems.getDefault.getPathMatcher("glob:**.java") + val scalaFile: PathMatcher = FileSystems.getDefault.getPathMatcher("glob:**.scala") + val expectFile: PathMatcher = FileSystems.getDefault.getPathMatcher("glob:**.expect.scala") + val rootSrc: Path = Paths.get(System.getProperty("dotty.tools.dotc.semanticdb.test")) + val expectSrc: Path = rootSrc.resolve("expect") + val javaRoot: Path = rootSrc.resolve("javacp") + val metacExpectFile: Path = rootSrc.resolve("metac.expect") @Category(Array(classOf[dotty.SlowTests])) @Test def expectTests: Unit = if (!scala.util.Properties.isWin) runExpectTest(updateExpectFiles = false) diff --git a/compiler/test/dotty/tools/repl/AbstractFileClassLoaderTest.scala b/compiler/test/dotty/tools/repl/AbstractFileClassLoaderTest.scala index 27796feb819b..44540629016e 100644 --- a/compiler/test/dotty/tools/repl/AbstractFileClassLoaderTest.scala +++ b/compiler/test/dotty/tools/repl/AbstractFileClassLoaderTest.scala @@ -29,7 +29,7 @@ class AbstractFileClassLoaderTest: // cf ScalaClassLoader#classBytes extension (loader: ClassLoader) // An InputStream representing the given class name, or null if not found. - def classAsStream(className: String) = loader.getResourceAsStream { + def classAsStream(className: String): InputStream = loader.getResourceAsStream { if className.endsWith(".class") then className else s"${className.replace('.', '/')}.class" // classNameToPath } diff --git a/compiler/test/dotty/tools/scripting/ScriptTestEnv.scala b/compiler/test/dotty/tools/scripting/ScriptTestEnv.scala index 771c3ba14af0..387e309f6aec 100644 --- a/compiler/test/dotty/tools/scripting/ScriptTestEnv.scala +++ b/compiler/test/dotty/tools/scripting/ScriptTestEnv.scala @@ -282,7 +282,7 @@ object ScriptTestEnv { } extension(f: File) { - def name = f.getName + def name: String = f.getName def norm: String = f.toPath.normalize.norm def absPath: String = f.getAbsolutePath.norm def relpath: Path = f.toPath.relpath @@ -305,7 +305,7 @@ object ScriptTestEnv { // dist[*]/target/universal/stage, if present // else, SCALA_HOME if defined // else, not defined - lazy val envScalaHome = + lazy val envScalaHome: String = printf("scalacPath: %s\n", scalacPath.norm) if scalacPath.isFile then scalacPath.replaceAll("/bin/scalac", "") else envOrElse("SCALA_HOME", "not-found").norm diff --git a/compiler/test/dotty/tools/utils.scala b/compiler/test/dotty/tools/utils.scala index 14981e001d38..5dd949643f7f 100644 --- a/compiler/test/dotty/tools/utils.scala +++ b/compiler/test/dotty/tools/utils.scala @@ -33,7 +33,7 @@ def scriptsDir(path: String): File = { dir } -extension (f: File) def absPath = +extension (f: File) def absPath: String = f.getAbsolutePath.replace('\\', '/') extension (str: String) def dropExtension = diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 35fbb6e5fb14..5ec53fcb207f 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -41,8 +41,8 @@ import dotty.tools.vulpix.TestConfiguration.defaultOptions * using this, you should be running your JUnit tests **sequentially**, as the * test suite itself runs with a high level of concurrency. */ -trait ParallelTesting extends RunnerOrchestration { self => - import ParallelTesting._ +trait ParallelTesting extends RunnerOrchestration: + import ParallelTesting.* /** If the running environment supports an interactive terminal, each `Test` * will be run with a progress bar and real time feedback @@ -353,8 +353,8 @@ trait ParallelTesting extends RunnerOrchestration { self => import summaryReport._ - protected final val realStdout = System.out - protected final val realStderr = System.err + protected final val realStdout: PrintStream = System.out + protected final val realStderr: PrintStream = System.err /** A runnable that logs its contents in a buffer */ trait LoggedRunnable extends Runnable { @@ -996,29 +996,33 @@ trait ParallelTesting extends RunnerOrchestration { self => (errorMap, expectedErrors) end getErrorMapAndExpectedCount - // return unfulfilled expected errors and unexpected diagnostics + // return unfulfilled expected errors and unexpected diagnostics. + // the errorMap of expected errors is drained and returned as unfulfilled. + // a diagnostic at EOF after NL is recorded at the preceding line, + // to obviate `anypos-error` in that case. def getMissingExpectedErrors(errorMap: HashMap[String, Integer], reporterErrors: Iterator[Diagnostic]): (List[String], List[String]) = val unexpected, unpositioned = ListBuffer.empty[String] // For some reason, absolute paths leak from the compiler itself... def relativize(path: String): String = path.split(JFile.separatorChar).dropWhile(_ != "tests").mkString(JFile.separator) def seenAt(key: String): Boolean = errorMap.get(key) match - case null => false - case 1 => errorMap.remove(key); true - case n => errorMap.put(key, n - 1); true + case null => false + case 1 => errorMap.remove(key); true + case n => errorMap.put(key, n - 1); true def sawDiagnostic(d: Diagnostic): Unit = - d.pos.nonInlined match - case srcpos if srcpos.exists => - val key = s"${relativize(srcpos.source.file.toString)}:${srcpos.line + 1}" - if !seenAt(key) then unexpected += key - case srcpos => - if !seenAt("nopos") then unpositioned += relativize(srcpos.source.file.toString) + val srcpos = d.pos.nonInlined.adjustedAtEOF + val relatively = relativize(srcpos.source.file.toString) + if srcpos.exists then + val key = s"${relatively}:${srcpos.line + 1}" + if !seenAt(key) then unexpected += key + else + if !seenAt("nopos") then unpositioned += relatively reporterErrors.foreach(sawDiagnostic) - errorMap.get("anypos") match - case n if n == unexpected.size => errorMap.remove("anypos") ; unexpected.clear() - case _ => + if errorMap.get("anypos") == unexpected.size then + errorMap.remove("anypos") + unexpected.clear() (errorMap.asScala.keys.toList, (unexpected ++ unpositioned).toList) end getMissingExpectedErrors @@ -1838,9 +1842,8 @@ trait ParallelTesting extends RunnerOrchestration { self => def isUserDebugging: Boolean = val mxBean = ManagementFactory.getRuntimeMXBean mxBean.getInputArguments.asScala.exists(_.contains("jdwp")) -} -object ParallelTesting { +object ParallelTesting: def defaultOutputDir: String = "out"+JFile.separator @@ -1855,4 +1858,14 @@ object ParallelTesting { def isBestEffortTastyFile(f: JFile): Boolean = f.getName.endsWith(".betasty") -} + extension (pos: SourcePosition) + private def adjustedAtEOF: SourcePosition = + if pos.span.isSynthetic + && pos.span.isZeroExtent + && pos.span.exists + && pos.span.start == pos.source.length + && pos.source(pos.span.start - 1) == '\n' + then + pos.withSpan(pos.span.shift(-1)) + else + pos diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index 9a5d3d4b2776..50cc178b86a5 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -261,6 +261,7 @@ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[ | ForExpr | [SimpleExpr ‘.’] id ‘=’ Expr Assign(expr, expr) | PrefixOperator SimpleExpr ‘=’ Expr Assign(expr, expr) + | InfixExpr id [nl] `=' Expr Assign(expr, expr) -- only if language.postfixOps is enabled | SimpleExpr ArgumentExprs ‘=’ Expr Assign(expr, expr) | PostfixExpr [Ascription] | ‘inline’ InfixExpr MatchClause diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index f2be16f3351c..188294d7a08a 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -246,6 +246,7 @@ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[ | ForExpr | [SimpleExpr ‘.’] id ‘=’ Expr | PrefixOperator SimpleExpr ‘=’ Expr + | InfixExpr id [nl] `=' Expr -- only if language.postfixOps is enabled | SimpleExpr ArgumentExprs ‘=’ Expr | PostfixExpr [Ascription] | ‘inline’ InfixExpr MatchClause diff --git a/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala b/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala index 8640f518c0f1..2aecbb7b36b6 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala @@ -26,8 +26,8 @@ class InferExpectedType( driver: InteractiveDriver, params: OffsetParams )(implicit rc: ReportContext): - val uri = params.uri().nn - val code = params.text().nn + val uri: java.net.URI = params.uri() + val code: String = params.text() val sourceFile = SourceFile.virtual(uri, code) driver.run(uri, sourceFile) diff --git a/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala b/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala index 4e89c687a7b8..9c7d0c3fbcf0 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala @@ -120,8 +120,9 @@ object MetalsInteractive: // For a named arg, find the target `DefDef` and jump to the param case NamedArg(name, _) :: Apply(fn, _) :: _ => val funSym = fn.symbol - if funSym.is(Synthetic) && funSym.owner.is(CaseClass) then - val sym = funSym.owner.info.member(name).symbol + lazy val owner = funSym.owner.companionClass + if funSym.is(Synthetic) && owner.is(CaseClass) then + val sym = owner.info.member(name).symbol List((sym, sym.info, None)) else val paramSymbol = @@ -130,6 +131,13 @@ object MetalsInteractive: val sym = paramSymbol.getOrElse(fn.symbol) List((sym, sym.info, None)) + case NamedArg(name, _) :: UnApply(s, _, _) :: _ => + lazy val owner = s.symbol.owner.companionClass + if s.symbol.is(Synthetic) && owner.is(CaseClass) then + val sym = owner.info.member(name).symbol + List((sym, sym.info, None)) + else Nil + case (_: untpd.ImportSelector) :: (imp: Import) :: _ => importedSymbols(imp, _.span.contains(pos.span)).map(sym => (sym, sym.info, None) diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcConvertToNamedLambdaParameters.scala b/presentation-compiler/src/main/dotty/tools/pc/PcConvertToNamedLambdaParameters.scala index 2ca50107c36b..ca5f336b7bcd 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcConvertToNamedLambdaParameters.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcConvertToNamedLambdaParameters.scala @@ -120,7 +120,7 @@ object PcConvertToNamedLambdaParameters: } def isWildcardParam(param: tpd.ValDef)(using Context): Boolean = - param.name.toString.startsWith("_$") && param.symbol.is(Flags.Synthetic) + param.name.toString.startsWith("_$") def findParamReferencePosition(param: tpd.ValDef, lambda: tpd.Tree)(using Context): Option[SourcePosition] = var pos: Option[SourcePosition] = None diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala index 29396a5c0d32..71aa1626bb18 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala @@ -39,11 +39,11 @@ class PcInlayHintsProvider( symbolSearch: SymbolSearch, )(using ReportContext): - val uri = params.uri().nn - val filePath = Paths.get(uri).nn - val sourceText = params.text().nn - val text = sourceText.toCharArray().nn - val source = + val uri: java.net.URI = params.uri() + val filePath: java.nio.file.Path = Paths.get(uri) + val sourceText: String = params.text() + val text: Array[Char] = sourceText.toCharArray() + val source: SourceFile = SourceFile.virtual(filePath.toString, sourceText) driver.run(uri, source) given InlayHintsParams = params @@ -54,7 +54,7 @@ class PcInlayHintsProvider( val pos = driver.sourcePosition(params) def provide(): List[InlayHint] = - val deepFolder = DeepFolder[InlayHints](collectDecorations) + val deepFolder = PcCollector.DeepFolderWithParent[InlayHints](collectDecorations) Interactive .pathTo(driver.openedTrees(uri), pos)(using driver.currentCtx) .headOption @@ -68,11 +68,23 @@ class PcInlayHintsProvider( def collectDecorations( inlayHints: InlayHints, tree: Tree, + parent: Option[Tree] ): InlayHints = + // XRay hints are not mutually exclusive with other hints, so they must be matched separately + val firstPassHints = (tree, parent) match { + case XRayModeHint(tpe, pos) => + inlayHints.addToBlock( + adjustPos(pos).toLsp, + LabelPart(": ") :: toLabelParts(tpe, pos), + InlayHintKind.Type + ) + case _ => inlayHints + } + tree match case ImplicitConversion(symbol, range) => val adjusted = adjustPos(range) - inlayHints + firstPassHints .add( adjusted.startPos.toLsp, labelPart(symbol, symbol.decodedName) :: LabelPart("(") :: Nil, @@ -84,17 +96,17 @@ class PcInlayHintsProvider( InlayHintKind.Parameter, ) case ImplicitParameters(trees, pos) => - inlayHints.add( + firstPassHints.add( adjustPos(pos).toLsp, ImplicitParameters.partsFromImplicitArgs(trees).map((label, maybeSymbol) => maybeSymbol match case Some(symbol) => labelPart(symbol, label) case None => LabelPart(label) ), - InlayHintKind.Parameter + InlayHintKind.Parameter, ) case ValueOf(label, pos) => - inlayHints.add( + firstPassHints.add( adjustPos(pos).toLsp, LabelPart("(") :: LabelPart(label) :: List(LabelPart(")")), InlayHintKind.Parameter, @@ -102,7 +114,7 @@ class PcInlayHintsProvider( case TypeParameters(tpes, pos, sel) if !syntheticTupleApply(sel) => val label = tpes.map(toLabelParts(_, pos)).separated("[", ", ", "]") - inlayHints.add( + firstPassHints.add( adjustPos(pos).endPos.toLsp, label, InlayHintKind.Type, @@ -110,16 +122,16 @@ class PcInlayHintsProvider( case InferredType(tpe, pos, defTree) if !isErrorTpe(tpe) => val adjustedPos = adjustPos(pos).endPos - if inlayHints.containsDef(adjustedPos.start) then inlayHints + if firstPassHints.containsDef(adjustedPos.start) then firstPassHints else - inlayHints + firstPassHints .add( adjustedPos.toLsp, LabelPart(": ") :: toLabelParts(tpe, pos), InlayHintKind.Type, ) .addDefinition(adjustedPos.start) - case Parameters(isInfixFun, args) => + case Parameters(isInfixFun, args) => def isNamedParam(pos: SourcePosition): Boolean = val start = text.indexWhere(!_.isWhitespace, pos.start) val end = text.lastIndexWhere(!_.isWhitespace, pos.end - 1) @@ -138,13 +150,13 @@ class PcInlayHintsProvider( pos.withStart(pos.start + 1) - args.foldLeft(inlayHints) { + args.foldLeft(firstPassHints) { case (ih, (name, pos0, isByName)) => val pos = adjustPos(pos0) val isBlock = isBlockParam(pos) - val namedLabel = + val namedLabel = if params.namedParameters() && !isInfixFun && !isBlock && !isNamedParam(pos) then s"${name} = " else "" - val byNameLabel = + val byNameLabel = if params.byNameParameters() && isByName && (!isInfixFun || isBlock) then "=> " else "" val labelStr = s"${namedLabel}${byNameLabel}" @@ -158,7 +170,7 @@ class PcInlayHintsProvider( ) else ih } - case _ => inlayHints + case _ => firstPassHints private def toLabelParts( tpe: Type, @@ -432,19 +444,19 @@ object InferredType: end InferredType object Parameters: - def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context): Option[(Boolean, List[(Name, SourcePosition, Boolean)])] = - def shouldSkipFun(fun: Tree)(using Context): Boolean = + def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context): Option[(Boolean, List[(Name, SourcePosition, Boolean)])] = + def shouldSkipFun(fun: Tree)(using Context): Boolean = fun match case sel: Select => isForComprehensionMethod(sel) || sel.symbol.name == nme.unapply || sel.symbol.is(Flags.JavaDefined) case _ => false - def isInfixFun(fun: Tree, args: List[Tree])(using Context): Boolean = + def isInfixFun(fun: Tree, args: List[Tree])(using Context): Boolean = val isInfixSelect = fun match case Select(sel, _) => sel.isInfix case _ => false val source = fun.source if args.isEmpty then isInfixSelect - else + else (!(fun.span.end until args.head.span.start) .map(source.apply) .contains('.') && fun.symbol.is(Flags.ExtensionMethod)) || isInfixSelect @@ -467,7 +479,7 @@ object Parameters: if (params.namedParameters() || params.byNameParameters()) then tree match - case Apply(fun, args) if isRealApply(fun) => + case Apply(fun, args) if isRealApply(fun) => val underlyingFun = getUnderlyingFun(fun) if shouldSkipFun(underlyingFun) then None @@ -475,7 +487,7 @@ object Parameters: val funTp = fun.typeOpt.widenTermRefExpr val paramNames = funTp.paramNamess.flatten val paramInfos = funTp.paramInfoss.flatten - + Some( isInfixFun(fun, args) || underlyingFun.isInfix, ( @@ -483,7 +495,7 @@ object Parameters: .zip(paramNames) .zip(paramInfos) .collect { - case ((arg, paramName), paramInfo) if !arg.span.isZeroExtent && !isDefaultArg(arg) => + case ((arg, paramName), paramInfo) if !arg.span.isZeroExtent && !isDefaultArg(arg) => (paramName.fieldName, arg.sourcePos, paramInfo.isByName) } ) @@ -491,3 +503,55 @@ object Parameters: case _ => None else None end Parameters + +object XRayModeHint: + def unapply(trees: (Tree, Option[Tree]))(using params: InlayHintsParams, ctx: Context): Option[(Type, SourcePosition)] = + if params.hintsXRayMode() then + val (tree, parent) = trees + val isParentApply = parent match + case Some(_: Apply) => true + case _ => false + val isParentOnSameLine = parent match + case Some(sel: Select) if sel.isForComprehensionMethod => false + case Some(par) if par.sourcePos.exists && par.sourcePos.line == tree.sourcePos.line => true + case _ => false + + tree match + /* + anotherTree + .innerSelect() + */ + case a @ Apply(inner, _) + if inner.sourcePos.exists && !isParentOnSameLine && !isParentApply && + endsInSimpleSelect(a) && isEndOfLine(tree.sourcePos) => + Some((a.tpe.widen.deepDealiasAndSimplify, tree.sourcePos)) + /* + innerTree + .select + */ + case select @ Select(innerTree, _) + if innerTree.sourcePos.exists && !isParentOnSameLine && !isParentApply && + isEndOfLine(tree.sourcePos) => + Some((select.tpe.widen.deepDealiasAndSimplify, tree.sourcePos)) + case _ => None + else None + + @tailrec + private def endsInSimpleSelect(ap: Tree)(using ctx: Context): Boolean = + ap match + case Apply(sel: Select, _) => + sel.name != nme.apply && !isInfix(sel) + case Apply(TypeApply(sel: Select, _), _) => + sel.name != nme.apply && !isInfix(sel) + case Apply(innerTree @ Apply(_, _), _) => + endsInSimpleSelect(innerTree) + case _ => false + + private def isEndOfLine(pos: SourcePosition): Boolean = + if pos.exists then + val source = pos.source + val end = pos.end + end >= source.length || source(end) == '\n' || source(end) == '\r' + else false + +end XRayModeHint diff --git a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala index 18311d1b7853..81517524e3a6 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala @@ -119,7 +119,7 @@ case class ScalaPresentationCompiler( ): PresentationCompiler = copy(completionItemPriority = priority) - override def withBuildTargetName(buildTargetName: String) = + override def withBuildTargetName(buildTargetName: String): PresentationCompiler = copy(buildTargetName = Some(buildTargetName)) override def withReportsLoggerLevel(level: String): PresentationCompiler = diff --git a/presentation-compiler/src/main/dotty/tools/pc/WithCompilationUnit.scala b/presentation-compiler/src/main/dotty/tools/pc/WithCompilationUnit.scala index 56be6614bbd4..5ba89324fca8 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/WithCompilationUnit.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/WithCompilationUnit.scala @@ -22,11 +22,11 @@ class WithCompilationUnit( val driver: InteractiveDriver, params: VirtualFileParams, ): - val uri = params.uri() - val filePath = Paths.get(uri) - val sourceText = params.text - val text = sourceText.toCharArray() - val source = + val uri: java.net.URI = params.uri() + val filePath: java.nio.file.Path = Paths.get(uri) + val sourceText: String = params.text + val text: Array[Char] = sourceText.toCharArray() + val source: SourceFile = SourceFile.virtual(filePath.toString, sourceText) driver.run(uri, source) given ctx: Context = driver.currentCtx diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala index 4ed58c773a7c..78f9f5f68bfb 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala @@ -56,6 +56,7 @@ case class CompletionAffix( private def loopPrefix(prefixes: List[PrefixKind]): String = prefixes match case PrefixKind.New :: tail => "new " + loopPrefix(tail) + case PrefixKind.Using :: tail => "using " + loopPrefix(tail) case _ => "" /** @@ -87,7 +88,7 @@ enum SuffixKind: case Brace, Bracket, Template, NoSuffix enum PrefixKind: - case New + case New, Using type Suffix = Affix[SuffixKind] type Prefix = Affix[PrefixKind] 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 b396dd780cc0..b594c6fbdb2f 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -15,6 +15,7 @@ import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.core.Comments.Comment import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Decorators.toTermName import dotty.tools.dotc.core.Denotations.SingleDenotation import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Flags.* @@ -74,7 +75,15 @@ class Completions( case tpe :: (appl: AppliedTypeTree) :: _ if appl.tpt == tpe => false case sel :: (funSel @ Select(fun, name)) :: (appl: GenericApply) :: _ if appl.fun == funSel && sel == fun => false - case _ => true) + case _ => true) && + (adjustedPath match + /* In case of `class X derives TC@@` we shouldn't add `[]` + */ + case Ident(_) :: (templ: untpd.DerivingTemplate) :: _ => + val pos = completionPos.toSourcePosition + !templ.derived.exists(_.sourcePos.contains(pos)) + case _ => true + ) private lazy val isNew: Boolean = Completion.isInNewContext(adjustedPath) @@ -193,13 +202,23 @@ class Completions( ) end isAbstractType - private def findSuffix(symbol: Symbol): CompletionAffix = + private def findSuffix(symbol: Symbol, adjustedPath: List[untpd.Tree]): CompletionAffix = CompletionAffix.empty .chain { suffix => // for [] suffix if shouldAddSuffix && symbol.info.typeParams.nonEmpty then suffix.withNewSuffixSnippet(Affix(SuffixKind.Bracket)) else suffix } + .chain{ suffix => + adjustedPath match + case (ident: Ident) :: (app@Apply(_, List(arg))) :: _ => + app.symbol.info match + case mt@MethodType(termNames) if app.symbol.paramSymss.last.exists(_.is(Given)) && + !text.substring(app.fun.span.start, arg.span.end).contains("using") => + suffix.withNewPrefix(Affix(PrefixKind.Using)) + case _ => suffix + case _ => suffix + } .chain { suffix => // for () suffix if shouldAddSuffix && symbol.is(Flags.Method) then val paramss = getParams(symbol) @@ -271,7 +290,7 @@ class Completions( val existsApply = extraMethodDenots.exists(_.symbol.name == nme.apply) extraMethodDenots.map { methodDenot => - val suffix = findSuffix(methodDenot.symbol) + val suffix = findSuffix(methodDenot.symbol, adjustedPath) val affix = if methodDenot.symbol.isConstructor && existsApply then adjustedPath match case (select @ Select(qual, _)) :: _ => @@ -293,7 +312,7 @@ class Completions( if skipOriginalDenot then extraCompletionValues else - val suffix = findSuffix(denot.symbol) + val suffix = findSuffix(denot.symbol, adjustedPath) val name = undoBacktick(label) val denotCompletionValue = toCompletionValue(name, denot, suffix) denotCompletionValue :: extraCompletionValues @@ -747,6 +766,13 @@ class Completions( ).flatMap(_.alternatives.map(_.symbol)).toSet ) + private lazy val EqualsClass: ClassSymbol = requiredClass("scala.Equals") + private lazy val ArrowAssocClass: ClassSymbol = requiredClass("scala.Predef.ArrowAssoc") + private lazy val EnsuringClass: ClassSymbol = requiredClass("scala.Predef.Ensuring") + private lazy val StringFormatClass: ClassSymbol = requiredClass("scala.Predef.StringFormat") + private lazy val nnMethod: Symbol = defn.ScalaPredefModule.info.member("nn".toTermName).symbol + private lazy val runtimeCheckedMethod: Symbol = defn.ScalaPredefModule.info.member("runtimeChecked".toTermName).symbol + private def isNotLocalForwardReference(sym: Symbol)(using Context): Boolean = !sym.isLocalToBlock || !sym.srcPos.isAfter(completionPos.originalCursorPosition) || @@ -765,6 +791,17 @@ class Completions( (sym.isField && !isJavaClass && !isModuleOrClass) || sym.getter != NoSymbol catch case _ => false + def isInheritedFromScalaLibrary(sym: Symbol) = + sym.owner == defn.AnyClass || + sym.owner == defn.ObjectClass || + sym.owner == defn.ProductClass || + sym.owner == EqualsClass || + sym.owner == ArrowAssocClass || + sym.owner == EnsuringClass || + sym.owner == StringFormatClass || + sym == nnMethod || + sym == runtimeCheckedMethod + def symbolRelevance(sym: Symbol): Int = var relevance = 0 // symbols defined in this file are more relevant @@ -782,7 +819,7 @@ class Completions( case _ => // symbols whose owner is a base class are less relevant - if sym.owner == defn.AnyClass || sym.owner == defn.ObjectClass + if isInheritedFromScalaLibrary(sym) then relevance |= IsInheritedBaseMethod // symbols not provided via an implicit are more relevant if sym.is(Implicit) || @@ -794,7 +831,7 @@ class Completions( // accessors of case class members are more relevant if !sym.is(CaseAccessor) then relevance |= IsNotCaseAccessor // public symbols are more relevant - if !sym.isPublic then relevance |= IsNotCaseAccessor + if !sym.isPublic then relevance |= IsNotPublic // synthetic symbols are less relevant (e.g. `copy` on case classes) if sym.is(Synthetic) && !sym.isAllOf(EnumCase) then relevance |= IsSynthetic @@ -888,7 +925,7 @@ class Completions( application: CompletionApplication ): Ordering[CompletionValue] = new Ordering[CompletionValue]: - val queryLower = completionPos.query.toLowerCase() + val queryLower: String = completionPos.query.toLowerCase() val fuzzyCache = mutable.Map.empty[CompletionValue, Int] def compareLocalSymbols(s1: Symbol, s2: Symbol): Int = diff --git a/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala b/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala index 32c6ad26c6a5..431e4908013a 100644 --- a/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/base/BaseInlayHintsSuite.scala @@ -34,6 +34,7 @@ class BaseInlayHintsSuite extends BasePCSuite { inferredTypes = true, typeParameters = true, implicitParameters = true, + hintsXRayMode = true, byNameParameters = true, implicitConversions = true, namedParameters = true, diff --git a/presentation-compiler/test/dotty/tools/pc/base/BasePCSuite.scala b/presentation-compiler/test/dotty/tools/pc/base/BasePCSuite.scala index a26c31ef084d..5128303d105f 100644 --- a/presentation-compiler/test/dotty/tools/pc/base/BasePCSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/base/BasePCSuite.scala @@ -25,7 +25,7 @@ import org.junit.runner.RunWith import scala.meta.pc.CompletionItemPriority object TestResources: - val classpath = BuildInfo.ideTestsDependencyClasspath.map(_.toPath).toSeq + val classpath: Seq[Path] = BuildInfo.ideTestsDependencyClasspath.map(_.toPath).toSeq val classpathSearch = ClasspathSearch.fromClasspath(classpath, ExcludedPackagesHandler.default) @@ -34,7 +34,7 @@ abstract class BasePCSuite extends PcAssertions: val completionItemPriority: CompletionItemPriority = (_: String) => 0 private val isDebug = ManagementFactory.getRuntimeMXBean.getInputArguments.toString.contains("-agentlib:jdwp") - val tmp = Files.createTempDirectory("stable-pc-tests") + val tmp: Path = Files.createTempDirectory("stable-pc-tests") val executorService: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor() val testingWorkspaceSearch = TestingWorkspaceSearch( diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala index 044b5456d31d..910044485896 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala @@ -238,6 +238,65 @@ class CompletionArgSuite extends BaseCompletionSuite: "" ) + @Test def `using` = + checkEdit( + s"""|def hello(using String): Unit = ??? + |@main def main1(): Unit = + | val str = "hello" + | hello(st@@) + |""".stripMargin, + s"""|def hello(using String): Unit = ??? + |@main def main1(): Unit = + | val str = "hello" + | hello(using str) + |""".stripMargin, + assertSingleItem = false) + + @Test def `using2` = + checkEdit( + s"""|def hello(using String): Unit = ??? + |@main def main1(): Unit = + | val str = "hello" + | hello(using st@@) + |""".stripMargin, + s"""|def hello(using String): Unit = ??? + |@main def main1(): Unit = + | val str = "hello" + | hello(using str) + |""".stripMargin, + assertSingleItem = false) + + @Test def `using3` = + checkEdit( + s"""|def hello(using String, Int): Unit = ??? + |@main def main1(): Unit = + | val str = "hello" + | val int = 4 + | hello(str, in@@) + |""".stripMargin, + s"""|def hello(using String, Int): Unit = ??? + |@main def main1(): Unit = + | val str = "hello" + | val int = 4 + | hello(str, int) + |""".stripMargin, + assertSingleItem = false) + + @Test def `using4` = + checkEdit( + s"""|def hello(name: String)(using String): Unit = ??? + |@main def main1(): Unit = + | val str = "hello" + | hello("name")(str@@) + |""".stripMargin, + s"""|def hello(name: String)(using String): Unit = ??? + |@main def main1(): Unit = + | val str = "hello" + | hello("name")(using str) + |""".stripMargin, + assertSingleItem = false + ) + @Test def `default-args` = check( s"""|object Main { diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtensionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtensionSuite.scala index b41084af4a8e..16535381165c 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtensionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtensionSuite.scala @@ -437,3 +437,44 @@ class CompletionExtensionSuite extends BaseCompletionSuite: |""".stripMargin, assertSingleItem = false ) + + @Test def `extension-for-case-class` = + check( + """|case class Bar(): + | def baz(): Unit = ??? + | + |object Bar: + | extension (f: Bar) + | def qux: Unit = ??? + | + |object Main: + | val _ = Bar().@@ + |""".stripMargin, + """|baz(): Unit + |copy(): Bar + |qux: Unit + |asInstanceOf[X0]: X0 + |canEqual(that: Any): Boolean + |equals(x$0: Any): Boolean + |getClass[X0 >: Bar](): Class[? <: X0] + |hashCode(): Int + |isInstanceOf[X0]: Boolean + |productArity: Int + |productElement(n: Int): Any + |productElementName(n: Int): String + |productElementNames: Iterator[String] + |productIterator: Iterator[Any] + |productPrefix: String + |synchronized[X0](x$0: X0): X0 + |toString(): String + |->[B](y: B): (Bar, B) + |ensuring(cond: Boolean): Bar + |ensuring(cond: Bar => Boolean): Bar + |ensuring(cond: Boolean, msg: => Any): Bar + |ensuring(cond: Bar => Boolean, msg: => Any): Bar + |nn: `?1`.type + |runtimeChecked: `?2`.type + |formatted(fmtstr: String): String + |→[B](y: B): (Bar, B) + | """.stripMargin + ) 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 277a579ba4ce..08e1b293a4ce 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -109,18 +109,9 @@ class CompletionSuite extends BaseCompletionSuite: |tabulate[A](n: Int)(f: Int => A): List[A] |unapplySeq[A](x: List[A] @uncheckedVariance): UnapplySeqWrapper[A] |unfold[A, S](init: S)(f: S => Option[(A, S)]): List[A] - |->[B](y: B): (List.type, B) - |ensuring(cond: Boolean): List.type - |ensuring(cond: List.type => Boolean): List.type - |ensuring(cond: Boolean, msg: => Any): List.type - |ensuring(cond: List.type => Boolean, msg: => Any): List.type |fromSpecific(from: Any)(it: IterableOnce[Nothing]): List[Nothing] |fromSpecific(it: IterableOnce[Nothing]): List[Nothing] - |nn: List.type - |runtimeChecked scala.collection.immutable |toFactory(from: Any): Factory[Nothing, List[Nothing]] - |formatted(fmtstr: String): String - |→[B](y: B): (List.type, B) |iterableFactory[A]: Factory[A, List[A]] |asInstanceOf[X0]: X0 |equals(x$0: Any): Boolean @@ -129,6 +120,15 @@ class CompletionSuite extends BaseCompletionSuite: |isInstanceOf[X0]: Boolean |synchronized[X0](x$0: X0): X0 |toString(): String + |->[B](y: B): (List.type, B) + |ensuring(cond: Boolean): List.type + |ensuring(cond: List.type => Boolean): List.type + |ensuring(cond: Boolean, msg: => Any): List.type + |ensuring(cond: List.type => Boolean, msg: => Any): List.type + |nn: List.type + |runtimeChecked scala.collection.immutable + |formatted(fmtstr: String): String + |→[B](y: B): (List.type, B) |""".stripMargin ) @@ -2053,6 +2053,28 @@ class CompletionSuite extends BaseCompletionSuite: """.stripMargin, ) + @Test def `namedTuple completions-3` = + check( + """|import scala.NamedTuple.* + | + |val person = (name = "Jakub", city = "Wrocław") + | + |val n = person.@@name""".stripMargin, + "name: String", + filter = _ == "name: String" + ) + + @Test def `namedTuple completions-4` = + check( + """|import scala.NamedTuple.* + | + |val person = (name = "Jakub", city = "Wrocław") + | + |val n = person.n@@ame""".stripMargin, + "name: String", + filter = _ == "name: String" + ) + @Test def `Selectable with namedTuple Fields member` = check( """|import scala.NamedTuple.* @@ -2285,3 +2307,11 @@ class CompletionSuite extends BaseCompletionSuite: |""".stripMargin, "asTerm: Term" ) + + @Test def `derives-no-square-brackets` = + check( + """ + |case class Miau(y: Int) derives Ordering, CanEqu@@ + |""".stripMargin, + "CanEqual scala" + ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/definition/PcDefinitionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/definition/PcDefinitionSuite.scala index 0eebade8afc9..108e74738f71 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/definition/PcDefinitionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/definition/PcDefinitionSuite.scala @@ -647,3 +647,34 @@ class PcDefinitionSuite extends BasePcDefinitionSuite: | export scala.collection.immutable.V/*scala/collection/immutable/Vector. Vector.scala*/@@ector |""".stripMargin ) + + @Test def i7763 = + check( + """|case class MyItem(<>: String) + | + |def handle(item: MyItem) = + | item match { + | case MyItem(na@@me = n2) => println(n2) + | } + |""".stripMargin + ) + + @Test def `i7763-neg` = + check( + """|object MyItem: + | def unapply(name: String): Option[Int] = ??? + | + |def handle(item: String) = + | item match { + | case MyItem(na@@me = n2) => println(n2) + | } + |""".stripMargin + ) + + @Test def `i7763-apply` = + check( + """|case class MyItem(<>: String) + | + |def handle(item: String) = MyItem(na@@me = item) + |""".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 60827f1e3590..16d92e0e2282 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/hover/HoverTermSuite.scala @@ -6,6 +6,18 @@ import org.junit.Test class HoverTermSuite extends BaseHoverSuite: + @Test def `n-ary lamba` = + check( + """|object testRepor { + | val listOfTuples = List(1 -> 1, 2 -> 2, 3 -> 3) + | + | listOfTuples.map((k@@ey, value) => key + value) + |} + |""".stripMargin, + """|val key: Int + |""".stripMargin.hover + ) + @Test def `map` = check( """object a { @@ -926,3 +938,15 @@ class HoverTermSuite extends BaseHoverSuite: |""".stripMargin, "val aa: Int".hover ) + + @Test def i7763 = + check( + """|case class MyItem(name: String) + | + |def handle(item: MyItem) = + | item match { + | case MyItem(na@@me = n2) => println(n2) + | } + |""".stripMargin, + "val name: String".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 d9c10080581f..daad8b974620 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/inlayHints/InlayHintsSuite.scala @@ -1334,4 +1334,338 @@ class InlayHintsSuite extends BaseInlayHintsSuite { |""".stripMargin ) + @Test def `xray-single-chain-same-line` = + check( + """|object Main{ + | trait Bar { + | def bar: Bar + | } + | + | trait Foo { + | def foo(): Foo + | } + | + |val bar: Bar = ??? + |val foo: Foo = ??? + | + |val thing1: Bar = bar.bar + |val thing2: Foo = foo.foo() + |} + |""".stripMargin, + """|object Main{ + | trait Bar { + | def bar: Bar + | } + | + | trait Foo { + | def foo(): Foo + | } + | + |val bar: Bar = ??? + |val foo: Foo = ??? + | + |val thing1: Bar = bar.bar + |val thing2: Foo = foo.foo() + |} + |""".stripMargin + ) + + @Test def `xray-multi-chain-same-line` = + check( + """|object Main{ + | trait Bar { + | def bar: Bar + | } + | + | trait Foo { + | def foo(): Foo + | } + | + |val bar: Bar = ??? + |val foo: Foo = ??? + | + |val thing1: Bar = bar.bar.bar + |val thing2: Foo = foo.foo().foo() + |} + |""".stripMargin, + """|object Main{ + | trait Bar { + | def bar: Bar + | } + | + | trait Foo { + | def foo(): Foo + | } + | + |val bar: Bar = ??? + |val foo: Foo = ??? + | + |val thing1: Bar = bar.bar.bar + |val thing2: Foo = foo.foo().foo() + |} + |""".stripMargin + ) + + @Test def `xray-single-chain-new-line` = + check( + """|object Main{ + | trait Bar { + | def bar: Bar + | } + | + | trait Foo { + | def foo(): Foo + | } + | + |val bar: Bar = ??? + |val foo: Foo = ??? + | + |val thing1: Bar = bar + | .bar + |val thing2: Foo = foo + | .foo() + |} + |""".stripMargin, + """|object Main{ + | trait Bar { + | def bar: Bar + | } + | + | trait Foo { + | def foo(): Foo + | } + | + |val bar: Bar = ??? + |val foo: Foo = ??? + | + |val thing1: Bar = bar + | .bar + |val thing2: Foo = foo + | .foo() + |} + |""".stripMargin + ) + + @Test def `xray-simple-chain` = + check( + """|object Main{ + | trait Foo { + | def bar: Bar + | } + | + | trait Bar { + | def foo(): Foo + | } + | + |val foo: Foo = ??? + | + |val thingy: Bar = foo + | .bar + | .foo() + | .bar + |} + |""".stripMargin, + """|object Main{ + | trait Foo { + | def bar: Bar + | } + | + | trait Bar { + | def foo(): Foo + | } + | + |val foo: Foo = ??? + | + |val thingy: Bar = foo + | .bar/* : Bar<<(6:8)>>*/ + | .foo()/*: Foo<<(2:8)>>*/ + | .bar/* : Bar<<(6:8)>>*/ + |} + |""".stripMargin + ) + + @Test def `xray-long-chain` = + check( + """|object Main{ + | trait Foo[F] { + | def intify: Foo[Int] + | def stringListify(s: String*): Foo[String] + | } + | + |val foo: Foo[String] = ??? + | + |val thingy: Foo[Int] = foo + | .intify + | .stringListify( + | "Hello", + | "World" + | ) + | .stringListify( + | "Hello", + | "World" + | ) + | .intify + | .intify + |} + |""".stripMargin, + """|object Main{ + | trait Foo[F] { + | def intify: Foo[Int] + | def stringListify(s: String*): Foo[String] + | } + | + |val foo: Foo[String] = ??? + | + |val thingy: Foo[Int] = foo + | .intify/*: Foo<<(2:8)>>[Int<>]*/ + | .stringListify( + | /*s = */"Hello", + | "World" + | )/* : Foo<<(2:8)>>[String<>]*/ + | .stringListify( + | /*s = */"Hello", + | "World" + | )/* : Foo<<(2:8)>>[String<>]*/ + | .intify/*: Foo<<(2:8)>>[Int<>]*/ + | .intify/*: Foo<<(2:8)>>[Int<>]*/ + |} + |""".stripMargin + ) + + @Test def `xray-long-chain-same-line` = + check( + """|object Main{ + | trait Foo[F] { + | def intify: Foo[Int] + | def stringListify(s: String*): Foo[String] + | } + | + |val foo: Foo[String] = ??? + | + |val thingy: Foo[Int] = foo + | .intify + | .stringListify( + | "Hello", + | "World" + | ) + | .stringListify( + | "Hello", + | "World" + | ) + | .intify.intify + |} + |""".stripMargin, + """|object Main{ + | trait Foo[F] { + | def intify: Foo[Int] + | def stringListify(s: String*): Foo[String] + | } + | + |val foo: Foo[String] = ??? + | + |val thingy: Foo[Int] = foo + | .intify/* : Foo<<(2:8)>>[Int<>]*/ + | .stringListify( + | /*s = */"Hello", + | "World" + | )/* : Foo<<(2:8)>>[String<>]*/ + | .stringListify( + | /*s = */"Hello", + | "World" + | )/* : Foo<<(2:8)>>[String<>]*/ + | .intify.intify/*: Foo<<(2:8)>>[Int<>]*/ + |} + |""".stripMargin + ) + + @Test def `xray-tikka-masala-curried` = + check( + """|object Main{ + | trait Foo[F] { + | def intify: Foo[Int] + | def stringListify(s: String)(s2: String): Foo[String] + | } + | + |val foo: Foo[String] = ??? + | + |val thingy: Foo[Int] = foo + | .intify + | .stringListify( + | "Hello" + | )( + | "World" + | ) + | .stringListify( + | "Hello" + | )( + | "World" + | ) + | .intify + | .intify + |} + |""".stripMargin, + """|object Main{ + | trait Foo[F] { + | def intify: Foo[Int] + | def stringListify(s: String)(s2: String): Foo[String] + | } + | + |val foo: Foo[String] = ??? + | + |val thingy: Foo[Int] = foo + | .intify/*: Foo<<(2:8)>>[Int<>]*/ + | .stringListify( + | /*s = */"Hello" + | )( + | /*s2 = */"World" + | )/* : Foo<<(2:8)>>[String<>]*/ + | .stringListify( + | /*s = */"Hello" + | )( + | /*s2 = */"World" + | )/* : Foo<<(2:8)>>[String<>]*/ + | .intify/*: Foo<<(2:8)>>[Int<>]*/ + | .intify/*: Foo<<(2:8)>>[Int<>]*/ + |} + |""".stripMargin + ) + + @Test def `xray-for-comprehension` = + check( + """|object Main{ + |trait Foo[A]{ + | def flatMap[B](f: A => Foo[B]): Foo[B] + | def map[B](f: A => B): Foo[B] + | def bar(s: String): Foo[A] + |} + |val foo1: Foo[String] = ??? + |val foo2: Foo[Int] = ??? + |val result = for { + | foo <- foo1 + | bar <- foo2 + | .bar(s = foo) + | .bar(s = foo) + | .bar(s = foo) + |} yield bar + |} + |""".stripMargin, + """|object Main{ + |trait Foo[A]{ + | def flatMap[B](f: A => Foo[B]): Foo[B] + | def map[B](f: A => B): Foo[B] + | def bar(s: String): Foo[A] + |} + |val foo1: Foo[String] = ??? + |val foo2: Foo[Int] = ??? + |val result/*: Foo<<(2:6)>>[Int<>]*/ = for { + | foo <- foo1 + | bar <- foo2 + | .bar(s = foo)/*: Foo<<(2:6)>>[Int<>]*/ + | .bar(s = foo)/*: Foo<<(2:6)>>[Int<>]*/ + | .bar(s = foo)/*: Foo<<(2:6)>>[Int<>]*/ + |} yield bar + |} + |""".stripMargin + ) + } diff --git a/presentation-compiler/test/dotty/tools/pc/utils/MockSymbolSearch.scala b/presentation-compiler/test/dotty/tools/pc/utils/MockSymbolSearch.scala index 459c41e3c8e5..735ee846be2d 100644 --- a/presentation-compiler/test/dotty/tools/pc/utils/MockSymbolSearch.scala +++ b/presentation-compiler/test/dotty/tools/pc/utils/MockSymbolSearch.scala @@ -66,7 +66,7 @@ class MockSymbolSearch( override def documentation( symbol: String, parents: ParentSymbols - ) = documentation(symbol, parents, ContentType.MARKDOWN) + ): Optional[SymbolDocumentation] = documentation(symbol, parents, ContentType.MARKDOWN) override def documentation( symbol: String, diff --git a/project/Build.scala b/project/Build.scala index cd45bdf4ceda..db4966cb4231 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" + val referenceVersion = "3.7.3" /** Version of the Scala compiler targeted in the current release cycle * Contains a version without RC/SNAPSHOT/NIGHTLY specific suffixes @@ -63,7 +63,7 @@ object Build { * * Warning: Change of this variable might require updating `expectedTastyVersion` */ - val developedVersion = "3.7.3" + val developedVersion = "3.7.4" /** The version of the compiler including the RC prefix. * Defined as common base before calculating environment specific suffixes in `dottyVersion` @@ -137,7 +137,7 @@ object Build { val mimaPreviousLTSDottyVersion = "3.3.0" /** Version of Scala CLI to download */ - val scalaCliLauncherVersion = "1.9.0" + val scalaCliLauncherVersion = "1.9.1" /** Version of Coursier to download for initializing the local maven repo of Scala command */ val coursierJarVersion = "2.1.24" @@ -670,6 +670,13 @@ object Build { recur(lines, false) } + /** Replaces JDK 9+ methods with their JDK 8 alterantive */ + def replaceJDKIncompatibilities(lines: List[String]): List[String] = { + lines.map( + _.replaceAll("""(\").repeat\((\w+)\)""", """$1 * $2""") + ) + } + /** replace imports of `com.google.protobuf.*` with compiler implemented version */ def replaceProtobuf(lines: List[String]): List[String] = { def recur(ls: List[String]): List[String] = ls match { @@ -2580,7 +2587,7 @@ object Build { BuildInfoPlugin.buildInfoDefaultSettings lazy val presentationCompilerSettings = { - val mtagsVersion = "1.5.3" + val mtagsVersion = "1.6.2" Seq( libraryDependencies ++= Seq( "org.lz4" % "lz4-java" % "1.8.0", @@ -2623,7 +2630,7 @@ object Build { val mtagsSharedSources = (targetDir ** "*.scala").get.toSet mtagsSharedSources.foreach(f => { val lines = IO.readLines(f) - val substitutions = (replaceProtobuf(_)) andThen (insertUnsafeNullsImport(_)) + val substitutions = (replaceProtobuf(_)) andThen (insertUnsafeNullsImport(_)) andThen (replaceJDKIncompatibilities(_)) IO.writeLines(f, substitutions(lines)) }) mtagsSharedSources diff --git a/project/ScaladocGeneration.scala b/project/ScaladocGeneration.scala index aac9f187a888..c06122f5fadf 100644 --- a/project/ScaladocGeneration.scala +++ b/project/ScaladocGeneration.scala @@ -141,6 +141,10 @@ object ScaladocGeneration { def key: String = "-dynamic-side-menu" } + case class SuppressCC(value: Boolean) extends Arg[Boolean] { + def key: String = "-suppressCC" + } + import _root_.scala.reflect._ trait GenerationConfig { diff --git a/project/build.properties b/project/build.properties index 6520f6981d5a..e480c675f2fd 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.11.0 +sbt.version=1.11.5 diff --git a/project/plugins.sbt b/project/plugins.sbt index 3e1ccf5e8710..510afef8d8aa 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -20,10 +20,7 @@ addSbtPlugin("ch.epfl.scala" % "sbt-tasty-mima" % "1.0.0") addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.10.0") -resolvers += - "Develocity Artifactory" at "https://repo.grdev.net/artifactory/public/" - -addSbtPlugin("com.gradle" % "sbt-develocity" % "1.2.2-rc-1") +addSbtPlugin("com.gradle" % "sbt-develocity" % "1.3.1") addSbtPlugin("com.gradle" % "sbt-develocity-common-custom-user-data" % "1.1") diff --git a/scala2-library-cc/src/scala/collection/IndexedSeqView.scala b/scala2-library-cc/src/scala/collection/IndexedSeqView.scala index 78f8abb8e327..07698bc09951 100644 --- a/scala2-library-cc/src/scala/collection/IndexedSeqView.scala +++ b/scala2-library-cc/src/scala/collection/IndexedSeqView.scala @@ -160,7 +160,7 @@ object IndexedSeqView { @SerialVersionUID(3L) class Reverse[A](underlying: SomeIndexedSeqOps[A]^) extends SeqView.Reverse[A](underlying) with IndexedSeqView[A] { - override def reverse: IndexedSeqView[A] = underlying match { + override def reverse: IndexedSeqView[A]^{underlying} = underlying match { case x: IndexedSeqView[A] => x case _ => super.reverse } diff --git a/scala2-library-cc/src/scala/collection/View.scala b/scala2-library-cc/src/scala/collection/View.scala index 72a073836e77..b8de97159f06 100644 --- a/scala2-library-cc/src/scala/collection/View.scala +++ b/scala2-library-cc/src/scala/collection/View.scala @@ -152,15 +152,13 @@ object View extends IterableFactory[View] { def apply[A](underlying: Iterable[A]^, p: A => Boolean, isFlipped: Boolean): Filter[A]^{underlying, p} = underlying match { case filter: Filter[A] if filter.isFlipped == isFlipped => - new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped) - .asInstanceOf[Filter[A]^{underlying, p}] + unsafeAssumeSeparate: + new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped) + .asInstanceOf[Filter[A]^{underlying, p}] // !!! asInstanceOf needed once paths were added, see path-patmat-should-be-pos.scala for minimization - //case filter: Filter[A]^{underlying} if filter.isFlipped == isFlipped => - // unsafeAssumeSeparate: // See filter-iterable.scala for a test where a variant of Filter // works without the unsafeAssumeSeparate. But it requires significant // changes compared to the version here. See also Filter in colltest5.CollectionStrawManCC5_1. - // new Filter(filter.underlying, a => filter.p(a) && p(a), isFlipped) case _ => new Filter(underlying, p, isFlipped) } } diff --git a/scala2-library-cc/src/scala/collection/mutable/CheckedIndexedSeqView.scala b/scala2-library-cc/src/scala/collection/mutable/CheckedIndexedSeqView.scala index 1c3f669f5358..89e3dfb78d8e 100644 --- a/scala2-library-cc/src/scala/collection/mutable/CheckedIndexedSeqView.scala +++ b/scala2-library-cc/src/scala/collection/mutable/CheckedIndexedSeqView.scala @@ -101,7 +101,7 @@ private[mutable] object CheckedIndexedSeqView { @SerialVersionUID(3L) class Reverse[A](underlying: SomeIndexedSeqOps[A]^)(protected val mutationCount: () ->{cap.rd} Int) extends IndexedSeqView.Reverse[A](underlying) with CheckedIndexedSeqView[A] { - override def reverse: IndexedSeqView[A] = underlying match { + override def reverse: IndexedSeqView[A]^{underlying} = underlying match { case x: IndexedSeqView[A] => x case _ => super.reverse } diff --git a/scaladoc-testcases/src/tests/extendsCall.scala b/scaladoc-testcases/src/tests/extendsCall.scala index b90af8162e15..3ccd70de4216 100644 --- a/scaladoc-testcases/src/tests/extendsCall.scala +++ b/scaladoc-testcases/src/tests/extendsCall.scala @@ -3,4 +3,4 @@ package extendsCall class Impl() extends Base(Seq.empty, c = "-") //expected: class Impl() extends Base -class Base(val a: Seq[String], val b: String = "", val c: String = "") //expected: class Base(val a: Seq[String], val b: String, val c: String) +class Base(val a: Seq[String], val b: String = "", val c: String = "") //expected: class Base(val a: Seq[String], val b: String = ..., val c: String = ...) diff --git a/scaladoc-testcases/src/tests/optionalParams.scala b/scaladoc-testcases/src/tests/optionalParams.scala new file mode 100644 index 000000000000..551e14f7f811 --- /dev/null +++ b/scaladoc-testcases/src/tests/optionalParams.scala @@ -0,0 +1,23 @@ +package tests +package optionalParams + +class C(val a: Seq[String], val b: String = "", var c: String = "") //expected: class C(val a: Seq[String], val b: String = ..., var c: String = ...) +{ + def m(x: Int, s: String = "a"): Nothing //expected: def m(x: Int, s: String = ...): Nothing + = ??? +} + +def f(x: Int, s: String = "a"): Nothing //expected: def f(x: Int, s: String = ...): Nothing + = ??? + +extension (y: Int) + def ext(x: Int = 0): Int //expected: def ext(x: Int = ...): Int + = 0 + +def byname(s: => String = "a"): Int //expected: def byname(s: => String = ...): Int + = 0 + +enum E(val x: Int = 0) //expected: enum E(val x: Int = ...) +{ + case E1(y: Int = 10) extends E(y) //expected: final case class E1(y: Int = ...) extends E +} diff --git a/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala b/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala index a2485085a927..50e7589d92fe 100644 --- a/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala +++ b/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala @@ -47,6 +47,7 @@ object Scaladoc: defaultTemplate: Option[String] = None, quickLinks: List[QuickLink] = List.empty, dynamicSideMenu: Boolean = false, + suppressCC: Boolean = false, // suppress rendering anything related to experimental capture checking ) def run(args: Array[String], rootContext: CompilerContext): Reporter = @@ -231,6 +232,7 @@ object Scaladoc: defaultTemplate.nonDefault, quickLinksParsed, dynamicSideMenu.get, + suppressCC.get, ) (Some(docArgs), newContext) } diff --git a/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala b/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala index 5acfac03d52c..8189d4f45286 100644 --- a/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala +++ b/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala @@ -144,5 +144,8 @@ class ScaladocSettings extends SettingGroup with AllScalaSettings: val dynamicSideMenu: Setting[Boolean] = BooleanSetting(RootSetting, "dynamic-side-menu", "Generate side menu via JS instead of embedding it in every html file", false) + val suppressCC: Setting[Boolean] = + BooleanSetting(RootSetting, "suppressCC", "Suppress rendering anything related to experimental capture checking", false) + def scaladocSpecificSettings: Set[Setting[?]] = - Set(sourceLinks, legacySourceLink, syntax, revision, externalDocumentationMappings, socialLinks, skipById, skipByRegex, deprecatedSkipPackages, docRootContent, snippetCompiler, generateInkuire, defaultTemplate, scastieConfiguration, quickLinks, dynamicSideMenu) + Set(sourceLinks, legacySourceLink, syntax, revision, externalDocumentationMappings, socialLinks, skipById, skipByRegex, deprecatedSkipPackages, docRootContent, snippetCompiler, generateInkuire, defaultTemplate, scastieConfiguration, quickLinks, dynamicSideMenu, suppressCC) diff --git a/scaladoc/src/dotty/tools/scaladoc/api.scala b/scaladoc/src/dotty/tools/scaladoc/api.scala index b39fdf157347..41ccd8fb2280 100644 --- a/scaladoc/src/dotty/tools/scaladoc/api.scala +++ b/scaladoc/src/dotty/tools/scaladoc/api.scala @@ -44,6 +44,7 @@ enum Modifier(val name: String, val prefix: Boolean): case Transparent extends Modifier("transparent", true) case Infix extends Modifier("infix", true) case AbsOverride extends Modifier("abstract override", true) + case Update extends Modifier("update", true) case class ExtensionTarget(name: String, typeParams: Seq[TypeParameter], argsLists: Seq[TermParameterList], signature: Signature, dri: DRI, position: Long) case class ImplicitConversion(from: DRI, to: DRI) @@ -69,7 +70,7 @@ enum Kind(val name: String): case Var extends Kind("var") case Val extends Kind("val") case Exported(base: Kind) extends Kind("export") - case Type(concreate: Boolean, opaque: Boolean, typeParams: Seq[TypeParameter]) + case Type(concreate: Boolean, opaque: Boolean, typeParams: Seq[TypeParameter], isCaptureVar: Boolean = false) extends Kind("type") // should we handle opaque as modifier? case Given(kind: Def | Class | Val.type, as: Option[Signature], conversion: Option[ImplicitConversion]) extends Kind("given") with ImplicitConversionProvider @@ -120,7 +121,8 @@ case class TypeParameter( variance: "" | "+" | "-", name: String, dri: DRI, - signature: Signature + signature: Signature, + isCaptureVar: Boolean = false // under capture checking ) case class Link(name: String, dri: DRI) diff --git a/scaladoc/src/dotty/tools/scaladoc/cc/CaptureOps.scala b/scaladoc/src/dotty/tools/scaladoc/cc/CaptureOps.scala new file mode 100644 index 000000000000..626162db55ec --- /dev/null +++ b/scaladoc/src/dotty/tools/scaladoc/cc/CaptureOps.scala @@ -0,0 +1,225 @@ +package dotty.tools.scaladoc + +package cc + +import scala.quoted._ + +object CaptureDefs: + // these should become part of the reflect API in the distant future + def retains(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.annotation.retains") + def retainsCap(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.annotation.retainsCap") + def retainsByName(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.annotation.retainsByName") + def CapsModule(using qctx: Quotes) = + qctx.reflect.Symbol.requiredPackage("scala.caps") + def captureRoot(using qctx: Quotes) = + qctx.reflect.Symbol.requiredPackage("scala.caps.cap") + def Caps_Capability(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.caps.Capability") + def Caps_CapSet(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.caps.CapSet") + def Caps_Mutable(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.caps.Mutable") + def Caps_SharedCapability(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.caps.SharedCapability") + def UseAnnot(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.caps.use") + def ConsumeAnnot(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.caps.consume") + def ReachCapabilityAnnot(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.annotation.internal.reachCapability") + def RootCapabilityAnnot(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.caps.internal.rootCapability") + def ReadOnlyCapabilityAnnot(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.annotation.internal.readOnlyCapability") + def RequiresCapabilityAnnot(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.annotation.internal.requiresCapability") + def OnlyCapabilityAnnot(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.annotation.internal.onlyCapability") + + def LanguageExperimental(using qctx: Quotes) = + qctx.reflect.Symbol.requiredPackage("scala.language.experimental") + + def ImpureFunction1(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.ImpureFunction1") + + def ImpureContextFunction1(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.ImpureContextFunction1") + + def Function1(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.Function1") + + def ContextFunction1(using qctx: Quotes) = + qctx.reflect.Symbol.requiredClass("scala.ContextFunction1") + + val useAnnotFullName: String = "scala.caps.use." + val consumeAnnotFullName: String = "scala.caps.consume." + val ccImportSelector = "captureChecking" +end CaptureDefs + +extension (using qctx: Quotes)(ann: qctx.reflect.Symbol) + /** This symbol is one of `retains` or `retainsCap` */ + def isRetains: Boolean = + ann == CaptureDefs.retains || ann == CaptureDefs.retainsCap + + /** This symbol is one of `retains`, `retainsCap`, or `retainsByName` */ + def isRetainsLike: Boolean = + ann.isRetains || ann == CaptureDefs.retainsByName + + def isReachCapabilityAnnot: Boolean = + ann == CaptureDefs.ReachCapabilityAnnot + + def isReadOnlyCapabilityAnnot: Boolean = + ann == CaptureDefs.ReadOnlyCapabilityAnnot + + def isOnlyCapabilityAnnot: Boolean = + ann == CaptureDefs.OnlyCapabilityAnnot +end extension + +extension (using qctx: Quotes)(tpe: qctx.reflect.TypeRepr) // FIXME clean up and have versions on Symbol for those + def isCaptureRoot: Boolean = + import qctx.reflect.* + tpe match + case TermRef(ThisType(TypeRef(NoPrefix(), "caps")), "cap") => true + case TermRef(TermRef(ThisType(TypeRef(NoPrefix(), "scala")), "caps"), "cap") => true + case TermRef(TermRef(TermRef(TermRef(NoPrefix(), "_root_"), "scala"), "caps"), "cap") => true + case _ => false + + // NOTE: There's something horribly broken with Symbols, and we can't rely on tests like .isContextFunctionType either, + // so we do these lame string comparisons instead. + def isImpureFunction1: Boolean = tpe.typeSymbol.fullName == "scala.ImpureFunction1" + + def isImpureContextFunction1: Boolean = tpe.typeSymbol.fullName == "scala.ImpureContextFunction1" + + def isFunction1: Boolean = tpe.typeSymbol.fullName == "scala.Function1" + + def isContextFunction1: Boolean = tpe.typeSymbol.fullName == "scala.ContextFunction1" + + def isAnyImpureFunction: Boolean = tpe.typeSymbol.fullName.startsWith("scala.ImpureFunction") + + def isAnyImpureContextFunction: Boolean = tpe.typeSymbol.fullName.startsWith("scala.ImpureContextFunction") + + def isAnyFunction: Boolean = tpe.typeSymbol.fullName.startsWith("scala.Function") + + def isAnyContextFunction: Boolean = tpe.typeSymbol.fullName.startsWith("scala.ContextFunction") + + def isCapSet: Boolean = tpe.typeSymbol == CaptureDefs.Caps_CapSet + + def isCapSetPure: Boolean = + tpe.isCapSet && tpe.match + case CapturingType(_, refs) => refs.isEmpty + case _ => true + + def isCapSetCap: Boolean = + tpe.isCapSet && tpe.match + case CapturingType(_, List(ref)) => ref.isCaptureRoot + case _ => false + + def isPureClass(from: qctx.reflect.ClassDef): Boolean = + import qctx.reflect._ + def check(sym: Tree): Boolean = sym match + case ClassDef(name, _, _, Some(ValDef(_, tt, _)), _) => tt.tpe match + case CapturingType(_, refs) => refs.isEmpty + case _ => true + case _ => false + + // Horrible hack to basically grab tpe1.asSeenFrom(from) + val tpe1 = from.symbol.typeRef.select(tpe.typeSymbol).simplified + val tpe2 = tpe1.classSymbol.map(_.typeRef).getOrElse(tpe1) + + // println(s"${tpe.show} -> (${tpe.typeSymbol} from ${from.symbol}) ${tpe1.show} -> ${tpe2} -> ${tpe2.baseClasses.filter(_.isClassDef)}") + val res = tpe2.baseClasses.exists(c => c.isClassDef && check(c.tree)) + // println(s"${tpe.show} is pure class = $res") + res +end extension + +extension (using qctx: Quotes)(typedef: qctx.reflect.TypeDef) + def derivesFromCapSet: Boolean = + import qctx.reflect.* + typedef.rhs.match + case t: TypeTree => t.tpe.derivesFrom(CaptureDefs.Caps_CapSet) + case t: TypeBoundsTree => t.tpe.derivesFrom(CaptureDefs.Caps_CapSet) + case _ => false +end extension + +/** Matches `import scala.language.experimental.captureChecking` */ +object CCImport: + def unapply(using qctx: Quotes)(tree: qctx.reflect.Tree): Boolean = + import qctx.reflect._ + tree match + case imprt: Import if imprt.expr.tpe.termSymbol == CaptureDefs.LanguageExperimental => + imprt.selectors.exists { + case SimpleSelector(s) if s == CaptureDefs.ccImportSelector => true + case _ => false + } + case _ => false + end unapply +end CCImport + +object ReachCapability: + def unapply(using qctx: Quotes)(ty: qctx.reflect.TypeRepr): Option[qctx.reflect.TypeRepr] = + import qctx.reflect._ + ty match + case AnnotatedType(base, Apply(Select(New(annot), _), Nil)) if annot.symbol.isReachCapabilityAnnot => + Some(base) + case _ => None +end ReachCapability + +object ReadOnlyCapability: + def unapply(using qctx: Quotes)(ty: qctx.reflect.TypeRepr): Option[qctx.reflect.TypeRepr] = + import qctx.reflect._ + ty match + case AnnotatedType(base, Apply(Select(New(annot), _), Nil)) if annot.symbol.isReadOnlyCapabilityAnnot => + Some(base) + case _ => None +end ReadOnlyCapability + +object OnlyCapability: + def unapply(using qctx: Quotes)(ty: qctx.reflect.TypeRepr): Option[(qctx.reflect.TypeRepr, qctx.reflect.Symbol)] = + import qctx.reflect._ + ty match + case AnnotatedType(base, app @ Apply(TypeApply(Select(New(annot), _), _), Nil)) if annot.tpe.typeSymbol.isOnlyCapabilityAnnot => + app.tpe.typeArgs.head.classSymbol.match + case Some(clazzsym) => Some((base, clazzsym)) + case None => None + case _ => None +end OnlyCapability + +/** Decompose capture sets in the union-type-encoding into the sequence of atomic `TypeRepr`s. + * Returns `None` if the type is not a capture set. +*/ +def decomposeCaptureRefs(using qctx: Quotes)(typ0: qctx.reflect.TypeRepr): Option[List[qctx.reflect.TypeRepr]] = + import qctx.reflect._ + val buffer = collection.mutable.ListBuffer.empty[TypeRepr] + def include(t: TypeRepr): Boolean = { buffer += t; true } + def traverse(typ: TypeRepr): Boolean = + typ match + case t if t.typeSymbol == defn.NothingClass => true + case OrType(t1, t2) => traverse(t1) && traverse(t2) + case t @ ThisType(_) => include(t) + case t @ TermRef(_, _) => include(t) + case t @ ParamRef(_, _) => include(t) + case t @ ReachCapability(_) => include(t) + case t @ ReadOnlyCapability(_) => include(t) + case t @ OnlyCapability(_, _) => include(t) + case t : TypeRef => include(t) + case _ => report.warning(s"Unexpected type tree $typ while trying to extract capture references from $typ0"); false + if traverse(typ0) then Some(buffer.toList) else None +end decomposeCaptureRefs + +object CaptureSetType: + def unapply(using qctx: Quotes)(tt: qctx.reflect.TypeTree): Option[List[qctx.reflect.TypeRepr]] = decomposeCaptureRefs(tt.tpe) +end CaptureSetType + +object CapturingType: + def unapply(using qctx: Quotes)(typ: qctx.reflect.TypeRepr): Option[(qctx.reflect.TypeRepr, List[qctx.reflect.TypeRepr])] = + import qctx.reflect._ + typ match + case AnnotatedType(base, Apply(TypeApply(Select(New(annot), _), List(CaptureSetType(refs))), Nil)) if annot.symbol.isRetainsLike => + Some((base, refs)) + case AnnotatedType(base, Apply(Select(New(annot), _), Nil)) if annot.symbol == CaptureDefs.retainsCap => + Some((base, List(CaptureDefs.captureRoot.termRef))) + case _ => None +end CapturingType diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala index af39870d87b5..119ec4fca3f8 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala @@ -177,7 +177,8 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext cls := s"documentableName $depStyle", ) - val signature: MemberSignature = signatureProvider.rawSignature(member)() + val ctx = summon[DocContext] + val signature: MemberSignature = signatureProvider.rawSignature(member)(!ctx.args.suppressCC)() val isSubtype = signature.suffix.exists { case Keyword(keyword) => keyword.contains("extends") case _ => false diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala index 3e49af2e0576..af30c3479d81 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala @@ -213,7 +213,7 @@ trait Resources(using ctx: DocContext) extends Locations, Writer: val (res, pageName) = page.content match case m: Member if m.kind != Kind.RootPackage => def processMember(member: Member, fqName: List[String]): Seq[(JSON, Seq[String])] = - val signature: MemberSignature = signatureProvider.rawSignature(member)() + val signature: MemberSignature = signatureProvider.rawSignature(member)(!ctx.args.suppressCC)() val sig = Signature(Plain(member.name)) ++ signature.suffix val descr = if member.kind == Kind.Package then "" else fqName.mkString(".") val extraDescr = member.docs.map(d => docPartRenderPlain(d.body)).getOrElse("") diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala index 81415377beeb..06fe658b850e 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala @@ -3,6 +3,7 @@ package tasty import scala.jdk.CollectionConverters._ import dotty.tools.scaladoc._ +import dotty.tools.scaladoc.cc.CaptureDefs import scala.quoted._ import SymOps._ @@ -42,7 +43,7 @@ trait BasicSupport: def getAnnotations(): List[Annotation] = // Custom annotations should be documented only if annotated by @java.lang.annotation.Documented // We allow also some special cases - val fqNameAllowlist = Set( + val fqNameAllowlist0 = Set( "scala.specialized", "scala.throws", "scala.transient", @@ -52,8 +53,12 @@ trait BasicSupport: "scala.annotation.static", "scala.annotation.targetName", "scala.annotation.threadUnsafe", - "scala.annotation.varargs" + "scala.annotation.varargs", ) + val fqNameAllowlist = + if ccEnabled then + fqNameAllowlist0 + CaptureDefs.useAnnotFullName + CaptureDefs.consumeAnnotFullName + else fqNameAllowlist0 val documentedSymbol = summon[Quotes].reflect.Symbol.requiredClass("java.lang.annotation.Documented") val annotations = sym.annotations.filter { a => a.tpe.typeSymbol.hasAnnotation(documentedSymbol) || fqNameAllowlist.contains(a.symbol.fullName) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 99aac7010d8b..1a64625d491c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -3,6 +3,8 @@ package dotty.tools.scaladoc.tasty import dotty.tools.scaladoc._ import dotty.tools.scaladoc.{Signature => DSignature} +import dotty.tools.scaladoc.cc.* + import scala.quoted._ import SymOps._ @@ -19,6 +21,15 @@ trait ClassLikeSupport: private given qctx.type = qctx + extension (symbol: Symbol) { + def getExtraModifiers(): Seq[Modifier] = + val mods = SymOps.getExtraModifiers(symbol)() + if ccEnabled && symbol.flags.is(Flags.Mutable)then + mods :+ Modifier.Update + else + mods + } + private def bareClasslikeKind(using Quotes)(symbol: reflect.Symbol): Kind = import reflect._ if symbol.flags.is(Flags.Module) then Kind.Object @@ -443,14 +454,15 @@ trait ClassLikeSupport: val inlinePrefix = if symbol.flags.is(Flags.Inline) then "inline " else "" val name = symbol.normalizedName val nameIfNotSynthetic = Option.when(!symbol.flags.is(Flags.Synthetic))(name) + val defaultValue = Option.when(symbol.flags.is(Flags.HasDefault))(Plain(" = ...")) api.TermParameter( symbol.getAnnotations(), inlinePrefix + prefix(symbol), nameIfNotSynthetic, symbol.dri, - argument.tpt.asSignature(classDef, symbol.owner), - isExtendedSymbol, - isGrouped + argument.tpt.asSignature(classDef, symbol.owner) :++ defaultValue, + isExtendedSymbol = isExtendedSymbol, + isGrouped = isGrouped ) def mkTypeArgument( @@ -465,6 +477,8 @@ trait ClassLikeSupport: else "" val name = symbol.normalizedName + val isCaptureVar = ccEnabled && argument.derivesFromCapSet + val normalizedName = if name.matches("_\\$\\d*") then "_" else name val boundsSignature = argument.rhs.asSignature(classDef, symbol.owner) val signature = boundsSignature ++ contextBounds.flatMap(tr => @@ -479,7 +493,8 @@ trait ClassLikeSupport: variancePrefix, normalizedName, symbol.dri, - signature + signature, + isCaptureVar, ) def parseTypeDef(typeDef: TypeDef, classDef: ClassDef): Member = @@ -489,6 +504,9 @@ trait ClassLikeSupport: case LambdaTypeTree(params, body) => isTreeAbstract(body) case _ => false } + + val isCaptureVar = ccEnabled && typeDef.derivesFromCapSet + val (generics, tpeTree) = typeDef.rhs match case LambdaTypeTree(params, body) => (params.map(mkTypeArgument(_, classDef)), body) case tpe => (Nil, tpe) @@ -528,7 +546,10 @@ trait ClassLikeSupport: case _ => symbol.getExtraModifiers() mkMember(symbol, kind, sig)( - modifiers = modifiers, + // Due to how capture checking encodes update methods (recycling the mutable flag for methods), + // we need to filter out the update modifier here. Otherwise, mutable fields will + // be documented as having the update modifier, which is not correct. + modifiers = modifiers.filterNot(_ == Modifier.Update), deprecated = symbol.isDeprecated(), experimental = symbol.isExperimental() ) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/NameNormalizer.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/NameNormalizer.scala index 196c3e056b36..8f42a28c2c35 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/NameNormalizer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/NameNormalizer.scala @@ -17,7 +17,7 @@ object NameNormalizer { val escaped = escapedName(constructorNormalizedName) escaped } - + def ownerNameChain: List[String] = { import reflect.* if s.isNoSymbol then List.empty @@ -25,8 +25,8 @@ object NameNormalizer { else if s == defn.RootPackage then List.empty else if s == defn.RootClass then List.empty else s.owner.ownerNameChain :+ s.normalizedName - } - + } + def normalizedFullName: String = s.ownerNameChain.mkString(".") diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/PackageSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/PackageSupport.scala index c0308336a2bf..234c2c3cc4f2 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/PackageSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/PackageSupport.scala @@ -5,6 +5,8 @@ import scala.jdk.CollectionConverters._ import SymOps._ +import dotty.tools.scaladoc.cc.CCImport + trait PackageSupport: self: TastyParser => import qctx.reflect._ @@ -13,6 +15,11 @@ trait PackageSupport: def parsePackage(pck: PackageClause): (String, Member) = val name = pck.symbol.fullName + ccFlag = false + pck.stats.foreach { + case CCImport() => ccFlag = true + case _ => + } (name, Member(name, "", pck.symbol.dri, Kind.Package)) def parsePackageObject(pckObj: ClassDef): (String, Member) = diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala index 1a8337e0c6b7..f7cdf62c5458 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala @@ -187,6 +187,9 @@ case class TastyParser( private given qctx.type = qctx + protected var ccFlag: Boolean = false + def ccEnabled: Boolean = !ctx.args.suppressCC && ccFlag + val intrinsicClassDefs = Set( defn.AnyClass, defn.MatchableClass, diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala index 24473c874c96..f347513120b8 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala @@ -6,8 +6,10 @@ import scala.jdk.CollectionConverters.* import scala.quoted.* import scala.util.control.NonFatal -import NameNormalizer.* -import SyntheticsSupport.* +import dotty.tools.scaladoc.cc.* + +import NameNormalizer._ +import SyntheticsSupport._ trait TypesSupport: self: TastyParser => @@ -19,7 +21,7 @@ trait TypesSupport: def asSignature(elideThis: reflect.ClassDef, originalOwner: reflect.Symbol, skipThisTypePrefix: Boolean): SSignature = import reflect._ tpeTree match - case TypeBoundsTree(low, high) => typeBoundsTreeOfHigherKindedType(low.tpe, high.tpe, skipThisTypePrefix)(using elideThis, originalOwner) + case TypeBoundsTree(low, high) => typeBoundsTreeOfHigherKindedType(low.tpe, high.tpe, skipThisTypePrefix)(using elideThis, originalOwner, inCC = None) case tpeTree: TypeTree => topLevelProcess(tpeTree.tpe, skipThisTypePrefix)(using elideThis, originalOwner) case term: Term => topLevelProcess(term.tpe, skipThisTypePrefix)(using elideThis, originalOwner) def asSignature(elideThis: reflect.ClassDef, originalOwner: reflect.Symbol): SSignature = @@ -37,19 +39,30 @@ trait TypesSupport: private def keyword(str: String): SignaturePart = Keyword(str) - private def tpe(str: String, dri: DRI): SignaturePart = dotty.tools.scaladoc.Type(str, Some(dri)) + private def tpe(str: String, dri: DRI)(using inCC: Option[Any]): SignaturePart = + if ccEnabled && inCC.isDefined then + dotty.tools.scaladoc.Plain(str) + else + dotty.tools.scaladoc.Type(str, Some(dri)) - private def tpe(str: String): SignaturePart = dotty.tools.scaladoc.Type(str, None) + private def tpe(str: String)(using inCC: Option[Any]): SignaturePart = + if ccEnabled && inCC.isDefined then + dotty.tools.scaladoc.Plain(str) + else + dotty.tools.scaladoc.Type(str, None) protected def inParens(s: SSignature, wrap: Boolean = true) = if wrap then plain("(").l ++ s ++ plain(")").l else s extension (on: SignaturePart) def l: List[SignaturePart] = List(on) - private def tpe(using Quotes)(symbol: reflect.Symbol): SSignature = + private def tpe(using Quotes)(symbol: reflect.Symbol)(using inCC: Option[Any]): SSignature = import SymOps._ val dri: Option[DRI] = Option(symbol).filterNot(_.isHiddenByVisibility).map(_.dri) - dotty.tools.scaladoc.Type(symbol.normalizedName, dri).l + if ccEnabled && inCC.isDefined then // we are in the context of a capture set and want paths to be rendered plainly + dotty.tools.scaladoc.Plain(symbol.normalizedName).l + else + dotty.tools.scaladoc.Type(symbol.normalizedName, dri).l private def commas(lists: List[SSignature]) = lists match case List(single) => single @@ -82,7 +95,7 @@ trait TypesSupport: // TODO #23 add support for all types signatures that make sense private def inner( - using Quotes, + using qctx: Quotes, )( tp: reflect.TypeRepr, skipThisTypePrefix: Boolean @@ -91,6 +104,8 @@ trait TypesSupport: originalOwner: reflect.Symbol, indent: Int = 0, skipTypeSuffix: Boolean = false, + // inCC means in capture-checking context. If defined, it carries the current capture-set contents. + inCC: Option[List[reflect.TypeRepr]] = None, ): SSignature = import reflect._ def noSupported(name: String): SSignature = @@ -105,7 +120,10 @@ trait TypesSupport: inParens(inner(left, skipThisTypePrefix), shouldWrapInParens(left, tp, true)) ++ keyword(" & ").l ++ inParens(inner(right, skipThisTypePrefix), shouldWrapInParens(right, tp, false)) - case ByNameType(tpe) => keyword("=> ") :: inner(tpe, skipThisTypePrefix) + case ByNameType(CapturingType(tpe, refs)) => + emitByNameArrow(using qctx)(Some(refs), skipThisTypePrefix) ++ (plain(" ") :: inner(tpe, skipThisTypePrefix)) + case ByNameType(tpe) => + emitByNameArrow(using qctx)(None, skipThisTypePrefix) ++ (plain(" ") :: inner(tpe, skipThisTypePrefix)) case ConstantType(constant) => plain(constant.show).l case ThisType(tpe) => @@ -116,6 +134,15 @@ trait TypesSupport: inner(tpe, skipThisTypePrefix) :+ plain("*") case AppliedType(repeatedClass, Seq(tpe)) if isRepeated(repeatedClass) => inner(tpe, skipThisTypePrefix) :+ plain("*") + case CapturingType(base, refs) if ccEnabled => + base match + case t @ AppliedType(base, args) if t.isFunctionType => + functionType(base, args, skipThisTypePrefix)(using inCC = Some(refs)) + case t : Refinement if t.isFunctionType => + inner(base, skipThisTypePrefix)(using indent = indent, skipTypeSuffix = skipTypeSuffix, inCC = Some(refs)) + case t if t.isCapSet => emitCaptureSet(refs, skipThisTypePrefix, omitCap = false) + case t if t.isPureClass(elideThis) => inner(base, skipThisTypePrefix) + case t => inner(base, skipThisTypePrefix) ++ emitCapturing(refs, skipThisTypePrefix) case AnnotatedType(tpe, _) => inner(tpe, skipThisTypePrefix) case tl @ TypeLambda(params, paramBounds, AppliedType(tpe, args)) @@ -127,7 +154,8 @@ trait TypesSupport: case tl @ TypeLambda(params, paramBounds, resType) => plain("[").l ++ commas(params.zip(paramBounds).map { (name, typ) => val normalizedName = if name.matches("_\\$\\d*") then "_" else name - tpe(normalizedName).l ++ inner(typ, skipThisTypePrefix) + val suffix = if ccEnabled && typ.derivesFrom(CaptureDefs.Caps_CapSet) then List(Keyword("^")) else Nil + tpe(normalizedName).l ++ suffix ++ inner(typ, skipThisTypePrefix) }) ++ plain("]").l ++ keyword(" =>> ").l ++ inner(resType, skipThisTypePrefix) @@ -139,14 +167,19 @@ trait TypesSupport: inner(Refinement(at, "apply", mt), skipThisTypePrefix) case r: Refinement => { //(parent, name, info) + val inCC0 = inCC + given Option[List[TypeRepr]] = None // do not propagate capture set beyond this point def getRefinementInformation(t: TypeRepr): List[TypeRepr] = t match { case r: Refinement => getRefinementInformation(r.parent) :+ r case t => List(t) } def getParamBounds(t: PolyType): SSignature = commas( - t.paramNames.zip(t.paramBounds.map(inner(_, skipThisTypePrefix))) - .map(b => tpe(b(0)).l ++ b(1)) + t.paramNames.zip(t.paramBounds.map(inner(_, skipThisTypePrefix))).zipWithIndex + .map { case ((name, bound), idx) => + val suffix = if ccEnabled && t.param(idx).derivesFrom(CaptureDefs.Caps_CapSet) then List(Keyword("^")) else Nil + tpe(name).l ++ suffix ++ bound + } ) def getParamList(m: MethodType): SSignature = @@ -189,9 +222,16 @@ trait TypesSupport: val isCtx = isContextualMethod(m) if isDependentMethod(m) then val paramList = getParamList(m) - val arrow = keyword(if isCtx then " ?=> " else " => ").l + val arrPrefix = if isCtx then "?" else "" + val arrow = + if ccEnabled then + inCC0 match + case None | Some(Nil) => keyword(arrPrefix + "->").l + case Some(List(c)) if c.isCaptureRoot => keyword(arrPrefix + "=>").l + case Some(refs) => keyword(arrPrefix + "->") :: emitCaptureSet(refs, skipThisTypePrefix) + else keyword(arrPrefix + "=>").l val resType = inner(m.resType, skipThisTypePrefix) - paramList ++ arrow ++ resType + paramList ++ (plain(" ") :: arrow) ++ (plain(" ") :: resType) else val sym = defn.FunctionClass(m.paramTypes.length, isCtx) inner(sym.typeRef.appliedTo(m.paramTypes :+ m.resType), skipThisTypePrefix) @@ -234,18 +274,12 @@ trait TypesSupport: ++ inParens(inner(rhs, skipThisTypePrefix), shouldWrapInParens(rhs, t, false)) case t @ AppliedType(tpe, args) if t.isFunctionType => - val arrow = if t.isContextFunctionType then " ?=> " else " => " - args match - case Nil => Nil - case List(rtpe) => plain("()").l ++ keyword(arrow).l ++ inner(rtpe, skipThisTypePrefix) - case List(arg, rtpe) => - val wrapInParens = stripAnnotated(arg) match - case _: TermRef | _: TypeRef | _: ConstantType | _: ParamRef => false - case at: AppliedType if !isInfix(at) && !at.isFunctionType && !at.isTupleN => false - case _ => true - inParens(inner(arg, skipThisTypePrefix), wrapInParens) ++ keyword(arrow).l ++ inner(rtpe, skipThisTypePrefix) - case _ => - plain("(").l ++ commas(args.init.map(inner(_, skipThisTypePrefix))) ++ plain(")").l ++ keyword(arrow).l ++ inner(args.last, skipThisTypePrefix) + val dealiased = t.dealiasKeepOpaques + if t == dealiased then + functionType(tpe, args, skipThisTypePrefix) + else + val AppliedType(tpe, args) = dealiased.asInstanceOf[AppliedType] + functionType(tpe, args, skipThisTypePrefix) case t @ AppliedType(tpe, typeList) => inner(tpe, skipThisTypePrefix) ++ plain("[").l ++ commas(typeList.map { t => t match @@ -253,6 +287,8 @@ trait TypesSupport: case _ => topLevelProcess(t, skipThisTypePrefix) }) ++ plain("]").l + case t : TypeRef if ccEnabled && t.isCapSet => emitCaptureSet(Nil, skipThisTypePrefix) + case tp @ TypeRef(qual, typeName) => inline def wrapping = shouldWrapInParens(inner = qual, outer = tp, isLeft = true) qual match { @@ -272,7 +308,7 @@ trait TypesSupport: tpe(tp.typeSymbol) else val sig = inParens( - inner(qual, skipThisTypePrefix)(using indent = indent, skipTypeSuffix = true), wrapping) + inner(qual, skipThisTypePrefix)(using indent = indent, skipTypeSuffix = true, inCC = inCC), wrapping) sig ++ plain(".").l ++ tpe(tp.typeSymbol) @@ -281,7 +317,7 @@ trait TypesSupport: 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 indent = indent, skipTypeSuffix = true) + inner(qual, skipThisTypePrefix)(using indent = indent, skipTypeSuffix = true, inCC = inCC) ++ plain(".").l ++ suffix case _ => @@ -292,7 +328,7 @@ trait TypesSupport: case tr @ TermRef(qual, typeName) => val prefix = qual match case t if skipPrefix(t, elideThis, originalOwner, skipThisTypePrefix) => Nil - case tp => inner(tp, skipThisTypePrefix)(using indent = indent, skipTypeSuffix = true) ++ plain(".").l + case tp => inner(tp, skipThisTypePrefix)(using indent = indent, skipTypeSuffix = true, inCC = inCC) ++ 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) => @@ -314,13 +350,13 @@ trait TypesSupport: keyword(caseSpaces + "case ").l ++ inner(from, skipThisTypePrefix) ++ keyword(" => ").l - ++ inner(to, skipThisTypePrefix)(using indent = indent + 2, skipTypeSuffix = skipTypeSuffix) + ++ inner(to, skipThisTypePrefix)(using indent = indent + 2, skipTypeSuffix = skipTypeSuffix, inCC = inCC) ++ plain("\n").l case TypeLambda(_, _, MatchCase(from, to)) => keyword(caseSpaces + "case ").l ++ inner(from, skipThisTypePrefix) ++ keyword(" => ").l - ++ inner(to, skipThisTypePrefix)(using indent = indent + 2, skipTypeSuffix = skipTypeSuffix) + ++ inner(to, skipThisTypePrefix)(using indent = indent + 2, skipTypeSuffix = skipTypeSuffix, inCC = inCC) ++ plain("\n").l } inner(sc, skipThisTypePrefix) ++ keyword(" match ").l ++ plain("{\n").l ++ casesTexts ++ plain(spaces + "}").l @@ -347,9 +383,34 @@ trait TypesSupport: s"${tpe.show(using Printer.TypeReprStructure)}" throw MatchError(msg) + private def functionType(using qctx: Quotes)(funTy: reflect.TypeRepr, args: List[reflect.TypeRepr], skipThisTypePrefix: Boolean)(using + elideThis: reflect.ClassDef, + originalOwner: reflect.Symbol, + indent: Int, + skipTypeSuffix: Boolean, + inCC: Option[List[reflect.TypeRepr]], + ): SSignature = + import reflect._ + val arrow = plain(" ") :: (emitFunctionArrow(using qctx)(funTy, inCC, skipThisTypePrefix) ++ plain(" ").l) + given Option[List[TypeRepr]] = None // do not propagate capture set beyond this point + args match + case Nil => Nil + case List(rtpe) => plain("()").l ++ arrow ++ inner(rtpe, skipThisTypePrefix) + case List(arg, rtpe) => + val wrapInParens = stripAnnotated(arg) match + case _: TermRef | _: TypeRef | _: ConstantType | _: ParamRef => false + case at: AppliedType if !isInfix(at) && !at.isFunctionType && !at.isTupleN => false + case _ => true + inParens(inner(arg, skipThisTypePrefix), wrapInParens) ++ arrow ++ inner(rtpe, skipThisTypePrefix) + case _ => + plain("(").l ++ commas(args.init.map(inner(_, skipThisTypePrefix))) ++ plain(")").l ++ arrow ++ inner(args.last, skipThisTypePrefix) + private def typeBound(using Quotes)(t: reflect.TypeRepr, low: Boolean, skipThisTypePrefix: Boolean)(using elideThis: reflect.ClassDef, originalOwner: reflect.Symbol) = import reflect._ - val ignore = if (low) t.typeSymbol == defn.NothingClass else t.typeSymbol == defn.AnyClass + val ignore = low && (ccEnabled && t.isCapSetPure + || t.typeSymbol == defn.NothingClass) + || !low && (ccEnabled && t.isCapSetCap + || t.typeSymbol == defn.AnyClass) val prefix = keyword(if low then " >: " else " <: ") t match { case l: TypeLambda => prefix :: inParens(inner(l, skipThisTypePrefix)(using elideThis, originalOwner)) @@ -359,18 +420,18 @@ trait TypesSupport: } private def typeBoundsTreeOfHigherKindedType(using Quotes)(low: reflect.TypeRepr, high: reflect.TypeRepr, skipThisTypePrefix: Boolean)( - using elideThis: reflect.ClassDef, originalOwner: reflect.Symbol + using elideThis: reflect.ClassDef, originalOwner: reflect.Symbol, inCC: Option[List[reflect.TypeRepr]] ) = import reflect._ def regularTypeBounds(low: TypeRepr, high: TypeRepr) = - if low == high then keyword(" = ").l ++ inner(low, skipThisTypePrefix)(using elideThis, originalOwner) + if low == high then keyword(" = ").l ++ inner(low, skipThisTypePrefix)(using elideThis, originalOwner, inCC = inCC) else typeBound(low, low = true, skipThisTypePrefix)(using elideThis, originalOwner) ++ typeBound(high, low = false, skipThisTypePrefix)(using elideThis, originalOwner) high.match case TypeLambda(params, paramBounds, resType) => if resType.typeSymbol == defn.AnyClass then plain("[").l ++ commas(params.zip(paramBounds).map { (name, typ) => val normalizedName = if name.matches("_\\$\\d*") then "_" else name - tpe(normalizedName).l ++ inner(typ, skipThisTypePrefix)(using elideThis, originalOwner) + tpe(normalizedName)(using inCC).l ++ inner(typ, skipThisTypePrefix)(using elideThis, originalOwner, inCC = inCC) }) ++ plain("]").l else regularTypeBounds(low, high) @@ -452,3 +513,66 @@ trait TypesSupport: tr match case AnnotatedType(tr, _) => stripAnnotated(tr) case other => other + + private def emitCapability(using Quotes)(ref: reflect.TypeRepr, skipThisTypePrefix: Boolean)(using elideThis: reflect.ClassDef, originalOwner: reflect.Symbol): SSignature = + import reflect._ + ref match + case ReachCapability(c) => emitCapability(c, skipThisTypePrefix) :+ Keyword("*") + case ReadOnlyCapability(c) => emitCapability(c, skipThisTypePrefix) :+ Keyword(".rd") + case OnlyCapability(c, cls) => emitCapability(c, skipThisTypePrefix) ++ List(Plain("."), Keyword("only"), Plain("[")) ++ inner(cls.typeRef, skipThisTypePrefix) :+ Plain("]") + case ThisType(_) => List(Keyword("this")) + case t => inner(t, skipThisTypePrefix)(using skipTypeSuffix = true, inCC = Some(Nil)) + + private def emitCaptureSet(using Quotes)(refs: List[reflect.TypeRepr], skipThisTypePrefix: Boolean, omitCap: Boolean = true)(using elideThis: reflect.ClassDef, originalOwner: reflect.Symbol): SSignature = + import reflect._ + refs match + case List(ref) if omitCap && ref.isCaptureRoot => Nil + case refs => + val res0 = refs.map(x => emitCapability(x, skipThisTypePrefix)) + val res1 = res0 match + case Nil => Nil + case other => other.reduce((r, e) => r ++ (List(Plain(", ")) ++ e)) + Plain("{") :: (res1 ++ List(Plain("}"))) + + // Within the context of `elideThis`, some capabilities can actually be pure. + private def isCapturedInContext(using Quotes)(ref: reflect.TypeRepr)(using elideThis: reflect.ClassDef): Boolean = + import reflect._ + ref match + case t if t.isCaptureRoot => true + case ReachCapability(c) => isCapturedInContext(c) + case ReadOnlyCapability(c) => isCapturedInContext(c) + case ThisType(tr) => !elideThis.symbol.typeRef.isPureClass(elideThis) /* is the current class pure? */ + case t => !t.isPureClass(elideThis) + + private def emitCapturing(using Quotes)(refs: List[reflect.TypeRepr], skipThisTypePrefix: Boolean)(using elideThis: reflect.ClassDef, originalOwner: reflect.Symbol): SSignature = + import reflect._ + val refs0 = refs.filter(isCapturedInContext) + if refs0.isEmpty then Nil else Keyword("^") :: emitCaptureSet(refs0, skipThisTypePrefix) + + private def emitFunctionArrow(using Quotes)(funTy: reflect.TypeRepr, captures: Option[List[reflect.TypeRepr]], skipThisTypePrefix: Boolean)(using elideThis: reflect.ClassDef, originalOwner: reflect.Symbol): SSignature = + import reflect._ + val isContextFun = funTy.isAnyContextFunction || funTy.isAnyImpureContextFunction + val prefix = if isContextFun then "?" else "" + if !ccEnabled then + List(Keyword(prefix + "=>")) + else + val isPureFun = funTy.isAnyFunction || funTy.isAnyContextFunction + val isImpureFun = funTy.isAnyImpureFunction || funTy.isAnyImpureContextFunction + captures match + case None => // means an explicit retains* annotation is missing + if isPureFun then + List(Keyword(prefix + "->")) + else if isImpureFun then + List(Keyword(prefix + "=>")) + else + report.error(s"Cannot emit function arrow: expected a (Context)Function* or Impure(Context)Function*, but got: ${funTy.show}") + Nil + case Some(refs) => + // there is some capture set + refs match + case Nil => List(Keyword(prefix + "->")) + case List(ref) if ref.isCaptureRoot => List(Keyword(prefix + "=>")) + case refs => Keyword(prefix + "->") :: emitCaptureSet(refs, skipThisTypePrefix) + + private def emitByNameArrow(using Quotes)(captures: Option[List[reflect.TypeRepr]], skipThisTypePrefix: Boolean)(using elideThis: reflect.ClassDef, originalOwner: reflect.Symbol): SSignature = + emitFunctionArrow(CaptureDefs.Function1.typeRef, captures, skipThisTypePrefix) diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala index a3ce15d70c64..31d57fe9a697 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala @@ -6,12 +6,12 @@ import scala.util.chaining._ class ScalaSignatureProvider: val builder = SignatureBuilder() given Conversion[SignatureBuilder, Signature] = bdr => bdr.content - def rawSignature(documentable: Member)(kind: Kind = documentable.kind): MemberSignature = + def rawSignature(documentable: Member)(allowCC: Boolean /*capture checking enabled?*/)(kind: Kind = documentable.kind): MemberSignature = kind match case Kind.Extension(_, m) => extensionSignature(documentable, m) case Kind.Exported(d) => - rawSignature(documentable)(d) + rawSignature(documentable)(allowCC)(d) case d: Kind.Def => methodSignature(documentable, d) case Kind.Constructor(d) => @@ -39,7 +39,7 @@ class ScalaSignatureProvider: case Kind.Val | Kind.Var | Kind.Implicit(Kind.Val, _) => fieldSignature(documentable, kind) case tpe: Kind.Type => - typeSignature(tpe, documentable) + typeSignature(tpe, documentable)(allowCC) case Kind.Package => MemberSignature( Nil, @@ -145,11 +145,11 @@ class ScalaSignatureProvider: case _ => fieldLikeSignature(field, field.kind, None) - private def typeSignature(tpe: Kind.Type, typeDef: Member): MemberSignature = + private def typeSignature(tpe: Kind.Type, typeDef: Member)(allowCC: Boolean = false): MemberSignature = MemberSignature( builder.modifiersAndVisibility(typeDef), builder.kind(tpe), - builder.name(typeDef.name, typeDef.dri), + builder.name(typeDef.name, typeDef.dri, isCaptureVar = allowCC && tpe.isCaptureVar), builder.typeParamList(tpe.typeParams).pipe { bdr => if (!tpe.opaque) { (if tpe.concreate then bdr.plain(" = ") else bdr) diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala index d28dd6ca18fe..7b3f2fa44acf 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala @@ -3,8 +3,12 @@ package translators case class SignatureBuilder(content: Signature = Nil) extends ScalaSignatureUtils: def plain(str: String): SignatureBuilder = copy(content = content :+ Plain(str)) - def name(str: String, dri: DRI): SignatureBuilder = copy(content = content :+ Name(str, dri)) - def tpe(text: String, dri: Option[DRI]): SignatureBuilder = copy(content = content :+ Type(text, dri)) + def name(str: String, dri: DRI, isCaptureVar: Boolean = false/*under CC*/): SignatureBuilder = + val suffix = if isCaptureVar then List(Keyword("^")) else Nil + copy(content = content ++ (Name(str, dri) :: suffix)) + def tpe(text: String, dri: Option[DRI], isCaptureVar: Boolean = false/*under CC*/): SignatureBuilder = + val suffix = if isCaptureVar then List(Keyword("^")) else Nil + copy(content = content ++ (Type(text, dri) :: suffix)) def keyword(str: String): SignatureBuilder = copy(content = content :+ Keyword(str)) def tpe(text: String, dri: DRI): SignatureBuilder = copy(content = content :+ Type(text, Some(dri))) def signature(s: Signature): SignatureBuilder = copy(content = content ++ s) @@ -90,7 +94,7 @@ case class SignatureBuilder(content: Signature = Nil) extends ScalaSignatureUtil } def typeParamList(on: TypeParameterList) = list(on.toList, List(Plain("[")), List(Plain("]"))){ (bdr, e) => - bdr.annotationsInline(e).keyword(e.variance).tpe(e.name, Some(e.dri)).signature(e.signature) + bdr.annotationsInline(e).keyword(e.variance).tpe(e.name, Some(e.dri), e.isCaptureVar).signature(e.signature) } def functionTermParameters(paramss: Seq[TermParameterList]) = diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala index 34e9bc128402..98656d3b5c4a 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala @@ -130,3 +130,5 @@ class RightAssocExtension extends SignatureTest("rightAssocExtension", Signature class NamedTuples extends SignatureTest("namedTuples", SignatureTest.all) class InnerClasses extends SignatureTest("innerClasses", SignatureTest.all) + +class OptionalParams extends SignatureTest("optionalParams", SignatureTest.all) diff --git a/tests/coverage/pos/Constructor.scoverage.check b/tests/coverage/pos/Constructor.scoverage.check index a95bb21488b8..ed9ac68752bb 100644 --- a/tests/coverage/pos/Constructor.scoverage.check +++ b/tests/coverage/pos/Constructor.scoverage.check @@ -110,6 +110,23 @@ C Class covtest.C x +161 +162 +13 + +Literal +false +0 +false +1 + +6 +Constructor.scala +covtest +C +Class +covtest.C +x 153 158 13 @@ -120,7 +137,7 @@ false false def x -6 +7 Constructor.scala covtest C @@ -137,7 +154,7 @@ false false f(x) -7 +8 Constructor.scala covtest C @@ -154,7 +171,24 @@ false false x -8 +9 +Constructor.scala +covtest +C +Class +covtest.C +g +188 +189 +16 + +Literal +false +0 +false +2 + +10 Constructor.scala covtest C @@ -171,7 +205,7 @@ false false def g -9 +11 Constructor.scala covtest O @@ -188,7 +222,24 @@ false false def g -10 +12 +Constructor.scala +covtest +O +Object +covtest.O +y +231 +232 +20 + +Literal +false +0 +false +1 + +13 Constructor.scala covtest O @@ -205,7 +256,7 @@ false false def y -11 +14 Constructor.scala covtest O @@ -222,20 +273,3 @@ false false g(y) -12 -Constructor.scala -covtest -O -Object -covtest.O - -237 -238 -21 -y -Ident -false -0 -false -y - diff --git a/tests/coverage/pos/ContextFunctions.scoverage.check b/tests/coverage/pos/ContextFunctions.scoverage.check index 0d616d811ffe..ee5c40b9b1ac 100644 --- a/tests/coverage/pos/ContextFunctions.scoverage.check +++ b/tests/coverage/pos/ContextFunctions.scoverage.check @@ -41,6 +41,23 @@ covtest Imperative Class covtest.Imperative +$anonfun +178 +184 +9 + +Literal +false +0 +false +"name" + +2 +ContextFunctions.scala +covtest +Imperative +Class +covtest.Imperative readName2 121 134 @@ -52,24 +69,24 @@ false false def readName2 -2 +3 ContextFunctions.scala covtest Imperative Class covtest.Imperative readPerson -252 -309 -14 -onError -Apply +243 +247 +13 + +Literal false 0 false -OnError((e) => readName2(using e)(using s)).onError(None) +null -3 +4 ContextFunctions.scala covtest Imperative @@ -77,16 +94,16 @@ Class covtest.Imperative readPerson 252 -295 +309 14 - +onError Apply false 0 false -OnError((e) => readName2(using e)(using s)) +OnError((e) => readName2(using e)(using s)).onError(None) -4 +5 ContextFunctions.scala covtest Imperative @@ -103,7 +120,7 @@ false false readName2(using e)(using s) -5 +6 ContextFunctions.scala covtest Imperative diff --git a/tests/coverage/pos/Enum.scoverage.check b/tests/coverage/pos/Enum.scoverage.check index 6806934e0dec..4ad02971f6cf 100644 --- a/tests/coverage/pos/Enum.scoverage.check +++ b/tests/coverage/pos/Enum.scoverage.check @@ -24,6 +24,57 @@ covtest Planet Class covtest.Planet + +322 +333 +14 + +Literal +false +0 +false +6.67300E-11 + +1 +Enum.scala +covtest +Planet +Class +covtest.Planet +surfaceGravity +359 +386 +15 +/ +Apply +false +0 +false +G * mass / (radius * radius + +2 +Enum.scala +covtest +Planet +Class +covtest.Planet +surfaceGravity +371 +386 +15 +* +Apply +false +0 +false +radius * radius + +3 +Enum.scala +covtest +Planet +Class +covtest.Planet surfaceGravity 338 356 @@ -35,7 +86,24 @@ false false def surfaceGravity -1 +4 +Enum.scala +covtest +Planet +Class +covtest.Planet +surfaceWeight +432 +458 +16 +* +Apply +false +0 +false +otherMass * surfaceGravity + +5 Enum.scala covtest Planet @@ -52,7 +120,7 @@ false false surfaceGravity -2 +6 Enum.scala covtest Planet @@ -69,7 +137,381 @@ false false def surfaceWeight +7 +Enum.scala +covtest +$anon +Class +covtest.$anon + +492 +501 +18 + +Literal +false +0 +false +3.303e+23 + +8 +Enum.scala +covtest +$anon +Class +covtest.$anon + +503 +511 +18 + +Literal +false +0 +false +2.4397e6 + +9 +Enum.scala +covtest +$anon +Class +covtest.$anon + +545 +554 +19 + +Literal +false +0 +false +4.869e+24 + +10 +Enum.scala +covtest +$anon +Class +covtest.$anon + +556 +564 +19 + +Literal +false +0 +false +6.0518e6 + +11 +Enum.scala +covtest +$anon +Class +covtest.$anon + +598 +607 +20 + +Literal +false +0 +false +5.976e+24 + +12 +Enum.scala +covtest +$anon +Class +covtest.$anon + +609 +618 +20 + +Literal +false +0 +false +6.37814e6 + +13 +Enum.scala +covtest +$anon +Class +covtest.$anon + +652 +661 +21 + +Literal +false +0 +false +6.421e+23 + +14 +Enum.scala +covtest +$anon +Class +covtest.$anon + +663 +671 +21 + +Literal +false +0 +false +3.3972e6 + +15 +Enum.scala +covtest +$anon +Class +covtest.$anon + +705 +712 +22 + +Literal +false +0 +false +1.9e+27 + +16 +Enum.scala +covtest +$anon +Class +covtest.$anon + +716 +724 +22 + +Literal +false +0 +false +7.1492e7 + +17 +Enum.scala +covtest +$anon +Class +covtest.$anon + +758 +767 +23 + +Literal +false +0 +false +5.688e+26 + +18 +Enum.scala +covtest +$anon +Class +covtest.$anon + +769 +777 +23 + +Literal +false +0 +false +6.0268e7 + +19 +Enum.scala +covtest +$anon +Class +covtest.$anon + +811 +820 +24 + +Literal +false +0 +false +8.686e+25 + +20 +Enum.scala +covtest +$anon +Class +covtest.$anon + +822 +830 +24 + +Literal +false +0 +false +2.5559e7 + +21 +Enum.scala +covtest +$anon +Class +covtest.$anon + +864 +873 +25 + +Literal +false +0 +false +1.024e+26 + +22 +Enum.scala +covtest +$anon +Class +covtest.$anon + +875 +883 +25 + +Literal +false +0 +false +2.4746e7 + +23 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +test +970 +1038 +30 +apply +Apply +false +0 +false +ListEnum.Cons(1, ListEnum.Cons(2, ListEnum.Cons(3, ListEnum.Empty))) + +24 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +test +984 +985 +30 + +Literal +false +0 +false +1 + +25 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +test +987 +1037 +30 +apply +Apply +false +0 +false +ListEnum.Cons(2, ListEnum.Cons(3, ListEnum.Empty)) + +26 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +test +1001 +1002 +30 + +Literal +false +0 +false +2 + +27 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +test +1004 +1036 +30 +apply +Apply +false +0 +false +ListEnum.Cons(3, ListEnum.Empty) + +28 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +test +1018 +1019 +30 + +Literal +false +0 +false 3 + +29 Enum.scala covtest EnumTypes @@ -86,7 +528,7 @@ false false println("Example 1: \\n"+emptyList) -4 +30 Enum.scala covtest EnumTypes @@ -103,7 +545,24 @@ false false "Example 1: \\n"+emptyList -5 +31 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +test +1051 +1066 +31 + +Literal +false +0 +false +"Example 1: \\n" + +32 Enum.scala covtest EnumTypes @@ -120,7 +579,7 @@ false false println(s"${list}\\n") -6 +33 Enum.scala covtest EnumTypes @@ -137,7 +596,24 @@ false false s"${list}\\n" -7 +34 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +calculateEarthWeightOnPlanets +1183 +1222 +35 +/ +Apply +false +0 +false +earthWeight/Planet.Earth.surfaceGravity + +35 Enum.scala covtest EnumTypes @@ -154,7 +630,7 @@ false false Planet.Earth.surfaceGravity -8 +36 Enum.scala covtest EnumTypes @@ -171,7 +647,7 @@ false false for p <- Planet.values do\n println(s"Your weight on $p is ${p.surfaceWeight(mass)}") -9 +37 Enum.scala covtest EnumTypes @@ -181,14 +657,14 @@ calculateEarthWeightOnPlanets 1238 1251 36 -refArrayOps -Apply +values +Select false 0 false Planet.values -10 +38 Enum.scala covtest EnumTypes @@ -205,7 +681,7 @@ false false println(s"Your weight on $p is ${p.surfaceWeight(mass)}") -11 +39 Enum.scala covtest EnumTypes @@ -222,7 +698,7 @@ false false s"Your weight on $p is ${p.surfaceWeight(mass)}" -12 +40 Enum.scala covtest EnumTypes @@ -239,7 +715,7 @@ false false p.surfaceWeight(mass) -13 +41 Enum.scala covtest EnumTypes @@ -256,7 +732,7 @@ false false def calculateEarthWeightOnPlanets -14 +42 Enum.scala covtest EnumTypes @@ -273,7 +749,7 @@ false false println("Example 2:") -15 +43 Enum.scala covtest EnumTypes @@ -290,7 +766,7 @@ false false calculateEarthWeightOnPlanets(80) -16 +44 Enum.scala covtest EnumTypes diff --git a/tests/coverage/pos/ExcludeClass.scoverage.check b/tests/coverage/pos/ExcludeClass.scoverage.check index 5e77f0ce21a1..370410b68091 100644 --- a/tests/coverage/pos/ExcludeClass.scoverage.check +++ b/tests/coverage/pos/ExcludeClass.scoverage.check @@ -25,6 +25,23 @@ Klass2 Class covtest.Klass2 abs +202 +207 +15 +> +Apply +false +0 +false +i > 0 + +1 +ExcludeClass.scala +covtest +Klass2 +Class +covtest.Klass2 +abs 219 220 16 @@ -35,7 +52,24 @@ true false i -1 +2 +ExcludeClass.scala +covtest +Klass2 +Class +covtest.Klass2 +abs +236 +238 +18 +unary_- +Select +false +0 +false +-i + +3 ExcludeClass.scala covtest Klass2 @@ -52,7 +86,7 @@ true false -i -2 +4 ExcludeClass.scala covtest Klass2 diff --git a/tests/coverage/pos/For.scoverage.check b/tests/coverage/pos/For.scoverage.check index 6eeab746b4c5..9942da22d02e 100644 --- a/tests/coverage/pos/For.scoverage.check +++ b/tests/coverage/pos/For.scoverage.check @@ -41,40 +41,6 @@ covtest For$package Object covtest.For$package -testForLoop -52 -59 -4 -to -Apply -false -0 -false -1 to 10 - -2 -For.scala -covtest -For$package -Object -covtest.For$package -testForLoop -52 -53 -4 -intWrapper -Apply -false -0 -false -1 - -3 -For.scala -covtest -For$package -Object -covtest.For$package $anonfun 67 77 @@ -86,7 +52,7 @@ false false println(i) -4 +2 For.scala covtest For$package @@ -103,92 +69,58 @@ false false def testForLoop -5 +3 For.scala covtest For$package Object covtest.For$package f -109 -114 +134 +138 8 -f -DefDef -false -0 -false -def f - -6 -For.scala -covtest -For$package -Object -covtest.For$package -testForAdvanced -141 -183 -9 -foreach -Apply + +Literal false 0 false -for j <- 1 to 10 if f(j) do\n println(j) +true -7 +4 For.scala covtest For$package Object covtest.For$package -testForAdvanced -145 -165 -9 -withFilter -Apply -false -0 -false -j <- 1 to 10 if f(j) - +f +109 +114 8 -For.scala -covtest -For$package -Object -covtest.For$package -testForAdvanced -150 -157 -9 -to -Apply +f +DefDef false 0 false -1 to 10 +def f -9 +5 For.scala covtest For$package Object covtest.For$package testForAdvanced -150 -151 +141 +183 9 -intWrapper +foreach Apply false 0 false -1 +for j <- 1 to 10 if f(j) do\n println(j) -10 +6 For.scala covtest For$package @@ -205,7 +137,7 @@ false false f(j) -11 +7 For.scala covtest For$package @@ -222,7 +154,7 @@ false false println(j) -12 +8 For.scala covtest For$package @@ -239,7 +171,7 @@ false false def testForAdvanced -13 +9 For.scala covtest For$package @@ -256,7 +188,7 @@ false false Nil.foreach(_ => println("user code here")) -14 +10 For.scala covtest For$package @@ -273,7 +205,7 @@ false false println("user code here") -15 +11 For.scala covtest For$package diff --git a/tests/coverage/pos/Givens.scoverage.check b/tests/coverage/pos/Givens.scoverage.check index 4442f329c6b2..b09e369ee076 100644 --- a/tests/coverage/pos/Givens.scoverage.check +++ b/tests/coverage/pos/Givens.scoverage.check @@ -42,6 +42,40 @@ Givens Class covtest.Givens test +182 +190 +11 +== +Apply +false +0 +false +3 == "3" + +2 +Givens.scala +covtest +Givens +Class +covtest.Givens +test +182 +183 +11 + +Literal +false +0 +false +3 + +3 +Givens.scala +covtest +Givens +Class +covtest.Givens +test 196 213 12 @@ -52,7 +86,41 @@ false false println(3 == 5.1) -2 +4 +Givens.scala +covtest +Givens +Class +covtest.Givens +test +204 +212 +12 +== +Apply +false +0 +false +3 == 5.1 + +5 +Givens.scala +covtest +Givens +Class +covtest.Givens +test +204 +205 +12 + +Literal +false +0 +false +3 + +6 Givens.scala covtest Givens @@ -69,7 +137,7 @@ false false def test -3 +7 Givens.scala covtest Givens @@ -86,7 +154,7 @@ false false println(msg) -4 +8 Givens.scala covtest Givens @@ -103,7 +171,7 @@ false false println(ctx.id) -5 +9 Givens.scala covtest Givens @@ -120,7 +188,24 @@ false false def printContext -6 +10 +Givens.scala +covtest +Givens +Class +covtest.Givens +getMessage +348 +358 +18 +toString +Apply +false +0 +false +i.toString + +11 Givens.scala covtest Givens @@ -137,7 +222,7 @@ false false def getMessage -7 +12 Givens.scala covtest Givens @@ -154,7 +239,7 @@ false false Context(0) -8 +13 Givens.scala covtest Givens @@ -171,7 +256,7 @@ false false printContext("test")(using c) -9 +14 Givens.scala covtest Givens @@ -188,7 +273,7 @@ false false printContext(getMessage(123)) -10 +15 Givens.scala covtest Givens @@ -205,7 +290,7 @@ false false getMessage(123) -11 +16 Givens.scala covtest Givens diff --git a/tests/coverage/pos/Inlined.scala b/tests/coverage/pos/Inlined.scala index 5e51f037220f..37519e9f63e3 100644 --- a/tests/coverage/pos/Inlined.scala +++ b/tests/coverage/pos/Inlined.scala @@ -1,6 +1,7 @@ package covtest // Checks that we use the new positions of the inlined code properly +// NOTE (12.08.2025): After recent changes, the inlined nodes will not be tagged in coverage def testInlined(): Unit = val l = 1 assert(l == 1) diff --git a/tests/coverage/pos/Inlined.scoverage.check b/tests/coverage/pos/Inlined.scoverage.check index c74868219b67..cf8d1bcdb0f7 100644 --- a/tests/coverage/pos/Inlined.scoverage.check +++ b/tests/coverage/pos/Inlined.scoverage.check @@ -25,230 +25,26 @@ Inlined$package Object covtest.Inlined$package testInlined -288 -330 -11 -assertFailed -Apply -false -0 -false -scala.runtime.Scala3RunTime.assertFailed() - -1 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -288 -330 -11 -assertFailed -Apply -true -0 -false -scala.runtime.Scala3RunTime.assertFailed() - -2 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -330 -330 -11 - -Literal -true -0 -false - - -3 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -155 -162 -7 -apply -Apply -false -0 -false -List(l) - -4 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -155 -169 -7 -length -Select -false -0 -false -List(l).length - -5 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -288 -330 -11 -assertFailed -Apply -false -0 -false -scala.runtime.Scala3RunTime.assertFailed() - +215 +216 6 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -288 -330 -11 -assertFailed -Apply -true -0 -false -scala.runtime.Scala3RunTime.assertFailed() - -7 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -330 -330 -11 Literal -true -0 -false - - -8 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -180 -187 -8 -apply -Apply false 0 false -List(l) +1 -9 +1 Inlined.scala covtest Inlined$package Object covtest.Inlined$package testInlined -180 +179 194 -8 -length -Select -false -0 -false -List(l).length - -10 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -288 -330 -11 -assertFailed -Apply -false -0 -false -scala.runtime.Scala3RunTime.assertFailed() - -11 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -288 -330 -11 -assertFailed -Apply -true -0 -false -scala.runtime.Scala3RunTime.assertFailed() - -12 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -330 -330 -11 - -Literal -true -0 -false - - -13 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -86 -101 -4 +5 testInlined DefDef false diff --git a/tests/coverage/pos/InlinedFromLib.scala b/tests/coverage/pos/InlinedFromLib.scala index 1b05e11b7558..7607ded865d7 100644 --- a/tests/coverage/pos/InlinedFromLib.scala +++ b/tests/coverage/pos/InlinedFromLib.scala @@ -2,6 +2,7 @@ package covtest // assert is a `transparent inline` in Predef, // but its source path should not appear in the coverage report. +// NOTE (12.08.2025): After recent changes, the inlined nodes will not be tagged in coverage def testInlined(): Unit = val l = 1 assert(l == 1) diff --git a/tests/coverage/pos/InlinedFromLib.scoverage.check b/tests/coverage/pos/InlinedFromLib.scoverage.check index 5aff5473f6d9..bd7bd1a65b6f 100644 --- a/tests/coverage/pos/InlinedFromLib.scoverage.check +++ b/tests/coverage/pos/InlinedFromLib.scoverage.check @@ -25,230 +25,26 @@ InlinedFromLib$package Object covtest.InlinedFromLib$package testInlined -169 -183 +258 +259 7 -assertFailed -Apply -false -0 -false -assert(l == 1) - -1 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -169 -183 -7 -assertFailed -Apply -true -0 -false -assert(l == 1) - -2 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -169 -183 -7 - -Literal -true -0 -false -assert(l == 1) - -3 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -198 -205 -8 -apply -Apply -false -0 -false -List(l) - -4 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -198 -212 -8 -length -Select -false -0 -false -List(l).length - -5 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -186 -213 -8 -assertFailed -Apply -false -0 -false -assert(l == List(l).length) - -6 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -186 -213 -8 -assertFailed -Apply -true -0 -false -assert(l == List(l).length) - -7 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -186 -213 -8 Literal -true -0 -false -assert(l == List(l).length) - -8 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -223 -230 -9 -apply -Apply false 0 false -List(l) +1 -9 +1 InlinedFromLib.scala covtest InlinedFromLib$package Object covtest.InlinedFromLib$package testInlined -223 +222 237 -9 -length -Select -false -0 -false -List(l).length - -10 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -216 -243 -9 -assertFailed -Apply -false -0 -false -assert(List(l).length == 1) - -11 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -216 -243 -9 -assertFailed -Apply -true -0 -false -assert(List(l).length == 1) - -12 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -216 -243 -9 - -Literal -true -0 -false -assert(List(l).length == 1) - -13 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -129 -144 -5 +6 testInlined DefDef false diff --git a/tests/coverage/pos/Lift.scoverage.check b/tests/coverage/pos/Lift.scoverage.check index cce8c18c6254..516692cd2503 100644 --- a/tests/coverage/pos/Lift.scoverage.check +++ b/tests/coverage/pos/Lift.scoverage.check @@ -25,6 +25,23 @@ SomeFunctions Class covtest.SomeFunctions f +56 +58 +4 + +Literal +false +0 +false +() + +1 +Lift.scala +covtest +SomeFunctions +Class +covtest.SomeFunctions +f 40 45 4 @@ -35,7 +52,24 @@ false false def f -1 +2 +Lift.scala +covtest +SomeFunctions +Class +covtest.SomeFunctions +g +71 +72 +5 + +Literal +false +0 +false +0 + +3 Lift.scala covtest SomeFunctions @@ -52,7 +86,7 @@ false false def g -2 +4 Lift.scala covtest SomeFunctions @@ -69,7 +103,7 @@ false false SomeFunctions() -3 +5 Lift.scala covtest SomeFunctions @@ -86,7 +120,7 @@ false false def c -4 +6 Lift.scala covtest SomeFunctions @@ -103,24 +137,7 @@ false false c.f(g()) -5 -Lift.scala -covtest -SomeFunctions -Class -covtest.SomeFunctions -test -113 -114 -8 -c -Select -false -0 -false -c - -6 +7 Lift.scala covtest SomeFunctions @@ -137,7 +154,7 @@ false false g() -7 +8 Lift.scala covtest SomeFunctions diff --git a/tests/coverage/pos/Literals.scoverage.check b/tests/coverage/pos/Literals.scoverage.check index cd58a841d5b6..c6ea348844ca 100644 --- a/tests/coverage/pos/Literals.scoverage.check +++ b/tests/coverage/pos/Literals.scoverage.check @@ -42,6 +42,57 @@ Literals$package Object covtest.Literals$package block +119 +121 +5 + +Literal +false +0 +false +12 + +2 +Literals.scala +covtest +Literals$package +Object +covtest.Literals$package +block +124 +128 +6 + +Literal +false +0 +false +true + +3 +Literals.scala +covtest +Literals$package +Object +covtest.Literals$package +block +131 +135 +7 + +Literal +false +0 +false +null + +4 +Literals.scala +covtest +Literals$package +Object +covtest.Literals$package +block 17 26 3 @@ -52,7 +103,7 @@ false false def block -2 +5 Literals.scala covtest Literals$package @@ -69,7 +120,7 @@ false false ??? -3 +6 Literals.scala covtest Literals$package @@ -86,7 +137,7 @@ false false def f -4 +7 Literals.scala covtest Literals$package @@ -103,7 +154,7 @@ false false f(0,1,2)(3) -5 +8 Literals.scala covtest Literals$package diff --git a/tests/coverage/pos/MatchNumbers.scoverage.check b/tests/coverage/pos/MatchNumbers.scoverage.check index 43e01018f0ac..74dd5db8fc87 100644 --- a/tests/coverage/pos/MatchNumbers.scoverage.check +++ b/tests/coverage/pos/MatchNumbers.scoverage.check @@ -25,6 +25,40 @@ MatchNumbers Object covtest.MatchNumbers f +121 +126 +7 +< +Apply +false +0 +false +x < 0 + +1 +MatchNumbers.scala +covtest +MatchNumbers +Object +covtest.MatchNumbers +f +130 +132 +7 + +Literal +false +0 +false +-1 + +2 +MatchNumbers.scala +covtest +MatchNumbers +Object +covtest.MatchNumbers +f 127 132 7 @@ -35,7 +69,7 @@ true false => -1 -1 +3 MatchNumbers.scala covtest MatchNumbers @@ -52,7 +86,24 @@ true false => x -2 +4 +MatchNumbers.scala +covtest +MatchNumbers +Object +covtest.MatchNumbers +f +174 +181 +9 +toInt +Select +false +0 +false +y.toInt + +5 MatchNumbers.scala covtest MatchNumbers @@ -69,7 +120,7 @@ true false => y.toInt -3 +6 MatchNumbers.scala covtest MatchNumbers @@ -86,7 +137,7 @@ false false def f -4 +7 MatchNumbers.scala covtest MatchNumbers @@ -103,7 +154,7 @@ false false f(0) -5 +8 MatchNumbers.scala covtest MatchNumbers diff --git a/tests/coverage/pos/PolymorphicExtensions.scoverage.check b/tests/coverage/pos/PolymorphicExtensions.scoverage.check index 64795070b34f..6eebb8cc94fa 100644 --- a/tests/coverage/pos/PolymorphicExtensions.scoverage.check +++ b/tests/coverage/pos/PolymorphicExtensions.scoverage.check @@ -110,15 +110,15 @@ PolyExt Object covtest.PolyExt -177 -186 -11 -foo +277 +287 +12 +get Apply false 0 false -"str".foo +123.get(0) 6 PolymorphicExtensions.scala @@ -126,16 +126,16 @@ covtest PolyExt Object covtest.PolyExt - -277 -287 -12 -get -Apply +foo +385 +387 +14 + +Literal false 0 false -123.get(0) +42 7 PolymorphicExtensions.scala @@ -177,40 +177,6 @@ covtest PolyExt Object covtest.PolyExt -bar -405 -412 -15 -tap -Apply -false -0 -false -foo.tap - -10 -PolymorphicExtensions.scala -covtest -PolyExt -Object -covtest.PolyExt -bar -405 -408 -15 -foo -Ident -false -0 -false -foo - -11 -PolymorphicExtensions.scala -covtest -PolyExt -Object -covtest.PolyExt $anonfun 413 420 @@ -222,7 +188,7 @@ false false println -12 +10 PolymorphicExtensions.scala covtest PolyExt diff --git a/tests/coverage/pos/PolymorphicMethods.scoverage.check b/tests/coverage/pos/PolymorphicMethods.scoverage.check index b66aaeb92661..01cc80f6a4dc 100644 --- a/tests/coverage/pos/PolymorphicMethods.scoverage.check +++ b/tests/coverage/pos/PolymorphicMethods.scoverage.check @@ -72,19 +72,19 @@ C[String]().f("str", 0) 3 PolymorphicMethods.scala covtest -PolyMeth -Object -covtest.PolyMeth - -147 -158 -7 - -Apply +C +Class +covtest.C +f +221 +223 +10 + +Literal false 0 false -C[String]() +() 4 PolymorphicMethods.scala diff --git a/tests/coverage/pos/Select.scoverage.check b/tests/coverage/pos/Select.scoverage.check index cfe719552e44..a4830c1a6d5c 100644 --- a/tests/coverage/pos/Select.scoverage.check +++ b/tests/coverage/pos/Select.scoverage.check @@ -195,23 +195,6 @@ Select$package Object covtest.Select$package test -263 -273 -19 -instance -Select -false -0 -false -a.instance - -11 -Select.scala -covtest -Select$package -Object -covtest.Select$package -test 345 354 20 @@ -222,7 +205,7 @@ false false a.print() -12 +11 Select.scala covtest Select$package diff --git a/tests/coverage/pos/SimpleMethods.scoverage.check b/tests/coverage/pos/SimpleMethods.scoverage.check index 067bd744177b..03d3c1a440ec 100644 --- a/tests/coverage/pos/SimpleMethods.scoverage.check +++ b/tests/coverage/pos/SimpleMethods.scoverage.check @@ -42,6 +42,23 @@ C Class covtest.C b +60 +66 +5 + +Literal +false +0 +false +return + +2 +SimpleMethods.scala +covtest +C +Class +covtest.C +b 46 51 5 @@ -52,7 +69,24 @@ false false def b -2 +3 +SimpleMethods.scala +covtest +C +Class +covtest.C +c +83 +85 +6 + +Literal +false +0 +false +() + +4 SimpleMethods.scala covtest C @@ -69,7 +103,24 @@ false false def c -3 +5 +SimpleMethods.scala +covtest +C +Class +covtest.C +d +101 +103 +7 + +Literal +false +0 +false +12 + +6 SimpleMethods.scala covtest C @@ -86,7 +137,24 @@ false false def d -4 +7 +SimpleMethods.scala +covtest +C +Class +covtest.C +e +120 +124 +8 + +Literal +false +0 +false +null + +8 SimpleMethods.scala covtest C @@ -103,7 +171,41 @@ false false def e -5 +9 +SimpleMethods.scala +covtest +C +Class +covtest.C +block +149 +158 +11 + +Literal +false +0 +false +"literal" + +10 +SimpleMethods.scala +covtest +C +Class +covtest.C +block +163 +164 +12 + +Literal +false +0 +false +0 + +11 SimpleMethods.scala covtest C @@ -120,7 +222,41 @@ false false def block -6 +12 +SimpleMethods.scala +covtest +C +Class +covtest.C +cond +195 +200 +15 + +Literal +false +0 +false +false + +13 +SimpleMethods.scala +covtest +C +Class +covtest.C +cond +206 +210 +15 + +Literal +false +0 +false +true + +14 SimpleMethods.scala covtest C @@ -137,7 +273,24 @@ true false true -7 +15 +SimpleMethods.scala +covtest +C +Class +covtest.C +cond +220 +225 +16 + +Literal +false +0 +false +false + +16 SimpleMethods.scala covtest C @@ -154,7 +307,7 @@ true false false -8 +17 SimpleMethods.scala covtest C @@ -171,7 +324,41 @@ false false def cond -9 +18 +SimpleMethods.scala +covtest +C +Class +covtest.C +partialCond +260 +265 +19 + +Literal +false +0 +false +false + +19 +SimpleMethods.scala +covtest +C +Class +covtest.C +partialCond +271 +273 +19 + +Literal +false +0 +false +() + +20 SimpleMethods.scala covtest C @@ -188,7 +375,7 @@ true false () -10 +21 SimpleMethods.scala covtest C @@ -205,7 +392,7 @@ true false -11 +22 SimpleMethods.scala covtest C @@ -222,7 +409,7 @@ false false def partialCond -12 +23 SimpleMethods.scala covtest C @@ -239,7 +426,24 @@ false false def new1 -13 +24 +SimpleMethods.scala +covtest +C +Class +covtest.C +tryCatch +330 +332 +24 + +Literal +false +0 +false +() + +25 SimpleMethods.scala covtest C @@ -256,7 +460,24 @@ true false () -14 +26 +SimpleMethods.scala +covtest +C +Class +covtest.C +tryCatch +370 +371 +26 + +Literal +false +0 +false +1 + +27 SimpleMethods.scala covtest C @@ -273,7 +494,7 @@ true false => 1 -15 +28 SimpleMethods.scala covtest C diff --git a/tests/coverage/pos/StructuralTypes.scoverage.check b/tests/coverage/pos/StructuralTypes.scoverage.check index a487ac29c9de..2f0390044f04 100644 --- a/tests/coverage/pos/StructuralTypes.scoverage.check +++ b/tests/coverage/pos/StructuralTypes.scoverage.check @@ -41,6 +41,23 @@ covtest Record Class covtest.Record +$anonfun +159 +171 +6 +== +Apply +false +0 +false +_._1 == name + +2 +StructuralTypes.scala +covtest +Record +Class +covtest.Record selectDynamic 148 176 @@ -52,7 +69,7 @@ false false elems.find(_._1 == name).get -2 +3 StructuralTypes.scala covtest Record @@ -69,7 +86,24 @@ false false def selectDynamic -3 +4 +StructuralTypes.scala +covtest +StructuralTypes +Object +covtest.StructuralTypes +test +270 +307 +13 +apply +Apply +false +0 +false +Record("name" -> "Emma", "age" -> 42) + +5 StructuralTypes.scala covtest StructuralTypes @@ -86,7 +120,7 @@ false false "name" -> "Emma" -4 +6 StructuralTypes.scala covtest StructuralTypes @@ -103,7 +137,7 @@ false false "age" -> 42 -5 +7 StructuralTypes.scala covtest StructuralTypes @@ -120,7 +154,7 @@ false false person.name -6 +8 StructuralTypes.scala covtest StructuralTypes diff --git a/tests/coverage/pos/TypeLambdas.scoverage.check b/tests/coverage/pos/TypeLambdas.scoverage.check index de519038c367..f5779a45268b 100644 --- a/tests/coverage/pos/TypeLambdas.scoverage.check +++ b/tests/coverage/pos/TypeLambdas.scoverage.check @@ -76,6 +76,23 @@ TypeLambdas Object covtest.TypeLambdas test +367 +377 +17 +apply +Apply +false +0 +false +("a", "b") + +4 +TypeLambdas.scala +covtest +TypeLambdas +Object +covtest.TypeLambdas +test 382 396 18 @@ -86,7 +103,7 @@ false false println(tuple) -4 +5 TypeLambdas.scala covtest TypeLambdas diff --git a/tests/coverage/pos/i21695/A.scala b/tests/coverage/pos/i21695/A.scala new file mode 100644 index 000000000000..afe534eac46e --- /dev/null +++ b/tests/coverage/pos/i21695/A.scala @@ -0,0 +1,9 @@ +package example + +trait A { + def x1: Builder[?] + def x2: Service + + def create: String = + x1.addService(x2).build() +} diff --git a/tests/coverage/pos/i21695/Builder.java b/tests/coverage/pos/i21695/Builder.java new file mode 100644 index 000000000000..c7685043ec00 --- /dev/null +++ b/tests/coverage/pos/i21695/Builder.java @@ -0,0 +1,6 @@ +package example; + +public abstract class Builder> { + public abstract String build(); + public abstract T addService(Service service); +} diff --git a/tests/coverage/pos/i21695/Service.java b/tests/coverage/pos/i21695/Service.java new file mode 100644 index 000000000000..624e3c04f67f --- /dev/null +++ b/tests/coverage/pos/i21695/Service.java @@ -0,0 +1,3 @@ +package example; + +public class Service{} diff --git a/tests/coverage/pos/i21695/test.scoverage.check b/tests/coverage/pos/i21695/test.scoverage.check new file mode 100644 index 000000000000..d9ee3a172e3f --- /dev/null +++ b/tests/coverage/pos/i21695/test.scoverage.check @@ -0,0 +1,71 @@ +# Coverage data, format version: 3.0 +# Statement data: +# - id +# - source path +# - package name +# - class name +# - class type (Class, Object or Trait) +# - full class name +# - method name +# - start offset +# - end offset +# - line number +# - symbol name +# - tree name +# - is branch +# - invocations count +# - is ignored +# - description (can be multi-line) +# ' ' sign +# ------------------------------------------ +0 +i21695/A.scala +example +A +Trait +example.A +create +94 +119 +8 +build +Apply +false +0 +false +x1.addService(x2).build() + +1 +i21695/A.scala +example +A +Trait +example.A +create +108 +110 +8 +x2 +Select +false +0 +false +x2 + +2 +i21695/A.scala +example +A +Trait +example.A +create +69 +79 +7 +create +DefDef +false +0 +false +def create + diff --git a/tests/coverage/pos/i21877.scala b/tests/coverage/pos/i21877.scala new file mode 100644 index 000000000000..13d4c9dfc8e2 --- /dev/null +++ b/tests/coverage/pos/i21877.scala @@ -0,0 +1,21 @@ + + +class Schema[T] +object Schema { + inline def derived[T]: Schema[T] = new Schema[T] +} + +case class Bar(x: Int) + +object Foo { + def foo(x: Int): String = { + val bar = Bar(x) + if (x == 5) "5" else "idk" + bar.toString + } + + implicit val schema: Schema[Bar] = Schema.derived +} + +@main def test() = + Foo.foo(4) + Foo.foo(5) diff --git a/tests/coverage/pos/i21877.scoverage.check b/tests/coverage/pos/i21877.scoverage.check new file mode 100644 index 000000000000..7f95bcb7c010 --- /dev/null +++ b/tests/coverage/pos/i21877.scoverage.check @@ -0,0 +1,224 @@ +# Coverage data, format version: 3.0 +# Statement data: +# - id +# - source path +# - package name +# - class name +# - class type (Class, Object or Trait) +# - full class name +# - method name +# - start offset +# - end offset +# - line number +# - symbol name +# - tree name +# - is branch +# - invocations count +# - is ignored +# - description (can be multi-line) +# ' ' sign +# ------------------------------------------ +0 +i21877.scala + +Foo +Object +.Foo +foo +169 +175 +12 +apply +Apply +false +0 +false +Bar(x) + +1 +i21877.scala + +Foo +Object +.Foo +foo +184 +190 +13 +== +Apply +false +0 +false +x == 5 + +2 +i21877.scala + +Foo +Object +.Foo +foo +192 +195 +13 + +Literal +false +0 +false +"5" + +3 +i21877.scala + +Foo +Object +.Foo +foo +192 +195 +13 + +Literal +true +0 +false +"5" + +4 +i21877.scala + +Foo +Object +.Foo +foo +201 +221 +13 ++ +Apply +false +0 +false +"idk" + bar.toString + +5 +i21877.scala + +Foo +Object +.Foo +foo +201 +206 +13 + +Literal +false +0 +false +"idk" + +6 +i21877.scala + +Foo +Object +.Foo +foo +209 +221 +13 +toString +Apply +false +0 +false +bar.toString + +7 +i21877.scala + +Foo +Object +.Foo +foo +201 +221 +13 ++ +Apply +true +0 +false +"idk" + bar.toString + +8 +i21877.scala + +Foo +Object +.Foo +foo +127 +134 +11 +foo +DefDef +false +0 +false +def foo + +9 +i21877.scala + +i21877$package +Object +.i21877$package +test +303 +313 +20 +foo +Apply +false +0 +false +Foo.foo(4) + +10 +i21877.scala + +i21877$package +Object +.i21877$package +test +316 +326 +21 +foo +Apply +false +0 +false +Foo.foo(5) + +11 +i21877.scala + +i21877$package +Object +.i21877$package +test +282 +296 +19 +test +DefDef +false +0 +false +@main def test + diff --git a/tests/coverage/pos/macro-late-suspend/test.scoverage.check b/tests/coverage/pos/macro-late-suspend/test.scoverage.check index f962705ed2ce..051f30e08e67 100644 --- a/tests/coverage/pos/macro-late-suspend/test.scoverage.check +++ b/tests/coverage/pos/macro-late-suspend/test.scoverage.check @@ -53,23 +53,6 @@ false private def mkVisitorTypeImpl 3 -macro-late-suspend/Test.scala -example -Test -Object -example.Test - -102 -121 -8 - -Apply -false -0 -false -mkVisitorType[Test] - -4 macro-late-suspend/UsesTest.scala example UsesTest$package diff --git a/tests/coverage/pos/scoverage-samples-case.scoverage.check b/tests/coverage/pos/scoverage-samples-case.scoverage.check index 4b67fa77541c..a0fa8febcd99 100644 --- a/tests/coverage/pos/scoverage-samples-case.scoverage.check +++ b/tests/coverage/pos/scoverage-samples-case.scoverage.check @@ -24,6 +24,23 @@ org.scoverage.samples PriceEngine Class org.scoverage.samples.PriceEngine + +293 +298 +14 + +Literal +false +0 +false +"abc" + +1 +scoverage-samples-case.scala +org.scoverage.samples +PriceEngine +Class +org.scoverage.samples.PriceEngine $anonfun 362 368 @@ -35,7 +52,7 @@ false false stop() -1 +2 scoverage-samples-case.scala org.scoverage.samples PriceEngine @@ -52,7 +69,7 @@ true false =>\n stop() -2 +3 scoverage-samples-case.scala org.scoverage.samples PriceEngine @@ -69,7 +86,7 @@ false false stop() -3 +4 scoverage-samples-case.scala org.scoverage.samples PriceEngine @@ -86,7 +103,7 @@ true false =>\n stop() -4 +5 scoverage-samples-case.scala org.scoverage.samples PriceEngine @@ -103,7 +120,24 @@ false false def receive -5 +6 +scoverage-samples-case.scala +org.scoverage.samples +PriceEngine +Class +org.scoverage.samples.PriceEngine +stop +443 +462 +25 +!= +Apply +false +0 +false +cancellable != null + +7 scoverage-samples-case.scala org.scoverage.samples PriceEngine @@ -120,7 +154,7 @@ false false println("stop") -6 +8 scoverage-samples-case.scala org.scoverage.samples PriceEngine @@ -137,7 +171,7 @@ true false println("stop") -7 +9 scoverage-samples-case.scala org.scoverage.samples PriceEngine @@ -154,7 +188,7 @@ true false -8 +10 scoverage-samples-case.scala org.scoverage.samples PriceEngine diff --git a/tests/coverage/pos/scoverage-samples-implicit-class.scoverage.check b/tests/coverage/pos/scoverage-samples-implicit-class.scoverage.check index f9bb9e0cd6a3..1c349a5de5c8 100644 --- a/tests/coverage/pos/scoverage-samples-implicit-class.scoverage.check +++ b/tests/coverage/pos/scoverage-samples-implicit-class.scoverage.check @@ -25,15 +25,15 @@ CreditEngine Class org.scoverage.samples.CreditEngine $anonfun -263 -276 -11 -! +245 +255 +10 +< Apply false 0 false -"if 1" ! "xd" +req < 2000 1 scoverage-samples-implicit-class.scala @@ -43,14 +43,14 @@ Class org.scoverage.samples.CreditEngine $anonfun 263 -269 +276 11 -StringOpssssss +! Apply false 0 false -"if 1" +"if 1" ! "xd" 2 scoverage-samples-implicit-class.scala @@ -161,23 +161,6 @@ StringOpssssss Class org.scoverage.samples.StringOpssssss ! -160 -167 -5 -+ -Apply -false -0 -false -s + "!" - -9 -scoverage-samples-implicit-class.scala -org.scoverage.samples -StringOpssssss -Class -org.scoverage.samples.StringOpssssss -! 124 129 5 @@ -188,7 +171,7 @@ false false def ! -10 +9 scoverage-samples-implicit-class.scala org.scoverage.samples scoverage-samples-implicit-class$package diff --git a/tests/coverage/run/currying/test.scoverage.check b/tests/coverage/run/currying/test.scoverage.check index abc1876942db..5d1b4233a226 100644 --- a/tests/coverage/run/currying/test.scoverage.check +++ b/tests/coverage/run/currying/test.scoverage.check @@ -25,6 +25,23 @@ Test Object .Test f1 +48 +53 +2 ++ +Apply +false +0 +false +a+b+c + +1 +currying/test.scala + +Test +Object +.Test +f1 15 21 2 @@ -35,7 +52,24 @@ false false def f1 -1 +2 +currying/test.scala + +Test +Object +.Test +$anonfun +105 +110 +4 ++ +Apply +false +0 +false +a+b+c + +3 currying/test.scala Test @@ -52,7 +86,24 @@ false false def f2 -2 +4 +currying/test.scala + +Test +Object +.Test +$anonfun +166 +171 +7 ++ +Apply +false +0 +false +a+b+c + +5 currying/test.scala Test @@ -69,7 +120,24 @@ false false def g1 -3 +6 +currying/test.scala + +Test +Object +.Test +g2 +226 +231 +9 ++ +Apply +false +0 +false +a+b+c + +7 currying/test.scala Test @@ -86,7 +154,7 @@ false false def g2 -4 +8 currying/test.scala Test @@ -103,7 +171,7 @@ false false println(f1(0)(1)(2)) -5 +9 currying/test.scala Test @@ -120,7 +188,7 @@ false false f1(0)(1)(2) -6 +10 currying/test.scala Test @@ -137,7 +205,7 @@ false false println(f2(0)(1)(2)) -7 +11 currying/test.scala Test @@ -154,58 +222,7 @@ false false f2(0)(1)(2) -8 -currying/test.scala - -Test -Object -.Test -main -310 -318 -13 -apply -Apply -false -0 -false -f2(0)(1) - -9 -currying/test.scala - -Test -Object -.Test -main -310 -315 -13 -apply -Apply -false -0 -false -f2(0) - -10 -currying/test.scala - -Test -Object -.Test -main -310 -312 -13 -f2 -Ident -false -0 -false -f2 - -11 +12 currying/test.scala Test @@ -222,7 +239,7 @@ false false println(g1(using 0)(using 1)(using 2)) -12 +13 currying/test.scala Test @@ -239,7 +256,7 @@ false false g1(using 0)(using 1)(using 2) -13 +14 currying/test.scala Test @@ -256,7 +273,7 @@ false false println(g2(using 0)(using 1)(using 2)) -14 +15 currying/test.scala Test @@ -273,7 +290,7 @@ false false g2(using 0)(using 1)(using 2) -15 +16 currying/test.scala Test diff --git a/tests/coverage/run/extend-case-class/test.scoverage.check b/tests/coverage/run/extend-case-class/test.scoverage.check index b355140d2520..107cef2cc371 100644 --- a/tests/coverage/run/extend-case-class/test.scoverage.check +++ b/tests/coverage/run/extend-case-class/test.scoverage.check @@ -21,6 +21,91 @@ 0 extend-case-class/test.scala +DecimalConf +Object +.DecimalConf + +194 +198 +4 + +Literal +false +0 +false +6178 + +1 +extend-case-class/test.scala + +DecimalConf +Object +.DecimalConf + +200 +203 +4 + +Literal +false +0 +false +308 + +2 +extend-case-class/test.scala + +test$package +Object +.test$package +Test +239 +279 +8 +apply +Apply +false +0 +false +DecimalConf(MathContext.DECIMAL32, 1, 0) + +3 +extend-case-class/test.scala + +test$package +Object +.test$package +Test +274 +275 +8 + +Literal +false +0 +false +1 + +4 +extend-case-class/test.scala + +test$package +Object +.test$package +Test +277 +278 +8 + +Literal +false +0 +false +0 + +5 +extend-case-class/test.scala + test$package Object .test$package @@ -35,7 +120,7 @@ false false println(c.scaleLimit) -1 +6 extend-case-class/test.scala test$package @@ -52,7 +137,7 @@ false false println(DecimalConf.scaleLimit) -2 +7 extend-case-class/test.scala test$package diff --git a/tests/coverage/run/i16940/test.scoverage.check b/tests/coverage/run/i16940/test.scoverage.check index 357080ba9da8..eb8cd5d7d61c 100644 --- a/tests/coverage/run/i16940/test.scoverage.check +++ b/tests/coverage/run/i16940/test.scoverage.check @@ -59,23 +59,6 @@ Test Object .Test -371 -454 -20 -sequence -Apply -false -0 -false -Future.sequence(Seq(brokenSynchronizedBlock(false), brokenSynchronizedBlock(true))) - -3 -i16940/i16940.scala - -Test -Object -.Test - 387 453 20 @@ -86,7 +69,7 @@ false false Seq(brokenSynchronizedBlock(false), brokenSynchronizedBlock(true)) -4 +3 i16940/i16940.scala Test @@ -103,7 +86,7 @@ false false brokenSynchronizedBlock(false) -5 +4 i16940/i16940.scala Test @@ -120,7 +103,7 @@ false false brokenSynchronizedBlock(true) -6 +5 i16940/i16940.scala Test @@ -137,75 +120,58 @@ false false println(test) -7 +6 i16940/i16940.scala Test Object .Test -$anonfun -508 -525 -23 -assertFailed + +539 +540 +25 +DurationInt Apply false 0 false -assert(test == 2) +3 -8 +7 i16940/i16940.scala Test Object .Test -$anonfun -508 -525 -23 -assertFailed -Apply -true -0 + +539 +548 +25 +seconds +Select false -assert(test == 2) - -9 -i16940/i16940.scala - -Test -Object -.Test -$anonfun -508 -525 -23 - -Literal -true 0 false -assert(test == 2) +3.seconds -10 +8 i16940/i16940.scala -Test +i16940$package Object -.Test +.i16940$package -539 -548 -25 -seconds -Select +125 +126 +5 + +Literal false 0 false -3.seconds +0 -11 +9 i16940/i16940.scala i16940$package @@ -222,7 +188,7 @@ false false Future {\n if (option) {\n Thread.sleep(500)\n }\n synchronized {\n val tmp = test\n Thread.sleep(1000)\n test = tmp + 1\n }\n} -12 +10 i16940/i16940.scala i16940$package @@ -239,7 +205,7 @@ false false Thread.sleep(500) -13 +11 i16940/i16940.scala i16940$package @@ -256,7 +222,7 @@ true false {\n Thread.sleep(500)\n } -14 +12 i16940/i16940.scala i16940$package @@ -273,7 +239,7 @@ true false -15 +13 i16940/i16940.scala i16940$package @@ -290,7 +256,7 @@ false false synchronized {\n val tmp = test\n Thread.sleep(1000)\n test = tmp + 1\n } -16 +14 i16940/i16940.scala i16940$package @@ -307,7 +273,24 @@ false false Thread.sleep(1000) -17 +15 +i16940/i16940.scala + +i16940$package +Object +.i16940$package +brokenSynchronizedBlock +310 +317 +14 ++ +Apply +false +0 +false +tmp + 1 + +16 i16940/i16940.scala i16940$package diff --git a/tests/coverage/run/i18233-min/test.scoverage.check b/tests/coverage/run/i18233-min/test.scoverage.check index 7570ebaaed96..6bc38ed61a85 100644 --- a/tests/coverage/run/i18233-min/test.scoverage.check +++ b/tests/coverage/run/i18233-min/test.scoverage.check @@ -42,23 +42,6 @@ Test Object .Test -139 -144 -11 -aList -Ident -false -0 -false -aList - -2 -i18233-min/i18233-min.scala - -Test -Object -.Test - 148 168 12 @@ -69,24 +52,7 @@ false false println(anotherList) -3 -i18233-min/i18233-min.scala - -Test -Object -.Test - -156 -167 -12 -anotherList -Ident -false -0 -false -anotherList - -4 +2 i18233-min/i18233-min.scala i18233-min$package @@ -103,7 +69,7 @@ false false List(Array[String]()*) -5 +3 i18233-min/i18233-min.scala i18233-min$package @@ -120,7 +86,7 @@ false false Array[String]() -6 +4 i18233-min/i18233-min.scala i18233-min$package @@ -137,7 +103,7 @@ false false def aList -7 +5 i18233-min/i18233-min.scala i18233-min$package @@ -154,6 +120,40 @@ false false Array("abc", "def") +6 +i18233-min/i18233-min.scala + +i18233-min$package +Object +.i18233-min$package +arr +56 +61 +5 + +Literal +false +0 +false +"abc" + +7 +i18233-min/i18233-min.scala + +i18233-min$package +Object +.i18233-min$package +arr +63 +68 +5 + +Literal +false +0 +false +"def" + 8 i18233-min/i18233-min.scala diff --git a/tests/coverage/run/inheritance/test.scoverage.check b/tests/coverage/run/inheritance/test.scoverage.check index 387a080463e2..48b8e02d59e1 100644 --- a/tests/coverage/run/inheritance/test.scoverage.check +++ b/tests/coverage/run/inheritance/test.scoverage.check @@ -21,6 +21,23 @@ 0 inheritance/test.scala +B +Class +.B + +61 +62 +2 + +Literal +false +0 +false +0 + +1 +inheritance/test.scala + C1 Class .C1 @@ -35,7 +52,24 @@ false false println("block") +2 +inheritance/test.scala + +C1 +Class +.C1 + +102 +103 +3 + +Literal +false +0 +false 1 + +3 inheritance/test.scala C2 @@ -52,7 +86,7 @@ false false A(2,2) -2 +4 inheritance/test.scala test$package @@ -69,7 +103,7 @@ false false println(C1().x) -3 +5 inheritance/test.scala test$package @@ -86,7 +120,7 @@ false false C1() -4 +6 inheritance/test.scala test$package @@ -103,7 +137,7 @@ false false println(C2().x) -5 +7 inheritance/test.scala test$package @@ -120,7 +154,7 @@ false false C2() -6 +8 inheritance/test.scala test$package diff --git a/tests/coverage/run/inline-def/test.scoverage.check b/tests/coverage/run/inline-def/test.scoverage.check index 17fa7c049107..7ca855c41008 100644 --- a/tests/coverage/run/inline-def/test.scoverage.check +++ b/tests/coverage/run/inline-def/test.scoverage.check @@ -76,23 +76,6 @@ test$package Object .test$package Test -134 -148 -8 -toString -Apply -false -0 -false -"foo".toString - -4 -inline-def/test.scala - -test$package -Object -.test$package -Test 263 277 16 @@ -103,24 +86,7 @@ false false println(a.bar) -5 -inline-def/test.scala - -test$package -Object -.test$package -Test -176 -190 -9 -toString -Apply -false -0 -false -"bar".toString - -6 +4 inline-def/test.scala test$package @@ -137,7 +103,7 @@ false false println(b.foo) -7 +5 inline-def/test.scala test$package @@ -154,7 +120,7 @@ false false b.foo -8 +6 inline-def/test.scala test$package diff --git a/tests/coverage/run/interpolation/test.scoverage.check b/tests/coverage/run/interpolation/test.scoverage.check index 37562dab5509..3147a19a61a0 100644 --- a/tests/coverage/run/interpolation/test.scoverage.check +++ b/tests/coverage/run/interpolation/test.scoverage.check @@ -127,15 +127,15 @@ Test Object .Test main -229 -278 -11 -map -Apply +200 +203 +10 + +Literal false 0 false -xs.zipWithIndex.map((s, i) => println(s"$i: $s")) +"d" 7 interpolation/test.scala @@ -144,17 +144,85 @@ Test Object .Test main +205 +208 +10 + +Literal +false +0 +false +"o" + +8 +interpolation/test.scala + +Test +Object +.Test +main +210 +213 +10 + +Literal +false +0 +false +"t" + +9 +interpolation/test.scala + +Test +Object +.Test +main +215 +218 +10 + +Literal +false +0 +false +"t" + +10 +interpolation/test.scala + +Test +Object +.Test +main +220 +223 +10 + +Literal +false +0 +false +"y" + +11 +interpolation/test.scala + +Test +Object +.Test +main 229 -244 +278 11 -zipWithIndex -Select +map +Apply false 0 false -xs.zipWithIndex +xs.zipWithIndex.map((s, i) => println(s"$i: $s")) -8 +12 interpolation/test.scala Test @@ -171,7 +239,7 @@ false false println(s"$i: $s") -9 +13 interpolation/test.scala Test @@ -188,7 +256,7 @@ false false s"$i: $s" -10 +14 interpolation/test.scala Test @@ -205,7 +273,7 @@ false false println(simple(1, "abc")) -11 +15 interpolation/test.scala Test @@ -222,7 +290,7 @@ false false simple(1, "abc") -12 +16 interpolation/test.scala Test @@ -239,7 +307,7 @@ false false println(hexa(127)) -13 +17 interpolation/test.scala Test @@ -256,7 +324,7 @@ false false hexa(127) -14 +18 interpolation/test.scala Test @@ -273,7 +341,7 @@ false false println(raw"a\\nb") -15 +19 interpolation/test.scala Test @@ -290,7 +358,7 @@ false false raw"a\\nb" -16 +20 interpolation/test.scala Test diff --git a/tests/coverage/run/java-methods/test.scoverage.check b/tests/coverage/run/java-methods/test.scoverage.check index 891af1804831..679a5b7dbff4 100644 --- a/tests/coverage/run/java-methods/test.scoverage.check +++ b/tests/coverage/run/java-methods/test.scoverage.check @@ -58,6 +58,23 @@ java-methods/test.scala test$package Object .test$package +$anonfun +124 +126 +6 + +Literal +false +0 +false +() + +3 +java-methods/test.scala + +test$package +Object +.test$package Test 140 152 @@ -69,7 +86,7 @@ false false JavaObject() -3 +4 java-methods/test.scala test$package @@ -86,7 +103,7 @@ false false obj.f() -4 +5 java-methods/test.scala test$package @@ -103,7 +120,7 @@ false false println(obj.identity[Int](0)) -5 +6 java-methods/test.scala test$package @@ -120,7 +137,7 @@ false false obj.identity[Int](0) -6 +7 java-methods/test.scala test$package @@ -137,7 +154,7 @@ false false println("ok!") -7 +8 java-methods/test.scala test$package diff --git a/tests/coverage/run/lifting-bool/test.scoverage.check b/tests/coverage/run/lifting-bool/test.scoverage.check index 5eb3d864939f..7ed81d8b535c 100644 --- a/tests/coverage/run/lifting-bool/test.scoverage.check +++ b/tests/coverage/run/lifting-bool/test.scoverage.check @@ -76,6 +76,40 @@ test$package Object .test$package Test +101 +120 +8 +|| +Apply +false +0 +false +true || notCalled() + +4 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +101 +105 +8 + +Literal +false +0 +false +true + +5 +lifting-bool/test.scala + +test$package +Object +.test$package +Test 109 120 8 @@ -86,7 +120,41 @@ false false notCalled() -4 +6 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +150 +170 +9 +&& +Apply +false +0 +false +false && notCalled() + +7 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +150 +155 +9 + +Literal +false +0 +false +false + +8 lifting-bool/test.scala test$package @@ -103,7 +171,41 @@ false false notCalled() -5 +9 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +200 +230 +10 +|| +Apply +false +0 +false +(true || false) || notCalled() + +10 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +201 +205 +10 + +Literal +false +0 +false +true + +11 lifting-bool/test.scala test$package @@ -120,7 +222,75 @@ false false notCalled() -6 +12 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +249 +278 +11 +&& +Apply +false +0 +false +true && (false && notCalled() + +13 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +249 +253 +11 + +Literal +false +0 +false +true + +14 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +258 +278 +11 +&& +Apply +false +0 +false +false && notCalled() + +15 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +258 +263 +11 + +Literal +false +0 +false +false + +16 lifting-bool/test.scala test$package @@ -137,7 +307,41 @@ false false notCalled() -7 +17 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +299 +329 +12 +&& +Apply +false +0 +false +(true && false) && notCalled() + +18 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +300 +304 +12 + +Literal +false +0 +false +true + +19 lifting-bool/test.scala test$package @@ -154,7 +358,7 @@ false false notCalled() -8 +20 lifting-bool/test.scala test$package @@ -171,7 +375,7 @@ false false println(s"$a $b $c $d $e") -9 +21 lifting-bool/test.scala test$package @@ -188,7 +392,7 @@ false false s"$a $b $c $d $e" -10 +22 lifting-bool/test.scala test$package @@ -205,7 +409,7 @@ false false f(true, false) -11 +23 lifting-bool/test.scala test$package @@ -222,7 +426,7 @@ false false println(x) -12 +24 lifting-bool/test.scala test$package @@ -239,7 +443,41 @@ false false f(true || notCalled(), false && notCalled()) -13 +25 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +424 +443 +18 +|| +Apply +false +0 +false +true || notCalled() + +26 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +424 +428 +18 + +Literal +false +0 +false +true + +27 lifting-bool/test.scala test$package @@ -256,7 +494,41 @@ false false notCalled() -14 +28 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +445 +465 +18 +&& +Apply +false +0 +false +false && notCalled() + +29 +lifting-bool/test.scala + +test$package +Object +.test$package +Test +445 +450 +18 + +Literal +false +0 +false +false + +30 lifting-bool/test.scala test$package @@ -273,7 +545,7 @@ false false notCalled() -15 +31 lifting-bool/test.scala test$package @@ -290,7 +562,7 @@ false false println(x) -16 +32 lifting-bool/test.scala test$package diff --git a/tests/coverage/run/lifting/test.scoverage.check b/tests/coverage/run/lifting/test.scoverage.check index 136b8e2e4fbb..f876ba013f46 100644 --- a/tests/coverage/run/lifting/test.scoverage.check +++ b/tests/coverage/run/lifting/test.scoverage.check @@ -42,15 +42,15 @@ Vals Class .Vals -41 -57 -3 -:: -Apply +27 +28 +2 + +Literal false 0 false -l :: List(1,2,3) +1 2 lifting/test.scala @@ -59,66 +59,66 @@ Vals Class .Vals -46 +41 57 3 -apply +:: Apply false 0 false -List(1,2,3) +l :: List(1,2,3) 3 lifting/test.scala -A +Vals Class -.A -msg -104 -136 -6 -+ -Apply +.Vals + +51 +52 +3 + +Literal false 0 false -"string" + a + "." + b + "." + c +1 4 lifting/test.scala -A +Vals Class -.A -msg -104 -132 -6 -+ -Apply +.Vals + +53 +54 +3 + +Literal false 0 false -"string" + a + "." + b + "." +2 5 lifting/test.scala -A +Vals Class -.A -msg -104 -126 -6 -+ -Apply +.Vals + +55 +56 +3 + +Literal false 0 false -"string" + a + "." + b +3 6 lifting/test.scala @@ -128,14 +128,14 @@ Class .A msg 104 -122 +136 6 + Apply false 0 false -"string" + a + "." +"string" + a + "." + b + "." + c 7 lifting/test.scala @@ -145,14 +145,14 @@ Class .A msg 104 -116 +112 6 -+ -Apply + +Literal false 0 false -"string" + a +"string" 8 lifting/test.scala @@ -178,6 +178,23 @@ A Class .A integer +158 +159 +7 + +Literal +false +0 +false +0 + +10 +lifting/test.scala + +A +Class +.A +integer 139 150 7 @@ -188,7 +205,7 @@ false false def integer -10 +11 lifting/test.scala A @@ -205,7 +222,7 @@ false false def ex -11 +12 lifting/test.scala test$package @@ -222,7 +239,41 @@ false false A() -12 +13 +lifting/test.scala + +test$package +Object +.test$package +Test +235 +238 +13 + +Literal +false +0 +false +123 + +14 +lifting/test.scala + +test$package +Object +.test$package +f +251 +253 +14 + +Literal +false +0 +false +-1 + +15 lifting/test.scala test$package @@ -239,7 +290,7 @@ false false def f -13 +16 lifting/test.scala test$package @@ -256,7 +307,24 @@ false false a.msg(i, 0, a.integer) -14 +17 +lifting/test.scala + +test$package +Object +.test$package +Test +273 +274 +15 + +Literal +false +0 +false +0 + +18 lifting/test.scala test$package @@ -273,7 +341,7 @@ false false a.integer -15 +19 lifting/test.scala test$package @@ -290,7 +358,7 @@ false false println(x) -16 +20 lifting/test.scala test$package @@ -307,24 +375,24 @@ false false a.ex.msg(i, 0, a.ex.integer) -17 +21 lifting/test.scala test$package Object .test$package Test -306 -310 +318 +319 17 -ex -Select + +Literal false 0 false -a.ex +0 -18 +22 lifting/test.scala test$package @@ -341,7 +409,7 @@ false false a.ex -19 +23 lifting/test.scala test$package @@ -358,7 +426,7 @@ false false a.ex.integer -20 +24 lifting/test.scala test$package @@ -375,7 +443,7 @@ false false println(x) -21 +25 lifting/test.scala test$package @@ -392,7 +460,7 @@ false false a.msg(f(), 0, i) -22 +26 lifting/test.scala test$package @@ -409,7 +477,24 @@ false false f() -23 +27 +lifting/test.scala + +test$package +Object +.test$package +Test +365 +366 +19 + +Literal +false +0 +false +0 + +28 lifting/test.scala test$package @@ -426,7 +511,7 @@ false false println(x) -24 +29 lifting/test.scala test$package diff --git a/tests/coverage/run/macro-suspend/test.scoverage.check b/tests/coverage/run/macro-suspend/test.scoverage.check index 759897eb7747..4cae5a319598 100644 --- a/tests/coverage/run/macro-suspend/test.scoverage.check +++ b/tests/coverage/run/macro-suspend/test.scoverage.check @@ -76,6 +76,23 @@ Greeting Object .Greeting greet +252 +259 +8 + +Literal +false +0 +false +"hello" + +4 +macro-suspend/Macro.scala + +Greeting +Object +.Greeting +greet 238 247 8 @@ -86,7 +103,7 @@ false false def greet -4 +5 macro-suspend/Test.scala Test @@ -103,23 +120,6 @@ false false println(Macro.decorate(Greeting.greet())) -5 -macro-suspend/Test.scala - -Test -Object -.Test -main -65 -97 -3 -+ -Apply -false -0 -false -Macro.decorate(Greeting.greet()) - 6 macro-suspend/Test.scala @@ -127,40 +127,6 @@ Test Object .Test main -65 -97 -3 -+ -Apply -false -0 -false -Macro.decorate(Greeting.greet()) - -7 -macro-suspend/Test.scala - -Test -Object -.Test -main -80 -96 -3 -greet -Apply -false -0 -false -Greeting.greet() - -8 -macro-suspend/Test.scala - -Test -Object -.Test -main 15 23 2 diff --git a/tests/coverage/run/parameterless/test.scoverage.check b/tests/coverage/run/parameterless/test.scoverage.check index 5050180e7886..b792ce9c73ea 100644 --- a/tests/coverage/run/parameterless/test.scoverage.check +++ b/tests/coverage/run/parameterless/test.scoverage.check @@ -42,6 +42,23 @@ O Object .O f +51 +60 +4 + +Literal +false +0 +false +"O.f_res" + +2 +parameterless/test.scala + +O +Object +.O +f 12 17 2 @@ -52,7 +69,7 @@ false false def f -2 +3 parameterless/test.scala O @@ -69,7 +86,24 @@ false false println("O.g") -3 +4 +parameterless/test.scala + +O +Object +.O +g +106 +115 +8 + +Literal +false +0 +false +"O.g_res" + +5 parameterless/test.scala O @@ -86,7 +120,7 @@ false false def g -4 +6 parameterless/test.scala test$package @@ -103,7 +137,24 @@ false false println("f") -5 +7 +parameterless/test.scala + +test$package +Object +.test$package +f +179 +186 +14 + +Literal +false +0 +false +"f_res" + +8 parameterless/test.scala test$package @@ -120,7 +171,7 @@ false false def f -6 +9 parameterless/test.scala test$package @@ -137,7 +188,24 @@ false false println("g") -7 +10 +parameterless/test.scala + +test$package +Object +.test$package +g +230 +237 +18 + +Literal +false +0 +false +"g_res" + +11 parameterless/test.scala test$package @@ -154,7 +222,7 @@ false false def g -8 +12 parameterless/test.scala test$package @@ -171,7 +239,7 @@ false false f -9 +13 parameterless/test.scala test$package @@ -188,7 +256,7 @@ false false g -10 +14 parameterless/test.scala test$package @@ -205,24 +273,7 @@ false false println(f) -11 -parameterless/test.scala - -test$package -Object -.test$package -Test -273 -274 -22 -f -Ident -false -0 -false -f - -12 +15 parameterless/test.scala test$package @@ -239,7 +290,7 @@ false false println(g) -13 +16 parameterless/test.scala test$package @@ -256,7 +307,7 @@ false false g -14 +17 parameterless/test.scala test$package @@ -273,7 +324,7 @@ false false println(O.f) -15 +18 parameterless/test.scala test$package @@ -290,7 +341,7 @@ false false O.f -16 +19 parameterless/test.scala test$package @@ -307,7 +358,7 @@ false false println(O.g) -17 +20 parameterless/test.scala test$package @@ -324,7 +375,7 @@ false false O.g -18 +21 parameterless/test.scala test$package diff --git a/tests/coverage/run/trait/test.scoverage.check b/tests/coverage/run/trait/test.scoverage.check index 19a88ebc7f6f..1f921a3bfae8 100644 --- a/tests/coverage/run/trait/test.scoverage.check +++ b/tests/coverage/run/trait/test.scoverage.check @@ -25,6 +25,23 @@ T1 Trait .T1 x +20 +21 +2 + +Literal +false +0 +false +0 + +1 +trait/test.scala + +T1 +Trait +.T1 +x 12 17 2 @@ -35,7 +52,24 @@ false false def x -1 +2 +trait/test.scala + +Impl2 +Class +.Impl2 + +94 +100 +7 + +Literal +false +0 +false +"test" + +3 trait/test.scala Impl3 @@ -52,7 +86,7 @@ false false Impl2() -2 +4 trait/test.scala test$package @@ -69,7 +103,7 @@ false false println(Impl1().x) -3 +5 trait/test.scala test$package @@ -86,7 +120,7 @@ false false Impl1() -4 +6 trait/test.scala test$package @@ -103,7 +137,7 @@ false false Impl1().x -5 +7 trait/test.scala test$package @@ -120,7 +154,7 @@ false false println(Impl2().p) -6 +8 trait/test.scala test$package @@ -137,7 +171,7 @@ false false Impl2() -7 +9 trait/test.scala test$package @@ -154,7 +188,7 @@ false false println(Impl3().p) -8 +10 trait/test.scala test$package @@ -171,7 +205,7 @@ false false Impl3() -9 +11 trait/test.scala test$package diff --git a/tests/coverage/run/type-apply/test.measurement.check b/tests/coverage/run/type-apply/test.measurement.check index f1d6ee365359..e199fee6b817 100644 --- a/tests/coverage/run/type-apply/test.measurement.check +++ b/tests/coverage/run/type-apply/test.measurement.check @@ -1,5 +1,7 @@ -4 +6 1 2 3 +4 +5 0 diff --git a/tests/coverage/run/type-apply/test.scoverage.check b/tests/coverage/run/type-apply/test.scoverage.check index 7d76b11f2f8b..c20e88c0351d 100644 --- a/tests/coverage/run/type-apply/test.scoverage.check +++ b/tests/coverage/run/type-apply/test.scoverage.check @@ -59,15 +59,15 @@ test$package Object .test$package Test -171 -182 +176 +177 5 -apply -Apply + +Literal false 0 false -List(1,2,3) +1 3 type-apply/test.scala @@ -75,6 +75,40 @@ type-apply/test.scala test$package Object .test$package +Test +178 +179 +5 + +Literal +false +0 +false +2 + +4 +type-apply/test.scala + +test$package +Object +.test$package +Test +180 +181 +5 + +Literal +false +0 +false +3 + +5 +type-apply/test.scala + +test$package +Object +.test$package $anonfun 192 199 @@ -86,7 +120,7 @@ false false List(a) -4 +6 type-apply/test.scala test$package diff --git a/tests/coverage/run/varargs/test.scoverage.check b/tests/coverage/run/varargs/test.scoverage.check index 3c31f9388409..024156dcfeb6 100644 --- a/tests/coverage/run/varargs/test.scoverage.check +++ b/tests/coverage/run/varargs/test.scoverage.check @@ -25,6 +25,23 @@ test_1$package Object .test_1$package repeated +75 +77 +4 + +Literal +false +0 +false +() + +1 +varargs/test_1.scala + +test_1$package +Object +.test_1$package +repeated 48 60 4 @@ -35,7 +52,7 @@ false false def repeated -1 +2 varargs/test_1.scala test_1$package @@ -52,7 +69,7 @@ false false def f -2 +3 varargs/test_1.scala test_1$package @@ -69,7 +86,7 @@ false false repeated() -3 +4 varargs/test_1.scala test_1$package @@ -86,7 +103,7 @@ false false repeated(f(""), "b") -4 +5 varargs/test_1.scala test_1$package @@ -103,7 +120,24 @@ false false f("") -5 +6 +varargs/test_1.scala + +test_1$package +Object +.test_1$package +Test +149 +152 +11 + +Literal +false +0 +false +"b" + +7 varargs/test_1.scala test_1$package @@ -120,7 +154,7 @@ false false JavaVarargs_1.method() -6 +8 varargs/test_1.scala test_1$package @@ -137,7 +171,24 @@ false false JavaVarargs_1.method("") -7 +9 +varargs/test_1.scala + +test_1$package +Object +.test_1$package +Test +202 +204 +13 + +Literal +false +0 +false +"" + +10 varargs/test_1.scala test_1$package @@ -154,7 +205,24 @@ false false JavaVarargs_1.multiple("first") -8 +11 +varargs/test_1.scala + +test_1$package +Object +.test_1$package +Test +240 +247 +15 + +Literal +false +0 +false +"first" + +12 varargs/test_1.scala test_1$package @@ -171,7 +239,7 @@ false false println(m) -9 +13 varargs/test_1.scala test_1$package @@ -188,7 +256,7 @@ false false JavaVarargs_1.multiple(f("first")) -10 +14 varargs/test_1.scala test_1$package @@ -205,7 +273,7 @@ false false f("first") -11 +15 varargs/test_1.scala test_1$package @@ -222,7 +290,7 @@ false false println(m) -12 +16 varargs/test_1.scala test_1$package @@ -239,7 +307,7 @@ false false JavaVarargs_1.multiple(f("first"), "a", "b", "c") -13 +17 varargs/test_1.scala test_1$package @@ -256,7 +324,58 @@ false false f("first") -14 +18 +varargs/test_1.scala + +test_1$package +Object +.test_1$package +Test +357 +360 +19 + +Literal +false +0 +false +"a" + +19 +varargs/test_1.scala + +test_1$package +Object +.test_1$package +Test +362 +365 +19 + +Literal +false +0 +false +"b" + +20 +varargs/test_1.scala + +test_1$package +Object +.test_1$package +Test +367 +370 +19 + +Literal +false +0 +false +"c" + +21 varargs/test_1.scala test_1$package @@ -273,7 +392,7 @@ false false println(m) -15 +22 varargs/test_1.scala test_1$package diff --git a/tests/explicit-nulls/warn/expose-flexible-types.check b/tests/explicit-nulls/warn/expose-flexible-types.check new file mode 100644 index 000000000000..5e0f8519b7e7 --- /dev/null +++ b/tests/explicit-nulls/warn/expose-flexible-types.check @@ -0,0 +1,56 @@ +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:4:6 -------------------------------------------------- +4 | def f = s.trim // warn + | ^ + | method f exposes a flexible type in its inferred result type (String)?. Consider annotating the type explicitly +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:8:6 -------------------------------------------------- +8 | def h = (s.trim, s.length) // warn + | ^ + |method h exposes a flexible type in its inferred result type ((String)?, Int). Consider annotating the type explicitly +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:9:16 ------------------------------------------------- +9 | protected def i = s.trim // warn + | ^ + | method i exposes a flexible type in its inferred result type (String)?. Consider annotating the type explicitly +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:11:19 ------------------------------------------------ +11 | private[foo] def k = s.trim // warn + | ^ + | method k exposes a flexible type in its inferred result type (String)?. Consider annotating the type explicitly +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:12:6 ------------------------------------------------- +12 | val ss = s.replace("a", "A") // warn + | ^ + | value ss exposes a flexible type in its inferred result type (String)?. Consider annotating the type explicitly +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:13:6 ------------------------------------------------- +13 | val ss2 = Seq(s.trim) // warn + | ^ + |value ss2 exposes a flexible type in its inferred result type Seq[(String)?]. Consider annotating the type explicitly +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:21:6 ------------------------------------------------- +21 | def f = s2.trim // warn + | ^ + | method f exposes a flexible type in its inferred result type (String)?. Consider annotating the type explicitly +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:25:6 ------------------------------------------------- +25 | def h = (s2.trim, s2.length) // warn + | ^ + |method h exposes a flexible type in its inferred result type ((String)?, Int). Consider annotating the type explicitly +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:26:6 ------------------------------------------------- +26 | val ss = s2.replace("a", "A") // warn + | ^ + | value ss exposes a flexible type in its inferred result type (String)?. Consider annotating the type explicitly +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:27:6 ------------------------------------------------- +27 | val ss2 = Seq(s2.trim) // warn + | ^ + |value ss2 exposes a flexible type in its inferred result type Seq[(String)?]. Consider annotating the type explicitly +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:32:4 ------------------------------------------------- +32 |def f = s2.trim // warn + | ^ + | method f exposes a flexible type in its inferred result type (String)?. Consider annotating the type explicitly +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:36:4 ------------------------------------------------- +36 |def h = (s2.trim, s2.length) // warn + | ^ + |method h exposes a flexible type in its inferred result type ((String)?, Int). Consider annotating the type explicitly +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:37:4 ------------------------------------------------- +37 |val ss = s2.replace("a", "A") // warn + | ^ + | value ss exposes a flexible type in its inferred result type (String)?. Consider annotating the type explicitly +-- Warning: tests/explicit-nulls/warn/expose-flexible-types.scala:38:4 ------------------------------------------------- +38 |val ss2 = Seq(s2.trim) // warn + | ^ + |value ss2 exposes a flexible type in its inferred result type Seq[(String)?]. Consider annotating the type explicitly diff --git a/tests/explicit-nulls/warn/expose-flexible-types.scala b/tests/explicit-nulls/warn/expose-flexible-types.scala new file mode 100644 index 000000000000..9908e6228cd4 --- /dev/null +++ b/tests/explicit-nulls/warn/expose-flexible-types.scala @@ -0,0 +1,41 @@ +package foo + +class C(s: String): + def f = s.trim // warn + def g: String = + val s2 = s.trim + s2 + def h = (s.trim, s.length) // warn + protected def i = s.trim // warn + private def j = s.trim + private[foo] def k = s.trim // warn + val ss = s.replace("a", "A") // warn + val ss2 = Seq(s.trim) // warn + val ss3: Seq[String] = + val s3 = s.trim + Seq(s3) + +val s2: String = "" + +object O: + def f = s2.trim // warn + def g: String = + val s3 = s2.trim + s3 + def h = (s2.trim, s2.length) // warn + val ss = s2.replace("a", "A") // warn + val ss2 = Seq(s2.trim) // warn + val ss3: Seq[String] = + val s3 = s2.trim + Seq(s3) + +def f = s2.trim // warn +def g: String = + val s3 = s2.trim + s3 +def h = (s2.trim, s2.length) // warn +val ss = s2.replace("a", "A") // warn +val ss2 = Seq(s2.trim) // warn +val ss3: Seq[String] = + val s3 = s2.trim + Seq(s3) \ No newline at end of file diff --git a/tests/explicit-nulls/warn/unnecessary-nn.scala b/tests/explicit-nulls/warn/unnecessary-nn.scala index 0e93b61fb408..82d87e75c0a5 100644 --- a/tests/explicit-nulls/warn/unnecessary-nn.scala +++ b/tests/explicit-nulls/warn/unnecessary-nn.scala @@ -9,7 +9,7 @@ def f6[T >: String|Null](s: String|Null): T = s.nn // warn def f5a[T <: String](s: T): String = s.nn // warn // flexible types -def f7(s: String|Null) = "".concat(s.nn) // warn +def f7(s: String|Null): String = "".concat(s.nn) // warn def f8(s: String): String = s.trim().nn // OK because the .nn could be useful as a dynamic null check diff --git a/tests/neg-custom-args/captures/i23726.check b/tests/neg-custom-args/captures/i23726.check new file mode 100644 index 000000000000..8c8ac94a61e0 --- /dev/null +++ b/tests/neg-custom-args/captures/i23726.check @@ -0,0 +1,51 @@ +-- Error: tests/neg-custom-args/captures/i23726.scala:10:5 ------------------------------------------------------------- +10 | f1(a) // error, as expected + | ^ + |Separation failure: argument of type (a : Ref^) + |to a function of type (x: Ref^) -> List[() ->{a, x} Unit] + |corresponds to capture-polymorphic formal parameter x of type Ref^² + |and hides capabilities {a}. + |Some of these overlap with the captures of the function result with type List[() ->{a} Unit]. + | + | Hidden set of current argument : {a} + | Hidden footprint of current argument : {a} + | Capture set of function result : {a} + | Footprint set of function result : {a} + | The two sets overlap at : {a} + | + |where: ^ refers to a fresh root capability classified as Mutable created in value a when constructing mutable Ref + | ^² refers to a fresh root capability classified as Mutable created in method test1 when checking argument to parameter x of method apply +-- Error: tests/neg-custom-args/captures/i23726.scala:15:5 ------------------------------------------------------------- +15 | f3(b) // error + | ^ + |Separation failure: argument of type (b : Ref^) + |to a function of type (x: Ref^) -> (op: () ->{b} Unit) -> List[() ->{op} Unit] + |corresponds to capture-polymorphic formal parameter x of type Ref^² + |and hides capabilities {b}. + |Some of these overlap with the captures of the function result with type (op: () ->{b} Unit) -> List[() ->{op} Unit]. + | + | Hidden set of current argument : {b} + | Hidden footprint of current argument : {b} + | Capture set of function result : {op} + | Footprint set of function result : {op, b} + | The two sets overlap at : {b} + | + |where: ^ refers to a fresh root capability classified as Mutable created in value b when constructing mutable Ref + | ^² refers to a fresh root capability classified as Mutable created in method test1 when checking argument to parameter x of method apply +-- Error: tests/neg-custom-args/captures/i23726.scala:23:5 ------------------------------------------------------------- +23 | f7(a) // error + | ^ + |Separation failure: argument of type (a : Ref^) + |to a function of type (x: Ref^) ->{a, b} (y: List[Ref^{a, b}]) ->{a, b} Unit + |corresponds to capture-polymorphic formal parameter x of type Ref^² + |and hides capabilities {a}. + |Some of these overlap with the captures of the function prefix. + | + | Hidden set of current argument : {a} + | Hidden footprint of current argument : {a} + | Capture set of function prefix : {f7*} + | Footprint set of function prefix : {f7*, a, b} + | The two sets overlap at : {a} + | + |where: ^ refers to a fresh root capability classified as Mutable created in value a when constructing mutable Ref + | ^² refers to a fresh root capability classified as Mutable created in method test1 when checking argument to parameter x of method apply diff --git a/tests/neg-custom-args/captures/i23726.scala b/tests/neg-custom-args/captures/i23726.scala new file mode 100644 index 000000000000..fc833ef29583 --- /dev/null +++ b/tests/neg-custom-args/captures/i23726.scala @@ -0,0 +1,23 @@ +import language.experimental.captureChecking +import language.experimental.separationChecking +import caps.* +class Ref extends Mutable +def swap(a: Ref^, b: Ref^): Unit = () +def test1(): Unit = + val a = Ref() + val b = Ref() + val f1: (x: Ref^) -> List[() ->{a,x} Unit] = ??? + f1(a) // error, as expected + val f2: (x: Ref^) -> List[() ->{x} Unit] = ??? + f2(a) // ok, as expected + val f3: (x: Ref^) -> (op: () ->{b} Unit) -> List[() ->{op} Unit] = ??? + f3(a) // ok + f3(b) // error + val f4: (x: Ref^) -> (y: Ref^{x}) ->{x} Unit = ??? + f4(a) // ok + val f5: (x: Ref^) -> (y: List[Ref^{a}]) ->{} Unit = ??? + f5(a) // ok + val f6: (x: Ref^) -> (y: List[Ref^{a, b}]) ->{} Unit = ??? + f6(b) // ok + val f7: (x: Ref^) ->{a, b} (y: List[Ref^{a, b}]) ->{a, b} Unit = ??? + f7(a) // error diff --git a/tests/neg-custom-args/captures/match.scala b/tests/neg-custom-args/captures/match.scala new file mode 100644 index 000000000000..cf0c0f1a3377 --- /dev/null +++ b/tests/neg-custom-args/captures/match.scala @@ -0,0 +1,24 @@ +import language.experimental.captureChecking + +trait A + +case class B(x: AnyRef^) extends A + +def test = + val x: AnyRef^ = new AnyRef + val a: A^{x} = B(x) + + val y1: A = a match + case b: B => b // error: (b: B) becomes B^{x} implicitly + + val y2: A^{x} = a match + case b: B => + val bb: B^{b} = b + val aa: A^{a} = bb + b // ok + + val x3: AnyRef = a match + case B(x2: AnyRef) => x2 // error: we lose some information about field x, but it still cannot be pure + + val x4: AnyRef = a match + case b: B => b.x // error diff --git a/tests/neg/context-function-syntax.scala b/tests/neg/context-function-syntax.scala index e411e840d8b5..5df5fd41aeb6 100644 --- a/tests/neg/context-function-syntax.scala +++ b/tests/neg/context-function-syntax.scala @@ -2,7 +2,7 @@ val test = (using x: Int) => x // error // error // error val f = () ?=> 23 // error -val g: ContextFunction0[Int] = ??? // ok +val g: ContextFunction0[Int] = ??? // error at typer for RHS not expanded val h: () ?=> Int = ??? // error object Example3 extends App { diff --git a/tests/neg/i10666.check b/tests/neg/i10666.check index a70aa9815dc5..491b88f1ffa5 100644 --- a/tests/neg/i10666.check +++ b/tests/neg/i10666.check @@ -1,6 +1,6 @@ -- Error: tests/neg/i10666.scala:8:6 ----------------------------------------------------------------------------------- 8 |class Bar extends Foo { // error - | ^ + | ^^^ | class Bar needs to be abstract, since def foo[T <: B](tx: T): Unit in trait Foo is not defined | (Note that | parameter T in def foo[T <: B](tx: T): Unit in trait Foo does not match diff --git a/tests/neg/i123577.check b/tests/neg/i123577.check new file mode 100644 index 000000000000..6d8f1403b819 --- /dev/null +++ b/tests/neg/i123577.check @@ -0,0 +1,7 @@ +-- [E007] Type Mismatch Error: tests/neg/i123577.scala:11:4 ------------------------------------------------------------ +11 | (msg: String) => ??? // error + | ^^^^^^^^^^^^^^^^^^^^ + | Found: String => Nothing + | Required: MillRpcChannel + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i123577.scala b/tests/neg/i123577.scala new file mode 100644 index 000000000000..fe69d3477757 --- /dev/null +++ b/tests/neg/i123577.scala @@ -0,0 +1,13 @@ +trait MillRpcMessage { + type Response +} + +trait MillRpcChannel { + def apply(requestId: Long, input: MillRpcMessage): input.Response +} + +object MillRpcChannel { + def createChannel: MillRpcChannel = { + (msg: String) => ??? // error + } +} diff --git a/tests/neg/i12828.check b/tests/neg/i12828.check index 070633fc35b3..e2a1cdb92dcd 100644 --- a/tests/neg/i12828.check +++ b/tests/neg/i12828.check @@ -1,6 +1,6 @@ -- Error: tests/neg/i12828.scala:7:7 ----------------------------------------------------------------------------------- 7 |object Baz extends Bar[Int] // error: not implemented - | ^ + | ^^^ | object creation impossible, since def foo(x: A): Unit in trait Foo is not defined | (Note that | parameter A in def foo(x: A): Unit in trait Foo does not match diff --git a/tests/neg/i13466.check b/tests/neg/i13466.check index a15ae059427f..ad097ddae96b 100644 --- a/tests/neg/i13466.check +++ b/tests/neg/i13466.check @@ -1,6 +1,6 @@ -- Error: tests/neg/i13466.scala:9:6 ----------------------------------------------------------------------------------- 9 |given none: SomeTrait[Finally] with {} // error - | ^ + | ^^^^ | object creation impossible, since: | it has 3 unimplemented members. | /** As seen from module class none$, the missing signatures are as follows. diff --git a/tests/neg/i17132.min.check b/tests/neg/i17132.min.check new file mode 100644 index 000000000000..f23d3e91549a --- /dev/null +++ b/tests/neg/i17132.min.check @@ -0,0 +1,31 @@ +-- Error: tests/neg/i17132.min.scala:4:7 ------------------------------------------------------------------------------- +4 |class Q[T <: P[Any]] extends P[R[T]] // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Recursion limit exceeded. + | Maybe there is an illegal cyclic reference? + | If that's not the case, you could also try to increase the stacksize using the -Xss JVM option. + | For the unprocessed stack trace, compile with -Xno-enrich-error-messages. + | A recurring operation is (inner to outer): + | + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | ... + | + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type t match ... + | reduce type T match ... diff --git a/tests/neg/i17132.min.scala b/tests/neg/i17132.min.scala new file mode 100644 index 000000000000..903b19e5cfed --- /dev/null +++ b/tests/neg/i17132.min.scala @@ -0,0 +1,9 @@ + +class P[T] +//class Q[T] extends P[R[T]] // ok +class Q[T <: P[Any]] extends P[R[T]] // error +//type Q[T <: P[Any]] <: P[R[T]] // ok + +type R[U] = U match + case Q[t] => R[t] + case P[t] => t diff --git a/tests/neg/i17132.scala b/tests/neg/i17132.scala new file mode 100644 index 000000000000..d4b97e54293a --- /dev/null +++ b/tests/neg/i17132.scala @@ -0,0 +1,11 @@ + +sealed trait Transformation[T] + +case object Count extends Transformation[Int] +case class MultiTransformation[T1 <: Transformation[?], T2 <: Transformation[?]](t1: T1, t2: T2) // error cyclic + extends Transformation[MultiTransformationResult[T1, T2]] + +type MultiTransformationResult[T1 <: Transformation[?], T2 <: Transformation[?]] <: Tuple = (T1, T2) match { + case (Transformation[t], MultiTransformation[t1, t2]) => t *: MultiTransformationResult[t1, t2] + case (Transformation[t1], Transformation[t2]) => (t1, t2) +} diff --git a/tests/neg/i19731.check b/tests/neg/i19731.check index eebfb924d199..5c6ef5246b1d 100644 --- a/tests/neg/i19731.check +++ b/tests/neg/i19731.check @@ -1,10 +1,10 @@ -- Error: tests/neg/i19731.scala:4:6 ----------------------------------------------------------------------------------- 4 |class F1 extends Foo: // error - | ^ + | ^^ | class F1 needs to be abstract, since def foo(): Unit in class F1 is not defined -- Error: tests/neg/i19731.scala:7:6 ----------------------------------------------------------------------------------- 7 |class F2 extends Foo: // error - | ^ + | ^^ | class F2 needs to be abstract, since: | it has 2 unimplemented members. | /** As seen from class F2, the missing signatures are as follows. @@ -14,7 +14,7 @@ | def foo(x: Int): Unit = ??? -- Error: tests/neg/i19731.scala:16:6 ---------------------------------------------------------------------------------- 16 |class B1 extends Bar: // error - | ^ + | ^^ | class B1 needs to be abstract, since: | it has 2 unimplemented members. | /** As seen from class B1, the missing signatures are as follows. diff --git a/tests/neg/i21321.check b/tests/neg/i21321.check new file mode 100644 index 000000000000..88cb76154352 --- /dev/null +++ b/tests/neg/i21321.check @@ -0,0 +1,19 @@ +-- Error: tests/neg/i21321.scala:3:42 ---------------------------------------------------------------------------------- +3 |val v1b: scala.ContextFunction0[String] = () ?=> "x" // error + | ^^ + | context function literals require at least one formal parameter +-- Error: tests/neg/i21321.scala:4:8 ----------------------------------------------------------------------------------- +4 |val v2: () ?=> String = "y" // error // error in parser + | ^^ + | context function types require at least one parameter +-- Error: tests/neg/i21321.scala:2:41 ---------------------------------------------------------------------------------- +2 |val v1: scala.ContextFunction0[String] = "x" // error + | ^^^ + | context function types require at least one parameter +-- [E007] Type Mismatch Error: tests/neg/i21321.scala:4:24 ------------------------------------------------------------- +4 |val v2: () ?=> String = "y" // error // error in parser + | ^^^ + | Found: ("y" : String) + | Required: () => String + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i21321.scala b/tests/neg/i21321.scala new file mode 100644 index 000000000000..a7c60994d351 --- /dev/null +++ b/tests/neg/i21321.scala @@ -0,0 +1,4 @@ + +val v1: scala.ContextFunction0[String] = "x" // error +val v1b: scala.ContextFunction0[String] = () ?=> "x" // error +val v2: () ?=> String = "y" // error // error in parser diff --git a/tests/neg/i21335.check b/tests/neg/i21335.check index a7ee092eec0e..ae2e09df1f61 100644 --- a/tests/neg/i21335.check +++ b/tests/neg/i21335.check @@ -1,8 +1,8 @@ -- Error: tests/neg/i21335.scala:7:6 ----------------------------------------------------------------------------------- 7 |class Z1 extends Bar1 // error - | ^ + | ^^ | class Z1 needs to be abstract, since override def bar(): Bar1 in trait Bar1 is not defined -- Error: tests/neg/i21335.scala:12:6 ---------------------------------------------------------------------------------- 12 |class Z2 extends Bar2 // error - | ^ + | ^^ | class Z2 needs to be abstract, since def bar(): Bar2 in trait Bar2 is not defined diff --git a/tests/neg/i22620.check b/tests/neg/i22620.check new file mode 100644 index 000000000000..51a79d7cabce --- /dev/null +++ b/tests/neg/i22620.check @@ -0,0 +1,21 @@ +-- Error: tests/neg/i22620.scala:4:34 ---------------------------------------------------------------------------------- +4 |class PrivateTest[-M](private val v: ArrayBuffer[M]) // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | contravariant type M occurs in invariant position in type scala.collection.mutable.ArrayBuffer[M] of value v + | + | Implementation limitation: private val parameters cannot be inferred to be local + | and therefore are always variance-checked. + | + | Potential fix: remove the private val modifiers on the parameter v. + | +-- Error: tests/neg/i22620.scala:6:37 ---------------------------------------------------------------------------------- +6 |class PrivateTestMut[-M](private var v: ArrayBuffer[M]) // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | contravariant type M occurs in invariant position in type scala.collection.mutable.ArrayBuffer[M] of variable v + | + | Implementation limitation: private var parameters cannot be inferred to be local + | and therefore are always variance-checked. + | + | Potential fix: remove the private var modifiers on the parameter v and add + | a field inside the class instead. + | diff --git a/tests/neg/i22620.scala b/tests/neg/i22620.scala index 97d1d55e3302..0f06d97f73e0 100644 --- a/tests/neg/i22620.scala +++ b/tests/neg/i22620.scala @@ -2,3 +2,7 @@ import scala.collection.mutable.ArrayBuffer class PrivateTest[-M](private val v: ArrayBuffer[M]) // error + +class PrivateTestMut[-M](private var v: ArrayBuffer[M]) // error + +class PrivateTestParamOnly[-M](v: ArrayBuffer[M]) // no error diff --git a/tests/neg/i22941.check b/tests/neg/i22941.check new file mode 100644 index 000000000000..81edebd098d3 --- /dev/null +++ b/tests/neg/i22941.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i22941.scala:4:6 ----------------------------------------------------------------------------------- +4 |class Baz extends Foo: // error + | ^^^ + | class Baz needs to be abstract, since def bar: String in trait Foo is not defined diff --git a/tests/neg/i22941.scala b/tests/neg/i22941.scala new file mode 100644 index 000000000000..3e8eb39777ab --- /dev/null +++ b/tests/neg/i22941.scala @@ -0,0 +1,5 @@ +trait Foo: + def bar: String + +class Baz extends Foo: // error + val a = "hello" diff --git a/tests/neg/i23119.check b/tests/neg/i23119.check new file mode 100644 index 000000000000..34f9e3c564ae --- /dev/null +++ b/tests/neg/i23119.check @@ -0,0 +1,20 @@ +-- [E161] Naming Error: tests/neg/i23119.scala:8:4 --------------------------------------------------------------------- +8 | given Option[List[Int]] = Some(List(x)) // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | given_Option_List is already defined as given instance given_Option_List + | + | Provide an explicit, unique name to given definitions, + | since the names assigned to anonymous givens may clash. For example: + | + | given myGiven: Option[List[String]] // define an instance + | given myGiven @ Option[List[String]] // as a pattern variable +-- [E161] Naming Error: tests/neg/i23119.scala:18:8 -------------------------------------------------------------------- +18 | given [A] => List[A] = ??? // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | given_List_A is already defined as given instance given_List_A + | + | Provide an explicit, unique name to given definitions, + | since the names assigned to anonymous givens may clash. For example: + | + | given myGiven: [A] => List[A] // define an instance + | given myGiven @ [A] => List[A] // as a pattern variable diff --git a/tests/neg/i23119.scala b/tests/neg/i23119.scala new file mode 100644 index 000000000000..0f882b66dc8d --- /dev/null +++ b/tests/neg/i23119.scala @@ -0,0 +1,19 @@ +//> using options -explain + +@main def test = println: + for x <- 1 to 2 + // works with explicit name + //ols @ given Option[List[String]] = Some(List(x.toString)) + given Option[List[String]] = Some(List(x.toString)) + given Option[List[Int]] = Some(List(x)) // error + yield summon[Option[List[String]]].map(ss => ss.corresponds(given_Option_List.get)((a, b) => a == b.toString)) + +// The naming clash is noticed when defining local values for "packaging": +// given_Option_List is already defined as given instance given_Option_List +// Previously the naming clash was noticed when extracting values in the map or do function: +// duplicate pattern variable: given_Option_List + +def also = + given [A] => List[A] = ??? + given [A] => List[A] = ??? // error + () diff --git a/tests/neg/i23350.check b/tests/neg/i23350.check index d9ae6a99cdca..ac64b3d22c1e 100644 --- a/tests/neg/i23350.check +++ b/tests/neg/i23350.check @@ -31,16 +31,15 @@ | | so the compiler cannot keep both: the generated bytecode symbols would collide. | - | To fix this error, you need to disambiguate the two definitions. You can either: + | To fix this error, you must disambiguate the two definitions by doing one of the following: | - | 1. Rename one of the definitions, or + | 1. Rename one of the definitions. | 2. Keep the same names in source but give one definition a distinct - | bytecode-level name via `@targetName` for example: + | bytecode-level name via `@targetName`; for example: | | @targetName("apply_2") | def apply(a: UndefOr2[String]): Unit | | Choose the `@targetName` argument carefully: it is the name that will be used | when calling the method externally, so it should be unique and descriptive. - | --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i23402.check b/tests/neg/i23402.check index 4a98af863348..b258ab79e75c 100644 --- a/tests/neg/i23402.check +++ b/tests/neg/i23402.check @@ -31,16 +31,15 @@ | | so the compiler cannot keep both: the generated bytecode symbols would collide. | - | To fix this error, you need to disambiguate the two definitions. You can either: + | To fix this error, you must disambiguate the two definitions by doing one of the following: | - | 1. Rename one of the definitions, or + | 1. Rename one of the definitions. | 2. Keep the same names in source but give one definition a distinct - | bytecode-level name via `@targetName` for example: + | bytecode-level name via `@targetName`; for example: | | @targetName("apply_2") | def apply(p1: String)(p2: Int): A | | Choose the `@targetName` argument carefully: it is the name that will be used | when calling the method externally, so it should be unique and descriptive. - | --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i23552.scala b/tests/neg/i23552.scala new file mode 100644 index 000000000000..ea3d0ff7f874 --- /dev/null +++ b/tests/neg/i23552.scala @@ -0,0 +1,32 @@ +import NamedTuple.AnyNamedTuple + +sealed trait Z +sealed trait S[n] + +type TupleList[+A, N] <: AnyNamedTuple = + N match + case Z => NamedTuple.Empty + case S[n] => (head: A, tail: TupleList[A, n]) + +sealed trait Vect[+A, N]: + def ::[A1 >: A](a: A1): Vect[A1, S[N]] = + Vect.Cons(a, this) + + def toTupleList: TupleList[A, N] + +object Vect: + case object Empty extends Vect[Nothing, Z]: + override def toTupleList: TupleList[Nothing, Z] = NamedTuple.Empty + + case class Cons[A, N](head: A, tail: Vect[A, N]) extends Vect[A, S[N]]: + override def toTupleList: TupleList[A, S[N]] = (head, tail.toTupleList) + +object Foo: + def unapply[A, N](as: Vect[A, N]): Some[TupleList[A, N]] = + Some(as.toTupleList) + +@main +def test: Unit = + (1 :: 2 :: 3 :: Vect.Empty) match + // missing parens around named tuple inside Foo causes compiler crash + case Foo(head = h, tail = t) => ??? // error \ No newline at end of file diff --git a/tests/neg/i23637.check b/tests/neg/i23637.check new file mode 100644 index 000000000000..d568c04a31b7 --- /dev/null +++ b/tests/neg/i23637.check @@ -0,0 +1,6 @@ +-- [E083] Type Error: tests/neg/i23637.scala:6:9 ----------------------------------------------------------------------- +6 | export foo.pin.* // error: (because we need reflection to get at foo.pin) + | ^^^^^^^ + | (Test.foo.pin : Object) is not a valid export prefix, since it is not an immutable path + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i23637.scala b/tests/neg/i23637.scala new file mode 100644 index 000000000000..aac728f4fd99 --- /dev/null +++ b/tests/neg/i23637.scala @@ -0,0 +1,12 @@ +trait Foo extends reflect.Selectable +object Test: + val foo = new Foo: + object pin: + val x = 1 + export foo.pin.* // error: (because we need reflection to get at foo.pin) + +object OK: + object Foo: + object pin: + val x = 1 + export Foo.pin.* diff --git a/tests/neg/i23832a.check b/tests/neg/i23832a.check new file mode 100644 index 000000000000..6886327484c3 --- /dev/null +++ b/tests/neg/i23832a.check @@ -0,0 +1,45 @@ +-- [E120] Naming Error: tests/neg/i23832a.scala:9:8 -------------------------------------------------------------------- +9 | given Special[Option[Int]] = ??? // error + | ^ + | Conflicting definitions: + | final lazy given val given_Special_Option: Special[Option[Long]] in object syntax at line 8 and + | final lazy given val given_Special_Option: Special[Option[Int]] in object syntax at line 9 + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | + | As part of the Scala compilation pipeline every type is reduced to its erased + | (runtime) form. In this phase, among other transformations, generic parameters + | disappear and separate parameter-list boundaries are flattened. + | + | For example, both `f[T](x: T)(y: String): Unit` and `f(x: Any, z: String): Unit` + | erase to the same runtime signature `f(x: Object, y: String): Unit`. Note that + | parameter names are irrelevant. + | + | In your code the two declarations + | + | final lazy given val given_Special_Option: Special[Option[Long]] + | final lazy given val given_Special_Option: Special[Option[Int]] + | + | erase to the identical signature + | + | Special + | + | so the compiler cannot keep both: the generated bytecode symbols would collide. + | + | To fix this error, you must disambiguate the two definitions by doing one of the following: + | + | 1. Rename one of the definitions. Provide an explicit, unique name to given definitions, + | since the names assigned to anonymous givens may clash. For example: + | + | given myGiven: Special[Option[Int]] + | + | 2. Keep the same names in source but give one definition a distinct + | bytecode-level name via `@targetName`; for example: + | + | @targetName("given_Special_Option_2") + | final lazy given val given_Special_Option: Special[Option[Int]] + | + | Choose the `@targetName` argument carefully: it is the name that will be used + | when calling the method externally, so it should be unique and descriptive. + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i23832a.scala b/tests/neg/i23832a.scala new file mode 100644 index 000000000000..5020c998ee96 --- /dev/null +++ b/tests/neg/i23832a.scala @@ -0,0 +1,9 @@ +//> using options -explain + +// follow-up to neg/i23402*.scala + +trait Special[A] + +object syntax: + given Special[Option[Long]] = ??? + given Special[Option[Int]] = ??? // error diff --git a/tests/neg/i23832b.check b/tests/neg/i23832b.check new file mode 100644 index 000000000000..82cb54044449 --- /dev/null +++ b/tests/neg/i23832b.check @@ -0,0 +1,45 @@ +-- [E120] Naming Error: tests/neg/i23832b.scala:9:8 -------------------------------------------------------------------- +9 | given [A] => Special[Option[A]] = ??? // error + | ^ + | Conflicting definitions: + | final lazy given val given_Special_Option: Special[Option[Long]] in object syntax at line 8 and + | final given def given_Special_Option[A]: Special[Option[A]] in object syntax at line 9 + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | + | As part of the Scala compilation pipeline every type is reduced to its erased + | (runtime) form. In this phase, among other transformations, generic parameters + | disappear and separate parameter-list boundaries are flattened. + | + | For example, both `f[T](x: T)(y: String): Unit` and `f(x: Any, z: String): Unit` + | erase to the same runtime signature `f(x: Object, y: String): Unit`. Note that + | parameter names are irrelevant. + | + | In your code the two declarations + | + | final lazy given val given_Special_Option: Special[Option[Long]] + | final given def given_Special_Option[A]: Special[Option[A]] + | + | erase to the identical signature + | + | (): Special + | + | so the compiler cannot keep both: the generated bytecode symbols would collide. + | + | To fix this error, you must disambiguate the two definitions by doing one of the following: + | + | 1. Rename one of the definitions. Provide an explicit, unique name to given definitions, + | since the names assigned to anonymous givens may clash. For example: + | + | given myGiven: [A] => Special[Option[A]] + | + | 2. Keep the same names in source but give one definition a distinct + | bytecode-level name via `@targetName`; for example: + | + | @targetName("given_Special_Option_2") + | final given def given_Special_Option[A]: Special[Option[A]] + | + | Choose the `@targetName` argument carefully: it is the name that will be used + | when calling the method externally, so it should be unique and descriptive. + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i23832b.scala b/tests/neg/i23832b.scala new file mode 100644 index 000000000000..6e43ed008047 --- /dev/null +++ b/tests/neg/i23832b.scala @@ -0,0 +1,9 @@ +//> using options -explain + +// follow-up to neg/i23402*.scala + +trait Special[A] + +object syntax: + given Special[Option[Long]] = ??? + given [A] => Special[Option[A]] = ??? // error diff --git a/tests/neg/i9329.check b/tests/neg/i9329.check index 7e4968edf607..e604a1b22888 100644 --- a/tests/neg/i9329.check +++ b/tests/neg/i9329.check @@ -1,5 +1,5 @@ -- Error: tests/neg/i9329.scala:8:6 ------------------------------------------------------------------------------------ 8 |class GrandSon extends Son // error - | ^ + | ^^^^^^^^ |class GrandSon needs to be abstract, since def name: String in trait Parent is not defined |(The class implements abstract override def name: String in trait Son but that definition still needs an implementation) diff --git a/tests/neg/parser-stability-11.scala b/tests/neg/parser-stability-11.scala index 582bcfb88117..b2ea61e594de 100644 --- a/tests/neg/parser-stability-11.scala +++ b/tests/neg/parser-stability-11.scala @@ -3,4 +3,4 @@ case class x0 // error // error } package x0 class x0 // error -// error \ No newline at end of file +// error diff --git a/tests/neg/targetName-override.check b/tests/neg/targetName-override.check index 2d21e8cbfbd4..230b7fe77745 100644 --- a/tests/neg/targetName-override.check +++ b/tests/neg/targetName-override.check @@ -15,5 +15,5 @@ | method ++ of type (xs: Alpha[String]): Alpha[String] misses a target name annotation @targetName(append) -- Error: tests/neg/targetName-override.scala:14:6 --------------------------------------------------------------------- 14 |class Beta extends Alpha[String] { // error: needs to be abstract - | ^ + | ^^^^ |class Beta needs to be abstract, since there is a deferred declaration of method foo in class Alpha of type (x: String): String which is not implemented in a subclass diff --git a/tests/pos-custom-args/captures/i23737.scala b/tests/pos-custom-args/captures/i23737.scala new file mode 100644 index 000000000000..3d2bb4f6791b --- /dev/null +++ b/tests/pos-custom-args/captures/i23737.scala @@ -0,0 +1,14 @@ +import language.experimental.captureChecking + +class C + +trait A[T] + +trait B[CC^] extends A[C^{CC}] // error: CC not found + +trait D[CC^]: + val x: Object^{CC} = ??? + +def f(c: C^) = + val b = new B[{c}] {} + val a: A[C^{c}] = b \ No newline at end of file diff --git a/tests/pos-custom-args/captures/tuple-ops-2.scala b/tests/pos-custom-args/captures/tuple-ops-2.scala new file mode 100644 index 000000000000..322a52feaaab --- /dev/null +++ b/tests/pos-custom-args/captures/tuple-ops-2.scala @@ -0,0 +1,19 @@ +sealed trait Tup +case object Emp extends Tup +type Emp = Emp.type +case class Cons[h, t <: Tup](hh: h, tt: t) extends Tup + +type Union[T <: Tup] = T match + case Emp => Nothing + case Cons[h, t] => h | Union[t] + +type Concat[T <: Tup, U <: Tup] <: Tup = T match + case Emp => U + case Cons[h, t] => Cons[h, Concat[t, U]] + +type FlatMap[T <: Tup, F[_ <: Union[T]] <: Tup] <: Tup = T match + case Emp => Emp + case Cons[h, t] => Concat[F[h], FlatMap[t, F]] + +type A = + FlatMap[Cons[Boolean, Cons[String, Emp]], [T] =>> Cons[T, Cons[List[T], Emp]]] \ No newline at end of file diff --git a/tests/pos-java16+/java-records/FromScala.scala b/tests/pos-java16+/java-records/FromScala.scala index 8654f88f1835..14f54b6d3868 100644 --- a/tests/pos-java16+/java-records/FromScala.scala +++ b/tests/pos-java16+/java-records/FromScala.scala @@ -49,3 +49,7 @@ object C: def useR4: Unit = val r4 = R4(1) val i: Int = r4.t + + def useR5: Unit = + val r5 = R5("hi", 1, 2, 3) + val xs: Array[Int] = r5.values diff --git a/tests/pos-java16+/java-records/R5.java b/tests/pos-java16+/java-records/R5.java new file mode 100644 index 000000000000..ee63bda40198 --- /dev/null +++ b/tests/pos-java16+/java-records/R5.java @@ -0,0 +1 @@ +public record R5(String s, int... values) {} diff --git a/tests/pos/i20225.scala b/tests/pos/i20225.scala new file mode 100644 index 000000000000..3704aa2034b5 --- /dev/null +++ b/tests/pos/i20225.scala @@ -0,0 +1,12 @@ +sealed abstract class Parent +class A extends Parent +class B extends Parent + +inline def matchAs[T <: Parent](p: Parent): Unit = p match + case _: T => () + case _ => () + +object Test: + def main(args: Array[String]): Unit = + matchAs[A](new B) + diff --git a/tests/pos/i20395.scala b/tests/pos/i20395.scala new file mode 100644 index 000000000000..3b0be1e31b5a --- /dev/null +++ b/tests/pos/i20395.scala @@ -0,0 +1,13 @@ +sealed trait NodeId +case object Hi extends NodeId +case object Hello extends NodeId + +extension (value: NodeId) + inline def parse[T <: NodeId] = value match + case _: T => () + case _ => () + +object Test: + def main(args: Array[String]): Unit = + Hi.parse[Hello.type] + diff --git a/tests/pos/i21951.scala b/tests/pos/i21951.scala new file mode 100644 index 000000000000..5b2b5b932fab --- /dev/null +++ b/tests/pos/i21951.scala @@ -0,0 +1,33 @@ +class A +object A: + given g[F[_]]: F[A] = ??? + +object Test: + summon[List[A]] // ok + def foo[F[_]] = + summon[F[A]] // error + +final case class X(val i: Int) +object X { + implicit final class XOps[F[_]](xs: F[X]) { + def unpack(implicit ev: F[X] <:< Iterable[X]): Iterable[Int] = xs.map(_.i) + } +} + +object App extends App { + // good + val ys: List[X] = List(X(1)) + println(ys.unpack) + + // bad + def printPolymorphic[F[_]](xs: F[X])(implicit ev: F[X] <:< Iterable[X]) = { + locally { + // implicit XOps is correct + import X.XOps + println(xs.unpack) // found + } + // but it's not being searched for in the companion object of X + println(xs.unpack) // error: unpack is not a member of F[X] + } + printPolymorphic[List](ys) +} \ No newline at end of file diff --git a/tests/pos/i21951b.scala b/tests/pos/i21951b.scala new file mode 100644 index 000000000000..30068b89d994 --- /dev/null +++ b/tests/pos/i21951b.scala @@ -0,0 +1,12 @@ + +class A +object A: + given A = ??? + +class B[X] +object B: + given g[T]: B[T] = ??? + +object Test: + def foo[X >: A] = summon[X] // was error + def bar[F[T] >: B[T]] = summon[F[Int]] // was error diff --git a/tests/pos/i23119.scala b/tests/pos/i23119.scala new file mode 100644 index 000000000000..cd8026005447 --- /dev/null +++ b/tests/pos/i23119.scala @@ -0,0 +1,29 @@ +//> using options -Wunused:patvars -Werror + +def make: IndexedSeq[FalsePositive] = + for { + i <- 1 to 2 + given Int = i + fp = FalsePositive() + } yield fp + +def broken = + for + i <- List(42) + (x, y) = "hello" -> "world" + yield + s"$x, $y" * i + +def alt: IndexedSeq[FalsePositive] = + given String = "hi" + for + given Int <- 1 to 2 + j: Int = summon[Int] // simple assign because irrefutable + _ = j + 1 + k :: Nil = j :: Nil : @unchecked // pattern in one var + fp = FalsePositive(using k) + yield fp + +class FalsePositive(using Int): + def usage(): Unit = + println(summon[Int]) diff --git a/tests/pos/i23734.scala b/tests/pos/i23734.scala new file mode 100644 index 000000000000..308bfdae3fa9 --- /dev/null +++ b/tests/pos/i23734.scala @@ -0,0 +1,18 @@ +trait Nodes1 { + sealed trait B + final case class R1() extends B +} + +trait Nodes2 extends Nodes1 { + final case class R2[T]() extends B +} + + +object Impl1 extends Nodes1 + +object test2 { + val a: Impl1.B = ??? + a match { + case Impl1.R1() => ??? + } +} diff --git a/tests/pos/vec-access-syntax.scala b/tests/pos/vec-access-syntax.scala new file mode 100644 index 000000000000..524ede685529 --- /dev/null +++ b/tests/pos/vec-access-syntax.scala @@ -0,0 +1,23 @@ +import scala.language.postfixOps + +class Vector(values: Int*) { + val data = values.toArray + class Getter(i: Int) { + def `>_=`(x: Int) = + data(i) = x + def > : Int = + data(i) + } + def < (i:Int) = new Getter(i) + override def toString = data.mkString("<", ", ", ">") +} + +object Test { + def main(args: Array[String]): Unit = { + val v = new Vector(1, 2, 3) + println(v) // prints <1, 2, 3> + v<1> = 10 // assign 10 to element at index 1 + println(v) // prints <1, 10, 3> + println(v<1>) // prints: value at 1 is 10 + } +} diff --git a/tests/rewrites/i24213.check b/tests/rewrites/i24213.check new file mode 100644 index 000000000000..d06d2f63c7c2 --- /dev/null +++ b/tests/rewrites/i24213.check @@ -0,0 +1,5 @@ +def Test = + try () + catch { + case x: Throwable if x.getMessage `contains` "error" => ??? + } \ No newline at end of file diff --git a/tests/rewrites/i24213.scala b/tests/rewrites/i24213.scala new file mode 100644 index 000000000000..49148abace00 --- /dev/null +++ b/tests/rewrites/i24213.scala @@ -0,0 +1,5 @@ +def Test = + try () + catch { + case x: Throwable if x.getMessage contains "error" => ??? + } \ No newline at end of file diff --git a/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala b/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala index ffb68c8d0d60..ea0bdc240e0c 100644 --- a/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala +++ b/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala @@ -457,13 +457,12 @@ object CollectionStrawMan5 { def apply[A](underlying: Iterable[A]^, pp: A => Boolean, isFlipped: Boolean): Filter[A]^{underlying, pp} = underlying match case filter: Filter[A] => - new Filter(filter.underlying, a => filter.p(a) && pp(a)) - .asInstanceOf[Filter[A]^{underlying, pp}] - //unsafeAssumeSeparate: + unsafeAssumeSeparate: + new Filter(filter.underlying, a => filter.p(a) && pp(a)) + .asInstanceOf[Filter[A]^{underlying, pp}] // See filter-iterable.scala for a test where a variant of Filter // works without the unsafeAssumeSeparate. But it requires significant // changes compared to the version here. - //new Filter(filter.underlying, a => filter.p(a) && pp(a)) case _ => new Filter(underlying, pp) case class Partition[A](val underlying: Iterable[A]^, p: A => Boolean) { diff --git a/tests/run/i20225.check b/tests/run/i20225.check new file mode 100644 index 000000000000..0f06315755de --- /dev/null +++ b/tests/run/i20225.check @@ -0,0 +1 @@ +unreachable case reached diff --git a/tests/run/i20225.scala b/tests/run/i20225.scala new file mode 100644 index 000000000000..ad9babf832f7 --- /dev/null +++ b/tests/run/i20225.scala @@ -0,0 +1,12 @@ +sealed abstract class Parent +class A extends Parent +class B extends Parent + +inline def matchAs[T <: Parent](p: Parent): Unit = p match + case _: T => () + case _ => println("unreachable case reached") + +object Test: + def main(args: Array[String]): Unit = + matchAs[A](new B) + diff --git a/tests/run/i20395.check b/tests/run/i20395.check new file mode 100644 index 000000000000..60653bdaa7f2 --- /dev/null +++ b/tests/run/i20395.check @@ -0,0 +1 @@ +not match diff --git a/tests/run/i20395.scala b/tests/run/i20395.scala new file mode 100644 index 000000000000..e432ea4bca6b --- /dev/null +++ b/tests/run/i20395.scala @@ -0,0 +1,13 @@ +sealed trait NodeId +case object Hi extends NodeId +case object Hello extends NodeId + +extension (value: NodeId) + inline def parse[T <: NodeId]: Unit = value match + case _: T => println("match") + case _ => println("not match") + +object Test: + def main(args: Array[String]): Unit = + Hi.parse[Hello.type] + diff --git a/tests/run/i23440.scala b/tests/run/i23440.scala new file mode 100644 index 000000000000..58885bd3bf05 --- /dev/null +++ b/tests/run/i23440.scala @@ -0,0 +1,12 @@ +@main def Test: Unit = + List((42, "asdads")) match + case List((a, b)) => 1 + List((a = 42, b = "asdads")) match + case List((a, b)) => 1 + val tuple_list = List((42, "asdads")) + tuple_list.map((a, b) => println(s"$a $b")) + val named_tuple_list = List((a = 42, b = "asdads")) + named_tuple_list.foreach((a, b) => println(s"$a $b")) + named_tuple_list.foreach { case (a, b) => println(s"$a $b") } + val l = Seq.empty[(name: String, age: Int)] + l.map((name, i) => name + i) \ No newline at end of file diff --git a/tests/run/i23693.scala b/tests/run/i23693.scala new file mode 100644 index 000000000000..c77959d99263 --- /dev/null +++ b/tests/run/i23693.scala @@ -0,0 +1,22 @@ +//> using options -Wtostring-interpolated + +// verify ~warning messages and~ runtime result +// never mind, the test rig doesn't log diagnostics! unlike beloved partest. + +// Sadly, junit is not available. +//import org.junit.Assert.assertEquals as jassert + +def assertEquals(expected: String)(actual: String): Unit = assert(expected == actual) + +case class K(i: Int) + +@main def Test = + val k = K(42) + assertEquals("k == K(42)"): + s"k == $k" + assertEquals("\\k == \\K(42)"): + raw"\k == \$k" + assertEquals("k == K(42)"): + f"k == $k" + assertEquals("k == K(42)"): + f"k == $k%s" diff --git a/tests/run/i23776.check b/tests/run/i23776.check new file mode 100644 index 000000000000..f2b2a017d25a --- /dev/null +++ b/tests/run/i23776.check @@ -0,0 +1 @@ +a = 0, b = 1, c = 2 diff --git a/tests/run/i23776.scala b/tests/run/i23776.scala new file mode 100644 index 000000000000..9d4a98144f2e --- /dev/null +++ b/tests/run/i23776.scala @@ -0,0 +1,7 @@ +inline def f(t0: Int, t1: Int, t2: Int) = { + inline (t0, t1, t2) match { + case (a: Int, b: Int, c: Int) => println(s"a = $a, b = $b, c = $c") + } +} + +@main def Test = f(0, 1, 2) diff --git a/tests/run/i23875.check b/tests/run/i23875.check new file mode 100644 index 000000000000..36bc6136b8bf --- /dev/null +++ b/tests/run/i23875.check @@ -0,0 +1,3 @@ +true +false +false diff --git a/tests/run/i23875.scala b/tests/run/i23875.scala new file mode 100644 index 000000000000..6993fb39f50b --- /dev/null +++ b/tests/run/i23875.scala @@ -0,0 +1,11 @@ +object Foo { + override def equals(that : Any) = that match { + case _: this.type => true + case _ => false + } +} + +@main def Test = + println(Foo.equals(Foo)) + println(Foo.equals(new AnyRef {})) + println(Foo.equals(0)) diff --git a/tests/run/i23901.scala b/tests/run/i23901.scala new file mode 100644 index 000000000000..7bfcfff23551 --- /dev/null +++ b/tests/run/i23901.scala @@ -0,0 +1,8 @@ +import scala.reflect.ClassTag + +object MyArray: + def empty[T: ClassTag]: Array[Array[T]] = new Array[Array[T]](0) + +@main def Test = + val arr: Array[Array[String]] = MyArray.empty[String] + assert(arr.length == 0) diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 1b303fa563db..13f68886dd87 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -519,6 +519,7 @@ Text => empty Language => Scala Symbols => 22 entries Occurrences => 17 entries +Synthetics => 2 entries Symbols: caseclass/CaseClass# => case class CaseClass extends Object with Product with Serializable { self: CaseClass => +8 decls } @@ -563,6 +564,10 @@ Occurrences: [6:15..6:24): CaseClass -> caseclass/CaseClass# [6:27..6:36): CaseClass -> caseclass/CaseClass. +Synthetics: +[5:35..5:52):CaseClass(int, 0) => apply(*) +[6:27..6:42):CaseClass(0, 0) => apply(*) + expect/Classes.scala -------------------- @@ -574,7 +579,7 @@ Language => Scala Symbols => 108 entries Occurrences => 127 entries Diagnostics => 11 entries -Synthetics => 2 entries +Synthetics => 3 entries Symbols: classes/C1# => final class C1 extends AnyVal { self: C1 => +2 decls } @@ -837,6 +842,7 @@ This construct can be rewritten automatically under -rewrite -source 3.4-migrati Synthetics: [51:16..51:27):List(1).map => *[Int] [51:16..51:20):List => *.apply[Int] +[51:16..51:23):List(1) => List.apply[Int](*) expect/Deprecated.scala ----------------------- @@ -926,6 +932,7 @@ Language => Scala Symbols => 30 entries Occurrences => 49 entries Diagnostics => 3 entries +Synthetics => 2 entries Symbols: endmarkers/Container# => class Container extends Object { self: Container => +5 decls } @@ -1015,6 +1022,10 @@ Diagnostics: [42:8..42:16): [warning] unused local definition [46:8..46:16): [warning] unused local definition +Synthetics: +[23:6..23:13):(1,2,3) => Tuple3.apply[Int, Int, Int](*) +[27:6..27:13):(4,5,6) => Tuple3.apply[Int, Int, Int](*) + expect/EndMarkers2.scala ------------------------ @@ -1097,7 +1108,7 @@ Language => Scala Symbols => 181 entries Occurrences => 159 entries Diagnostics => 1 entries -Synthetics => 6 entries +Synthetics => 9 entries Symbols: _empty_/Enums. => final object Enums extends Object { self: Enums.type => +30 decls } @@ -1447,11 +1458,14 @@ Diagnostics: [30:12..30:17): [warning] unused explicit parameter Synthetics: +[49:27..49:33):Refl() => Refl.apply[T](*) [52:9..52:13):Refl => *.unapply[Option[B]] [52:31..52:50):identity[Option[B]] => *[Function1[A, Option[B]]] [54:14..54:18):Some => *.apply[Some[Int]] +[54:14..54:27):Some(Some(1)) => Some.apply[Some[Int]](*) [54:14..54:34):Some(Some(1)).unwrap => *(given_<:<_T_T[Option[Int]]) [54:19..54:23):Some => *.apply[Int] +[54:19..54:26):Some(1) => Some.apply[Int](*) [54:28..54:34):unwrap => *[Some[Int], Int] expect/EtaExpansion.scala @@ -1464,7 +1478,7 @@ Text => empty Language => Scala Symbols => 3 entries Occurrences => 9 entries -Synthetics => 5 entries +Synthetics => 7 entries Symbols: example/EtaExpansion# => class EtaExpansion extends Object { self: EtaExpansion => +1 decls } @@ -1485,9 +1499,11 @@ Occurrences: Synthetics: [3:2..3:13):Some(1).map => *[Int] [3:2..3:6):Some => *.apply[Int] +[3:2..3:9):Some(1) => Some.apply[Int](*) [3:14..3:22):identity => *[Int] [4:2..4:18):List(1).foldLeft => *[String] [4:2..4:6):List => *.apply[Int] +[4:2..4:9):List(1) => List.apply[Int](*) expect/Example.scala -------------------- @@ -1500,6 +1516,7 @@ Language => Scala Symbols => 5 entries Occurrences => 23 entries Diagnostics => 1 entries +Synthetics => 1 entries Symbols: example/Example. => final object Example extends Object { self: Example.type => +3 decls } @@ -1536,6 +1553,9 @@ Occurrences: Diagnostics: [2:24..2:30): [warning] unused import +Synthetics: +[9:37..9:37): => ClassTag.apply[Int](*) + expect/Extension.scala ---------------------- @@ -1546,7 +1566,7 @@ Text => empty Language => Scala Symbols => 32 entries Occurrences => 66 entries -Synthetics => 1 entries +Synthetics => 2 entries Symbols: ext/DeckUsage. => final object DeckUsage extends Object { self: DeckUsage.type => +2 decls } @@ -1651,6 +1671,7 @@ Occurrences: [26:7..26:14): fooSize -> ext/Extension$package.Deck.fooSize(). Synthetics: +[4:36..4:42):(s, i) => Tuple2.apply[String, Int](*) [14:46..14:61):summon[Read[T]] => *(x$2) expect/ForComprehension.scala @@ -1663,7 +1684,7 @@ Text => empty Language => Scala Symbols => 13 entries Occurrences => 53 entries -Synthetics => 6 entries +Synthetics => 23 entries Symbols: example/ForComprehension# => class ForComprehension extends Object { self: ForComprehension => +1 decls } @@ -1737,11 +1758,50 @@ Occurrences: Synthetics: [4:9..4:13):List => *.apply[Int] +[4:9..4:16):List(1) => List.apply[Int](*) +[5:4..7:5):b <- List(1) + if b > 1 + c => Tuple2.apply[Int, Int](*) [5:9..5:13):List => *.apply[Int] +[5:9..5:16):List(1) => List.apply[Int](*) +[8:10..8:19):(a, b, c) => Tuple3.apply[Int, Int, Int](*) [10:9..10:13):List => *.apply[Int] +[10:9..10:16):List(1) => List.apply[Int](*) [11:9..11:13):List => *.apply[Int] +[11:9..11:16):List(a) => List.apply[Int](*) +[12:7..15:5):( + a, + b + ) => Tuple2.apply[Int, Int](*) +[15:9..15:15):(1, 2) => Tuple2.apply[Int, Int](*) [19:9..19:13):List => *.apply[Tuple2[Int, Int]] +[19:9..19:21):List((a, b)) => List.apply[Tuple2[Int, Int]](*) +[19:14..19:20):(a, b) => Tuple2.apply[Int, Int](*) +[20:7..25:5):( + a, + b, + c, + d + ) => Tuple4.apply[Int, Int, Int, Int](*) +[25:9..25:21):(1, 2, 3, 4) => Tuple4.apply[Int, Int, Int, Int](*) +[26:4..26:5):e => Tuple2.apply[Tuple2[Int, Int], Tuple4[Int, Int, Int, Int]](*) +[26:8..31:5):( + a, + b, + c, + d + ) => Tuple4.apply[Int, Int, Int, Int](*) +[32:12..32:24):(1, 2, 3, 4) => Tuple4.apply[Int, Int, Int, Int](*) [33:9..33:13):List => *.apply[Tuple4[Int, Int, Int, Int]] +[33:9..33:16):List(e) => List.apply[Tuple4[Int, Int, Int, Int]](*) +[35:4..42:5):( + a, + b, + c, + d, + e, + f + ) => Tuple6.apply[Int, Int, Int, Int, Tuple4[Int, Int, Int, Int], Tuple4[Int, Int, Int, Int]](*) expect/Givens.scala ------------------- @@ -1753,7 +1813,7 @@ Text => empty Language => Scala Symbols => 33 entries Occurrences => 72 entries -Synthetics => 3 entries +Synthetics => 6 entries Symbols: a/b/Givens. => final object Givens extends Object { self: Givens.type => +13 decls } @@ -1865,6 +1925,9 @@ Occurrences: [27:59..27:64): empty -> a/b/Givens.Monoid#empty(). Synthetics: +[6:21..6:37):Hello, I am $any => apply(*) +[9:23..9:41):Goodbye, from $any => apply(*) +[10:22..10:40):So Long, from $any => apply(*) [12:17..12:25):sayHello => *[Int] [13:19..13:29):sayGoodbye => *[Int] [14:18..14:27):saySoLong => *[Int] @@ -1879,7 +1942,7 @@ Text => empty Language => Scala Symbols => 23 entries Occurrences => 52 entries -Synthetics => 6 entries +Synthetics => 9 entries Symbols: example/ImplicitConversion# => class ImplicitConversion extends Object { self: ImplicitConversion => +9 decls } @@ -1961,12 +2024,17 @@ Occurrences: [34:58..34:63): other -> example/ImplicitConversion.newAny2stringadd#`+`().(other) Synthetics: +[11:14..11:20):(1, 2) => Tuple2.apply[Int, Int](*) [15:2..15:9):message => augmentString(*) [17:2..17:7):tuple => newAny2stringadd[Tuple2[Int, Int]](*) [20:15..20:22):message => string2Number(*) +[23:4..23:26):Hello $message $number => apply(*) [24:2..26:16):s"""Hello |$message |$number""" => augmentString(*) +[24:6..26:13):Hello + |$message + |$number => apply(*) [28:15..28:19):char => char2int(*) [29:16..29:20):char => char2long(*) @@ -2017,7 +2085,7 @@ Text => empty Language => Scala Symbols => 7 entries Occurrences => 23 entries -Synthetics => 6 entries +Synthetics => 7 entries Symbols: _empty_/InfoMacro. => final object InfoMacro extends Object { self: InfoMacro.type => +3 decls } @@ -2057,6 +2125,7 @@ Synthetics: [3:48..3:69):reportInfoMacro('msg) => *(contextual$1) [3:64..3:68):'msg => orig()(contextual$1) [6:11..6:17):quotes => *(x$2) +[9:18..9:54):Info from macro: ${msg.valueOrAbort} => apply(*) [9:37..9:53):msg.valueOrAbort => *(StringFromExpr[String]) [9:41..9:53):valueOrAbort => *[String] [11:4..11:11):'{ () } => orig(())(x$2) @@ -2100,7 +2169,7 @@ Text => empty Language => Scala Symbols => 8 entries Occurrences => 53 entries -Synthetics => 2 entries +Synthetics => 4 entries Symbols: example/InstrumentTyper# => class InstrumentTyper extends Object { self: AnyRef & InstrumentTyper => +5 decls } @@ -2169,7 +2238,22 @@ Occurrences: Synthetics: [8:12..8:16):List => *.apply[Char | String | LinkOption | Int | Long | Class[Option[Int]] | Float | Double | Boolean | Unit | List[Nothing]] +[8:12..21:3):List( + Literal.int, + Literal.long, + Literal.float, + Literal.double, + Literal.nil, + Literal.char, + Literal.string, + Literal.bool, + Literal.unit, + Literal.javaEnum, + Literal.clazzOf, + List() + ) => List.apply[Char | String | LinkOption | Int | Long | Class[Option[Int]] | Float | Double | Boolean | Unit | List[Nothing]](*) [20:4..20:8):List => *.apply[Nothing] +[20:4..20:10):List() => List.apply[Nothing](*) expect/InventedNames.scala -------------------------- @@ -2317,7 +2401,7 @@ Text => empty Language => Scala Symbols => 7 entries Occurrences => 24 entries -Synthetics => 3 entries +Synthetics => 5 entries Symbols: example/Issue1749# => class Issue1749 extends Object { self: Issue1749 => +3 decls } @@ -2356,8 +2440,10 @@ Occurrences: Synthetics: [8:2..8:10):(x1, x1) => orderingToOrdered[Tuple2[Int, Int]](*) +[8:2..8:10):(x1, x1) => Tuple2.apply[Int, Int](*) [8:2..8:10):(x1, x1) => *(Tuple2(Int, Int)) [8:10..8:10): => *(Int, Int) +[9:13..9:21):(x2, x2) => Tuple2.apply[Int, Int](*) expect/JavaStaticVar.scala -------------------------- @@ -2432,7 +2518,7 @@ Text => empty Language => Scala Symbols => 3 entries Occurrences => 6 entries -Synthetics => 1 entries +Synthetics => 2 entries Symbols: local0 => val local x: Int @@ -2449,6 +2535,7 @@ Occurrences: Synthetics: [5:4..5:8):List => *.apply[Int] +[5:4..5:11):List(x) => List.apply[Int](*) expect/MatchType.scala ---------------------- @@ -2960,6 +3047,7 @@ Text => empty Language => Scala Symbols => 41 entries Occurrences => 41 entries +Synthetics => 1 entries Symbols: example/NamedApplyBlockCaseClassConstruction. => final object NamedApplyBlockCaseClassConstruction extends Object { self: NamedApplyBlockCaseClassConstruction.type => +6 decls } @@ -3047,6 +3135,9 @@ Occurrences: [12:16..12:24): bodyText -> example/NamedApplyBlockCaseClassConstruction.bodyText. [12:26..12:30): tail -> example/NamedApplyBlockCaseClassConstruction.Msg.apply().(tail) +Synthetics: +[12:12..12:40):Msg(bodyText, tail = "tail") => apply(*) + expect/NamedArguments.scala --------------------------- @@ -3058,6 +3149,7 @@ Language => Scala Symbols => 16 entries Occurrences => 12 entries Diagnostics => 2 entries +Synthetics => 1 entries Symbols: example/NamedArguments# => class NamedArguments extends Object { self: NamedArguments => +4 decls } @@ -3095,6 +3187,9 @@ Diagnostics: [4:2..4:21): [warning] A pure expression does nothing in statement position [5:2..5:27): [warning] A pure expression does nothing in statement position +Synthetics: +[4:2..4:21):User(name = "John") => apply(*) + expect/NewModifiers.scala ------------------------- @@ -3287,7 +3382,7 @@ Language => Scala Symbols => 68 entries Occurrences => 115 entries Diagnostics => 1 entries -Synthetics => 3 entries +Synthetics => 4 entries Symbols: example/C# => class C extends Object { self: C => +3 decls } @@ -3483,6 +3578,7 @@ This construct can be rewritten automatically under -rewrite -source 3.4-migrati Synthetics: [15:23..15:34):elems.toMap => *[String, Any] [15:23..15:34):elems.toMap => *(refl[Tuple2[String, Any]]) +[16:41..16:53):fields(name) => apply(*) [32:47..32:56):s.pickOne => *[String] expect/RightAssociativeExtension.scala @@ -3495,6 +3591,7 @@ Text => empty Language => Scala Symbols => 5 entries Occurrences => 12 entries +Synthetics => 1 entries Symbols: ext/RightAssociativeExtension$package. => final package object ext extends Object { self: ext.type => +3 decls } @@ -3517,6 +3614,9 @@ Occurrences: [5:4..5:5): b <- ext/RightAssociativeExtension$package.b. [5:14..5:17): :*: -> ext/RightAssociativeExtension$package.`:*:`(). +Synthetics: +[3:36..3:42):(s, i) => Tuple2.apply[String, Int](*) + expect/Selfs.scala ------------------ @@ -3583,7 +3683,7 @@ Language => Scala Symbols => 20 entries Occurrences => 31 entries Diagnostics => 14 entries -Synthetics => 1 entries +Synthetics => 2 entries Symbols: example/Shadow# => class Shadow extends Object { self: Shadow => +5 decls } @@ -3658,6 +3758,7 @@ Diagnostics: Synthetics: [16:16..16:20):List => *.apply[Int] +[16:16..16:27):List(1,2,3) => List.apply[Int](*) expect/StructuralTypes.scala ---------------------------- @@ -3743,7 +3844,7 @@ Language => Scala Symbols => 62 entries Occurrences => 165 entries Diagnostics => 4 entries -Synthetics => 39 entries +Synthetics => 48 entries Symbols: example/Synthetic# => class Synthetic extends Object { self: Synthetic => +23 decls } @@ -3985,11 +4086,14 @@ Diagnostics: Synthetics: [5:2..5:13):List(1).map => *[Int] [5:2..5:6):List => *.apply[Int] +[5:2..5:9):List(1) => List.apply[Int](*) [6:2..6:18):Array.empty[Int] => intArrayOps(*) +[6:18..6:18): => ClassTag.apply[Int](*) [7:2..7:8):"fooo" => augmentString(*) [10:13..10:24):"name:(.*)" => augmentString(*) [11:8..11:11):#:: => *.unapply[Int] [11:17..11:25):LazyList => *.apply[Int] +[11:17..11:31):LazyList(1, 2) => LazyList.apply[Int](*) [13:4..13:28):#:: 2 #:: LazyList.empty => *[Int] [13:8..13:28):2 #:: LazyList.empty => toDeferrer[Int](*) [13:10..13:28):#:: LazyList.empty => *[Int] @@ -3998,6 +4102,7 @@ Synthetics: [15:9..15:12):#:: => *.unapply[Int] [15:16..15:19):#:: => *.unapply[Int] [15:25..15:33):LazyList => *.apply[Int] +[15:25..15:39):LazyList(1, 2) => LazyList.apply[Int](*) [17:14..17:38):#:: 2 #:: LazyList.empty => *[Int] [17:18..17:38):2 #:: LazyList.empty => toDeferrer[Int](*) [17:20..17:38):#:: LazyList.empty => *[Int] @@ -4009,8 +4114,13 @@ Synthetics: [19:46..19:47):x => ArrowAssoc[Int](*) [20:12..20:13):1 => intWrapper(*) [20:26..20:27):0 => intWrapper(*) +[20:44..20:50):(i, j) => Tuple2.apply[Int, Int](*) [21:12..21:13):1 => intWrapper(*) [21:26..21:27):0 => intWrapper(*) +[21:58..21:64):(i, j) => Tuple2.apply[Int, Int](*) +[25:4..25:7):s() => apply(*) +[28:4..28:9):Bar() => apply(*) +[29:4..29:36):null.asInstanceOf[Int => Int](2) => apply(*) [32:35..32:49):Array.empty[T] => *(evidence$1) [36:22..36:27):new F => orderingToOrdered[F](*) [36:22..36:27):new F => *(ordering) @@ -4033,7 +4143,7 @@ Text => empty Language => Scala Symbols => 2 entries Occurrences => 5 entries -Synthetics => 2 entries +Synthetics => 3 entries Symbols: example/Tabs$package. => final package object example extends Object { self: example.type => +2 decls } @@ -4050,6 +4160,7 @@ Synthetics: [3:1..4:6):List(1,2,3) .map => *[Int] [3:1..3:5):List => *.apply[Int] +[3:1..3:12):List(1,2,3) => List.apply[Int](*) expect/TargetName.scala ----------------------- @@ -4144,7 +4255,7 @@ Language => Scala Symbols => 22 entries Occurrences => 45 entries Diagnostics => 3 entries -Synthetics => 11 entries +Synthetics => 21 entries Symbols: example/ValPattern# => class ValPattern extends Object { self: ValPattern => +14 decls } @@ -4223,17 +4334,41 @@ Diagnostics: [31:15..31:25): [warning] unset local variable, consider using an immutable val instead Synthetics: +[4:22..4:28):(1, 2) => Tuple2.apply[Int, Int](*) [5:6..5:10):Some => *.unapply[Int] [6:4..6:8):Some => *.apply[Int] +[6:4..6:11):Some(1) => Some.apply[Int](*) [8:6..8:10):List => *.unapplySeq[Nothing] [8:11..8:15):Some => *.unapply[Nothing] +[10:28..10:34):(1, 2) => Tuple2.apply[Int, Int](*) [11:6..11:10):Some => *.unapply[Int] [12:4..12:8):Some => *.apply[Int] +[12:4..12:11):Some(1) => Some.apply[Int](*) +[16:6..23:7):( + number1, + left, + right, + number1Var, + leftVar, + rightVar + ) => Tuple6.apply[Int, Int, Int, Int, Int, Int](*) [25:4..25:11):locally => *[Unit] +[26:26..26:32):(1, 2) => Tuple2.apply[Int, Int](*) [27:10..27:14):Some => *.unapply[Int] [28:8..28:12):Some => *.apply[Int] +[28:8..28:15):Some(1) => Some.apply[Int](*) +[30:32..30:38):(1, 2) => Tuple2.apply[Int, Int](*) [31:10..31:14):Some => *.unapply[Int] [32:8..32:12):Some => *.apply[Int] +[32:8..32:15):Some(1) => Some.apply[Int](*) +[34:8..41:9):( + number1, + left, + right, + number1Var, + leftVar, + rightVar + ) => Tuple6.apply[Int, Int, Int, Int, Int, Int](*) expect/Vals.scala ----------------- @@ -4833,6 +4968,7 @@ Language => Scala Symbols => 24 entries Occurrences => 63 entries Diagnostics => 2 entries +Synthetics => 1 entries Symbols: _empty_/Copy# => trait Copy [typeparam In <: Txn[In], typeparam Out <: Txn[Out]] extends Object { self: Copy[In, Out] => +5 decls } @@ -4929,6 +5065,9 @@ Diagnostics: [13:12..13:17): [warning] unused pattern variable [13:28..13:34): [warning] unused pattern variable +Synthetics: +[12:4..12:13):(in, out) => Tuple2.apply[Repr[In], Repr[Out]](*) + expect/inlineconsume.scala -------------------------- @@ -5023,7 +5162,7 @@ Text => empty Language => Scala Symbols => 17 entries Occurrences => 31 entries -Synthetics => 1 entries +Synthetics => 2 entries Symbols: _empty_/Concrete# => class Concrete extends NullaryTest[Int, List] { self: Concrete => +3 decls } @@ -5079,6 +5218,7 @@ Occurrences: Synthetics: [13:17..13:21):List => *.apply[Int] +[13:17..13:28):List(1,2,3) => List.apply[Int](*) expect/recursion.scala ---------------------- @@ -5090,6 +5230,7 @@ Text => empty Language => Scala Symbols => 36 entries Occurrences => 48 entries +Synthetics => 2 entries Symbols: local0 => type N$1 <: Nat @@ -5179,6 +5320,10 @@ Occurrences: [23:35..23:39): Zero -> recursion/Nats.Zero. [23:40..23:42): ++ -> recursion/Nats.Nat#`++`(). +Synthetics: +[5:50..5:60):Succ(this) => Succ.apply[Nat.this.type](*) +[5:50..5:60):Succ(this) => Succ.apply[Nat.this.type](*) + expect/semanticdb-Definitions.scala ----------------------------------- @@ -5798,7 +5943,7 @@ Text => empty Language => Scala Symbols => 18 entries Occurrences => 21 entries -Synthetics => 3 entries +Synthetics => 6 entries Symbols: _empty_/AnObject. => final object AnObject extends Object { self: AnObject.type => +6 decls } @@ -5845,8 +5990,11 @@ Occurrences: Synthetics: [11:2..11:6):List => *.apply[Int] +[11:2..11:12):List(1, 2) => List.apply[Int](*) [12:2..12:12):List.apply => *[Nothing] +[12:2..12:14):List.apply() => List.apply[Nothing](*) [13:2..13:14):List.`apply` => *[Nothing] +[13:2..13:16):List.`apply`() => List.apply[Nothing](*) expect/toplevel.scala --------------------- diff --git a/tests/warn/i15503a.scala b/tests/warn/i15503a.scala index 8fc97888b584..78234e6e777b 100644 --- a/tests/warn/i15503a.scala +++ b/tests/warn/i15503a.scala @@ -39,6 +39,10 @@ object FooGiven: val foo = summon[Int] +object SomeGivenImports: + given Int = 0 + given String = "foo" + /** * Import used as type name are considered * as used. @@ -69,7 +73,7 @@ object InlineChecks: object InlinedBar: import collection.mutable.Set // warn (don't be fooled by inline expansion) import collection.mutable.Map // warn - val a = InlineFoo.getSet + val a = InlineFoo.getSet // expansion is attributed mutable.Set.apply(1) object MacroChecks: object StringInterpol: @@ -91,12 +95,7 @@ object IgnoreExclusion: def check = val a = Set(1) val b = Map(1 -> 2) -/** - * Some given values for the test - */ -object SomeGivenImports: - given Int = 0 - given String = "foo" + def c = Seq(42) /* BEGIN : Check on packages*/ package nestedpackageimport: diff --git a/tests/warn/i21187.scala b/tests/warn/i21187.scala new file mode 100644 index 000000000000..d6ea131afdfb --- /dev/null +++ b/tests/warn/i21187.scala @@ -0,0 +1,22 @@ +//> using options -Wall + +def oops(msg: String) = sys.error(msg) + +class Zone +object Zone: + inline def apply[T](inline f: Zone ?=> T): T = f(using new Zone) + +inline def zone[A](inline f: Zone ?=> A) = Zone.apply(z => f(using z)) // warn suspicious contextualizing + +def zone_?[A](f: Zone ?=> A) = Zone.apply(z => f(using z)) // warn + +// intended +//inline def zone[A](inline f: Zone ?=> A): A = Zone.apply(z ?=> f(using z)) + +@main def hello = + // this swallows exceptions! + zone(oops("here")) // warn function value is not used + zone_?(oops("here")) // warn + + // this doesn't + Zone(oops("not here")) diff --git a/tests/warn/i21805.scala b/tests/warn/i21805.scala new file mode 100644 index 000000000000..4fdcae85910d --- /dev/null +++ b/tests/warn/i21805.scala @@ -0,0 +1,31 @@ +//> using options -Wunused:imports + +def i23967: Boolean = { + //import scala.compiletime.testing.typeCheckErrors + import scala.compiletime.testing.* // nowarn + typeChecks("2 + 2") +} + +package p: + val code = """"hello, world"""" +package c: + class C(i: Int) + +package q: + import c.* // warn should be nowarn + import p.* // warn should be nowarn + import scala.compiletime.testing.* + def test() = typeCheckErrors("""println(C("hello, world"))""") + def ok() = typeChecks("println(code)") + inline def f(inline i: Int) = 42 + i + +package i23967b: + package ok: + import scala.compiletime.testing.* // nowarn + def test() = typeChecks("42 + 27") + package nok: + import scala.compiletime.testing.typeChecks // nowarn + def test() = typeChecks("42 + 27") + +@main def Test = println: + q.f(27) diff --git a/tests/warn/i23369.scala b/tests/warn/i23369.scala new file mode 100644 index 000000000000..5f4130ab96d3 --- /dev/null +++ b/tests/warn/i23369.scala @@ -0,0 +1,26 @@ +class Module { + type BarTy + sealed trait Adt[A] + case class Foo() extends Adt[String] + case class Bar[A <: BarTy](x: BarTy) extends Adt[A] +} + +object Basic extends Module { + type BarTy = String +} + +def test(a: Basic.Adt[String]) = { + a match { // warn: match may not be exhaustive + case Basic.Foo() => + } +} + +object Basic2 extends Module { + type BarTy = Int +} + +def test2(a: Basic2.Adt[String]) = { + a match { + case Basic2.Foo() => + } +} diff --git a/tests/warn/i23693.check b/tests/warn/i23693.check new file mode 100644 index 000000000000..66b238de13a2 --- /dev/null +++ b/tests/warn/i23693.check @@ -0,0 +1,20 @@ +-- [E209] Interpolation Warning: tests/warn/i23693.scala:11:12 --------------------------------------------------------- +11 | s"k == $k" // warn + | ^ + | interpolation uses toString +-- [E209] Interpolation Warning: tests/warn/i23693.scala:13:16 --------------------------------------------------------- +13 | raw"\k == \$k" // warn + | ^ + | interpolation uses toString +-- [E209] Interpolation Warning: tests/warn/i23693.scala:15:12 --------------------------------------------------------- +15 | f"k == $k" // warn + | ^ + | interpolation uses toString +-- [E209] Interpolation Warning: tests/warn/i23693.scala:17:14 --------------------------------------------------------- +17 | f"k == $k%s" // warn + | ^ + | interpolation uses toString +-- [E209] Interpolation Warning: tests/warn/i23693.scala:19:18 --------------------------------------------------------- +19 | s"show == ${k.show}" // warn + | ^^^^^^ + | interpolated Unit value diff --git a/tests/warn/i23693.scala b/tests/warn/i23693.scala new file mode 100644 index 000000000000..341977dc717c --- /dev/null +++ b/tests/warn/i23693.scala @@ -0,0 +1,19 @@ +//> using options -Wtostring-interpolated + +// verify warning messages; cf run test; we must verify runtime while warning. + +case class K(i: Int): + def show: Unit = () + +@main def Test = + val k = K(42) + println: + s"k == $k" // warn + println: + raw"\k == \$k" // warn + println: + f"k == $k" // warn + println: + f"k == $k%s" // warn + println: + s"show == ${k.show}" // warn diff --git a/tests/warn/i23704.check b/tests/warn/i23704.check new file mode 100644 index 000000000000..71a69aefef11 --- /dev/null +++ b/tests/warn/i23704.check @@ -0,0 +1,20 @@ +-- [E198] Unused Symbol Warning: tests/warn/i23704.scala:9:8 ----------------------------------------------------------- +9 | var position: Int = 0 // warn position is assigned but not read + | ^^^^^^^^ + | private variable was mutated but not read +-- [E198] Unused Symbol Warning: tests/warn/i23704.scala:16:14 --------------------------------------------------------- +16 | private var myvar: Int = 0 // warn for the same case with simpler syntax + | ^^^^^ + | private variable was mutated but not read +-- [E198] Unused Symbol Warning: tests/warn/i23704.scala:26:8 ---------------------------------------------------------- +26 | var localvar = 0 // warn local variable was mutated but not read + | ^^^^^^^^ + | local variable was mutated but not read +-- [E198] Unused Symbol Warning: tests/warn/i23704.scala:33:8 ---------------------------------------------------------- +33 | var settable: Int = 0 // warn private variable was mutated but not read + | ^^^^^^^^ + | private variable was mutated but not read +-- [E198] Unused Symbol Warning: tests/warn/i23704.scala:39:14 --------------------------------------------------------- +39 | private var myvar: Int = 0 // warn plain unused + | ^^^^^ + | unused private member diff --git a/tests/warn/i23704.scala b/tests/warn/i23704.scala new file mode 100644 index 000000000000..9cfaae3278c1 --- /dev/null +++ b/tests/warn/i23704.scala @@ -0,0 +1,39 @@ +//> using options -Wunused:all + +trait Test { + def incr(): Unit +} + +object Test { + val test = new Test { + var position: Int = 0 // warn position is assigned but not read + + def incr(): Unit = { position += 1 } + } +} + +class C: + private var myvar: Int = 0 // warn for the same case with simpler syntax + def mine: Int = + myvar = 42 + 27 + +class D: + private var myvar: Int = 0 // nowarn (although read is RHS of assignment) + def incr(): Unit = myvar = myvar + 1 + + def local(): Unit = + var localvar = 0 // warn local variable was mutated but not read + localvar += 1 + +class E: + trait Setting: + def setting(): Unit + val test = new Test: + var settable: Int = 0 // warn private variable was mutated but not read + def setting(): Unit = + settable_=(42) + def incr() = setting() + +class F: + private var myvar: Int = 0 // warn plain unused diff --git a/tests/warn/i24034/circe.scala b/tests/warn/i24034/circe.scala new file mode 100644 index 000000000000..5d2e646f67b2 --- /dev/null +++ b/tests/warn/i24034/circe.scala @@ -0,0 +1,97 @@ + +// circe.scala + +package io.circe + +import scala.compiletime.* +import scala.deriving.Mirror +import scala.quoted.* + +trait Encoder[A]: + def encode(value: A): String + +object Encoder: + trait AsObject[A] extends Encoder[A] + given Encoder[String] = ??? + +trait Configuration +object Configuration: + val default: Configuration = ??? + +object Default: + given [A]: Default[A] = ??? +trait Default[T] + +trait Codec[A] extends Encoder[A] +object Codec: + trait AsObject[A] extends Encoder.AsObject[A] + object AsObject: + inline final def derived[A](using inline A: Mirror.Of[A]): Codec.AsObject[A] = + ConfiguredCodec.derived[A](using Configuration.default) + inline final def derivedConfigured[A](using + inline A: Mirror.Of[A], + inline conf: Configuration + ): Codec.AsObject[A] = ConfiguredCodec.derived[A] + +trait ConfiguredEncoder[A](using conf: Configuration) extends Encoder.AsObject[A] +trait ConfiguredCodec[A] extends Codec.AsObject[A], ConfiguredEncoder[A] +object ConfiguredCodec: + inline final def derive[A: Mirror.Of](): ConfiguredCodec[A] = + derived[A](using Configuration.default) + inline final def derived[A](using + conf: Configuration, + inline mirror: Mirror.Of[A] + ): ConfiguredCodec[A] = ${ derivedImpl[A]('conf, 'mirror) } + def ofProduct[A]( + encoders: => List[Encoder[?]] + )(using Configuration, Default[A]): ConfiguredCodec[A] = ??? + def derivedImpl[A: Type](conf: Expr[Configuration], mirror: Expr[Mirror.Of[A]])(using + q: Quotes + ): Expr[ConfiguredCodec[A]] = { + mirror match { + case '{ + ${ _ }: Mirror.ProductOf[A] { + type MirroredLabel = l + type MirroredElemLabels = el + type MirroredElemTypes = et + } + } => + '{ + ConfiguredCodec.ofProduct[A]( + derivation.summonEncoders[et & Tuple](false)(using $conf) + )(using $conf) + } + } + } + +object derivation: + sealed trait Inliner[A, Arg]: + inline def apply[T](inline arg: Arg): A + + class EncoderNotDeriveSum(using config: Configuration) extends Inliner[Encoder[?], Unit]: + inline def apply[T](inline arg: Unit): Encoder[?] = summonEncoder[T](false) + + inline final def loopUnrolled[A, Arg, T <: Tuple](f: Inliner[A, Arg], inline arg: Arg): List[A] = + inline erasedValue[T] match + case _: EmptyTuple => Nil + case _: (h *: ts) => f[h](arg) :: loopUnrolled[A, Arg, ts](f, arg) + + inline def loopUnrolledNoArg[A, T <: Tuple](f: Inliner[A, Unit]): List[A] = + loopUnrolled[A, Unit, T](f, ()) + + inline final def summonEncoders[T <: Tuple](inline derivingForSum: Boolean)(using + Configuration + ): List[Encoder[?]] = + loopUnrolledNoArg[Encoder[?], T]( + inline if (derivingForSum) compiletime.error("unreachable") + else new EncoderNotDeriveSum + ) + + private[circe] inline final def summonEncoder[A]( + inline derivingForSum: Boolean + )(using Configuration): Encoder[A] = summonFrom { + case encodeA: Encoder[A] => encodeA + case _: Mirror.Of[A] => + inline if (derivingForSum) compiletime.error("unreachable") + else error("Failed to find an instance of Encoder[]") + } diff --git a/tests/warn/i24034/iron.scala b/tests/warn/i24034/iron.scala new file mode 100644 index 000000000000..17cb26bf6408 --- /dev/null +++ b/tests/warn/i24034/iron.scala @@ -0,0 +1,30 @@ + +// iron.scala + +package iron + +import io.circe.* + +opaque type IronType[A, C] <: A = A +type :|[A, C] = IronType[A, C] +trait Constraint[A, C] + +package constraint: + object string: + final class StartWith[V <: String] + object StartWith: + inline given [V <: String]: Constraint[String, StartWith[V]] = ??? + +object circe: + inline given XXX[A, B](using inline encoder: Encoder[A]): Encoder[A :| B] = ??? + inline given YYY[A, B](using inline encoder: Encoder[A], dummy: scala.util.NotGiven[DummyImplicit]): Encoder[A :| B] = ??? + // inline given [T](using mirror: RefinedTypeOps.Mirror[T], ev: Encoder[mirror.IronType]): Encoder[T] = ??? + +// trait RefinedTypeOps[A, C, T]: +// inline given RefinedTypeOps.Mirror[T] = ??? +// object RefinedTypeOps: +// trait Mirror[T]: +// type BaseType +// type ConstraintType +// type IronType = BaseType :| ConstraintType + diff --git a/tests/warn/i24034/test.scala b/tests/warn/i24034/test.scala new file mode 100644 index 000000000000..f87be8da2c36 --- /dev/null +++ b/tests/warn/i24034/test.scala @@ -0,0 +1,10 @@ +//> using options -Wunused:all + +import io.circe.Codec + +import iron.:| +import iron.circe.given +import iron.constraint.string.StartWith + +case class Alien(name: String :| StartWith["alen"]) derives Codec.AsObject + diff --git a/tests/warn/i24248/lib.scala b/tests/warn/i24248/lib.scala new file mode 100644 index 000000000000..8244d509f8f4 --- /dev/null +++ b/tests/warn/i24248/lib.scala @@ -0,0 +1,18 @@ + +import scala.quoted.* + +trait Thing +object Stuff: + given Thing() + +object lib: + inline def m: Thing = ${ mImpl[Thing] } + + def mImpl[T](using Quotes, Type[T]): Expr[T] = + import quotes.reflect.* + val thing = Implicits.search(TypeRepr.of[T]) match + case iss: ImplicitSearchSuccess => iss.tree.asExprOf[T] + '{ + val res = $thing + res + } diff --git a/tests/warn/i24248/test.scala b/tests/warn/i24248/test.scala new file mode 100644 index 000000000000..35137a5646c9 --- /dev/null +++ b/tests/warn/i24248/test.scala @@ -0,0 +1,6 @@ +//> using options -Werror -Wunused:all + +import Stuff.given + +@main def test = println: + lib.m diff --git a/tests/warn/i24263.scala b/tests/warn/i24263.scala new file mode 100644 index 000000000000..fc4768de2597 --- /dev/null +++ b/tests/warn/i24263.scala @@ -0,0 +1,6 @@ +//> using options -Werror -Wunused:all + +object test { + def f(t: Tuple): Nothing = ??? + val _ = (inputTuple: NamedTuple.NamedTuple[Tuple, Tuple]) => f(inputTuple) +} diff --git a/tests/warn/i24265.scala b/tests/warn/i24265.scala new file mode 100644 index 000000000000..9c5f189b26d6 --- /dev/null +++ b/tests/warn/i24265.scala @@ -0,0 +1,10 @@ +//> using options -Wall -Werror + +object test { + inline def f(testFun: => Any) = testFun + + f { + val i = 1 + summon[i.type <:< Int] + } +}