diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 000000000000..03fa049e3ea9 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,4 @@ +46a26945a172429740ebdd1fc83517130670080b + +# Add initial formatting to presentation-compiler directory +e24e3b64331fa6fdf122fce551ff5436e2a85340 diff --git a/.github/Dockerfile b/.github/Dockerfile index 3ba5466c30b8..e9b449421858 100644 --- a/.github/Dockerfile +++ b/.github/Dockerfile @@ -22,5 +22,5 @@ RUN apt-get update && \ # Install sbt ENV SBT_HOME /usr/local/sbt ENV PATH ${SBT_HOME}/bin:${PATH} -ENV SBT_VERSION 1.10.5 +ENV SBT_VERSION 1.10.7 RUN curl -sL "https://github.com/sbt/sbt/releases/download/v$SBT_VERSION/sbt-$SBT_VERSION.tgz" | gunzip | tar -x -C /usr/local \ No newline at end of file diff --git a/.github/workflows/build-chocolatey.yml b/.github/workflows/build-chocolatey.yml index 9de87d8e5ad6..e4d72f474780 100644 --- a/.github/workflows/build-chocolatey.yml +++ b/.github/workflows/build-chocolatey.yml @@ -27,7 +27,7 @@ jobs: build: runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Replace the version placeholder uses: richardrigutins/replace-in-files@v2 with: @@ -49,7 +49,7 @@ jobs: - name: Build the Chocolatey package (.nupkg) run: choco pack ./pkgs/chocolatey/scala.nuspec --out ./pkgs/chocolatey - name: Upload the Chocolatey package to GitHub - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: scala.nupkg path: ./pkgs/chocolatey/scala.${{ inputs.version }}.nupkg diff --git a/.github/workflows/build-sdk.yml b/.github/workflows/build-sdk.yml index 63b31b742d8b..01e6a30e03c2 100644 --- a/.github/workflows/build-sdk.yml +++ b/.github/workflows/build-sdk.yml @@ -34,8 +34,8 @@ jobs: universal-digest : ${{ steps.universal-digest.outputs.digest }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@v6 + - uses: actions/setup-java@v5 with: distribution: temurin java-version: ${{ inputs.java-version }} @@ -45,7 +45,7 @@ jobs: run : ./project/scripts/sbt dist/pack - name: Upload zip archive to GitHub Artifact (universal) - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 id : universal with: path: ./dist/target/pack/* diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9239020335e7..2b82f9f232b7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -84,13 +84,13 @@ jobs: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - name: Checkout cleanup script - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Cleanup run: .github/workflows/cleanup.sh - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Add SBT proxy repositories run: cp -vf .github/workflows/repositories /root/.sbt/ ; true @@ -137,13 +137,13 @@ jobs: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - name: Checkout cleanup script - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Cleanup run: .github/workflows/cleanup.sh - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Add SBT proxy repositories run: cp -vf .github/workflows/repositories /root/.sbt/ ; true @@ -180,7 +180,7 @@ jobs: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Test run: sbt ";scala3-bootstrapped/compile; scala3-bootstrapped/testCompilation; scala3-presentation-compiler/test; scala3-language-server/test" @@ -224,7 +224,7 @@ jobs: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Test run: sbt ";dist/pack ;scala3-bootstrapped/compile ;scala3-bootstrapped/test" @@ -266,13 +266,13 @@ jobs: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - name: Checkout cleanup script - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Cleanup run: .github/workflows/cleanup.sh - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Add SBT proxy repositories run: cp -vf .github/workflows/repositories /root/.sbt/ ; true @@ -313,13 +313,13 @@ jobs: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - name: Checkout cleanup script - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Cleanup run: .github/workflows/cleanup.sh - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Add SBT proxy repositories run: cp -vf .github/workflows/repositories /root/.sbt/ ; true @@ -367,13 +367,13 @@ jobs: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - name: Checkout cleanup script - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Cleanup run: .github/workflows/cleanup.sh - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Add SBT proxy repositories run: cp -vf .github/workflows/repositories /root/.sbt/ ; true @@ -421,13 +421,13 @@ jobs: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - name: Checkout cleanup script - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Cleanup run: .github/workflows/cleanup.sh - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Add SBT proxy repositories run: cp -vf .github/workflows/repositories /root/.sbt/ ; true @@ -474,13 +474,13 @@ jobs: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - name: Checkout cleanup script - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Cleanup run: .github/workflows/cleanup.sh - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Add SBT proxy repositories run: cp -vf .github/workflows/repositories /root/.sbt/ ; true @@ -506,7 +506,6 @@ jobs: || ( github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]') - && contains(github.event.pull_request.body, '[test_java8]') ) || ( github.event_name == 'workflow_dispatch' @@ -523,13 +522,13 @@ jobs: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - name: Checkout cleanup script - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Cleanup run: .github/workflows/cleanup.sh - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Add SBT proxy repositories run: cp -vf .github/workflows/repositories /root/.sbt/ ; true @@ -571,13 +570,13 @@ jobs: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - name: Checkout cleanup script - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Cleanup run: .github/workflows/cleanup.sh - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Add SBT proxy repositories run: cp -vf .github/workflows/repositories /root/.sbt/ ; true @@ -605,7 +604,7 @@ jobs: - name: Publish Nightly if: "steps.not_yet_published.outcome == 'success'" run: | - ./project/scripts/sbtPublish ";project scala3-bootstrapped ;publishSigned ;sonatypeBundleRelease" + ./project/scripts/sbtPublish ";project scala3-bootstrapped ;publishSigned ;sonaRelease" nightly_documentation: runs-on: [self-hosted, Linux] @@ -635,13 +634,13 @@ jobs: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - name: Checkout cleanup script - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Cleanup run: .github/workflows/cleanup.sh - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Add SBT proxy repositories run: cp -vf .github/workflows/repositories /root/.sbt/ ; true @@ -690,13 +689,13 @@ jobs: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/scala/scala3" && git reset --hard FETCH_HEAD || true - name: Checkout cleanup script - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Cleanup run: .github/workflows/cleanup.sh - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Add SBT proxy repositories run: cp -vf .github/workflows/repositories /root/.sbt/ ; true @@ -746,7 +745,7 @@ jobs: ./dist/target/sha256sum.txt - name: Publish Release - run: ./project/scripts/sbtPublish ";project scala3-bootstrapped ;publishSigned ;sonatypeBundleUpload" + run: ./project/scripts/sbtPublish ";project scala3-bootstrapped ;publishSigned ;sonaUpload" open_issue_on_failure: @@ -759,7 +758,7 @@ jobs: if: "failure() && github.event_name == 'schedule'" steps: - name: Checkout issue template - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Open an issue uses: JasonEtco/create-an-issue@v2 @@ -782,15 +781,24 @@ jobs: uses: ./.github/workflows/build-chocolatey.yml needs: [ build-sdk-package ] with: - version: 3.3.5-local # unused - url : https://api.github.com/repos/scala/scala3/actions/artifacts/${{ needs.build-sdk-package.outputs.universal-id }}/zip - digest : ${{ needs.build-sdk-package.outputs.universal-digest }} + version: 3.3.7-SNAPSHOT # Fake version, used only for choco tests + url : https://api.github.com/repos/scala/scala3/actions/artifacts/${{ needs.build-sdk-package.outputs.win-x86_64-id }}/zip + digest : ${{ needs.build-sdk-package.outputs.win-x86_64-digest }} test-chocolatey-package: uses: ./.github/workflows/test-chocolatey.yml with: - version : 3.3.5-local # unused + version : 3.3.7-SNAPSHOT # Fake version, used only for choco tests java-version: 8 if: github.event_name == 'pull_request' && contains(github.event.pull_request.body, '[test_chocolatey]') needs: [ build-chocolatey-package ] + scalafmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - uses: coursier/cache-action@v8 + - uses: VirtusLab/scala-cli-setup@v1.12.5 + - run: scala-cli format --check diff --git a/.github/workflows/dependency-graph.yml b/.github/workflows/dependency-graph.yml index 7b4d7237a500..41b82382dfe0 100644 --- a/.github/workflows/dependency-graph.yml +++ b/.github/workflows/dependency-graph.yml @@ -8,6 +8,6 @@ jobs: name: Update Dependency Graph runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: sbt/setup-sbt@v1 - uses: scalacenter/sbt-dependency-submission@v3 diff --git a/.github/workflows/language-reference.yaml b/.github/workflows/language-reference.yaml index 6406996b7ec5..a4120a27ace9 100644 --- a/.github/workflows/language-reference.yaml +++ b/.github/workflows/language-reference.yaml @@ -24,14 +24,14 @@ jobs: run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: path: 'dotty' fetch-depth: 0 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 @@ -47,7 +47,7 @@ jobs: - name: Push changes to scala3-reference-docs if: github.event_name == 'push' - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: repository: lampepfl/scala3-reference-docs fetch-depth: 0 diff --git a/.github/workflows/publish-chocolatey.yml b/.github/workflows/publish-chocolatey.yml index 3b31728a50ba..a40f83395e38 100644 --- a/.github/workflows/publish-chocolatey.yml +++ b/.github/workflows/publish-chocolatey.yml @@ -31,9 +31,11 @@ jobs: runs-on: windows-latest steps: - name: Fetch the Chocolatey package from GitHub - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: scala.nupkg - name: Publish the package to Chocolatey - run: choco push scala.nupkg --source https://push.chocolatey.org/ --api-key ${{ secrets.API-KEY }} - \ No newline at end of file + env: + VERSION: ${{ inputs.version }} + KEY: ${{ secrets.API-KEY }} + run: choco push "scala.$VERSION.nupkg" --source https://push.chocolatey.org/ --api-key "$KEY" diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index 03ff19444d2f..1369578f2de3 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -56,8 +56,10 @@ jobs: steps: - name: Compute the SHA256 of scala3-${{ inputs.version }}.zip in GitHub Release id: digest + env: + VERSION: ${{ inputs.version }} run: | - curl -o artifact.zip -L https://github.com/scala/scala3/releases/download/${{ inputs.version }}/scala3-${{ inputs.version }}.zip + curl -o artifact.zip -L "https://github.com/scala/scala3/releases/download/$VERSION/scala3-$VERSION.zip" echo "digest=$(sha256sum artifact.zip | cut -d " " -f 1)" >> "$GITHUB_OUTPUT" build-chocolatey: diff --git a/.github/workflows/scaladoc.yaml b/.github/workflows/scaladoc.yaml index 22c49d2ff9c1..2ee0e32a2b5c 100644 --- a/.github/workflows/scaladoc.yaml +++ b/.github/workflows/scaladoc.yaml @@ -27,10 +27,10 @@ jobs: steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: 17 @@ -76,10 +76,10 @@ jobs: steps: - name: Git Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - 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 b6ca9bf74b12..8bba9d4e5d3c 100644 --- a/.github/workflows/test-chocolatey.yml +++ b/.github/workflows/test-chocolatey.yml @@ -21,18 +21,21 @@ on: env: CHOCOLATEY-REPOSITORY: chocolatey-pkgs - DOTTY_CI_INSTALLATION: ${{ secrets.GITHUB_TOKEN }} + # Controls behaviour of chocolatey{Install,Uninstall}.ps1 scripts + # During snapshot releases it uses a different layout and requires access token to GH Actions artifacts + # During stable releases it uses publically available archives + DOTTY_CI_INSTALLATION: ${{ endsWith(inputs.version, '-SNAPSHOT') && secrets.GITHUB_TOKEN || '' }} jobs: test: runs-on: windows-latest steps: - - uses: actions/setup-java@v4 + - 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@v8 with: name: scala.nupkg path: ${{ env.CHOCOLATEY-REPOSITORY }} diff --git a/.gitignore b/.gitignore index e4947b9bfebb..428eab3a2152 100644 --- a/.gitignore +++ b/.gitignore @@ -98,3 +98,6 @@ docs/_spec/.jekyll-metadata # scaladoc related scaladoc/output/ + +# only used in local development +.enable_bsp_all_projects \ No newline at end of file diff --git a/.jvmopts b/.jvmopts index a50abf36aa42..d8606eba733e 100644 --- a/.jvmopts +++ b/.jvmopts @@ -1,5 +1,5 @@ --Xss1m --Xms512m --Xmx4096m +-Xss2m +-Xms1024m +-Xmx8192m -XX:MaxInlineLevel=35 -XX:ReservedCodeCacheSize=512m diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 000000000000..284d48efaabc --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,40 @@ +version = "3.10.3" + +align.preset = none + +align.tokens = [{ + code = "<-" + owners = [{ + regex = "Enumerator.Generator" + }] +}] + +runner.dialect = scala3 + +maxColumn = 120 + +docstrings.wrapMaxColumn = 80 +docstrings.style = AsteriskSpace +docstrings.wrap = "keep" + +rewrite.trailingCommas.style = "keep" + +rewrite.scala3.endMarker.spanIs = "blankgaps" # when no blanks are used, drop the end marker +rewrite.scala3.endMarker.removeMaxSpan = 1 # maximum number of blank gaps to allow +rewrite.scala3.convertToNewSyntax = true # switch to new scala 3 syntax + +rewrite.rules = [Imports] +rewrite.imports.sort = scalastyle +rewrite.imports.groups = [["java\\..*"], ["scala\\..*"], ["dotty\\..*"]] + +# These options do not take into account blank gaps, so they are disabled by default +# rewrite.scala3.removeOptionalBraces.enabled = true +# rewrite.scala3.removeOptionalBraces.fewerBracesMinSpan = 1 +# rewrite.scala3.removeOptionalBraces.fewerBracesMaxSpan = 1 + +newlines.source = keep +newlines.implicitParamListModifierPrefer = before + +project.includePaths = [ + "glob:**/presentation-compiler/**/*.scala", +] \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000000..2c1eae52cc8f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,64 @@ +# AGENTS.md + +This file provides guidance to AI coding agents when working with code in this repository. + +## Project Overview + +Dotty is the Scala 3 compiler (`dotc`). It compiles Scala source code into JVM bytecode (and optionally Scala.js IR). The compiler is itself written in Scala 3. + +## Build Commands + +Use `sbt --client` (sbt with native thin client) to run commands. This keeps an sbt server running for faster subsequent invocations. + +### Compilation +- `sbt --client scalac ` - Compile a file with the current compiler changes +- `sbt --client scala ` - Run a compiled class + +Use the `local/` directory for temporary test files (gitignored). + +### Testing +- `sbt --client testCompilation` - Run the whole compilation test suite. +- `sbt --client testCompilation ` - Run compilation tests matching `` (e.g., `sbt --client testCompilation i12345`) +- `sbt --client scala3-bootstrapped/test` - Run all tests (slow, CI-level) +- `sbt --client "testCompilation --update-checkfiles"` - Update `.check` files with actual output + + +## Test Directory Structure + +Tests are in `tests/` with categories: +- `tests/pos` - Should compile successfully +- `tests/neg` - Should fail to compile (requires `// error` annotations on expected error lines) +- `tests/run` - Compile and execute (requires `@main def Test` or `object Test extends App`) +- `tests/pos-custom-args`, `tests/neg-custom-args` - Tests requiring special compiler options + +Custom compiler options can be specified: +1. Per-directory in `compiler/test/dotty/tools/dotc/CompilationTests.scala` +2. Per-file with `//> using options ` directive at the top of the file + +Capture checking tests are in `tests/{pos,neg}-custom-args/captures`. Use `-Ycc-verbose` for verbose capture checking output. + +Multi-file tests: Use `_1.scala`, `_2.scala` suffixes for ordered separate compilation, or no suffix for joint compilation. + +Checkfiles: `.check` files contain expected compiler output or program output. + +Test annotations (magic comments): +- `// error` - Expect an error on this line (use multiple times for multiple errors) +- `// warn` - Expect a warning on this line +- `// nopos-error` - Expect an error with no position (can be anywhere in file) +- `// anypos-error` - Expect an error with position that can't be annotated +- `// scalajs: --skip` - Skip this test for Scala.js + +## Bootstrapping + +The compiler has two configurations: +- **nonbootstrapped** - Built with a released Scala 3 compiler (faster iteration) +- **bootstrapped** - Built with itself (required for some tests, final validation) + +Use `scala3-compiler-bootstrapped/testCompilation` for tests requiring the bootstrapped compiler. + +## Documentation + +Documentation lives in `docs/_docs/`: +- `docs/_docs/reference/` - Scala 3 language reference +- `docs/_docs/reference/experimental/capture-checking/` - Capture checking specification +- `docs/_docs/internals/` - Compiler internals documentation diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 90496bcd0c0a..b3e4d22ae960 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,112 @@ -# Contributing to Dotty +# Contributing to Scala 3 -Firstly, thanks for being willing to contribute to Dotty! Head on over the -[Scala 3 Contributing -Guide](https://dotty.epfl.ch/docs/contributing/index.html), which should have all the info you're looking for. +Thanks for being willing to contribute! +We maintainers love contributions that fix bugs, improve error messages, simplify code, speed up the compiler, +and generally make the Scala 3 experience better. + +Look at this repo's issues to find useful areas for contributions. +You can of course open your own if you've spotted a bug or have an idea for improvement. +We will generally not accept pull requests without a corresponding issue, unless they are short and self-contained such as fixing typos. + +This repo contains the compiler, standard library, and some related components, you may also want to contribute to +[scala-cli](https://github.com/VirtusLab/scala-cli), [scala-native](https://github.com/scala-native/scala-native), [metals](https://github.com/scalameta/metals), +and [docs.scala-lang](https://github.com/scala/docs.scala-lang). + +For _standard library API changes_, please refer to the [Standard Library Changes process](https://nightly.scala-lang.org/docs/contributing/procedures/contributing-to-stdlib.html). + +For _language changes_, please refer to the [Scala Improvement Process](https://docs.scala-lang.org/sips/process-specification.html). + +Refer to our [LLM policy](LLM_POLICY.md) for rules and guidelines regarding usage of LLM-based tools in your contributions. + +## Getting started + +Scala's compiler is written in Scala. +A typical compiler contributor knows the language already and is familiar with the general architecture of compilers. +One good way to get started is to participate in the [Scala Compiler Spree](https://airtable.com/app94nwzow5R6W1O6/pagvjIzxYnqTTlhwY/form)! +You'll be assigned to a team and work with experienced contributors to fix an issue. + +If you're a beginner, we recommend you start by contributing to less complex Scala projects. +Scaladex has [a list of Scala projects looking for contributions](https://index.scala-lang.org/search?q=&contributingSearch=true). + +To learn how to set up your local environment, how the compiler works, how to reproduce issues, and so on, +head to the [Scala 3 Contributing Guide](https://nightly.scala-lang.org/docs/contributing/index.html). + +You may also find the ["Compiler Academy"](https://www.youtube.com/channel/UCIH0OgqE54-KEvYDg4LRhKQ) videos useful to learn more about specific parts of the compiler. + +## Encouraged + +- Do let us know you'd like to work on an issue by posting a comment, so we can assign it, keep track of work and avoid duplicate work +- Do discuss a sketch of your solution on an issue so we can give you feedback +- Do open a draft pull request if you have a solution that mostly works but need help on specific sub-problems +- Do ask questions if there are specific things you can't find in the documentation or in the code +- Do make sure you understand what the code you touch is doing and why your solution works +- Do keep in mind general software engineering practices, such as encapsulation and code sharing +- Do ping maintainers if you think you've done everything on your side and aren't getting a response after a few weeks + +## Discouraged + +- Don't open large pull requests with no prior discussion +- Don't make stylistic changes based on your personal taste that make pull requests harder to review +- Don't open pull requests you cannot explain even if they pass tests, such as "don't call this method on those inputs because it crashes" + (one can accidentally introduce unsoundness this way, and special cases are generally not the way to go) +- Don't try to fix `TODO`s or `FIXME`s in the codebase without a deep understanding of their context + (remember, if they took 5 minutes to fix, the person writing them would most likely have fixed them instead of adding a comment) +- Don't ask general questions such as "how does a compiler work" or "what is JVM bytecode", these are better answered by existing resources +- Don't expect maintainers to respond immediately every time you have a question + (most maintainers do this part time, so we cannot commit to as short a turnaround time as we would ideally like) +- Don't break our LLM policy linked above, especially in the form of PRs "vibe-coded" without proper understanding of the contributed change + +## Maintainers + +Principal areas of the compiler and internal team members responsible for their maintenance: + +**Compiler** +- Parser: @odersky +- Typer: @odersky, @noti0nal, @bracevac, (@smarter) +- Erasure: @odersky, (@smarter) +- Enums: @zielinsky +- Derivation & Mirrors: +- Export: @odersky +- Pattern Matching: @sjrd, @noti0na1, @zielinsky +- Inline: @odersky, @jchyb +- Metaprogramming (Quotes, Reflect, Staging): @jchyb +- Match types: @sjrd, @Linyxus +- GADT: @Linyxus +- Initialization checker: @liufengyun, @olhotak, @EnzeXing +- Transforms: @sjrd, @odersky, @smarter +- Tailrec: @sjrd, @mbovel +- JS backend: @sjrd +- JVM backend: @sjrd, @tanishiking, @lrytz +- Java-compat: @tanishiking, @lrytz, (@smarter) +- Extension Methods: @odersky +- Safe nulls (experimental): @noti0na1 +- Capture checker (experimental): @odersky, @Linyxus, @bracevac, @noti0na1 +- Modularity (experimental): (@KacperFKorban) +- Named Tuples: @odersky, @aherlihy +- Standard library: @lrytz, @SethTisue, @natsukagami, @noti0na1 + +**Tooling** +- REPL: +- Runner/CLI: @Gedochao, (@tgodzik) +- IDE: @tgodzik, @zielinsky +- Scaladoc: (@Florian3k) +- SemanticDB: @natsukagami, (@tanishiking) +- Coverage: (@KacperFKorban) +- Linting (especially unused warnings) / Reporting UX: @som-snytt, (@tgodzik) +- Presentation Compiler: @tgodzik, @natsukagami, @zielinsky, (@rochala) +- Debug Adapter: @tgodzik +- Scastie: @warcholjakub, (@rochala) + +**Infrastructure** +- CI: @WojciechMazur +- Community Build: @WojciechMazur +- Open Community Build: @WojciechMazur +- Vulpix: +- Benchmarks: @mbovel +- Releases: @WojciechMazur (Scala 3 Next), @tgodzik (Scala 3.3 LTS) + +**Misc** +- Issue tracker: @Gedochao +- Coordination with the Scala ecosystem: @tgodzik, @SethTisue, (@Gedochao) +- Roadmap: @Gedochao +- SLC: @tgodzik diff --git a/MAINTENANCE.md b/MAINTENANCE.md deleted file mode 100644 index 11579838a9a0..000000000000 --- a/MAINTENANCE.md +++ /dev/null @@ -1,100 +0,0 @@ -# Issue Supervisor Role - -This document formally defines the _Issue Supervisor_ role. This is a repository maintenance role that is assigned to core contributors on a rotating basis. - -## Responsibilities - -The issue supervisor is responsible for: - -- The health of the CI, nightly releases and benchmark infrastructure. -- PRs of external contributors: assigning someone to review, or handling themselves. -- Triaging issues (especially new): - - Each issue needs to be assigned an `itype` and 1 or more `area` labels. - - Where applicable, new issues need to be designated for the Spree or Semester projects with the corresponding labels. - - Regressions from an earlier Scala 3 release must be classified with the “regression” label and assigned to the next release’s milestone. - - Modifying issue labels to best capture information about the issues - - Attempting to reproduce the issue (or label “stat:cannot reproduce”) - - Further minimizing the issue or asking the reporter of the issue to minimize it correctly (or label “stat:needs minimization”) - - Identifying which issues are of considerable importance and bringing them to the attention of the team during the Dotty meeting, where they can be filtered and added to the [Future Versions](https://github.com/lampepfl/dotty/milestone/46) milestone. - - Identifying if a report is really a feature request and if so, converting it to - a [feature request discussion](https://github.com/lampepfl/dotty/discussions/categories/feature-requests). -- Keeping an eye on new -[discussions](https://github.com/lampepfl/dotty/discussions), making sure they -don't go unanswered and also correctly labeling new feature requests. - -Other core teammates are responsible for providing information to the issue supervisor in a timely manner when it is requested if they have that information. - -## Assignment - -The issue supervisor is appointed for 7 days and is responsible for what is specified in the “Responsibilities” section during those 7 days. Their assumption of the role starts from the Dotty Meeting on Monday and ends on the next Dotty Meeting on Monday. - -During the Dotty Meeting, an issue supervisor is assigned for the current week and for the week after that. - -The issue supervisor schedule is maintained in the [Issue Supervisor Statistics spreadsheet](https://docs.google.com/spreadsheets/d/19IAqNzHfJ9rsii3EsjIGwPz5BLTFJs_byGM3FprmX3E/edit?usp=sharing). So, someone who knows their availability several weeks ahead into the future can assign themselves to be an issue supervisor well ahead of time. - -## Prerequisites - -An issue supervisor needs to have all the accesses and privileges required to get their job done. This might include: - -- Admin rights in lampepfl/dotty repository -- Permission to create new repositories in lampepfl organization (needed to fork repositories for the community build) -- Access to the LAMP slack to be able to ask for help with the infrastructure, triaging and such - -## Procedures - -To ensure the proper health of the infrastructure, the supervisor regularly monitors its proper operation. If a malfunction is detected, the supervisor's job is to ensure that someone is working on it (or solve it on their own). - -If it is unclear what area an issue belongs to, the supervisor asks for advice from other team members on Slack or GitHub. If, after asking for advice, it turns out that nobody in the team knows how to classify it, the issue must be classified with a “stat:needs triage” label. - -If it is unclear who should review an external PR, the supervisor asks for advice from the rest of the core team. If after asking for advice, it is still unclear who should do it, the reviewer for such a PR will be decided at the next Dotty meeting. - -In general, if anything else is unclear for the proper fulfillment of responsibilities, the supervisor must proactively seek advice from other team members on Slack or other channels. - -## Reporting - -At the end of their supervision period, the supervisor reports to the team during the Dotty meeting on the following points: - -- Whether there were any incidents with the CI, nightlies and benchmarks, how they were resolved and what steps were taken to prevent them from happening in the future. -- How many new external contributors’ PRs were there and what they were about (in brief). -- How many new issues were opened during their supervision period? Were there any areas that got a lot of issues? How many regressions from a prior Scala 3 release were there? Which were designated for an MSc project or an Issue Spree? -- If new labels were created or old ones were removed, or there is any other feedback on how to improve the issue supervision, mention that. -- Unassigned PRs and issues that the team failed to classify: bring them one by one so that the team can make a decision on them. -- Issues of importance – candidates for the Future Versions milestone. - -## Maintenance List - -The following is the list of all the principal areas of the compiler and the core team members who are responsible for their maintenance: - -### Compiler -- Parser: @odersky -- Typer: @odersky, @smarter, (@dwijnand) -- Erasure: @smarter, @odersky -- Enums: @bishabosha -- Derivation & Mirrors: @bishabosha, (@dwijnand) -- Export: @bishabosha, @odersky -- Pattern Matching: @dwijnand, (@liufengyun), @sjrd -- Inline: @nicolasstucki, @odersky -- Metaprogramming (Quotes, Reflect, Staging): @nicolasstucki, @jchyb -- Match types: @sjrd, @dwijnand, @Decel -- GADT: @dwijnand, @Linyxus -- Initialization checker: @olhotak, @liufengyun -- Safe nulls: @noti0na1, @olhotak -- Lazy vals: @szymon-rd, @sjrd -- tailrec: @sjrd, @mbovel -- JS backend: @sjrd -- JVM backend: @sjrd -- Java-compat: @smarter - -### Tooling -- REPL: @dwijnand, @prolativ -- Scaladoc: @Florian3k -- SemanticDB: @tanishiking -- Coverage: @TheElectronWill -- Linting (especially unused warnings) / Reporting UX: @szymon-rd - -### Infrastructure -- CI: @anatoliykmetyuk -- Community Build: @anatoliykmetyuk -- Open Community Build: @WojciechMazur -- Vulpix: @dwijnand, @prolativ -- Benchmarks: @mbovel diff --git a/NOTICE.md b/NOTICE.md index 9bb846dbc3f5..e8e79f7e4d41 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1,6 +1,6 @@ Scala 3 (https://www.scala-lang.org) -Copyright 2012-2025 EPFL -Copyright 2012-2025 Lightbend, Inc. dba Akka +Copyright 2012-2026 EPFL +Copyright 2012-2026 Lightbend, Inc. dba Akka Licensed under the Apache License, Version 2.0 (the "License"): http://www.apache.org/licenses/LICENSE-2.0 @@ -105,4 +105,4 @@ major authors were omitted by oversight. [3] https://github.com/sbt/sbt/tree/0.13/compile/interface/src/main/scala/xsbt [4] https://github.com/scoverage/scalac-scoverage-plugin [5] https://github.com/scalameta/metals -[6] https://github.com/lampepfl/dotty/pull/5783/files +[6] https://github.com/scalapb/protobuf-scala-runtime diff --git a/README.md b/README.md index 1e62e90a1845..af635f810261 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ -Dotty +Scala 3 ===== -[![Dotty CI](https://github.com/lampepfl/dotty/workflows/Dotty/badge.svg?branch=master)](https://github.com/lampepfl/dotty/actions?query=branch%3Amain) +[![Scala 3 CI](https://github.com/scala/scala3/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/scala/scala3/actions/workflows/ci.yaml?query=branch%3Amain) [![Join the chat at https://discord.com/invite/scala](https://img.shields.io/discord/632150470000902164)](https://discord.com/invite/scala) +This is the home of the [Scala 3](https://www.scala-lang.org) standard library, compiler, and language spec. + * [Documentation](https://docs.scala-lang.org/scala3/) Try it out @@ -27,4 +29,4 @@ How to Contribute License ======= -Dotty is licensed under the [Apache License Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) +Scala 3 is licensed under the [Apache License Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) \ No newline at end of file diff --git a/bench-run/src/main/scala/dotty/tools/benchmarks/Main.scala b/bench-run/src/main/scala/dotty/tools/benchmarks/Main.scala index ac26e57c7381..5d2d7052ec0a 100644 --- a/bench-run/src/main/scala/dotty/tools/benchmarks/Main.scala +++ b/bench-run/src/main/scala/dotty/tools/benchmarks/Main.scala @@ -58,7 +58,7 @@ object Bench { } def paramsFromFile(file: String): Array[(String, Array[String])] = { - Using(Source.fromFile(file))(_.getLines.toArray).get.map { l => + Using(Source.fromFile(file))(_.getLines().toArray).get.map { l => val Array(param, values) = l split ':' (param, values split ',') } diff --git a/bench/src/main/scala/Benchmarks.scala b/bench/src/main/scala/Benchmarks.scala index ccb778453d91..d97fc7b654f5 100644 --- a/bench/src/main/scala/Benchmarks.scala +++ b/bench/src/main/scala/Benchmarks.scala @@ -91,7 +91,7 @@ object Bench { } def readCompileOptions: Seq[String] = - Using(Source.fromFile(COMPILE_OPTS_FILE))(_.getLines.toSeq).get + Using(Source.fromFile(COMPILE_OPTS_FILE))(_.getLines().toSeq).get } @State(Scope.Benchmark) diff --git a/bin/replQ b/bin/replQ new file mode 100755 index 000000000000..5d0b84c4a229 --- /dev/null +++ b/bin/replQ @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" >& /dev/null && pwd)/.." +. $ROOT/bin/commonQ + +java -Dscala.usejavacp=true -cp $cp dotty.tools.repl.Main -usejavacp "$@" diff --git a/changelogs/3.3.6-RC1.md b/changelogs/3.3.6-RC1.md new file mode 100644 index 000000000000..e51c46523772 --- /dev/null +++ b/changelogs/3.3.6-RC1.md @@ -0,0 +1,252 @@ +# Highlights of the release + +- Partial Fix #21242: Add REPL init script setting [#22206](https://github.com/scala/scala3/pull/22206) +- Update asm to patched 9.7.1 [#22250](https://github.com/scala/scala3/pull/22250) +- REPL: Add back `:silent` command [#22248](https://github.com/scala/scala3/pull/22248) +- Scala 2 forwardport: `-Yprofile-trace` [#19897](https://github.com/scala/scala3/pull/19897) + +# Other changes and fixes + +## Annotations + +- Make sure symbols in annotation trees are fresh before pickling [#22002](https://github.com/scala/scala3/pull/22002) +- Fix Java parsing of annotations on qualified types [#21867](https://github.com/scala/scala3/pull/21867) +- Rename `InlineCopier` to `ConservativeTreeCopier`, use it in `TypeMap`s [#21941](https://github.com/scala/scala3/pull/21941) +- Consider all arguments in Annotations.refersToParamOf [#22001](https://github.com/scala/scala3/pull/22001) + +## Backend + +- Flag class file collision as error [#19332](https://github.com/scala/scala3/pull/19332) +- Backend computes line number from source of position [#21763](https://github.com/scala/scala3/pull/21763) + +## CI + +- In CI, use new standard scala/cla-checker action [#22203](https://github.com/scala/scala3/pull/22203) +- Cancel stale CI executions when CI is re-triggered in the same branch/PR [#21974](https://github.com/scala/scala3/pull/21974) +- Drop support for old experimental in community-build [#21729](https://github.com/scala/scala3/pull/21729) + +## Coverage + +- Fix coverage serialization when encountering macro suspension [#22303](https://github.com/scala/scala3/pull/22303) + +## Dependencies + +- Sbt 1.10.5 (was 1.9.9) [#20157](https://github.com/scala/scala3/pull/20157) +- Scala 2.13.16 (was .15) [#22386](https://github.com/scala/scala3/pull/22386) + +## Desugaring + +- Fix #22051: only trust the type application part for case class unapplies [#22099](https://github.com/scala/scala3/pull/22099) + +## Documentation + +- Fix: typo in documentation for Tuple.zip [#22313](https://github.com/scala/scala3/pull/22313) +- Copyright 2025 [#22326](https://github.com/scala/scala3/pull/22326) +- Documentation only: update Example code linked to obsolete content in macros-spec.md [#22256](https://github.com/scala/scala3/pull/22256) +- Doc fix, removed repeated use of the word with [#21915](https://github.com/scala/scala3/pull/21915) +- Fix typos [#21913](https://github.com/scala/scala3/pull/21913) + +## Enums + +- Add type parameters derived from enum to default param getters of enum cases [#22157](https://github.com/scala/scala3/pull/22157) + +## Extension Methods + +- Add regression test for #22320 [#22321](https://github.com/scala/scala3/pull/22321) +- Tweak ExtensionNullifiedByMember [#22268](https://github.com/scala/scala3/pull/22268) + +# Infrastructure + +- Improve, document and group versioning code in `Build.scala` [#21837](https://github.com/scala/scala3/pull/21837) +- Fix: drop jackson-module-scala from CB [#22353](https://github.com/scala/scala3/pull/22353) +- Fix inductive-implicits benchmark [#22007](https://github.com/scala/scala3/pull/22007) +- Remove tests/pos-with-compiler-cc from VSCode ignored files [#22198](https://github.com/scala/scala3/pull/22198) +- Bump webrick from 1.8.2 to 1.9.1 in /docs/_spec [#22066](https://github.com/scala/scala3/pull/22066) +- Fix Windows OS detection in TraceNameManglingTest [#21937](https://github.com/scala/scala3/pull/21937) +- Don't warn for deprecated Thread.getId() calls in `ChromeTrace` [#21831](https://github.com/scala/scala3/pull/21831) +- Improvement: Remove workaround for Bloop and update docs [#21842](https://github.com/scala/scala3/pull/21842) +- Ensure ChromeTraceTest event timestamps are emitted in correct invervals [#21911](https://github.com/scala/scala3/pull/21911) +- Remove tests/pos-with-compiler-cc [#21956](https://github.com/scala/scala3/pull/21956) +- Shade scalajs.ir under dotty.tools [#21765](https://github.com/scala/scala3/pull/21765) + +## Implicits + +- Rollback constraints in compareAppliedTypeParamRef [#22339](https://github.com/scala/scala3/pull/22339) +- Apply implicit conversion from derived Conversion instance defined as implicit rather than given [#21785](https://github.com/scala/scala3/pull/21785) + +## Imports + +- Allow imports nested in packagings to shadow [#21539](https://github.com/scala/scala3/pull/21539) + +## Inline + +- Fix inline reduction for CaseDef guards with asInstanceOf [#22305](https://github.com/scala/scala3/pull/22305) +- Avoid using the current denotation in NamedType.disambiguate [#21414](https://github.com/scala/scala3/pull/21414) + +## Linting + +- Append instead of prepending import selectors for the current scope when collecting them in CheckUnused [#22314](https://github.com/scala/scala3/pull/22314) +- Allow discarding "Discarded non-Unit" warnings with `: Unit` [#21927](https://github.com/scala/scala3/pull/21927) +- Disallow open modifier on objects [#21922](https://github.com/scala/scala3/pull/21922) +- Only count associated files of direct members of package objects in dropStale [#22190](https://github.com/scala/scala3/pull/22190) + +## Metaprogramming + +- Rethrow SuspendExceptions caught in CodeGen phase [#22009](https://github.com/scala/scala3/pull/22009) +- Extend compiletime.testing.typechecks with certain transform phases [#21185](https://github.com/scala/scala3/pull/21185) + +# Match Types + +- Fix #21841: Check more that an `unapplySeq` on a `NonEmptyTuple` is valid. [#22366](https://github.com/scala/scala3/pull/22366) +- Type avoidance in MT bound inference [#22142](https://github.com/scala/scala3/pull/22142) + + +## Nullability + +- Fix #21619: Refactor NotNullInfo to record every reference which is retracted once. [#21624](https://github.com/scala/scala3/pull/21624) + +## Parser + +- Fix: don't consider `into` as a soft-modifier [#21924](https://github.com/scala/scala3/pull/21924) +- Fix: Allow `as` as an infix type in non context bound types [#21849](https://github.com/scala/scala3/pull/21849) +- Undo patch of double-block apply [#21982](https://github.com/scala/scala3/pull/21982) + +## Pattern Matching + +- (Re-)Drop inaccessible subclasses from refineUsingParent [#21930](https://github.com/scala/scala3/pull/21930) +- Fix #21914: Don't project nested wildcard patterns to nullable [#21934](https://github.com/scala/scala3/pull/21934) +- Fix use of class terms in match analysis [#21848](https://github.com/scala/scala3/pull/21848) +- Fix provablyDisjoint handling enum constants with mixins [#21876](https://github.com/scala/scala3/pull/21876) +- Do not consider uninhabited constructors when performing exhaustive match checking [#21750](https://github.com/scala/scala3/pull/21750) + +## Pickling + +- Avoid orphan param from default arg [#21824](https://github.com/scala/scala3/pull/21824) +- Make sure definition tree has the defined symbol [#21851](https://github.com/scala/scala3/pull/21851) + +## Presentation Compiler + +- Use new infer expected type for singleton complations [#21421](https://github.com/scala/scala3/pull/21421) +- Fix presentation compiler testcases [#22253](https://github.com/scala/scala3/pull/22253) +- Fix match error in keyword completions [#22138](https://github.com/scala/scala3/pull/22138) +- Add test cases project for presentation compiler [#22101](https://github.com/scala/scala3/pull/22101) +- Improvement: Update mtags to 1.4.1 and backport remaining changes [#21859](https://github.com/scala/scala3/pull/21859) +- Backport changes for the presentation compiler from Metals [#21756](https://github.com/scala/scala3/pull/21756) + +## Quotes + +- Fix tupleTypeFromSeq for XXL tuples [#21782](https://github.com/scala/scala3/pull/21782) +- Do not bring forward symbols created in transform and backend phases [#21865](https://github.com/scala/scala3/pull/21865) +- Fix #20471: owners of top-level symbols in cached quoted code being incorrect [#21945](https://github.com/scala/scala3/pull/21945) +- Fix for macro annotation that resolves macro-based implicit crashing the compiler [#20353](https://github.com/scala/scala3/pull/20353) +- Allow macro annotations to recover from suspension [#21969](https://github.com/scala/scala3/pull/21969) + +# REPL + +- REPL: JLine: follow recommendation to use JNI, not JNA; also JLine 3.27.1 (was 3.27.0) [#22205](https://github.com/scala/scala3/pull/22205) +- Fix #9879: allow top-level opaque type definitions in REPL [#21753](https://github.com/scala/scala3/pull/21753) + +## Reflection + +- Do not return java outline dummy constructor in `primaryConstructor` [#22104](https://github.com/scala/scala3/pull/22104) + +## Reporting + +- Normalise the types for Type Mismatch Error (E007) [#22337](https://github.com/scala/scala3/pull/22337) +- Do not warn about expected missing positions in quotes.reflect.Symbol [#21677](https://github.com/scala/scala3/pull/21677) +- Add missing error messages to asserts in QuotesImpl [#21852](https://github.com/scala/scala3/pull/21852) +- Don't point to the compiler backlog when a compiler plugin phase crashes [#21887](https://github.com/scala/scala3/pull/21887) +- Nowarn extension matching nonpublic member [#21825](https://github.com/scala/scala3/pull/21825) +- Fix: better error messages when an enum derives from AnyVal [#22236](https://github.com/scala/scala3/pull/22236) + +## Scaladoc + +- [ISSUE-#19208] If scaladocs on file protocol don't do SPA routing. [#22013](https://github.com/scala/scala3/pull/22013) +- Fix scaladoc graph highlight background color in dark mode [#21814](https://github.com/scala/scala3/pull/21814) + +## SemanticDB + +- Extract semanticDB for lifted definitions [#21856](https://github.com/scala/scala3/pull/21856) + +## Specification + +- Align the spec to allow the marker [#22323](https://github.com/scala/scala3/pull/22323) +- Fix #20271: Bring for comprehension spec up to date. [#21702](https://github.com/scala/scala3/pull/21702) +- Spec: Integrate the specification for match types. [#22164](https://github.com/scala/scala3/pull/22164) + +## Settings + +- Limit exposure to ConcurrentModificationException when sys props are replaced or mutated [#22180](https://github.com/scala/scala3/pull/22180) + +# Tooling + +- Connect the input to the compiler in `sbt` [#22336](https://github.com/scala/scala3/pull/22336) +- Ensure to escape characters before constructing JSON profile trace [#21872](https://github.com/scala/scala3/pull/21872) +- Fix: correctly print literal types in the refined printer [#22351](https://github.com/scala/scala3/pull/22351) + +## Transform + +- Attempt to beta reduce only if parameters and arguments have same shape [#21970](https://github.com/scala/scala3/pull/21970) +- Fix enclosingClass from returning refinement classes [#21411](https://github.com/scala/scala3/pull/21411) +- Fix #22226: Use `classOf[BoxedUnit]` for Unit array in `ArrayConstructors`. [#22238](https://github.com/scala/scala3/pull/22238) +- Fix typo in `allTermArguments` [#21836](https://github.com/scala/scala3/pull/21836) + +## Typer + +- Fixes for isLegalPrefix change [#22241](https://github.com/scala/scala3/pull/22241) +- Resolve name when named imp is behind wild imps [#21888](https://github.com/scala/scala3/pull/21888) +- Do not crash when typing a closure with unknown type, since it can occur for erroneous input [#21178](https://github.com/scala/scala3/pull/21178) +- Allow autotupling if fn's param is a type param [#21741](https://github.com/scala/scala3/pull/21741) +- Fix ctx implicits under case unapplySeq [#21748](https://github.com/scala/scala3/pull/21748) +- Avoid using ExplainingTypeComparer in regular code [#21583](https://github.com/scala/scala3/pull/21583) +- Avoid erasure/preErasure issues around Any in transformIsInstanceOf [#21647](https://github.com/scala/scala3/pull/21647) +- Remove tvars introduced while testing normalizedCompatible [#21466](https://github.com/scala/scala3/pull/21466) + +## Miscellaneous + +- Replace deprecated AnyRefMap [#22230](https://github.com/scala/scala3/pull/22230) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to git shortlog -sn --no-merges 3.3.5..3.3.6-RC1 these are: + +``` + 63 Tomasz Godzik + 30 Dale Wijnand + 26 Wojciech Mazur + 20 noti0na1 + 15 Hamza Remmal + 15 Matt Bovel + 10 Jan Chyb + 9 Kacper Korban + 6 Som Snytt + 4 Sébastien Doeraene + 3 HarrisL2 + 3 Jamie Thompson + 3 Martin Odersky + 3 Seth Tisue + 3 kasiaMarek + 2 Friendseeker + 2 João Ferreira + 2 Kenji Yoshida + 2 Natsu Kagami + 2 Pascal Weisenburger + 2 dependabot[bot] + 1 Adrien Piquerez + 1 Alden Torres + 1 Alexander + 1 Georgi Krastev + 1 Guillaume Martres + 1 Jentsch + 1 Lunfu Zhong + 1 Mathias + 1 Michał Pałka + 1 Oleg Zenzin + 1 Oliver Bračevac + 1 friendseeker + 1 philippus + 1 tgodzik + 1 xiaoshihou +``` diff --git a/changelogs/3.3.6-RC2.md b/changelogs/3.3.6-RC2.md new file mode 100644 index 000000000000..28f9da5f3eea --- /dev/null +++ b/changelogs/3.3.6-RC2.md @@ -0,0 +1,13 @@ +# Reverted changes + +- Do not lift annotation arguments [#22035](https://github.com/scala/scala3/pull/22035) and [#22046](https://github.com/scala/scala3/pull/22046) + +# Contributors + +Thank you to all theq contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.6-RC1..3.3.6-RC2` these are: + +``` + 3 Tomasz Godzik +``` diff --git a/changelogs/3.3.6-RC3.md b/changelogs/3.3.6-RC3.md new file mode 100644 index 000000000000..4dd8e2dfd223 --- /dev/null +++ b/changelogs/3.3.6-RC3.md @@ -0,0 +1,16 @@ +# Backported fixes + +- classfile reader: handle JDK 9+ constant types in constant pool [#19533](https://github.com/scala/scala3/pull/19533) +- Update asm to 9.8.0 [#23004](https://github.com/scala/scala3/pull/23004) + +# Contributors + +Thank you to all theq contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.6-RC2..3.3.6-RC3` these are: + +``` + 2 Seth Tisue + 1 Tomasz Godzik + 1 philippus +``` diff --git a/changelogs/3.3.6.md b/changelogs/3.3.6.md new file mode 100644 index 000000000000..189911d99178 --- /dev/null +++ b/changelogs/3.3.6.md @@ -0,0 +1,253 @@ +# Highlights of the release + +- REPL: Add REPL init script setting [#22206](https://github.com/scala/scala3/pull/22206) +- Update asm to patched 9.8.0 to support new JDK versions [#23004](https://github.com/scala/scala3/pull/23004) +- classfile reader: handle JDK 9+ constant types in constant pool [#19533](https://github.com/scala/scala3/pull/19533) +- REPL: Add back `:silent` command [#22248](https://github.com/scala/scala3/pull/22248) +- Scala 2 forward port: `-Yprofile-trace` [#19897](https://github.com/scala/scala3/pull/19897) + +# Other changes and fixes + +## Annotations + +- Make sure symbols in annotation trees are fresh before pickling [#22002](https://github.com/scala/scala3/pull/22002) +- Fix Java parsing of annotations on qualified types [#21867](https://github.com/scala/scala3/pull/21867) +- Rename `InlineCopier` to `ConservativeTreeCopier`, use it in `TypeMap`s [#21941](https://github.com/scala/scala3/pull/21941) +- Consider all arguments in Annotations.refersToParamOf [#22001](https://github.com/scala/scala3/pull/22001) + +## Backend + +- Flag class file collision as error [#19332](https://github.com/scala/scala3/pull/19332) +- Backend computes line number from source of position [#21763](https://github.com/scala/scala3/pull/21763) + +## CI + +- In CI, use new standard scala/cla-checker action [#22203](https://github.com/scala/scala3/pull/22203) +- Cancel stale CI executions when CI is re-triggered in the same branch/PR [#21974](https://github.com/scala/scala3/pull/21974) +- Drop support for old experimental in community-build [#21729](https://github.com/scala/scala3/pull/21729) + +## Coverage + +- Fix coverage serialization when encountering macro suspension [#22303](https://github.com/scala/scala3/pull/22303) + +## Dependencies + +- Sbt 1.10.5 (was 1.9.9) [#20157](https://github.com/scala/scala3/pull/20157) +- Scala 2.13.16 (was .15) [#22386](https://github.com/scala/scala3/pull/22386) + +## Desugaring + +- Fix #22051: only trust the type application part for case class unapplies [#22099](https://github.com/scala/scala3/pull/22099) + +## Documentation + +- Fix: typo in documentation for Tuple.zip [#22313](https://github.com/scala/scala3/pull/22313) +- Copyright 2025 [#22326](https://github.com/scala/scala3/pull/22326) +- Documentation only: update Example code linked to obsolete content in macros-spec.md [#22256](https://github.com/scala/scala3/pull/22256) +- Doc fix, removed repeated use of the word with [#21915](https://github.com/scala/scala3/pull/21915) +- Fix typos [#21913](https://github.com/scala/scala3/pull/21913) + +## Enums + +- Add type parameters derived from enum to default param getters of enum cases [#22157](https://github.com/scala/scala3/pull/22157) + +## Extension Methods + +- Add regression test for #22320 [#22321](https://github.com/scala/scala3/pull/22321) +- Tweak ExtensionNullifiedByMember [#22268](https://github.com/scala/scala3/pull/22268) + +# Infrastructure + +- Improve, document and group versioning code in `Build.scala` [#21837](https://github.com/scala/scala3/pull/21837) +- Fix: drop jackson-module-scala from CB [#22353](https://github.com/scala/scala3/pull/22353) +- Fix inductive-implicits benchmark [#22007](https://github.com/scala/scala3/pull/22007) +- Remove tests/pos-with-compiler-cc from VSCode ignored files [#22198](https://github.com/scala/scala3/pull/22198) +- Bump webrick from 1.8.2 to 1.9.1 in /docs/_spec [#22066](https://github.com/scala/scala3/pull/22066) +- Fix Windows OS detection in TraceNameManglingTest [#21937](https://github.com/scala/scala3/pull/21937) +- Don't warn for deprecated Thread.getId() calls in `ChromeTrace` [#21831](https://github.com/scala/scala3/pull/21831) +- Improvement: Remove workaround for Bloop and update docs [#21842](https://github.com/scala/scala3/pull/21842) +- Ensure ChromeTraceTest event timestamps are emitted in correct invervals [#21911](https://github.com/scala/scala3/pull/21911) +- Remove tests/pos-with-compiler-cc [#21956](https://github.com/scala/scala3/pull/21956) +- Shade scalajs.ir under dotty.tools [#21765](https://github.com/scala/scala3/pull/21765) + +## Implicits + +- Rollback constraints in compareAppliedTypeParamRef [#22339](https://github.com/scala/scala3/pull/22339) +- Apply implicit conversion from derived Conversion instance defined as implicit rather than given [#21785](https://github.com/scala/scala3/pull/21785) + +## Imports + +- Allow imports nested in packagings to shadow [#21539](https://github.com/scala/scala3/pull/21539) + +## Inline + +- Fix inline reduction for CaseDef guards with asInstanceOf [#22305](https://github.com/scala/scala3/pull/22305) +- Avoid using the current denotation in NamedType.disambiguate [#21414](https://github.com/scala/scala3/pull/21414) + +## Linting + +- Append instead of prepending import selectors for the current scope when collecting them in CheckUnused [#22314](https://github.com/scala/scala3/pull/22314) +- Allow discarding "Discarded non-Unit" warnings with `: Unit` [#21927](https://github.com/scala/scala3/pull/21927) +- Disallow open modifier on objects [#21922](https://github.com/scala/scala3/pull/21922) +- Only count associated files of direct members of package objects in dropStale [#22190](https://github.com/scala/scala3/pull/22190) + +## Metaprogramming + +- Rethrow SuspendExceptions caught in CodeGen phase [#22009](https://github.com/scala/scala3/pull/22009) +- Extend compiletime.testing.typechecks with certain transform phases [#21185](https://github.com/scala/scala3/pull/21185) + +# Match Types + +- Fix #21841: Check more that an `unapplySeq` on a `NonEmptyTuple` is valid. [#22366](https://github.com/scala/scala3/pull/22366) +- Type avoidance in MT bound inference [#22142](https://github.com/scala/scala3/pull/22142) + + +## Nullability + +- Fix #21619: Refactor NotNullInfo to record every reference which is retracted once. [#21624](https://github.com/scala/scala3/pull/21624) + +## Parser + +- Fix: don't consider `into` as a soft-modifier [#21924](https://github.com/scala/scala3/pull/21924) +- Fix: Allow `as` as an infix type in non context bound types [#21849](https://github.com/scala/scala3/pull/21849) +- Undo patch of double-block apply [#21982](https://github.com/scala/scala3/pull/21982) + +## Pattern Matching + +- (Re-)Drop inaccessible subclasses from refineUsingParent [#21930](https://github.com/scala/scala3/pull/21930) +- Fix #21914: Don't project nested wildcard patterns to nullable [#21934](https://github.com/scala/scala3/pull/21934) +- Fix use of class terms in match analysis [#21848](https://github.com/scala/scala3/pull/21848) +- Fix provablyDisjoint handling enum constants with mixins [#21876](https://github.com/scala/scala3/pull/21876) +- Do not consider uninhabited constructors when performing exhaustive match checking [#21750](https://github.com/scala/scala3/pull/21750) + +## Pickling + +- Avoid orphan param from default arg [#21824](https://github.com/scala/scala3/pull/21824) +- Make sure definition tree has the defined symbol [#21851](https://github.com/scala/scala3/pull/21851) + +## Presentation Compiler + +- Use new infer expected type for singleton complations [#21421](https://github.com/scala/scala3/pull/21421) +- Fix presentation compiler testcases [#22253](https://github.com/scala/scala3/pull/22253) +- Fix match error in keyword completions [#22138](https://github.com/scala/scala3/pull/22138) +- Add test cases project for presentation compiler [#22101](https://github.com/scala/scala3/pull/22101) +- Improvement: Update mtags to 1.4.1 and backport remaining changes [#21859](https://github.com/scala/scala3/pull/21859) +- Backport changes for the presentation compiler from Metals [#21756](https://github.com/scala/scala3/pull/21756) + +## Quotes + +- Fix tupleTypeFromSeq for XXL tuples [#21782](https://github.com/scala/scala3/pull/21782) +- Do not bring forward symbols created in transform and backend phases [#21865](https://github.com/scala/scala3/pull/21865) +- Fix #20471: owners of top-level symbols in cached quoted code being incorrect [#21945](https://github.com/scala/scala3/pull/21945) +- Fix for macro annotation that resolves macro-based implicit crashing the compiler [#20353](https://github.com/scala/scala3/pull/20353) +- Allow macro annotations to recover from suspension [#21969](https://github.com/scala/scala3/pull/21969) + +# REPL + +- REPL: JLine: follow recommendation to use JNI, not JNA; also JLine 3.27.1 (was 3.27.0) [#22205](https://github.com/scala/scala3/pull/22205) +- Fix #9879: allow top-level opaque type definitions in REPL [#21753](https://github.com/scala/scala3/pull/21753) + +## Reflection + +- Do not return java outline dummy constructor in `primaryConstructor` [#22104](https://github.com/scala/scala3/pull/22104) + +## Reporting + +- Normalise the types for Type Mismatch Error (E007) [#22337](https://github.com/scala/scala3/pull/22337) +- Do not warn about expected missing positions in quotes.reflect.Symbol [#21677](https://github.com/scala/scala3/pull/21677) +- Add missing error messages to asserts in QuotesImpl [#21852](https://github.com/scala/scala3/pull/21852) +- Don't point to the compiler backlog when a compiler plugin phase crashes [#21887](https://github.com/scala/scala3/pull/21887) +- Nowarn extension matching nonpublic member [#21825](https://github.com/scala/scala3/pull/21825) +- Fix: better error messages when an enum derives from AnyVal [#22236](https://github.com/scala/scala3/pull/22236) + +## Scaladoc + +- [ISSUE-#19208] If scaladocs on file protocol don't do SPA routing. [#22013](https://github.com/scala/scala3/pull/22013) +- Fix scaladoc graph highlight background color in dark mode [#21814](https://github.com/scala/scala3/pull/21814) + +## SemanticDB + +- Extract semanticDB for lifted definitions [#21856](https://github.com/scala/scala3/pull/21856) + +## Specification + +- Align the spec to allow the marker [#22323](https://github.com/scala/scala3/pull/22323) +- Fix #20271: Bring for comprehension spec up to date. [#21702](https://github.com/scala/scala3/pull/21702) +- Spec: Integrate the specification for match types. [#22164](https://github.com/scala/scala3/pull/22164) + +## Settings + +- Limit exposure to ConcurrentModificationException when sys props are replaced or mutated [#22180](https://github.com/scala/scala3/pull/22180) + +# Tooling + +- Connect the input to the compiler in `sbt` [#22336](https://github.com/scala/scala3/pull/22336) +- Ensure to escape characters before constructing JSON profile trace [#21872](https://github.com/scala/scala3/pull/21872) +- Fix: correctly print literal types in the refined printer [#22351](https://github.com/scala/scala3/pull/22351) + +## Transform + +- Attempt to beta reduce only if parameters and arguments have same shape [#21970](https://github.com/scala/scala3/pull/21970) +- Fix enclosingClass from returning refinement classes [#21411](https://github.com/scala/scala3/pull/21411) +- Fix #22226: Use `classOf[BoxedUnit]` for Unit array in `ArrayConstructors`. [#22238](https://github.com/scala/scala3/pull/22238) +- Fix typo in `allTermArguments` [#21836](https://github.com/scala/scala3/pull/21836) + +## Typer + +- Fixes for isLegalPrefix change [#22241](https://github.com/scala/scala3/pull/22241) +- Resolve name when named imp is behind wild imps [#21888](https://github.com/scala/scala3/pull/21888) +- Do not crash when typing a closure with unknown type, since it can occur for erroneous input [#21178](https://github.com/scala/scala3/pull/21178) +- Allow autotupling if fn's param is a type param [#21741](https://github.com/scala/scala3/pull/21741) +- Fix ctx implicits under case unapplySeq [#21748](https://github.com/scala/scala3/pull/21748) +- Avoid using ExplainingTypeComparer in regular code [#21583](https://github.com/scala/scala3/pull/21583) +- Avoid erasure/preErasure issues around Any in transformIsInstanceOf [#21647](https://github.com/scala/scala3/pull/21647) +- Remove tvars introduced while testing normalizedCompatible [#21466](https://github.com/scala/scala3/pull/21466) + +## Miscellaneous + +- Replace deprecated AnyRefMap [#22230](https://github.com/scala/scala3/pull/22230) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to git shortlog -sn --no-merges 3.3.5..3.3.6-RC1 these are: + +``` + 63 Tomasz Godzik + 30 Dale Wijnand + 26 Wojciech Mazur + 20 noti0na1 + 15 Hamza Remmal + 15 Matt Bovel + 10 Jan Chyb + 9 Kacper Korban + 6 Som Snytt + 4 Sébastien Doeraene + 3 HarrisL2 + 3 Jamie Thompson + 3 Martin Odersky + 3 Seth Tisue + 3 kasiaMarek + 2 Friendseeker + 2 João Ferreira + 2 Kenji Yoshida + 2 Natsu Kagami + 2 Pascal Weisenburger + 2 dependabot[bot] + 1 Adrien Piquerez + 1 Alden Torres + 1 Alexander + 1 Georgi Krastev + 1 Guillaume Martres + 1 Jentsch + 1 Lunfu Zhong + 1 Mathias + 1 Michał Pałka + 1 Oleg Zenzin + 1 Oliver Bračevac + 1 friendseeker + 1 philippus + 1 tgodzik + 1 xiaoshihou +``` diff --git a/changelogs/3.3.7-RC1.md b/changelogs/3.3.7-RC1.md new file mode 100644 index 000000000000..1a05d824ab95 --- /dev/null +++ b/changelogs/3.3.7-RC1.md @@ -0,0 +1,218 @@ +# Highlights of the release + +- Warn if interpolator uses toString [#20578](https://github.com/scala/scala3/pull/20578) +- Fixes #15736 blocking Scala 3 on Android [#22632](https://github.com/scala/scala3/pull/22632) +- Implement :jar (deprecate :require) [#22343](https://github.com/scala/scala3/pull/22343) +- In selector check, prefix of reference must match import qualifier [#20894](https://github.com/scala/scala3/pull/20894) +- Fix #21242: Add REPL flag to quit after evaluating init script [#22636](https://github.com/scala/scala3/pull/22636) +- Warn if implicit default shadows given [#23559](https://github.com/scala/scala3/pull/23559) + +# Other changes and fixes + +## Annotations + +- Approximate annotated types in `wildApprox` [#22893](https://github.com/scala/scala3/pull/22893) +- Fix copy of annotation on @main methods [#22582](https://github.com/scala/scala3/pull/22582) + +## CI + +- Disable Cats flaky tests [#23007](https://github.com/scala/scala3/pull/23007) + +## Developer Experience + +- Add support for running the `test` sub-command with the bisect script [#22796](https://github.com/scala/scala3/pull/22796) + +## Documentation + +- Improve the usage of inclusive language [#22360](https://github.com/scala/scala3/pull/22360) +- Update indentation.md to fix a typo [#23505](https://github.com/scala/scala3/pull/23505) + +## Enums + +- Make hashcode of enum items stable [#23218](https://github.com/scala/scala3/pull/23218) + +## Erasure + +- Add regression test for #23616 [#23623](https://github.com/scala/scala3/pull/23623) +- Disallow context function types as value-class parameters [#23015](https://github.com/scala/scala3/pull/23015) +- Handle type aliases in contextFunctionResultTypeAfter [#21517](https://github.com/scala/scala3/pull/21517) +- Align erasure of `Array[Nothing]` and `Array[Null]` with Scala 2 [#22517](https://github.com/scala/scala3/pull/22517) + +## Experimental: Erased definitions + +- Erased fields are not nullable [#23311](https://github.com/scala/scala3/pull/23311) + +## Implicits + +- Refine implicit search fallbacks for better ClassTag handling [#23532](https://github.com/scala/scala3/pull/23532) +- Fix #20335: Try extensions for arguments with type mismatch error [#23212](https://github.com/scala/scala3/pull/23212) + +## Inline + +- Fix Symbol.info remapping in TreeTypeMap [#23432](https://github.com/scala/scala3/pull/23432) + +## Lambda Lift + +- Fix: treat static vals as enclosures in lambdalift [#22452](https://github.com/scala/scala3/pull/22452) +- Fix: record calls to constructors in lambdaLift [#22487](https://github.com/scala/scala3/pull/22487) + +## Linting + +- Check OrType in interpolated toString lint [#23365](https://github.com/scala/scala3/pull/23365) +- Consider setter of effectively private var [#23211](https://github.com/scala/scala3/pull/23211) +- Nowarn receiver of extension taking params [#23351](https://github.com/scala/scala3/pull/23351) +- Dealias when looking into imports [#22889](https://github.com/scala/scala3/pull/22889) +- Revert unconditional lint of Inlined expansion [#22815](https://github.com/scala/scala3/pull/22815) +- Warn unused member of anonymous class [#22729](https://github.com/scala/scala3/pull/22729) +- No warning for parameter of overriding method [#22757](https://github.com/scala/scala3/pull/22757) +- Lazy val def member is pattern var [#22750](https://github.com/scala/scala3/pull/22750) +- Ignore params to default arg getters [#22749](https://github.com/scala/scala3/pull/22749) +- Restore resolving prefixes of implicit Ident [#22751](https://github.com/scala/scala3/pull/22751) +- Exclude synthetic this.m, Any.m from import lookup [#22695](https://github.com/scala/scala3/pull/22695) +- Nowarn public implicit val class params [#22664](https://github.com/scala/scala3/pull/22664) +- Don't warn retainedBody [#22510](https://github.com/scala/scala3/pull/22510) +- Suppress spurious Suppression [#22383](https://github.com/scala/scala3/pull/22383) +- Handle Typeable [#22663](https://github.com/scala/scala3/pull/22663) +- CheckUnused checks span.exists before testing its parts [#22504](https://github.com/scala/scala3/pull/22504) +- Process Export for unused check [#22984](https://github.com/scala/scala3/pull/22984) +- Enclosing package p.q not visible as q [#23069](https://github.com/scala/scala3/pull/23069) +- Remove premature caching of lookups for unused lint [#22982](https://github.com/scala/scala3/pull/22982) +- Improve checking LHS of Assign [#22977](https://github.com/scala/scala3/pull/22977) +- Improve Unit ascription escape hatch [#23147](https://github.com/scala/scala3/pull/23147) +- Mention extension in unused param warning [#23132](https://github.com/scala/scala3/pull/23132) +- Dealias for unused param check [#23256](https://github.com/scala/scala3/pull/23256) +- Take inferred or explicit refinement result for unused check [#23325](https://github.com/scala/scala3/pull/23325) +- Add accessible check for import usage [#23348](https://github.com/scala/scala3/pull/23348) +- Use result of lambda type of implicit in CheckUnused [#23497](https://github.com/scala/scala3/pull/23497) + +## Match Types + +- Handle NoType in TypeComparer.disjointnessBoundary [#21520](https://github.com/scala/scala3/pull/21520) +- Fix: #23261 Distinguish 0.0 and -0.0 in ConstantType match types [#23265](https://github.com/scala/scala3/pull/23265) + +## Metaprogramming + +- Add a check for correct Array shape in quotes.reflect.ClassOfConstant [#22033](https://github.com/scala/scala3/pull/22033) + +## Opaque Types + +- Fix stack overflow errors when generating opaque type proxies [#22479](https://github.com/scala/scala3/pull/22479) + +## Optional Braces + +- Correctly detect colon lambda eol indent for optional brace of argument [#22477](https://github.com/scala/scala3/pull/22477) + +## Overloading + +- Fail compilation if multiple conflicting top-level private defs/vals are in the same package [#22759](https://github.com/scala/scala3/pull/22759) + +## Parser + +- Allow observing an indent after conditional [#22611](https://github.com/scala/scala3/pull/22611) +- No outdent at eof [#22435](https://github.com/scala/scala3/pull/22435) +- Fix annotations being not expected in the middle of an array type by java parser [#22391](https://github.com/scala/scala3/pull/22391) +- Fix incorrect warning with -no-indent [#23216](https://github.com/scala/scala3/pull/23216) + +## Pattern Matching + +- Fix issue in lazy symbol completion or bug in nested classfile parser [#23634](https://github.com/scala/scala3/pull/23634) +- Fix existing GADT constraints with introduced pattern-bound symbols [#22928](https://github.com/scala/scala3/pull/22928) +- Avoid crash in uninhab check in Space [#22601](https://github.com/scala/scala3/pull/22601) + +## Pickling + +- Try to handle SkolemTypes in SingletonTypeTree during pickling [#23236](https://github.com/scala/scala3/pull/23236) + +## Positions + +- Compare span points in pathTo to determine best span [#23581](https://github.com/scala/scala3/pull/23581) + +## Presentation Compiler + +- Fix: Fix extracting refinements from intersection types in dynamic select hovers [#23640](https://github.com/scala/scala3/pull/23640) +- Completions for requests just before string [#22894](https://github.com/scala/scala3/pull/22894) +- Add enum type param support in sourceSymbol [#18603](https://github.com/scala/scala3/pull/18603) +- Use untpd.Tree instead of tpd.Tree for SelectionRangeProvider [#22702](https://github.com/scala/scala3/pull/22702) +- Fix: handle multiple params lists in for infer type [#23197](https://github.com/scala/scala3/pull/23197) +- Fix completion mode filtering + optimize scopeCompletions [#23172](https://github.com/scala/scala3/pull/23172) +- Add selection ranges for more names [#23257](https://github.com/scala/scala3/pull/23257) +- Add inlay hints for by-name parameters [#23283](https://github.com/scala/scala3/pull/23283) +- Add jpath to VirtualFile (for pc) [#23203](https://github.com/scala/scala3/pull/23203) + +## Quotes + +- Fix issue with static `this` references erroring in quoted code [#22618](https://github.com/scala/scala3/pull/22618) +- Skip splice level checking for symbols [#22782](https://github.com/scala/scala3/pull/22782) +- Fix stale top level synthetic package object being used in later runs [#23464](https://github.com/scala/scala3/pull/23464) + +## REPL + +- REPL: JLine 3.29.0 (was 3.27.1) [#22679](https://github.com/scala/scala3/pull/22679) +- Repl: emit warning for the `:sh` command [#22694](https://github.com/scala/scala3/pull/22694) + +## Reflection + +- Fix regression: do not approximate prefixes when using memberType in reflect API [#22448](https://github.com/scala/scala3/pull/22448) +- Forbid `StringConstant(null)` [#23064](https://github.com/scala/scala3/pull/23064) +- Quotes reflect: sort the typeMembers output list and filter out non-members [#22876](https://github.com/scala/scala3/pull/22876) + +## Reporting + +- Register nowarn when inlining [#22682](https://github.com/scala/scala3/pull/22682) +- Filter help renders box border [#22434](https://github.com/scala/scala3/pull/22434) +- Fix incorrect warning on type ascription for backquoted identifiers [#23088](https://github.com/scala/scala3/pull/23088) +- Add an explainer to the DoubleDefinition error [#23470](https://github.com/scala/scala3/pull/23470) + +## Scaladoc + +- Encode path of class [#23503](https://github.com/scala/scala3/pull/23503) + +## Settings + +- Chore: filter allowed source versions by import and by settings [#23215](https://github.com/scala/scala3/pull/23215) + +## Testing framework + +- Revert dubious retry in vulpix [#21801](https://github.com/scala/scala3/pull/21801) + +## Transform + +- Check only stable qual for import prefix [#22633](https://github.com/scala/scala3/pull/22633) +- Warn trivial recursion with module prefix [#23278](https://github.com/scala/scala3/pull/23278) + +## Tuples + +- Normalize tuple types in var args seq literals and classOf instances [#23465](https://github.com/scala/scala3/pull/23465) + +## Typer + +- Generalize "Don't approximate a type using Nothing as prefix" [#23628](https://github.com/scala/scala3/pull/23628) +- Don't approximate a type using `Nothing` as prefix [#23531](https://github.com/scala/scala3/pull/23531) +- Tighten condition to preserve denotation in IntegrateMap [#23060](https://github.com/scala/scala3/pull/23060) +- Disallow context bounds in type lambdas [#22659](https://github.com/scala/scala3/pull/22659) +- Fix #22724: Revert the PolyType case in #21744 [#22820](https://github.com/scala/scala3/pull/22820) +- Fix isGenericArrayElement for higher-kinded types [#22938](https://github.com/scala/scala3/pull/22938) +- Revert lambda cleanup [#22697](https://github.com/scala/scala3/pull/22697) +- Constructor companion gets privateWithin [#22627](https://github.com/scala/scala3/pull/22627) +- Add regression test for #22076 [#22602](https://github.com/scala/scala3/pull/22602) +- Constructor proxy is restricted if class is protected [#22563](https://github.com/scala/scala3/pull/22563) +- Check if a prefix is valid before selecting from a type [#22368](https://github.com/scala/scala3/pull/22368) +- Preserve hard unions in widenSingletons [#22369](https://github.com/scala/scala3/pull/22369) +- Revert recent changes to opaque type proxy generation [#23059](https://github.com/scala/scala3/pull/23059) +- Compare TypeVar and TypeParamRef in mergeRefinedOrApplied [#23045](https://github.com/scala/scala3/pull/23045) +- Only keep denotation for methods in IntegrateMap [#23226](https://github.com/scala/scala3/pull/23226) +- Tighten condition when to do SAM type conversion [#23246](https://github.com/scala/scala3/pull/23246) +- Revert "Make overload pruning based on result types less aggressive (#21744)" in main [#23331](https://github.com/scala/scala3/pull/23331) +- Fix #22922: Add TypeParamRef handling in isSingletonBounded [#23501](https://github.com/scala/scala3/pull/23501) +- Fix this references everywhere in dependent function types [#23514](https://github.com/scala/scala3/pull/23514) +- More careful ClassTag instantiation [#23659](https://github.com/scala/scala3/pull/23659) +- Use more context for implicit search only if no default argument [#23664](https://github.com/scala/scala3/pull/23664) + +## JDK + +- Check path of module prefix for tailrec [#23491](https://github.com/scala/scala3/pull/23491) + +## Releases + +- [CI] Switch releasing to Sontype Central instead of legacy Sonatype OSS [#23290](https://github.com/scala/scala3/pull/23290) + diff --git a/changelogs/3.3.7-RC2.md b/changelogs/3.3.7-RC2.md new file mode 100644 index 000000000000..dcee5f9ab7bd --- /dev/null +++ b/changelogs/3.3.7-RC2.md @@ -0,0 +1,19 @@ +# Backported fixes + +- Backport: Make coverage more similar to the one in Scala 2 [#23955](https://github.com/scala/scala3/pull/23955) +- Backport: latest Scala Presentation Improvements to 3.3.7 [#23945](https://github.com/scala/scala3/pull/23945). This includes [X-Ray Inlay Hints](https://github.com/scala/scala3/pull/23891), [using completions](https://github.com/scala/scala3/pull/23647), [do not add \[\] after `derives`](https://github.com/scala/scala3/pull/23811). +- Revert: "Check exhaustivity of any case class" to 3.3 LTS [#23943](https://github.com/scala/scala3/pull/23943) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.7-RC1..3.3.7-RC2` these are: + +``` + 2 Tomasz Godzik + 1 Jan Chyb + 1 Vadim Chelyshov + 1 Zieliński Patryk + 1 vder +``` \ No newline at end of file diff --git a/changelogs/3.3.7.md b/changelogs/3.3.7.md new file mode 100644 index 000000000000..87f4220ef16c --- /dev/null +++ b/changelogs/3.3.7.md @@ -0,0 +1,283 @@ +# Highlights of the release + +- Warn a standard interpolator used toString on a reference type with `-Wtostring-interpolated` [#20578](https://github.com/scala/scala3/pull/20578) +- Unblock Scala 3 on Android [#22632](https://github.com/scala/scala3/pull/22632) +- Implement :jar (deprecate :require) in REPL [#22343](https://github.com/scala/scala3/pull/22343) +- Linting rework: In selector check, prefix of reference must match import qualifier [#20894](https://github.com/scala/scala3/pull/20894) +- Add REPL flag to quit after evaluating init script [#22636](https://github.com/scala/scala3/pull/22636) +- Warn if implicit default shadows given with `-Wrecurse-with-default` [#23559](https://github.com/scala/scala3/pull/23559) + +# Other changes and fixes + +## Annotations + +- Approximate annotated types in `wildApprox` [#22893](https://github.com/scala/scala3/pull/22893) +- Fix copy of annotation on @main methods [#22582](https://github.com/scala/scala3/pull/22582) + +## Coverage + +- Make coverage more similar to the one in Scala 2 [#23722](https://github.com/scala/scala3/pull/23722) + +## CI + +- Disable Cats flaky tests [#23007](https://github.com/scala/scala3/pull/23007) +- Switch releasing to Sontype Central instead of legacy Sonatype OSS [#23290](https://github.com/scala/scala3/pull/23290) +- Revert dubious retry in vulpix [#21801](https://github.com/scala/scala3/pull/21801) + +## Developer Experience + +- Add support for running the `test` sub-command with the bisect script [#22796](https://github.com/scala/scala3/pull/22796) + +## Documentation + +- Improve the usage of inclusive language [#22360](https://github.com/scala/scala3/pull/22360) +- Update indentation.md to fix a typo [#23505](https://github.com/scala/scala3/pull/23505) + +## Enums + +- Make hashcode of enum items stable [#23218](https://github.com/scala/scala3/pull/23218) + +## Erasure + +- Add regression test for [#23616](https://github.com/scala/scala3/issues/23616) [#23623](https://github.com/scala/scala3/pull/23623) +- Disallow context function types as value-class parameters to avoid crashes [#23015](https://github.com/scala/scala3/pull/23015) +- Handle type aliases in contextFunctionResultTypeAfter [#21517](https://github.com/scala/scala3/pull/21517) +- Align erasure of `Array[Nothing]` and `Array[Null]` with Scala 2 [#22517](https://github.com/scala/scala3/pull/22517) + +## Experimental: Erased definitions + +- Erased fields are not nullable [#23311](https://github.com/scala/scala3/pull/23311) + +## Implicits + +- Refine implicit search fallbacks for better ClassTag handling [#23532](https://github.com/scala/scala3/pull/23532) +- Try extensions for arguments with type mismatch error [#23212](https://github.com/scala/scala3/pull/23212) + +## Inline + +- Fix Symbol.info remapping in TreeTypeMap [#23432](https://github.com/scala/scala3/pull/23432) + +## Lambda Lift + +- Fix: treat static vals as enclosures in lambdalift [#22452](https://github.com/scala/scala3/pull/22452) +- Fix: record calls to constructors in lambdaLift [#22487](https://github.com/scala/scala3/pull/22487) + +## Linting + +- Check OrType in interpolated toString lint [#23365](https://github.com/scala/scala3/pull/23365) +- Consider setter of effectively private var [#23211](https://github.com/scala/scala3/pull/23211) +- Nowarn receiver of extension taking params [#23351](https://github.com/scala/scala3/pull/23351) +- Dealias when looking into imports [#22889](https://github.com/scala/scala3/pull/22889) +- Revert unconditional lint of Inlined expansion [#22815](https://github.com/scala/scala3/pull/22815) +- Warn unused member of anonymous class [#22729](https://github.com/scala/scala3/pull/22729) +- No warning for parameter of overriding method [#22757](https://github.com/scala/scala3/pull/22757) +- Lazy val def member is pattern var [#22750](https://github.com/scala/scala3/pull/22750) +- Ignore params to default arg getters [#22749](https://github.com/scala/scala3/pull/22749) +- Restore resolving prefixes of implicit Ident [#22751](https://github.com/scala/scala3/pull/22751) +- Exclude synthetic this.m, Any.m from import lookup [#22695](https://github.com/scala/scala3/pull/22695) +- Nowarn public implicit val class params [#22664](https://github.com/scala/scala3/pull/22664) +- Don't warn retainedBody [#22510](https://github.com/scala/scala3/pull/22510) +- Suppress spurious Suppression [#22383](https://github.com/scala/scala3/pull/22383) +- Handle Typeable [#22663](https://github.com/scala/scala3/pull/22663) +- CheckUnused checks span.exists before testing its parts [#22504](https://github.com/scala/scala3/pull/22504) +- Process Export for unused check [#22984](https://github.com/scala/scala3/pull/22984) +- Enclosing package p.q not visible as q [#23069](https://github.com/scala/scala3/pull/23069) +- Remove premature caching of lookups for unused lint [#22982](https://github.com/scala/scala3/pull/22982) +- Improve checking LHS of Assign [#22977](https://github.com/scala/scala3/pull/22977) +- Improve Unit ascription escape hatch [#23147](https://github.com/scala/scala3/pull/23147) +- Mention extension in unused param warning [#23132](https://github.com/scala/scala3/pull/23132) +- Dealias for unused param check [#23256](https://github.com/scala/scala3/pull/23256) +- Take inferred or explicit refinement result for unused check [#23325](https://github.com/scala/scala3/pull/23325) +- Add accessible check for import usage [#23348](https://github.com/scala/scala3/pull/23348) +- Use result of lambda type of implicit in CheckUnused [#23497](https://github.com/scala/scala3/pull/23497) + +## Match Types + +- Handle NoType in TypeComparer.disjointnessBoundary [#21520](https://github.com/scala/scala3/pull/21520) +- Distinguish 0.0 and -0.0 in ConstantType match types [#23265](https://github.com/scala/scala3/pull/23265) + +## Metaprogramming + +- Add a check for correct Array shape in quotes.reflect.ClassOfConstant [#22033](https://github.com/scala/scala3/pull/22033) + +## Opaque Types + +- Fix stack overflow errors when generating opaque type proxies [#22479](https://github.com/scala/scala3/pull/22479) + +## Optional Braces + +- Correctly detect colon lambda eol indent for optional brace of argument [#22477](https://github.com/scala/scala3/pull/22477) + +## Overloading + +- Fail compilation if multiple conflicting top-level private defs/vals are in the same package [#22759](https://github.com/scala/scala3/pull/22759) + +## Parser + +- Allow observing an indent after conditional [#22611](https://github.com/scala/scala3/pull/22611) +- No outdent at eof [#22435](https://github.com/scala/scala3/pull/22435) +- Fix annotations not expected in the middle of an array type by java parser [#22391](https://github.com/scala/scala3/pull/22391) +- Fix incorrect warning with -no-indent [#23216](https://github.com/scala/scala3/pull/23216) + +## Pattern Matching + +- Fix issue in lazy symbol completion or bug in nested classfile parser [#23634](https://github.com/scala/scala3/pull/23634) +- Fix existing GADT constraints with introduced pattern-bound symbols [#22928](https://github.com/scala/scala3/pull/22928) +- Avoid crash in uninhab check in Space [#22601](https://github.com/scala/scala3/pull/22601) + +## Pickling + +- Try to handle SkolemTypes in SingletonTypeTree during pickling [#23236](https://github.com/scala/scala3/pull/23236) + +## Presentation Compiler + +- Compare span points in pathTo to determine best span [#23581](https://github.com/scala/scala3/pull/23581) +- Fix: Fix extracting refinements from intersection types in dynamic select hovers [#23640](https://github.com/scala/scala3/pull/23640) +- Completions for requests just before string [#22894](https://github.com/scala/scala3/pull/22894) +- Add enum type param support in sourceSymbol [#18603](https://github.com/scala/scala3/pull/18603) +- Use untpd.Tree instead of tpd.Tree for SelectionRangeProvider [#22702](https://github.com/scala/scala3/pull/22702) +- Fix: handle multiple params lists in for infer type [#23197](https://github.com/scala/scala3/pull/23197) +- Fix completion mode filtering + optimize scopeCompletions [#23172](https://github.com/scala/scala3/pull/23172) +- Add selection ranges for more names [#23257](https://github.com/scala/scala3/pull/23257) +- Add inlay hints for by-name parameters [#23283](https://github.com/scala/scala3/pull/23283) +- Add jpath to VirtualFile (for pc) [#23203](https://github.com/scala/scala3/pull/23203) +- Add X-Ray Inlay Hints [#23891](https://github.com/scala/scala3/pull/23891) +- Add using to completions when applicable [#23647](https://github.com/scala/scala3/pull/23647) +- Do not add \[\] after `derives` [#23811](https://github.com/scala/scala3/pull/23811) + +## Quotes + +- Fix issue with static `this` references erroring in quoted code [#22618](https://github.com/scala/scala3/pull/22618) +- Skip splice level checking for symbols [#22782](https://github.com/scala/scala3/pull/22782) +- Fix stale top level synthetic package object being used in later runs [#23464](https://github.com/scala/scala3/pull/23464) + +## REPL + +- REPL: JLine 3.29.0 (was 3.27.1) [#22679](https://github.com/scala/scala3/pull/22679) +- REPL: emit warning for the `:sh` command [#22694](https://github.com/scala/scala3/pull/22694) + +## Reflection + +- Fix regression: do not approximate prefixes when using memberType in reflect API [#22448](https://github.com/scala/scala3/pull/22448) +- Forbid `StringConstant(null)` [#23064](https://github.com/scala/scala3/pull/23064) +- Quotes reflect: sort the typeMembers output list and filter out non-members [#22876](https://github.com/scala/scala3/pull/22876) + +## Reporting + +- Register nowarn when inlining [#22682](https://github.com/scala/scala3/pull/22682) +- Filter help renders box border [#22434](https://github.com/scala/scala3/pull/22434) +- Fix incorrect warning on type ascription for backquoted identifiers [#23088](https://github.com/scala/scala3/pull/23088) +- Add an explainer to the DoubleDefinition error [#23470](https://github.com/scala/scala3/pull/23470) + +## Scaladoc + +- Encode path of class [#23503](https://github.com/scala/scala3/pull/23503) + +## Settings + +- Chore: filter allowed source versions by import and by settings [#23215](https://github.com/scala/scala3/pull/23215) + +## Transform + +- Check only stable qual for import prefix [#22633](https://github.com/scala/scala3/pull/22633) +- Warn trivial recursion with module prefix [#23278](https://github.com/scala/scala3/pull/23278) + +## Tuples + +- Normalize tuple types in var args seq literals and classOf instances [#23465](https://github.com/scala/scala3/pull/23465) + +## Typer + +- Generalize "Don't approximate a type using Nothing as prefix" [#23628](https://github.com/scala/scala3/pull/23628) +- Don't approximate a type using `Nothing` as prefix [#23531](https://github.com/scala/scala3/pull/23531) +- Tighten condition to preserve denotation in IntegrateMap [#23060](https://github.com/scala/scala3/pull/23060) +- Disallow context bounds in type lambdas [#22659](https://github.com/scala/scala3/pull/22659) +- Revert the PolyType case in #21744 [#22820](https://github.com/scala/scala3/pull/22820) +- Fix isGenericArrayElement for higher-kinded types [#22938](https://github.com/scala/scala3/pull/22938) +- Revert lambda cleanup [#22697](https://github.com/scala/scala3/pull/22697) +- Constructor companion gets privateWithin [#22627](https://github.com/scala/scala3/pull/22627) +- Add regression test for #22076 [#22602](https://github.com/scala/scala3/pull/22602) +- Constructor proxy is restricted if class is protected [#22563](https://github.com/scala/scala3/pull/22563) +- Check if a prefix is valid before selecting from a type [#22368](https://github.com/scala/scala3/pull/22368) +- Preserve hard unions in widenSingletons [#22369](https://github.com/scala/scala3/pull/22369) +- Revert recent changes to opaque type proxy generation [#23059](https://github.com/scala/scala3/pull/23059) +- Compare TypeVar and TypeParamRef in mergeRefinedOrApplied [#23045](https://github.com/scala/scala3/pull/23045) +- Only keep denotation for methods in IntegrateMap [#23226](https://github.com/scala/scala3/pull/23226) +- Tighten condition when to do SAM type conversion [#23246](https://github.com/scala/scala3/pull/23246) +- Revert "Make overload pruning based on result types less aggressive (#21744)" in main [#23331](https://github.com/scala/scala3/pull/23331) +- Fix #22922: Add TypeParamRef handling in isSingletonBounded [#23501](https://github.com/scala/scala3/pull/23501) +- Fix this references everywhere in dependent function types [#23514](https://github.com/scala/scala3/pull/23514) +- More careful ClassTag instantiation [#23659](https://github.com/scala/scala3/pull/23659) +- Use more context for implicit search only if no default argument [#23664](https://github.com/scala/scala3/pull/23664) + +## JDK + +- Check path of module prefix for tailrec [#23491](https://github.com/scala/scala3/pull/23491) + + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.6..3.3.7` these are: + +``` + 164 Tomasz Godzik + 81 Som Snytt + 27 Jan Chyb + 25 Matt Bovel + 23 aherlihy + 20 kasiaMarek + 15 Martin Odersky + 13 Hamza Remmal + 11 Dale Wijnand + 11 noti0na1 + 10 Kacper Korban + 10 Wojciech Mazur + 8 Guillaume Martres + 7 Zieliński Patryk + 5 Jędrzej Rochala + 5 Quentin Bernet + 5 Seth Tisue + 4 Alexander + 4 Joel Wilsson + 4 Katarzyna Marek + 4 Oliver Bračevac + 3 Natsu Kagami + 3 Yoonjae Jeon + 3 anna herlihy + 2 Daisy Li + 2 Dolphin von Chips + 2 Mikołaj Fornal + 2 Sébastien Doeraene + 2 Vadim Chelyshov + 2 rochala + 1 Alec Theriault + 1 Aleksey Troitskiy + 1 Alex1005a + 1 Daniel Thoma + 1 Felix Herrmann + 1 HarrisL2 + 1 Jan + 1 Jan-Pieter van den Heuvel + 1 Jentsch + 1 João Ferreira + 1 Lukas Rytz + 1 Marc GRIS + 1 MatthieuSLR9 + 1 Nikita Glushchenko + 1 Niklas Fiekas + 1 Patryk Zieliński + 1 Piotr Chabelski + 1 Przemysław Sajnóg + 1 Rocco Mathijn Andela + 1 Tomas Mikula + 1 Yichen Xu + 1 bingchen-li + 1 fan-tom + 1 katrinafyi + 1 kijuky + 1 philippus + 1 philwalk + 1 vder +``` diff --git a/changelogs/3.3.8-RC1.md b/changelogs/3.3.8-RC1.md new file mode 100644 index 000000000000..e8efca88d934 --- /dev/null +++ b/changelogs/3.3.8-RC1.md @@ -0,0 +1,713 @@ +# Highlights of the release + +- Support for JDK 26 [#24430](https://github.com/scala/scala3/pull/24430) +- Multiple coverage improvement to make it on par with Scala 2 support including + `// $COVERAGE-OFF$` and `// $COVERAGE-ON$` markers +- New @uncheckedOverride annotation for definitions that may override + [#24545](https://github.com/scala/scala3/pull/24545) +- Trap Ctrl-C in the REPL: if no command is running clear the prompt, if some + command is running ask for confirmation before exiting + [#24127](https://github.com/scala/scala3/pull/24127) +- Numerous linting and presentation compiler improvements and fixes + +# Other changes and fixes + +## Access modifiers + +- Check protected parent constructor accesses only occurs in child constructors + [#25511](https://github.com/scala/scala3/pull/25511) +- Fix protected constructor access + [#25523](https://github.com/scala/scala3/pull/25523) + +## Annotations + +- Improve ref check of constructors + [#24342](https://github.com/scala/scala3/pull/24342) +- Also copy param annots at mixin + [#24916](https://github.com/scala/scala3/pull/24916) +- Named arg may be deprecatedName + [#21588](https://github.com/scala/scala3/pull/21588) +- Fix: emit deprecation warnings when a symbol is annotated by a deprecated + annotation [#23906](https://github.com/scala/scala3/pull/23906) +- Fix #20063: Java leading annotation parsing + [#25503](https://github.com/scala/scala3/pull/25503) +- Accommodate adapted Scala 2 annotation value + [#17516](https://github.com/scala/scala3/pull/17516) +- Regression test for #3403 [#25521](https://github.com/scala/scala3/pull/25521) +- Fix: do not allow `@targetName` on primary constructor too + [#24682](https://github.com/scala/scala3/pull/24682) +- Add `@uncheckedOverride` annotation for definitions that may override + [#24545](https://github.com/scala/scala3/pull/24545) + +## Backend + +- Chore: add regression test for #4528 + [#24663](https://github.com/scala/scala3/pull/24663) +- Cast inline call results to result type, not method type + [#25111](https://github.com/scala/scala3/pull/25111) +- Regression tests for #25 [#25536](https://github.com/scala/scala3/pull/25536) +- Enforce JVM UTF-8 string limits in the backend + [#25300](https://github.com/scala/scala3/pull/25300) +- Do not simplify isInstanceOf if unrelated types might be subtypes at run-time + [#25535](https://github.com/scala/scala3/pull/25535) +- Adjust purity of trait with lazy member + [#23788](https://github.com/scala/scala3/pull/23788) +- Use `Objects.equals` in translating `==` + [#25314](https://github.com/scala/scala3/pull/25314) +- Fix generic signatures for mixin forwarders conflicting type parameter names + [#24567](https://github.com/scala/scala3/pull/24567) +- No mixin forwarder when ancestor is sealed + [#23482](https://github.com/scala/scala3/pull/23482) +- Simplify JVM interface list emission, remove a mini-phase + [#25203](https://github.com/scala/scala3/pull/25203) +- Chore: optimize lazy val with power of two + [#22428](https://github.com/scala/scala3/pull/22428) + +## Classpath + +- Skip `module-info.class` files during REPL tab-completion + [#24838](https://github.com/scala/scala3/pull/24838) +- Fix for #22461 Empty ClassPath attribute in one or more classpath jars causes + crash [#22462](https://github.com/scala/scala3/pull/22462) + +## Coverage + +- Make coverage more similar to the one in Scala 2 + [#23722](https://github.com/scala/scala3/pull/23722) +- Fix #25555: Preserve singleton precision during scoverage lifting + [#25556](https://github.com/scala/scala3/pull/25556) +- Fixes #25482: Ensure package transformation properly handles language imports + in InstrumentCoverage [#25546](https://github.com/scala/scala3/pull/25546) +- Bugfix: Fix issues with Scala JS and coverage + [#24769](https://github.com/scala/scala3/pull/24769) +- Make coverage support incremental compilation + [#24773](https://github.com/scala/scala3/pull/24773) +- Add support for local coverage on and off with `// $COVERAGE-OFF$` and + `// $COVERAGE-ON$` [#24486](https://github.com/scala/scala3/pull/24486) +- Improvement: Try to optimize coverage phase + [#24925](https://github.com/scala/scala3/pull/24925) +- Scoverage: do not instrument erased values + [#25298](https://github.com/scala/scala3/pull/25298) + +## Default parameters + +- Fix named arguments evaluation order with by-name parameters + [#25161](https://github.com/scala/scala3/pull/25161) + +## Documentation + +- Docs: Enforce links checks in the generated docs + [#25425](https://github.com/scala/scala3/pull/25425) +- Improve the contributing guide + [#25384](https://github.com/scala/scala3/pull/25384) +- Extend copyright into 2026 + [#24927](https://github.com/scala/scala3/pull/24927) +- Update README: This is Scala 3 + [#25424](https://github.com/scala/scala3/pull/25424) +- Explain implicit vs default args + [#25311](https://github.com/scala/scala3/pull/25311) +- Fix some scaladoc in compiler + [#24166](https://github.com/scala/scala3/pull/24166) +- Docs: fix task name for scaladoc/testOnly + [#23678](https://github.com/scala/scala3/pull/23678) +- Replace internal tab char in comment in Synthesizer.scala + [#23691](https://github.com/scala/scala3/pull/23691) + +## Enums + +- Fix Java enum forwarders to pull from companion on initialization + [#24361](https://github.com/scala/scala3/pull/24361) +- Make enum apply ascriptions inferred types + [#25562](https://github.com/scala/scala3/pull/25562) + +## Exports + +- Fix: look at the underlying of a TermRef for a getter + [#24565](https://github.com/scala/scala3/pull/24565) + +## Extension Methods + +- Accept using right associative parameters + [#24766](https://github.com/scala/scala3/pull/24766) +- Add ctor proxies before exports + [#24884](https://github.com/scala/scala3/pull/24884) +- Fix pkg obj prefix of opaque tp ext meth + [#21527](https://github.com/scala/scala3/pull/21527) +- Better handling of illegal trees in `extension` + [#25368](https://github.com/scala/scala3/pull/25368) +- Fix LiftToAnchors for higher-kinded type applications + [#23672](https://github.com/scala/scala3/pull/23672) +- Prevent crash when extension not found + [#18830](https://github.com/scala/scala3/pull/18830) + +## GADTs + +- Also trust TypeTest when there is a Bind + [#24568](https://github.com/scala/scala3/pull/24568) + +## Implicits + +- Only check seen for LazyRef for TypeSizeAccumulator + [#20459](https://github.com/scala/scala3/pull/20459) +- Chore: add regression test for #8581 + [#24666](https://github.com/scala/scala3/pull/24666) +- Fix "Regression in zio/zio-schema for typer/implicit resolution" + [#24156](https://github.com/scala/scala3/pull/24156) +- Fix implicit scope liftToAnchors for parameter lower bounds + [#23679](https://github.com/scala/scala3/pull/23679) + +## Inference + +- Tweak type for tryParameterless + [#24716](https://github.com/scala/scala3/pull/24716) + +## Initialization + +- Refine parameter adaptation logic for arrays + [#23591](https://github.com/scala/scala3/pull/23591) + +## Infrastructure + +- Tweak tests to not fail under jdk 25 + [#25148](https://github.com/scala/scala3/pull/25148) +- Bump actions/upload-artifact from 6 to 7 + [#25423](https://github.com/scala/scala3/pull/25423) +- Bump actions/download-artifact from 7 to 8 + [#25420](https://github.com/scala/scala3/pull/25420) +- Bump VirtusLab/scala-cli-setup from 1.12.2 to 1.12.3 + [#25422](https://github.com/scala/scala3/pull/25422) +- Bump coursier/cache-action from 7 to 8 + [#25220](https://github.com/scala/scala3/pull/25220) +- Bump actions/upload-artifact from 5 to 6 + [#24758](https://github.com/scala/scala3/pull/24758) +- Bump actions/download-artifact from 6 to 7 + [#24759](https://github.com/scala/scala3/pull/24759) +- Bump actions/checkout from 5 to 6 + [#24517](https://github.com/scala/scala3/pull/24517) +- Bump actions/upload-artifact from 4 to 5 + [#24268](https://github.com/scala/scala3/pull/24268) +- Bump actions/download-artifact from 5 to 6 + [#24271](https://github.com/scala/scala3/pull/24271) +- Bump actions/download-artifact from 4 to 5 + [#23711](https://github.com/scala/scala3/pull/23711) +- Bump actions/checkout from 4 to 5 + [#23710](https://github.com/scala/scala3/pull/23710) +- Chore: bump sbt to 1.11.5 [#23805](https://github.com/scala/scala3/pull/23805) +- Chore(deps): bump actions/setup-java from 4 to 5 + [#23812](https://github.com/scala/scala3/pull/23812) +- Chore: Use numberOfWorkers instead + [#24338](https://github.com/scala/scala3/pull/24338) +- Improvement: Additional way of defining ENABLE_BSP_ALL_PROJECTS + [#24122](https://github.com/scala/scala3/pull/24122) +- Formatting: Format presentation-compiler with scalafmt + [#24977](https://github.com/scala/scala3/pull/24977) + +## Inline + +- Chore: add regression test for #22575 + [#25337](https://github.com/scala/scala3/pull/25337) +- Ensure the inline match scrutinee type conforms to the recalculated type + [#24988](https://github.com/scala/scala3/pull/24988) +- Make containsOuterRefsAnywhere follow aliases + [#25214](https://github.com/scala/scala3/pull/25214) +- Find annotation args in inline expansion + [#24895](https://github.com/scala/scala3/pull/24895) +- Chore: add regression test for #10343 + [#24668](https://github.com/scala/scala3/pull/24668) +- Improve the test for creating protected method accessors for inlines + [#25865](https://github.com/scala/scala3/pull/25865) +- Do not update Symbol defTrees when retyping after Inlining + [#23870](https://github.com/scala/scala3/pull/23870) +- Prevent opaque types leaking from transparent inline methods + [#23792](https://github.com/scala/scala3/pull/23792) +- Chore: add regression test for #8194 + [#24655](https://github.com/scala/scala3/pull/24655) +- Also substitute symbols in case bindings symbols + [#24849](https://github.com/scala/scala3/pull/24849) +- Inlines: decide sealing against original target, while using widened type as + cast destination [#25448](https://github.com/scala/scala3/pull/25448) +- Fix inline match on blocks with multiple statements + [#20125](https://github.com/scala/scala3/pull/20125) + +## JDK + +- Skip over JEP 445 compact compilation units + [#23247](https://github.com/scala/scala3/pull/23247) +- Handle MissingType during Erasure phase + [#24817](https://github.com/scala/scala3/pull/24817) +- Don't generate bridge methods for inaccessible Java package-private methods + [#25166](https://github.com/scala/scala3/pull/25166) +- Fix isJvmAccessible to handle nested protected Java classes + [#24625](https://github.com/scala/scala3/pull/24625) +- Upgrade ASM to 9.9 for JDK 26 support + [#24430](https://github.com/scala/scala3/pull/24430) +- Fix java record varargs field accessor + [#24172](https://github.com/scala/scala3/pull/24172) + +## Linting + +- Fix false unused import warning for given CanEqual in pattern matching + [#25231](https://github.com/scala/scala3/pull/25231) +- Move second CheckUnused after pattern matcher + [#25114](https://github.com/scala/scala3/pull/25114) +- Extension check handles hiBound of opaque type + [#25172](https://github.com/scala/scala3/pull/25172) +- Unused lint ignores args to ctor of enclosing class + [#24768](https://github.com/scala/scala3/pull/24768) +- Parameter name can be masked in import + [#24635](https://github.com/scala/scala3/pull/24635) +- CheckUnused examines type of Apply + [#24465](https://github.com/scala/scala3/pull/24465) +- Check for synthetic case methods in unused lint + [#24239](https://github.com/scala/scala3/pull/24239) +- 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) +- Ignore warnings when compiletime.testing is imported + [#24036](https://github.com/scala/scala3/pull/24036) +- Unused lint does not rewrite suppressed warnings + [#24055](https://github.com/scala/scala3/pull/24055) +- Drill through type lambda for tree symbol + [#23699](https://github.com/scala/scala3/pull/23699) +- Invent given pattern name in for comprehension + [#23121](https://github.com/scala/scala3/pull/23121) +- Check inline expansion for exclusion + [#23019](https://github.com/scala/scala3/pull/23019) +- Unused var message mentions unread or unset + [#23719](https://github.com/scala/scala3/pull/23719) +- Warn unused masking imports, remove obsolete unused options + [#23757](https://github.com/scala/scala3/pull/23757) +- Distrust tree pos for patvar lint + [#25043](https://github.com/scala/scala3/pull/25043) +- Match apply in PolyFunction for override + [#25505](https://github.com/scala/scala3/pull/25505) +- Value discard checks inline call for exemption + [#25554](https://github.com/scala/scala3/pull/25554) +- Lint enum case in outer context + [#24652](https://github.com/scala/scala3/pull/24652) +- Preserve select of exported member in newExpr + [#24570](https://github.com/scala/scala3/pull/24570) +- Always traverse Inlined.call in linter + [#24043](https://github.com/scala/scala3/pull/24043) +- Lint function arrow intended context function + [#23847](https://github.com/scala/scala3/pull/23847) +- 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) +- Regression test locals [#25433](https://github.com/scala/scala3/pull/25433) +- Augment regression test [#24327](https://github.com/scala/scala3/pull/24327) + +## Match Types + +- Fix regression: Prioritize tree type over proto type when typing Binds + [#24175](https://github.com/scala/scala3/pull/24175) + +## Metaprogramming + +- Warn about encoded pkg obj names + [#22707](https://github.com/scala/scala3/pull/22707) +- Bugfix: Fix possible SuspendException thrown when using macros + [#24174](https://github.com/scala/scala3/pull/24174) + +## Nullability + +- Patch to allow interop with XMLBuilder and -Yexplicit-nulls + [#25221](https://github.com/scala/scala3/pull/25221) +- Add jspecify NonNull annotation to NotNullAnnots list + [#24487](https://github.com/scala/scala3/pull/24487) +- Add quick fix to add .nn [#23598](https://github.com/scala/scala3/pull/23598) + +## Opaque Types + +- Preserve insertion order when balancing And/OrType + [#25361](https://github.com/scala/scala3/pull/25361) +- Treat synthetic casts as pure + [#25126](https://github.com/scala/scala3/pull/25126) +- Make opaque types decomposable + [#24068](https://github.com/scala/scala3/pull/24068) + +## Overloading + +- Pretype args before skipping clause of overloaded application + [#23012](https://github.com/scala/scala3/pull/23012) +- Fix varargs overload resolution with wildcard types + [#24669](https://github.com/scala/scala3/pull/24669) +- Fix wildcardArgOK for mixed wildcard/concrete type args + [#25001](https://github.com/scala/scala3/pull/25001) +- Regression test #16004 [#24086](https://github.com/scala/scala3/pull/24086) +- Tweak tryParameterless to use readapt + [#24078](https://github.com/scala/scala3/pull/24078) +- Fix override checking for Java methods with covariant array + [#24408](https://github.com/scala/scala3/pull/24408) +- Prefer non-extension to extension + [#24943](https://github.com/scala/scala3/pull/24943) +- Fix class super-call invokespecial owner + [#25437](https://github.com/scala/scala3/pull/25437) + +## Parser + +- Accept `primitive.class` in annotations + [#25363](https://github.com/scala/scala3/pull/25363) +- End of new expr never shrinks + [#25253](https://github.com/scala/scala3/pull/25253) +- Permit new with trivial end + [#24331](https://github.com/scala/scala3/pull/24331) +- Improve message for nested package missing braces + [#23816](https://github.com/scala/scala3/pull/23816) +- Check seen for TypeParamRef in TypeSizeAccumulator + [#25264](https://github.com/scala/scala3/pull/25264) +- Observe outdent also for type cases and type blocks in quotes + [#25519](https://github.com/scala/scala3/pull/25519) +- Fix: import resolution for Java + [#25537](https://github.com/scala/scala3/pull/25537) +- Error tree on outdent is zero extent + [#23796](https://github.com/scala/scala3/pull/23796) +- Don't automatically insert OUTDENT before COMMA + [#25501](https://github.com/scala/scala3/pull/25501) +- Fix: allow postfix setters under language.postfixOps + [#23775](https://github.com/scala/scala3/pull/23775) +- Additional regression tests + [#25506](https://github.com/scala/scala3/pull/25506) + +## Pattern Matching + +- Emit unchecked warnings for trait patterns + [#24962](https://github.com/scala/scala3/pull/24962) +- Fix false exhaustivity warning for seq match + [#23968](https://github.com/scala/scala3/pull/23968) +- Call inhabited for AppliedType recursively + [#23964](https://github.com/scala/scala3/pull/23964) +- Fix Ycheck crash from singleton tuple selects after specialization + [#25331](https://github.com/scala/scala3/pull/25331) +- Fix false unreachable case warning + [#23800](https://github.com/scala/scala3/pull/23800) +- Use upper bound of abstract types in exhaustivity checking + [#23909](https://github.com/scala/scala3/pull/23909) +- Extend caching in the space engine beyond local context + [#23483](https://github.com/scala/scala3/pull/23483) +- Prevent from getting children in case of non-sealed private + [#25359](https://github.com/scala/scala3/pull/25359) +- Use MatchCaseUnreachable [#24829](https://github.com/scala/scala3/pull/24829) +- Preserve singleton types for final val aliases to Java enum constants + [#24980](https://github.com/scala/scala3/pull/24980) +- Try expanding case class to the product of its fields + [#25454](https://github.com/scala/scala3/pull/25454) +- Never widen constant type on selector type in matches + [#25197](https://github.com/scala/scala3/pull/25197) + +## Pickling + +- Fix #24456: Cleanup the info of a Bind symbol in posttyper + [#24490](https://github.com/scala/scala3/pull/24490) +- Fix #24207: Cleanup the type of `UnApply` trees in posttyper + [#24259](https://github.com/scala/scala3/pull/24259) +- Chore: add a regression test for #15786 + [#24658](https://github.com/scala/scala3/pull/24658) +- Don't absolutize sources outside of the sourceroot in TASTy + [#25429](https://github.com/scala/scala3/pull/25429) + +## Positions + +- Preserve span of `this` during statification of trait constructors + [#25354](https://github.com/scala/scala3/pull/25354) +- Compute the right span for abstract error messages + [#23853](https://github.com/scala/scala3/pull/23853) +- Use a better span for an anonymous class + [#24640](https://github.com/scala/scala3/pull/24640) + +## Presentation Compiler + +- Improvement: Use method type if it's available + [#25641](https://github.com/scala/scala3/pull/25641) +- Bugfix: Don't add square braces in context bounds + [#25798](https://github.com/scala/scala3/pull/25798) +- Bugfix: Fix issue with empty name throwing exceptions + [#25752](https://github.com/scala/scala3/pull/25752) +- Bugfix: Fix wrong type in InferredType code action + [#25469](https://github.com/scala/scala3/pull/25469) +- Bugfix: Fix issues in presentation compiler when using Scala JS + [#25450](https://github.com/scala/scala3/pull/25450) +- Improvement: Add fallbacks in non critical places for presentation compiler + [#25483](https://github.com/scala/scala3/pull/25483) +- Fix & feat: improvements in selection ranges + [#25290](https://github.com/scala/scala3/pull/25290) +- Feat(pc): add closing labels inlay hints + [#25178](https://github.com/scala/scala3/pull/25178) +- Bugfix: remove stale top-level symbols between runs + [#25317](https://github.com/scala/scala3/pull/25317) +- Bugfix: Fix various minor presentation compiler issues + [#25256](https://github.com/scala/scala3/pull/25256) +- Fix: ignore implicit apply for signature help + [#25294](https://github.com/scala/scala3/pull/25294) +- Fix incorrect XRay hint [#24738](https://github.com/scala/scala3/pull/24738) +- Presentation compiler: propagate isDebug timeout check to all tests + [#25068](https://github.com/scala/scala3/pull/25068) +- Handle inlined tree in implicit parameter inlay hints + [#24762](https://github.com/scala/scala3/pull/24762) +- Create extension method in infer method action + [#24721](https://github.com/scala/scala3/pull/24721) +- Fix opaque types displayed as `Nothing & Any` in hover and signature help + [#24921](https://github.com/scala/scala3/pull/24921) +- Fix negative activeParameter in signature help + [#24945](https://github.com/scala/scala3/pull/24945) +- Closes #7532: Parameter hints not working for functions + [#23854](https://github.com/scala/scala3/pull/23854) +- Improvement: Allow passing -explain to the presentation compiler + [#24740](https://github.com/scala/scala3/pull/24740) +- Exclude XRayModeHint for synthetics + [#24722](https://github.com/scala/scala3/pull/24722) +- Add unsafe RawPresentationCompiler implementation + [#24133](https://github.com/scala/scala3/pull/24133) +- Bugfix: Make sure that we can query tree.source + [#24123](https://github.com/scala/scala3/pull/24123) +- Improvement: additional completions for using clause + [#23647](https://github.com/scala/scala3/pull/23647) +- Inlining interpolated variables + [#25144](https://github.com/scala/scala3/pull/25144) +- Bugfix: inline value false-positive shadowing with lambda params + [#25140](https://github.com/scala/scala3/pull/25140) +- Fix issue with pc breaking in requiredMethod on newly overloaded valueOf + [#23708](https://github.com/scala/scala3/pull/23708) +- Pc: completions - do not add `[]` for `... derives TC@@` + [#23811](https://github.com/scala/scala3/pull/23811) +- Porting XRayModeHints [#23891](https://github.com/scala/scala3/pull/23891) +- Improve symbol order in completions provided by the presentation compiler + [#23888](https://github.com/scala/scala3/pull/23888) +- Bugfix: Fix issues with Scala JS and the presentation compiler + [#25550](https://github.com/scala/scala3/pull/25550) +- Bugfix: Show hover on derives clause + [#25804](https://github.com/scala/scala3/pull/25804) +- Improvement: improve perfomance of the presentation compiler + [#25769](https://github.com/scala/scala3/pull/25769) +- Coursier dependency imports should have lexicographic ordering + [#21592](https://github.com/scala/scala3/pull/21592) + +## Quotes + +- Fix crashing for context bound type variables in quoted patterns + [#25286](https://github.com/scala/scala3/pull/25286) +- Fix #25208: handle AndType(TermRef, TypeRef) in TreeUnpickler path trees + [#25209](https://github.com/scala/scala3/pull/25209) +- Remove redundant mapOver in fixThisTypeModuleClassReferences + [#25440](https://github.com/scala/scala3/pull/25440) +- Make typeArgs in QuotesImpl work for AnnotatedTypes + [#24018](https://github.com/scala/scala3/pull/24018) +- Improve LambdaTypes handling in Quotes reflect memberType + [#25786](https://github.com/scala/scala3/pull/25786) + +## Reflection + +- Fix Select.copy in reflect API losing DerivedName for default getters + [#25269](https://github.com/scala/scala3/pull/25269) +- Do not expose ClassInfo in memberType in reflect API + [#24922](https://github.com/scala/scala3/pull/24922) +- Quotes reflect: Allow to return DefDef from a val symbol tree + [#22603](https://github.com/scala/scala3/pull/22603) + +## Reporting + +- Force conditional warnings when configured + [#25552](https://github.com/scala/scala3/pull/25552) +- Use deprecatedOverriding message + [#25439](https://github.com/scala/scala3/pull/25439) +- Fix #24711: Use dot notation for Java nested types in error messages + [#24919](https://github.com/scala/scala3/pull/24919) +- Enhance WInferUnion to not warn propagated explicit union types + [#25280](https://github.com/scala/scala3/pull/25280) +- Fixes #24233: Remove misleading JDK compatibility hint for TASTy errors + [#25171](https://github.com/scala/scala3/pull/25171) +- More descriptive "not found" message when type/term exists with the expected + term/type name [#24959](https://github.com/scala/scala3/pull/24959) +- Mention named givens in double def explainer + [#23833](https://github.com/scala/scala3/pull/23833) +- Do not discard amended format when f-interpolator warns + [#23697](https://github.com/scala/scala3/pull/23697) +- Use dollar-free param names in stub implementations + [#25646](https://github.com/scala/scala3/pull/25646) +- Error mentions bad \_1 selector + [#25616](https://github.com/scala/scala3/pull/25616) +- Add ErrorMessageID to -Winfer-union warnings (#25073) + [#25249](https://github.com/scala/scala3/pull/25249) +- Addendum when apply error involves default arg + [#21133](https://github.com/scala/scala3/pull/21133) +- Warn on Scala 2 auto-apply if loosely nullary + [#24811](https://github.com/scala/scala3/pull/24811) +- Forward reference error includes line numbers + [#22534](https://github.com/scala/scala3/pull/22534) +- Use other tree for actual symbol of Assign + [#22869](https://github.com/scala/scala3/pull/22869) +- Add "did you mean to use parentheses/a block" when finding a complex + expression on the RHS of an infix operator + [#25099](https://github.com/scala/scala3/pull/25099) +- Fix printing references to nested objects + [#25086](https://github.com/scala/scala3/pull/25086) +- Add error note for type mismatch involving blocks without last expression + [#25181](https://github.com/scala/scala3/pull/25181) +- Don't suggest invisible members of root imports + [#24999](https://github.com/scala/scala3/pull/24999) +- Improve multi-line code message rendering + [#24723](https://github.com/scala/scala3/pull/24723) +- Warn if type argument was inferred as union type + [#24258](https://github.com/scala/scala3/pull/24258) +- Improve printing of strings + [#22945](https://github.com/scala/scala3/pull/22945) +- Better error message: reassignment to val + [#24184](https://github.com/scala/scala3/pull/24184) +- Add addendum to `private val` parameter variance error message + [#23876](https://github.com/scala/scala3/pull/23876) +- Regression tests [#24876](https://github.com/scala/scala3/pull/24876) + +## REPL + +- Make REPL type echo-ing use partially qualified names where made possible by + imports [#24850](https://github.com/scala/scala3/pull/24850) +- Remove pipes from multi-line REPL prompts + [#24307](https://github.com/scala/scala3/pull/24307) +- Fix "Colon as operator part at beginning of line in REPL" + [#24942](https://github.com/scala/scala3/pull/24942) +- Trap Ctrl-C in the REPL: if no command is running clear the prompt, if some + command is running ask for confirmation before exiting + [#24127](https://github.com/scala/scala3/pull/24127) + +## Rewrites + +- Deduplicate patches before applying them to sources + [#24215](https://github.com/scala/scala3/pull/24215) + +## Runner + +- Fix for #22443: prevent stty from being called when not running in a terminal + [#22444](https://github.com/scala/scala3/pull/22444) + +## Scaladoc + +- Do not hide when a `class` or a `trait` extends `Any` + [#25403](https://github.com/scala/scala3/pull/25403) +- Ability to hide snippets from the documentation + [#25411](https://github.com/scala/scala3/pull/25411) +- Use `` tags for exponents in Scaladoc comments + [#25645](https://github.com/scala/scala3/pull/25645) +- Fix duplicate member entries in Scaladoc generation + [#24712](https://github.com/scala/scala3/pull/24712) +- Scaladoc Overload Method Link Fix + [#25156](https://github.com/scala/scala3/pull/25156) +- Init MacroClassLoader in scaladoc + [#25141](https://github.com/scala/scala3/pull/25141) +- Scaladoc: Take title from sidebar.yml into account + [#18997](https://github.com/scala/scala3/pull/18997) +- [scaladoc] Allow to additional scalacOptions in Markdown code snippets + [#24755](https://github.com/scala/scala3/pull/24755) +- Fetch the right kind for synthesised definitions + [#25405](https://github.com/scala/scala3/pull/25405) +- Add the `transparent` flag to `java.lang.Object` + [#25394](https://github.com/scala/scala3/pull/25394) + +## SemanticDB + +- Add context parameters to SemanticDB synthetics + [#23381](https://github.com/scala/scala3/pull/23381) +- Better refinement symbols in semanticDB + [#18604](https://github.com/scala/scala3/pull/18604) +- Encapsulate SemanticDB package, unify symbol and document printing + [#25084](https://github.com/scala/scala3/pull/25084) + +## Testing + +- Fix line and path separators in test + [#23534](https://github.com/scala/scala3/pull/23534) +- Fix: update checkfile for i23905 + [#24064](https://github.com/scala/scala3/pull/24064) + +## Testing framework + +- Reject check file match with stack trace + [#22389](https://github.com/scala/scala3/pull/22389) +- Cleanup compiler test. remove unnecessary `isJavaAtLeast` conditions + [#24589](https://github.com/scala/scala3/pull/24589) +- Bugfix: Don't run coverage tests in testCompilation without --coverage flag + [#24932](https://github.com/scala/scala3/pull/24932) +- Cleaner, quieter test rig tests + [#24073](https://github.com/scala/scala3/pull/24073) +- Skip tests with no sources such as .scala-build dir + [#25173](https://github.com/scala/scala3/pull/25173) +- Report info logs in checked test output if enabled with -Ylog + [#25391](https://github.com/scala/scala3/pull/25391) +- Bugfix: Fix flaky test in ScalaJSCompletionsSuite + [#25539](https://github.com/scala/scala3/pull/25539) +- Add regression test for #23195 + [#24853](https://github.com/scala/scala3/pull/24853) +- Test hover on backticked identifiers with whitespace + [#25142](https://github.com/scala/scala3/pull/25142) + +## Transform + +- Check extension method at typer + [#24199](https://github.com/scala/scala3/pull/24199) +- Always encode in derived name + [#23478](https://github.com/scala/scala3/pull/23478) +- Fix "Scala 3.8.3 compiling covariant overrides with separate compilation + causes AbstractMethodError at runtime" + [#25659](https://github.com/scala/scala3/pull/25659) +- Make Phase.run protected [#25366](https://github.com/scala/scala3/pull/25366) + +## Tuples + +- Fix inconsistent `typeSize` calculation for `TupleN` vs recursive pair + encodings [#24743](https://github.com/scala/scala3/pull/24743) +- Fix: make vals created in desugaring of n-ary lambdas non-synthetic + [#23896](https://github.com/scala/scala3/pull/23896) +- Normalize tuple types when typing patterns + [#25335](https://github.com/scala/scala3/pull/25335) +- Fix #24997: Avoid unnecessary CHECKCAST in untupled function parameters + [#25085](https://github.com/scala/scala3/pull/25085) + +## Typeclass Derivation + +- Fix #23043: spurious package conflict from non-class directories + [#25441](https://github.com/scala/scala3/pull/25441) +- Also checkRefinement for Singleton mirror + [#24080](https://github.com/scala/scala3/pull/24080) + +## Typer + +- Use TypeComparer.constValue in TypeEval + [#24718](https://github.com/scala/scala3/pull/24718) +- Fix crash in Selectable unapply with custom applyDynamic + [#24343](https://github.com/scala/scala3/pull/24343) +- Prevent crash in SAM conversion with mismatched arity + [#23877](https://github.com/scala/scala3/pull/23877) +- Detect `InsertedApply` on `Apply` to break synthetic apply loops, not just + `Select` [#25463](https://github.com/scala/scala3/pull/25463) +- Typer: remove by-name lifting and replace default-getter args with fresh syms + [#25502](https://github.com/scala/scala3/pull/25502) +- Refine isEffectivelyFinal to avoid no-owner crash + [#23675](https://github.com/scala/scala3/pull/23675) +- Do not generate access proxies for locally defined protected defs + [#25056](https://github.com/scala/scala3/pull/25056) +- Use `dealiasKeepRefiningAnnots` in `AvoidMap` + [#25609](https://github.com/scala/scala3/pull/25609) +- Fix #24573: Add stricter checks for platform SAM compatibility + [#24624](https://github.com/scala/scala3/pull/24624) +- Make isExactlyNothing and isExactlyAny work for And/OrTypes + [#24016](https://github.com/scala/scala3/pull/24016) +- Handle assertion error in TyperState + [#23665](https://github.com/scala/scala3/pull/23665) +- Fix: correctly require a `ClassTag` when building a multidimensional `Array` + [#23902](https://github.com/scala/scala3/pull/23902) +- Fix crash when processing path dependent context functions using HKTs + [#25530](https://github.com/scala/scala3/pull/25530) +- Regression test [#24820](https://github.com/scala/scala3/pull/24820) +- Chore: add regression test for #13889 + [#24657](https://github.com/scala/scala3/pull/24657) + +## XML + +- Use `toVector` for XML literal sequences + [#23221](https://github.com/scala/scala3/pull/23221) + +## Union types + +- Dealias in isSimpleThrowable check + [#24744](https://github.com/scala/scala3/pull/24744) diff --git a/changelogs/3.3.8-RC2.md b/changelogs/3.3.8-RC2.md new file mode 100644 index 000000000000..be3a2c6e3442 --- /dev/null +++ b/changelogs/3.3.8-RC2.md @@ -0,0 +1,38 @@ +# Backported fixes + +- Backport: Retry backporting #23665 + ([#910](https://github.com/scala/scala3-lts/pull/910)) +- Backport "Do not overwrite the supertype of non-trait super in Erasure" to 3.3 + LTS ([#909](https://github.com/scala/scala3-lts/pull/909)) +- Backport "feature: Improve the logical sources provider" to 3.3 LTS + ([#908](https://github.com/scala/scala3-lts/pull/908)) +- Backport "feature: Add logical package parsing for sourcepath" to 3.3 LTS + ([#907](https://github.com/scala/scala3-lts/pull/907)) +- Backport "Don't lookahead from interpolation" to 3.3 LTS + ([#905](https://github.com/scala/scala3-lts/pull/905)) +- Backport "Fix infinite loop with bad section ends in TastyHeaderUnpickler" to + 3.3 LTS ([#904](https://github.com/scala/scala3-lts/pull/904)) +- Backport "Context bound always has a span" to 3.3 LTS + ([#903](https://github.com/scala/scala3-lts/pull/903)) +- Backport "Parse HTML properly in Scaladoc" to 3.3 LTS + ([#902](https://github.com/scala/scala3-lts/pull/902)) +- Backport "Don't pop outermost region when skipping" to 3.3 LTS + ([#901](https://github.com/scala/scala3-lts/pull/901)) +- Backport "Fix TastyPrinter's JAR-walking logic to include subdirectories" to + 3.3 LTS ([#900](https://github.com/scala/scala3-lts/pull/900)) +- Backport "Ensure mistakes in CI inputs are caught early" to 3.3 LTS + ([#899](https://github.com/scala/scala3-lts/pull/899)) +- Backport "Delete dead code in io/Path" to 3.3 LTS + ([#898](https://github.com/scala/scala3-lts/pull/898)) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.8-RC1..3.3.8-RC2` these are: + +```bash + 16 Tomasz Godzik + 2 Solal Pirelli + 2 Som Snytt +``` diff --git a/changelogs/3.3.8.md b/changelogs/3.3.8.md new file mode 100644 index 000000000000..d792cca05a09 --- /dev/null +++ b/changelogs/3.3.8.md @@ -0,0 +1,737 @@ +# Highlights of the release + +- Support for JDK 26 [#24430](https://github.com/scala/scala3/pull/24430) +- Multiple coverage improvement to make it on par with Scala 2 support including + `// $COVERAGE-OFF$` and `// $COVERAGE-ON$` markers. +- New @uncheckedOverride annotation for definitions that may override. + [#24545](https://github.com/scala/scala3/pull/24545) +- Trap Ctrl-C in the REPL: if no command is running clear the prompt, if some + command is running ask for confirmation before exiting. + [#24127](https://github.com/scala/scala3/pull/24127) +- Numerous linting and presentation compiler improvements and fixes. +- Support for upcoming Metals 2 sourcepath option. + [#25446](https://github.com/scala/scala3/pull/25446) + +# Other changes and fixes + +## Access modifiers + +- Check protected parent constructor accesses only occurs in child constructors + [#25511](https://github.com/scala/scala3/pull/25511) +- Fix protected constructor access + [#25523](https://github.com/scala/scala3/pull/25523) + +## Annotations + +- Improve ref check of constructors + [#24342](https://github.com/scala/scala3/pull/24342) +- Also copy param annots at mixin + [#24916](https://github.com/scala/scala3/pull/24916) +- Named arg may be deprecatedName + [#21588](https://github.com/scala/scala3/pull/21588) +- Fix: emit deprecation warnings when a symbol is annotated by a deprecated + annotation [#23906](https://github.com/scala/scala3/pull/23906) +- Fix #20063: Java leading annotation parsing + [#25503](https://github.com/scala/scala3/pull/25503) +- Accommodate adapted Scala 2 annotation value + [#17516](https://github.com/scala/scala3/pull/17516) +- Regression test for #3403 [#25521](https://github.com/scala/scala3/pull/25521) +- Fix: do not allow `@targetName` on primary constructor too + [#24682](https://github.com/scala/scala3/pull/24682) +- Add `@uncheckedOverride` annotation for definitions that may override + [#24545](https://github.com/scala/scala3/pull/24545) + +## Backend + +- Chore: add regression test for #4528 + [#24663](https://github.com/scala/scala3/pull/24663) +- Cast inline call results to result type, not method type + [#25111](https://github.com/scala/scala3/pull/25111) +- Regression tests for #25 [#25536](https://github.com/scala/scala3/pull/25536) +- Enforce JVM UTF-8 string limits in the backend + [#25300](https://github.com/scala/scala3/pull/25300) +- Do not simplify isInstanceOf if unrelated types might be subtypes at run-time + [#25535](https://github.com/scala/scala3/pull/25535) +- Adjust purity of trait with lazy member + [#23788](https://github.com/scala/scala3/pull/23788) +- Use `Objects.equals` in translating `==` + [#25314](https://github.com/scala/scala3/pull/25314) +- Fix generic signatures for mixin forwarders conflicting type parameter names + [#24567](https://github.com/scala/scala3/pull/24567) +- No mixin forwarder when ancestor is sealed + [#23482](https://github.com/scala/scala3/pull/23482) +- Simplify JVM interface list emission, remove a mini-phase + [#25203](https://github.com/scala/scala3/pull/25203) +- Chore: optimize lazy val with power of two + [#22428](https://github.com/scala/scala3/pull/22428) +- Fix infinite loop with bad section ends in TastyHeaderUnpickler + [#25676](https://github.com/scala/scala3/pull/25676) +- Fix TastyPrinter's JAR-walking logic to include subdirectories + [#25678](https://github.com/scala/scala3/pull/25678) + +## Classpath + +- Skip `module-info.class` files during REPL tab-completion + [#24838](https://github.com/scala/scala3/pull/24838) +- Fix for #22461 Empty ClassPath attribute in one or more classpath jars causes + crash [#22462](https://github.com/scala/scala3/pull/22462) + +## Coverage + +- Make coverage more similar to the one in Scala 2 + [#23722](https://github.com/scala/scala3/pull/23722) +- Fix #25555: Preserve singleton precision during scoverage lifting + [#25556](https://github.com/scala/scala3/pull/25556) +- Fixes #25482: Ensure package transformation properly handles language imports + in InstrumentCoverage [#25546](https://github.com/scala/scala3/pull/25546) +- Bugfix: Fix issues with Scala JS and coverage + [#24769](https://github.com/scala/scala3/pull/24769) +- Make coverage support incremental compilation + [#24773](https://github.com/scala/scala3/pull/24773) +- Add support for local coverage on and off with `// $COVERAGE-OFF$` and + `// $COVERAGE-ON$` [#24486](https://github.com/scala/scala3/pull/24486) +- Improvement: Try to optimize coverage phase + [#24925](https://github.com/scala/scala3/pull/24925) +- Scoverage: do not instrument erased values + [#25298](https://github.com/scala/scala3/pull/25298) + +## Default parameters + +- Fix named arguments evaluation order with by-name parameters + [#25161](https://github.com/scala/scala3/pull/25161) + +## Documentation + +- Docs: Enforce links checks in the generated docs + [#25425](https://github.com/scala/scala3/pull/25425) +- Improve the contributing guide + [#25384](https://github.com/scala/scala3/pull/25384) +- Extend copyright into 2026 + [#24927](https://github.com/scala/scala3/pull/24927) +- Update README: This is Scala 3 + [#25424](https://github.com/scala/scala3/pull/25424) +- Explain implicit vs default args + [#25311](https://github.com/scala/scala3/pull/25311) +- Fix some scaladoc in compiler + [#24166](https://github.com/scala/scala3/pull/24166) +- Docs: fix task name for scaladoc/testOnly + [#23678](https://github.com/scala/scala3/pull/23678) +- Replace internal tab char in comment in Synthesizer.scala + [#23691](https://github.com/scala/scala3/pull/23691) + +## Enums + +- Fix Java enum forwarders to pull from companion on initialization + [#24361](https://github.com/scala/scala3/pull/24361) +- Make enum apply ascriptions inferred types + [#25562](https://github.com/scala/scala3/pull/25562) + +## Exports + +- Fix: look at the underlying of a TermRef for a getter + [#24565](https://github.com/scala/scala3/pull/24565) + +## Extension Methods + +- Accept using right associative parameters + [#24766](https://github.com/scala/scala3/pull/24766) +- Add ctor proxies before exports + [#24884](https://github.com/scala/scala3/pull/24884) +- Fix pkg obj prefix of opaque tp ext meth + [#21527](https://github.com/scala/scala3/pull/21527) +- Better handling of illegal trees in `extension` + [#25368](https://github.com/scala/scala3/pull/25368) +- Fix LiftToAnchors for higher-kinded type applications + [#23672](https://github.com/scala/scala3/pull/23672) +- Prevent crash when extension not found + [#18830](https://github.com/scala/scala3/pull/18830) + +## GADTs + +- Also trust TypeTest when there is a Bind + [#24568](https://github.com/scala/scala3/pull/24568) + +## Implicits + +- Only check seen for LazyRef for TypeSizeAccumulator + [#20459](https://github.com/scala/scala3/pull/20459) +- Chore: add regression test for #8581 + [#24666](https://github.com/scala/scala3/pull/24666) +- Fix "Regression in zio/zio-schema for typer/implicit resolution" + [#24156](https://github.com/scala/scala3/pull/24156) +- Fix implicit scope liftToAnchors for parameter lower bounds + [#23679](https://github.com/scala/scala3/pull/23679) + +## Inference + +- Tweak type for tryParameterless + [#24716](https://github.com/scala/scala3/pull/24716) + +## Initialization + +- Refine parameter adaptation logic for arrays + [#23591](https://github.com/scala/scala3/pull/23591) + +## Infrastructure + +- Tweak tests to not fail under jdk 25 + [#25148](https://github.com/scala/scala3/pull/25148) +- Bump actions/upload-artifact from 6 to 7 + [#25423](https://github.com/scala/scala3/pull/25423) +- Bump actions/download-artifact from 7 to 8 + [#25420](https://github.com/scala/scala3/pull/25420) +- Bump VirtusLab/scala-cli-setup from 1.12.2 to 1.12.3 + [#25422](https://github.com/scala/scala3/pull/25422) +- Bump coursier/cache-action from 7 to 8 + [#25220](https://github.com/scala/scala3/pull/25220) +- Bump actions/upload-artifact from 5 to 6 + [#24758](https://github.com/scala/scala3/pull/24758) +- Bump actions/download-artifact from 6 to 7 + [#24759](https://github.com/scala/scala3/pull/24759) +- Bump actions/checkout from 5 to 6 + [#24517](https://github.com/scala/scala3/pull/24517) +- Bump actions/upload-artifact from 4 to 5 + [#24268](https://github.com/scala/scala3/pull/24268) +- Bump actions/download-artifact from 5 to 6 + [#24271](https://github.com/scala/scala3/pull/24271) +- Bump actions/download-artifact from 4 to 5 + [#23711](https://github.com/scala/scala3/pull/23711) +- Bump actions/checkout from 4 to 5 + [#23710](https://github.com/scala/scala3/pull/23710) +- Chore: bump sbt to 1.11.5 [#23805](https://github.com/scala/scala3/pull/23805) +- Chore(deps): bump actions/setup-java from 4 to 5 + [#23812](https://github.com/scala/scala3/pull/23812) +- Chore: Use numberOfWorkers instead + [#24338](https://github.com/scala/scala3/pull/24338) +- Improvement: Additional way of defining ENABLE_BSP_ALL_PROJECTS + [#24122](https://github.com/scala/scala3/pull/24122) +- Formatting: Format presentation-compiler with scalafmt + [#24977](https://github.com/scala/scala3/pull/24977) +- Ensure mistakes in CI inputs are caught early + [#25677](https://github.com/scala/scala3/pull/25677) + +## Inline + +- Chore: add regression test for #22575 + [#25337](https://github.com/scala/scala3/pull/25337) +- Ensure the inline match scrutinee type conforms to the recalculated type + [#24988](https://github.com/scala/scala3/pull/24988) +- Make containsOuterRefsAnywhere follow aliases + [#25214](https://github.com/scala/scala3/pull/25214) +- Find annotation args in inline expansion + [#24895](https://github.com/scala/scala3/pull/24895) +- Chore: add regression test for #10343 + [#24668](https://github.com/scala/scala3/pull/24668) +- Improve the test for creating protected method accessors for inlines + [#25865](https://github.com/scala/scala3/pull/25865) +- Do not update Symbol defTrees when retyping after Inlining + [#23870](https://github.com/scala/scala3/pull/23870) +- Prevent opaque types leaking from transparent inline methods + [#23792](https://github.com/scala/scala3/pull/23792) +- Chore: add regression test for #8194 + [#24655](https://github.com/scala/scala3/pull/24655) +- Also substitute symbols in case bindings symbols + [#24849](https://github.com/scala/scala3/pull/24849) +- Inlines: decide sealing against original target, while using widened type as + cast destination [#25448](https://github.com/scala/scala3/pull/25448) +- Fix inline match on blocks with multiple statements + [#20125](https://github.com/scala/scala3/pull/20125) + +## JDK + +- Skip over JEP 445 compact compilation units + [#23247](https://github.com/scala/scala3/pull/23247) +- Handle MissingType during Erasure phase + [#24817](https://github.com/scala/scala3/pull/24817) +- Don't generate bridge methods for inaccessible Java package-private methods + [#25166](https://github.com/scala/scala3/pull/25166) +- Fix isJvmAccessible to handle nested protected Java classes + [#24625](https://github.com/scala/scala3/pull/24625) +- Upgrade ASM to 9.9 for JDK 26 support + [#24430](https://github.com/scala/scala3/pull/24430) +- Fix java record varargs field accessor + [#24172](https://github.com/scala/scala3/pull/24172) +- Do not overwrite the supertype of non-trait super in Erasure + [#26081](https://github.com/scala/scala3/pull/26081) + +## Linting + +- Fix false unused import warning for given CanEqual in pattern matching + [#25231](https://github.com/scala/scala3/pull/25231) +- Move second CheckUnused after pattern matcher + [#25114](https://github.com/scala/scala3/pull/25114) +- Extension check handles hiBound of opaque type + [#25172](https://github.com/scala/scala3/pull/25172) +- Unused lint ignores args to ctor of enclosing class + [#24768](https://github.com/scala/scala3/pull/24768) +- Parameter name can be masked in import + [#24635](https://github.com/scala/scala3/pull/24635) +- CheckUnused examines type of Apply + [#24465](https://github.com/scala/scala3/pull/24465) +- Check for synthetic case methods in unused lint + [#24239](https://github.com/scala/scala3/pull/24239) +- 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) +- Ignore warnings when compiletime.testing is imported + [#24036](https://github.com/scala/scala3/pull/24036) +- Unused lint does not rewrite suppressed warnings + [#24055](https://github.com/scala/scala3/pull/24055) +- Drill through type lambda for tree symbol + [#23699](https://github.com/scala/scala3/pull/23699) +- Invent given pattern name in for comprehension + [#23121](https://github.com/scala/scala3/pull/23121) +- Check inline expansion for exclusion + [#23019](https://github.com/scala/scala3/pull/23019) +- Unused var message mentions unread or unset + [#23719](https://github.com/scala/scala3/pull/23719) +- Warn unused masking imports, remove obsolete unused options + [#23757](https://github.com/scala/scala3/pull/23757) +- Distrust tree pos for patvar lint + [#25043](https://github.com/scala/scala3/pull/25043) +- Match apply in PolyFunction for override + [#25505](https://github.com/scala/scala3/pull/25505) +- Value discard checks inline call for exemption + [#25554](https://github.com/scala/scala3/pull/25554) +- Lint enum case in outer context + [#24652](https://github.com/scala/scala3/pull/24652) +- Preserve select of exported member in newExpr + [#24570](https://github.com/scala/scala3/pull/24570) +- Always traverse Inlined.call in linter + [#24043](https://github.com/scala/scala3/pull/24043) +- Lint function arrow intended context function + [#23847](https://github.com/scala/scala3/pull/23847) +- 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) +- Regression test locals [#25433](https://github.com/scala/scala3/pull/25433) +- Augment regression test [#24327](https://github.com/scala/scala3/pull/24327) + +## Match Types + +- Fix regression: Prioritize tree type over proto type when typing Binds + [#24175](https://github.com/scala/scala3/pull/24175) + +## Metaprogramming + +- Warn about encoded pkg obj names + [#22707](https://github.com/scala/scala3/pull/22707) +- Bugfix: Fix possible SuspendException thrown when using macros + [#24174](https://github.com/scala/scala3/pull/24174) + +## Nullability + +- Patch to allow interop with XMLBuilder and -Yexplicit-nulls + [#25221](https://github.com/scala/scala3/pull/25221) +- Add jspecify NonNull annotation to NotNullAnnots list + [#24487](https://github.com/scala/scala3/pull/24487) +- Add quick fix to add .nn [#23598](https://github.com/scala/scala3/pull/23598) + +## Opaque Types + +- Preserve insertion order when balancing And/OrType + [#25361](https://github.com/scala/scala3/pull/25361) +- Treat synthetic casts as pure + [#25126](https://github.com/scala/scala3/pull/25126) +- Make opaque types decomposable + [#24068](https://github.com/scala/scala3/pull/24068) + +## Overloading + +- Pretype args before skipping clause of overloaded application + [#23012](https://github.com/scala/scala3/pull/23012) +- Fix varargs overload resolution with wildcard types + [#24669](https://github.com/scala/scala3/pull/24669) +- Fix wildcardArgOK for mixed wildcard/concrete type args + [#25001](https://github.com/scala/scala3/pull/25001) +- Regression test #16004 [#24086](https://github.com/scala/scala3/pull/24086) +- Tweak tryParameterless to use readapt + [#24078](https://github.com/scala/scala3/pull/24078) +- Fix override checking for Java methods with covariant array + [#24408](https://github.com/scala/scala3/pull/24408) +- Prefer non-extension to extension + [#24943](https://github.com/scala/scala3/pull/24943) +- Fix class super-call invokespecial owner + [#25437](https://github.com/scala/scala3/pull/25437) + +## Parser + +- Accept `primitive.class` in annotations + [#25363](https://github.com/scala/scala3/pull/25363) +- End of new expr never shrinks + [#25253](https://github.com/scala/scala3/pull/25253) +- Permit new with trivial end + [#24331](https://github.com/scala/scala3/pull/24331) +- Improve message for nested package missing braces + [#23816](https://github.com/scala/scala3/pull/23816) +- Check seen for TypeParamRef in TypeSizeAccumulator + [#25264](https://github.com/scala/scala3/pull/25264) +- Observe outdent also for type cases and type blocks in quotes + [#25519](https://github.com/scala/scala3/pull/25519) +- Fix: import resolution for Java + [#25537](https://github.com/scala/scala3/pull/25537) +- Error tree on outdent is zero extent + [#23796](https://github.com/scala/scala3/pull/23796) +- Don't automatically insert OUTDENT before COMMA + [#25501](https://github.com/scala/scala3/pull/25501) +- Fix: allow postfix setters under language.postfixOps + [#23775](https://github.com/scala/scala3/pull/23775) +- Additional regression tests + [#25506](https://github.com/scala/scala3/pull/25506) +- Context bound always has a span + [#25809](https://github.com/scala/scala3/pull/25809) +- Don't pop outermost region when skipping + [#25738](https://github.com/scala/scala3/pull/25738) + +## Pattern Matching + +- Emit unchecked warnings for trait patterns + [#24962](https://github.com/scala/scala3/pull/24962) +- Fix false exhaustivity warning for seq match + [#23968](https://github.com/scala/scala3/pull/23968) +- Call inhabited for AppliedType recursively + [#23964](https://github.com/scala/scala3/pull/23964) +- Fix Ycheck crash from singleton tuple selects after specialization + [#25331](https://github.com/scala/scala3/pull/25331) +- Fix false unreachable case warning + [#23800](https://github.com/scala/scala3/pull/23800) +- Use upper bound of abstract types in exhaustivity checking + [#23909](https://github.com/scala/scala3/pull/23909) +- Extend caching in the space engine beyond local context + [#23483](https://github.com/scala/scala3/pull/23483) +- Prevent from getting children in case of non-sealed private + [#25359](https://github.com/scala/scala3/pull/25359) +- Use MatchCaseUnreachable [#24829](https://github.com/scala/scala3/pull/24829) +- Preserve singleton types for final val aliases to Java enum constants + [#24980](https://github.com/scala/scala3/pull/24980) +- Try expanding case class to the product of its fields + [#25454](https://github.com/scala/scala3/pull/25454) +- Never widen constant type on selector type in matches + [#25197](https://github.com/scala/scala3/pull/25197) + +## Pickling + +- Fix #24456: Cleanup the info of a Bind symbol in posttyper + [#24490](https://github.com/scala/scala3/pull/24490) +- Fix #24207: Cleanup the type of `UnApply` trees in posttyper + [#24259](https://github.com/scala/scala3/pull/24259) +- Chore: add a regression test for #15786 + [#24658](https://github.com/scala/scala3/pull/24658) +- Don't absolutize sources outside of the sourceroot in TASTy + [#25429](https://github.com/scala/scala3/pull/25429) + +## Positions + +- Preserve span of `this` during statification of trait constructors + [#25354](https://github.com/scala/scala3/pull/25354) +- Compute the right span for abstract error messages + [#23853](https://github.com/scala/scala3/pull/23853) +- Use a better span for an anonymous class + [#24640](https://github.com/scala/scala3/pull/24640) + +## Presentation Compiler + +- Improvement: Use method type if it's available + [#25641](https://github.com/scala/scala3/pull/25641) +- Bugfix: Don't add square braces in context bounds + [#25798](https://github.com/scala/scala3/pull/25798) +- Bugfix: Fix issue with empty name throwing exceptions + [#25752](https://github.com/scala/scala3/pull/25752) +- Bugfix: Fix wrong type in InferredType code action + [#25469](https://github.com/scala/scala3/pull/25469) +- Bugfix: Fix issues in presentation compiler when using Scala JS + [#25450](https://github.com/scala/scala3/pull/25450) +- Improvement: Add fallbacks in non critical places for presentation compiler + [#25483](https://github.com/scala/scala3/pull/25483) +- Fix & feat: improvements in selection ranges + [#25290](https://github.com/scala/scala3/pull/25290) +- Feat(pc): add closing labels inlay hints + [#25178](https://github.com/scala/scala3/pull/25178) +- Bugfix: remove stale top-level symbols between runs + [#25317](https://github.com/scala/scala3/pull/25317) +- Bugfix: Fix various minor presentation compiler issues + [#25256](https://github.com/scala/scala3/pull/25256) +- Fix: ignore implicit apply for signature help + [#25294](https://github.com/scala/scala3/pull/25294) +- Fix incorrect XRay hint [#24738](https://github.com/scala/scala3/pull/24738) +- Presentation compiler: propagate isDebug timeout check to all tests + [#25068](https://github.com/scala/scala3/pull/25068) +- Handle inlined tree in implicit parameter inlay hints + [#24762](https://github.com/scala/scala3/pull/24762) +- Create extension method in infer method action + [#24721](https://github.com/scala/scala3/pull/24721) +- Fix opaque types displayed as `Nothing & Any` in hover and signature help + [#24921](https://github.com/scala/scala3/pull/24921) +- Fix negative activeParameter in signature help + [#24945](https://github.com/scala/scala3/pull/24945) +- Closes #7532: Parameter hints not working for functions + [#23854](https://github.com/scala/scala3/pull/23854) +- Improvement: Allow passing -explain to the presentation compiler + [#24740](https://github.com/scala/scala3/pull/24740) +- Exclude XRayModeHint for synthetics + [#24722](https://github.com/scala/scala3/pull/24722) +- Add unsafe RawPresentationCompiler implementation + [#24133](https://github.com/scala/scala3/pull/24133) +- Bugfix: Make sure that we can query tree.source + [#24123](https://github.com/scala/scala3/pull/24123) +- Improvement: additional completions for using clause + [#23647](https://github.com/scala/scala3/pull/23647) +- Inlining interpolated variables + [#25144](https://github.com/scala/scala3/pull/25144) +- Bugfix: inline value false-positive shadowing with lambda params + [#25140](https://github.com/scala/scala3/pull/25140) +- Fix issue with pc breaking in requiredMethod on newly overloaded valueOf + [#23708](https://github.com/scala/scala3/pull/23708) +- Pc: completions - do not add `[]` for `... derives TC@@` + [#23811](https://github.com/scala/scala3/pull/23811) +- Porting XRayModeHints [#23891](https://github.com/scala/scala3/pull/23891) +- Improve symbol order in completions provided by the presentation compiler + [#23888](https://github.com/scala/scala3/pull/23888) +- Bugfix: Fix issues with Scala JS and the presentation compiler + [#25550](https://github.com/scala/scala3/pull/25550) +- Bugfix: Show hover on derives clause + [#25804](https://github.com/scala/scala3/pull/25804) +- Improvement: improve perfomance of the presentation compiler + [#25769](https://github.com/scala/scala3/pull/25769) +- Coursier dependency imports should have lexicographic ordering + [#21592](https://github.com/scala/scala3/pull/21592) +- feature: Improve the logical sources provider + ([#26079](https://github.com/scala/scala3/pull/26079)) +- feature: Add logical package parsing for sourcepath + ([#25446](https://github.com/scala/scala3-lts/pull/25446)) + +## Quotes + +- Fix crashing for context bound type variables in quoted patterns + [#25286](https://github.com/scala/scala3/pull/25286) +- Fix #25208: handle AndType(TermRef, TypeRef) in TreeUnpickler path trees + [#25209](https://github.com/scala/scala3/pull/25209) +- Remove redundant mapOver in fixThisTypeModuleClassReferences + [#25440](https://github.com/scala/scala3/pull/25440) +- Make typeArgs in QuotesImpl work for AnnotatedTypes + [#24018](https://github.com/scala/scala3/pull/24018) +- Improve LambdaTypes handling in Quotes reflect memberType + [#25786](https://github.com/scala/scala3/pull/25786) +- Backport "Don't lookahead from interpolation" to 3.3 LTS + ([#25834](https://github.com/scala/scala3/pull/25834)) + +## Reflection + +- Fix Select.copy in reflect API losing DerivedName for default getters + [#25269](https://github.com/scala/scala3/pull/25269) +- Do not expose ClassInfo in memberType in reflect API + [#24922](https://github.com/scala/scala3/pull/24922) +- Quotes reflect: Allow to return DefDef from a val symbol tree + [#22603](https://github.com/scala/scala3/pull/22603) + +## Reporting + +- Force conditional warnings when configured + [#25552](https://github.com/scala/scala3/pull/25552) +- Use deprecatedOverriding message + [#25439](https://github.com/scala/scala3/pull/25439) +- Fix #24711: Use dot notation for Java nested types in error messages + [#24919](https://github.com/scala/scala3/pull/24919) +- Enhance WInferUnion to not warn propagated explicit union types + [#25280](https://github.com/scala/scala3/pull/25280) +- Fixes #24233: Remove misleading JDK compatibility hint for TASTy errors + [#25171](https://github.com/scala/scala3/pull/25171) +- More descriptive "not found" message when type/term exists with the expected + term/type name [#24959](https://github.com/scala/scala3/pull/24959) +- Mention named givens in double def explainer + [#23833](https://github.com/scala/scala3/pull/23833) +- Do not discard amended format when f-interpolator warns + [#23697](https://github.com/scala/scala3/pull/23697) +- Use dollar-free param names in stub implementations + [#25646](https://github.com/scala/scala3/pull/25646) +- Error mentions bad \_1 selector + [#25616](https://github.com/scala/scala3/pull/25616) +- Add ErrorMessageID to -Winfer-union warnings (#25073) + [#25249](https://github.com/scala/scala3/pull/25249) +- Addendum when apply error involves default arg + [#21133](https://github.com/scala/scala3/pull/21133) +- Warn on Scala 2 auto-apply if loosely nullary + [#24811](https://github.com/scala/scala3/pull/24811) +- Forward reference error includes line numbers + [#22534](https://github.com/scala/scala3/pull/22534) +- Use other tree for actual symbol of Assign + [#22869](https://github.com/scala/scala3/pull/22869) +- Add "did you mean to use parentheses/a block" when finding a complex + expression on the RHS of an infix operator + [#25099](https://github.com/scala/scala3/pull/25099) +- Fix printing references to nested objects + [#25086](https://github.com/scala/scala3/pull/25086) +- Add error note for type mismatch involving blocks without last expression + [#25181](https://github.com/scala/scala3/pull/25181) +- Don't suggest invisible members of root imports + [#24999](https://github.com/scala/scala3/pull/24999) +- Improve multi-line code message rendering + [#24723](https://github.com/scala/scala3/pull/24723) +- Warn if type argument was inferred as union type + [#24258](https://github.com/scala/scala3/pull/24258) +- Improve printing of strings + [#22945](https://github.com/scala/scala3/pull/22945) +- Better error message: reassignment to val + [#24184](https://github.com/scala/scala3/pull/24184) +- Add addendum to `private val` parameter variance error message + [#23876](https://github.com/scala/scala3/pull/23876) +- Regression tests [#24876](https://github.com/scala/scala3/pull/24876) + +## REPL + +- Make REPL type echo-ing use partially qualified names where made possible by + imports [#24850](https://github.com/scala/scala3/pull/24850) +- Remove pipes from multi-line REPL prompts + [#24307](https://github.com/scala/scala3/pull/24307) +- Fix "Colon as operator part at beginning of line in REPL" + [#24942](https://github.com/scala/scala3/pull/24942) +- Trap Ctrl-C in the REPL: if no command is running clear the prompt, if some + command is running ask for confirmation before exiting + [#24127](https://github.com/scala/scala3/pull/24127) + +## Rewrites + +- Deduplicate patches before applying them to sources + [#24215](https://github.com/scala/scala3/pull/24215) + +## Runner + +- Fix for #22443: prevent stty from being called when not running in a terminal + [#22444](https://github.com/scala/scala3/pull/22444) + +## Scaladoc + +- Do not hide when a `class` or a `trait` extends `Any` + [#25403](https://github.com/scala/scala3/pull/25403) +- Ability to hide snippets from the documentation + [#25411](https://github.com/scala/scala3/pull/25411) +- Use `` tags for exponents in Scaladoc comments + [#25645](https://github.com/scala/scala3/pull/25645) +- Fix duplicate member entries in Scaladoc generation + [#24712](https://github.com/scala/scala3/pull/24712) +- Scaladoc Overload Method Link Fix + [#25156](https://github.com/scala/scala3/pull/25156) +- Init MacroClassLoader in scaladoc + [#25141](https://github.com/scala/scala3/pull/25141) +- Scaladoc: Take title from sidebar.yml into account + [#18997](https://github.com/scala/scala3/pull/18997) +- [scaladoc] Allow to additional scalacOptions in Markdown code snippets + [#24755](https://github.com/scala/scala3/pull/24755) +- Fetch the right kind for synthesised definitions + [#25405](https://github.com/scala/scala3/pull/25405) +- Add the `transparent` flag to `java.lang.Object` + [#25394](https://github.com/scala/scala3/pull/25394) +- Parse HTML properly in Scaladoc + [#25681](https://github.com/scala/scala3/pull/25681) + +## SemanticDB + +- Add context parameters to SemanticDB synthetics + [#23381](https://github.com/scala/scala3/pull/23381) +- Better refinement symbols in semanticDB + [#18604](https://github.com/scala/scala3/pull/18604) +- Encapsulate SemanticDB package, unify symbol and document printing + [#25084](https://github.com/scala/scala3/pull/25084) + +## Testing + +- Fix line and path separators in test + [#23534](https://github.com/scala/scala3/pull/23534) +- Fix: update checkfile for i23905 + [#24064](https://github.com/scala/scala3/pull/24064) + +## Testing framework + +- Reject check file match with stack trace + [#22389](https://github.com/scala/scala3/pull/22389) +- Cleanup compiler test. remove unnecessary `isJavaAtLeast` conditions + [#24589](https://github.com/scala/scala3/pull/24589) +- Bugfix: Don't run coverage tests in testCompilation without --coverage flag + [#24932](https://github.com/scala/scala3/pull/24932) +- Cleaner, quieter test rig tests + [#24073](https://github.com/scala/scala3/pull/24073) +- Skip tests with no sources such as .scala-build dir + [#25173](https://github.com/scala/scala3/pull/25173) +- Report info logs in checked test output if enabled with -Ylog + [#25391](https://github.com/scala/scala3/pull/25391) +- Bugfix: Fix flaky test in ScalaJSCompletionsSuite + [#25539](https://github.com/scala/scala3/pull/25539) +- Add regression test for #23195 + [#24853](https://github.com/scala/scala3/pull/24853) +- Test hover on backticked identifiers with whitespace + [#25142](https://github.com/scala/scala3/pull/25142) + +## Transform + +- Check extension method at typer + [#24199](https://github.com/scala/scala3/pull/24199) +- Always encode in derived name + [#23478](https://github.com/scala/scala3/pull/23478) +- Fix "Scala 3.8.3 compiling covariant overrides with separate compilation + causes AbstractMethodError at runtime" + [#25659](https://github.com/scala/scala3/pull/25659) +- Make Phase.run protected [#25366](https://github.com/scala/scala3/pull/25366) + +## Tuples + +- Fix inconsistent `typeSize` calculation for `TupleN` vs recursive pair + encodings [#24743](https://github.com/scala/scala3/pull/24743) +- Fix: make vals created in desugaring of n-ary lambdas non-synthetic + [#23896](https://github.com/scala/scala3/pull/23896) +- Normalize tuple types when typing patterns + [#25335](https://github.com/scala/scala3/pull/25335) +- Fix #24997: Avoid unnecessary CHECKCAST in untupled function parameters + [#25085](https://github.com/scala/scala3/pull/25085) + +## Typeclass Derivation + +- Fix #23043: spurious package conflict from non-class directories + [#25441](https://github.com/scala/scala3/pull/25441) +- Also checkRefinement for Singleton mirror + [#24080](https://github.com/scala/scala3/pull/24080) + +## Typer + +- Use TypeComparer.constValue in TypeEval + [#24718](https://github.com/scala/scala3/pull/24718) +- Fix crash in Selectable unapply with custom applyDynamic + [#24343](https://github.com/scala/scala3/pull/24343) +- Prevent crash in SAM conversion with mismatched arity + [#23877](https://github.com/scala/scala3/pull/23877) +- Detect `InsertedApply` on `Apply` to break synthetic apply loops, not just + `Select` [#25463](https://github.com/scala/scala3/pull/25463) +- Typer: remove by-name lifting and replace default-getter args with fresh syms + [#25502](https://github.com/scala/scala3/pull/25502) +- Refine isEffectivelyFinal to avoid no-owner crash + [#23675](https://github.com/scala/scala3/pull/23675) +- Do not generate access proxies for locally defined protected defs + [#25056](https://github.com/scala/scala3/pull/25056) +- Use `dealiasKeepRefiningAnnots` in `AvoidMap` + [#25609](https://github.com/scala/scala3/pull/25609) +- Fix #24573: Add stricter checks for platform SAM compatibility + [#24624](https://github.com/scala/scala3/pull/24624) +- Make isExactlyNothing and isExactlyAny work for And/OrTypes + [#24016](https://github.com/scala/scala3/pull/24016) +- Handle assertion error in TyperState + [#23665](https://github.com/scala/scala3/pull/23665) +- Fix: correctly require a `ClassTag` when building a multidimensional `Array` + [#23902](https://github.com/scala/scala3/pull/23902) +- Fix crash when processing path dependent context functions using HKTs + [#25530](https://github.com/scala/scala3/pull/25530) +- Regression test [#24820](https://github.com/scala/scala3/pull/24820) +- Chore: add regression test for #13889 + [#24657](https://github.com/scala/scala3/pull/24657) +- Handle assertion error in TyperState + [#23665](https://github.com/scala/scala3/pull/23665) + +## XML + +- Use `toVector` for XML literal sequences + [#23221](https://github.com/scala/scala3/pull/23221) + +## Union types + +- Dealias in isSimpleThrowable check + [#24744](https://github.com/scala/scala3/pull/24744) diff --git a/community-build/community-projects/cats b/community-build/community-projects/cats index 771c6c802f59..683f28dd0da4 160000 --- a/community-build/community-projects/cats +++ b/community-build/community-projects/cats @@ -1 +1 @@ -Subproject commit 771c6c802f59c72dbc1be1898081c9c882ddfeb0 +Subproject commit 683f28dd0da42e20c4bbf1515c7a7839c3d3c7a9 diff --git a/community-build/community-projects/scala-collection-compat b/community-build/community-projects/scala-collection-compat index c9d3a8b160a3..53c3758f7ccc 160000 --- a/community-build/community-projects/scala-collection-compat +++ b/community-build/community-projects/scala-collection-compat @@ -1 +1 @@ -Subproject commit c9d3a8b160a35c9915816dd84a1063e18db4a84a +Subproject commit 53c3758f7ccc382dbd7e7f7d0eb6e915dff072ba diff --git a/community-build/community-projects/scodec b/community-build/community-projects/scodec index b74f2085f071..9b0423b90de9 160000 --- a/community-build/community-projects/scodec +++ b/community-build/community-projects/scodec @@ -1 +1 @@ -Subproject commit b74f2085f07165d84b32c39eb214c9cc838711cc +Subproject commit 9b0423b90de95fc968fafe4543e6b16ef9f81d08 diff --git a/community-build/src/scala/dotty/communitybuild/CommunityBuildRunner.scala b/community-build/src/scala/dotty/communitybuild/CommunityBuildRunner.scala index b3065fefe87f..6aaaedb8a3dd 100644 --- a/community-build/src/scala/dotty/communitybuild/CommunityBuildRunner.scala +++ b/community-build/src/scala/dotty/communitybuild/CommunityBuildRunner.scala @@ -13,8 +13,7 @@ object CommunityBuildRunner: * is necessary since we run tests each time on a fresh * Docker container. We run the update on Docker container * creation time to create the cache of the dependencies - * and avoid network overhead. See https://github.com/lampepfl/dotty-drone - * for more infrastructural details. + * and avoid network overhead. */ extension (self: CommunityProject) def run()(using suite: CommunityBuildRunner): Unit = diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index c8e16d7df946..bd5b8f48485a 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.5", + "-sbt-version", "1.11.5", "-Dsbt.supershell=false", s"-Ddotty.communitybuild.dir=$communitybuildDir", s"--addPluginSbtFile=$sbtPluginFilePath" diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index 61a69398d66e..ee19f3424bb4 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -77,7 +77,7 @@ class CommunityBuildTestC: @Test def parboiled2 = projects.parboiled2.run() @Test def playJson = projects.playJson.run() @Test def pprint = projects.pprint.run() - @Test def protoquill = projects.protoquill.run() + // @Test def protoquill = projects.protoquill.run() @Test def requests = projects.requests.run() @Test def scalacheck = projects.scalacheck.run() @Test def scalaCollectionCompat = projects.scalaCollectionCompat.run() diff --git a/compiler/src/dotty/tools/MainGenericRunner.scala b/compiler/src/dotty/tools/MainGenericRunner.scala index 1540cc86d7a6..1df98399cb5c 100644 --- a/compiler/src/dotty/tools/MainGenericRunner.scala +++ b/compiler/src/dotty/tools/MainGenericRunner.scala @@ -160,7 +160,7 @@ object MainGenericRunner { .withScriptArgs(tail*) .noSave // -save not useful here case arg :: tail => - val line = Try(Source.fromFile(arg).getLines.toList).toOption.flatMap(_.headOption) + val line = Try(Source.fromFile(arg).getLines().toList).toOption.flatMap(_.headOption) lazy val hasScalaHashbang = { val s = line.getOrElse("") ; s.startsWith("#!") && s.contains("scala") } if arg.endsWith(".scala") || arg.endsWith(".sc") || hasScalaHashbang then settings diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala index 9c270482c6d4..75e281c1b64f 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala @@ -3,18 +3,15 @@ package backend package jvm import scala.language.unsafeNulls - import scala.annotation.{switch, tailrec} import scala.collection.mutable.SortedMap - import scala.tools.asm import scala.tools.asm.{Handle, Opcodes} import BCodeHelpers.InvokeStyle - import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.CompilationUnit import dotty.tools.dotc.core.Constants.* -import dotty.tools.dotc.core.Flags.{Label => LabelFlag, _} +import dotty.tools.dotc.core.Flags.{Label as LabelFlag, *} import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.core.StdNames.{nme, str} import dotty.tools.dotc.core.Symbols.* @@ -25,6 +22,7 @@ import dotty.tools.dotc.core.Phases.* import dotty.tools.dotc.core.Decorators.em import dotty.tools.dotc.report import dotty.tools.dotc.ast.Trees.SyntheticUnit +import dotty.tools.dotc.util.SrcPos /* * @@ -442,12 +440,12 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { genLoad(t, generatedType) } - case Literal(value) => + case l @ Literal(value) => if (value.tag != UnitTag) (value.tag, expectedType) match { case (IntTag, LONG ) => bc.lconst(value.longValue); generatedType = LONG case (FloatTag, DOUBLE) => bc.dconst(value.doubleValue); generatedType = DOUBLE case (NullTag, _ ) => bc.emit(asm.Opcodes.ACONST_NULL); generatedType = srNullRef - case _ => genConstant(value); generatedType = tpeTK(tree) + case _ => genConstant(value, l.srcPos); generatedType = tpeTK(tree) } case blck @ Block(stats, expr) => @@ -555,8 +553,8 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { * must-single-thread * Otherwise it's safe to call from multiple threads. */ - def genConstant(const: Constant): Unit = { - (const.tag/*: @switch*/) match { + private def genConstant(const: Constant, pos: SrcPos): Unit = { + (const.tag: @switch) match { case BooleanTag => bc.boolconst(const.booleanValue) @@ -573,7 +571,10 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { case StringTag => assert(const.value != null, const) // TODO this invariant isn't documented in `case class Constant` - mnode.visitLdcInsn(const.stringValue) // `stringValue` special-cases null, but not for a const with StringTag + if BCodeUtils.checkConstantStringLength(const.stringValue) then + mnode.visitLdcInsn(const.stringValue) // `stringValue` special-cases null, but not for a const with StringTag + else + report.error("String constant is too long for the JVM", pos) case NullTag => emit(asm.Opcodes.ACONST_NULL) @@ -588,7 +589,11 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { jlClassRef.descriptor ) else - mnode.visitLdcInsn(tp.toASMType) + val toASM = tp.toASMType + if BCodeUtils.checkConstantStringLength(toASM.getInternalName) then + mnode.visitLdcInsn(toASM) + else + report.error("Type name is too long for the JVM", pos) case _ => abort(s"Unknown constant value: $const") } @@ -759,7 +764,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { if (t.symbol ne defn.Object_synchronized) genTypeApply(t) else genSynchronized(app, expectedType) - case Apply(fun @ DesugaredSelect(Super(superQual, _), _), args) => + case Apply(fun @ DesugaredSelect(superRef @ Super(superQual, _), _), args) => // 'super' call: Note: since constructors are supposed to // return an instance of what they construct, we have to take // special care. On JVM they are 'void', and Scala forbids (syntactically) @@ -772,7 +777,20 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { stack.push(superQualTK) genLoadArguments(args, paramTKs(app)) stack.pop() - generatedType = genCallMethod(fun.symbol, InvokeStyle.Super, app.span) + // The receiver in bytecode should be based on the call site qualifier type, + // instead of method declaration owner class + // (`fun.symbol.owner`, which is default receiver type in `genCallMethod`). + // This avoids IllegalAccessError when the declaration owner is less accessible than the + // direct superclass (see https://github.com/scala/scala3/issues/22628). + val specificReceiver = superRef.tpe.dealias match { + case superType: SuperType => + val sym = superType.supertpe.typeSymbol + if (sym.isClass && !isEmittedInterface(sym)) sym + else null + case _ => + null + } + generatedType = genCallMethod(fun.symbol, InvokeStyle.Super, app.span, specificReceiver) // 'new' constructor call: Note: since constructors are // thought to return an instance of what they construct, @@ -1395,7 +1413,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { /** * Generate a method invocation. If `specificReceiver != null`, it is used as receiver in the * invocation instruction, otherwise `method.owner`. A specific receiver class is needed to - * prevent an IllegalAccessError, (aladdin bug 455). + * prevent IllegalAccessError in some virtual and super calls (aladdin bug 455, i22628). */ def genCallMethod(method: Symbol, style: InvokeStyle, pos: Span = NoSpan, specificReceiver: Symbol = null): BType = { val methodOwner = method.owner @@ -1403,7 +1421,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { // the class used in the invocation's method descriptor in the classfile val receiverClass = { if (specificReceiver != null) - assert(style.isVirtual || specificReceiver == methodOwner, s"specificReceiver can only be specified for virtual calls. $method - $specificReceiver") + assert(style.isVirtual || style.isSuper || specificReceiver == methodOwner, s"specificReceiver can only be specified for virtual and super calls. $method - $specificReceiver") val useSpecificReceiver = specificReceiver != null && !defn.isBottomClass(specificReceiver) && !method.isScalaStatic val receiver = if (useSpecificReceiver) specificReceiver else methodOwner @@ -1443,10 +1461,11 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val isInterface = isEmittedInterface(receiverClass) import InvokeStyle.* if (style == Super) { + val ownerBType = toTypeKind(method.owner.info) + if (isInterface) { + superCallTargets.add(ownerBType.asClassBType) + } if (isInterface && !method.is(JavaDefined)) { - val args = new Array[BType](bmType.argumentTypes.length + 1) - val ownerBType = toTypeKind(method.owner.info) - bmType.argumentTypes.copyToArray(args, 1) val staticDesc = MethodBType(ownerBType :: bmType.argumentTypes, bmType.returnType).descriptor val staticName = traitSuperAccessorName(method) bc.invokestatic(receiverName, staticName, staticDesc, isInterface) @@ -1712,27 +1731,12 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { genCallMethod(defn.Any_equals, InvokeStyle.Virtual) genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump) } else { - // l == r -> if (l eq null) r eq null else l.equals(r) - val eqEqTempLocal = locals.makeLocal(ObjectRef, nme.EQEQ_LOCAL_VAR.mangledString, defn.ObjectType, r.span) - val lNull = new asm.Label - val lNonNull = new asm.Label - + // l == r -> Objects.equals(l, r) genLoad(l, ObjectRef) stack.push(ObjectRef) genLoad(r, ObjectRef) stack.pop() - locals.store(eqEqTempLocal) - bc dup ObjectRef - genCZJUMP(lNull, lNonNull, Primitives.EQ, ObjectRef, targetIfNoJump = lNull) - - markProgramPoint(lNull) - bc drop ObjectRef - locals.load(eqEqTempLocal) - genCZJUMP(success, failure, Primitives.EQ, ObjectRef, targetIfNoJump = lNonNull) - - markProgramPoint(lNonNull) - locals.load(eqEqTempLocal) - genCallMethod(defn.Any_equals, InvokeStyle.Virtual) + genCallMethod(defn.Objects_equals, InvokeStyle.Static) genCZJUMP(success, failure, Primitives.NE, BOOL, targetIfNoJump) } } @@ -1773,8 +1777,6 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { val returnUnit = lambdaTarget.info.resultType.typeSymbol == defn.UnitClass val functionalInterfaceDesc: String = generatedType.descriptor val desc = capturedParamsTypes.map(tpe => toTypeKind(tpe)).mkString(("("), "", ")") + functionalInterfaceDesc - // TODO specialization - val instantiatedMethodType = new MethodBType(lambdaParamTypes.map(p => toTypeKind(p)), toTypeKind(lambdaTarget.info.resultType)).toASMType val samMethod = atPhase(erasurePhase) { val samMethods = toDenot(functionalInterface).info.possibleSamMethods.toList @@ -1787,7 +1789,21 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { } val methodName = samMethod.javaSimpleName - val samMethodType = asmMethodType(samMethod).toASMType + val samMethodBType = asmMethodType(samMethod) + val samMethodType = samMethodBType.toASMType + + def boxInstantiated(instantiatedType: BType, samType: BType): BType = + if(!samType.isPrimitive && instantiatedType.isPrimitive) + boxedClassOfPrimitive(instantiatedType.asPrimitiveBType) + else instantiatedType + // TODO specialization + val instantiatedMethodBType = new MethodBType( + lambdaParamTypes.map(p => toTypeKind(p)), + boxInstantiated(toTypeKind(lambdaTarget.info.resultType), samMethodBType.returnType) + ) + + val instantiatedMethodType = instantiatedMethodBType.toASMType + // scala/bug#10334: make sure that a lambda object for `T => U` has a method `apply(T)U`, not only the `(Object)Object` // version. Using the lambda a structural type `{def apply(t: T): U}` causes a reflective lookup for this method. val needsGenericBridge = samMethodType != instantiatedMethodType diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala index 46732bf868fc..a1ed111d4561 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala @@ -30,6 +30,7 @@ import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.core.TypeErasure import dotty.tools.dotc.transform.GenericSignatures import dotty.tools.dotc.transform.ElimErasedValueType +import dotty.tools.dotc.transform.Mixin import dotty.tools.io.AbstractFile import dotty.tools.dotc.report @@ -295,6 +296,17 @@ trait BCodeHelpers extends BCodeIdiomatic { val edesc = innerClasesStore.typeDescriptor(t.tpe) // the class descriptor of the enumeration class. val evalue = t.symbol.javaSimpleName // value the actual enumeration value. av.visitEnum(name, edesc, evalue) + // Handle final val aliases to Java enum values. + // Check if the symbol's pre-erasure type was a singleton of a Java enum value. + case t: tpd.RefTree if atPhase(erasurePhase) { + t.symbol.info.finalResultType match + case tr: TermRef => tr.termSymbol.owner.linkedClass.isAllOf(JavaEnumTrait) + case _ => false + } => + val enumRef = atPhase(erasurePhase)(t.symbol.info.finalResultType.asInstanceOf[TermRef]) + val edesc = innerClasesStore.typeDescriptor(enumRef) + val evalue = enumRef.termSymbol.javaSimpleName + av.visitEnum(name, edesc, evalue) case t: SeqLiteral => val arrAnnotV: AnnotationVisitor = av.visitArray(name) for (arg <- t.elems) { emitArgument(arrAnnotV, null, arg, bcodeStore)(innerClasesStore) } @@ -315,9 +327,8 @@ trait BCodeHelpers extends BCodeIdiomatic { case e => List(e) } } - for(arg <- flatArgs) { + for arg <- flatArgs do emitArgument(arrAnnotV, null, arg, bcodeStore)(innerClasesStore) - } arrAnnotV.visitEnd() /* case sb @ ScalaSigBytes(bytes) => @@ -338,6 +349,9 @@ trait BCodeHelpers extends BCodeIdiomatic { val nestedVisitor = av.visitAnnotation(name, desc) emitAssocs(nestedVisitor, assocs, bcodeStore)(innerClasesStore) + case Inlined(_, _, expansion) => + emitArgument(av, name, arg = expansion, bcodeStore)(innerClasesStore) + case t => report.error(em"Annotation argument is not a constant", t.sourcePos) } @@ -392,12 +406,39 @@ trait BCodeHelpers extends BCodeIdiomatic { * Machine Specification, §4.3.4, or `null` if `sym` doesn't need a generic signature. * @see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.4 */ - def getGenericSignature(sym: Symbol, owner: Symbol): String = { + def getGenericSignature(sym: Symbol, owner: Symbol): String | Null = { atPhase(erasurePhase) { - val memberTpe = - if (sym.is(Method)) sym.denot.info - else owner.denot.thisType.memberInfo(sym) - getGenericSignatureHelper(sym, owner, memberTpe).orNull + // Finding the member's type is nontrivial because of erasure and how it interacts with other phases. + def computeMemberType(): Type = { + // Mixins are resolved _after_ erasure, so we cannot simply ask for "the information before erasure" for these, + // since that information never existed. + // Thus, we first check if the symbol was specifically marked as having generic information, + if sym.is(MixedIn) then mixinPhase.asInstanceOf[Mixin].mixinGenericInfos.get(sym) match + // and if so, we use it. + case Some(genericInfo) => return genericInfo + case _ => () + + // Methods are straightforward. + if sym.is(Method) then + return sym.denot.info + + // Fields have two special cases: + if sym.isField then + // first, we must use the getter if entered after erasure, see tests/generic-java-signatures/17069.scala for an example + if sym.denot.validFor.phaseId > erasurePhase.id && sym.getter.exists then + return sym.getter.denot.info.resultType + + // second, mixins part 2: there might be a getter created after erasure by the mixin phase, + // and if so we must use the information that the mixin phase stored for it. + val mixinGetter = atPhase(mixinPhase.next) { sym.getter } + if mixinGetter.exists then mixinPhase.asInstanceOf[Mixin].mixinGenericInfos.get(mixinGetter) match + case Some(ExprType(genericInfo)) => return genericInfo // since we're looking for the getter, we get an ExprType + case _ => () + + owner.denot.thisType.memberInfo(sym) + } + + getGenericSignatureHelper(sym, owner, computeMemberType()).orNull } } @@ -434,6 +475,11 @@ trait BCodeHelpers extends BCodeIdiomatic { val jReturnType = toTypeKind(methodInfo.resultType) val mdesc = MethodBType(paramJavaTypes, jReturnType).descriptor val mirrorMethodName = m.javaSimpleName + val lengthOk = if jgensig ne null then BCodeUtils.checkConstantStringLength(jgensig) + else BCodeUtils.checkConstantStringLength(mirrorMethodName, mdesc) + if !lengthOk then + report.error("Mirror method signature is too long for the JVM", m.srcPos) + return val mirrorMethod: asm.MethodVisitor = jclass.visitMethod( flags, mirrorMethodName, @@ -596,6 +642,9 @@ trait BCodeHelpers extends BCodeIdiomatic { val mirrorName = bType.internalName val mirrorClass = new asm.tree.ClassNode + if !BCodeUtils.checkConstantStringLength(mirrorName) then + report.error("Mirror class name is too long for the JVM", moduleClass.srcPos) + return mirrorClass // not filled, but we cannot create it, and we just reported an error mirrorClass.visit( backendUtils.classfileVersion, bType.info.flags, diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index ef65b699d06d..5f69edc1e850 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -151,6 +151,9 @@ trait BCodeSkelBuilder extends BCodeHelpers { var isCZParcelable = false var isCZStaticModule = false + // keep track of interfaces that are used in super calls, as they need to be directly inherited even if they are also indirectly inherited + val superCallTargets = mutable.LinkedHashSet[ClassBType]() + /* ---------------- idiomatic way to ask questions to typer ---------------- */ def paramTKs(app: Apply, take: Int = -1): List[BType] = app match { @@ -169,7 +172,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { /* ---------------- helper utils for generating classes and fields ---------------- */ - def genPlainClass(cd0: TypeDef) = cd0 match { + def genPlainClass(cd0: TypeDef) = (cd0: @unchecked) match { case TypeDef(_, impl: Template) => assert(cnode == null, "GenBCode detected nested methods.") @@ -180,8 +183,6 @@ trait BCodeSkelBuilder extends BCodeHelpers { cnode = new ClassNode1() - initJClass(cnode) - val cd = if (isCZStaticModule) { // Move statements from the primary constructor following the superclass constructor call to // a newly synthesised tree representing the "", which also assigns the MODULE$ field. @@ -291,11 +292,12 @@ trait BCodeSkelBuilder extends BCodeHelpers { addClassFields() gen(cd.rhs) + // This needs to wait until now since it uses `superCallTargets` which is populating while emitting the class body + initJClass(cnode) if (AsmUtils.traceClassEnabled && cnode.name.contains(AsmUtils.traceClassPattern)) AsmUtils.traceClass(cnode) - cnode.innerClasses assert(cd.symbol == claszSymbol, "Someone messed up BCodePhase.claszSymbol during genPlainClass().") } // end of method genPlainClass() @@ -307,7 +309,17 @@ trait BCodeSkelBuilder extends BCodeHelpers { val ps = claszSymbol.info.parents val superClass: String = if (ps.isEmpty) ObjectRef.internalName else internalName(ps.head.typeSymbol) - val interfaceNames0 = classBTypeFromSymbol(claszSymbol).info.interfaces.map(_.internalName) + + // We need to emit not only directly implemented interfaces, but also any indirectly implemented ones that are the target of super calls. + // (This somewhat convoluted sequence of operations exists to maintain the exact order of inheritance from a previous version. + // It could be cleaned up given some work to make sure changing the order isn't a problem.) + val directInterfaces = claszSymbol.directlyInheritedTraits + val directInterfacesBTypes = directInterfaces.map(classBTypeFromSymbol) + val baseClassesBTypes = directInterfaces.iterator.flatMap(_.asClass.baseClasses.drop(1)).map(classBTypeFromSymbol).toSet + val additionalBTypes = superCallTargets.filter(!directInterfacesBTypes.contains(_)) + val interfaces = directInterfacesBTypes.filter(t => !baseClassesBTypes(t) || superCallTargets(t)) ++ additionalBTypes + + val interfaceNames0 = interfaces.iterator.map(_.internalName).toList /* To avoid deadlocks when combining objects, lambdas and multi-threading, * lambdas in objects are compiled to instance methods of the module class * instead of static methods (see tests/run/deadlock.scala and @@ -331,6 +343,11 @@ trait BCodeSkelBuilder extends BCodeHelpers { val flags = javaFlags(claszSymbol) val thisSignature = getGenericSignature(claszSymbol, claszSymbol.owner) + val lengthOk = if thisSignature ne null then BCodeUtils.checkConstantStringLength(thisSignature) + else BCodeUtils.checkConstantStringLength(thisName) + if !lengthOk then + report.error("Class name is too long for the JVM", claszSymbol.srcPos) + return cnode.visit(backendUtils.classfileVersion, flags, thisName, thisSignature, superClass, interfaceNames.toArray) @@ -711,7 +728,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { /* * must-single-thread */ - def initJMethod(flags: Int, params: List[Symbol]): Unit = { + private def initJMethod(flags: Int, params: List[Symbol]): Unit = { val jgensig = getGenericSignature(methSymbol, claszSymbol) val (excs, others) = methSymbol.annotations.partition(_.symbol eq defn.ThrowsAnnot) @@ -722,6 +739,11 @@ trait BCodeSkelBuilder extends BCodeHelpers { else jMethodName val mdesc = asmMethodType(methSymbol).descriptor + val lengthOk = if jgensig ne null then BCodeUtils.checkConstantStringLength(jgensig) + else BCodeUtils.checkConstantStringLength(bytecodeName, mdesc) + if !lengthOk then + report.error("Method signature is too long for the JVM", methSymbol.srcPos) + return mnode = cnode.visitMethod( flags, bytecodeName, @@ -766,7 +788,10 @@ trait BCodeSkelBuilder extends BCodeHelpers { typeMap = _.substThis(enclosingClass, selfParamRef.symbol.termRef) .subst(dd.termParamss.head.map(_.symbol), regularParamRefs.map(_.symbol.termRef)), treeMap = { - case tree: This if tree.symbol == enclosingClass => selfParamRef + case tree: This if tree.symbol == enclosingClass => + // Since we want the positions to be accurate in the bytecode, we preserve + // the original span of the `this` node when we `statify` it. + selfParamRef.withSpan(tree.span) case tree => tree }, oldOwners = origSym :: Nil, @@ -818,7 +843,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { methSymbol = dd.symbol jMethodName = methSymbol.javaSimpleName - returnType = asmMethodType(dd.symbol).returnType + returnType = asmMethodType(methSymbol).returnType isMethSymStaticCtor = methSymbol.isStaticConstructor resetMethodBookkeeping(dd) @@ -853,7 +878,9 @@ trait BCodeSkelBuilder extends BCodeHelpers { // TODO needed? for(ann <- m.symbol.annotations) { ann.symbol.initialize } val paramSyms = params.map(_.symbol) initJMethod(flags, paramSyms) - + if mnode eq null then + // we failed to emit the method header, no point in continuing + return if (!isAbstractMethod && !isNative) { // #14773 Reuse locals slots for tailrec-generated mutable vars @@ -915,7 +942,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { for (p <- params) { emitLocalVarScope(p.symbol, veryFirstProgramPoint, onePastLastProgramPoint, force = true) } } - if (isMethSymStaticCtor) { appendToStaticCtor(dd) } + if (isMethSymStaticCtor) { appendToStaticCtor() } } // end of emitNormalMethodBody() lineNumber(rhs) @@ -936,7 +963,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { * * TODO document, explain interplay with `fabricateStaticInitAndroid()` */ - private def appendToStaticCtor(dd: DefDef): Unit = { + private def appendToStaticCtor(): Unit = { def insertBefore( location: asm.tree.AbstractInsnNode, diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeUtils.scala b/compiler/src/dotty/tools/backend/jvm/BCodeUtils.scala new file mode 100644 index 000000000000..f823fb29db7f --- /dev/null +++ b/compiler/src/dotty/tools/backend/jvm/BCodeUtils.scala @@ -0,0 +1,34 @@ +package dotty.tools.backend.jvm + +object BCodeUtils { + // The JVM enforces a max length of 65535 bytes per UTF-8 constant. + // Java uses "Modified UTF-8", in which the null character specifically is two bytes, + // and the rest of the BMP is as usual, see https://docs.oracle.com/javase/8/docs/api/java/io/DataInput.html#modified-utf-8 + // Outside the BMP, characters are represented as surrogate pairs, i.e., 2+2 bytes, but `charAt` sees them as separate "characters". + // This means if we see a surrogate pair in a string, we should count each half as 2 bytes, since the encoded UTF-8 character will be 4 bytes. + // One consequence of this is that the maximum number of UTF-8 bytes for a single Java `char` (not codepoint!) is 3. + private val MAX_BYTES_PER_UTF8_CONSTANT = 65535 + private val MAX_BYTES_PER_CHAR = 3 + + /** Checks that the given name, or the concatenation of the given two names and descriptor if present, do not exceed the JVM's UTF-8 text size limits. */ + def checkConstantStringLength(name: String, other: String = ""): Boolean = { + var byteCount = 0 + def check(str: String): Boolean = + var i = 0 + while i < str.length do + val c = str.charAt(i) + byteCount += ( + if c == 0x00 then 2 + else if c <= 0x7F then 1 + else if c <= 0x7FF then 2 + else if Character.isHighSurrogate(c) || Character.isLowSurrogate(c) then 2 + else 3 + ) + if byteCount > MAX_BYTES_PER_UTF8_CONSTANT then + return false + i += 1 + true + // For performance, since we expect few large strings, check if the string is obviously fine first. + name.length + other.length <= MAX_BYTES_PER_UTF8_CONSTANT / MAX_BYTES_PER_CHAR || (check(name) && check(other)) + } +} \ No newline at end of file diff --git a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala index d1927c22e8d2..10e27530e928 100644 --- a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala +++ b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala @@ -105,23 +105,12 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I, val frontendAcce val superClass = if (superClassSym == NoSymbol) None else Some(classBTypeFromSymbol(superClassSym)) - /** - * All interfaces implemented by a class, except for those inherited through the superclass. - * Redundant interfaces are removed unless there is a super call to them. - */ - extension (sym: Symbol) def superInterfaces: List[Symbol] = { - val directlyInheritedTraits = sym.directlyInheritedTraits - val directlyInheritedTraitsSet = directlyInheritedTraits.toSet - val allBaseClasses = directlyInheritedTraits.iterator.flatMap(_.asClass.baseClasses.drop(1)).toSet - val superCalls = superCallsMap.getOrElse(sym, List.empty) - val superCallsSet = superCalls.toSet - val additional = superCalls.filter(t => !directlyInheritedTraitsSet(t) && t.is(Trait)) -// if (additional.nonEmpty) -// println(s"$fullName: adding supertraits $additional") - directlyInheritedTraits.filter(t => !allBaseClasses(t) || superCallsSet(t)) ++ additional - } - - val interfaces = classSym.superInterfaces.map(classBTypeFromSymbol) + // List only directly inherited interfaces. + // This is not only a performance optimization (as the JVM needs to handle fewer inheritance declarations), + // but also required for correctness in the presence of sealed interfaces (see i23479): + // if `C` inherits from `non-sealed A` which itself inherits from `sealed B permits A`, then having `C` inherit from `B` directly is illegal. + val allBaseClasses = classSym.directlyInheritedTraits.iterator.flatMap(_.asClass.baseClasses.drop(1)).toSet + val interfaces = classSym.directlyInheritedTraits.filter(!allBaseClasses(_)).map(classBTypeFromSymbol) val flags = javaFlags(classSym) diff --git a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala index 973e9cddcadd..d84f76492800 100644 --- a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala +++ b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala @@ -186,6 +186,8 @@ object BackendUtils { 21 -> asm.Opcodes.V21, 22 -> asm.Opcodes.V22, 23 -> asm.Opcodes.V23, - 24 -> asm.Opcodes.V24 + 24 -> asm.Opcodes.V24, + 25 -> asm.Opcodes.V25, + 26 -> asm.Opcodes.V26, ) } diff --git a/compiler/src/dotty/tools/backend/jvm/ClassfileWriters.scala b/compiler/src/dotty/tools/backend/jvm/ClassfileWriters.scala index 536ab887a740..5dcafbef750f 100644 --- a/compiler/src/dotty/tools/backend/jvm/ClassfileWriters.scala +++ b/compiler/src/dotty/tools/backend/jvm/ClassfileWriters.scala @@ -12,10 +12,10 @@ import java.util.zip.{CRC32, Deflater, ZipEntry, ZipOutputStream} import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Decorators.em +import dotty.tools.dotc.util.chaining.* import dotty.tools.io.{AbstractFile, PlainFile, VirtualFile} import dotty.tools.io.PlainFile.toPlainFile import BTypes.InternalName -import scala.util.chaining._ import dotty.tools.io.JarArchive import scala.language.unsafeNulls @@ -180,7 +180,7 @@ class ClassfileWriters(frontendAccess: PostProcessorFrontendAccess) { // important detail here, even on Windows, Zinc expects the separator within the jar // to be the system default, (even if in the actual jar file the entry always uses '/'). // see https://github.com/sbt/zinc/blob/dcddc1f9cfe542d738582c43f4840e17c053ce81/internal/compiler-bridge/src/main/scala/xsbt/JarUtils.scala#L47 - val pathInJar = + val pathInJar = if File.separatorChar == '/' then relativePath else relativePath.replace('/', File.separatorChar) PlainFile.toPlainFile(Paths.get(s"${file.absolutePath}!$pathInJar")) diff --git a/compiler/src/dotty/tools/backend/jvm/CollectSuperCalls.scala b/compiler/src/dotty/tools/backend/jvm/CollectSuperCalls.scala deleted file mode 100644 index 94a946989d23..000000000000 --- a/compiler/src/dotty/tools/backend/jvm/CollectSuperCalls.scala +++ /dev/null @@ -1,48 +0,0 @@ -package dotty.tools.backend.jvm - -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Contexts.* -import dotty.tools.dotc.core.Phases.* -import dotty.tools.dotc.core.Symbols.* -import dotty.tools.dotc.core.Flags.Trait -import dotty.tools.dotc.transform.MegaPhase.MiniPhase - -/** Collect all super calls to trait members. - * - * For each super reference to trait member, register a call from the current class to the - * owner of the referenced member. - * - * This information is used to know if it is safe to remove a redundant mixin class. - * A redundant mixin class is one that is implemented by another mixin class. As the - * methods in a redundant mixin class could be implemented with a default abstract method, - * the redundant mixin class could be required as a parent by the JVM. - */ -class CollectSuperCalls extends MiniPhase { - import tpd.* - - override def phaseName: String = CollectSuperCalls.name - - override def description: String = CollectSuperCalls.description - - override def transformSelect(tree: Select)(using Context): Tree = { - tree.qualifier match { - case sup: Super => - if (tree.symbol.owner.is(Trait)) - registerSuperCall(ctx.owner.enclosingClass.asClass, tree.symbol.owner.asClass) - case _ => - } - tree - } - - private def registerSuperCall(sym: ClassSymbol, calls: ClassSymbol)(using Context) = { - genBCodePhase match { - case genBCodePhase: GenBCode => - genBCodePhase.registerSuperCall(sym, calls) - case _ => - } - } -} - -object CollectSuperCalls: - val name: String = "collectSuperCalls" - val description: String = "find classes that are called with super" diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index ed4ee4a72778..5d4eba6c1afb 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -25,7 +25,7 @@ import StdNames.nme import NameKinds.{LazyBitMapName, LazyLocalName} import Names.Name -class DottyBackendInterface(val superCallsMap: ReadOnlyMap[Symbol, List[ClassSymbol]])(using val ctx: Context) { +class DottyBackendInterface(using val ctx: Context) { private val desugared = new java.util.IdentityHashMap[Type, tpd.Select] diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index 401f8c4bcae7..c7a2d9035bc5 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -16,13 +16,6 @@ class GenBCode extends Phase { self => override def description: String = GenBCode.description - private val superCallsMap = new MutableSymbolMap[List[ClassSymbol]] - def registerSuperCall(sym: Symbol, calls: ClassSymbol): Unit = { - val old = superCallsMap.getOrElse(sym, List.empty) - if (!old.contains(calls)) - superCallsMap.update(sym, old :+ calls) - } - private val entryPoints = new mutable.HashSet[String]() def registerEntryPoint(s: String): Unit = entryPoints += s @@ -33,7 +26,7 @@ class GenBCode extends Phase { self => val backendCtx = ctx match case fc: FreshContext => fc case ctx => ctx.fresh - _backendInterface = DottyBackendInterface(superCallsMap)(using backendCtx) + _backendInterface = DottyBackendInterface(using backendCtx) _backendInterface } @@ -74,7 +67,7 @@ class GenBCode extends Phase { self => _generatedClassHandler.nn } - override def run(using Context): Unit = + protected def run(using Context): Unit = frontendAccess.frontendSynchWithoutContext { backendInterface.ctx .asInstanceOf[FreshContext] diff --git a/compiler/src/dotty/tools/backend/jvm/Primitives.scala b/compiler/src/dotty/tools/backend/jvm/Primitives.scala index c9ddfeab24e1..b173736b946c 100644 --- a/compiler/src/dotty/tools/backend/jvm/Primitives.scala +++ b/compiler/src/dotty/tools/backend/jvm/Primitives.scala @@ -150,13 +150,12 @@ object Primitives { case object XOR extends LogicalOp /** Signals the beginning of a series of concatenations. - * On the JVM platform, it should create a new StringBuffer - */ + * On the JVM platform, it should create a new StringBuilder. + */ case object StartConcat extends Primitive - /** - * type: (buf) => STR - * jvm : It should turn the StringBuffer into a String. + /** type: (buf) => STR + * jvm : It should turn the StringBuilder into a String. */ case object EndConcat extends Primitive diff --git a/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala index 262b5df43362..8942d5daa3b3 100644 --- a/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala +++ b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala @@ -46,7 +46,7 @@ class DottyPrimitives(ictx: Context) { * operation is an array get/set, we inspect the type of the receiver * to demux the operation. * - * @param fun The method symbol + * @param app The method symbol * @param tpe The type of the receiver object. It is used only for array * operations */ diff --git a/compiler/src/dotty/tools/backend/sjs/GenSJSIR.scala b/compiler/src/dotty/tools/backend/sjs/GenSJSIR.scala index 6467e44c072d..bb1ab3bf9a3d 100644 --- a/compiler/src/dotty/tools/backend/sjs/GenSJSIR.scala +++ b/compiler/src/dotty/tools/backend/sjs/GenSJSIR.scala @@ -14,7 +14,7 @@ class GenSJSIR extends Phase { override def isEnabled(using Context): Boolean = ctx.settings.scalajs.value - def run(using Context): Unit = + override protected def run(using Context): Unit = new JSCodeGen().run() } diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 1572ed476c6c..4e63d31b6842 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -1496,7 +1496,7 @@ class JSCodeGen()(using genCtx: Context) { def jsParams = params.map(genParamDef(_)) - if (primitives.isPrimitive(sym)) { + if (primitives.isPrimitive(sym) && sym != defn.newArrayMethod) { None } else if (sym.is(Deferred) && currentClassSym.isNonNativeJSClass) { // scala-js/#4409: Do not emit abstract methods in non-native JS classes @@ -2553,6 +2553,8 @@ class JSCodeGen()(using genCtx: Context) { genCoercion(tree, receiver, code) else if (code == JSPrimitives.THROW) genThrow(tree, args) + else if (code == JSPrimitives.NEW_ARRAY) + genNewArray(tree, args) else if (JSPrimitives.isJSPrimitive(code)) genJSPrimitive(tree, args, code, isStat) else @@ -3022,6 +3024,24 @@ class JSCodeGen()(using genCtx: Context) { } } + /** Gen a call to the special `newArray` method. */ + private def genNewArray(tree: Apply, args: List[Tree]): js.Tree = { + implicit val pos: SourcePosition = tree.sourcePos + + val List(elemClazz, Literal(arrayClassConstant), dimsArray: JavaSeqLiteral) = args: @unchecked + + dimsArray.elems match { + case singleDim :: Nil => + // Use a js.NewArray + val arrayTypeRef = toTypeRef(arrayClassConstant.typeValue).asInstanceOf[jstpe.ArrayTypeRef] + js.NewArray(arrayTypeRef, List(genExpr(singleDim))) + case _ => + // Delegate to jlr.Array.newInstance + js.ApplyStatic(js.ApplyFlags.empty, JLRArrayClassName, js.MethodIdent(JLRArrayNewInstanceMethodName), + List(genExpr(elemClazz), genJavaSeqLiteral(dimsArray)))(jstpe.AnyType) + } + } + /** Gen a "normal" apply (to a true method). * * But even these are further refined into: @@ -4835,6 +4855,7 @@ class JSCodeGen()(using genCtx: Context) { object JSCodeGen { private val NullPointerExceptionClass = ClassName("java.lang.NullPointerException") + private val JLRArrayClassName = ClassName("java.lang.reflect.Array") private val JSObjectClassName = ClassName("scala.scalajs.js.Object") private val JavaScriptExceptionClassName = ClassName("scala.scalajs.js.JavaScriptException") @@ -4844,6 +4865,9 @@ object JSCodeGen { private val selectedValueMethodName = MethodName("selectedValue", Nil, ObjectClassRef) + private val JLRArrayNewInstanceMethodName = + MethodName("newInstance", List(jstpe.ClassRef(jsNames.ClassClass), jstpe.ArrayTypeRef(jstpe.IntRef, 1)), ObjectClassRef) + private val ObjectArgConstructorName = MethodName.constructor(List(ObjectClassRef)) private val thisOriginalName = OriginalName("this") diff --git a/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala index cfd6e243ca32..dda38973924f 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSDefinitions.scala @@ -19,7 +19,13 @@ object JSDefinitions { ctx.platform.asInstanceOf[SJSPlatform].jsDefinitions } -final class JSDefinitions()(using Context) { +final class JSDefinitions() { + + private var initCtx: Context = _ + private given currentContext[Dummy_so_its_a_def]: Context = initCtx + + def init()(using Context): Unit = + this.initCtx = ctx @threadUnsafe lazy val InlineAnnotType: TypeRef = requiredClassRef("scala.inline") def InlineAnnot(using Context) = InlineAnnotType.symbol.asClass diff --git a/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala b/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala index a3a37795826a..f23c816b5204 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala @@ -48,9 +48,10 @@ object JSPrimitives { inline val UNWRAP_FROM_THROWABLE = WRAP_AS_THROWABLE + 1 // js.special.unwrapFromThrowable inline val DEBUGGER = UNWRAP_FROM_THROWABLE + 1 // js.special.debugger - inline val THROW = DEBUGGER + 1 + inline val THROW = DEBUGGER + 1 // .throw + inline val NEW_ARRAY = THROW + 1 // scala.runtime.Arrays.newArray - inline val UNION_FROM = THROW + 1 // js.|.from + inline val UNION_FROM = NEW_ARRAY + 1 // js.|.from inline val UNION_FROM_TYPE_CONSTRUCTOR = UNION_FROM + 1 // js.|.fromTypeConstructor inline val REFLECT_SELECTABLE_SELECTDYN = UNION_FROM_TYPE_CONSTRUCTOR + 1 // scala.reflect.Selectable.selectDynamic @@ -137,6 +138,7 @@ class JSPrimitives(ictx: Context) extends DottyPrimitives(ictx) { addPrimitive(jsdefn.Special_debugger, DEBUGGER) addPrimitive(defn.throwMethod, THROW) + addPrimitive(defn.newArrayMethod, NEW_ARRAY) addPrimitive(jsdefn.PseudoUnion_from, UNION_FROM) addPrimitive(jsdefn.PseudoUnion_fromTypeConstructor, UNION_FROM_TYPE_CONSTRUCTOR) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index b796b6c3cb5b..e0c7e63be261 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -134,7 +134,7 @@ object CompilationUnit { /** Create a compilation unit corresponding to an in-memory String. * Used for `compiletime.testing.typeChecks`. */ - def apply(name: String, source: String)(using Context): CompilationUnit = { + def apply(name: String, source: String): CompilationUnit = { val src = SourceFile.virtual(name = name, content = source, maybeIncomplete = false) new CompilationUnit(src) } diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index b3757a9ddce7..c175e19f4b86 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -8,8 +8,7 @@ import cc.CheckCaptures import parsing.Parser import Phases.Phase import transform.* -import dotty.tools.backend -import backend.jvm.{CollectSuperCalls, GenBCode} +import backend.jvm.GenBCode import localopt.StringInterpolatorOpt /** The central class of the dotc compiler. The job of a compiler is to create @@ -35,11 +34,12 @@ class Compiler { protected def frontendPhases: List[List[Phase]] = List(new Parser) :: // Compiler frontend: scanner, parser List(new TyperPhase) :: // Compiler frontend: namer, typer - List(new CheckUnused.PostTyper) :: // Check for unused elements - List(new CheckShadowing) :: // Check shadowing elements + List(new WInferUnion, // Check for type arguments inferred as union types + CheckUnused.PostTyper(), // Check for unused + CheckShadowing()) :: // Check for shadowed elements List(new YCheckPositions) :: // YCheck positions List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks - List(new semanticdb.ExtractSemanticDB.ExtractSemanticInfo) :: // Extract info into .semanticdb files + List(new semanticdb.ExtractSemanticInfo) :: // Extract info into .semanticdb files List(new PostTyper) :: // Additional checks and cleanups after type checking List(new sjs.PrepJSInterop) :: // Additional checks and transformations for Scala.js (Scala.js only) List(new sbt.ExtractAPI) :: // Sends a representation of the API of classes to sbt via callbacks @@ -51,7 +51,6 @@ class Compiler { List(new Pickler) :: // Generate TASTY info List(new Inlining) :: // Inline and execute macros List(new PostInlining) :: // Add mirror support for inlined code - List(new CheckUnused.PostInlining) :: // Check for unused elements List(new Staging) :: // Check staging levels and heal staged types List(new Splicing) :: // Replace level 1 splices with holes List(new PickleQuotes) :: // Turn quoted trees into explicit run-time data structures @@ -71,7 +70,6 @@ class Compiler { new ExpandSAMs, // Expand single abstract method closures to anonymous classes new ElimRepeated, // Rewrite vararg parameters and arguments new RefChecks) :: // Various checks mostly related to abstract members and overriding - List(new semanticdb.ExtractSemanticDB.AppendDiagnostics) :: // Attach warnings to extracted SemanticDB and write to .semanticdb file List(new init.Checker) :: // Check initialization of objects List(new ProtectedAccessors, // Add accessors for protected members new ExtensionMethods, // Expand methods of value classes with extension methods @@ -87,6 +85,8 @@ class Compiler { List(new TestRecheck) :: // Test only: run rechecker, enabled under -Yrecheck-test List(new CheckCaptures.Pre) :: // Preparations for check captures phase, enabled under captureChecking List(new CheckCaptures) :: // Check captures, enabled under captureChecking + List(CheckUnused.PostPatMat()) :: // Check for unused elements and report + List(new semanticdb.AppendDiagnostics) :: // Attach warnings to extracted SemanticDB and write to .semanticdb file List(new ElimOpaque, // Turn opaque into normal aliases new sjs.ExplicitJSClasses, // Make all JS classes explicit (Scala.js only) new ExplicitOuter, // Add accessors to outer classes from nested ones. @@ -142,7 +142,6 @@ class Compiler { new SelectStatic, // get rid of selects that would be compiled into GetStatic new sjs.JUnitBootstrappers, // Generate JUnit-specific bootstrapper classes for Scala.js (not enabled by default) new CollectEntryPoints, // Collect all entry points and save them in the context - new CollectSuperCalls, // Find classes that are called with super new RepeatableAnnotations) :: // Aggregate repeatable annotations Nil diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index fc5367d2ccba..da52fe776bc4 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -10,6 +10,7 @@ import dotty.tools.io.AbstractFile import reporting.* import core.Decorators.* import config.Feature +import util.chaining.* import scala.util.control.NonFatal import fromtasty.{TASTYCompiler, TastyFileUtil} @@ -78,15 +79,28 @@ class Driver { MacroClassLoader.init(ictx) Positioned.init(using ictx) - inContext(ictx) { + inContext(ictx): if !ctx.settings.YdropComments.value || ctx.settings.YreadComments.value then ictx.setProperty(ContextDoc, new ContextDocstrings) val fileNamesOrNone = command.checkUsage(summary, sourcesRequired)(using ctx.settings)(using ctx.settingsState) - fileNamesOrNone.map { fileNames => + fileNamesOrNone.map: fileNames => val files = fileNames.map(ctx.getFile) (files, fromTastySetup(files)) - } - } + .tap: _ => + if !ctx.settings.Yreporter.isDefault then + ctx.settings.Yreporter.value match + case "help" => + case reporterClassName => + try + val newCls = Class.forName(reporterClassName).nn + val newInst = newCls.getDeclaredConstructor().nn.newInstance().nn + newInst match + case userReporter: Reporter => + ictx.setReporter(userReporter) + case badReporter => report.error: + em"Not a reporter: ${ctx.settings.Yreporter.value}" + catch case e: ReflectiveOperationException => report.error: + em"Could not create reporter ${ctx.settings.Yreporter.value}: ${e}" } /** Setup extra classpath of tasty and jar files */ diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 8e98f276a7c2..8b2af2d06316 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -17,26 +17,28 @@ import Phases.{unfusedPhases, Phase} import sbt.interfaces.ProgressCallback import util.* -import reporting.{Suppression, Action, Profile, ActiveProfile, NoProfile} -import reporting.Diagnostic -import reporting.Diagnostic.Warning +import reporting.{Suppression, Action, Profile, ActiveProfile, MessageFilter, NoProfile, WConf} +import reporting.Diagnostic, Diagnostic.Warning import rewrites.Rewrites import profile.Profiler import printing.XprintMode import typer.ImplicitRunInfo import config.Feature import StdNames.nme +import Spans.Span import java.io.{BufferedWriter, OutputStreamWriter} import java.nio.charset.StandardCharsets -import scala.collection.mutable +import scala.collection.mutable, mutable.ListBuffer import scala.util.control.NonFatal import scala.io.Codec import Run.Progress import scala.compiletime.uninitialized import dotty.tools.dotc.transform.MegaPhase +import dotty.tools.dotc.util.chaining.* +import java.util.{Timer, TimerTask} /** A compiler run. Exports various methods to compile source files */ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with ConstraintRunInfo { @@ -68,7 +70,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint private var myFiles: Set[AbstractFile] = _ // `@nowarn` annotations by source file, populated during typer - private val mySuppressions: mutable.LinkedHashMap[SourceFile, mutable.ListBuffer[Suppression]] = mutable.LinkedHashMap.empty + private val mySuppressions: mutable.LinkedHashMap[SourceFile, ListBuffer[Suppression]] = mutable.LinkedHashMap.empty // source files whose `@nowarn` annotations are processed private val mySuppressionsComplete: mutable.Set[SourceFile] = mutable.Set.empty // warnings issued before a source file's `@nowarn` annotations are processed, suspended so that `@nowarn` can filter them @@ -88,18 +90,44 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint mySuspendedMessages.getOrElseUpdate(warning.pos.source, mutable.LinkedHashSet.empty) += warning def nowarnAction(dia: Diagnostic): Action.Warning.type | Action.Verbose.type | Action.Silent.type = - mySuppressions.getOrElse(dia.pos.source, Nil).find(_.matches(dia)) match { - case Some(s) => + mySuppressions.get(dia.pos.source) match + case Some(suppressions) => + val matching = suppressions.iterator.filter(_.matches(dia)) + if matching.hasNext then + val s = matching.next() + for other <- matching do + if !other.used then + other.markSuperseded() // superseded unless marked used later s.markUsed() - if (s.verbose) Action.Verbose + if s.verbose then Action.Verbose else Action.Silent - case _ => + else Action.Warning - } + case none => + Action.Warning + + def registerNowarn(annotPos: SourcePosition, range: Span)(conf: String, pos: SrcPos)(using Context): Unit = + var verbose = false + val filters = conf match + case "" => + List(MessageFilter.Any) + case "none" => + List(MessageFilter.None) + case "verbose" | "v" => + verbose = true + List(MessageFilter.Any) + case conf => + WConf.parseFilters(conf).left.map: parseErrors => + report.warning(s"Invalid message filter\n${parseErrors.mkString("\n")}", pos) + List(MessageFilter.None) + .merge + addSuppression: + Suppression(annotPos, filters, range.start, range.end, verbose) def addSuppression(sup: Suppression): Unit = - val source = sup.annotPos.source - mySuppressions.getOrElseUpdate(source, mutable.ListBuffer.empty) += sup + val suppressions = mySuppressions.getOrElseUpdate(sup.annotPos.source, ListBuffer.empty) + if sup.start != sup.end then + suppressions += sup def reportSuspendedMessages(source: SourceFile)(using Context): Unit = { // sort suppressions. they are not added in any particular order because of lazy type completion @@ -109,17 +137,25 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint mySuspendedMessages.remove(source).foreach(_.foreach(ctx.reporter.issueIfNotSuppressed)) } - def runFinished(hasErrors: Boolean): Unit = + def runFinished()(using Context): Unit = + val hasErrors = ctx.reporter.hasErrors // report suspended messages (in case the run finished before typer) mySuspendedMessages.keysIterator.toList.foreach(reportSuspendedMessages) // report unused nowarns only if all all phases are done if !hasErrors && ctx.settings.WunusedHas.nowarn then - for { + for source <- mySuppressions.keysIterator.toList sups <- mySuppressions.remove(source) - sup <- sups.reverse - } if (!sup.used) - report.warning("@nowarn annotation does not suppress any warnings", sup.annotPos) + do + val suppressions = sups.reverse.toList + for sup <- suppressions do + if !sup.used + && !suppressions.exists(s => s.ne(sup) && s.used && s.annotPos == sup.annotPos) // duplicate + && sup.filters != List(MessageFilter.None) // invalid suppression, don't report as unused + then + val more = if sup.superseded then " but matches a diagnostic" else "" + report.warning("@nowarn annotation does not suppress any warnings"+more, sup.annotPos) + end suppressions /** The compilation units currently being compiled, this may return different * results over time. @@ -167,7 +203,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint val staticRefs = util.EqHashMap[Name, Denotation](initialCapacity = 1024) /** Actions that need to be performed at the end of the current compilation run */ - private var finalizeActions = mutable.ListBuffer[() => Unit]() + private var finalizeActions = ListBuffer.empty[() => Unit] private var _progress: Progress | Null = null // Set if progress reporting is enabled @@ -314,7 +350,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint profiler.onPhase(phase): try units = phase.runOn(units) catch case _: InterruptedException => cancelInterrupted() - if (ctx.settings.Xprint.value.containsPhase(phase)) + if (ctx.settings.Vprint.value.containsPhase(phase)) for (unit <- units) lastPrintedTree = printTree(lastPrintedTree)(using ctx.fresh.setPhase(phase.next).setCompilationUnit(unit)) @@ -348,7 +384,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint runPhases(allPhases = fusedPhases)(using runCtx) if (!ctx.reporter.hasErrors) Rewrites.writeBack() - suppressions.runFinished(hasErrors = ctx.reporter.hasErrors) + suppressions.runFinished() while (finalizeActions.nonEmpty && canProgress()) { val action = finalizeActions.remove(0) action() diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 9db12e21913a..7daa5bdae83a 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -9,16 +9,16 @@ import Decorators.* import Annotations.Annotation import NameKinds.{UniqueName, ContextBoundParamName, ContextFunctionParamName, DefaultGetterName, WildcardParamName} import typer.{Namer, Checking} -import util.{Property, SourceFile, SourcePosition, Chars} +import util.{Property, SourceFile, SourcePosition, Chars, NoSourcePosition} import config.Feature.{sourceVersion, migrateTo3, enabled} import config.SourceVersion.* import collection.mutable.ListBuffer import reporting.* -import annotation.constructorOnly import printing.Formatting.hl import config.Printers import scala.annotation.internal.sharable +import scala.annotation.{unchecked as _, *} import dotty.tools.dotc.util.SrcPos object desugar { @@ -45,6 +45,16 @@ object desugar { */ val UntupledParam: Property.Key[Unit] = Property.StickyKey() + /** An attachment key to indicate that a ValDef originated from a pattern. + */ + val PatternVar: Property.Key[Unit] = Property.StickyKey() + + /** An attachment key for Trees originating in for-comprehension, such as tupling of assignments. + */ + val ForArtifact: 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 @@ -330,6 +340,7 @@ object desugar { .withMods(Modifiers( meth.mods.flags & (AccessFlags | Synthetic) | (vparam.mods.flags & Inline), meth.mods.privateWithin)) + .withSpan(vparam.rhs.span) val rest = defaultGetters(vparams :: paramss1, n + 1) if vparam.rhs.isEmpty then rest else defaultGetter :: rest case _ :: paramss1 => // skip empty parameter lists and type parameters @@ -930,63 +941,69 @@ object desugar { } } - def extMethod(mdef: DefDef, extParamss: List[ParamClause])(using Context): DefDef = + def extMethod(mdef: DefDef, extParamss: List[ParamClause])(using Context): DefDef = { + def finish(problem: String = "", pos: SrcPos = NoSourcePosition) = + if !problem.isEmpty then + report.error(em"right-associative extension method $problem", pos) + extParamss ++ mdef.paramss + + def rightAssocParams = { + val (rightTyParams, paramss) = mdef.paramss.span(isTypeParamClause) // first extract type parameters + + paramss match + case (rightParam @ ValDefs(vparam :: Nil)) :: paramss if !vparam.mods.is(Given) => + // must be a single parameter without `given` flag for rassoc rewrite + // we merge the extension parameters with the method parameters, + // swapping the operator arguments: + // e.g. + // extension [A](using B)(c: C)(using D) + // def %:[E](f: F)(g: G)(using H): Res = ??? + // will be encoded as + // def %:[A](using B)[E](f: F)(c: C)(using D)(g: G)(using H): Res = ??? + // + // If you change the names in the clauses below, also change them in right-associative-extension-methods.md + val (leftTyParamsAndLeadingUsing, leftParamAndTrailingUsing) = extParamss.span(isUsingOrTypeParamClause) + + val names = (for ps <- mdef.paramss; p <- ps yield p.name).toSet[Name] + + val tt = new untpd.UntypedTreeTraverser: + def traverse(tree: Tree)(using Context): Unit = tree match + case tree: Ident if names.contains(tree.name) => + finish(s"cannot have a forward reference to ${tree.name}", tree.srcPos) + case _ => traverseChildren(tree) + + for ts <- leftParamAndTrailingUsing; t <- ts do + tt.traverse(t) + + leftTyParamsAndLeadingUsing ::: rightTyParams ::: rightParam :: leftParamAndTrailingUsing ::: paramss + case ValDefs(vparam :: _) :: _ => + if vparam.mods.is(Given) then + // no explicit value parameters, so not an infix operator. + finish() + else + finish("must start with a single parameter, consider a tupled parameter instead", mdef.srcPos) + case _ => + // no value parameters, so not an infix operator. + finish() + } + cpy.DefDef(mdef)( name = normalizeName(mdef, mdef.tpt).asTermName, paramss = if mdef.name.isRightAssocOperatorName then - val (rightTyParams, paramss) = mdef.paramss.span(isTypeParamClause) // first extract type parameters - - paramss match - case rightParam :: paramss1 => // `rightParam` must have a single parameter and without `given` flag - - def badRightAssoc(problem: String, pos: SrcPos) = - report.error(em"right-associative extension method $problem", pos) - extParamss ++ mdef.paramss - - rightParam match - case ValDefs(vparam :: Nil) => - if !vparam.mods.is(Given) then - // we merge the extension parameters with the method parameters, - // swapping the operator arguments: - // e.g. - // extension [A](using B)(c: C)(using D) - // def %:[E](f: F)(g: G)(using H): Res = ??? - // will be encoded as - // def %:[A](using B)[E](f: F)(c: C)(using D)(g: G)(using H): Res = ??? - // - // If you change the names of the clauses below, also change them in right-associative-extension-methods.md - val (leftTyParamsAndLeadingUsing, leftParamAndTrailingUsing) = extParamss.span(isUsingOrTypeParamClause) - - val names = (for ps <- mdef.paramss; p <- ps yield p.name).toSet[Name] - - val tt = new untpd.UntypedTreeTraverser: - def traverse(tree: Tree)(using Context): Unit = tree match - case tree: Ident if names.contains(tree.name) => - badRightAssoc(s"cannot have a forward reference to ${tree.name}", tree.srcPos) - case _ => traverseChildren(tree) - - for ts <- leftParamAndTrailingUsing; t <- ts do - tt.traverse(t) - - leftTyParamsAndLeadingUsing ::: rightTyParams ::: rightParam :: leftParamAndTrailingUsing ::: paramss1 - else - badRightAssoc("cannot start with using clause", mdef.srcPos) - case _ => - badRightAssoc("must start with a single parameter", mdef.srcPos) - case _ => - // no value parameters, so not an infix operator. - extParamss ++ mdef.paramss + rightAssocParams else - extParamss ++ mdef.paramss + finish() ).withMods(mdef.mods | ExtensionMethod) + } + end extMethod /** Transform extension construct to list of extension methods */ def extMethods(ext: ExtMethods)(using Context): Tree = flatTree { - ext.methods map { + ext.methods.map: case exp: Export => exp case mdef: DefDef => defDef(extMethod(mdef, ext.paramss)) - } + case _ => EmptyTree // we ignore all the other trees. Error was reported during parsing. } /** Transforms * @@ -1013,25 +1030,27 @@ object desugar { else tree } - def checkPackageName(mdef: ModuleDef | PackageDef)(using Context): Unit = - - def check(name: Name, errSpan: Span): Unit = name match - case name: SimpleName if !errSpan.isSynthetic && name.exists(Chars.willBeEncoded) => - report.warning(em"The package name `$name` will be encoded on the classpath, and can lead to undefined behaviour.", mdef.source.atSpan(errSpan)) - case _ => - - def loop(part: RefTree): Unit = part match - case part @ Ident(name) => check(name, part.span) - case part @ Select(qual: RefTree, name) => - check(name, part.nameSpan) - loop(qual) + def checkSimplePackageName(name: Name, errSpan: Span, source: SourceFile, isPackageObject: Boolean)(using Context) = + if !ctx.isAfterTyper then + name match + case name: SimpleName if (isPackageObject || !errSpan.isSynthetic) && name.exists(Chars.willBeEncoded) => + report.warning(EncodedPackageName(name), source.atSpan(errSpan)) case _ => + def checkPackageName(mdef: ModuleDef | PackageDef)(using Context): Unit = + def check(name: Name, errSpan: Span) = checkSimplePackageName(name, errSpan, mdef.source, isPackageObject = false) mdef match - case pdef: PackageDef => loop(pdef.pid) - case mdef: ModuleDef if mdef.mods.is(Package) => check(mdef.name, mdef.nameSpan) - case _ => - end checkPackageName + case pdef: PackageDef => + def loop(part: RefTree): Unit = part match + case part @ Ident(name) => check(name, part.span) + case part @ Select(qual: RefTree, name) => + check(name, part.nameSpan) + loop(qual) + case _ => + loop(pdef.pid) + case mdef: ModuleDef if mdef.mods.is(Package) => + check(mdef.name, mdef.nameSpan) + case _ => /** The normalized name of `mdef`. This means * 1. Check that the name does not redefine a Scala core class. @@ -1129,7 +1148,7 @@ object desugar { } } - /** The selector of a match, which depends of the given `checkMode`. + /** The selector of a match, which depends on the given `checkMode`. * @param sel the original selector * @return if `checkMode` is * - None : sel @unchecked @@ -1221,7 +1240,7 @@ object desugar { val matchExpr = if (tupleOptimizable) rhs else - val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids)) + val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids).withAttachment(ForArtifact, ())) Match(makeSelector(rhs, MatchCheck.IrrefutablePatDef), caseDef :: Nil) vars match { case Nil if !mods.is(Lazy) => @@ -1246,11 +1265,13 @@ object desugar { DefDef(named.name.asTermName, Nil, tpt, selector(n)) .withMods(mods &~ Lazy) .withSpan(named.span) + .withAttachment(PatternVar, ()) else valDef( ValDef(named.name.asTermName, tpt, selector(n)) .withMods(mods) .withSpan(named.span) + .withAttachment(PatternVar, ()) ) flatTree(firstDef :: restDefs) } @@ -1358,10 +1379,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 @@ -1476,18 +1499,18 @@ object desugar { /** Map n-ary function `(x1: T1, ..., xn: Tn) => body` where n != 1 to unary function as follows: * * (x$1: (T1, ..., Tn)) => { - * def x1: T1 = x$1._1 + * val x1: T1 = x$1._1 * ... - * def xn: Tn = x$1._n + * val xn: Tn = x$1._n * body * } * * or if `isGenericTuple` * * (x$1: (T1, ... Tn) => { - * def x1: T1 = x$1.apply(0) + * val x1: T1 = x$1.apply(0) * ... - * def xn: Tn = x$1.apply(n-1) + * val xn: Tn = x$1.apply(n-1) * body * } * @@ -1502,16 +1525,26 @@ object desugar { flags = if params.nonEmpty && params.head.mods.is(Given) then SyntheticTermParam | Given else SyntheticTermParam) + + def paramIsUsed(name: Name, body: Tree): Boolean = + val acc = new UntypedTreeAccumulator[Boolean]: + def apply(x: Boolean, t: Tree)(using Context) = + if x then true + else t match + case Ident(id) => id == name + case _ => foldOver(x, t) + acc(false, body) + def selector(n: Int) = if (isGenericTuple) Apply(Select(refOfDef(param), nme.apply), Literal(Constant(n))) else Select(refOfDef(param), nme.selectorName(n)) val vdefs = - params.zipWithIndex.map { - case (param, idx) => + params.zipWithIndex.collect { + case (param, idx) if param.name != nme.WILDCARD && + (!param.name.is(WildcardParamName) || paramIsUsed(param.name, body)) => ValDef(param.name, param.tpt, selector(idx)) .withSpan(param.span) .withAttachment(UntupledParam, ()) - .withFlags(Synthetic) } Function(param :: Nil, Block(vdefs, body)) } @@ -1542,10 +1575,12 @@ object desugar { val vdef = ValDef(named.name.asTermName, tpt, rhs) .withMods(mods) .withSpan(original.span.withPoint(named.span.start)) + .withAttachment(PatternVar, ()) val mayNeedSetter = valDef(vdef) mayNeedSetter } + @unused private def derivedDefDef(original: Tree, named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers)(implicit src: SourceFile) = DefDef(named.name.asTermName, Nil, tpt, rhs) .withMods(mods) @@ -1616,7 +1651,19 @@ object desugar { val matchCheckMode = if (gen.checkMode == GenCheckMode.Check || gen.checkMode == GenCheckMode.CheckAndFilter) MatchCheck.IrrefutableGenFrom else MatchCheck.None - makeCaseLambda(CaseDef(gen.pat, EmptyTree, body) :: Nil, matchCheckMode) + val pat = gen.pat.match + case Tuple(pats) if pats.length > Definitions.MaxImplementedFunctionArity => + /* The pattern case is a tupleXXL, because we have bound > 21 variables in the comprehension. + * In this case, we need to mark all the typed patterns as @unchecked, or get loads of warnings. + * Cf. warn test i23164.scala */ + Tuple: + pats.map: + case t @ Bind(name, tp @ Typed(id, tpt)) => + val annotated = Annotated(tpt, New(ref(defn.UncheckedAnnot.typeRef))) + cpy.Bind(t)(name, cpy.Typed(tp)(id, annotated)).withMods(t.mods) + case t => t + case _ => gen.pat + makeCaseLambda(CaseDef(pat, EmptyTree, body) :: Nil, matchCheckMode) } /** If `pat` is not an Identifier, a Typed(Ident, _), or a Bind, wrap @@ -1624,18 +1671,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) => inventGivenOrExtensionName(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 } @@ -1733,7 +1781,7 @@ object desugar { case _ => Modifiers() makePatDef(valeq, mods, defpat, rhs) } - val rhs1 = makeFor(nme.map, nme.flatMap, GenFrom(defpat0, gen.expr, gen.checkMode) :: Nil, Block(pdefs, makeTuple(id0 :: ids))) + val rhs1 = makeFor(nme.map, nme.flatMap, GenFrom(defpat0, gen.expr, gen.checkMode) :: Nil, Block(pdefs, makeTuple(id0 :: ids).withAttachment(ForArtifact, ()))) val allpats = gen.pat :: pats val vfrom1 = GenFrom(makeTuple(allpats), rhs1, GenCheckMode.Ignore) makeFor(mapName, flatMapName, vfrom1 :: rest1, body) diff --git a/compiler/src/dotty/tools/dotc/ast/MainProxies.scala b/compiler/src/dotty/tools/dotc/ast/MainProxies.scala index 8ee75cbf364b..d04dd82fb424 100644 --- a/compiler/src/dotty/tools/dotc/ast/MainProxies.scala +++ b/compiler/src/dotty/tools/dotc/ast/MainProxies.scala @@ -103,17 +103,17 @@ object MainProxies { val body = Try(call, handler :: Nil, EmptyTree) val mainArg = ValDef(nme.args, TypeTree(defn.ArrayType.appliedTo(defn.StringType)), EmptyTree) .withFlags(Param) - /** Replace typed `Ident`s that have been typed with a TypeSplice with the reference to the symbol. - * The annotations will be retype-checked in another scope that may not have the same imports. + + /** This context is used to create the `TypeSplices` wrapping annotations + * below. These should have `mainFun` as their owner (and not the + * enclosing package class that we would get otherwise) so that + * subsequent owner changes (for example in `Typer.typedTypedSplice`) are + * correct. See #22364 and associated tests. */ - def insertTypeSplices = new TreeMap { - override def transform(tree: Tree)(using Context): Tree = tree match - case tree: tpd.Ident @unchecked => TypedSplice(tree) - case tree => super.transform(tree) - } + val annotsCtx = ctx.fresh.setOwner(mainFun) val annots = mainFun.annotations .filterNot(_.matches(defn.MainAnnot)) - .map(annot => insertTypeSplices.transform(annot.tree)) + .map(annot => TypedSplice(annot.tree)(using annotsCtx)) val mainMeth = DefDef(nme.main, (mainArg :: Nil) :: Nil, TypeTree(defn.UnitType), body) .withFlags(JavaStatic | Synthetic) .withAnnotations(annots) diff --git a/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala b/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala index e77642a8e2b9..861db55b1903 100644 --- a/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala +++ b/compiler/src/dotty/tools/dotc/ast/NavigateAST.scala @@ -94,7 +94,8 @@ object NavigateAST { * When choosing better fit we compare spans. If candidate span has starting or ending point inside (exclusive) * current best fit it is selected as new best fit. This means that same spans are failing the first predicate. * - * In case when spans start and end at same offsets we prefer non synthethic one. + * In case when spans start and end at same offsets we prefer non synthethic one, + * and then one with better point (see isBetterPoint below). */ def isBetterFit(currentBest: List[Positioned], candidate: List[Positioned]): Boolean = if currentBest.isEmpty && candidate.nonEmpty then true @@ -102,9 +103,20 @@ object NavigateAST { val bestSpan = currentBest.head.span val candidateSpan = candidate.head.span - bestSpan != candidateSpan && - envelops(bestSpan, candidateSpan) || - bestSpan.contains(candidateSpan) && bestSpan.isSynthetic && !candidateSpan.isSynthetic + def isBetterPoint = + // Given two spans with same end points, + // we compare their points in relation to the point we are looking for (span.point) + // The candidate (candidateSpan.point) is better than what we have so far (bestSpan.point), when: + // 1) candidate is closer to target from the right + span.point <= candidateSpan.point && candidateSpan.point < bestSpan.point + // 2) candidate is closer to target from the left + || bestSpan.point < candidateSpan.point && candidateSpan.point <= span.point + // 3) candidate is to on the left side of target, and best so far is on the right + || candidateSpan.point <= span.point && span.point < bestSpan.point + + bestSpan != candidateSpan && envelops(bestSpan, candidateSpan) + || bestSpan.contains(candidateSpan) && bestSpan.isSynthetic && !candidateSpan.isSynthetic + || candidateSpan.start == bestSpan.start && candidateSpan.end == bestSpan.end && isBetterPoint else false def isRecoveryTree(sel: untpd.Select): Boolean = @@ -141,7 +153,9 @@ object NavigateAST { case _ => val iterator = p match case defdef: DefTree[?] => - p.productIterator ++ defdef.mods.productIterator + val mods = defdef.mods + val annotations = defdef.symbol.annotations.filter(_.tree.span.contains(span)).map(_.tree) + p.productIterator ++ annotations ++ mods.productIterator case _ => p.productIterator childPath(iterator, p :: path) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 8c682971adcb..d77b9d915605 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -142,7 +142,7 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => def allTermArguments(tree: Tree): List[Tree] = unsplice(tree) match { case Apply(fn, args) => allTermArguments(fn) ::: args case TypeApply(fn, args) => allTermArguments(fn) - case Block(Nil, expr) => allTermArguments(expr) + case Block(_, expr) => allTermArguments(expr) case _ => Nil } @@ -150,7 +150,7 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => def allArguments(tree: Tree): List[Tree] = unsplice(tree) match { case Apply(fn, args) => allArguments(fn) ::: args case TypeApply(fn, args) => allArguments(fn) ::: args - case Block(Nil, expr) => allArguments(expr) + case Block(_, expr) => allArguments(expr) case _ => Nil } @@ -262,6 +262,19 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => case _ => false } + /** Expression was written `e: Unit` to quell warnings. Looks into adapted tree. */ + def isAscribedToUnit(tree: Tree): Boolean = + import typer.Typer.AscribedToUnit + tree.hasAttachment(AscribedToUnit) + || { + def loop(tree: Tree): Boolean = tree match + case Apply(fn, _) => fn.hasAttachment(AscribedToUnit) || loop(fn) + case TypeApply(fn, _) => fn.hasAttachment(AscribedToUnit) || loop(fn) + case Block(_, expr) => expr.hasAttachment(AscribedToUnit) || loop(expr) + case _ => false + loop(tree) + } + /** Does this CaseDef catch Throwable? */ def catchesThrowable(cdef: CaseDef)(using Context): Boolean = catchesAllOf(cdef, defn.ThrowableType) @@ -441,7 +454,9 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] */ private def defKind(tree: Tree)(using Context): FlagSet = unsplice(tree) match { case EmptyTree | _: Import => NoInitsInterface - case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface + case tree: TypeDef => + if tree.isClassDef then NoInits + else NoInitsInterface case tree: DefDef => if tree.unforcedRhs == EmptyTree && tree.paramss.forall { @@ -450,8 +465,6 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] } then NoInitsInterface - else if tree.mods.is(Given) && tree.paramss.isEmpty then - EmptyFlags // might become a lazy val: TODO: check whether we need to suppress NoInits once we have new lazy val impl else NoInits case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags @@ -470,7 +483,7 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] /** An extractor for trees of the form `id` or `id: T` */ object IdPattern { def unapply(tree: Tree)(using Context): Option[VarInfo] = tree match { - case id: Ident if id.name != nme.WILDCARD => Some(id, TypeTree()) + case id: Ident if id.name != nme.WILDCARD => Some((id, TypeTree())) case Typed(id: Ident, tpt) => Some((id, tpt)) case _ => None } @@ -803,7 +816,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => /** An extractor for def of a closure contained the block of the closure. */ object closureDef { def unapply(tree: Tree)(using Context): Option[DefDef] = tree match { - case Block((meth : DefDef) :: Nil, closure: Closure) if meth.symbol == closure.meth.symbol => + case Block((meth: DefDef) :: Nil, closure: Closure) if meth.symbol == closure.meth.symbol => Some(meth) case Block(Nil, expr) => unapply(expr) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 5ddbdd990182..0298c306d24d 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -224,6 +224,11 @@ class TreeTypeMap( val tmap1 = tmap.withMappedSyms( origCls(cls).typeParams ::: origDcls, cls.typeParams ::: mappedDcls) + mapped.foreach { sym => + // outer Symbols can reference nested ones in info, + // so we remap that once again with the updated TreeTypeMap + sym.info = tmap1.mapType(sym.info) + } origDcls.lazyZip(mappedDcls).foreach(cls.asClass.replace) tmap1 } diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 7652dcfc9928..11c464b2a667 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -31,7 +31,7 @@ object Trees { /** Property key for backquoted identifiers and definitions */ val Backquoted: Property.StickyKey[Unit] = Property.StickyKey() - + val SyntheticUnit: Property.StickyKey[Unit] = Property.StickyKey() /** Trees take a parameter indicating what the type of their `tpe` field @@ -319,6 +319,8 @@ object Trees { import WithEndMarker.* + /** The span of the name in the end marker, or `NoSpan` if none. + */ final def endSpan(using Context): Span = if hasEndMarker then val realName = srcName.stripModuleClassSuffix.lastPart @@ -329,8 +331,8 @@ object Trees { /** The name in source code that represents this construct, * and is the name that the user must write to create a valid * end marker. - * e.g. a constructor definition is terminated in the source - * code by `end this`, so it's `srcName` should return `this`. + * E.g., a constructor definition is terminated in the source + * code by `end this`, so its `srcName` should return `this`. */ protected def srcName(using Context): Name @@ -460,8 +462,11 @@ object Trees { else if qualifier.span.exists && qualifier.span.start > span.point then // right associative val realName = name.stripModuleClassSuffix.lastPart Span(span.start, span.start + realName.length, point) - else - Span(point, span.end, point) + else if span.pointMayBeIncorrect then + val realName = name.stripModuleClassSuffix.lastPart + val probablyPoint = span.end - realName.length + Span(probablyPoint, span.end, probablyPoint) + else Span(point, span.end, point) else span } @@ -765,6 +770,7 @@ object Trees { override def isEmpty: Boolean = !hasType override def toString: String = s"TypeTree${if (hasType) s"[$typeOpt]" else ""}" + def isInferred = false } /** Tree that replaces a level 1 splices in pickled (level 0) quotes. @@ -787,6 +793,7 @@ object Trees { */ class InferredTypeTree[+T <: Untyped](implicit @constructorOnly src: SourceFile) extends TypeTree[T]: type ThisTree[+T <: Untyped] <: InferredTypeTree[T] + override def isInferred = true /** ref.type */ case class SingletonTypeTree[+T <: Untyped] private[ast] (ref: Tree[T])(implicit @constructorOnly src: SourceFile) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index c119dfd8d982..5d6cd7f7018a 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -365,7 +365,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { * new parents { termForwarders; typeAliases } * * @param parents a non-empty list of class types - * @param termForwarders a non-empty list of forwarding definitions specified by their name and the definition they forward to. + * @param termForwarders a non-empty list of forwarding definitions specified by their name + * and the definition they forward to. * @param typeMembers a possibly-empty list of type members specified by their name and their right hand side. * @param adaptVarargs if true, allow matching a vararg superclass constructor * with a missing argument in superArgs, and synthesize an @@ -477,6 +478,21 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def ref(sym: Symbol)(using Context): Tree = ref(NamedType(sym.owner.thisType, sym.name, sym.denot)) + // Like `ref`, but avoids wrapping innermost module class references with This(), + // instead mapping those to objects, so that the resulting trees can be used in + // largest scope possible (method added for macros) + def generalisedRef(sym: Symbol)(using Context): Tree = + // Removes ThisType from inner module classes, replacing those with references to objects + def simplifyThisTypePrefix(tpe: Type)(using Context): Type = + tpe match + case ThisType(tref @ TypeRef(prefix, _)) if tref.symbol.flags.is(Module) => + TermRef(simplifyThisTypePrefix(prefix), tref.symbol.companionModule) + case TypeRef(prefix, designator) => + TypeRef(simplifyThisTypePrefix(prefix), designator) + case _ => + tpe + ref(NamedType(simplifyThisTypePrefix(sym.owner.thisType), sym.name, sym.denot)) + private def followOuterLinks(t: Tree)(using Context) = t match { case t: This if ctx.erasedTypes && !(t.symbol == ctx.owner.enclosingClass || t.symbol.isStaticOwner) => // after erasure outer paths should be respected @@ -817,14 +833,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Closure(tree: Tree)(env, meth, tpt) } - // This is a more fault-tolerant copier that does not cause errors when - // function types in applications are undefined. - // This was called `Inliner.InlineCopier` before 3.6.3. - class ConservativeTreeCopier() extends TypedTreeCopier: - override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(using Context): Apply = - if fun.tpe.widen.exists then super.Apply(tree)(fun, args) - else untpd.cpy.Apply(tree)(fun, args).withTypeUnchecked(tree.tpe) - override def skipTransform(tree: Tree)(using Context): Boolean = tree.tpe.isError implicit class TreeOps[ThisTree <: tpd.Tree](private val tree: ThisTree) extends AnyVal { @@ -1511,7 +1519,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { * @param selectorPredicate A test to find the selector to use. * @return The symbols imported. */ - def importedSymbols(imp: Import, + def importedSymbols(imp: ImportOrExport, selectorPredicate: untpd.ImportSelector => Boolean = util.common.alwaysTrue) (using Context): List[Symbol] = imp.selectors.find(selectorPredicate) match diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 4c8fea17c076..84253fa63b1f 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -138,6 +138,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case Ident(rename: TermName) => rename case _ => name + /** It's a masking import if `!isWildcard`. */ def isUnimport = rename == nme.WILDCARD } diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 3ba7c6019246..2b38e2c5bee5 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -159,7 +159,7 @@ class CheckCaptures extends Recheck, SymTransformer: def newRechecker()(using Context) = CaptureChecker(ctx) - override def run(using Context): Unit = + override protected def run(using Context): Unit = if Feature.ccEnabled then super.run diff --git a/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala b/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala index 51b261583feb..ec04eb3db8a1 100644 --- a/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala +++ b/compiler/src/dotty/tools/dotc/classpath/AggregateClassPath.scala @@ -51,7 +51,6 @@ case class AggregateClassPath(aggregates: Seq[ClassPath]) extends ClassPath { case (_, s) => s } } - override def asURLs: Seq[URL] = aggregates.flatMap(_.asURLs) override def asClassPathStrings: Seq[String] = aggregates.map(_.asClassPathString).distinct diff --git a/compiler/src/dotty/tools/dotc/classpath/ClassPathFactory.scala b/compiler/src/dotty/tools/dotc/classpath/ClassPathFactory.scala index 0b66f339bf53..605d00b56d17 100644 --- a/compiler/src/dotty/tools/dotc/classpath/ClassPathFactory.scala +++ b/compiler/src/dotty/tools/dotc/classpath/ClassPathFactory.scala @@ -7,12 +7,16 @@ import dotty.tools.io.{AbstractFile, VirtualDirectory} import FileUtils.* import dotty.tools.io.ClassPath import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.interactive.LogicalSourcePath +import dotty.tools.dotc.interactive.LogicalPackagesProvider +import dotty.tools.dotc.interactive.LogicalPackage +import java.nio.file.Files /** * Provides factory methods for classpath. When creating classpath instances for a given path, * it uses proper type of classpath depending on a types of particular files containing sources or classes. */ -class ClassPathFactory { +class ClassPathFactory(precomputedSourcePackages: Option[LogicalPackage] = None) { /** * Create a new classpath based on the abstract file. */ @@ -22,12 +26,16 @@ class ClassPathFactory { * Creators for sub classpaths which preserve this context. */ def sourcesInPath(path: String)(using Context): List[ClassPath] = - for { - file <- expandPath(path, expandStar = false) - dir <- Option(AbstractFile getDirectory file) + precomputedSourcePackages match { + // We also accept files in case of YlogicalPackageLoading + case Some(rootPackage) if ctx.settings.YlogicalPackageLoading.value => + List(new LogicalSourcePath(path, rootPackage)) + case _ => + for + file <- expandPath(path, expandStar = false) + dir <- Option(AbstractFile.getDirectory(file)) + yield createSourcePath(dir) } - yield createSourcePath(dir) - def expandPath(path: String, expandStar: Boolean = true): List[String] = dotty.tools.io.ClassPath.expandPath(path, expandStar) diff --git a/compiler/src/dotty/tools/dotc/classpath/VirtualDirectoryClassPath.scala b/compiler/src/dotty/tools/dotc/classpath/VirtualDirectoryClassPath.scala index 39b1ae5f4277..407f39c0615a 100644 --- a/compiler/src/dotty/tools/dotc/classpath/VirtualDirectoryClassPath.scala +++ b/compiler/src/dotty/tools/dotc/classpath/VirtualDirectoryClassPath.scala @@ -15,7 +15,7 @@ case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath wi var file: AbstractFile = base val dirParts = pathParts.init.iterator while (dirParts.hasNext) { - val dirPart = dirParts.next + val dirPart = dirParts.next() file = file.lookupName(dirPart, directory = true) if (file == null) return null @@ -27,7 +27,7 @@ case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath wi protected def getSubDir(packageDirName: String): Option[AbstractFile] = Option(lookupPath(dir)(packageDirName.split(java.io.File.separator).toIndexedSeq, directory = true)) protected def listChildren(dir: AbstractFile, filter: Option[AbstractFile => Boolean] = None): Array[F] = filter match { - case Some(f) => dir.iterator.filter(f).toArray + case Some(f) => dir.iterator().filter(f).toArray case _ => dir.toArray } def getName(f: AbstractFile): String = f.name diff --git a/compiler/src/dotty/tools/dotc/config/CliCommand.scala b/compiler/src/dotty/tools/dotc/config/CliCommand.scala index b76af885765c..ce97e15483fd 100644 --- a/compiler/src/dotty/tools/dotc/config/CliCommand.scala +++ b/compiler/src/dotty/tools/dotc/config/CliCommand.scala @@ -7,7 +7,7 @@ import Settings.* import core.Contexts.* import printing.Highlighting -import scala.util.chaining.given +import dotty.tools.dotc.util.chaining.* import scala.PartialFunction.cond trait CliCommand: @@ -33,8 +33,8 @@ trait CliCommand: | means one or a comma-separated list of: | - (partial) phase names with an optional "+" suffix to include the next phase | - the string "all" - | example: -Xprint:all prints all phases. - | example: -Xprint:typer,mixin prints the typer and mixin phases. + | example: -Vprint:all prints all phases. + | example: -Vprint:typer,mixin prints the typer and mixin phases. | example: -Ylog:erasure+ logs the erasure phase and the phase after the erasure phase. | This is useful because during the tree transform of phase X, we often | already are in phase X + 1. diff --git a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala index 884e40e97d96..52c7e3551232 100644 --- a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala +++ b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala @@ -7,15 +7,16 @@ import classpath.AggregateClassPath import core.* import Symbols.*, Types.*, Contexts.*, StdNames.* import Flags.* +import interactive.LogicalPackage import transform.ExplicitOuter -class JavaPlatform extends Platform { +class JavaPlatform(precomputedSourcePackages: Option[LogicalPackage] = None) extends Platform { private var currentClassPath: Option[ClassPath] = None def classPath(using Context): ClassPath = { if (currentClassPath.isEmpty) - currentClassPath = Some(new PathResolver().result) + currentClassPath = Some(new PathResolver(precomputedSourcePackages).result) val cp = currentClassPath.get cp } @@ -27,6 +28,13 @@ class JavaPlatform extends Platform { case _ => false }) + def addToClassPath(cPath: ClassPath)(using Context): Unit = classPath match { + case AggregateClassPath(entries) => + currentClassPath = Some(AggregateClassPath(entries :+ cPath)) + case cp: ClassPath => + currentClassPath = Some(AggregateClassPath(cp :: cPath :: Nil)) + } + /** Update classpath with a substituted subentry */ def updateClassPath(subst: Map[ClassPath, ClassPath]): Unit = currentClassPath.get match { case AggregateClassPath(entries) => @@ -43,7 +51,10 @@ class JavaPlatform extends Platform { cls.superClass == defn.ObjectClass && cls.directlyInheritedTraits.forall(_.is(NoInits)) && !ExplicitOuter.needsOuterIfReferenced(cls) && - cls.typeRef.fields.isEmpty // Superaccessors already show up as abstract methods here, so no test necessary + // Superaccessors already show up as abstract methods here, so no test necessary + cls.typeRef.fields.isEmpty && + // Check if the SAM can be implemented via LambdaMetaFactory + TypeErasure.samExpansionNotNeeded(cls) /** We could get away with excluding BoxedBooleanClass for the * purpose of equality testing since it need not compare equal @@ -66,4 +77,8 @@ class JavaPlatform extends Platform { def newClassLoader(bin: AbstractFile)(using Context): SymbolLoader = new ClassfileLoader(bin) + + def typeMightBeSubtypeAtRuntime(c: Symbol, potentialSuperClass: Symbol)(using Context): Boolean = + // On the JVM, we add an implementation of Serializable to everything + potentialSuperClass == defn.JavaSerializableClass } diff --git a/compiler/src/dotty/tools/dotc/config/PathResolver.scala b/compiler/src/dotty/tools/dotc/config/PathResolver.scala index f60727e6bba2..0c05c461ac94 100644 --- a/compiler/src/dotty/tools/dotc/config/PathResolver.scala +++ b/compiler/src/dotty/tools/dotc/config/PathResolver.scala @@ -12,6 +12,7 @@ import PartialFunction.condOpt import core.Contexts.* import Settings.* import dotty.tools.io.File +import dotty.tools.dotc.interactive.LogicalPackage object PathResolver { @@ -53,8 +54,7 @@ object PathResolver { def classPathEnv: String = envOrElse("CLASSPATH", "") def sourcePathEnv: String = envOrElse("SOURCEPATH", "") - //using propOrNone/getOrElse instead of propOrElse so that searchForBootClasspath is lazy evaluated - def javaBootClassPath: String = propOrNone("sun.boot.class.path") getOrElse searchForBootClasspath + def javaBootClassPath: String = propOrElse("sun.boot.class.path", searchForBootClasspath) def javaExtDirs: String = propOrEmpty("java.ext.dirs") def scalaHome: String = propOrEmpty("scala.home") @@ -172,10 +172,10 @@ object PathResolver { import PathResolver.{Defaults, ppcp} -class PathResolver(using c: Context) { +class PathResolver(precomputedSourcePackages: Option[LogicalPackage] = None)(using c: Context) { import c.base.settings - private val classPathFactory = new ClassPathFactory + private val classPathFactory = new ClassPathFactory(precomputedSourcePackages) private def cmdLineOrElse(name: String, alt: String) = commandLineFor(name) match { diff --git a/compiler/src/dotty/tools/dotc/config/Platform.scala b/compiler/src/dotty/tools/dotc/config/Platform.scala index db05996d98fd..21c8afa3fcb6 100644 --- a/compiler/src/dotty/tools/dotc/config/Platform.scala +++ b/compiler/src/dotty/tools/dotc/config/Platform.scala @@ -12,6 +12,11 @@ import core.Flags.Module */ abstract class Platform { + /** Initialize platform-specific definitions for the current run. + * Called at the start of each compilation run. + */ + def init()(using Context): Unit = () + /** The root symbol loader. */ def rootLoader(root: TermSymbol)(using Context): SymbolLoader @@ -21,6 +26,9 @@ abstract class Platform { /** Update classpath with a substitution that maps entries to entries */ def updateClassPath(subst: Map[ClassPath, ClassPath]): Unit + /** Add new entry to classpath */ + def addToClassPath(cPath: ClassPath)(using Context): Unit + /** Any platform-specific phases. */ //def platformPhases: List[SubComponent] @@ -43,4 +51,12 @@ abstract class Platform { final def hasMainMethod(sym: Symbol)(using Context): Boolean = sym.info.member(nme.main).hasAltWith(d => isMainMethod(d.symbol) && (sym.is(Module) || d.symbol.isStatic)) + + /** + * Whether instances of the class represented by the class symbol `c` **might** be a subtype of + * the class represented by the class symbol `potentialSuperClass` at runtime on this platform. + * This does not imply that they always will be, + * only that the compiler cannot fold `x.isInstanceOf[potentialSuperClass]` to false for for `x: c` + */ + def typeMightBeSubtypeAtRuntime(c: Symbol, potentialSuperClass: Symbol)(using Context): Boolean } diff --git a/compiler/src/dotty/tools/dotc/config/Properties.scala b/compiler/src/dotty/tools/dotc/config/Properties.scala index 4ea058d2cca8..610428b0f752 100644 --- a/compiler/src/dotty/tools/dotc/config/Properties.scala +++ b/compiler/src/dotty/tools/dotc/config/Properties.scala @@ -45,7 +45,7 @@ trait PropertiesTrait { def propIsSet(name: String): Boolean = System.getProperty(name) != null def propIsSetTo(name: String, value: String): Boolean = propOrNull(name) == value - def propOrElse(name: String, alt: String): String = System.getProperty(name, alt) + def propOrElse(name: String, alt: => String): String = Option(System.getProperty(name)).getOrElse(alt) def propOrEmpty(name: String): String = propOrElse(name, "") def propOrNull(name: String): String = propOrElse(name, null) def propOrNone(name: String): Option[String] = Option(propOrNull(name)) @@ -53,11 +53,11 @@ trait PropertiesTrait { def setProp(name: String, value: String): String = System.setProperty(name, value) def clearProp(name: String): String = System.clearProperty(name) - def envOrElse(name: String, alt: String): String = Option(System getenv name) getOrElse alt + def envOrElse(name: String, alt: => String): String = Option(System getenv name) getOrElse alt def envOrNone(name: String): Option[String] = Option(System getenv name) // for values based on propFilename - def scalaPropOrElse(name: String, alt: String): String = scalaProps.getProperty(name, alt) + def scalaPropOrElse(name: String, alt: => String): String = scalaProps.getProperty(name, alt) def scalaPropOrEmpty(name: String): String = scalaPropOrElse(name, "") def scalaPropOrNone(name: String): Option[String] = Option(scalaProps.getProperty(name)) diff --git a/compiler/src/dotty/tools/dotc/config/SJSPlatform.scala b/compiler/src/dotty/tools/dotc/config/SJSPlatform.scala index f6e29754ef94..18457f18760e 100644 --- a/compiler/src/dotty/tools/dotc/config/SJSPlatform.scala +++ b/compiler/src/dotty/tools/dotc/config/SJSPlatform.scala @@ -5,6 +5,7 @@ import Contexts.* import Symbols.* import dotty.tools.backend.sjs.JSDefinitions +import dotty.tools.dotc.interactive.LogicalPackage object SJSPlatform { /** The `SJSPlatform` for the current context. */ @@ -12,11 +13,14 @@ object SJSPlatform { ctx.platform.asInstanceOf[SJSPlatform] } -class SJSPlatform()(using Context) extends JavaPlatform { +class SJSPlatform(precomputedSourcePackages: Option[LogicalPackage] = None) extends JavaPlatform(precomputedSourcePackages) { /** Scala.js-specific definitions. */ val jsDefinitions: JSDefinitions = new JSDefinitions() + override def init()(using Context): Unit = + jsDefinitions.init() + /** Is the SAMType `cls` also a SAM under the rules of the Scala.js back-end? */ override def isSam(cls: ClassSymbol)(using Context): Boolean = defn.isFunctionClass(cls) @@ -32,4 +36,10 @@ class SJSPlatform()(using Context) extends JavaPlatform { */ override def shouldReceiveJavaSerializationMethods(sym: ClassSymbol)(using Context): Boolean = !sym.isSubClass(jsDefinitions.JSAnyClass) + + override def typeMightBeSubtypeAtRuntime(c: Symbol, potentialSuperClass: Symbol)(using Context): Boolean = + // because we deal with primitive types, we also have to deal with the boxed version, + // otherwise the compiler will error with, e.g., "cannot test if scala.Double is a subtype of java.lang.Integer" + (defn.ScalaNumericValueClasses()(c) || defn.ScalaNumericBoxedClasses()(c)) && + (defn.ScalaNumericValueClasses()(potentialSuperClass) || defn.ScalaNumericBoxedClasses()(potentialSuperClass)) } diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 6928e0617069..d462a1eec0a2 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -12,7 +12,7 @@ import dotty.tools.io.{AbstractFile, Directory, JDK9Reflectors, PlainDirectory} import dotty.tools.backend.jvm.BackendUtils.classfileVersionMap import Setting.ChoiceWithHelp -import scala.util.chaining.* +import dotty.tools.dotc.util.chaining.* import java.util.zip.Deflater @@ -37,7 +37,8 @@ object ScalaSettings: ScalaRelease.values.toList.map(_.show) def supportedSourceVersions: List[String] = - SourceVersion.values.toList.map(_.toString) + SourceVersion.values.diff(SourceVersion.illegalInSettings) + .map(_.toString).toList def defaultClasspath: String = sys.env.getOrElse("CLASSPATH", ".") @@ -135,6 +136,7 @@ trait CommonScalaSettings: val usejavacp: Setting[Boolean] = BooleanSetting("-usejavacp", "Utilize the java.class.path in classpath resolution.", aliases = List("--use-java-class-path")) val scalajs: Setting[Boolean] = BooleanSetting("-scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).", aliases = List("--scalajs")) val replInitScript: Setting[String] = StringSetting("-repl-init-script", "code", "The code will be run on REPL startup.", "", aliases = List("--repl-init-script")) + val replQuitAfterInit: Setting[Boolean] = BooleanSetting("-repl-quit-after-init", "Quit REPL after evaluating the init script.", aliases = List("--repl-quit-after-init")) end CommonScalaSettings @@ -152,7 +154,7 @@ private sealed trait PluginSettings: private sealed trait VerboseSettings: self: SettingGroup => val Vhelp: Setting[Boolean] = BooleanSetting("-V", "Print a synopsis of verbose options.") - val Xprint: Setting[List[String]] = PhasesSetting("-Vprint", "Print out program after", aliases = List("-Xprint")) + val Vprint: Setting[List[String]] = PhasesSetting("-Vprint", "Print out program after", aliases = List("-Xprint")) val XshowPhases: Setting[Boolean] = BooleanSetting("-Vphases", "List compiler phases.", aliases = List("-Xshow-phases")) val Vprofile: Setting[Boolean] = BooleanSetting("-Vprofile", "Show metrics about sources and internal representations to estimate compile-time complexity.") @@ -172,6 +174,10 @@ private sealed trait WarningSettings: private val WvalueDiscard: Setting[Boolean] = BooleanSetting("-Wvalue-discard", "Warn when non-Unit expression results are unused.") private val WNonUnitStatement = BooleanSetting("-Wnonunit-statement", "Warn when block statements are non-Unit expressions.") private val WenumCommentDiscard = BooleanSetting("-Wenum-comment-discard", "Warn when a comment ambiguously assigned to multiple enum cases is discarded.") + private val WtoStringInterpolated = BooleanSetting("-Wtostring-interpolated", "Warn a standard interpolator used toString on a reference type.") + private val WrecurseWithDefault = BooleanSetting("-Wrecurse-with-default", "Warn when a method calls itself with a default argument.") + private val WwrongArrow = BooleanSetting("-Wwrong-arrow", "Warn if function arrow was used instead of context literal ?=>.") + private val WinferUnion = BooleanSetting("-Winfer-union", "Warn if type argument was inferred as union type.") private val Wunused: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( name = "-Wunused", helpArg = "warning", @@ -179,28 +185,15 @@ private sealed trait WarningSettings: choices = List( ChoiceWithHelp("nowarn", ""), ChoiceWithHelp("all", ""), - ChoiceWithHelp( - name = "imports", - description = "Warn if an import selector is not referenced.\n" + - "NOTE : overrided by -Wunused:strict-no-implicit-warn"), + ChoiceWithHelp("imports", "Warn if an import selector is not referenced."), ChoiceWithHelp("privates", "Warn if a private member is unused"), ChoiceWithHelp("locals", "Warn if a local definition is unused"), ChoiceWithHelp("explicits", "Warn if an explicit parameter is unused"), ChoiceWithHelp("implicits", "Warn if an implicit parameter is unused"), ChoiceWithHelp("params", "Enable -Wunused:explicits,implicits"), + ChoiceWithHelp("patvars","Warn if a variable bound in a pattern is unused"), + //ChoiceWithHelp("inlined", "Apply -Wunused to inlined expansions"), // TODO ChoiceWithHelp("linted", "Enable -Wunused:imports,privates,locals,implicits"), - ChoiceWithHelp( - name = "strict-no-implicit-warn", - description = "Same as -Wunused:import, only for imports of explicit named members.\n" + - "NOTE : This overrides -Wunused:imports and NOT set by -Wunused:all" - ), - // ChoiceWithHelp("patvars","Warn if a variable bound in a pattern is unused"), - ChoiceWithHelp( - name = "unsafe-warn-patvars", - description = "(UNSAFE) Warn if a variable bound in a pattern is unused.\n" + - "This warning can generate false positive, as warning cannot be\n" + - "suppressed yet." - ) ), default = Nil ) @@ -212,7 +205,6 @@ private sealed trait WarningSettings: // Is any choice set for -Wunused? def any(using Context): Boolean = Wall.value || Wunused.value.nonEmpty - // overrided by strict-no-implicit-warn def imports(using Context) = (allOr("imports") || allOr("linted")) && !(strictNoImplicitWarn) def locals(using Context) = @@ -226,9 +218,8 @@ private sealed trait WarningSettings: def params(using Context) = allOr("params") def privates(using Context) = allOr("privates") || allOr("linted") - def patvars(using Context) = - isChoiceSet("unsafe-warn-patvars") // not with "all" - // allOr("patvars") // todo : rename once fixed + def patvars(using Context) = allOr("patvars") || isChoiceSet("unsafe-warn-patvars") + def inlined(using Context) = isChoiceSet("inlined") def linted(using Context) = allOr("linted") def strictNoImplicitWarn(using Context) = @@ -297,7 +288,11 @@ private sealed trait WarningSettings: def valueDiscard(using Context): Boolean = allOr(WvalueDiscard) def nonUnitStatement(using Context): Boolean = allOr(WNonUnitStatement) def enumCommentDiscard(using Context): Boolean = allOr(WenumCommentDiscard) + def toStringInterpolated(using Context): Boolean = allOr(WtoStringInterpolated) + def recurseWithDefault(using Context): Boolean = allOr(WrecurseWithDefault) def checkInit(using Context): Boolean = allOr(YcheckInit) + def wrongArrow(using Context): Boolean = allOr(WwrongArrow) + def inferUnion(using Context): Boolean = allOr(WinferUnion) /** -X "Extended" or "Advanced" settings */ private sealed trait XSettings: @@ -327,7 +322,7 @@ private sealed trait XSettings: val XmixinForceForwarders = ChoiceSetting( name = "-Xmixin-force-forwarders", helpArg = "mode", - descr = "Generate forwarder methods in classes inhering concrete methods from traits.", + descr = "Generate forwarder methods in classes inheriting concrete methods from traits.", choices = List("true", "junit", "false"), default = "true") @@ -384,6 +379,7 @@ private sealed trait YSettings: val YdisableFlatCpCaching: Setting[Boolean] = BooleanSetting("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.") val Yscala2Unpickler: Setting[String] = StringSetting("-Yscala2-unpickler", "", "Control where we may get Scala 2 symbols from. This is either \"always\", \"never\", or a classpath.", "always") + val Yreporter: Setting[String] = StringSetting(name = "-Yreporter", helpArg = "", descr = "Specify a dotty.tools.dotc.reporting.Reporter", default = "dotty.tools.dotc.reporting.ConsoleReporter") val YnoImports: Setting[Boolean] = BooleanSetting("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.") val Yimports: Setting[List[String]] = MultiStringSetting("-Yimports", helpArg="", "Custom root imports. If set, none of scala.*, java.lang.*, or Predef.* will be imported unless explicitly included.") @@ -397,6 +393,7 @@ private sealed trait YSettings: val YstopAfter: Setting[List[String]] = PhasesSetting("-Ystop-after", "Stop after", aliases = List("-stop")) // backward compat val YstopBefore: Setting[List[String]] = PhasesSetting("-Ystop-before", "Stop before") // stop before erasure as long as we have not debugged it fully val YshowSuppressedErrors: Setting[Boolean] = BooleanSetting("-Yshow-suppressed-errors", "Also show follow-on errors and warnings that are normally suppressed.") + val YlogicalPackageLoading: Setting[Boolean] = BooleanSetting("-Ylogical-package-loading", "Enable logical package loading. This will load the logical package structure by preparsing the source files to discover the package structure. To be used together with -sourcepath option.") val YdetailedStats: Setting[Boolean] = BooleanSetting("-Ydetailed-stats", "Show detailed internal compiler stats (needs Stats.enabled to be set to true).") val YkindProjector: Setting[String] = ChoiceSetting("-Ykind-projector", "[underscores, disable]", "Allow `*` as type lambda placeholder to be compatible with kind projector. When invoked as -Ykind-projector:underscores will repurpose `_` to be a type parameter placeholder, this will disable usage of underscore as a wildcard.", List("disable", "", "underscores"), "disable") val YprintPos: Setting[Boolean] = BooleanSetting("-Yprint-pos", "Show tree positions.") @@ -424,6 +421,7 @@ private sealed trait YSettings: val YfromTastyIgnoreList: Setting[List[String]] = MultiStringSetting("-Yfrom-tasty-ignore-list", "file", "List of `tasty` files in jar files that will not be loaded when using -from-tasty.") val YnoExperimental: Setting[Boolean] = BooleanSetting("-Yno-experimental", "Disable experimental language features.") val YlegacyLazyVals: Setting[Boolean] = BooleanSetting("-Ylegacy-lazy-vals", "Use legacy (pre 3.3.0) implementation of lazy vals.") + val YfutureLazyVals: Setting[Boolean] = BooleanSetting("-Yfuture-lazy-vals", "Use future (post 3.8.0) implementation of lazy vals compatible with JDK 25+ - requires JDK 9+ and explicit --java-output-version flag. ") val YprofileEnabled: Setting[Boolean] = BooleanSetting("-Yprofile-enabled", "Enable profiling.") val YprofileDestination: Setting[String] = StringSetting("-Yprofile-destination", "file", "Where to send profiling output - specify a file, default is to the console.", "", depends = List(YprofileEnabled -> true)) diff --git a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala index b8fa7994ce0c..b59d89818483 100644 --- a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala +++ b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala @@ -6,10 +6,12 @@ import core.Decorators.* import util.Property enum SourceVersion: - case `3.0-migration`, `3.0`, `3.1` // Note: do not add `3.1-migration` here, 3.1 is the same language as 3.0. + case `3.0-migration`, `3.0` + case `3.1-migration`, `3.1` case `3.2-migration`, `3.2` case `3.3-migration`, `3.3` case `future-migration`, `future` + case `never` // needed for MigrationVersion.errorFrom if we never want to issue an error val isMigrating: Boolean = toString.endsWith("-migration") @@ -21,10 +23,17 @@ enum SourceVersion: def isAtMost(v: SourceVersion) = stable.ordinal <= v.ordinal object SourceVersion extends Property.Key[SourceVersion]: + def defaultSourceVersion = `3.3` + /* Illegal source versions that may not appear in the settings `-source:<...>` */ + val illegalInSettings = List(`3.1-migration`, `never`) + + /* Illegal source versions that may not appear as an import `import scala.language.<...>` */ + val illegalInImports = List(`3.1-migration`, `never`) + /** language versions that may appear in a language import, are deprecated, but not removed from the standard library. */ - val illegalSourceVersionNames = List("3.1-migration").map(_.toTermName) + val illegalSourceVersionNames = illegalInImports.map(_.toString.toTermName) /** language versions that the compiler recognises. */ val validSourceVersionNames = values.toList.map(_.toString.toTermName) diff --git a/compiler/src/dotty/tools/dotc/config/WrappedProperties.scala b/compiler/src/dotty/tools/dotc/config/WrappedProperties.scala index 20304b74c1da..a72830331e9f 100644 --- a/compiler/src/dotty/tools/dotc/config/WrappedProperties.scala +++ b/compiler/src/dotty/tools/dotc/config/WrappedProperties.scala @@ -14,12 +14,12 @@ trait WrappedProperties extends PropertiesTrait { protected def propCategory: String = "wrapped" protected def pickJarBasedOn: Class[?] = this.getClass - override def propIsSet(name: String): Boolean = wrap(super.propIsSet(name)) exists (x => x) - override def propOrElse(name: String, alt: String): String = wrap(super.propOrElse(name, alt)) getOrElse alt - override def setProp(name: String, value: String): String = wrap(super.setProp(name, value)).orNull - override def clearProp(name: String): String = wrap(super.clearProp(name)).orNull - override def envOrElse(name: String, alt: String): String = wrap(super.envOrElse(name, alt)) getOrElse alt - override def envOrNone(name: String): Option[String] = wrap(super.envOrNone(name)).flatten + override def propIsSet(name: String): Boolean = wrap(super.propIsSet(name)) exists (x => x) + override def propOrElse(name: String, alt: => String): String = wrap(super.propOrElse(name, alt)) getOrElse alt + override def setProp(name: String, value: String): String = wrap(super.setProp(name, value)).orNull + override def clearProp(name: String): String = wrap(super.clearProp(name)).orNull + override def envOrElse(name: String, alt: => String): String = wrap(super.envOrElse(name, alt)) getOrElse alt + override def envOrNone(name: String): Option[String] = wrap(super.envOrNone(name)).flatten def systemProperties: Iterator[(String, String)] = { import scala.jdk.CollectionConverters.* diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index f5825df84a8b..10be94ae2ff4 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -38,7 +38,7 @@ object Annotations { if (i < args.length) Some(args(i)) else None } def argumentConstant(i: Int)(using Context): Option[Constant] = - for (case ConstantType(c) <- argument(i) map (_.tpe.widenTermRefExpr.normalized)) yield c + for case ConstantType(c) <- argument(i).map(stripCast(_).tpe.widenTermRefExpr.normalized) yield c def argumentConstantString(i: Int)(using Context): Option[String] = for (case Constant(s: String) <- argumentConstant(i)) yield s @@ -65,7 +65,7 @@ object Annotations { if tm.isRange(x) then x else val tp1 = tm(tree.tpe) - foldOver(if !tp1.exists || (tp1 frozen_=:= tree.tpe) then x else tp1, tree) + foldOver(if !tp1.exists || tp1.eql(tree.tpe) then x else tp1, tree) val diff = findDiff(NoType, args) if tm.isRange(diff) then EmptyAnnotation else if diff.exists then derivedAnnotation(tm.mapOver(tree)) diff --git a/compiler/src/dotty/tools/dotc/core/Comments.scala b/compiler/src/dotty/tools/dotc/core/Comments.scala index 92160c97973d..b1d1e387c2cf 100644 --- a/compiler/src/dotty/tools/dotc/core/Comments.scala +++ b/compiler/src/dotty/tools/dotc/core/Comments.scala @@ -405,15 +405,10 @@ object Comments { val Trim = "(?s)^[\\s&&[^\n\r]]*(.*?)\\s*$".r val raw = ctx.docCtx.flatMap(_.docstring(sym).map(_.raw)).getOrElse("") - defs(sym) ++= defines(raw).map { - str => { - val start = skipWhitespace(str, "@define".length) - val (key, value) = str.splitAt(skipVariable(str, start)) - key.drop(start) -> value - } - } map { - case (key, Trim(value)) => - variableName(key) -> value.replaceAll("\\s+\\*+$", "") + defs(sym) ++= defines(raw).map { str => + val start = skipWhitespace(str, "@define".length) + val (key, Trim(value)) = str.splitAt(skipVariable(str, start)): @unchecked + variableName(key.drop(start)) -> value.replaceAll("\\s+\\*+$", "") } } diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 0ceaaac75063..617104630850 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -660,11 +660,20 @@ trait ConstraintHandling { def widenOr(tp: Type) = if widenUnions then val tpw = tp.widenUnion - if (tpw ne tp) && !isTransparent(tpw, traitOnly = false) && (tpw <:< bound) then tpw else tp + if tpw ne tp then + if tpw.isTransparent() then + // Now also widen singletons of soft unions. Before these were skipped + // since widenUnion on soft unions is independent of whether singletons + // are widened or not. This avoids an expensive subtype check in widenSingle, + // see i19907_*.scala for test cases. + widenSingle(tp, skipSoftUnions = false) + else if tpw <:< bound then tpw + else tp + else tp else tp.hardenUnions - def widenSingle(tp: Type) = - val tpw = tp.widenSingletons + def widenSingle(tp: Type, skipSoftUnions: Boolean) = + val tpw = tp.widenSingletons(skipSoftUnions) if (tpw ne tp) && (tpw <:< bound) then tpw else tp def isSingleton(tp: Type): Boolean = tp match @@ -674,7 +683,7 @@ trait ConstraintHandling { val wideInst = if isSingleton(bound) then inst else - val widenedFromSingle = widenSingle(inst) + val widenedFromSingle = widenSingle(inst, skipSoftUnions = widenUnions) val widenedFromUnion = widenOr(widenedFromSingle) val widened = dropTransparentTraits(widenedFromUnion, bound) widenIrreducible(widened) diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index bde23de135e4..6f6e8fcbf682 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -121,7 +121,7 @@ object Contexts { * risk of capturing complete trees. * - To make sure these rules are kept, it would be good to do a sanity * check using bytecode inspection with javap or scalap: Keep track - * of all class fields of type context; allow them only in whitelisted + * of all class fields of type context; allow them only in allowlisted * classes (which should be short-lived). */ abstract class Context(val base: ContextBase) { thiscontext => @@ -179,7 +179,7 @@ object Contexts { val local = incCallback local != null && local.enabled || forceRun - /** The Zinc compile progress callback implementation if we are run from Zinc, null otherwise */ + /** The Zinc compile progress callback implementation if we are run from Zinc or used by presentation compiler, null otherwise */ def progressCallback: ProgressCallback | Null = store(progressCallbackLoc) /** Run `op` if there exists a Zinc progress callback */ @@ -365,6 +365,7 @@ object Contexts { /** Is current phase after TyperPhase? */ final def isAfterTyper = base.isAfterTyper(phase) + final def isAfterInlining = base.isAfterInlining(phase) final def isTyper = base.isTyper(phase) /** Is this a context for the members of a class definition? */ @@ -436,6 +437,14 @@ object Contexts { .setScope(this.scope) } + /** Is this a super call context? + * This is the case if we are in a primary constructor and + * the outer context has as owner the owner of the enclosing class. + * The enclosing class is `owner.owner`, hence `outer.owner == owner.owner.owner`. + */ + def isSuperCallContext: Boolean = + owner.isPrimaryConstructor && outer.owner == owner.owner.owner + /** The super- or this-call context with given owner and locals. */ private def superOrThisCallContext(owner: Symbol, locals: Scope): FreshContext = { val classCtx = outersIterator.dropWhile(!_.isClassDefContext).next() @@ -899,6 +908,7 @@ object Contexts { */ def initialize()(using Context): Unit = { _platform = newPlatform + platform.init() definitions.init() } diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 4aed3d310b03..32cbd48d3b5b 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -314,7 +314,7 @@ class Definitions { val cls = requiredClass("java.lang.Object") assert(!cls.isCompleted, "race for completing java.lang.Object") cls.info = ClassInfo(cls.owner.thisType, cls, List(AnyType, MatchableType), newScope) - cls.setFlag(NoInits | JavaDefined) + cls.setFlag(NoInits | JavaDefined | TransparentType) ensureConstructor(cls, cls.denot.asClass, EmptyScope) val companion = JavaLangPackageVal.info.decl(nme.Object).symbol.asTerm @@ -482,6 +482,8 @@ class Definitions { @tu lazy val Predef_undefined: Symbol = ScalaPredefModule.requiredMethod(nme.???) @tu lazy val ScalaPredefModuleClass: ClassSymbol = ScalaPredefModule.moduleClass.asClass + @tu lazy val SameTypeClass: ClassSymbol = requiredClass("scala.=:=") + @tu lazy val SameType_refl: Symbol = SameTypeClass.companionModule.requiredMethod(nme.refl) @tu lazy val SubTypeClass: ClassSymbol = requiredClass("scala.<:<") @tu lazy val SubType_refl: Symbol = SubTypeClass.companionModule.requiredMethod(nme.refl) @@ -494,6 +496,9 @@ class Definitions { @tu lazy val ScalaRuntime_toArray: Symbol = ScalaRuntimeModule.requiredMethod(nme.toArray) @tu lazy val ScalaRuntime_toObjectArray: Symbol = ScalaRuntimeModule.requiredMethod(nme.toObjectArray) + @tu lazy val MurmurHash3Module: Symbol = requiredModule("scala.util.hashing.MurmurHash3") + @tu lazy val MurmurHash3_productHash = MurmurHash3Module.info.member(termName("productHash")).suchThat(_.info.firstParamTypes.size == 3).symbol + @tu lazy val BoxesRunTimeModule: Symbol = requiredModule("scala.runtime.BoxesRunTime") @tu lazy val BoxesRunTimeModule_externalEquals: Symbol = BoxesRunTimeModule.info.decl(nme.equals_).suchThat(toDenot(_).info.firstParamTypes.size == 2).symbol @tu lazy val ScalaStaticsModule: Symbol = requiredModule("scala.runtime.Statics") @@ -693,6 +698,12 @@ class Definitions { @tu lazy val JavaFormattableClass: ClassSymbol = requiredClass("java.util.Formattable") @tu lazy val JavaRecordClass: Symbol = getClassIfDefined("java.lang.Record") + @tu lazy val JavaUtilObjectsClass: ClassSymbol = requiredModule("java.util.Objects").moduleClass.asClass + def Objects_hashCode(using Context): Symbol = + JavaUtilObjectsClass.info.member(nme.hashCode_).suchThat(_.info.firstParamTypes.length == 1).symbol + def Objects_equals(using Context): Symbol = + JavaUtilObjectsClass.info.member(nme.equals_).suchThat(_.info.firstParamTypes.length == 2).symbol + @tu lazy val JavaEnumClass: ClassSymbol = { val cls = requiredClass("java.lang.Enum") // jl.Enum has a single constructor protected(name: String, ordinal: Int). @@ -726,7 +737,10 @@ class Definitions { def JavaEnumType = JavaEnumClass.typeRef @tu lazy val MethodHandleClass: ClassSymbol = requiredClass("java.lang.invoke.MethodHandle") + @tu lazy val MethodHandlesClass: TermSymbol = requiredModule("java.lang.invoke.MethodHandles") + @tu lazy val MethodHandles_lookup: Symbol = MethodHandlesClass.requiredMethod("lookup") @tu lazy val MethodHandlesLookupClass: ClassSymbol = requiredClass("java.lang.invoke.MethodHandles.Lookup") + @tu lazy val MethodHandlesLookup_FindVarHandle: Symbol = MethodHandlesLookupClass.requiredMethod("findVarHandle") @tu lazy val VarHandleClass: ClassSymbol = requiredClass("java.lang.invoke.VarHandle") @tu lazy val StringBuilderClass: ClassSymbol = requiredClass("scala.collection.mutable.StringBuilder") @@ -809,6 +823,7 @@ class Definitions { @tu lazy val ReflectSelectableTypeRef: TypeRef = requiredClassRef("scala.reflect.Selectable") + @tu lazy val TypeableType: TypeSymbol = requiredPackage("scala.reflect.Typeable$package").moduleClass.requiredType("Typeable") @tu lazy val TypeTestClass: ClassSymbol = requiredClass("scala.reflect.TypeTest") @tu lazy val TypeTest_unapply: Symbol = TypeTestClass.requiredMethod(nme.unapply) @tu lazy val TypeTestModule_identity: Symbol = TypeTestClass.companionModule.requiredMethod(nme.identity) @@ -816,6 +831,7 @@ class Definitions { @tu lazy val QuotedExprClass: ClassSymbol = requiredClass("scala.quoted.Expr") @tu lazy val QuotesClass: ClassSymbol = requiredClass("scala.quoted.Quotes") + @tu lazy val Quotes_reflectModule: Symbol = QuotesClass.requiredClass("reflectModule") @tu lazy val Quotes_reflect: Symbol = QuotesClass.requiredValue("reflect") @tu lazy val Quotes_reflect_asTerm: Symbol = Quotes_reflect.requiredMethod("asTerm") @tu lazy val Quotes_reflect_Apply: Symbol = Quotes_reflect.requiredValue("Apply") @@ -941,6 +957,7 @@ class Definitions { def NonEmptyTupleClass(using Context): ClassSymbol = NonEmptyTupleTypeRef.symbol.asClass lazy val NonEmptyTuple_tail: Symbol = NonEmptyTupleClass.requiredMethod("tail") @tu lazy val PairClass: ClassSymbol = requiredClass("scala.*:") + @tu lazy val PairClass_unapply: Symbol = PairClass.companionModule.requiredMethod("unapply") @tu lazy val TupleXXLClass: ClassSymbol = requiredClass("scala.runtime.TupleXXL") def TupleXXLModule(using Context): Symbol = TupleXXLClass.companionModule @@ -999,6 +1016,7 @@ class Definitions { @tu lazy val DeprecatedAnnot: ClassSymbol = requiredClass("scala.deprecated") @tu lazy val DeprecatedOverridingAnnot: ClassSymbol = requiredClass("scala.deprecatedOverriding") @tu lazy val DeprecatedInheritanceAnnot: ClassSymbol = requiredClass("scala.deprecatedInheritance") + @tu lazy val DeprecatedNameAnnot: ClassSymbol = requiredClass("scala.deprecatedName") @tu lazy val ImplicitAmbiguousAnnot: ClassSymbol = requiredClass("scala.annotation.implicitAmbiguous") @tu lazy val ImplicitNotFoundAnnot: ClassSymbol = requiredClass("scala.annotation.implicitNotFound") @tu lazy val InlineParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.InlineParam") @@ -1026,11 +1044,13 @@ class Definitions { @tu lazy val ThrowsAnnot: ClassSymbol = requiredClass("scala.throws") @tu lazy val TransientAnnot: ClassSymbol = requiredClass("scala.transient") @tu lazy val UncheckedAnnot: ClassSymbol = requiredClass("scala.unchecked") + @tu lazy val UncheckedOverrideAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedOverride") @tu lazy val UncheckedStableAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedStable") @tu lazy val UncheckedVarianceAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedVariance") @tu lazy val UncheckedCapturesAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedCaptures") @tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile") @tu lazy val WithPureFunsAnnot: ClassSymbol = requiredClass("scala.annotation.internal.WithPureFuns") + @tu lazy val LanguageFeatureMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.languageFeature") @tu lazy val BeanGetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanGetter") @tu lazy val BeanSetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanSetter") @tu lazy val FieldMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.field") @@ -1078,7 +1098,8 @@ class Definitions { "lombok.NonNull" :: "reactor.util.annotation.NonNull" :: "reactor.util.annotation.NonNullApi" :: - "io.reactivex.annotations.NonNull" :: Nil) + "io.reactivex.annotations.NonNull" :: + "org.jspecify.annotations.NonNull" :: Nil) // convenient one-parameter method types def methOfAny(tp: Type): MethodType = MethodType(List(AnyType), tp) @@ -1717,18 +1738,30 @@ class Definitions { def isPolymorphicAfterErasure(sym: Symbol): Boolean = (sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf) || (sym eq Object_synchronized) - /** Is this type a `TupleN` type? + def isTypeTestOrCast(sym: Symbol): Boolean = + (sym eq Any_isInstanceOf) + || (sym eq Any_asInstanceOf) + || (sym eq Any_typeTest) + || (sym eq Any_typeCast) + + /** Is `tp` a `TupleN` type? + * + * @return true if the type of `tp` is `TupleN[T1, T2, ..., Tn]` + */ + def isDirectTupleNType(tp: Type)(using Context): Boolean = + val arity = tp.argInfos.length + arity <= MaxTupleArity && { + val tupletp = TupleType(arity) + tupletp != null && tp.isRef(tupletp.symbol) + } + + /** Is `tp` (an alias of) a `TupleN` type? * * @return true if the dealiased type of `tp` is `TupleN[T1, T2, ..., Tn]` */ - def isTupleNType(tp: Type)(using Context): Boolean = { + def isTupleNType(tp: Type)(using Context): Boolean = val tp1 = tp.dealias - val arity = tp1.argInfos.length - arity <= MaxTupleArity && { - val tupletp = TupleType(arity) - tupletp != null && tp1.isRef(tupletp.symbol) - } - } + isDirectTupleNType(tp1) def tupleType(elems: List[Type]): Type = { val arity = elems.length @@ -1954,7 +1987,6 @@ class Definitions { "AnyVal" -> Set("scala"), "Matchable" -> Set("scala"), "Product" -> Set("scala"), - "Object" -> Set("java.lang"), "Comparable" -> Set("java.lang"), "Serializable" -> Set("java.io"), "BitSetOps" -> Set("scala.collection"), @@ -2017,8 +2049,11 @@ class Definitions { val ScalaNumericValueClasses: PerRun[collection.Set[Symbol]] = new PerRun(ScalaNumericValueTypes.map(_.symbol)) val ScalaValueClasses: PerRun[collection.Set[Symbol]] = new PerRun(ScalaValueTypes.map(_.symbol)) + val ScalaNumericBoxedClasses: PerRun[collection.Set[Symbol]] = new PerRun( + Set(BoxedByteClass, BoxedShortClass, BoxedCharClass, BoxedIntClass, BoxedLongClass, BoxedFloatClass, BoxedDoubleClass) + ) val ScalaBoxedClasses: PerRun[collection.Set[Symbol]] = new PerRun( - Set(BoxedByteClass, BoxedShortClass, BoxedCharClass, BoxedIntClass, BoxedLongClass, BoxedFloatClass, BoxedDoubleClass, BoxedUnitClass, BoxedBooleanClass) + ScalaNumericBoxedClasses() `union` Set(BoxedUnitClass, BoxedBooleanClass) ) private val valueTypeEnc = mutable.Map[TypeName, PrimitiveClassEnc]() diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 84ef94687b13..11c694bed23b 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -476,9 +476,14 @@ object Denotations { val relaxedOverriding = ctx.explicitNulls && (sym1.is(JavaDefined) || sym2.is(JavaDefined)) val matchLoosely = sym1.matchNullaryLoosely || sym2.matchNullaryLoosely - if symScore <= 0 && info2.overrides(info1, relaxedOverriding, matchLoosely, checkClassInfo = false) then + val compareCtx = + if sym1.is(JavaDefined) && sym2.is(JavaDefined) then + ctx.withProperty(TypeComparer.ComparingJavaMethods, Some(())) + else ctx + + if symScore <= 0 && info2.overrides(info1, relaxedOverriding, matchLoosely, checkClassInfo = false) (using compareCtx) then denot2 - else if symScore >= 0 && info1.overrides(info2, relaxedOverriding, matchLoosely, checkClassInfo = false) then + else if symScore >= 0 && info1.overrides(info2, relaxedOverriding, matchLoosely, checkClassInfo = false)(using compareCtx) then denot1 else val jointInfo = infoMeet(info1, info2, safeIntersection) @@ -1299,9 +1304,9 @@ object Denotations { } /** The current denotation of the static reference given by path, - * or a MissingRef or NoQualifyingRef instance, if it does not exist. - * if generateStubs is set, generates stubs for missing top-level symbols - */ + * or a MissingRef or NoQualifyingRef instance, if it does not exist. + * if generateStubs is set, generates stubs for missing top-level symbols + */ def staticRef(path: Name, generateStubs: Boolean = true, isPackage: Boolean = false)(using Context): Denotation = { def select(prefix: Denotation, selector: Name): Denotation = { val owner = prefix.disambiguate(_.info.isParameterless) diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index 44ce31b27a73..d685b3253f59 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -33,6 +33,18 @@ class GadtConstraint private ( reverseMapping = reverseMapping.updated(tv.origin, sym), ) + def replace(param: TypeParamRef, tp: Type)(using Context) = + var constr = constraint + for + poly <- constraint.domainLambdas + paramRef <- poly.paramRefs + do + val entry0 = constr.entry(paramRef) + val entry1 = entry0.substParam(param, tp) + if entry1 ne entry0 then + constr = constr.updateEntry(paramRef, entry1) + withConstraint(constr) + /** Is `sym1` ordered to be less than `sym2`? */ def isLess(sym1: Symbol, sym2: Symbol)(using Context): Boolean = constraint.isLess(tvarOrError(sym1).origin, tvarOrError(sym2).origin) @@ -241,6 +253,9 @@ sealed trait GadtState { result } + def replace(param: TypeParamRef, tp: Type)(using Context) = + gadt = gadt.replace(param, tp) + /** See [[ConstraintHandling.approximation]] */ def approximation(sym: Symbol, fromBelow: Boolean, maxLevel: Int = Int.MaxValue)(using Context): Type = { approximation(gadt.tvarOrError(sym).origin, fromBelow, maxLevel).match diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index 40ef270ac813..f7e82e5c291d 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -179,7 +179,7 @@ object NameKinds { def apply(qual: TermName, num: Int): TermName = qual.derived(new NumberedInfo(num)) def unapply(name: DerivedName): Option[(TermName, Int)] = name match { - case DerivedName(underlying, info: this.NumberedInfo) => Some((underlying, info.num)) + case DerivedName(underlying, info: this.NumberedInfo) if info.kind.tag == this.tag => Some((underlying, info.num)) case _ => None } protected def skipSeparatorAndNum(name: SimpleName, separator: String): Int = { @@ -398,6 +398,7 @@ object NameKinds { val DirectMethName: SuffixNameKind = new SuffixNameKind(DIRECT, "$direct") val AdaptedClosureName: SuffixNameKind = new SuffixNameKind(ADAPTEDCLOSURE, "$adapted") { override def definesNewName = true } val SyntheticSetterName: SuffixNameKind = new SuffixNameKind(SETTER, "_$eq") + val LazyVarHandleName: SuffixNameKind = new SuffixNameKind(LAZYVALVARHANDLE, "$lzyHandle") /** A name together with a signature. Used in Tasty trees. */ object SignedName extends NameKind(SIGNED) { diff --git a/compiler/src/dotty/tools/dotc/core/NameTags.scala b/compiler/src/dotty/tools/dotc/core/NameTags.scala index 59dfaa3d437b..1e45ceac30cd 100644 --- a/compiler/src/dotty/tools/dotc/core/NameTags.scala +++ b/compiler/src/dotty/tools/dotc/core/NameTags.scala @@ -40,6 +40,8 @@ object NameTags extends TastyFormat.NameTags { inline val EXPLICITFIELD = 38 // An explicitly named field, introduce to avoid a clash // with a regular field of the underlying name + inline val LAZYVALVARHANDLE = 39 // A field containing a VarHandle generated for lazy vals + def nameTagToString(tag: Int): String = tag match { case UTF8 => "UTF8" case QUALIFIED => "QUALIFIED" diff --git a/compiler/src/dotty/tools/dotc/core/NamerOps.scala b/compiler/src/dotty/tools/dotc/core/NamerOps.scala index e4364d168267..eace0c288564 100644 --- a/compiler/src/dotty/tools/dotc/core/NamerOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NamerOps.scala @@ -9,6 +9,25 @@ import TypeApplications.EtaExpansion /** Operations that are shared between Namer and TreeUnpickler */ object NamerOps: + /** A completer supporting cleanup actions. + * Needed to break the loop between completion of class and companion object. + * If we try to complete the class first, and completion needs the companion + * object (for instance for processing an import) then the companion object + * completion would consult the companion class info for constructor that + * need a constructor proxy in the object. This can lead to a cyclic reference. + * We break the cycle by delaying adding constructor proxies to be a cleanuo + * action instead. + */ + trait CompleterWithCleanup extends LazyType: + private var cleanupActions: List[() => Unit] = Nil + def addCleanupAction(op: () => Unit): Unit = + cleanupActions = op :: cleanupActions + def cleanup(): Unit = + if cleanupActions.nonEmpty then + cleanupActions.reverse.foreach(_()) + cleanupActions = Nil + end CompleterWithCleanup + /** The type of the constructed instance is returned * * @param ctor the constructor @@ -110,11 +129,22 @@ object NamerOps: */ def addConstructorApplies(scope: MutableScope, cls: ClassSymbol, modcls: ClassSymbol)(using Context): scope.type = def proxy(constr: Symbol): Symbol = + var flags = ApplyProxyFlags | (constr.flagsUNSAFE & AccessFlags) + if cls.is(Protected) && !modcls.is(Protected) then flags |= Protected newSymbol( - modcls, nme.apply, ApplyProxyFlags | (constr.flagsUNSAFE & AccessFlags), - ApplyProxyCompleter(constr), coord = constr.coord) - for dcl <- cls.info.decls do + modcls, nme.apply, + flags, + ApplyProxyCompleter(constr), + cls.privateWithin, + constr.coord) + def doAdd() = for dcl <- cls.info.decls do if dcl.isConstructor then scope.enter(proxy(dcl)) + cls.infoOrCompleter match + case completer: CompleterWithCleanup if cls.is(Touched) => + // Taking the info would lead to a cyclic reference here - delay instead until cleanup of `cls` + completer.addCleanupAction(doAdd) + case _ => + doAdd() scope end addConstructorApplies @@ -133,10 +163,13 @@ object NamerOps: /** A new symbol that is the constructor companion for class `cls` */ def classConstructorCompanion(cls: ClassSymbol)(using Context): TermSymbol = + var flags = ConstructorCompanionFlags + if cls.is(Protected) then flags |= Protected val companion = newModuleSymbol( cls.owner, cls.name.toTermName, - ConstructorCompanionFlags, ConstructorCompanionFlags, + flags, flags, constructorCompanionCompleter(cls), + cls.privateWithin, coord = cls.coord, assocFile = cls.assocFile) companion.moduleClass.registerCompanion(cls) @@ -144,8 +177,9 @@ object NamerOps: companion def typeConstructorCompanion(tsym: Symbol, prefix: Type, proxy: Symbol)(using Context): TermSymbol = - newSymbol(tsym.owner, tsym.name.toTermName, - ConstructorCompanionFlags | StableRealizable | Method, ExprType(prefix.select(proxy)), coord = tsym.coord) + inline def core = ConstructorCompanionFlags | StableRealizable | Method + inline def flags = if tsym.is(Exported) then core | Exported else core + newSymbol(tsym.owner, tsym.name.toTermName, flags, ExprType(prefix.select(proxy)), coord = tsym.coord) /** Add all necesssary constructor proxy symbols for members of class `cls`. This means: * diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 3f9667b08067..873bf3b253c9 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -401,7 +401,7 @@ object Names { } /** It's OK to take a toString if the stacktrace does not contain a method - * from GenBCode or it also contains one of the whitelisted methods below. + * from GenBCode or it also contains one of the allowed methods below. */ private def toStringOK = { val trace: Array[StackTraceElement] = Thread.currentThread.nn.getStackTrace.asInstanceOf[Array[StackTraceElement]] @@ -506,7 +506,7 @@ object Names { } override def isEmpty: Boolean = false - override def encode: ThisName = underlying.encode.derived(info.map(_.encode)) + override def encode: ThisName = underlying.encode.derived(info.map(NameTransformer.encode)) // encodes override def decode: ThisName = underlying.decode.derived(info.map(_.decode)) override def firstPart: SimpleName = underlying.firstPart override def lastPart: SimpleName = info match { diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index c9b60ac051ad..78c717539763 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -263,9 +263,9 @@ class OrderingConstraint(private val boundsMap: ParamBounds, private var coDeps: ReverseDeps = SimpleIdentityMap.empty /** A map that associates type parameters of this constraint with all other type - * parameters that refer to them in their bounds covariantly, such that, if the + * parameters that refer to them in their bounds contravariantly, such that, if the * type parameter is instantiated to a smaller type, the constraint would be narrowed. - * (i.e. solution set changes other than simply being made larger). + * (i.e. solution set changes other than simply being made smaller). */ private var contraDeps: ReverseDeps = SimpleIdentityMap.empty @@ -368,7 +368,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, /** Adjust reverse dependencies of all type parameters referenced by `bound` * @param isLower `bound` is a lower bound - * @param add if true, add referenced variables to dependencoes, otherwise drop them. + * @param add if true, add referenced variables to dependencies, otherwise drop them. */ def adjustReferenced(bound: Type, isLower: Boolean, add: Boolean) = adjuster.variance = if isLower then 1 else -1 @@ -394,8 +394,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } case _ => false - /** Add or remove depenencies referenced in `bounds`. - * @param add if true, dependecies are added, otherwise they are removed + /** Add or remove dependencies referenced in `bounds`. + * @param add if true, dependencies are added, otherwise they are removed */ def adjustBounds(bounds: TypeBounds, add: Boolean) = adjustReferenced(bounds.lo, isLower = true, add) diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index 8d690a28a403..89f161ac690c 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -30,7 +30,7 @@ object Phases { @sharable object NoPhase extends Phase { override def exists: Boolean = false def phaseName: String = "" - def run(using Context): Unit = unsupported("run") + protected def run(using Context): Unit = unsupported("run") def transform(ref: SingleDenotation)(using Context): SingleDenotation = unsupported("transform") } @@ -42,13 +42,13 @@ object Phases { object SomePhase extends Phase { def phaseName: String = "" - def run(using Context): Unit = unsupported("run") + protected def run(using Context): Unit = unsupported("run") } /** A sentinel transformer object */ class TerminalPhase extends DenotTransformer { def phaseName: String = "terminal" - def run(using Context): Unit = unsupported("run") + protected def run(using Context): Unit = unsupported("run") def transform(ref: SingleDenotation)(using Context): SingleDenotation = unsupported("transform") override def lastPhaseId(using Context): Int = id @@ -227,6 +227,7 @@ object Phases { private var myErasurePhase: Phase = _ private var myElimErasedValueTypePhase: Phase = _ private var myLambdaLiftPhase: Phase = _ + private var myMixinPhase: Phase = _ private var myCountOuterAccessesPhase: Phase = _ private var myFlattenPhase: Phase = _ private var myGenBCodePhase: Phase = _ @@ -253,6 +254,7 @@ object Phases { final def gettersPhase: Phase = myGettersPhase final def erasurePhase: Phase = myErasurePhase final def elimErasedValueTypePhase: Phase = myElimErasedValueTypePhase + final def mixinPhase: Phase = myMixinPhase final def lambdaLiftPhase: Phase = myLambdaLiftPhase final def countOuterAccessesPhase = myCountOuterAccessesPhase final def flattenPhase: Phase = myFlattenPhase @@ -281,6 +283,7 @@ object Phases { myErasurePhase = phaseOfClass(classOf[Erasure]) myElimErasedValueTypePhase = phaseOfClass(classOf[ElimErasedValueType]) myPatmatPhase = phaseOfClass(classOf[PatternMatcher]) + myMixinPhase = phaseOfClass(classOf[Mixin]) myLambdaLiftPhase = phaseOfClass(classOf[LambdaLift]) myCountOuterAccessesPhase = phaseOfClass(classOf[CountOuterAccesses]) myFlattenPhase = phaseOfClass(classOf[Flatten]) @@ -291,6 +294,8 @@ object Phases { } final def isAfterTyper(phase: Phase): Boolean = phase.id > typerPhase.id + final def isAfterInlining(phase: Phase): Boolean = + inliningPhase != NoPhase && phase.id > inliningPhase.id final def isTyper(phase: Phase): Boolean = phase.id == typerPhase.id } @@ -300,7 +305,7 @@ object Phases { * instance, it is possible to print trees after a given phase using: * * ```bash - * $ ./bin/scalac -Xprint: sourceFile.scala + * $ ./bin/scalac -Vprint: sourceFile.scala * ``` */ def phaseName: String @@ -328,7 +333,7 @@ object Phases { final def traversals: Int = if subPhases.isEmpty then 1 else subPhases.length /** @pre `isRunnable` returns true */ - def run(using Context): Unit + protected def run(using Context): Unit /** @pre `isRunnable` returns true */ def runOn(units: List[CompilationUnit])(using runCtx: Context): List[CompilationUnit] = @@ -336,12 +341,14 @@ object Phases { for unit <- units do ctx.profiler.onUnit(this, unit): given unitCtx: Context = runCtx.fresh.setPhase(this.start).setCompilationUnit(unit).withRootImports + val previousTyperState = unitCtx.typerState.snapshot() if ctx.run.enterUnit(unit) then try run buf += unitCtx.compilationUnit catch case _: CompilationUnit.SuspendException => // this unit will be run again in `Run#compileSuspendedUnits` + unitCtx.typerState.resetTo(previousTyperState) case ex: Throwable if !ctx.run.enrichedErrorMessage => println(ctx.run.enrichErrorMessage(s"unhandled exception while running $phaseName on $unit")) throw ex @@ -462,15 +469,14 @@ object Phases { * Enrich crash messages. */ final def monitor(doing: String)(body: Context ?=> Unit)(using Context): Boolean = - val unit = ctx.compilationUnit - if ctx.run.enterUnit(unit) then + ctx.run.enterUnit(ctx.compilationUnit) + && { try {body; true} catch case NonFatal(ex) if !ctx.run.enrichedErrorMessage => - report.echo(ctx.run.enrichErrorMessage(s"exception occurred while $doing $unit")) + report.echo(ctx.run.enrichErrorMessage(s"exception occurred while $doing ${ctx.compilationUnit}")) throw ex finally ctx.run.advanceUnit() - else - false + } inline def runSubPhase[T](id: Run.SubPhase)(inline body: (Run.SubPhase, Context) ?=> T)(using Context): T = given Run.SubPhase = id @@ -507,6 +513,7 @@ object Phases { def gettersPhase(using Context): Phase = ctx.base.gettersPhase def erasurePhase(using Context): Phase = ctx.base.erasurePhase def elimErasedValueTypePhase(using Context): Phase = ctx.base.elimErasedValueTypePhase + def mixinPhase(using Context): Phase = ctx.base.mixinPhase def lambdaLiftPhase(using Context): Phase = ctx.base.lambdaLiftPhase def flattenPhase(using Context): Phase = ctx.base.flattenPhase def genBCodePhase(using Context): Phase = ctx.base.genBCodePhase diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index c26d8c320897..ee19c79359f1 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -466,7 +466,7 @@ object SymDenotations { /** The expanded name of this denotation. */ final def expandedName(using Context): Name = - if (name.is(ExpandedName) || isConstructor) name + if name.is(ExpandedName) || isConstructor then name else name.expandedName(initial.owner) // need to use initial owner to disambiguate, as multiple private symbols with the same name // might have been moved from different origins into the same class @@ -684,7 +684,7 @@ object SymDenotations { isAbstractOrAliasType && !isAbstractOrParamType /** Is this symbol an abstract or alias type? */ - final def isAbstractOrAliasType: Boolean = isType & !isClass + final def isAbstractOrAliasType: Boolean = isType && !isClass /** Is this symbol an abstract type or type parameter? */ final def isAbstractOrParamType(using Context): Boolean = this.isOneOf(DeferredOrTypeParam) @@ -902,15 +902,16 @@ object SymDenotations { } /** Is protected access to target symbol permitted? */ - def isProtectedAccessOK: Boolean = + def isProtectedAccessOK: Boolean = { val cls = owner.enclosingSubClass if !cls.exists then pre.termSymbol.isPackageObject && accessWithin(pre.termSymbol.owner) else + def isConstructorAccessOK = isConstructor && ctx.isSuperCallContext // allow accesses to types from arbitrary subclasses fixes #4737 // don't perform this check for static members - isType || pre.derivesFrom(cls) || isConstructor || owner.is(ModuleClass) - end isProtectedAccessOK + isType || pre.derivesFrom(cls) || isConstructorAccessOK || owner.is(ModuleClass) + } if pre eq NoPrefix then true else if isAbsent() then false @@ -1036,10 +1037,10 @@ object SymDenotations { */ def matchNullaryLoosely(using Context): Boolean = { def test(sym: Symbol) = - sym.is(JavaDefined) || - sym.owner == defn.AnyClass || - sym == defn.Object_clone || - sym.owner.is(Scala2x) + sym.is(JavaDefined) + || sym.owner == defn.AnyClass + || sym == defn.Object_clone + || sym.owner.is(Scala2x) this.exists && (test(symbol) || allOverriddenSymbols.exists(test)) } @@ -1185,7 +1186,7 @@ object SymDenotations { || is(Inline, butNot = Deferred) || is(JavaDefinedVal, butNot = Method) || isConstructor - || !owner.isExtensibleClass + || exists && !owner.isExtensibleClass /** A class is effectively sealed if has the `final` or `sealed` modifier, or it * is defined in Scala 3 and is neither abstract nor open. @@ -1218,6 +1219,15 @@ object SymDenotations { else if (this.exists) owner.enclosingMethod else NoSymbol + /** The closest enclosing method or static symbol containing this definition. + * A local dummy owner is mapped to the primary constructor of the class. + */ + final def enclosingMethodOrStatic(using Context): Symbol = + if this.is(Method) || this.hasAnnotation(defn.ScalaStaticAnnot) then symbol + else if this.isClass then primaryConstructor + else if this.exists then owner.enclosingMethodOrStatic + else NoSymbol + /** The closest enclosing extension method containing this definition, * including methods outside the current class. */ @@ -1388,7 +1398,7 @@ object SymDenotations { final def nextOverriddenSymbol(using Context): Symbol = { val overridden = allOverriddenSymbols if (overridden.hasNext) - overridden.next + overridden.next() else NoSymbol } @@ -1466,10 +1476,10 @@ object SymDenotations { val candidates = owner.info.decls.lookupAll(name) def test(sym: Symbol): Symbol = if (sym == symbol || sym.signature == signature) sym - else if (candidates.hasNext) test(candidates.next) + else if (candidates.hasNext) test(candidates.next()) else NoSymbol if (candidates.hasNext) { - val sym = candidates.next + val sym = candidates.next() if (candidates.hasNext) test(sym) else sym } else NoSymbol @@ -1923,7 +1933,7 @@ object SymDenotations { case _ => NoSymbol /** The explicitly given self type (self types of modules are assumed to be - * explcitly given here). + * explicitly given here). */ def givenSelfType(using Context): Type = classInfo.selfInfo match { case tp: Type => tp @@ -1936,7 +1946,6 @@ object SymDenotations { /** The this-type depends on the kind of class: * - for a package class `p`: ThisType(TypeRef(Noprefix, p)) - * - for a module class `m`: A term ref to m's source module. * - for all other classes `c` with owner `o`: ThisType(TypeRef(o.thisType, c)) */ override def thisType(using Context): Type = { @@ -2597,7 +2606,7 @@ object SymDenotations { for (sym <- scope.toList.iterator) // We need to be careful to not force the denotation of `sym` here, // otherwise it will be brought forward to the current run. - if (sym.defRunId != ctx.runId && sym.isClass && sym.asClass.assocFile == file) + if (sym.defRunId != ctx.runId && sym.isClass && sym.asClass.assocFile != null && sym.asClass.assocFile.nn.path == file.path) scope.unlink(sym, sym.lastKnownDenotation.name) } } @@ -2674,7 +2683,7 @@ object SymDenotations { || owner.isRefinementClass || owner.is(Scala2x) || owner.unforcedDecls.contains(denot.name, denot.symbol) - || (denot.is(Synthetic) && denot.is(ModuleClass) && stillValidInOwner(denot.companionClass)) + || (denot.is(Synthetic) && denot.is(ModuleClass) && denot.companionClass.exists && stillValidInOwner(denot.companionClass)) || denot.isSelfSym || denot.isLocalDummy) catch case ex: StaleSymbol => false diff --git a/compiler/src/dotty/tools/dotc/core/SymUtils.scala b/compiler/src/dotty/tools/dotc/core/SymUtils.scala index b6657615d505..06b18d08de30 100644 --- a/compiler/src/dotty/tools/dotc/core/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/SymUtils.scala @@ -42,8 +42,8 @@ class SymUtils: } /** All traits implemented by a class, except for those inherited through the superclass. - * The empty list if `self` is a trait. - */ + * The empty list if `self` is a trait. + */ def mixins(using Context): List[ClassSymbol] = if (self.is(Trait)) Nil else directlyInheritedTraits diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index a0b3460d06e4..d98a8f6b546c 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -7,6 +7,7 @@ import java.nio.channels.ClosedByInterruptException import scala.util.control.NonFatal +import dotty.tools.dotc.classpath.PackageNameUtils import dotty.tools.io.{ ClassPath, ClassRepresentation, AbstractFile, NoAbstractFile } import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions @@ -194,7 +195,9 @@ object SymbolLoaders { /** Initialize toplevel class and module symbols in `owner` from class path representation `classRep` */ def initializeFromClassPath(owner: Symbol, classRep: ClassRepresentation)(using Context): Unit = - ((classRep.binary, classRep.source): @unchecked) match { + // module-info is a special class added by JPMS (Java 9+) that cannot be parsed like a normal class + if classRep.name != "module-info" then + ((classRep.binary, classRep.source): @unchecked) match { case (Some(bin), Some(src)) if needCompile(bin, src) && !binaryOnly(owner, nameOf(classRep)) => if (ctx.settings.verbose.value) report.inform("[symloader] picked up newer source file for " + src.path) enterToplevelsFromSource(owner, nameOf(classRep), src) @@ -265,7 +268,7 @@ object SymbolLoaders { def maybeModuleClass(classRep: ClassRepresentation): Boolean = classRep.name.last == '$' - private def enterClasses(root: SymDenotation, packageName: String, flat: Boolean)(using Context) = { + def enterClasses(root: SymDenotation, packageName: String, flat: Boolean)(using Context) = { def isAbsent(classRep: ClassRepresentation) = !root.unforcedDecls.lookup(classRep.name.toTypeName).exists @@ -304,11 +307,47 @@ object SymbolLoaders { if (packageName.isEmpty) fullName else fullName.substring(packageName.length + 1).nn - enterPackage(root.symbol, name.toTermName, - (module, modcls) => new PackageLoader(module, classPath)) + // If the directory name conflicts with an existing class or object, + // verify it actually contains class/tasty files or sub-packages before + // treating it as a package. Spurious directories (e.g., created by a + // compiler plugin writing non-class files) should not shadow existing + // definitions. See #23043. + val hasConflict = currentDecls.lookup(name.toTermName) != NoSymbol + if !hasConflict + || classPath.list(fullName).classesAndSources.nonEmpty + || classPath.packages(fullName).nonEmpty + then + enterPackage(root.symbol, name.toTermName, + (module, modcls) => new PackageLoader(module, classPath)) } } } + + def mergeNewEntries( + packageClass: ClassSymbol, fullPackageName: String, + jarClasspath: ClassPath, fullClasspath: ClassPath, + )(using Context): Unit = + if jarClasspath.classes(fullPackageName).nonEmpty then + // if the package contains classes in jarClasspath, the package is invalidated (or removed if there are no more classes in it) + val packageVal = packageClass.sourceModule.asInstanceOf[TermSymbol] + if packageClass.isRoot then + val loader = new PackageLoader(packageVal, fullClasspath) + loader.enterClasses(defn.EmptyPackageClass, fullPackageName, flat = false) + loader.enterClasses(defn.EmptyPackageClass, fullPackageName, flat = true) + else if packageClass.ownersIterator.contains(defn.ScalaPackageClass) then + () // skip + else if fullClasspath.hasPackage(fullPackageName) then + packageClass.info = new PackageLoader(packageVal, fullClasspath) + else + packageClass.owner.info.decls.openForMutations.unlink(packageVal) + else + for p <- jarClasspath.packages(fullPackageName) do + val subPackageName = PackageNameUtils.separatePkgAndClassNames(p.name)._2.toTermName + val subPackage = packageClass.info.decl(subPackageName).orElse: + // package does not exist in symbol table, create a new symbol + enterPackage(packageClass, subPackageName, (module, modcls) => new PackageLoader(module, fullClasspath)) + mergeNewEntries(subPackage.asSymDenotation.moduleClass.asClass, p.name, jarClasspath, fullClasspath) + end mergeNewEntries } /** A lazy type that completes itself by calling parameter doComplete. diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 893392794a51..e2cc098398e9 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -323,13 +323,25 @@ object Symbols extends SymUtils { targets.match case (tp: NamedType) :: _ => tp.symbol.sourceSymbol case _ => this - else if (denot.is(Synthetic)) { + else if denot.is(Synthetic) then val linked = denot.linkedClass if (linked.exists && !linked.is(Synthetic)) linked else denot.owner.sourceSymbol - } + else if ( + denot.is(TypeParam) && + denot.maybeOwner.maybeOwner.isAllOf(EnumCase) && + denot.maybeOwner.isPrimaryConstructor + ) then + val enclosingEnumCase = denot.maybeOwner.maybeOwner + val caseTypeParam = enclosingEnumCase.typeParams.find(_.name == denot.name) + if caseTypeParam.exists(_.is(Synthetic)) then + val enumClass = enclosingEnumCase.info.firstParent.typeSymbol + val sourceTypeParam = enumClass.typeParams.find(_.name == denot.name) + sourceTypeParam.getOrElse(this) + else + caseTypeParam.getOrElse(this) else if (denot.isPrimaryConstructor) denot.owner.sourceSymbol else this diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index ab216bfd147d..64b9a1f082ba 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -310,6 +310,34 @@ class TypeApplications(val self: Type) extends AnyVal { def ensureLambdaSub(using Context): Type = if (isLambdaSub) self else EtaExpansion(self) + + /** Convert a type constructor `TC` which has type parameters `X1, ..., Xn` + * to `[X1, ..., Xn] -> TC[X1, ..., Xn]`. + */ + def etaExpand(using Context): Type = + val tparams = self.typeParams + val resType = self.appliedTo(tparams.map(_.paramRef)) + self.dealias match + case self: TypeRef if tparams.nonEmpty && self.symbol.isClass => + val owner = self.symbol.owner + // Calling asSeenFrom on the type parameter infos is important + // so that class type references within another prefix have + // their type parameters' info fixed. + // e.g. from pos/i18569: + // trait M1: + // trait A + // trait F[T <: A] + // object M2 extends M1 + // Type parameter T in M1.F has an upper bound of M1#A + // But eta-expanding M2.F should have type parameters with an upper-bound of M2.A. + // So we take the prefix M2.type and the F symbol's owner, M1, + // to call asSeenFrom on T's info. + HKTypeLambda(tparams.map(_.paramName))( + tl => tparams.map(p => HKTypeLambda.toPInfo(tl.integrate(tparams, p.paramInfo.asSeenFrom(self.prefix, owner)))), + tl => tl.integrate(tparams, resType)) + case _ => + HKTypeLambda.fromParams(tparams, resType) + /** Eta expand if `self` is a (non-lambda) class reference and `bound` is a higher-kinded type */ def EtaExpandIfHK(bound: Type)(using Context): Type = { val hkParams = bound.hkTypeParams diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index c69ea177ee96..b8b786b75721 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -390,7 +390,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling compareErasedValueType case ConstantType(v2) => tp1 match { - case ConstantType(v1) => v1.value == v2.value && recur(v1.tpe, v2.tpe) + case ConstantType(v1) => v1 == v2 && recur(v1.tpe, v2.tpe) case _ => secondTry } case tp2: AnyConstantType => @@ -479,7 +479,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling def widenOK = (tp2.widenSingletons eq tp2) && (tp1.widenSingletons ne tp1) - && inFrozenGadtAndConstraint(recur(tp1.widenSingletons, tp2)) + && inFrozenGadtAndConstraint(recur(tp1.widenSingletons(), tp2)) def joinOK = tp2.dealiasKeepRefiningAnnots match { case tp2: AppliedType if !tp2.tycon.typeSymbol.isClass => @@ -917,7 +917,20 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling (sym1 eq NullClass) && isNullable(tp2) } case tp1 @ AppliedType(tycon1, args1) => - compareAppliedType1(tp1, tycon1, args1) + // Special case: Java arrays are covariant. + // When checking overrides (frozenConstraint) of Java methods, allow B[] <: A[] if B <: A. + def checkJavaArrayCovariance: Boolean = tp2 match { + case AppliedType(tycon2, arg2 :: Nil) + if frozenConstraint + && tycon1.typeSymbol == defn.ArrayClass + && tycon2.typeSymbol == defn.ArrayClass + && args1.length == 1 => + // Arrays are covariant in Java: B[] <: A[] if B <: A + isSubType(args1.head, arg2) + case _ => false + } + (checkJavaArrayCovariance && ctx.property(ComparingJavaMethods).isDefined) + || compareAppliedType1(tp1, tycon1, args1) case tp1: SingletonType => def comparePaths = tp2 match case tp2: (TermRef | ThisType) => @@ -2772,11 +2785,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling * * 1. Single inheritance of classes * 2. Final classes cannot be extended - * 3. ConstantTypes with distinct values are non intersecting - * 4. TermRefs with distinct values are non intersecting + * 3. ConstantTypes with distinct values are non-intersecting + * 4. TermRefs with distinct values are non-intersecting * 5. There is no value of type Nothing * - * Note on soundness: the correctness of match types relies on on the + * Note on soundness: the correctness of match types relies on the * property that in all possible contexts, the same match type expression * is either stuck or reduces to the same case. */ @@ -2964,6 +2977,14 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling object TypeComparer { + import util.Property + + /** A property key to indicate we're comparing Java-defined methods. + * When it is set, arrays are treated as covariant for override checking. + */ + val ComparingJavaMethods = new Property.Key[Unit] + + /** A richer compare result, returned by `testSubType` and `test`. */ enum CompareResult: case OK, Fail, OKwithGADTUsed diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 17ddc93a10f2..df3aef071551 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -5,7 +5,6 @@ package core import Symbols.*, Types.*, Contexts.*, Flags.*, Names.*, StdNames.*, Phases.* import Flags.JavaDefined import Uniques.unique -import TypeOps.makePackageObjPrefixExplicit import backend.sjs.JSDefinitions import transform.ExplicitOuter.* import transform.ValueClasses.* @@ -73,7 +72,7 @@ end SourceLanguage * only for isInstanceOf, asInstanceOf: PolyType, TypeParamRef, TypeBounds * */ -object TypeErasure { +object TypeErasure: private def erasureDependsOnArgs(sym: Symbol)(using Context) = sym == defn.ArrayClass || sym == defn.PairClass || sym.isDerivedValueClass @@ -327,7 +326,7 @@ object TypeErasure { val sym = t.symbol // Only a few classes have both primitives and references as subclasses. if (sym eq defn.AnyClass) || (sym eq defn.AnyValClass) || (sym eq defn.MatchableClass) || (sym eq defn.SingletonClass) - || isScala2 && !(t.derivesFrom(defn.ObjectClass) || t.isNullType) then + || isScala2 && !(t.derivesFrom(defn.ObjectClass) || t.isNullType | t.isNothingType) then NoSymbol // We only need to check for primitives because derived value classes in arrays are always boxed. else if sym.isPrimitiveValueClass then @@ -365,6 +364,8 @@ object TypeErasure { case tp: MatchType => val alts = tp.alternatives alts.nonEmpty && !fitsInJVMArray(alts.reduce(OrType(_, _, soft = true))) + case tp @ AppliedType(tycon, _) if tycon.isLambdaSub => + !fitsInJVMArray(tp.translucentSuperType) case tp: TypeProxy => isGenericArrayElement(tp.translucentSuperType, isScala2) case tp: AndType => @@ -385,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 @@ -572,7 +579,102 @@ object TypeErasure { defn.FunctionType(n = info.erasedParams.count(_ == false)) } erasure(functionType(applyInfo)) -} + + /** Check if LambdaMetaFactory can handle signature adaptation between two method types. + * + * LMF has limitations on what type adaptations it can perform automatically. + * This method checks whether manual bridging is needed for params and/or result. + * + * The adaptation rules are: + * - For parameters: primitives and value classes cannot be auto-adapted by LMF + * because the Scala spec requires null to be "unboxed" to the default value, + * but LMF throws `NullPointerException` instead. + * - For results: value classes and Unit cannot be auto-adapted by LMF. + * Non-Unit primitives can be auto-adapted since LMF only needs to box (not unbox). + * - LMF cannot auto-adapt between Object and Array types. + * + * @param implParamTypes Parameter types of the implementation method + * @param implResultType Result type of the implementation method + * @param samParamTypes Parameter types of the SAM method + * @param samResultType Result type of the SAM method + * + * @return (paramNeeded, resultNeeded) indicating what needs bridging + */ + def additionalAdaptationNeeded( + implParamTypes: List[Type], + implResultType: Type, + samParamTypes: List[Type], + samResultType: Type + )(using Context): (Boolean, Boolean) = + def sameClass(tp1: Type, tp2: Type) = tp1.classSymbol == tp2.classSymbol + + /** Can the implementation parameter type `tp` be auto-adapted to a different + * parameter type in the SAM? + * + * For derived value classes, we always need to do the bridging manually. + * For primitives, we cannot rely on auto-adaptation on the JVM because + * the Scala spec requires null to be "unboxed" to the default value of + * the value class, but the adaptation performed by LambdaMetaFactory + * will throw a `NullPointerException` instead. + */ + def autoAdaptedParam(tp: Type) = !tp.isErasedValueType && !tp.isPrimitiveValueType + + /** Can the implementation result type be auto-adapted to a different result + * type in the SAM? + * + * For derived value classes, it's the same story as for parameters. + * For non-Unit primitives, we can actually rely on the `LambdaMetaFactory` + * adaptation, because it only needs to box, not unbox, so no special + * handling of null is required. + */ + def autoAdaptedResult(tp: Type) = + !tp.isErasedValueType && !(tp.classSymbol eq defn.UnitClass) + + val paramAdaptationNeeded = + implParamTypes.lazyZip(samParamTypes).exists((implType, samType) => + !sameClass(implType, samType) && (!autoAdaptedParam(implType) + // LambdaMetaFactory cannot auto-adapt between Object and Array types + || samType.isInstanceOf[JavaArrayType])) + + val resultAdaptationNeeded = + !sameClass(implResultType, samResultType) && !autoAdaptedResult(implResultType) + + (paramAdaptationNeeded, resultAdaptationNeeded) + end additionalAdaptationNeeded + + /** Check if LambdaMetaFactory can handle the SAM method's required signature adaptation. + * + * When a SAM method overrides other methods, the erased signatures must be compatible + * to be qualifies as a valid functional interface on JVM. + * This method returns true if all overridden methods have compatible erased signatures + * that LMF can auto-adapt (or don't need adaptation). + * + * When this returns true, the SAM class does not need to be expanded. + * + * @param cls The SAM class to check + * @return true if LMF can handle the required adaptation + */ + def samExpansionNotNeeded(cls: ClassSymbol)(using Context): Boolean = cls.typeRef.possibleSamMethods match + case Seq(samMeth) => + val samMethSym = samMeth.symbol + val erasedSamInfo = transformInfo(samMethSym, samMeth.info) + + val (erasedSamParamTypes, erasedSamResultType) = erasedSamInfo match + case mt: MethodType => (mt.paramInfos, mt.resultType) + case _ => return false + + samMethSym.allOverriddenSymbols.forall { overridden => + val erasedOverriddenInfo = transformInfo(overridden, overridden.info) + erasedOverriddenInfo match + case mt: MethodType => + val (paramNeeded, resultNeeded) = + additionalAdaptationNeeded(erasedSamParamTypes, erasedSamResultType, mt.paramInfos, mt.resultType) + !(paramNeeded || resultNeeded) + case _ => true + } + case _ => false + end samExpansionNotNeeded +end TypeErasure import TypeErasure.* @@ -597,8 +699,8 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst * will be returned. * * In all other situations, |T| will be computed as follow: - * - For a refined type scala.Array+[T]: - * - if T is Nothing or Null, []Object + * - For a refined type scala.Array[T]: + * - {Scala 2} if T is Nothing or Null, []Object * - otherwise, if T <: Object, []|T| * - otherwise, if T is a type parameter coming from Java, []Object * - otherwise, Object @@ -779,12 +881,14 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst private def eraseArray(tp: Type)(using Context) = { val defn.ArrayOf(elemtp) = tp: @unchecked - if (isGenericArrayElement(elemtp, isScala2 = sourceLanguage.isScala2)) defn.ObjectType + if isGenericArrayElement(elemtp, isScala2 = sourceLanguage.isScala2) then + defn.ObjectType + else if sourceLanguage.isScala2 && (elemtp.hiBound.isNullType || elemtp.hiBound.isNothingType) then + JavaArrayType(defn.ObjectType) else - try - val eElem = erasureFn(sourceLanguage, semiEraseVCs = false, isConstructor, isSymbol, inSigName)(elemtp) - if eElem.isInstanceOf[WildcardType] then WildcardType - else JavaArrayType(eElem) + try erasureFn(sourceLanguage, semiEraseVCs = false, isConstructor, isSymbol, inSigName)(elemtp) match + case _: WildcardType => WildcardType + case elem => JavaArrayType(elem) catch case ex: Throwable => handleRecursive("erase array type", tp.show, ex) } @@ -813,11 +917,15 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst // forwarders to mixin methods. // See doc comment for ElimByName for speculation how we could improve this. else - MethodType(Nil, Nil, - eraseResult(rt.translateFromRepeated(toArray = sourceLanguage.isJava))) + val erasedRT = eraseResult(rt.translateFromRepeated(toArray = sourceLanguage.isJava)) + if !erasedRT.exists then NoType + else MethodType(Nil, Nil, erasedRT) case tp1: PolyType => eraseResult(tp1.resultType) match case rt: MethodType => rt + case rt if !rt.exists => + // Same situation as MethodType + eraseResult => NoType in `apply` (see i15377). + NoType case rt => MethodType(Nil, Nil, rt) case tp1 => this(tp1) @@ -896,7 +1004,6 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst tp match { case tp: TypeRef => if (!tp.denot.exists) - // println(i"missing: ${tp.toString} ${tp.denot} / ${tp.prefix.member(tp.name)}") throw new MissingType(tp.prefix, tp.name) val sym = tp.symbol if (!sym.isClass) { diff --git a/compiler/src/dotty/tools/dotc/core/TypeEval.scala b/compiler/src/dotty/tools/dotc/core/TypeEval.scala index b5684b07f181..6ea07df7b775 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeEval.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeEval.scala @@ -2,6 +2,8 @@ package dotty.tools package dotc package core +import scala.reflect.Typeable + import Types.*, Contexts.*, Symbols.*, Constants.*, Decorators.* import config.Printers.typr import reporting.trace @@ -23,33 +25,22 @@ object TypeEval: if tp1.isStable then tp1.fixForEvaluation else tp case tp => tp - def constValue(tp: Type): Option[Any] = tp.fixForEvaluation match - case ConstantType(Constant(n)) => Some(n) - case _ => None + extension (tp: Type) def constant[T: Typeable]: Option[T] = + TypeComparer.constValue(tp.fixForEvaluation).collect { case Constant(c: T) => c } + + def constValue(tp: Type): Option[Any] = tp.constant[Any] - def boolValue(tp: Type): Option[Boolean] = tp.fixForEvaluation match - case ConstantType(Constant(n: Boolean)) => Some(n) - case _ => None + def boolValue(tp: Type): Option[Boolean] = tp.constant[Boolean] - def intValue(tp: Type): Option[Int] = tp.fixForEvaluation match - case ConstantType(Constant(n: Int)) => Some(n) - case _ => None + def intValue(tp: Type): Option[Int] = tp.constant[Int] - def longValue(tp: Type): Option[Long] = tp.fixForEvaluation match - case ConstantType(Constant(n: Long)) => Some(n) - case _ => None + def longValue(tp: Type): Option[Long] = tp.constant[Long] - def floatValue(tp: Type): Option[Float] = tp.fixForEvaluation match - case ConstantType(Constant(n: Float)) => Some(n) - case _ => None + def floatValue(tp: Type): Option[Float] = tp.constant[Float] - def doubleValue(tp: Type): Option[Double] = tp.fixForEvaluation match - case ConstantType(Constant(n: Double)) => Some(n) - case _ => None + def doubleValue(tp: Type): Option[Double] = tp.constant[Double] - def stringValue(tp: Type): Option[String] = tp.fixForEvaluation match - case ConstantType(Constant(n: String)) => Some(n) - case _ => None + def stringValue(tp: Type): Option[String] = tp.constant[String] // Returns Some(true) if the type is a constant. // Returns Some(false) if the type is not a constant. diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 8f87eddb6cef..c1812c97ee07 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -123,6 +123,8 @@ object TypeOps: } def isLegalPrefix(pre: Type)(using Context): Boolean = + // isLegalPrefix is relaxed after typer unless we're doing an implicit + // search (this matters when doing summonInline in an inline def like in tests/pos/i17222.8.scala). pre.isStable || !ctx.phase.isTyper /** Implementation of Types#simplified */ @@ -284,7 +286,15 @@ object TypeOps: } case AndType(tp11, tp12) => mergeRefinedOrApplied(tp11, tp2) & mergeRefinedOrApplied(tp12, tp2) - case tp1: TypeParamRef if tp1 == tp2 => tp1 + case tp1: TypeParamRef => + tp2.stripTypeVar match + case tp2: TypeParamRef if tp1 == tp2 => tp1 + case _ => fail + case tp1: TypeVar => + tp2 match + case tp2: TypeVar if tp1 == tp2 => tp1 + case tp2: TypeParamRef if tp1.stripTypeVar == tp2 => tp2 + case _ => fail case _ => fail } } @@ -465,7 +475,7 @@ object TypeOps: try tp match case tp: TermRef if toAvoid(tp) => - tp.info.widenExpr.dealias match { + tp.info.widenExpr.dealiasKeepRefiningAnnots match { case info: SingletonType => apply(info) case info => range(defn.NothingType, apply(info)) } @@ -588,36 +598,6 @@ object TypeOps: widenMap(tp) } - /** If `tpe` is of the form `p.x` where `p` refers to a package - * but `x` is not owned by a package, expand it to - * - * p.package.x - */ - def makePackageObjPrefixExplicit(tpe: NamedType)(using Context): Type = { - def tryInsert(pkgClass: SymDenotation): Type = pkgClass match { - case pkg: PackageClassDenotation => - var sym = tpe.symbol - if !sym.exists && tpe.denot.isOverloaded then - // we know that all alternatives must come from the same package object, since - // otherwise we would get "is already defined" errors. So we can take the first - // symbol we see. - sym = tpe.denot.alternatives.head.symbol - val pobj = pkg.packageObjFor(sym) - if (pobj.exists) tpe.derivedSelect(pobj.termRef) - else tpe - case _ => - tpe - } - if (tpe.symbol.isRoot) - tpe - else - tpe.prefix match { - case pre: ThisType if pre.cls.is(Package) => tryInsert(pre.cls) - case pre: TermRef if pre.symbol.is(Package) => tryInsert(pre.symbol.moduleClass) - case _ => tpe - } - } - /** An argument bounds violation is a triple consisting of * - the argument tree * - a string "upper" or "lower" indicating which bound is violated diff --git a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala index 41d6bf1f4fae..ada318285a4b 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeUtils.scala @@ -3,7 +3,10 @@ package dotc package core import TypeErasure.ErasedValueType -import Types.*, Contexts.*, Symbols.*, Flags.*, Decorators.* +import Types.*, Contexts.*, Symbols.*, Flags.*, Decorators.*, SymDenotations.* +import Names.{Name, TermName} +import Constants.Constant + import Names.Name import StdNames.nme @@ -60,7 +63,7 @@ class TypeUtils: def tupleElementTypesUpTo(bound: Int, normalize: Boolean = true)(using Context): Option[List[Type]] = def recur(tp: Type, bound: Int): Option[List[Type]] = if bound < 0 then Some(Nil) - else (if normalize then tp.normalized else tp).dealias match + else (if normalize then tp.dealias.normalized else tp).dealias match case AppliedType(tycon, hd :: tl :: Nil) if tycon.isRef(defn.PairClass) => recur(tl, bound - 1).map(hd :: _) case tp: AppliedType if defn.isTupleNType(tp) && normalize => @@ -115,6 +118,16 @@ class TypeUtils: case Some(types) => TypeOps.nestedPairs(types) case None => throw new AssertionError("not a tuple") + /** If this is a generic tuple type with arity <= MaxTupleArity, return the + * corresponding TupleN type, otherwise return this. + */ + def normalizedTupleType(using Context): Type = + if self.isGenericTuple then + self.tupleElementTypes match + case Some(elems) if elems.size <= Definitions.MaxTupleArity => defn.tupleType(elems) + case _ => self + else + self def refinedWith(name: Name, info: Type)(using Context) = RefinedType(self, name, info) /** Is this type a methodic type that takes at least one parameter? */ @@ -127,6 +140,47 @@ class TypeUtils: case mt: MethodType => mt.isImplicitMethod || mt.resType.takesImplicitParams case _ => false + /** Is this a type deriving only from transparent classes? + * @param traitOnly if true, all class symbols must be transparent traits + */ + def isTransparent(traitOnly: Boolean = false)(using Context): Boolean = self match + case AndType(tp1, tp2) => + tp1.isTransparent(traitOnly) && tp2.isTransparent(traitOnly) + case _ => + val cls = self.underlyingClassRef(refinementOK = false).typeSymbol + cls.isTransparentClass && (!traitOnly || cls.is(Trait)) + + + /** If `self` is of the form `p.x` where `p` refers to a package + * but `x` is not owned by a package, expand it to + * + * p.package.x + */ + def makePackageObjPrefixExplicit(using Context): Type = + def tryInsert(tpe: NamedType, pkgClass: SymDenotation): Type = pkgClass match + case pkg: PackageClassDenotation => + var sym = tpe.symbol + if !sym.exists && tpe.denot.isOverloaded then + // we know that all alternatives must come from the same package object, since + // otherwise we would get "is already defined" errors. So we can take the first + // symbol we see. + sym = tpe.denot.alternatives.head.symbol + val pobj = pkg.packageObjFor(sym) + if pobj.exists then tpe.derivedSelect(pobj.termRef) + else tpe + case _ => + tpe + self match + case tpe: NamedType => + if tpe.symbol.isRoot then + tpe + else + tpe.prefix match + case pre: ThisType if pre.cls.is(Package) => tryInsert(tpe, pre.cls) + case pre: TermRef if pre.symbol.is(Package) => tryInsert(tpe, pre.symbol.moduleClass) + case _ => tpe + case tpe => tpe + /** The constructors of this type that are applicable to `argTypes`, without needing * an implicit conversion. Curried constructors are always excluded. * @param adaptVarargs if true, allow a constructor with just a varargs argument to diff --git a/compiler/src/dotty/tools/dotc/core/TyperState.scala b/compiler/src/dotty/tools/dotc/core/TyperState.scala index 046cfe3e935c..65c04af9a610 100644 --- a/compiler/src/dotty/tools/dotc/core/TyperState.scala +++ b/compiler/src/dotty/tools/dotc/core/TyperState.scala @@ -27,6 +27,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) @@ -42,7 +44,7 @@ object TyperState { } class TyperState() { - import TyperState.LevelMap + import TyperState.{BadTyperStateAssertion, LevelMap} private var myId: Int = _ def id: Int = myId @@ -268,8 +270,9 @@ 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 6cfe58bae2ea..3d5197386913 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -243,6 +243,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 } @@ -250,6 +254,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 } @@ -338,6 +346,23 @@ object Types extends TypeUtils { case AndType(tpL, tpR) => tpL.isEffectivelySingleton || tpR.isEffectivelySingleton case _ => false + /** Is this upper-bounded by a (possibly aliased) singleton type? + * Overridden in TypeVar + */ + def isSingletonBounded(frozen: Boolean)(using Context): Boolean = this.dealias.normalized match + case tp: SingletonType => tp.isStable + case tp: TypeParamRef => + ctx.typerState.constraint.bounds(tp).hi.isSingletonBounded(frozen) + case tp: TypeRef => + tp.name == tpnme.Singleton && tp.symbol == defn.SingletonClass + || tp.superType.isSingletonBounded(frozen) + case tp: TypeVar if !tp.isInstantiated => + if frozen then tp frozen_<:< defn.SingletonType else tp <:< defn.SingletonType + case tp: HKTypeLambda => false + case tp: TypeProxy => tp.superType.isSingletonBounded(frozen) + case tp: AndType => tp.tp1.isSingletonBounded(frozen) || tp.tp2.isSingletonBounded(frozen) + case _ => false + /** Is this type of kind `AnyKind`? */ def hasAnyKind(using Context): Boolean = { @tailrec def loop(tp: Type): Boolean = tp match { @@ -392,10 +417,9 @@ object Types extends TypeUtils { case _ => false /** Does the type carry an annotation that is an instance of `cls`? */ - @tailrec final def hasAnnotation(cls: ClassSymbol)(using Context): Boolean = stripTypeVar match { - case AnnotatedType(tp, annot) => (annot matches cls) || (tp hasAnnotation cls) + @tailrec final def hasAnnotation(cls: ClassSymbol)(using Context): Boolean = stripTypeVar match + case AnnotatedType(tp, annot) => annot.matches(cls) || tp.hasAnnotation(cls) case _ => false - } /** Does this type have a supertype with an annotation satisfying given predicate `p`? */ def derivesAnnotWith(p: Annotation => Boolean)(using Context): Boolean = this match { @@ -556,8 +580,8 @@ object Types extends TypeUtils { case AndType(l, r) => val lsym = l.classSymbol val rsym = r.classSymbol - if (lsym isSubClass rsym) lsym - else if (rsym isSubClass lsym) rsym + if lsym.isSubClass(rsym) then lsym + else if rsym.isSubClass(lsym) then rsym else NoSymbol case tp: OrType => if tp.tp1.hasClassSymbol(defn.NothingClass) then @@ -696,7 +720,7 @@ object Types extends TypeUtils { case tp: TypeProxy => tp.superType.findDecl(name, excluded) case err: ErrorType => - newErrorSymbol(classSymbol orElse defn.RootClass, name, err.msg) + newErrorSymbol(classSymbol.orElse(defn.RootClass), name, err.msg) case _ => NoDenotation } @@ -776,8 +800,10 @@ object Types extends TypeUtils { goOr(tp) case tp: JavaArrayType => defn.ObjectType.findMember(name, pre, required, excluded) + case tp: WildcardType => + go(tp.bounds) case err: ErrorType => - newErrorSymbol(pre.classSymbol orElse defn.RootClass, name, err.msg) + newErrorSymbol(pre.classSymbol.orElse(defn.RootClass), name, err.msg) case _ => NoDenotation } @@ -872,7 +898,7 @@ object Types extends TypeUtils { // member in Super instead of Sub. // As an example of this in the wild, see // loadClassWithPrivateInnerAndSubSelf in ShowClassTests - go(tp.cls.typeRef) orElse d + go(tp.cls.typeRef).orElse(d) def goParam(tp: TypeParamRef) = { val next = tp.underlying @@ -966,6 +992,23 @@ object Types extends TypeUtils { buf.toList } + /** For use in quotes reflect. + * A bit slower than the usual approach due to the use of LinkedHashSet. + **/ + def sortedParents(using Context): mutable.LinkedHashSet[Type] = this match + case tp: ClassInfo => + mutable.LinkedHashSet(tp) | mutable.LinkedHashSet(tp.declaredParents.flatMap(_.sortedParents.toList)*) + case tp: RefinedType => + tp.parent.sortedParents + case tp: TypeProxy => + tp.superType.sortedParents + case tp: AndType => + tp.tp1.sortedParents | tp.tp2.sortedParents + case tp: OrType => + tp.tp1.sortedParents & tp.tp2.sortedParents + case _ => + mutable.LinkedHashSet() + /** The set of abstract term members of this type. */ final def abstractTermMembers(using Context): Seq[SingleDenotation] = { record("abstractTermMembers") @@ -1110,7 +1153,7 @@ object Types extends TypeUtils { false def relaxed_<:<(that: Type)(using Context): Boolean = - (this <:< that) || (this isValueSubType that) + (this <:< that) || this.isValueSubType(that) /** Is this type a legal type for member `sym1` that overrides another * member `sym2` of type `that`? This is the same as `<:<`, except that @@ -1164,10 +1207,10 @@ object Types extends TypeUtils { * vice versa. */ def matchesLoosely(that: Type)(using Context): Boolean = - (this matches that) || { + this.matches(that) || { val thisResult = this.widenExpr val thatResult = that.widenExpr - (this eq thisResult) != (that eq thatResult) && (thisResult matchesLoosely thatResult) + (this eq thisResult) != (that eq thatResult) && thisResult.matchesLoosely(thatResult) } /** The basetype of this type with given class symbol, NoType if `base` is not a class. */ @@ -1361,19 +1404,19 @@ object Types extends TypeUtils { case tp => tp - /** Widen all top-level singletons reachable by dealiasing - * and going to the operands of & and |. - * Overridden and cached in OrType. + /** Widen all top-level singletons reachable by dealiasing and going to the + * operands of intersections and soft unions (only when `skipSoftUnions` is + * `false`). Overridden and cached in [[OrType]]. */ - def widenSingletons(using Context): Type = dealias match { + def widenSingletons(skipSoftUnions: Boolean = false)(using Context): Type = dealias match { case tp: SingletonType => tp.widen case tp: OrType => - val tp1w = tp.widenSingletons + val tp1w = tp.widenSingletons(skipSoftUnions) if (tp1w eq tp) this else tp1w case tp: AndType => - val tp1w = tp.tp1.widenSingletons - val tp2w = tp.tp2.widenSingletons + val tp1w = tp.tp1.widenSingletons(skipSoftUnions) + val tp2w = tp.tp2.widenSingletons(skipSoftUnions) if ((tp.tp1 eq tp1w) && (tp.tp2 eq tp2w)) this else tp1w & tp2w case _ => this @@ -1798,7 +1841,7 @@ object Types extends TypeUtils { * no symbol it tries `member` as an alternative. */ def typeParamNamed(name: TypeName)(using Context): Symbol = - classSymbol.unforcedDecls.lookup(name) orElse member(name).symbol + classSymbol.unforcedDecls.lookup(name).orElse(member(name).symbol) /** If this is a prototype with some ignored component, reveal one more * layer of it. Otherwise the type itself. @@ -1816,14 +1859,6 @@ object Types extends TypeUtils { /** If this is a proto type, WildcardType, otherwise the type itself */ def dropIfProto: Type = this - /** If this is an AndType, the number of factors, 1 for all other types */ - def andFactorCount: Int = 1 - - /** If this is a OrType, the number of factors if that match `soft`, - * 1 for all other types. - */ - def orFactorCount(soft: Boolean): Int = 1 - // ----- Substitutions ----------------------------------------------------- /** Substitute all types that refer in their symbol attribute to @@ -1931,9 +1966,9 @@ object Types extends TypeUtils { def annotatedToRepeated(using Context): Type = this match { case tp @ ExprType(tp1) => tp.derivedExprType(tp1.annotatedToRepeated) - case self @ AnnotatedType(tp, annot) if annot matches defn.RetainsByNameAnnot => + case self @ AnnotatedType(tp, annot) if annot.matches(defn.RetainsByNameAnnot) => self.derivedAnnotatedType(tp.annotatedToRepeated, annot) - case AnnotatedType(tp, annot) if annot matches defn.RepeatedAnnot => + case AnnotatedType(tp, annot) if annot.matches(defn.RepeatedAnnot) => val typeSym = tp.typeSymbol.asClass assert(typeSym == defn.SeqClass || typeSym == defn.ArrayClass) tp.translateParameterized(typeSym, defn.RepeatedParamClass) @@ -2245,7 +2280,7 @@ object Types extends TypeUtils { def _1: Type def _2: Designator - assert(NamedType.validPrefix(prefix), s"invalid prefix $prefix") + if !NamedType.validPrefix(prefix) then throw InvalidPrefix() private var myName: Name | Null = null private var lastDenotation: Denotation | Null = null @@ -2412,7 +2447,7 @@ object Types extends TypeUtils { else lastd match { case lastd: SymDenotation => if stillValid(lastd) && checkedPeriod.code != NowhereCode then finish(lastd.current) - else finish(memberDenot(lastd.initial.name, allowPrivate = false)) + else finish(memberDenot(lastd.initial.name, allowPrivate = lastd.is(Private))) case _ => fromDesignator } @@ -2633,9 +2668,9 @@ object Types extends TypeUtils { */ final def controlled[T](op: => T)(using Context): T = try { ctx.base.underlyingRecursions += 1 - if (ctx.base.underlyingRecursions < Config.LogPendingUnderlyingThreshold) + if ctx.base.underlyingRecursions < Config.LogPendingUnderlyingThreshold then op - else if (ctx.pendingUnderlying contains this) + else if ctx.pendingUnderlying.contains(this) then throw CyclicReference(symbol) else try { @@ -3022,6 +3057,8 @@ object Types extends TypeUtils { apply(prefix, designatorFor(prefix, name, denot)).withDenot(denot) } + class InvalidPrefix extends Exception + // --- Other SingletonTypes: ThisType/SuperType/ConstantType --------------------------- /** The type cls.this @@ -3375,8 +3412,8 @@ object Types extends TypeUtils { val bcs1set = BaseClassSet(bcs1) def recur(bcs2: List[ClassSymbol]): List[ClassSymbol] = bcs2 match { case bc2 :: bcs2rest => - if (bcs1set contains bc2) - if (bc2.is(Trait)) recur(bcs2rest) + if bcs1set.contains(bc2) then + if bc2.is(Trait) then recur(bcs2rest) else bcs1 // common class, therefore rest is the same in both sequences else bc2 :: recur(bcs2rest) case nil => bcs1 @@ -3387,12 +3424,6 @@ object Types extends TypeUtils { myBaseClasses } - private var myFactorCount = 0 - override def andFactorCount = - if myFactorCount == 0 then - myFactorCount = tp1.andFactorCount + tp2.andFactorCount - myFactorCount - def derivedAndType(tp1: Type, tp2: Type)(using Context): Type = if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this else AndType.make(tp1, tp2, checkValid = true) @@ -3422,22 +3453,26 @@ object Types extends TypeUtils { expectValueTypeOrWildcard(tp2, where) unchecked(tp1, tp2) - def balanced(tp1: Type, tp2: Type)(using Context): AndType = - tp1 match - case AndType(tp11, tp12) if tp1.andFactorCount > tp2.andFactorCount * 2 => - if tp11.andFactorCount < tp12.andFactorCount then - return apply(tp12, balanced(tp11, tp2)) - else - return apply(tp11, balanced(tp12, tp2)) - case _ => - tp2 match - case AndType(tp21, tp22) if tp2.andFactorCount > tp1.andFactorCount * 2 => - if tp22.andFactorCount < tp21.andFactorCount then - return apply(balanced(tp1, tp22), tp21) - else - return apply(balanced(tp1, tp21), tp22) - case _ => - apply(tp1, tp2) + def balanced(tp1: Type, tp2: Type)(using Context): Type = + def size(tp: Type): Int = tp match + case AndType(l, r) => size(l) + size(r) + case _ => 1 + + def iterator(tp: Type): Iterator[Type] = tp match + case AndType(l, r) => iterator(l) ++ iterator(r) + case _ => Iterator.single(tp) + + def build(count: Int, it: Iterator[Type]): Type = + if count == 1 then it.next() + else + val leftSize = count / 2 + val rightSize = count - leftSize + apply(build(leftSize, it), build(rightSize, it)) + + val size1 = size(tp1) + val size2 = size(tp2) + if Math.abs(size1 - size2) <= 1 then apply(tp1, tp2) + else build(size1 + size2, iterator(tp1) ++ iterator(tp2)) def unchecked(tp1: Type, tp2: Type)(using Context): AndType = { assertUnerased() @@ -3472,9 +3507,8 @@ object Types extends TypeUtils { val bcs1set = BaseClassSet(bcs1) def recur(bcs2: List[ClassSymbol]): List[ClassSymbol] = bcs2 match { case bc2 :: bcs2rest => - if (bcs1set contains bc2) - if (bc2.is(Trait)) bc2 :: recur(bcs2rest) - else bcs2 + if bcs1set.contains(bc2) then + if bc2.is(Trait) then bc2 :: recur(bcs2rest) else bcs2 else recur(bcs2rest) case nil => bcs2 @@ -3485,14 +3519,6 @@ object Types extends TypeUtils { myBaseClasses } - private var myFactorCount = 0 - override def orFactorCount(soft: Boolean) = - if this.isSoft == soft then - if myFactorCount == 0 then - myFactorCount = tp1.orFactorCount(soft) + tp2.orFactorCount(soft) - myFactorCount - else 1 - private var myJoin: Type = _ private var myJoinPeriod: Period = Nowhere @@ -3532,8 +3558,8 @@ object Types extends TypeUtils { else tp1.atoms | tp2.atoms private def computeWidenSingletons()(using Context): Type = - val tp1w = tp1.widenSingletons - val tp2w = tp2.widenSingletons + val tp1w = tp1.widenSingletons() + val tp2w = tp2.widenSingletons() if ((tp1 eq tp1w) && (tp2 eq tp2w)) this else TypeComparer.lub(tp1w, tp2w, isSoft = isSoft) override def atoms(using Context): Atoms = @@ -3542,11 +3568,13 @@ object Types extends TypeUtils { if !isProvisional then atomsRunId = ctx.runId myAtoms - override def widenSingletons(using Context): Type = - if widenedRunId != ctx.runId then - myWidened = computeWidenSingletons() - if !isProvisional then widenedRunId = ctx.runId - myWidened + override def widenSingletons(skipSoftUnions: Boolean)(using Context): Type = + if !isSoft || skipSoftUnions then this + else + if widenedRunId != ctx.runId then + myWidened = computeWidenSingletons() + if !isProvisional then widenedRunId = ctx.runId + myWidened def derivedOrType(tp1: Type, tp2: Type, soft: Boolean = isSoft)(using Context): Type = if ((tp1 eq this.tp1) && (tp2 eq this.tp2) && soft == isSoft) this @@ -3577,22 +3605,26 @@ object Types extends TypeUtils { unique(new CachedOrType(tp1, tp2, soft)) } - def balanced(tp1: Type, tp2: Type, soft: Boolean)(using Context): OrType = - tp1 match - case OrType(tp11, tp12) if tp1.orFactorCount(soft) > tp2.orFactorCount(soft) * 2 => - if tp11.orFactorCount(soft) < tp12.orFactorCount(soft) then - return apply(tp12, balanced(tp11, tp2, soft), soft) - else - return apply(tp11, balanced(tp12, tp2, soft), soft) - case _ => - tp2 match - case OrType(tp21, tp22) if tp2.orFactorCount(soft) > tp1.orFactorCount(soft) * 2 => - if tp22.orFactorCount(soft) < tp21.orFactorCount(soft) then - return apply(balanced(tp1, tp22, soft), tp21, soft) - else - return apply(balanced(tp1, tp21, soft), tp22, soft) - case _ => - apply(tp1, tp2, soft) + def balanced(tp1: Type, tp2: Type, soft: Boolean)(using Context): Type = + def size(tp: Type): Int = tp match + case o@OrType(l, r) if o.isSoft == soft => size(l) + size(r) + case _ => 1 + + def iterator(tp: Type): Iterator[Type] = tp match + case o@OrType(l, r) if o.isSoft == soft => iterator(l) ++ iterator(r) + case _ => Iterator.single(tp) + + def build(count: Int, it: Iterator[Type]): Type = + if count == 1 then it.next() + else + val leftSize = count / 2 + val rightSize = count - leftSize + apply(build(leftSize, it), build(rightSize, it), soft) + + val size1 = size(tp1) + val size2 = size(tp2) + if Math.abs(size1 - size2) <= 1 then apply(tp1, tp2, soft) + else build(size1 + size2, iterator(tp1) ++ iterator(tp2)) def make(tp1: Type, tp2: Type, soft: Boolean)(using Context): Type = if (tp1 eq tp2) tp1 @@ -3730,9 +3762,51 @@ object Types extends TypeUtils { def integrate(tparams: List[ParamInfo], tp: Type)(using Context): Type = (tparams: @unchecked) match { case LambdaParam(lam, _) :: _ => tp.subst(lam, this) // This is where the precondition is necessary. - case params: List[Symbol @unchecked] => tp.subst(params, paramRefs) + case params: List[Symbol @unchecked] => IntegrateMap(params, paramRefs)(tp) } + /** A map that replaces references to symbols in `params` by the types in + * `paramRefs`. + * + * It is similar to [[Substituters#subst]] but avoids reloading denotations + * of named types by overriding `derivedSelect`. + * + * This is needed because during integration, [[TermParamRef]]s refer to a + * [[LambdaType]] that is not yet fully constructed, in particular for wich + * `paramInfos` is `null`. In that case all [[TermParamRef]]s have + * [[NoType]] as underlying type. Reloading denotions of selections + * involving such [[TermParamRef]]s in [[NamedType#withPrefix]] could then + * result in a [[NoDenotation]], which would make later disambiguation of + * overloads impossible. See `tests/pos/annot-17242.scala` for example. + */ + private class IntegrateMap(from: List[Symbol], to: List[Type])(using Context) extends TypeMap: + override def apply(tp: Type) = + // Same implementation as in `SubstMap`, except the `derivedSelect` in + // the `NamedType` case, and the default case that just calls `mapOver`. + tp match + case tp: NamedType => + val sym = tp.symbol + var fs = from + var ts = to + while (fs.nonEmpty && ts.nonEmpty) { + if (fs.head eq sym) return ts.head + fs = fs.tail + ts = ts.tail + } + if (tp.prefix `eq` NoPrefix) tp + else derivedSelect(tp, apply(tp.prefix)) + case _: BoundType | _: ThisType => tp + case _ => mapOver(tp) + + override final def derivedSelect(tp: NamedType, pre: Type): Type = + if tp.prefix eq pre then tp + else + pre match + case ref: ParamRef if (ref.binder eq self) && tp.symbol.exists && tp.symbol.is(Method) => + NamedType(pre, tp.name, tp.denot.asSeenFrom(pre)) + case _ => + tp.derivedSelect(pre) + final def derivedLambdaType(paramNames: List[ThisName] = this.paramNames, paramInfos: List[PInfo] = this.paramInfos, resType: Type = this.resType)(using Context): This = @@ -3993,9 +4067,14 @@ object Types extends TypeUtils { } dropDependencies(resultType) else resultType + + /** Are all parameter names synthetic? */ + def allParamNamesSynthetic = paramNames.zipWithIndex.forall{ (name, i) => + name == nme.syntheticParamName(i) + } } - abstract case class MethodType(paramNames: List[TermName])( + abstract case class MethodType(paramNames: List[TermName])( paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type) extends MethodOrPoly with TermLambda with NarrowCached { thisMethodType => @@ -5413,11 +5492,11 @@ object Types extends TypeUtils { parent.hashIsStable override def eql(that: Type): Boolean = that match - case that: AnnotatedType => (parent eq that.parent) && (annot eql that.annot) + case that: AnnotatedType => (parent eq that.parent) && annot.eql(that.annot) case _ => false override def iso(that: Any, bs: BinderPairs): Boolean = that match - case that: AnnotatedType => parent.equals(that.parent, bs) && (annot eql that.annot) + case that: AnnotatedType => parent.equals(that.parent, bs) && annot.eql(that.annot) case _ => false } @@ -5625,19 +5704,18 @@ object Types extends TypeUtils { end samParent def samClass(tp: Type)(using Context): Symbol = tp match - case tp: ClassInfo => - val cls = tp.cls + case tp @ ClassInfo(_, cls, _, _, _) => def takesNoArgs(tp: Type) = !tp.classSymbol.primaryConstructor.exists // e.g. `ContextFunctionN` does not have constructors - || tp.applicableConstructors(Nil, adaptVarargs = true).lengthCompare(1) == 0 + || tp.applicableConstructors(argTypes = Nil, adaptVarargs = true).lengthCompare(1) == 0 // we require a unique constructor so that SAM expansion is deterministic val noArgsNeeded: Boolean = takesNoArgs(tp) - && (!tp.cls.is(Trait) || takesNoArgs(tp.parents.head)) + && (!cls.is(Trait) || takesNoArgs(tp.parents.head)) def isInstantiable = - !tp.cls.isOneOf(FinalOrSealed) && (tp.appliedRef <:< tp.selfType) - if noArgsNeeded && isInstantiable then tp.cls + !cls.isOneOf(FinalOrSealed) && (tp.appliedRef <:< tp.selfType) + if noArgsNeeded && isInstantiable then cls else NoSymbol case tp: AppliedType => samClass(tp.superType) @@ -5792,11 +5870,13 @@ object Types extends TypeUtils { protected def derivedLambdaType(tp: LambdaType)(formals: List[tp.PInfo], restpe: Type): Type = tp.derivedLambdaType(tp.paramNames, formals, restpe) + protected def mapArg(arg: Type, tparam: ParamInfo): Type = arg match + case arg: TypeBounds => this(arg) + case arg => atVariance(variance * tparam.paramVarianceSign)(this(arg)) + protected def mapArgs(args: List[Type], tparams: List[ParamInfo]): List[Type] = args match case arg :: otherArgs if tparams.nonEmpty => - val arg1 = arg match - case arg: TypeBounds => this(arg) - case arg => atVariance(variance * tparams.head.paramVarianceSign)(this(arg)) + val arg1 = mapArg(arg, tparams.head) val otherArgs1 = mapArgs(otherArgs, tparams.tail) if ((arg1 eq arg) && (otherArgs1 eq otherArgs)) args else arg1 :: otherArgs1 @@ -5937,14 +6017,7 @@ object Types extends TypeUtils { } } - private def treeTypeMap = new TreeTypeMap( - typeMap = this, - // Using `ConservativeTreeCopier` is needed when copying dependent annoted - // types, where we can refer to a previous parameter represented as - // `TermParamRef` that has no underlying type yet. - // See tests/pos/annot-17242.scala. - cpy = ConservativeTreeCopier() - ) + private def treeTypeMap = new TreeTypeMap(typeMap = this) def mapOver(syms: List[Symbol]): List[Symbol] = mapSymbols(syms, treeTypeMap) @@ -5965,6 +6038,8 @@ object Types extends TypeUtils { override def stopAt = thisMap.stopAt def apply(tp: Type) = f(thisMap(tp)) } + + override def toString = s"${getClass.getSimpleName}@$hashCode" // otherwise would print as } /** A type map that maps also parents and self type of a ClassInfo */ @@ -5980,7 +6055,7 @@ object Types extends TypeUtils { } } - @sharable object IdentityTypeMap extends TypeMap()(NoContext) { + @sharable object IdentityTypeMap extends TypeMap()(using NoContext) { def apply(tp: Type): Type = tp } @@ -5994,9 +6069,30 @@ object Types extends TypeUtils { abstract class ApproximatingTypeMap(using Context) extends TypeMap { thisMap => protected def range(lo: Type, hi: Type): Type = - if (variance > 0) hi - else if (variance < 0) lo - else if (lo `eq` hi) lo + if variance > 0 then hi + else if variance < 0 then + if (lo eq defn.NothingType) then + // Approximate by Nothing & hi instead of just Nothing, in case the + // approximated type is used as the prefix of another type (this would + // lead to a type with a `NoDenotation` denot and a possible + // MissingType in `TypeErasure#sigName`). + // + // Note that we cannot simply check for a `Nothing` prefix in + // `derivedSelect`, because the substitution might be done lazily (for + // example if Nothing is the type of a parameter being depended on in + // a MethodType) + // + // Test case in tests/pos/i23530.scala (and tests/pos/i23627.scala for + // the higher-kinded case which requires eta-expansion) + hi.etaExpand match + case expandedHi: HKTypeLambda => + expandedHi.derivedLambdaType(resType = AndType(lo, expandedHi.resType)) + case _ => + // simple-kinded case + AndType(lo, hi) + else + lo + else if lo `eq` hi then lo else Range(lower(lo), upper(hi)) protected def emptyRange = range(defn.NothingType, defn.AnyType) @@ -6451,7 +6547,7 @@ object Types extends TypeUtils { def maybeAdd(xs: List[NamedType], tp: NamedType): List[NamedType] = if p(tp) then tp :: xs else xs val seen = util.HashSet[Type]() def apply(xs: List[NamedType], tp: Type): List[NamedType] = - if seen contains tp then xs + if seen.contains(tp) then xs else seen += tp tp match @@ -6489,23 +6585,32 @@ object Types extends TypeUtils { class TypeSizeAccumulator(using Context) extends TypeAccumulator[Int] { var seen = util.HashSet[Type](initialCapacity = 8) def apply(n: Int, tp: Type): Int = - if seen.contains(tp) then n - else { - seen += tp - tp match { - case tp: AppliedType => - val tpNorm = tp.tryNormalize - if tpNorm.exists then apply(n, tpNorm) - else foldOver(n + 1, tp) - case tp: RefinedType => - foldOver(n + 1, tp) - case tp: TypeRef if tp.info.isTypeAlias => - apply(n, tp.superType) - case tp: TypeParamRef => - apply(n, TypeComparer.bounds(tp)) - case _ => + tp match { + case tp: AppliedType if defn.isTupleNType(tp) => + foldOver(n + 1, tp.toNestedPairs) + case tp: AppliedType => + val tpNorm = tp.tryNormalize + if tpNorm.exists then apply(n, tpNorm) + else foldOver(n + 1, tp) + case tp: RefinedType => + foldOver(n + 1, tp) + case tp: TypeRef if tp.info.isTypeAlias => + apply(n, tp.superType) + case tp: TypeParamRef => + if seen.contains(tp) then n + else + seen += tp + val bounds = TypeComparer.bounds(tp) + val loSize = apply(n, bounds.lo) + val hiSize = apply(n, bounds.hi) + hiSize max loSize + case tp: LazyRef => + if seen.contains(tp) then n + else + seen += tp foldOver(n, tp) - } + case _ => + foldOver(n, tp) } } @@ -6630,7 +6735,7 @@ object Types extends TypeUtils { object fieldFilter extends NameFilter { def apply(pre: Type, name: Name)(using Context): Boolean = - name.isTermName && (pre member name).hasAltWith(!_.symbol.is(Method)) + name.isTermName && pre.member(name).hasAltWith(!_.symbol.is(Method)) def isStable = true } diff --git a/compiler/src/dotty/tools/dotc/core/Uniques.scala b/compiler/src/dotty/tools/dotc/core/Uniques.scala index da6b0aba88bd..b50905c22c98 100644 --- a/compiler/src/dotty/tools/dotc/core/Uniques.scala +++ b/compiler/src/dotty/tools/dotc/core/Uniques.scala @@ -3,6 +3,7 @@ package core import Types.*, Contexts.*, util.Stats.*, Hashable.*, Names.* import config.Config +import Symbols.Symbol import Decorators.* import util.{WeakHashSet, Stats} import WeakHashSet.Entry @@ -41,8 +42,10 @@ object Uniques: val h = doHash(null, designator, prefix) if monitored then recordCaching(h, classOf[NamedType]) def newType = - if (isTerm) new CachedTermRef(prefix, designator, h) - else new CachedTypeRef(prefix, designator, h) + try + if isTerm then new CachedTermRef(prefix, designator, h) + else new CachedTypeRef(prefix, designator, h) + catch case ex: InvalidPrefix => badPrefix(prefix, designator) if h == NotCached then newType else // Inlined from WeakHashSet#put @@ -61,6 +64,14 @@ object Uniques: linkedListLoop(oldHead) end if + end enterIfNew + + private def badPrefix(prefix: Type, desig: Designator)(using Context): Nothing = + def name = desig match + case desig: Name => desig + case desig: Symbol => desig.name + throw TypeError(em"invalid prefix $prefix when trying to form $prefix . $name") + end NamedTypeUniques final class AppliedUniques extends WeakHashSet[AppliedType](Config.initialUniquesCapacity * 2) with Hashable: diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala index 6ad71c5fd1ce..7a2232891a83 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala @@ -73,7 +73,10 @@ object ClassfileConstants { inline val CONSTANT_METHODHANDLE = 15 inline val CONSTANT_METHODTYPE = 16 + inline val CONSTANT_DYNAMIC = 17 inline val CONSTANT_INVOKEDYNAMIC = 18 + inline val CONSTANT_MODULE = 19 + inline val CONSTANT_PACKAGE = 20 // tags describing the type of a literal in attribute values inline val BYTE_TAG = 'B' diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index c20b6c73b1ea..1db814c5e3d8 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -5,7 +5,7 @@ package classfile import scala.language.unsafeNulls -import dotty.tools.tasty.{ TastyReader, TastyHeaderUnpickler } +import dotty.tools.tasty.{ TastyReader, TastyHeaderUnpickler, UnpickleException } import Contexts.*, Symbols.*, Types.*, Names.*, StdNames.*, NameOps.*, Scopes.*, Decorators.* import SymDenotations.*, unpickleScala2.Scala2Unpickler.*, Constants.*, Annotations.*, util.Spans.* @@ -131,7 +131,9 @@ class ClassfileParser( catch { case e: RuntimeException => if (ctx.debug) e.printStackTrace() - val addendum = Header.Version.brokenVersionAddendum(classfileVersion) + val addendum = e match + case _: UnpickleException => "" + case _ => Header.Version.brokenVersionAddendum(classfileVersion) throw new IOException( i""" class file ${classfile.canonicalPath} is broken$addendum, | reading aborted with ${e.getClass}: @@ -194,27 +196,21 @@ class ClassfileParser( * Updates the read pointer of 'in'. */ def parseParents: List[Type] = { val superType = - if (classRoot.symbol == defn.ComparableClass || - classRoot.symbol == defn.JavaCloneableClass || - classRoot.symbol == defn.JavaSerializableClass) { - // Treat these interfaces as universal traits - in.nextChar + val superClass = in.nextChar + // Treat these interfaces as universal traits + if classRoot.symbol == defn.ComparableClass + || classRoot.symbol == defn.JavaCloneableClass + || classRoot.symbol == defn.JavaSerializableClass + then defn.AnyType - } else - pool.getSuperClass(in.nextChar).typeRef + pool.getSuperClass(superClass).typeRef val ifaceCount = in.nextChar - var ifaces = for (i <- (0 until ifaceCount).toList) yield pool.getSuperClass(in.nextChar).typeRef - // Dotty deviation: was - // var ifaces = for (i <- List.range(0, ifaceCount)) ... - // This does not typecheck because the type parameter of List is now lower-bounded by Int | Char. - // Consequently, no best implicit for the "Integral" evidence parameter of "range" - // is found. Previously, this worked because of weak conformance, which has been dropped. - + val ifaces = List.fill(ifaceCount.toInt): + pool.getSuperClass(in.nextChar).typeRef superType :: ifaces } - val result = unpickleOrParseInnerClasses() if (!result.isDefined) { var classInfo: Type = TempClassInfoType(parseParents, instanceScope, classRoot.symbol) @@ -233,8 +229,8 @@ class ClassfileParser( moduleRoot.setPrivateWithin(privateWithin) moduleRoot.sourceModule.setPrivateWithin(privateWithin) - for (i <- 0 until in.nextChar) parseMember(method = false) - for (i <- 0 until in.nextChar) parseMember(method = true) + for (_ <- 0 until in.nextChar) parseMember(method = false) + for (_ <- 0 until in.nextChar) parseMember(method = true) classRoot.registerCompanion(moduleRoot.symbol) moduleRoot.registerCompanion(classRoot.symbol) @@ -904,7 +900,7 @@ class ClassfileParser( // Nothing$ and Null$ were incorrectly emitted with a Scala attribute // instead of ScalaSignature before 2.13.0-M2, see https://github.com/scala/scala/pull/5952 - private val scalaUnpickleWhitelist = List(tpnme.nothingClass, tpnme.nullClass) + private val scalaUnpickleAllowlist = List(tpnme.nothingClass, tpnme.nullClass) /** Parse inner classes. Expects `in.bp` to point to the superclass entry. * Restores the old `bp`. @@ -1028,7 +1024,7 @@ class ClassfileParser( report.error(s"Found a TASTY attribute with a length different from 16 in $classfile. This is likely a bug in the compiler. Please report.", NoSourcePosition) } - if scan(tpnme.ScalaATTR) && !scalaUnpickleWhitelist.contains(classRoot.name) + if scan(tpnme.ScalaATTR) && !scalaUnpickleAllowlist.contains(classRoot.name) && !(classRoot.name.startsWith("Tuple") && classRoot.name.endsWith("$sp")) && !(classRoot.name.startsWith("Product") && classRoot.name.endsWith("$sp")) then @@ -1224,13 +1220,14 @@ class ClassfileParser( (in.nextByte.toInt: @switch) match { case CONSTANT_UTF8 | CONSTANT_UNICODE => in.skip(in.nextChar) - case CONSTANT_CLASS | CONSTANT_STRING | CONSTANT_METHODTYPE => + case CONSTANT_CLASS | CONSTANT_STRING | CONSTANT_METHODTYPE + | CONSTANT_MODULE | CONSTANT_PACKAGE => in.skip(2) case CONSTANT_METHODHANDLE => in.skip(3) case CONSTANT_FIELDREF | CONSTANT_METHODREF | CONSTANT_INTFMETHODREF | CONSTANT_NAMEANDTYPE | CONSTANT_INTEGER | CONSTANT_FLOAT - | CONSTANT_INVOKEDYNAMIC => + | CONSTANT_INVOKEDYNAMIC | CONSTANT_DYNAMIC => in.skip(4) case CONSTANT_LONG | CONSTANT_DOUBLE => in.skip(8) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala index 3d8080e72a29..90f0483b6bdc 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -43,19 +43,19 @@ object PositionPickler: pickler.newSection(PositionsSection, buf) - /** Pickle the number of lines followed by the length of each line */ - def pickleLineOffsets(): Unit = { + /** Pickle the number of lines followed by the size of each line */ + def pickleLinesSizes(): Unit = { val content = source.content() buf.writeNat(content.count(_ == '\n') + 1) // number of lines - var lastIndex = content.indexOf('\n', 0) - buf.writeNat(lastIndex) // size of first line + var lastIndex = content.indexOf('\n') + buf.writeNat(if lastIndex != -1 then lastIndex else content.length) // size of first line while lastIndex != -1 do val nextIndex = content.indexOf('\n', lastIndex + 1) val end = if nextIndex != -1 then nextIndex else content.length buf.writeNat(end - lastIndex - 1) // size of the next line lastIndex = nextIndex } - pickleLineOffsets() + pickleLinesSizes() var lastIndex = 0 var lastSpan = Span(0, 0) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala index 975264a288dd..5dda0740710e 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/PositionUnpickler.scala @@ -27,7 +27,10 @@ class PositionUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName) { myLineSizes = new Array[Int](lines) var i = 0 while i < lines do - myLineSizes(i) += readNat() + // This should be `readNat()` per the spec, + // but previous compiler versions had a bug and could write `-1` as a Nat + val lineSize = readLongNat() + myLineSizes(i) += (if lineSize == 0xFFFFFFFFL then -1 else lineSize.toInt) i += 1 mySpans = util.HashMap[Addr, Span]() @@ -68,6 +71,7 @@ class PositionUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName) { mySourceNameRefs } + // Can include -1 entries for "unknown", as previous compiler versions had a bug private[tasty] def lineSizes: Array[Int] = { ensureDefined() myLineSizes diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala index 8bd94b553545..0ecc34712b4b 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -4,14 +4,14 @@ package tasty import dotty.tools.tasty.{TastyBuffer, TastyReader} import TastyBuffer.NameRef - -import Contexts.*, Decorators.* +import Contexts.* +import Decorators.* import Names.Name import TastyUnpickler.* import util.Spans.offsetToInt import dotty.tools.tasty.TastyFormat.{ASTsSection, PositionsSection, CommentsSection} import java.nio.file.{Files, Paths} -import dotty.tools.io.{JarArchive, Path} +import dotty.tools.io.{AbstractFile, JarArchive, Path} import dotty.tools.tasty.TastyFormat.header import scala.compiletime.uninitialized @@ -50,8 +50,11 @@ object TastyPrinter: System.exit(1) else if arg.endsWith(".jar") then val jar = JarArchive.open(Path(arg), create = false) + def tastyFiles(file: AbstractFile): Iterator[AbstractFile] = + if file.isDirectory then file.iterator().flatMap(tastyFiles) + else Iterator.single(file).filter(_.name.endsWith(".tasty")) try - for file <- jar.iterator() if file.name.endsWith(".tasty") do + for file <- tastyFiles(jar) do printTasty(s"$arg ${file.path}", file.toByteArray) finally jar.close() else @@ -103,7 +106,7 @@ class TastyPrinter(bytes: Array[Byte]) { unpickle(new TreeSectionUnpickler(sb)) unpickle(new PositionSectionUnpickler(sb)) unpickle(new CommentSectionUnpickler(sb)) - sb.result + sb.result() } class TreeSectionUnpickler(sb: StringBuilder) extends SectionUnpickler[Unit](ASTsSection) { @@ -186,7 +189,7 @@ class TastyPrinter(bytes: Array[Byte]) { sb.append(s" lines: ${lineSizes.length}\n") sb.append(s" line sizes:\n") val windowSize = 20 - for window <-posUnpickler.lineSizes.sliding(windowSize, windowSize) do + for window <- lineSizes.sliding(windowSize, windowSize) do sb.append(" ").append(window.mkString(", ")).append("\n") // sb.append(posUnpickler.lineSizes.mkString(" line sizes: ", ", ", "\n")) sb.append(" positions:\n") diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 75fcaaf544a3..016884c90011 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -630,8 +630,13 @@ class TreePickler(pickler: TastyPickler) { case tree: TypeTree => pickleType(tree.tpe) case SingletonTypeTree(ref) => - writeByte(SINGLETONtpt) - pickleTree(ref) + val tp = ref.tpe + val tp1 = tp.deskolemized + if tp1 ne tp then + pickleType(tp1) + else + writeByte(SINGLETONtpt) + pickleTree(ref) case RefinedTypeTree(parent, refinements) => if (refinements.isEmpty) pickleTree(parent) else { @@ -843,7 +848,7 @@ class TreePickler(pickler: TastyPickler) { val it = mp.keysIterator var i = 0 while i < keys.length do - keys(i) = it.next + keys(i) = it.next() i += 1 assert(!it.hasNext) i = 0 diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index ee202330f172..3bcf4bffd784 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -127,7 +127,8 @@ class TreeUnpickler(reader: TastyReader, } } - class Completer(reader: TastyReader)(using @constructorOnly _ctx: Context) extends LazyType { + class Completer(reader: TastyReader)(using @constructorOnly _ctx: Context) + extends LazyType, CompleterWithCleanup { import reader.* val owner = ctx.owner val mode = ctx.mode @@ -147,6 +148,8 @@ class TreeUnpickler(reader: TastyReader, case ex: CyclicReference => throw ex case ex: AssertionError => fail(ex) case ex: Exception => fail(ex) + finally + cleanup() } class TreeReader(val reader: TastyReader) { @@ -159,7 +162,7 @@ class TreeUnpickler(reader: TastyReader, if (tag >= firstLengthTreeTag) goto(readEnd()) else if (tag >= firstNatASTTreeTag) { readNat(); skipTree() } else if (tag >= firstASTTreeTag) skipTree() - else if (tag >= firstNatTreeTag) readNat() + else if (tag >= firstNatTreeTag) readLongInt() } def skipTree(): Unit = skipTree(readByte()) @@ -196,7 +199,7 @@ class TreeUnpickler(reader: TastyReader, if (mode == MemberDefsOnly) skipTree(tag) else if (tag >= firstLengthTreeTag) { val end = readEnd() - var nrefs = numRefs(tag) + val nrefs = numRefs(tag) if (nrefs < 0) { for (i <- nrefs until 0) scanTree(buf) goto(end) @@ -212,7 +215,7 @@ class TreeUnpickler(reader: TastyReader, } else if (tag >= firstNatASTTreeTag) { readNat(); scanTree(buf) } else if (tag >= firstASTTreeTag) scanTree(buf) - else if (tag >= firstNatTreeTag) readNat() + else if (tag >= firstNatTreeTag) readLongInt() } } @@ -770,8 +773,8 @@ class TreeUnpickler(reader: TastyReader, if (sym.isTerm && !sym.isOneOf(DeferredOrLazyOrMethod)) initsFlags = EmptyFlags else if (sym.isClass || - sym.is(Method, butNot = Deferred) && !sym.isConstructor) - initsFlags &= NoInits + sym.isOneOf(Lazy | Method, butNot = Deferred) && !sym.isConstructor) + initsFlags &= NoInits // i.e. initsFlags &~= PureInterface case IMPORT | EXPORT => skipTree() case PACKAGE => @@ -1106,7 +1109,6 @@ class TreeUnpickler(reader: TastyReader, inline def readImportOrExport(inline mkTree: (Tree, List[untpd.ImportSelector]) => Tree)()(using Context): Tree = { val start = currentAddr - assert(sourcePathAt(start).isEmpty) readByte() readEnd() val expr = readTree() @@ -1179,6 +1181,8 @@ class TreeUnpickler(reader: TastyReader, readType() match { case path: TypeRef => TypeTree(path) case path: TermRef => ref(path) + case path @ AndType(_: TermRef, _: TypeRef) => TypeTree(path) + case path @ AndType(_: TypeRef, _: TermRef) => TypeTree(path) case path: ThisType => untpd.This(untpd.EmptyTypeIdent).withType(path) case path: ConstantType => Literal(path.value) } @@ -1190,7 +1194,7 @@ class TreeUnpickler(reader: TastyReader, val tpe0 = name match case name: TypeName => TypeRef(qualType, name, denot) case name: TermName => TermRef(qualType, name, denot) - val tpe = TypeOps.makePackageObjPrefixExplicit(tpe0) + val tpe = tpe0.makePackageObjPrefixExplicit ConstFold.Select(untpd.Select(qual, name).withType(tpe)) def completeSelect(name: Name, sig: Signature, target: Name): Select = diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index f1f5222b73a1..a3fc5972913f 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -64,9 +64,15 @@ object Scala2Unpickler { denot.info = PolyType.fromParams(denot.owner.typeParams, denot.info) } - def ensureConstructor(cls: ClassSymbol, clsDenot: ClassDenotation, scope: Scope)(using Context): Unit = { - if (scope.lookup(nme.CONSTRUCTOR) == NoSymbol) { - val constr = newDefaultConstructor(cls) + def ensureConstructor(cls: ClassSymbol, clsDenot: ClassDenotation, scope: Scope)(using Context): Unit = + doEnsureConstructor(cls, clsDenot, scope, fromScala2 = true) + + private def doEnsureConstructor(cls: ClassSymbol, clsDenot: ClassDenotation, scope: Scope, fromScala2: Boolean) + (using Context): Unit = + if scope.lookup(nme.CONSTRUCTOR) == NoSymbol then + val constr = + if fromScala2 || cls.isAllOf(Trait | JavaDefined) then newDefaultConstructor(cls) + else newConstructor(cls, Private, paramNames = Nil, paramTypes = Nil) // Scala 2 traits have a constructor iff they have initialization code // In dotc we represent that as !StableRealizable, which is also owner.is(NoInits) if clsDenot.flagsUNSAFE.is(Trait) then @@ -74,8 +80,6 @@ object Scala2Unpickler { clsDenot.setFlag(NoInits) addConstructorTypeParams(constr) cls.enter(constr, scope) - } - } def setClassInfo(denot: ClassDenotation, info: Type, fromScala2: Boolean, selfInfo: Type = NoType)(using Context): Unit = { val cls = denot.classSymbol @@ -109,7 +113,7 @@ object Scala2Unpickler { if (tsym.exists) tsym.setFlag(TypeParam) else denot.enter(tparam, decls) } - if (!denot.flagsUNSAFE.isAllOf(JavaModule)) ensureConstructor(cls, denot, decls) + if (!denot.flagsUNSAFE.isAllOf(JavaModule)) doEnsureConstructor(cls, denot, decls, fromScala2) val scalacCompanion = denot.classSymbol.scalacLinkedClass @@ -821,7 +825,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas val args = until(end, () => readTypeRef()) if (sym == defn.ByNameParamClass2x) ExprType(args.head.adaptByNameArgUnderPureFuns) else if (ctx.settings.scalajs.value && args.length == 2 && - sym.owner == JSDefinitions.jsdefn.ScalaJSJSPackageClass && sym == JSDefinitions.jsdefn.PseudoUnionClass) { + sym.owner == JSDefinitions.jsdefn.ScalaJSJSPackageClass && sym.name == tpnme.raw.BAR) { // Treat Scala.js pseudo-unions as real unions, this requires a // special-case in erasure, see TypeErasure#eraseInfo. OrType(args(0), args(1), soft = false) @@ -969,14 +973,14 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas /** Read an annotation argument, which is pickled either * as a Constant or a Tree. */ - protected def readAnnotArg(i: Int)(using Context): untpd.Tree = untpd.TypedSplice(bytes(index(i)) match + protected def readAnnotArg(i: Int)(using Context): untpd.Tree = untpd.TypedSplice: + bytes(index(i)) match case TREE => at(i, () => readTree()) case _ => at(i, () => readConstant() match case c: Constant => Literal(c) case tp: TermRef => ref(tp) ) - ) /** Read a ClassfileAnnotArg (argument to a classfile annotation) */ diff --git a/compiler/src/dotty/tools/dotc/coverage/Coverage.scala b/compiler/src/dotty/tools/dotc/coverage/Coverage.scala index 3061bfa4ee5c..3c5297334a3b 100644 --- a/compiler/src/dotty/tools/dotc/coverage/Coverage.scala +++ b/compiler/src/dotty/tools/dotc/coverage/Coverage.scala @@ -8,21 +8,16 @@ import java.nio.file.Path class Coverage: private val statementsById = new mutable.LongMap[Statement](256) - private var statementId: Int = 0 - - def nextStatementId(): Int = - statementId += 1 - statementId - 1 + private var _nextStatementId: Int = 0 + def nextStatementId(): Int = _nextStatementId + def setNextStatementId(id: Int): Unit = _nextStatementId = id def statements: Iterable[Statement] = statementsById.values - def addStatement(stmt: Statement): Unit = statementsById(stmt.id) = stmt - - def removeStatementsFromFile(sourcePath: Path) = - val removedIds = statements.filter(_.location.sourcePath == sourcePath).map(_.id.toLong) - removedIds.foreach(statementsById.remove) - + def addStatement(stmt: Statement): Unit = + if stmt.id >= _nextStatementId then _nextStatementId = stmt.id + 1 + statementsById(stmt.id) = stmt /** * A statement that can be invoked, and thus counted as "covered" by code coverage tools. diff --git a/compiler/src/dotty/tools/dotc/coverage/Location.scala b/compiler/src/dotty/tools/dotc/coverage/Location.scala index de1a3db710a3..de8eb7a607db 100644 --- a/compiler/src/dotty/tools/dotc/coverage/Location.scala +++ b/compiler/src/dotty/tools/dotc/coverage/Location.scala @@ -46,5 +46,5 @@ object Location: s"$packageName.$className", classType, methodName, - source.file.absolute.jpath + source.file.absolute.jpath.nn ) diff --git a/compiler/src/dotty/tools/dotc/coverage/Serializer.scala b/compiler/src/dotty/tools/dotc/coverage/Serializer.scala index 26efa8934e00..9fa095b0030d 100644 --- a/compiler/src/dotty/tools/dotc/coverage/Serializer.scala +++ b/compiler/src/dotty/tools/dotc/coverage/Serializer.scala @@ -1,23 +1,28 @@ package dotty.tools.dotc package coverage +import java.nio.charset.StandardCharsets.UTF_8 import java.nio.file.{Path, Paths, Files} import java.io.Writer import scala.language.unsafeNulls import scala.collection.mutable.StringBuilder +import scala.io.Source /** * Serializes scoverage data. - * @see https://github.com/scoverage/scalac-scoverage-plugin/blob/main/scalac-scoverage-plugin/src/main/scala/scoverage/Serializer.scala + * @see https://github.com/scoverage/scalac-scoverage-plugin/blob/main/serializer/src/main/scala/scoverage/serialize/Serializer.scala */ object Serializer: private val CoverageFileName = "scoverage.coverage" private val CoverageDataFormatVersion = "3.0" + def coverageFilePath(dataDir: String): Path = + Paths.get(dataDir, CoverageFileName).toAbsolutePath + /** Write out coverage data to the given data directory, using the default coverage filename */ def serialize(coverage: Coverage, dataDir: String, sourceRoot: String): Unit = - serialize(coverage, Paths.get(dataDir, CoverageFileName).toAbsolutePath, Paths.get(sourceRoot).toAbsolutePath) + serialize(coverage, coverageFilePath(dataDir), Paths.get(sourceRoot).toAbsolutePath) /** Write out coverage data to a file. */ def serialize(coverage: Coverage, file: Path, sourceRoot: Path): Unit = @@ -86,6 +91,64 @@ object Serializer: .sortBy(_.id) .foreach(stmt => writeStatement(stmt, writer)) + def deserialize(file: Path, sourceRoot: String): Coverage = + val source = Source.fromFile(file.toFile(), UTF_8.name()) + try deserialize(source.getLines(), Paths.get(sourceRoot).toAbsolutePath) + finally source.close() + + def deserialize(lines: Iterator[String], sourceRoot: Path): Coverage = + def toStatement(lines: Iterator[String]): Statement = + val id: Int = lines.next().toInt + val sourcePath = lines.next() + val packageName = lines.next() + val className = lines.next() + val classType = lines.next() + val fullClassName = lines.next() + val method = lines.next() + val loc = Location( + packageName, + className, + fullClassName, + classType, + method, + sourceRoot.resolve(sourcePath).normalize() + ) + val start: Int = lines.next().toInt + val end: Int = lines.next().toInt + val lineNo: Int = lines.next().toInt + val symbolName: String = lines.next() + val treeName: String = lines.next() + val branch: Boolean = lines.next().toBoolean + val count: Int = lines.next().toInt + val ignored: Boolean = lines.next().toBoolean + val desc = lines.toList.mkString("\n") + Statement( + loc, + id, + start, + end, + lineNo, + desc, + symbolName, + treeName, + branch, + ignored + ) + + val headerFirstLine = lines.next() + require( + headerFirstLine == s"# Coverage data, format version: $CoverageDataFormatVersion", + "Wrong file format" + ) + + val linesWithoutHeader = lines.dropWhile(_.startsWith("#")) + val coverage = Coverage() + while !linesWithoutHeader.isEmpty do + val oneStatementLines = linesWithoutHeader.takeWhile(_ != "\f") + coverage.addStatement(toStatement(oneStatementLines)) + end while + coverage + /** Makes a String suitable for output in the coverage statement data as a single line. * Escaped characters: '\\' (backslash), '\n', '\r', '\f' */ diff --git a/compiler/src/dotty/tools/dotc/decompiler/DecompilationPrinter.scala b/compiler/src/dotty/tools/dotc/decompiler/DecompilationPrinter.scala index 16e7cc9c7adc..461223e201bb 100644 --- a/compiler/src/dotty/tools/dotc/decompiler/DecompilationPrinter.scala +++ b/compiler/src/dotty/tools/dotc/decompiler/DecompilationPrinter.scala @@ -22,7 +22,7 @@ class DecompilationPrinter extends Phase { override def phaseName: String = "decompilationPrinter" - override def run(using Context): Unit = + override protected def run(using Context): Unit = if (ctx.settings.outputDir.isDefault) printToOutput(System.out) else { val outputDir = ctx.settings.outputDir.value diff --git a/compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala b/compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala index 3330e9118102..1a0293dffb3a 100644 --- a/compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala +++ b/compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala @@ -87,5 +87,5 @@ class ReadTasty extends Phase { Some(unit) } - def run(using Context): Unit = unsupported("run") + protected def run(using Context): Unit = unsupported("run") } diff --git a/compiler/src/dotty/tools/dotc/fromtasty/TastyFileUtil.scala b/compiler/src/dotty/tools/dotc/fromtasty/TastyFileUtil.scala index bc04cc648a65..e6baafe9bdb7 100644 --- a/compiler/src/dotty/tools/dotc/fromtasty/TastyFileUtil.scala +++ b/compiler/src/dotty/tools/dotc/fromtasty/TastyFileUtil.scala @@ -15,7 +15,7 @@ object TastyFileUtil { * package foo * class Foo * ``` - * then `getClassName("./out/foo/Foo.tasty") returns `Some("./out")` + * then `getClassPath("./out/foo/Foo.tasty") returns `Some("./out")` */ def getClassPath(file: AbstractFile): Option[String] = getClassName(file).map { className => @@ -37,13 +37,11 @@ object TastyFileUtil { assert(file.extension == "tasty") val bytes = file.toByteArray val names = new TastyClassName(bytes).readName() - names.map { case (packageName, className) => - val fullName = packageName match { - case EMPTY_PACKAGE => s"${className.lastPart}" - case _ => s"$packageName.${className.lastPart}" - } - fullName - } + names.map: (packageName, className) => + if packageName == EMPTY_PACKAGE then + s"${className.lastPart.encode}" + else + s"${packageName.encode}.${className.lastPart.encode}" } } diff --git a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala index 94755ea41c69..d9180281932e 100644 --- a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala +++ b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala @@ -95,7 +95,10 @@ class InlineReducer(inliner: Inliner)(using Context): false } } - val idx = cls.asClass.paramAccessors.indexWhere(matches(_, tree.symbol)) + val accessors = + if (cls.asClass.is(Scala2x)) cls.asClass.paramAccessors.filter(_.is(Method)) + else cls.asClass.paramAccessors + val idx = accessors.indexWhere(matches(_, tree.symbol)) if (idx >= 0 && idx < args.length) { def finish(arg: Tree) = new TreeTypeMap().transform(arg) // make sure local bindings in argument have fresh symbols @@ -344,31 +347,25 @@ class InlineReducer(inliner: Inliner)(using Context): def reduceCase(cdef: CaseDef): MatchReduxWithGuard = { val caseBindingMap = new mutable.ListBuffer[(Symbol, MemberDef)]() - def substBindings( - bindings: List[(Symbol, MemberDef)], - bbuf: mutable.ListBuffer[MemberDef], - from: List[Symbol], to: List[Symbol]): (List[MemberDef], List[Symbol], List[Symbol]) = - bindings match { - case (sym, binding) :: rest => - bbuf += binding.subst(from, to).asInstanceOf[MemberDef] - if (sym.exists) substBindings(rest, bbuf, sym :: from, binding.symbol :: to) - else substBindings(rest, bbuf, from, to) - case Nil => (bbuf.toList, from, to) - } + def substBindings(bindings: List[(Symbol, MemberDef)]): (List[MemberDef], List[Symbol], List[Symbol]) = + val (from, to) = bindings.collect { case (sym, bnd) if sym.exists => (sym, bnd.symbol) }.unzip + to.foreach(sym => sym.info = sym.info.substSym(from, to)) + val substituted = bindings.map { case (sym, bnd) => bnd.subst(from, to) } + (substituted, from, to) if (!isImplicit) caseBindingMap += ((NoSymbol, scrutineeBinding)) val gadtCtx = ctx.fresh.setFreshGADTBounds.addMode(Mode.GadtConstraintInference) if (reducePattern(caseBindingMap, scrutineeSym.termRef, cdef.pat)(using gadtCtx)) { - val (caseBindings, from, to) = substBindings(caseBindingMap.toList, mutable.ListBuffer(), Nil, Nil) + val (caseBindings, from, to) = substBindings(caseBindingMap.toList) val (guardOK, canReduceGuard) = if cdef.guard.isEmpty then (true, true) else stripInlined(typer.typed(cdef.guard.subst(from, to), defn.BooleanType)) match { case ConstantValue(v: Boolean) => (v, true) case _ => (false, false) } - if guardOK then Some((caseBindings.map(_.subst(from, to)), cdef.body.subst(from, to), canReduceGuard)) + if guardOK then Some((caseBindings, cdef.body.subst(from, to), canReduceGuard)) else if canReduceGuard then None - else Some((caseBindings.map(_.subst(from, to)), cdef.body.subst(from, to), canReduceGuard)) + else Some((caseBindings, cdef.body.subst(from, to), canReduceGuard)) } else None } diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index d83817dd90e0..461b0263eae9 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -96,9 +96,19 @@ object Inliner: } end isElideableExpr + // InlineCopier is a more fault-tolerant copier that does not cause errors when + // function types in applications are undefined. This is necessary since we copy at + // the same time as establishing the proper context in which the copied tree should + // be evaluated. This matters for opaque types, see neg/i14653.scala. + private class InlineCopier() extends TypedTreeCopier: + override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(using Context): Apply = + if fun.tpe.widen.exists then super.Apply(tree)(fun, args) + else untpd.cpy.Apply(tree)(fun, args).withTypeUnchecked(tree.tpe) + // InlinerMap is a TreeTypeMap with special treatment for inlined arguments: // They are generally left alone (not mapped further, and if they wrap a type - // the type Inlined wrapper gets dropped + // the type Inlined wrapper gets dropped. + // As a side effect, register @nowarn annotations from annotated expressions. private class InlinerMap( typeMap: Type => Type, treeMap: Tree => Tree, @@ -107,13 +117,24 @@ object Inliner: substFrom: List[Symbol], substTo: List[Symbol])(using Context) extends TreeTypeMap( - typeMap, treeMap, oldOwners, newOwners, substFrom, substTo, - // It is necessary to use the `ConservativeTreeCopier` since we copy at - // the same time as establishing the proper context in which the copied - // tree should be evaluated. This matters for opaque types, see - // neg/i14653.scala. - ConservativeTreeCopier() - ): + typeMap, treeMap, oldOwners, newOwners, substFrom, substTo, InlineCopier()): + + override def transform(tree: Tree)(using Context): Tree = + tree match + case Typed(expr, tpt) => + def loop(tpe: Type): Unit = + tpe match + case AnnotatedType(parent, annot) => + if annot.hasSymbol(defn.NowarnAnnot) then + val argPos = annot.argument(0).getOrElse(tree).sourcePos + val conf = annot.argumentConstantString(0).getOrElse("") + ctx.run.nn.suppressions.registerNowarn(tree.sourcePos, expr.span)(conf, argPos) + else + loop(parent) + case _ => + loop(tpt.tpe) + case _ => + super.transform(tree) override def copy( typeMap: Type => Type, @@ -373,11 +394,26 @@ class Inliner(val call: tpd.Tree)(using Context): case (from, to) if from.symbol == ref.symbol && from =:= ref => to } + private def mapRefBack(ref: TermRef): Option[TermRef] = + opaqueProxies.collectFirst { + case (from, to) if to.symbol == ref.symbol && to =:= ref => from + } + /** If `tp` contains TermRefs that refer to objects with opaque * type aliases, add proxy definitions to `opaqueProxies` that expose these aliases. */ private def addOpaqueProxies(tp: Type, span: Span, forThisProxy: Boolean)(using Context): Unit = - tp.foreachPart { + val foreachTpPart = + (p: Type => Unit) => + if forThisProxy then + // Performs operations on all parts of this type, outside of the applied type arguments + new ForeachAccumulator(p, StopAt.None) { + override def apply(x: Unit, tp: Type) = tp match + case AppliedType(tycon, _) => super.apply(x, tycon) + case other => super.apply(x, other) + }.apply((), tp) + else tp.foreachPart(p) + foreachTpPart { case ref: TermRef => for cls <- ref.widen.baseClasses do if cls.containsOpaques @@ -407,6 +443,22 @@ class Inliner(val call: tpd.Tree)(using Context): } ) + /** Map back all TermRefs that match the right element in `opaqueProxies` to the + * corresponding left element. + * E.g. for a previously created + * `val proxy$1 = Time {type OpaqueInt = Int}` as part of the ongoing inlining + * a `List[proxy$1.OpaqueInt]` will be mapped back into a `List[Time.OpaqueInt]`. + */ + protected val mapBackToOpaques = TreeTypeMap( + typeMap = new TypeMap: + override def stopAt = StopAt.Package + def apply(t: Type) = mapOver { + t match + case ref: TermRef => mapRefBack(ref).getOrElse(ref) + case _ => t + } + ) + /** If `binding` contains TermRefs that refer to objects with opaque * type aliases, add proxy definitions that expose these aliases * and substitute such TermRefs with theproxies. Example from pos/opaque-inline1.scala: @@ -456,6 +508,48 @@ class Inliner(val call: tpd.Tree)(using Context): private def adaptToPrefix(tp: Type) = tp.asSeenFrom(inlineCallPrefix.tpe, inlinedMethod.owner) + def thisTypeProxyExists = !thisProxy.isEmpty + + /** Maps a type that includes a thisProxy (e.g. `TermRef(NoPrefix,val Foo$_this)`) + * by reading the defTree belonging to that thisProxy (`val Foo$_this: Foo.type = AnotherProxy`) + * back into its original reference (`AnotherProxy`, which is directly or indirectly a refinement on `Foo`) + * + * Usually when we end up with another proxy like this, we will be able to further unwrap it back + * into `Foo` with mapBackToOpaques, but, for nested transparent inline calls, `AnotherProxy` will be + * a proxy created by inlining the outer calls, that we might not be able to further unwrap this way + * (as those proxies will not be a part of opaqueProxies created during this inlining). + * We leave that as it is and treat this behavior as intended (see documentation with an example in + * `Opaque Types in Transparent Inline Methods` section in `opaques-details.md`), + * as we might need those opaques to have visible right hand sides for successful + * typechecking of the outer inline call. + */ + val thisTypeUnpacker = + TreeTypeMap( + typeMap = new TypeMap: + override def stopAt = StopAt.Package + def apply(t: Type) = mapOver { + t match + case a: TermRef if thisProxy.values.exists(_ == a) => + a.termSymbol.defTree match + case untpd.ValDef(a, tpt, _) => tpt.tpe + case _ => t + } + ) + + /** Returns the result type of the Inlined code block after removing thisProxy and opaqueProxy TermRefs. + * E.g. for an Inlined tree returning Block of type `Option[Foo$_this.OpaqueInt]`, + * and for proxies: + * ``` + * val $proxy1: Foo.type{type OpaqueInt = Int} = = ... + * val Foo$_this: ($proxy1 : Foo.type{type OpaqueInt = Int}) = ... + * ``` + * the method will return: `Foo.OpaqueInt` + */ + def unpackProxiesFromResultType(inlined: Inlined): Type = + if thisTypeProxyExists then mapBackToOpaques.typeMap(thisTypeUnpacker.typeMap(inlined.expansion.tpe)) + else inlined.tpe + + /** Populate `thisProxy` and `paramProxy` as follows: * * 1a. If given type refers to a static this, thisProxy binds it to corresponding global reference, @@ -652,7 +746,7 @@ class Inliner(val call: tpd.Tree)(using Context): // call. This way, a defensively written rewrite method can always // report bad inputs at the point of call instead of revealing its internals. val callToReport = if (enclosingInlineds.nonEmpty) enclosingInlineds.last else call - val ctxToReport = ctx.outersIterator.dropWhile(enclosingInlineds(using _).nonEmpty).next + val ctxToReport = ctx.outersIterator.dropWhile(enclosingInlineds(using _).nonEmpty).next() // The context in which we report should still use the existing context reporter val ctxOrigReporter = ctxToReport.fresh.setReporter(ctx.reporter) inContext(ctxOrigReporter) { @@ -873,53 +967,82 @@ class Inliner(val call: tpd.Tree)(using Context): if (!tree.isInline || ctx.owner.isInlineMethod) // don't reduce match of nested inline method yet super.typedMatchFinish(tree, sel, wideSelType, cases, pt) else { - def selTyped(sel: Tree): Type = sel match { + def selTyped(sel: Tree): Tree = sel match { case Typed(sel2, _) => selTyped(sel2) - case Block(Nil, sel2) => selTyped(sel2) - case Inlined(_, Nil, sel2) => selTyped(sel2) - case _ => sel.tpe + case block @ Block(Nil, sel2) => tpd.cpy.Block(block)(Nil, selTyped(sel2)) + case inlined @ Inlined(_, Nil, sel2) => tpd.cpy.Inlined(inlined)(inlined.call, inlined.bindings, selTyped(sel2)) + case _ => sel + } + val (retypedSel, selType) = + if (sel.isEmpty) (sel, wideSelType) + else + val retypedSel = selTyped(sel) + (retypedSel, retypedSel.tpe) + + /** Make an Inlined that has no bindings. */ + def flattenInlineBlock(tree: Tree): Tree = { + def inlineBlock(call: Tree, stats: List[Tree], expr: Tree): Block = + def inlinedTree(tree: Tree) = Inlined(call, Nil, tree).withSpan(tree.span) + val stats1 = stats.map: + case stat: ValDef => cpy.ValDef(stat)(rhs = inlinedTree(stat.rhs)) + case stat: DefDef => cpy.DefDef(stat)(rhs = inlinedTree(stat.rhs)) + case stat => inlinedTree(stat) + cpy.Block(tree)(stats1, flattenInlineBlock(inlinedTree(expr))) + + tree match + case tree @ Inlined(call, bindings, expr) if !bindings.isEmpty => + inlineBlock(call, bindings, expr) + case tree @ Inlined(call, Nil, Block(stats, expr)) => + inlineBlock(call, stats, expr) + case _ => + tree } - val selType = if (sel.isEmpty) wideSelType else selTyped(sel) - reduceInlineMatch(sel, selType, cases.asInstanceOf[List[CaseDef]], this) match { - case Some((caseBindings, rhs0)) => - // drop type ascriptions/casts hiding pattern-bound types (which are now aliases after reducing the match) - // note that any actually necessary casts will be reinserted by the typing pass below - val rhs1 = rhs0 match { - case Block(stats, t) if t.span.isSynthetic => - t match { - case Typed(expr, _) => - Block(stats, expr) - case TypeApply(sel@Select(expr, _), _) if sel.symbol.isTypeCast => - Block(stats, expr) - case _ => - rhs0 + + def reduceInlineMatchExpr(sel: Tree): Tree = flattenInlineBlock(sel) match + case Block(stats, expr) => + cpy.Block(sel)(stats, reduceInlineMatchExpr(expr)) + case _ => + reduceInlineMatch(sel, selType, cases.asInstanceOf[List[CaseDef]], this) match { + case Some((caseBindings, rhs0)) => + // drop type ascriptions/casts hiding pattern-bound types (which are now aliases after reducing the match) + // note that any actually necessary casts will be reinserted by the typing pass below + val rhs1 = rhs0 match { + case Block(stats, t) if t.span.isSynthetic => + t match { + case Typed(expr, _) => + Block(stats, expr) + case TypeApply(sel@Select(expr, _), _) if sel.symbol.isTypeCast => + Block(stats, expr) + case _ => + rhs0 + } + case _ => rhs0 } - case _ => rhs0 - } - val rhs2 = rhs1 match { - case Typed(expr, tpt) if rhs1.span.isSynthetic => constToLiteral(expr) - case _ => constToLiteral(rhs1) + val rhs2 = rhs1 match { + case Typed(expr, tpt) if rhs1.span.isSynthetic => constToLiteral(expr) + case _ => constToLiteral(rhs1) + } + val (usedBindings, rhs3) = dropUnusedDefs(caseBindings, rhs2) + val rhs = seq(usedBindings, rhs3) + inlining.println(i"""--- reduce: + |$tree + |--- to: + |$rhs""") + typedExpr(rhs, pt) + case None => + def guardStr(guard: untpd.Tree) = if (guard.isEmpty) "" else i" if $guard" + def patStr(cdef: untpd.CaseDef) = i"case ${cdef.pat}${guardStr(cdef.guard)}" + val msg = + if (tree.selector.isEmpty) + em"""cannot reduce summonFrom with + | patterns : ${tree.cases.map(patStr).mkString("\n ")}""" + else + em"""cannot reduce inline match with + | scrutinee: $sel : ${selType} + | patterns : ${tree.cases.map(patStr).mkString("\n ")}""" + errorTree(tree, msg) } - val (usedBindings, rhs3) = dropUnusedDefs(caseBindings, rhs2) - val rhs = seq(usedBindings, rhs3) - inlining.println(i"""--- reduce: - |$tree - |--- to: - |$rhs""") - typedExpr(rhs, pt) - case None => - def guardStr(guard: untpd.Tree) = if (guard.isEmpty) "" else i" if $guard" - def patStr(cdef: untpd.CaseDef) = i"case ${cdef.pat}${guardStr(cdef.guard)}" - val msg = - if (tree.selector.isEmpty) - em"""cannot reduce summonFrom with - | patterns : ${tree.cases.map(patStr).mkString("\n ")}""" - else - em"""cannot reduce inline match with - | scrutinee: $sel : ${selType} - | patterns : ${tree.cases.map(patStr).mkString("\n ")}""" - errorTree(tree, msg) - } + reduceInlineMatchExpr(retypedSel) } override def newLikeThis(nestingLevel: Int): Typer = new InlineTyper(initialErrorCount, nestingLevel) @@ -1067,9 +1190,9 @@ class Inliner(val call: tpd.Tree)(using Context): if suspendable then ctx.compilationUnit.suspend() // this throws a SuspendException - val evaluatedSplice = inContext(quoted.MacroExpansion.context(inlinedFrom)) { - Splicer.splice(body, splicePos, inlinedFrom.srcPos, MacroClassLoader.fromContext) - } + val evaluatedSplice = + inContext(quoted.MacroExpansion.context(inlinedFrom)): + Splicer.splice(body, splicePos, inlinedFrom.srcPos, MacroClassLoader.fromContext) val inlinedNormalizer = new TreeMap { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match { case tree @ Inlined(_, Nil, expr) if tree.inlinedFromOuterScope && enclosingInlineds.isEmpty => transform(expr) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index e9ac2b92a488..5f94050b6cf5 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -23,6 +23,7 @@ import collection.mutable import reporting.{NotConstant, trace} import util.Spans.Span import dotty.tools.dotc.core.Periods.PhaseId +import dotty.tools.dotc.util.chaining.* /** Support for querying inlineable methods and for inlining calls to such methods */ object Inlines: @@ -44,6 +45,11 @@ object Inlines: def bodyToInline(sym: SymDenotation)(using Context): Tree = if hasBodyToInline(sym) then sym.getAnnotation(defn.BodyAnnot).get.tree + .tap: body => + for annot <- sym.getAnnotation(defn.NowarnAnnot) do + val argPos = annot.argument(0).getOrElse(annot.tree).sourcePos + val conf = annot.argumentConstantString(0).getOrElse("") + ctx.run.nn.suppressions.registerNowarn(annot.tree.sourcePos, body.span)(conf, argPos) else EmptyTree @@ -91,11 +97,10 @@ object Inlines: * inline depth is exceeded. * * @param tree The call to inline - * @param pt The expected type of the call. * @return An `Inlined` node that refers to the original call and the inlined bindings * and body that replace it. */ - def inlineCall(tree: Tree)(using Context): Tree = + def inlineCall(tree: Tree)(using Context): Tree = ctx.profiler.onInlineCall(tree.symbol): if tree.symbol.denot != SymDenotations.NoDenotation && tree.symbol.effectiveOwner == defn.CompiletimeTestingPackage.moduleClass then @@ -387,6 +392,11 @@ object Inlines: case ConstantType(Constant(code: String)) => val unitName = "tasty-reflect" val source2 = SourceFile.virtual(unitName, code) + def compilationUnits(untpdTree: untpd.Tree, tpdTree: Tree): List[CompilationUnit] = + val compilationUnit = CompilationUnit(unitName, code) + compilationUnit.tpdTree = tpdTree + compilationUnit.untpdTree = untpdTree + List(compilationUnit) // We need a dummy owner, as the actual one does not have a computed denotation yet, // but might be inspected in a transform phase, leading to cyclic errors val dummyOwner = newSymbol(ctx.owner, "$dummySymbol$".toTermName, Private, defn.AnyType, NoSymbol) @@ -394,31 +404,30 @@ object Inlines: ctx.fresh.setNewTyperState().setTyper(new Typer(ctx.nestingLevel + 1)).setSource(source2) inContext(newContext) { - val tree2 = new Parser(source2).block() - if ctx.reporter.allErrors.nonEmpty then + def noErrors = ctx.reporter.allErrors.isEmpty + val untpdTree = new Parser(source2).block() + if !noErrors then ctx.reporter.allErrors.map((ErrorKind.Parser, _)) else - val tree3 = ctx.typer.typed(tree2) + val tpdTree1 = ctx.typer.typed(untpdTree) ctx.base.postTyperPhase match - case postTyper: PostTyper if ctx.reporter.allErrors.isEmpty => - val tree4 = atPhase(postTyper) { postTyper.newTransformer.transform(tree3) } + case postTyper: PostTyper if noErrors => + val tpdTree2 = + atPhase(postTyper) { postTyper.runOn(compilationUnits(untpdTree, tpdTree1)).head.tpdTree } ctx.base.setRootTreePhase match - case setRootTree => - val tree5 = - val compilationUnit = CompilationUnit(unitName, code) - compilationUnit.tpdTree = tree4 - compilationUnit.untpdTree = tree2 - var units = List(compilationUnit) - atPhase(setRootTree)(setRootTree.runOn(units).head.tpdTree) + case setRootTree if noErrors => // might be noPhase, if -Yretain-trees is not used + val tpdTree3 = + atPhase(setRootTree)(setRootTree.runOn(compilationUnits(untpdTree, tpdTree2)).head.tpdTree) ctx.base.inliningPhase match - case inlining: Inlining if ctx.reporter.allErrors.isEmpty => - val tree6 = atPhase(inlining) { inlining.newTransformer.transform(tree5) } - if ctx.reporter.allErrors.isEmpty && reconstructedTransformPhases.nonEmpty then - var transformTree = tree6 + case inlining: Inlining if noErrors => + val tpdTree4 = atPhase(inlining) { inlining.newTransformer.transform(tpdTree3) } + if noErrors && reconstructedTransformPhases.nonEmpty then + var transformTree = tpdTree4 for phase <- reconstructedTransformPhases do - if ctx.reporter.allErrors.isEmpty then + if noErrors then transformTree = atPhase(phase.end + 1)(phase.transformUnit(transformTree)) case _ => + case _ => case _ => ctx.reporter.allErrors.map((ErrorKind.Typer, _)) } @@ -522,16 +531,63 @@ object Inlines: // different for bindings from arguments and bindings from body. val inlined = tpd.Inlined(call, bindings, expansion) - if !hasOpaqueProxies then inlined + val hasOpaquesInResultFromCallWithTransparentContext = + val owners = call.symbol.ownersIterator.toSet + call.tpe.widenTermRefExpr.existsPart( + part => part.typeSymbol.is(Opaque) && owners.contains(part.typeSymbol.owner) + ) + + /** Remap ThisType nodes that are incorrect in the inlined context. + * Incorrect ThisType nodes can cause unwanted opaque type dealiasing later. + * E.g. if inlined in a `.Foo` package (but outside of .Foo.Bar object) we will map + * `TermRef(ThisType(TypeRef(ThisType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object Foo),Bar$)),MyOpaque$)),one)` + * into + * `TermRef(TermRef(TermRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object Foo),object Bar),object MyOpaque),val one)` + * See test i13461-d + */ + def fixThisTypeModuleClassReferences(tpe: Type): Type = + val owners = ctx.owner.ownersIterator.toSet + TreeTypeMap( + typeMap = new TypeMap: + override def stopAt = StopAt.Package + def apply(t: Type) = + t match + case ThisType(tref @ TypeRef(prefix, _)) if tref.symbol.flags.is(Module) && !owners.contains(tref.symbol) => + TermRef(apply(prefix), tref.symbol.companionModule) + case _ => mapOver(t) + ).typeMap(tpe) + + if !hasOpaqueProxies && !hasOpaquesInResultFromCallWithTransparentContext then inlined else - val target = - if inlinedMethod.is(Transparent) then call.tpe & inlined.tpe - else call.tpe - inlined.ensureConforms(target) + val (target, forceCast) = + if inlinedMethod.is(Transparent) then + val unpacked = unpackProxiesFromResultType(inlined) + val withAdjustedThisTypes = if call.symbol.is(Macro) then fixThisTypeModuleClassReferences(unpacked) else unpacked + (call.tpe & withAdjustedThisTypes, withAdjustedThisTypes != unpacked) + else (call.tpe, false) + // `target` might contain a method reference, which is an invalid cast target. Use its return type instead. + // see https://github.com/scala/scala3/issues/25091 + val resultType = target.widenIfUnstable + if forceCast then + // we need to force the cast for issues with ThisTypes, as ensureConforms will just + // check subtyping and then choose not to cast, leaving the previous, incorrect type + inlined.cast(resultType) + else if !(inlined.tpe <:< target) then // Make sure that the sealing with the declared type // is type correct. Without it we might get problems since the // expression's type is the opaque alias but the call's type is // the opaque type itself. An example is in pos/opaque-inline1.scala. + // + // Here we can't just use `inlined.ensureConforms(resultType)`: + // `target.widenIfUnstable` is an upper approximation of `target`, + // so a tree may conform to it while still not conforming to `target`. + // This can happen when widening drops path-/prefix-sensitive information + // (e.g. projected opaque-proxy types). + // We check conformance against the original `target`, but cast to the + // widened type to avoid NoType issues at erasure (see #25091, #25417). + inlined.cast(resultType) + else + inlined end expand end InlineCall end Inlines diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 0b5c72bd997f..9cba859235cc 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -61,16 +61,26 @@ object PrepareInlineable { * and it is not part of the tree that gets inlined. The latter test is implemented * by excluding all symbols properly contained in the inline method. * + * Protected definitions do not need an accessor if they refer to symbols + * inherited by a class defined inside of the inline method. + * * Constant vals don't need accessors since they are inlined in FirstTransform. * Inline methods don't need accessors since they are inlined in Typer. * * When creating accessors for staged/quoted code we only need to create accessors * for the code that is staged. This excludes code at level 0 (except if it is inlined). */ - def needsAccessor(sym: Symbol)(using Context): Boolean = + def needsAccessor(refTree: RefTree | TypeApply | Apply)(using Context): Boolean = + def referencesInlineContainedClassDef(tree: Tree): Boolean = tree match + case Apply(qual, _) => referencesInlineContainedClassDef(qual) + case TypeApply(qual, _) => referencesInlineContainedClassDef(qual) + case Select(qual, _) => qual.tpe.isInstanceOf[ThisType] && qual.symbol.isContainedIn(inlineSym) + case _ => false + val sym = refTree.symbol sym.isTerm && (sym.isOneOf(AccessFlags) || sym.privateWithin.exists) && !sym.isContainedIn(inlineSym) && + !(sym.is(Protected) && referencesInlineContainedClassDef(refTree)) && !(sym.isStableMember && sym.info.widenTermRefExpr.isInstanceOf[ConstantType]) && !sym.isInlineMethod && (Inlines.inInlineMethod || StagingLevel.level > 0) @@ -95,7 +105,7 @@ object PrepareInlineable { */ class MakeInlineableDirect(inlineSym: Symbol) extends MakeInlineableMap(inlineSym) { def preTransform(tree: Tree)(using Context): Tree = tree match { - case tree: RefTree if needsAccessor(tree.symbol) => + case tree: RefTree if needsAccessor(tree) => if (tree.symbol.isConstructor) { report.error("Implementation restriction: cannot use private constructors in inline methods", tree.srcPos) tree // TODO: create a proper accessor for the private constructor @@ -139,8 +149,8 @@ object PrepareInlineable { class MakeInlineablePassing(inlineSym: Symbol) extends MakeInlineableMap(inlineSym) { def preTransform(tree: Tree)(using Context): Tree = tree match { - case _: Apply | _: TypeApply | _: RefTree - if needsAccessor(tree.symbol) && tree.isTerm && !tree.symbol.isConstructor => + case tree: (Apply | TypeApply | RefTree) + if needsAccessor(tree) && tree.isTerm && !tree.symbol.isConstructor => val refPart = funPart(tree) val argss = allArgss(tree) val qual = qualifier(refPart) diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index af27a4006140..8579576e8571 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -33,6 +33,8 @@ import dotty.tools.dotc.core.Types import dotty.tools.dotc.core.Symbols import dotty.tools.dotc.core.Constants +import java.util.logging.Logger + /** * One of the results of a completion query. * @@ -46,6 +48,15 @@ case class Completion(label: String, description: String, symbols: List[Symbol]) object Completion: + private val logger = Logger.getLogger(this.getClass.getName) + + def scopeContext(pos: SourcePosition, tpdPath: List[tpd.Tree], completionContext: Context)(using Context): CompletionResult = + inContext(completionContext): + val untpdPath = Interactive.resolveTypedOrUntypedPath(tpdPath, pos) + // Lazy mode is to avoid too many checks as it's mostly for printing types + val completer = new Completer(Mode.Lazy, pos, untpdPath, _ => true) + completer.scopeCompletions + /** Get possible completions from tree at `pos` * * @return offset and list of symbols for possible completions @@ -58,7 +69,6 @@ object Completion: val mode = completionMode(untpdPath, pos) val rawPrefix = completionPrefix(untpdPath, pos) val completions = rawCompletions(pos, mode, rawPrefix, tpdPath, untpdPath) - postProcessCompletions(untpdPath, completions, rawPrefix) /** Get possible completions from tree at `pos` @@ -72,10 +82,11 @@ object Completion: rawPrefix: String, tpdPath: List[tpd.Tree], untpdPath: List[untpd.Tree], - customMatcher: Option[Name => Boolean] = None + customMatcher: Option[Name => Boolean] = None, + calculatedScopeContext: Option[CompletionResult] = None )(using Context): CompletionMap = val adjustedPath = typeCheckExtensionConstructPath(untpdPath, tpdPath, pos) - computeCompletions(pos, mode, rawPrefix, adjustedPath, untpdPath, customMatcher) + computeCompletions(pos, mode, rawPrefix, adjustedPath, untpdPath, customMatcher, calculatedScopeContext) /** * Inspect `path` to determine what kinds of symbols should be considered. @@ -88,19 +99,21 @@ object Completion: * Otherwise, provide no completion suggestion. */ def completionMode(path: List[untpd.Tree], pos: SourcePosition): Mode = path match + // Ignore `package foo@@` and `package foo.bar@@` + case ((_: tpd.Select) | (_: tpd.Ident)):: (_ : tpd.PackageDef) :: _ => Mode.None case GenericImportSelector(sel) => if sel.imported.span.contains(pos.span) then Mode.ImportOrExport // import scala.@@ else if sel.isGiven && sel.bound.span.contains(pos.span) then Mode.ImportOrExport else Mode.None // import scala.{util => u@@} case GenericImportOrExport(_) => Mode.ImportOrExport | Mode.Scope // import TrieMa@@ + case untpd.InterpolatedString(_, untpd.Literal(Constants.Constant(_: String)) :: _) :: _ => + Mode.Term | Mode.Scope case untpd.Literal(Constants.Constant(_: String)) :: _ => Mode.Term | Mode.Scope // literal completions case (ref: untpd.RefTree) :: _ => val maybeSelectMembers = if ref.isInstanceOf[untpd.Select] then Mode.Member else Mode.Scope - if (ref.name.isTermName) Mode.Term | maybeSelectMembers else if (ref.name.isTypeName) Mode.Type | maybeSelectMembers else Mode.None - case _ => Mode.None /** When dealing with in varios palces we check to see if they are @@ -145,11 +158,12 @@ object Completion: checkBacktickPrefix(ident.source.content(), ident.span.start, ident.span.end) case (tree: untpd.RefTree) :: _ if tree.name != nme.ERROR => - tree.name.toString.take(pos.span.point - tree.span.point) - - case _ => naiveCompletionPrefix(pos.source.content().mkString, pos.point) - + val nameStart = tree.span.point + val start = if pos.source.content().lift(nameStart).contains('`') then nameStart + 1 else nameStart + tree.name.toString.take(pos.span.point - start) + case _ => + naiveCompletionPrefix(pos.source.content().mkString, pos.point) end completionPrefix private object GenericImportSelector: @@ -166,6 +180,14 @@ object Completion: case (importOrExport: untpd.ImportOrExport) :: _ => Some(importOrExport) case _ => None + private object StringContextApplication: + def unapply(path: List[tpd.Tree]): Option[tpd.Apply] = + path match + case tpd.Select(qual @ tpd.Apply(tpd.Select(tpd.Select(_, nme.StringContext), _), _), _) :: _ => + Some(qual) + case _ => None + + /** Inspect `path` to determine the offset where the completion result should be inserted. */ def completionOffset(untpdPath: List[untpd.Tree]): Int = untpdPath match @@ -204,21 +226,37 @@ object Completion: mode: Mode, rawPrefix: String, adjustedPath: List[tpd.Tree], untpdPath: List[untpd.Tree], - matches: Option[Name => Boolean] - )(using Context): CompletionMap = + matches: Option[Name => Boolean], + calculatedScopeContext: Option[CompletionResult] + )(using ctx: Context): CompletionMap = val hasBackTick = rawPrefix.headOption.contains('`') val prefix = if hasBackTick then rawPrefix.drop(1) else rawPrefix val matches0 = matches.getOrElse(_.startsWith(prefix)) - val completer = new Completer(mode, pos, untpdPath, matches0) + lazy val completer = new Completer(mode, pos, untpdPath, matches0) + lazy val scopeContextNames = calculatedScopeContext match + case Some(scopeContext) => + val isNew = isInNewContext(untpdPath) + scopeContext.names.flatMap { + case (name, CompletionDenotation(denots, site)) if matches0(name) => + def isAccessible(denot: SingleDenotation): Boolean = + site.map(denot.symbol.isAccessibleFrom(_)).getOrElse(true) + val filtered = denots.filter(denot => + isValidCompletionSymbol(denot.symbol, mode, isNew) && isAccessible(denot) + ) + if filtered.nonEmpty then Some(name -> filtered) else None + case _ => None + }.toMap + case None => completer.scopeCompletions.names.map((name, denot) => name -> denot.denots) val result = adjustedPath match // Ignore synthetic select from `This` because in code it was `Ident` // See example in dotty.tools.languageserver.CompletionTest.syntheticThis - case tpd.Select(qual @ tpd.This(_), _) :: _ if qual.span.isSynthetic => completer.scopeCompletions - case tpd.Select(qual, _) :: _ if qual.typeOpt.hasSimpleKind => completer.selectionCompletions(qual) - case tpd.Select(qual, _) :: _ => Map.empty + case tpd.Select(qual @ tpd.This(_), _) :: _ if qual.span.isSynthetic => scopeContextNames + case StringContextApplication(qual) => + scopeContextNames ++ completer.selectionCompletions(qual) + case tpd.Select(qual, _) :: _ => completer.selectionCompletions(qual) case (tree: tpd.ImportOrExport) :: _ => completer.directMemberCompletions(tree.expr) - case _ => completer.scopeCompletions + case _ => scopeContextNames interactiv.println(i"""completion info with pos = $pos, | term = ${completer.mode.is(Mode.Term)}, @@ -281,8 +319,10 @@ object Completion: Completion(name.show, description(denot), List(denot.symbol)) def description(denot: SingleDenotation)(using Context): String = - if denot.isType then denot.symbol.showFullName - else denot.info.widenTermRefExpr.show + try + if denot.isType then denot.symbol.showFullName + else denot.info.widenTermRefExpr.show + catch case NonFatal(_) => denot.symbol.name.toString def isInNewContext(untpdPath: List[untpd.Tree]): Boolean = untpdPath match @@ -300,17 +340,17 @@ object Completion: * 8. symbol is not a constructor proxy module when in type completion mode * 9. have same term/type kind as name prefix given so far */ - def isValidCompletionSymbol(sym: Symbol, completionMode: Mode, isNew: Boolean)(using Context): Boolean = - + def isValidCompletionSymbol(sym: Symbol, completionMode: Mode, isNew: Boolean)(using Context): Boolean = try lazy val isEnum = sym.is(Enum) || (sym.companionClass.exists && sym.companionClass.is(Enum)) sym.exists && - !sym.isAbsent() && + !sym.isAbsent(canForce = false) && !sym.isPrimaryConstructor && - sym.sourceSymbol.exists && + // running sourceSymbol on ExportedTerm will force a lot of computation from collectSubTrees + (sym.is(ExportedTerm) || sym.sourceSymbol.exists) && (!sym.is(Package) || sym.is(ModuleClass)) && - !sym.isAllOf(Mutable | Accessor) && + !(sym.is(Mutable) && sym.is(Accessor)) && !sym.isPackageObject && !sym.is(Artifact) && !(completionMode.is(Mode.Type) && sym.isAllOf(ConstructorProxyModule)) && @@ -319,6 +359,10 @@ object Completion: (completionMode.is(Mode.Term) && (sym.isTerm || sym.is(ModuleClass)) || (completionMode.is(Mode.Type) && (sym.isType || sym.isStableMember))) ) + catch + case NonFatal(ex) => + false + end isValidCompletionSymbol given ScopeOrdering(using Context): Ordering[Seq[SingleDenotation]] with val order = @@ -338,7 +382,7 @@ object Completion: * For the results of all `xyzCompletions` methods term names and type names are always treated as different keys in the same map * and they never conflict with each other. */ - class Completer(val mode: Mode, pos: SourcePosition, untpdPath: List[untpd.Tree], matches: Name => Boolean): + class Completer(val mode: Mode, pos: SourcePosition, untpdPath: List[untpd.Tree], matches: Name => Boolean)(using Context): /** Completions for terms and types that are currently in scope: * the members of the current class, local definitions and the symbols that have been imported, * recursively adding completions from outer scopes. @@ -352,39 +396,44 @@ object Completion: * (even if the import follows it syntactically) * - a more deeply nested import shadowing a member or a local definition causes an ambiguity */ - def scopeCompletions(using context: Context): CompletionMap = + lazy val scopeCompletions: CompletionResult = /** Temporary data structure representing denotations with the same name introduced in a given scope * as a member of a type, by a local definition or by an import clause */ - case class ScopedDenotations private (denots: Seq[SingleDenotation], ctx: Context) + case class ScopedDenotations private (denot: CompletionDenotation, ctx: Context) object ScopedDenotations: - def apply(denots: Seq[SingleDenotation], ctx: Context, includeFn: SingleDenotation => Boolean): ScopedDenotations = - ScopedDenotations(denots.filter(includeFn), ctx) + def apply(denot: CompletionDenotation, ctx: Context, includeFn: SingleDenotation => Boolean): ScopedDenotations = + ScopedDenotations(CompletionDenotation(denot.denots.filter(includeFn), denot.site), ctx) val mappings = collection.mutable.Map.empty[Name, List[ScopedDenotations]].withDefaultValue(List.empty) + val renames = collection.mutable.Map.empty[Symbol, Name] def addMapping(name: Name, denots: ScopedDenotations) = mappings(name) = mappings(name) :+ denots ctx.outersIterator.foreach { case ctx @ given Context => if ctx.isImportContext then - importedCompletions.foreach { (name, denots) => - addMapping(name, ScopedDenotations(denots, ctx, include(_, name))) + val imported = importedCompletions + imported.names.foreach { (name, denot) => + addMapping(name, ScopedDenotations(denot, ctx, include(_, name))) + } + imported.renames.foreach { (name, newName) => + renames(name) = newName } else if ctx.owner.isClass then accessibleMembers(ctx.owner.thisType) .groupByName.foreach { (name, denots) => - addMapping(name, ScopedDenotations(denots, ctx, include(_, name))) + addMapping(name, ScopedDenotations(CompletionDenotation(denots, Some(ctx.owner.thisType)), ctx, include(_, name))) } else if ctx.scope ne EmptyScope then ctx.scope.toList.filter(symbol => include(symbol, symbol.name)) .flatMap(_.alternatives) .groupByName.foreach { (name, denots) => - addMapping(name, ScopedDenotations(denots, ctx, include(_, name))) + addMapping(name, ScopedDenotations(CompletionDenotation(denots, None), ctx, include(_, name))) } } - var resultMappings = Map.empty[Name, Seq[SingleDenotation]] + var resultMappings = Map.empty[Name, CompletionDenotation] mappings.foreach { (name, denotss) => val first = denotss.head @@ -396,7 +445,7 @@ object Completion: def isImportedInDifferentScope = first.ctx.scope ne denotss(1).ctx.scope // import a.C // import a.C - def isSameSymbolImportedDouble = denotss.forall(_.denots == first.denots) + def isSameSymbolImportedDouble = denotss.forall(_.denot.denots == first.denot.denots) // https://scala-lang.org/files/archive/spec/3.4/02-identifiers-names-and-scopes.html // import java.lang.* @@ -408,23 +457,22 @@ object Completion: // } // } def notConflictingWithDefaults = // is imported symbol - denotss.filterNot(_.denots.exists(denot => Interactive.isImportedByDefault(denot.symbol))).size <= 1 + denotss.filterNot(_.denot.denots.exists(denot => Interactive.isImportedByDefault(denot.symbol))).size <= 1 denotss.find(!_.ctx.isImportContext) match { // most deeply nested member or local definition if not shadowed by an import case Some(local) if local.ctx.scope == first.ctx.scope => - resultMappings += name -> local.denots - + resultMappings += name -> local.denot case None if isSingleImport || isImportedInDifferentScope || isSameSymbolImportedDouble => - resultMappings += name -> first.denots + resultMappings += name -> first.denot case None if notConflictingWithDefaults => - val ordered = denotss.map(_.denots).sorted + val ordered = denotss.map(_.denot).sortBy(_.denots) resultMappings += name -> ordered.head case _ => } } - resultMappings + CompletionResult(resultMappings, renames.toMap) end scopeCompletions /** Widen only those types which are applied or are exactly nothing @@ -442,9 +490,15 @@ object Completion: def selectionCompletions(qual: tpd.Tree)(using Context): CompletionMap = val adjustedQual = widenQualifier(qual) - implicitConversionMemberCompletions(adjustedQual) ++ + if qual.symbol.is(Package) then + directMemberCompletions(adjustedQual) + else if qual.typeOpt.hasSimpleKind then + implicitConversionMemberCompletions(adjustedQual) ++ extensionCompletions(adjustedQual) ++ directMemberCompletions(adjustedQual) + else + Map.empty + /** Completions for members of `qual`'s type. * These include inherited definitions but not members added by extensions or implicit conversions @@ -458,15 +512,20 @@ object Completion: /** Completions introduced by imports directly in this context. * Completions from outer contexts are not included. */ - private def importedCompletions(using Context): CompletionMap = + private def importedCompletions(using Context): CompletionResult = val imp = ctx.importInfo + val renames = collection.mutable.Map.empty[Symbol, Name] if imp == null then - Map.empty + CompletionResult(Map.empty, Map.empty) else def fromImport(name: Name, nameInScope: Name): Seq[(Name, SingleDenotation)] = imp.site.member(name).alternatives - .collect { case denot if include(denot, nameInScope) => nameInScope -> denot } + .collect { case denot if include(denot, nameInScope) => + if name != nameInScope then + renames(denot.symbol) = nameInScope + nameInScope -> denot + } val givenImports = imp.importedImplicits .map { ref => (ref.implicitName: Name, ref.underlyingRef.denot.asSingleDenotation) } @@ -492,7 +551,8 @@ object Completion: fromImport(original.toTypeName, nameInScope.toTypeName) }.toSeq.groupByName - givenImports ++ wildcardMembers ++ explicitMembers + val results = givenImports ++ wildcardMembers ++ explicitMembers + CompletionResult(results.map((name, denots) => name -> CompletionDenotation(denots, Some(imp.site))), renames.toMap) end importedCompletions /** Completions from implicit conversions including old style extensions using implicit classes */ @@ -522,12 +582,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.nn.warning( + s"Exception when trying to apply extension method:\n ${ex.getMessage()}\n${ex.getStackTrace().nn.mkString("\n")}" + ) + None def extractMemberExtensionMethods(types: Seq[Type]): Seq[(TermRef, TermName)] = object DenotWithMatchingName: @@ -545,10 +612,10 @@ object Completion: // There are four possible ways for an extension method to be applicable // 1. The extension method is visible under a simple name, by being defined or inherited or imported in a scope enclosing the reference. - val termCompleter = new Completer(Mode.Term, pos, untpdPath, matches) - val extMethodsInScope = termCompleter.scopeCompletions.toList.flatMap: - case (name, denots) => denots.collect: - case d: SymDenotation if d.isTerm && d.termRef.symbol.is(Extension) => (d.termRef, name.asTermName) + val extMethodsInScope = scopeCompletions.names.toList.flatMap: + case (name, denot) => + denot.denots.collect: + case d if d.isTerm && d.symbol.is(Extension) => (d.symbol.termRef, name.asTermName) // 2. The extension method is a member of some given instance that is visible at the point of the reference. val givensInScope = ctx.implicits.eligible(defn.AnyType).map(_.implicitRef.underlyingRef) @@ -581,7 +648,7 @@ object Completion: private def include(denot: SingleDenotation, nameInScope: Name)(using Context): Boolean = matches(nameInScope) && completionsFilter(NoType, nameInScope) && - isValidCompletionSymbol(denot.symbol, mode, isNew) + (mode.is(Mode.Lazy) || isValidCompletionSymbol(denot.symbol, mode, isNew)) private def extractRefinements(site: Type)(using Context): Seq[SingleDenotation] = site match @@ -611,7 +678,7 @@ object Completion: val members = site.memberDenots(completionsFilter, appendMemberSyms).collect { case mbr if include(mbr, mbr.name) - && mbr.symbol.isAccessibleFrom(site) => mbr + && (mode.is(Mode.Lazy) || mbr.symbol.isAccessibleFrom(site)) => mbr } val refinements = extractRefinements(site).filter(mbr => include(mbr, mbr.name)) @@ -625,18 +692,22 @@ 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).allImplicits interactiv.println(i"implicit conversion targets considered: ${conversions.toList}%, %") conversions - } + } catch case NonFatal(ex) => + logger.nn.warning( + s"Exception when searching for implicit conversions:\n ${ex.getMessage()}\n${ex.getStackTrace().nn.mkString("\n")}" + ) + Set.empty /** Filter for names that should appear when looking for completions. */ private object completionsFilter extends NameFilter: def apply(pre: Type, name: Name)(using Context): Boolean = - !name.isConstructorName && name.toTermName.info.kind == SimpleNameKind + !name.isConstructorName && name.toTermName.info.kind == SimpleNameKind && matches(name) def isStable = true extension (denotations: Seq[SingleDenotation]) @@ -647,7 +718,9 @@ object Completion: def groupByName: CompletionMap = namedDenotations.groupMap((name, denot) => name)((name, denot) => denot) private type CompletionMap = Map[Name, Seq[SingleDenotation]] - + // A list of denotations together with site for checking accessibility + case class CompletionDenotation(denots: Seq[SingleDenotation], site: Option[Type]) + case class CompletionResult(names: Map[Name, CompletionDenotation], renames: Map[Symbol, Name]) /** * The completion mode: defines what kinds of symbols should be included in the completion * results. @@ -673,3 +746,5 @@ object Completion: val Member: Mode = new Mode(16) + val Lazy: Mode = new Mode(32) + diff --git a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala index a03ae502f2f1..f1db0c949cef 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala @@ -144,6 +144,7 @@ object Interactive { ( sym == tree.symbol || sym.exists && sym == tree.symbol.sourceSymbol + || sym.exists && sym.sourceSymbol == tree.symbol || !include.isEmpty && sym.name == tree.symbol.name && sym.maybeOwner != tree.symbol.maybeOwner && ( include.isOverridden && overrides(sym, tree.symbol) || include.isOverriding && overrides(tree.symbol, sym) diff --git a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala index 8f42c62cb3b0..e505508f3a0e 100644 --- a/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala +++ b/compiler/src/dotty/tools/dotc/interactive/InteractiveDriver.scala @@ -14,9 +14,11 @@ import java.util.zip.* import scala.collection.* import scala.io.Codec +import dotty.tools.dotc.sbt.interfaces.ProgressCallback import dotty.tools.io.AbstractFile import ast.{Trees, tpd} +import config.* import core.*, core.Decorators.* import Contexts.*, Names.*, NameOps.*, Symbols.*, SymDenotations.*, Trees.*, Types.* import Denotations.staticRef @@ -24,12 +26,29 @@ import classpath.* import reporting.* import util.* +private class InteractiveContextBase(precomputedSourcePackages: Option[LogicalPackage]) extends ContextBase { + + override protected def newPlatform(using Context): Platform = + if (settings.scalajs.value) new SJSPlatform(precomputedSourcePackages) + else new JavaPlatform(precomputedSourcePackages) + +} + /** A Driver subclass designed to be used from IDEs */ -class InteractiveDriver(val settings: List[String]) extends Driver { +class InteractiveDriver( + val settings: List[String], + val logicalRootPackage: Option[LogicalPackage] = None +) extends Driver { import tpd.* + override protected def initCtx: Context = + new InteractiveContextBase(logicalRootPackage).initialCtx + override def sourcesRequired: Boolean = false + private var myProgressCallback: ProgressCallback = new ProgressCallback: + override def isCancelled(): Boolean = Thread.interrupted() + private val myInitCtx: Context = { val rootCtx = initCtx.fresh.addMode(Mode.ReadPositions).addMode(Mode.Interactive) rootCtx.setSetting(rootCtx.settings.YretainTrees, true) @@ -151,7 +170,7 @@ class InteractiveDriver(val settings: List[String]) extends Driver { val reporter = new StoreReporter(null) with UniqueMessagePositions with HideNonSensicalMessages - val run = compiler.newRun(using myInitCtx.fresh.setReporter(reporter)) + val run = compiler.newRun(using myInitCtx.fresh.setReporter(reporter).setProgressCallback(myProgressCallback)) myCtx = run.runContext.withRootImports given Context = myCtx @@ -169,8 +188,7 @@ class InteractiveDriver(val settings: List[String]) extends Driver { myCtx = myCtx.fresh.setPhase(myInitCtx.base.typerPhase) reporter.removeBufferedMessages - } - catch { + } catch { case ex: FatalError => myCtx = previousCtx close(uri) diff --git a/compiler/src/dotty/tools/dotc/interactive/LogicalPackage.scala b/compiler/src/dotty/tools/dotc/interactive/LogicalPackage.scala new file mode 100644 index 000000000000..18fd5b0e4185 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/interactive/LogicalPackage.scala @@ -0,0 +1,40 @@ +package dotty.tools.dotc.interactive + +import dotty.tools.io.AbstractFile + +/** + * A logical package representation. This is disconnected from the file system, and faithfully + * represents the nesting of packages and sources that contribute classes to those packages. + */ +trait LogicalPackage { + + def name: String + + def packages: Seq[LogicalPackage] + + /** + * Return all sources contained by this package. Only direct members are returned, and there are no duplicates. + * + */ + def sources: Seq[AbstractFile] + + def getPackage(name: String): Option[LogicalPackage] + + def prettyPrint(): String = { + prettyPrintWith().toString().trim().nn + } + + private def prettyPrintWith( + indent: Int = 0, + sb: StringBuilder = new StringBuilder + ): StringBuilder = + sb ++= " " * indent + sb ++= s"$name\n" + packages.sortBy(_.name).foreach(_.prettyPrintWith(indent + 4, sb)) + sources.foreach { s => + sb ++= (" " * (indent + 4)) + sb ++= s.name + "\n" + } + sb + +} diff --git a/compiler/src/dotty/tools/dotc/interactive/LogicalPackagesProvider.scala b/compiler/src/dotty/tools/dotc/interactive/LogicalPackagesProvider.scala new file mode 100644 index 000000000000..59c70c64805c --- /dev/null +++ b/compiler/src/dotty/tools/dotc/interactive/LogicalPackagesProvider.scala @@ -0,0 +1,170 @@ +package dotty.tools.dotc.interactive + +import dotty.tools.dotc.ast.untpd +import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.dotc.parsing.JavaParsers +import dotty.tools.dotc.parsing.Parsers +import dotty.tools.dotc.util.SourceFile +import dotty.tools.io.AbstractFile +import dotty.tools.io.ClassPath +import dotty.tools.io.Path + +import java.io.File +import scala.collection.mutable +import scala.language.unsafeNulls +import scala.util.control.NonFatal + +/** + * A compiler component that adds support for parsing Scala and Java source files and finding out + * the logical package structure of the whole source path. + */ +class LogicalPackagesProvider(sourcePath: String){ + + // We only use it for parser + private given Context = new ContextBase().initialCtx + + private lazy val sourceRoots: Seq[SourceFile] = + allSources(sourcePath).map(f => SourceFile(f, f.toCharArray)) + + lazy val root: LogicalPackage = parseSourcePath() + + /** + * Parse all source files in the sourcepath and build the logical package structure. + */ + def parseSourcePath(): LogicalPackage = + val pkg: ParsedLogicalPackage = newPackage() + for sourceFile <- sourceRoots do + try + parseSourceFile(sourceFile, pkg) + catch + case NonFatal(e) => + // Silently ignore parsing errors + pkg + + private def newPackage(): ParsedLogicalPackage = + new ParsedLogicalPackage("", None) + + private def parseSourceFile( + sourceFile: SourceFile, + rootPackage: ParsedLogicalPackage + ): Unit = + val fileName = sourceFile.path + if fileName.endsWith(".scala") then + parseScalaSourceFile(sourceFile, fileName, rootPackage) + else if fileName.endsWith(".java") then + parseJavaSourceFile(sourceFile, fileName, rootPackage) + + private def parseScalaSourceFile( + sourceFile: SourceFile, + fileName: String, + rootPackage: ParsedLogicalPackage + ): Unit = + try + // Use OutlineParser for fast parsing that skips method bodies + val parser = new Parsers.OutlineParser(sourceFile) + val tree = parser.parse() + val traverser = new SourceFileTraverser(fileName, rootPackage) + traverser.traverse(tree) + catch + case NonFatal(e) => + // Silently ignore parsing errors + + private def parseJavaSourceFile( + sourceFile: SourceFile, + fileName: String, + rootPackage: ParsedLogicalPackage + ): Unit = + try + // Use OutlineJavaParser for fast parsing + val parser = new JavaParsers.OutlineJavaParser(sourceFile) + val tree = parser.parse() + + // Traverse the tree to extract package info + val traverser = new SourceFileTraverser(fileName, rootPackage) + traverser.traverse(tree) + catch + case NonFatal(e) => + // Silently ignore parsing errors + + /** + * Traverse an untyped AST to extract package and class definitions. + */ + private class SourceFileTraverser( + fileName: String, + rootPackage: ParsedLogicalPackage + ) { + private var currentPackage = rootPackage + + def traverse(tree: untpd.Tree): Unit = tree match + case untpd.Thicket(trees) => + trees.foreach(traverse) + case pkg: untpd.PackageDef => + traversePackageDef(pkg) + case _: untpd.MemberDef => + // Top-level class or object in default package + currentPackage.enterSource(fileName) + case _ => + + private def traversePackageDef(pkg: untpd.PackageDef): Unit = { + // Navigate to the package + val pkgName = packageNameFromTree(pkg.pid) + val newCurrentPackage = { + var current = currentPackage + for (part <- pkgName.split('.') if part.nonEmpty) { + current = current.enterPackage(part) + } + current + } + // Traverse nested definitions + val oldPackage = currentPackage + currentPackage = newCurrentPackage + pkg.stats.foreach(traverse) + currentPackage = oldPackage + } + + private def packageNameFromTree(tree: untpd.Tree): String = tree match { + case untpd.Ident(name) => + if (name == nme.ROOT || name == nme.EMPTY_PACKAGE || name == nme.EMPTY) "" + else name.toString + case untpd.Select(qual, name) => + val qualName = packageNameFromTree(qual) + if (qualName.isEmpty) name.toString + else s"$qualName.${name.toString}" + case _ => + "" + } + } + + /** + * Return all Scala and Java sources from the given sourcepath string. + */ + private def allSources(srcPath: String): Seq[AbstractFile] = { + val entries = ClassPath.split(srcPath).map(Path(_)) + def isRelevantFile(path: Path) = + path.extension == "scala" || path.extension == "java" + // avoid using IO operation, assume standard extensions, Metals sends files so no sense checking for directories eagerly + val rootDirs = entries.filter(f => !isRelevantFile(f)) + val rootFiles = for { + e <- entries + if isRelevantFile(e) + f <- Option(AbstractFile.getFile(e)) + } yield f + rootFiles ++ rootDirs.flatMap{ dir => + Option(AbstractFile.getDirectory(dir)).toSeq.flatMap(sourcesIn(_, "scala", "java")) + } + } + + /** + * Recursively find all source files with given extensions in a directory. + */ + private def sourcesIn( + dir: AbstractFile, + extensions: String* + ): Seq[AbstractFile] = + dir.iterator().toSeq.flatMap { file => + if (file.isDirectory) sourcesIn(file, extensions*) + else if (extensions.exists(ext => file.name.endsWith(s".$ext"))) Seq(file) + else Seq.empty[AbstractFile] + } +} diff --git a/compiler/src/dotty/tools/dotc/interactive/LogicalSourcePath.scala b/compiler/src/dotty/tools/dotc/interactive/LogicalSourcePath.scala new file mode 100644 index 000000000000..4627d96fed08 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/interactive/LogicalSourcePath.scala @@ -0,0 +1,79 @@ +package dotty.tools.dotc.interactive + +import dotty.tools.dotc.classpath.ClassFileEntry +import dotty.tools.dotc.classpath.ClassPathEntries +import dotty.tools.dotc.classpath.PackageEntry +import dotty.tools.dotc.classpath.PackageEntryImpl +import dotty.tools.dotc.classpath.PackageName +import dotty.tools.dotc.classpath.SourceFileEntry +import dotty.tools.dotc.classpath.SourceFileEntryImpl +import dotty.tools.io.AbstractFile +import dotty.tools.io.ClassPath + +import java.io.File +import java.net.URL +import java.nio.file.Path + +/** + * A ClassPath implementation that can find sources regardless of the directory where they're declared. + */ +class LogicalSourcePath(val sourcepath: String, rootPackage: LogicalPackage) + extends ClassPath { + + override def findClassFile(className: String): Option[AbstractFile] = None + override def classes(inPackage: PackageName): Seq[ClassFileEntry] = Seq.empty + + override def hasPackage(inPackage: PackageName): Boolean = + findPackage( + inPackage.dottedString + ).isDefined + + /** Return all packages contained inside `inPackage`. Package entries contain the *full name* of the package. */ + override def packages(inPackage: PackageName): Seq[PackageEntry] = + val rawPackage = inPackage.dottedString + findPackage(rawPackage) match + case Some(pkg) => packagesIn(pkg, rawPackage) + case None => Seq.empty[PackageEntry] + + + /** Return all sources contained directly inside `inPackage` */ + override def sources(inPackage: PackageName): Seq[SourceFileEntry] = + val rawPackage = inPackage.dottedString + findPackage(rawPackage) match + case Some(pkg) => + sourcesIn(pkg) + case None => Seq.empty[SourceFileEntry] + + private def sourcesIn(pkg: LogicalPackage) = + pkg.sources.map(p => SourceFileEntryImpl(p)) + + private def packagesIn(pkg: LogicalPackage, prefix: String) = + val pre = if (prefix.isEmpty) prefix else s"$prefix." + pkg.packages.map(p => PackageEntryImpl(pre + p.name)) + + override def list(inPackage: PackageName): ClassPathEntries = + val rawPackage = inPackage.dottedString + val res = findPackage(rawPackage) match + case Some(pkg) => + ClassPathEntries(packagesIn(pkg, rawPackage), sourcesIn(pkg)) + case None => ClassPathEntries(Seq(), Seq()) + res + + override def asURLs: Seq[URL] = sourcepath.split(File.pathSeparator).nn.toIndexedSeq.nn.map(new File(_)).nn.map(_.toURI.nn.toURL.nn) + + override def asClassPathStrings: Seq[String] = Seq() + + override def asSourcePathString: String = sourcepath + + /** Return the package for the given fullName, if any */ + private def findPackage(fullName: String): Option[LogicalPackage] = + if fullName == "" then Option(rootPackage) + else + fullName.split('.').foldLeft(Option(rootPackage)) { (pkg, name) => + pkg.flatMap(_.getPackage(name)) + } + + override def toString: String = rootPackage.prettyPrint() + +} + diff --git a/compiler/src/dotty/tools/dotc/interactive/ParsedLogicalPackage.scala b/compiler/src/dotty/tools/dotc/interactive/ParsedLogicalPackage.scala new file mode 100644 index 000000000000..5c9830a6d74e --- /dev/null +++ b/compiler/src/dotty/tools/dotc/interactive/ParsedLogicalPackage.scala @@ -0,0 +1,107 @@ +package dotty.tools.dotc.interactive + +import scala.collection.mutable +import dotty.tools.io.AbstractFile +import scala.jdk.CollectionConverters.* + +/** + * Represent a package and its contents in a way that's close to the file system. + * + * A package contains any number of nested packages and source files. It is mutable in order to allow adding + * members at any level, as they are discovered by parsing source files. + * + * @param name simple name of the package + * @note This class is not thread safe + */ +class ParsedLogicalPackage( + val name: String, + val parent: Option[ParsedLogicalPackage] +) extends LogicalPackage { + require( + (name.trim.nn.isEmpty && parent.isEmpty) || (name.trim.nn.nonEmpty && parent.nonEmpty), + s"Unexpected package name `$name` and parent `$parent`." + ) + + def this(name: String, parent: ParsedLogicalPackage) = + this(name, Some(parent)) + + private val subpackages = + mutable.LinkedHashMap.empty[String, ParsedLogicalPackage] + private val directSources = mutable.ListBuffer.empty[String] + + def fullName: String = + if (parent.isEmpty || parent.get.name.isEmpty) name + else s"${parent.get.fullName}.$name" + + def removeEmptyPackages(): Unit = + subpackages.values.foreach(_.removeEmptyPackages()) + if subpackages.isEmpty && directSources.isEmpty then + parent.foreach(_.subpackages.remove(name)) + + + /** + * Return the existing member package, or create a new one and add it to this package. + */ + def enterPackage(name: String): ParsedLogicalPackage = synchronized{ + subpackages.get(name) match { + case Some(p) => p + case None => + val p = new ParsedLogicalPackage(name, this) + subpackages(name) = p + p + } + } + + def getPackage(name: String): Option[ParsedLogicalPackage] = + subpackages.get(name) + + def enterSource(fileName: String): this.type = synchronized: + directSources += fileName + this + + /** Return all member packages. Only direct members are returned. */ + def packages: Seq[ParsedLogicalPackage] = subpackages.values.toList + + /** + * Return all sources contained by this package. Only direct members are returned, and there are no duplicates. + * + * The return type is a sequence and not a Set in order to have deterministic runs + */ + def sources: Seq[AbstractFile] = directSources.toSeq.distinct.flatMap(name => Option(AbstractFile.getFile(name))) + + override def toString(): String = + s"package $name(${packages.size} packages and ${sources.size} files)" +} + +object ParsedLogicalPackage{ + + def fromMbtIndex( + packages: java.util.Map[String, java.util.Set[java.nio.file.Path]] + ): ParsedLogicalPackage = + val root = new ParsedLogicalPackage("", None) + val disallowedPackages: Set[String] = Set("scala", "scala.test", "_empty_") + val validExtensions: Set[String] = Set(".scala", ".java") + + def isSupported(path: java.nio.file.Path): Boolean = + val filename = path.getFileName.toString + validExtensions.exists(filename.endsWith) + + def enterNestedPackage(pkg: String) = + val parts = pkg.split('/') + var current = root + for part <- parts if !part.isEmpty do + current = current.enterPackage(part) + current + + for ((pkg, paths) <- packages.asScala) do + val p = enterNestedPackage(pkg) + // we don't enter anything in the empty package, which is special and generally not useful (it is not) + // visible from other packages. Similarly, the scala package is special and we don't want to enter any + // symbols into it, since they might hide standard library symbols, such as scala.Option. + if p.name.nonEmpty && !disallowedPackages.contains(p.fullName) then + for path <- paths.asScala if isSupported(path) do + p.enterSource(path.toString) + + root.removeEmptyPackages() + root +} \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 092ab2eeea0d..245be779b88e 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -106,6 +106,9 @@ object JavaParsers { def arrayOf(tpt: Tree): AppliedTypeTree = AppliedTypeTree(scalaDot(tpnme.Array), List(tpt)) + def classOf(tpt: Tree): Tree = + TypeApply(Select(scalaDot(nme.Predef), nme.classOf), List(tpt)) + def makeTemplate(parents: List[Tree], stats: List[Tree], tparams: List[TypeDef], needsDummyConstr: Boolean): Template = { def pullOutFirstConstr(stats: List[Tree]): (Tree, List[Tree]) = stats match { case (meth: DefDef) :: rest if meth.name == nme.CONSTRUCTOR => (meth, rest) @@ -254,7 +257,8 @@ object JavaParsers { t } - def optArrayBrackets(tpt: Tree): Tree = + def optArrayBrackets(tpt: Tree): Tree = { + annotations() if (in.token == LBRACKET) { val tpt1 = atSpan(tpt.span.start, in.offset) { arrayOf(tpt) } in.nextToken() @@ -262,6 +266,7 @@ object JavaParsers { optArrayBrackets(tpt1) } else tpt + } def basicType(): Tree = atSpan(in.offset) { @@ -335,7 +340,7 @@ object JavaParsers { } def annotations(): List[Tree] = { - var annots = new ListBuffer[Tree] + val annots = new ListBuffer[Tree] while (in.token == AT) { in.nextToken() annotation() match { @@ -362,18 +367,13 @@ object JavaParsers { * but instead we skip entire annotation silently. */ def annotation(): Option[Tree] = { - def classOrId(): Tree = - val id = qualId() + def classOrId(tree: RefTree | TypedSplice): Tree = if in.token == DOT && in.lookaheadToken == CLASS then - accept(DOT) - accept(CLASS) - TypeApply( - Select( - scalaDot(nme.Predef), - nme.classOf), - convertToTypeId(id) :: Nil - ) - else id + accept(DOT) ; accept(CLASS) + tree match + case id: RefTree => classOf(convertToTypeId(tree)) + case tree: TypedSplice => classOf(tree) + else tree def array(): Option[Tree] = accept(LBRACE) @@ -395,8 +395,24 @@ object JavaParsers { case AT => in.nextToken() annotation() - case IDENTIFIER => Some(classOrId()) case LBRACE => array() + case IDENTIFIER => Some(classOrId(qualId())) + case BYTE => + accept(BYTE); Some(classOrId(TypeTree(ByteType))) + case SHORT => + accept(SHORT); Some(classOrId(TypeTree(ShortType))) + case CHAR => + accept(CHAR); Some(classOrId(TypeTree(CharType))) + case INT => + accept(INT); Some(classOrId(TypeTree(IntType))) + case LONG => + accept(LONG); Some(classOrId(TypeTree(LongType))) + case FLOAT => + accept(FLOAT); Some(classOrId(TypeTree(FloatType))) + case DOUBLE => + accept(DOUBLE); Some(classOrId(TypeTree(DoubleType))) + case BOOLEAN => + accept(BOOLEAN); Some(classOrId(TypeTree(BooleanType))) case _ => None } } @@ -435,11 +451,11 @@ object JavaParsers { } } - def modifiers(inInterface: Boolean): Modifiers = { + def modifiers(inInterface: Boolean, annots0: List[Tree] = Nil): Modifiers = { var flags: FlagSet = Flags.JavaDefined // assumed true unless we see public/private/protected var isPackageAccess = true - var annots = new ListBuffer[Tree] + var annots = ListBuffer.from[Tree](annots0) def addAnnot(tpt: Tree) = annots += atSpan(in.offset) { in.nextToken() @@ -581,7 +597,7 @@ object JavaParsers { if in.token == IDENTIFIER && in.name == jnme.RECORDid then in.token = RECORD - def termDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = { + def termDecl(start: Offset, mods: Modifiers, parentToken: Int): List[Tree] = { val inInterface = definesInterface(parentToken) val tparams = if (in.token == LT) typeParams(Flags.JavaDefined | Flags.Param) else List() val isVoid = in.token == VOID @@ -753,11 +769,9 @@ object JavaParsers { ValDef(name, tpt2, if (mods.is(Flags.Param)) EmptyTree else unimplementedExpr).withMods(mods1) } - def memberDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = in.token match - case CLASS | ENUM | RECORD | INTERFACE | AT => - typeDecl(start, if definesInterface(parentToken) then mods | Flags.JavaStatic else mods) - case _ => - termDecl(start, mods, parentToken, parentTParams) + def memberDecl(start: Offset, mods: Modifiers, parentToken: Int): List[Tree] = + if (isTypeDeclStart()) typeDecl(start, if definesInterface(parentToken) then mods | Flags.JavaStatic else mods) + else termDecl(start, mods, parentToken) def makeCompanionObject(cdef: TypeDef, statics: List[Tree]): Tree = atSpan(cdef.span) { @@ -871,9 +885,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 @@ -941,7 +960,7 @@ object JavaParsers { else { adaptRecordIdentifier() if (in.token == ENUM || in.token == RECORD || definesInterface(in.token)) mods |= Flags.JavaStatic - val decls = memberDecl(start, mods, parentToken, parentTParams) + val decls = memberDecl(start, mods, parentToken) (if (mods.is(Flags.JavaStatic) || inInterface && !(decls exists (_.isInstanceOf[DefDef]))) statics else @@ -1041,6 +1060,10 @@ object JavaParsers { } } + def isTypeDeclStart() = in.token match { + case CLASS | ENUM | RECORD | INTERFACE | AT => true + case _ => false + } def typeDecl(start: Offset, mods: Modifiers): List[Tree] = in.token match case ENUM => enumDecl(start, mods) case INTERFACE => interfaceDecl(start, mods) @@ -1072,39 +1095,51 @@ object JavaParsers { } } - /** CompilationUnit ::= [package QualId semi] TopStatSeq + /** CompilationUnit ::= {Annotation} [package QualId semi] {Import} {TypeDecl} */ def compilationUnit(): Tree = { - val start = in.offset + val buf = ListBuffer.empty[Tree] + var start = in.offset + val leadingAnnots = if (in.token == AT) annotations() else Nil val pkg: RefTree = - if (in.token == AT || in.token == PACKAGE) { - annotations() + if in.token == PACKAGE then + if leadingAnnots.nonEmpty then + start = in.offset accept(PACKAGE) val pkg = qualId() accept(SEMI) pkg - } else + if leadingAnnots.nonEmpty then + buf ++= typeDecl(start, modifiers(inInterface = false, annots0 = leadingAnnots)) Ident(nme.EMPTY_PACKAGE) thisPackageName = convertToTypeName(pkg) match { case Some(t) => t.name.toTypeName case _ => tpnme.EMPTY } - val buf = new ListBuffer[Tree] - while (in.token == IMPORT) - buf ++= importDecl() + var compact = false + def typeDeclOrCompact(start: Offset, mods: Modifiers): List[Tree] = + if (isTypeDeclStart()) typeDecl(start, mods) + else + val ts = termDecl(start, mods, CLASS) + if (ts.nonEmpty) compact = true + Nil + if buf.isEmpty then + while (in.token == IMPORT) + buf ++= importDecl() while (in.token != EOF && in.token != RBRACE) { while (in.token == SEMI) in.nextToken() if (in.token != EOF) { val start = in.offset val mods = modifiers(inInterface = false) adaptRecordIdentifier() // needed for typeDecl - buf ++= typeDecl(start, mods) + buf ++= typeDeclOrCompact(start, mods) } } val unit = atSpan(start) { PackageDef(pkg, buf.toList) } accept(EOF) - unit match + if (compact) EmptyTree + else unit match case PackageDef(Ident(nme.EMPTY_PACKAGE), Nil) => EmptyTree case _ => unit } diff --git a/compiler/src/dotty/tools/dotc/parsing/ParserPhase.scala b/compiler/src/dotty/tools/dotc/parsing/ParserPhase.scala index bcabfbd03a1d..f331722639d3 100644 --- a/compiler/src/dotty/tools/dotc/parsing/ParserPhase.scala +++ b/compiler/src/dotty/tools/dotc/parsing/ParserPhase.scala @@ -57,7 +57,7 @@ class Parser extends Phase { unitContexts0.map(_.compilationUnit) } - def run(using Context): Unit = unsupported("run") + protected def run(using Context): Unit = unsupported("run") } object Parser{ diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 4bd7769187d5..13a989d20d47 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -33,6 +33,7 @@ import config.Feature import config.Feature.{sourceVersion, migrateTo3, globalOnlyImports} import config.SourceVersion.* import config.SourceVersion +import dotty.tools.dotc.util.chaining.* object Parsers { @@ -66,7 +67,6 @@ object Parsers { this == Given || this == ExtensionFollow def acceptsVariance = this == Class || this == CaseClass || this == Type - end ParamOwner enum ParseKind: @@ -96,6 +96,9 @@ object Parsers { private val InCase: Region => Region = Scanners.InCase(_) private val InCond: Region => Region = Scanners.InParens(LPAREN, _) private val InFor : Region => Region = Scanners.InBraces(_) + private val InOldCond: Region => Region = // old-style Cond to allow indent when InParens, see #22608 + case p: Scanners.InParens => Scanners.Indented(p.indentWidth, p.prefix, p) + case r => r abstract class ParserCommon(val source: SourceFile)(using Context) { @@ -337,43 +340,49 @@ object Parsers { if in.isNewLine then in.nextToken() else accept(SEMI) /** Parse statement separators and end markers. Ensure that there is at least - * one statement separator unless the next token terminates a statement´sequence. - * @param stats the statements parsed to far + * one statement separator unless the next token terminates a statement sequence. + * @param stats the statements parsed so far * @param noPrevStat true if there was no immediately preceding statement parsed * @param what a string indicating what kind of statement is parsed * @param altEnd a token that is also considered as a terminator of the statement - * sequence (the default `EOF` already assumes to terminate a statement + * sequence (the default `EOF` is already assumed to terminate a statement * sequence). * @return true if the statement sequence continues, false if it terminates. */ def statSepOrEnd[T <: Tree](stats: ListBuffer[T], noPrevStat: Boolean = false, what: String = "statement", altEnd: Token = EOF): Boolean = + inline def stopping = false + inline def continuing = true def recur(sepSeen: Boolean, endSeen: Boolean): Boolean = if isStatSep then in.nextToken() - recur(true, endSeen) + recur(sepSeen = true, endSeen) else if in.token == END then if endSeen then syntaxError(em"duplicate end marker") checkEndMarker(stats) recur(sepSeen, endSeen = true) else if isStatSeqEnd || in.token == altEnd then - false + stopping else if sepSeen || endSeen then - true + continuing else val found = in.token val statFollows = mustStartStatTokens.contains(found) + val extra = + if in.isOperator && Tokens.canStartExprTokens3.contains(in.lookahead.token) then + " - did you intend to apply an infix operator? If so, wrap the right operand in parentheses or in a block." + else "" syntaxError( if noPrevStat then IllegalStartOfStatement(what, isModifier, statFollows) - else em"end of $what expected but ${showToken(found)} found") - if mustStartStatTokens.contains(found) then - false // it's a statement that might be legal in an outer context + else em"end of $what expected but ${showToken(found)} found$extra") + if statFollows then + stopping // it's a statement that might be legal in an outer context else in.nextToken() // needed to ensure progress; otherwise we might cycle forever skip() - true + continuing in.observeOutdented() - recur(false, false) + recur(sepSeen = false, endSeen = false) end statSepOrEnd def rewriteNotice(version: SourceVersion = `3.0-migration`, additionalOption: String = "") = @@ -398,7 +407,9 @@ object Parsers { false } - def errorTermTree(start: Offset): Tree = atSpan(Span(start, in.offset)) { unimplementedExpr } + def errorTermTree(start: Offset): Tree = + val end = if in.token == OUTDENT then start else in.offset + atSpan(Span(start, end)) { unimplementedExpr } private var inFunReturnType = false private def fromWithinReturnType[T](body: => T): T = { @@ -676,7 +687,7 @@ object Parsers { def checkNextNotIndented(): Unit = if in.isNewLine then val nextIndentWidth = in.indentWidth(in.next.offset) - if in.currentRegion.indentWidth < nextIndentWidth then + if in.currentRegion.indentWidth < nextIndentWidth && in.currentRegion.closedBy == OUTDENT then warning(em"Line is indented too far to the right, or a `{` or `:` is missing", in.next.offset) /* -------- REWRITES ----------------------------------------------------------- */ @@ -688,16 +699,17 @@ object Parsers { def testChar(idx: Int, p: Char => Boolean): Boolean = { val txt = source.content - idx < txt.length && p(txt(idx)) + idx >= 0 && idx < txt.length && p(txt(idx)) } def testChar(idx: Int, c: Char): Boolean = { val txt = source.content - idx < txt.length && txt(idx) == c + idx >= 0 && idx < txt.length && txt(idx) == c } def testChars(from: Int, str: String): Boolean = - str.isEmpty || + str.isEmpty + || testChar(from, str.head) && testChars(from + 1, str.tail) def skipBlanks(idx: Int, step: Int = 1): Int = @@ -1020,10 +1032,11 @@ object Parsers { */ def followingIsLambdaAfterColon(): Boolean = val lookahead = in.LookaheadScanner(allowIndent = true) + .tap(_.currentRegion.knownWidth = in.currentRegion.indentWidth) def isArrowIndent() = lookahead.isArrow && { - lookahead.nextToken() + lookahead.observeArrowIndented() lookahead.token == INDENT || lookahead.token == EOF } lookahead.nextToken() @@ -1436,15 +1449,23 @@ object Parsers { if sourceVersion.isMigrating then patch(source, Span(in.offset), " ") - def possibleTemplateStart(isNew: Boolean = false): Unit = + inline transparent def possibleTemplateStart(inline isNew: Boolean = false) = + inline if isNew then newTemplateStart() else (newTemplateStart(): Unit) + + /** Return true on trivial end */ + def newTemplateStart(): Boolean = in.observeColonEOL(inTemplate = true) if in.token == COLONeol then - if in.lookahead.token == END then in.token = NEWLINE + if in.lookahead.token == END then + in.token = NEWLINE + true else in.nextToken() if in.token != LBRACE then acceptIndent() + false else newLineOptWhenFollowedBy(LBRACE) + false def checkEndMarker[T <: Tree](stats: ListBuffer[T]): Unit = @@ -2127,25 +2148,25 @@ object Parsers { def condExpr(altToken: Token): Tree = val t: Tree = if in.token == LPAREN then - var t: Tree = atSpan(in.offset): - makeTupleOrParens(inParensWithCommas(commaSeparated(exprInParens))) - if in.token != altToken then - if toBeContinued(altToken) then - t = inSepRegion(InCond) { + inSepRegion(InOldCond): // allow inferred NEWLINE for observeIndented below + atSpan(in.offset): + makeTupleOrParens(inParensWithCommas(commaSeparated(exprInParens))) + .pipe: t => + if in.token == altToken then t + else if toBeContinued(altToken) then + inSepRegion(InCond): expr1Rest( postfixExprRest( simpleExprRest(t, Location.ElseWhere), Location.ElseWhere), Location.ElseWhere) - } else if rewriteToNewSyntax(t.span) then - dropParensOrBraces(t.span.start, s"${tokenString(altToken)}") + dropParensOrBraces(t.span.start, tokenString(altToken)) in.observeIndented() return t - t else if in.isNestedStart then - try expr() finally newLinesOpt() + expr().tap(_ => newLinesOpt()) else inSepRegion(InCond)(expr()) if rewriteToOldSyntax(t.span.startPos) then revertToParens(t) @@ -2174,6 +2195,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 @@ -2334,7 +2356,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))) @@ -2633,20 +2655,30 @@ object Parsers { */ def newExpr(): Tree = val start = in.skipToken() - def reposition(t: Tree) = t.withSpan(Span(start, in.lastOffset)) + def reposition(t: Tree) = + val end = if t.span.exists then in.lastOffset max t.span.end else in.lastOffset + t.withSpan(Span(start, end)) possibleTemplateStart() val parents = if in.isNestedStart then Nil else constrApps(exclude = COMMA) - possibleTemplateStart(isNew = true) - parents match { - case parent :: Nil if !in.isNestedStart => - reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent) - case tkn if in.token == INDENT => - New(templateBodyOpt(emptyConstructor, parents, Nil)) - case _ => - New(reposition(templateBodyOpt(emptyConstructor, parents, Nil))) - } + val colonized = possibleTemplateStart(isNew = true) + parents match + case parent :: Nil if !in.isNestedStart => + reposition: + if colonized then New(Template(emptyConstructor, parents, derived = Nil, self = EmptyValDef, body = Nil)) + else if parent.isType then ensureApplied(wrapNew(parent)) + else parent + case parents => + // With brace syntax, the last token consumed by a parser is }, but with indent syntax, + // the last token consumed by a parser is OUTDENT, which causes mismatching spans, so don't update end. + val indented = in.token == INDENT + val body = + val tbo = templateBodyOpt(emptyConstructor, parents, derived = Nil) + if indented then tbo.withSpan(tbo.span.withStart(start)) + else reposition(tbo) + New(body) + end newExpr /** ExprsInParens ::= ExprInParens {`,' ExprInParens} * Bindings ::= Binding {`,' Binding} @@ -2926,6 +2958,7 @@ object Parsers { } CaseDef(pat, EmptyTree, atSpan(accept(ARROW)) { val t = rejectWildcardType(typ()) + in.observeOutdented() if in.token == SEMI then in.nextToken() newLinesOptWhenFollowedBy(CASE) t @@ -2954,14 +2987,18 @@ object Parsers { def pattern1(location: Location = Location.InPattern): Tree = val p = pattern2() if in.isColon then - val isVariableOrNumber = isVarPattern(p) || p.isInstanceOf[Number] + val isVariable = unsplice(p) match { + case x: Ident => x.name.isVarPattern + case _ => false + } + val isVariableOrNumber = isVariable || p.isInstanceOf[Number] if !isVariableOrNumber then report.gradualErrorOrMigrationWarning( em"""Type ascriptions after patterns other than: | * variable pattern, e.g. `case x: String =>` | * number literal pattern, e.g. `case 10.5: Double =>` |are no longer supported. Remove the type ascription or move it to a separate variable pattern.""", - in.sourcePos(), + p.sourcePos, warnFrom = `3.3`, errorFrom = future ) @@ -3273,7 +3310,7 @@ object Parsers { ok def typeParam(): TypeDef = { - val isAbstractOwner = paramOwner == ParamOwner.Type || paramOwner == ParamOwner.TypeParam + val isAbstractOwner = (paramOwner == ParamOwner.Type || paramOwner == ParamOwner.TypeParam) val start = in.offset var mods = annotsAsMods() | Param if paramOwner == ParamOwner.Class || paramOwner == ParamOwner.CaseClass then @@ -3294,7 +3331,13 @@ object Parsers { } else ident().toTypeName val hkparams = typeParamClauseOpt(ParamOwner.Type) - val bounds = if (isAbstractOwner) typeBounds() else typeParamBounds(name) + // val bounds = if (isAbstractOwner) typeBounds() else typeParamBounds(name) + val bounds = typeParamBounds(name) match + case bounds: TypeBoundsTree => bounds + case bounds: ContextBounds if !isAbstractOwner => bounds + case ContextBounds(bounds, cxBounds) => + for cbound <- cxBounds do report.error(IllegalContextBounds(), cbound.srcPos) + bounds TypeDef(name, lambdaAbstract(hkparams, bounds)).withMods(mods) } } @@ -3402,7 +3445,10 @@ object Parsers { // begin termParamClause inParensWithCommas { if in.token == RPAREN && paramOwner != ParamOwner.ExtensionPrefix && !impliedMods.is(Given) - then Nil + then + if paramOwner.takesOnlyUsingClauses then + syntaxError(em"`using` expected") + Nil else val clause = if paramOwner == ParamOwner.ExtensionPrefix @@ -3415,7 +3461,7 @@ object Parsers { syntaxError(em"`using` expected") val (firstParamMod, isParams) = var mods = EmptyModifiers - if in.lookahead.isColon then + if in.token != INTERPOLATIONID && in.lookahead.isColon then (mods, true) else if isErased then mods = addModifier(mods) @@ -3878,14 +3924,12 @@ object Parsers { /** ClassDef ::= id ClassConstr TemplateOpt */ - def classDef(start: Offset, mods: Modifiers): TypeDef = atSpan(start, nameStart) { - classDefRest(start, mods, ident().toTypeName) - } - - def classDefRest(start: Offset, mods: Modifiers, name: TypeName): TypeDef = - val constr = classConstr(if mods.is(Case) then ParamOwner.CaseClass else ParamOwner.Class) - val templ = templateOpt(constr) - finalizeDef(TypeDef(name, templ), mods, start) + def classDef(start: Offset, mods: Modifiers): TypeDef = + atSpan(start, nameStart): + val name = ident().toTypeName + val constr = classConstr(if mods.is(Case) then ParamOwner.CaseClass else ParamOwner.Class) + val templ = templateOpt(constr) + finalizeDef(TypeDef(name, templ), mods, start) /** ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsTermParamClauses */ @@ -4050,7 +4094,10 @@ object Parsers { leadParamss += extParams isUsingClause(extParams) do () - leadParamss ++= termParamClauses(ParamOwner.ExtensionFollow, numLeadParams) + // Empty parameter clauses are filtered out. They are already reported as syntax errors and are not + // allowed here. + val extFollowParams = termParamClauses(ParamOwner.ExtensionFollow, numLeadParams).filterNot(_.isEmpty) + leadParamss ++= extFollowParams if in.isColon then syntaxError(em"no `:` expected here") in.nextToken() @@ -4087,13 +4134,31 @@ object Parsers { val meths = new ListBuffer[Tree] while val start = in.offset - if in.token == EXPORT then - meths ++= exportClause() - else - val mods = defAnnotsMods(modifierTokens) - if in.token != EOF then + val mods = defAnnotsMods(modifierTokens) + in.token match + case IMPORT => + syntaxError(em"imports are not allowed inside extensions") + meths ++= importClause() + case EXPORT => + meths ++= exportClause() + case VAL => + accept(VAL) + val tree = patDefOrDcl(start, mods) + meths += tree + syntaxError(em"values are not allowed inside extensions", tree.span) + case VAR => + accept(VAR) + val tree = patDefOrDcl(start, mods) // we don't set the Var modifier here (we don't care) + meths += tree + syntaxError(em"values are not allowed inside extensions", tree.span) + case DEF => accept(DEF) meths += defDefOrDcl(start, mods, numLeadParams) + case TYPE => + val tree = typeDefOrDcl(start, in.skipToken(mods)) + meths += tree + syntaxError(em"types are not allowed inside extensions", tree.span) + case _ => in.token != EOF && statSepOrEnd(meths, what = "extension method") do () if meths.isEmpty then syntaxErrorOrIncomplete(em"`def` expected") diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index aadedde612f7..7c8156d67689 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -305,7 +305,7 @@ object Scanners { println(s"\nSTART SKIP AT ${sourcePos().line + 1}, $this in $currentRegion") var noProgress = 0 // Defensive measure to ensure we always get out of the following while loop - // even if source file is weirly formatted (i.e. we never reach EOF) + // even if source file is weirdly formatted (i.e. we never reach EOF) var prevOffset = offset while !atStop && noProgress < 3 do nextToken() @@ -367,7 +367,7 @@ object Scanners { } case OUTDENT => currentRegion match - case r: Indented => currentRegion = r.enclosing + case r: Indented if !r.isOutermost => currentRegion = r.enclosing case _ => case STRINGLIT => currentRegion match { @@ -601,6 +601,20 @@ object Scanners { lastWidth = r.knownWidth newlineIsSeparating = r.isInstanceOf[InBraces] + // can emit OUTDENT if line is not non-empty blank line at EOF + inline def isTrailingBlankLine: Boolean = + token == EOF && { + val end = buf.length - 1 // take terminal NL as empty last line + val prev = buf.lastIndexWhere(!isWhitespace(_), end = end) + prev < 0 || end - prev > 0 && isLineBreakChar(buf(prev)) + } + + inline def canDedent: Boolean = + lastToken != INDENT + && !isLeadingInfixOperator(nextWidth) + && !statCtdTokens.contains(lastToken) + && !isTrailingBlankLine + if newlineIsSeparating && canEndStatTokens.contains(lastToken) && canStartStatTokens.contains(token) @@ -613,7 +627,7 @@ object Scanners { || nextWidth == lastWidth && (indentPrefix == MATCH || indentPrefix == CATCH) && token != CASE then if currentRegion.isOutermost then if nextWidth < lastWidth then currentRegion = topLevelRegion(nextWidth) - else if !isLeadingInfixOperator(nextWidth) && !statCtdTokens.contains(lastToken) && lastToken != INDENT then + else if canDedent then currentRegion match case r: Indented => insert(OUTDENT, offset) @@ -635,7 +649,6 @@ object Scanners { insert(OUTDENT, offset) else if r.isInstanceOf[InBraces] && !closingRegionTokens.contains(token) then report.warning("Line is indented too far to the left, or a `}` is missing", sourcePos()) - else if lastWidth < nextWidth || lastWidth == nextWidth && (lastToken == MATCH || lastToken == CATCH) && token == CASE then if canStartIndentTokens.contains(lastToken) then @@ -655,7 +668,7 @@ object Scanners { def spaceTabMismatchMsg(lastWidth: IndentWidth, nextWidth: IndentWidth): Message = em"""Incompatible combinations of tabs and spaces in indentation prefixes. |Previous indent : $lastWidth - |Latest indent : $nextWidth""" + |Latest indent : $nextWidth""" def observeColonEOL(inTemplate: Boolean): Unit = val enabled = @@ -669,6 +682,23 @@ object Scanners { reset() if atEOL then token = COLONeol + // consume => and insert if applicable. Used to detect colon arrow: x => + def observeArrowIndented(): Unit = + if isArrow && indentSyntax then + peekAhead() + val atEOL = isAfterLineEnd + val atEOF = token == EOF + reset() + if atEOF then + token = EOF + else if atEOL then + val nextWidth = indentWidth(next.offset) + val lastWidth = currentRegion.indentWidth + if lastWidth < nextWidth then + currentRegion = Indented(nextWidth, COLONeol, currentRegion) + offset = next.offset + token = INDENT + def observeIndented(): Unit = if indentSyntax && isNewLine then val nextWidth = indentWidth(next.offset) @@ -680,16 +710,17 @@ object Scanners { end observeIndented /** Insert an token if next token closes an indentation region. - * Exception: continue if indentation region belongs to a `match` and next token is `case`. + * or next token is a comma and we expect a comma in an outer region. + * Exception: don't insert in front of case. */ def observeOutdented(): Unit = currentRegion match case r: Indented if !r.isOutermost - && closingRegionTokens.contains(token) - && !(token == CASE && r.prefix == MATCH) - && next.token == EMPTY // can be violated for ill-formed programs, e.g. neg/i12605.scala - => - insert(OUTDENT, offset) + && (acceptOutdentTokens.contains(token) + || token == COMMA && r.outer.commasExpectedInEnclosing) + && next.token == EMPTY + => + insert(OUTDENT, offset) case _ => def peekAhead() = @@ -726,25 +757,20 @@ object Scanners { peekAhead() if (token != ELSE) reset() case COMMA => - def isEnclosedInParens(r: Region): Boolean = r match - case r: Indented => isEnclosedInParens(r.outer) - case _: InParens => true - case _ => false + if closingParens.contains(currentRegion.closedBy) && currentRegion.commasExpected then + peekAhead() + if isAfterLineEnd && closingParens.contains(token) then + // encountered a trailing comma + // reset only the lastOffset + // so that the tree's span is correct + lastOffset = prev.lastOffset + else + reset() + case ARROW => currentRegion match - case r: Indented if isEnclosedInParens(r.outer) => - insert(OUTDENT, offset) - case _ => - peekAhead() - if isAfterLineEnd - && currentRegion.commasExpected - && (token == RPAREN || token == RBRACKET || token == RBRACE || token == OUTDENT) - then - // encountered a trailing comma - // reset only the lastOffset - // so that the tree's span is correct - lastOffset = prev.lastOffset - else - reset() + case r: Indented if r.outer.isInstanceOf[InCase] => + insert(OUTDENT, offset) + case _ => case END => if !isEndMarker then token = IDENTIFIER case COLONop => @@ -1097,7 +1123,7 @@ object Scanners { reset() next - class LookaheadScanner(val allowIndent: Boolean = false) extends Scanner(source, offset, allowIndent = allowIndent) { + class LookaheadScanner(allowIndent: Boolean = false) extends Scanner(source, offset, allowIndent = allowIndent) { override protected def initialCharBufferSize = 8 override def languageImportContext = Scanner.this.languageImportContext } @@ -1624,6 +1650,11 @@ object Scanners { def commasExpected = myCommasExpected + def commasExpectedInEnclosing: Boolean = + commasExpected || this.match + case r: Indented => r.outer.commasExpectedInEnclosing + case _ => false + def toList: List[Region] = this :: (if outer == null then Nil else outer.toList) @@ -1649,7 +1680,7 @@ object Scanners { case class InCase(outer: Region) extends Region(OUTDENT) /** A class describing an indentation region. - * @param width The principal indendation width + * @param width The principal indentation width * @param prefix The token before the initial of the region */ case class Indented(width: IndentWidth, prefix: Token, outer: Region | Null) extends Region(OUTDENT): diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index 416ba6b55401..7da4bed6a3bc 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -278,6 +278,8 @@ object Tokens extends TokensCommon { final val closingRegionTokens = BitSet(RBRACE, RPAREN, RBRACKET, CASE) | statCtdTokens + final val acceptOutdentTokens = BitSet(RBRACE, RPAREN, RBRACKET) | statCtdTokens + final val canStartIndentTokens: BitSet = statCtdTokens | BitSet(COLONeol, WITH, EQUALS, ARROW, CTXARROW, LARROW, WHILE, TRY, FOR, IF, THROW, RETURN) diff --git a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala index 6e37acfaac84..1c3fcd558a4f 100644 --- a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala @@ -375,7 +375,7 @@ object MarkupParsers { content_LT(ts) charComingAfter(xSpaceOpt()) == '<' } do () - handle.makeXMLseq(Span(start, curOffset, start), ts) + handle.makeXMLseq(Span(start, curOffset, start), ts, toVector = false) } else { assert(ts.length == 1, "Require one tree") diff --git a/compiler/src/dotty/tools/dotc/parsing/xml/SymbolicXMLBuilder.scala b/compiler/src/dotty/tools/dotc/parsing/xml/SymbolicXMLBuilder.scala index 28eced07d188..0b330bbdd713 100644 --- a/compiler/src/dotty/tools/dotc/parsing/xml/SymbolicXMLBuilder.scala +++ b/compiler/src/dotty/tools/dotc/parsing/xml/SymbolicXMLBuilder.scala @@ -13,6 +13,7 @@ import Names.*, StdNames.*, ast.Trees.*, ast.{tpd, untpd} import Symbols.*, Contexts.* import util.Spans.* import Parsers.Parser +import dotty.tools.dotc.ast.tpd.TreeOps /** This class builds instance of `Tree` that represent XML. * @@ -58,16 +59,23 @@ class SymbolicXMLBuilder(parser: Parser, preserveWS: Boolean)(using Context) { val _plus: TermName = "&+" val _tmpscope: TermName = "$tmpscope" val _xml: TermName = "xml" + val _toVector: TermName = "toVector" } import xmltypes.{_Comment, _Elem, _EntityRef, _Group, _MetaData, _NamespaceBinding, _NodeBuffer, _PrefixedAttribute, _ProcInstr, _Text, _Unparsed, _UnprefixedAttribute} - import xmlterms.{_Null, __Elem, __Text, _buf, _md, _plus, _scope, _tmpscope, _xml} + import xmlterms.{_Null, __Elem, __Text, _buf, _md, _plus, _scope, _tmpscope, _xml, _toVector} // convenience methods private def LL[A](x: A*): List[List[A]] = List(List(x:_*)) - private def const(x: Any) = Literal(Constant(x)) + // Changing type to String causes a compiler crash in pickler phase, + // so we leave it as Any for now. + private def const(x: Any) = + val lit = Literal(Constant(x)) + if ctx.explicitNulls && x == null + then TypeApply(Select(lit, nme.asInstanceOf_), TypeTree(defn.StringType) :: Nil) + else lit private def wild = Ident(nme.WILDCARD) private def wildStar = Ident(tpnme.WILDCARD_STAR) private def _scala(name: Name) = scalaDot(name) @@ -102,7 +110,7 @@ class SymbolicXMLBuilder(parser: Parser, preserveWS: Boolean)(using Context) { { def starArgs = if (children.isEmpty) Nil - else List(Typed(makeXMLseq(span, children), wildStar)) + else List(Typed(makeXMLseq(span, children, toVector = true), wildStar)) def pat = Apply(_scala_xml__Elem, List(pre, label, wild, wild) ::: convertToTextPat(children)) def nonpat = New(_scala_xml_Elem, List(List(pre, label, attrs, scope, if (empty) Literal(Constant(true)) else Literal(Constant(false))) ::: starArgs)) @@ -151,7 +159,7 @@ class SymbolicXMLBuilder(parser: Parser, preserveWS: Boolean)(using Context) { ts match { case Nil => TypedSplice(tpd.ref(defn.NilModule).withSpan(span)) case t :: Nil => t - case _ => makeXMLseq(span, ts) + case _ => makeXMLseq(span, ts, toVector = true) } } @@ -161,11 +169,12 @@ class SymbolicXMLBuilder(parser: Parser, preserveWS: Boolean)(using Context) { } /** could optimize if args.length == 0, args.length == 1 AND args(0) is <: Node. */ - def makeXMLseq(span: Span, args: collection.Seq[Tree]): Block = { + def makeXMLseq(span: Span, args: collection.Seq[Tree], toVector: Boolean): Block = { val buffer = ValDef(_buf, TypeTree(), New(_scala_xml_NodeBuffer, ListOfNil)) val applies = args filterNot isEmptyText map (t => Apply(Select(Ident(_buf), _plus), List(t))) - atSpan(span)(new XMLBlock(buffer :: applies.toList, Ident(_buf)) ) + val res = if (toVector) Select(Ident(_buf), _toVector) else Ident(_buf) + atSpan(span)(new XMLBlock(buffer :: applies.toList, res)) } /** Returns (Some(prefix) | None, rest) based on position of ':' */ @@ -176,7 +185,7 @@ class SymbolicXMLBuilder(parser: Parser, preserveWS: Boolean)(using Context) { /** Various node constructions. */ def group(span: Span, args: collection.Seq[Tree]): Tree = - atSpan(span)( New(_scala_xml_Group, LL(makeXMLseq(span, args))) ) + atSpan(span)( New(_scala_xml_Group, LL(makeXMLseq(span, args, toVector = true))) ) def unparsed(span: Span, str: String): Tree = atSpan(span)( New(_scala_xml_Unparsed, LL(const(str))) ) diff --git a/compiler/src/dotty/tools/dotc/printing/Formatting.scala b/compiler/src/dotty/tools/dotc/printing/Formatting.scala index c88f71f2d07d..c86804def59d 100644 --- a/compiler/src/dotty/tools/dotc/printing/Formatting.scala +++ b/compiler/src/dotty/tools/dotc/printing/Formatting.scala @@ -10,7 +10,7 @@ import core.* import Texts.*, Types.*, Flags.*, Symbols.*, Contexts.* import Decorators.* import reporting.Message -import util.DiffUtil +import util.{Chars, DiffUtil} import Highlighting.* object Formatting { @@ -129,6 +129,7 @@ object Formatting { given Show[Class[?]] = ShowAny given Show[Throwable] = ShowAny given Show[StringBuffer] = ShowAny + given Show[StringBuilder] = ShowAny given Show[CompilationUnit] = ShowAny given Show[Phases.Phase] = ShowAny given Show[TyperState] = ShowAny @@ -167,7 +168,8 @@ object Formatting { } def assemble(args: Seq[Shown])(using Context): String = { - def isLineBreak(c: Char) = c == '\n' || c == '\f' // compatible with StringLike#isLineBreak + // compatible with CharArrayReader (not StringOps) + inline def isLineBreak(c: Char) = c == Chars.LF || c == Chars.FF def stripTrailingPart(s: String) = { val (pre, post) = s.span(c => !isLineBreak(c)) pre ++ post.stripMargin diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index fce460d5acb3..b3798af9dc6b 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -11,7 +11,7 @@ import ast.Trees.* import typer.Implicits.* import typer.ImportInfo import Variances.varianceSign -import util.SourcePosition +import util.{Chars, SourcePosition} import scala.util.control.NonFatal import scala.annotation.switch import config.{Config, Feature} @@ -388,14 +388,29 @@ class PlainPrinter(_ctx: Context) extends Printer { def toTextPrefixOf(tp: NamedType): Text = controlled { homogenize(tp.prefix) match { case NoPrefix => "" - case tp: SingletonType => toTextRef(tp) ~ "." - case tp => trimPrefix(toTextLocal(tp)) ~ "#" + case prefix: SingletonType => toTextRef(prefix) ~ "." + case prefix => + // Use "." for Java nested classes (e.g., java.util.Map.Entry) + // Use "#" for Scala type projections (e.g., Outer#Inner) + val separator = if (isJavaNestedClass(tp)) "." else "#" + trimPrefix(toTextLocal(prefix)) ~ separator } } protected def isEmptyPrefix(sym: Symbol): Boolean = sym.isEffectiveRoot || sym.isAnonymousClass || sym.name.isReplWrapperName + /** Check if tp represents a Java nested class that should use "." separator. */ + protected def isJavaNestedClass(tp: NamedType)(using Context): Boolean = { + val sym = tp.symbol + sym.exists && + sym.is(JavaDefined) && + sym.isClass && + sym.owner.exists && + sym.owner.is(JavaDefined) && + sym.owner.isClass + } + /** String representation of a definition's type following its name, * if symbol is completed, "?" otherwise. */ @@ -579,22 +594,10 @@ class PlainPrinter(_ctx: Context) extends Printer { def toText(denot: Denotation): Text = toText(denot.symbol) ~ "/D" - private def escapedChar(ch: Char): String = (ch: @switch) match { - case '\b' => "\\b" - case '\t' => "\\t" - case '\n' => "\\n" - case '\f' => "\\f" - case '\r' => "\\r" - case '"' => "\\\"" - case '\'' => "\\\'" - case '\\' => "\\\\" - case _ => if ch.isControl then f"${"\\"}u${ch.toInt}%04x" else String.valueOf(ch).nn - } - def toText(const: Constant): Text = const.tag match { - case StringTag => stringText("\"" + escapedString(const.value.toString) + "\"") + case StringTag => stringText(Chars.escapedString(const.value.toString, quoted = true)) case ClazzTag => "classOf[" ~ toText(const.typeValue) ~ "]" - case CharTag => literalText(s"'${escapedChar(const.charValue)}'") + case CharTag => literalText(Chars.escapedChar(const.charValue)) case LongTag => literalText(const.longValue.toString + "L") case DoubleTag => literalText(const.doubleValue.toString + "d") case FloatTag => literalText(const.floatValue.toString + "f") @@ -612,7 +615,7 @@ class PlainPrinter(_ctx: Context) extends Printer { ~ (if param.isTypeParam then "" else ": ") ~ toText(param.paramInfo) - protected def escapedString(str: String): String = str flatMap escapedChar + protected final def escapedString(str: String): String = Chars.escapedString(str, quoted = false) def dclsText(syms: List[Symbol], sep: String): Text = Text(syms map dclText, sep) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 4fdfd7cb3e60..98b1d5e4f62a 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -104,8 +104,17 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { override def toTextRef(tp: SingletonType): Text = controlled { tp match { case tp: ThisType if !printDebug => - if (tp.cls.isAnonymousClass) keywordStr("this") - if (tp.cls.is(ModuleClass)) fullNameString(tp.cls.sourceModule) + val cls = tp.cls + if cls.isAnonymousClass then + keywordStr("this") + else if cls.is(ModuleClass) then + if cls.isStatic then + fullNameString(tp.cls.sourceModule) + else cls.owner.thisType match + case tp: SingletonType => + toTextRef(tp) ~ "." ~ nameString(cls) + case _ => // owner is a term, just print the name + nameString(cls) else super.toTextRef(tp) case tp: TermRef if !printDebug => if tp.symbol.is(Package) then fullNameString(tp.symbol) @@ -159,7 +168,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { changePrec(GlobalPrec) { val argStr: Text = if args.length == 2 - && !defn.isTupleNType(args.head) + && !defn.isDirectTupleNType(args.head) && !isGiven then atPrec(InfixPrec) { argText(args.head) } @@ -1040,7 +1049,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def recur(t: untpd.Tree): Text = t match case Apply(fn, Nil) => recur(fn) case Apply(fn, args) => - val explicitArgs = args.filterNot(untpd.stripNamedArg(_).symbol.name.is(DefaultGetterName)) + val explicitArgs = args.filterNot(_.symbol.name.is(DefaultGetterName)) recur(fn) ~ "(" ~ toTextGlobal(explicitArgs, ", ") ~ ")" case TypeApply(fn, args) => recur(fn) ~ "[" ~ toTextGlobal(args, ", ") ~ "]" case Select(qual, nme.CONSTRUCTOR) => recur(qual) diff --git a/compiler/src/dotty/tools/dotc/printing/ReplPrinter.scala b/compiler/src/dotty/tools/dotc/printing/ReplPrinter.scala index f02cbf159224..61c0b8dffa4a 100644 --- a/compiler/src/dotty/tools/dotc/printing/ReplPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/ReplPrinter.scala @@ -9,12 +9,79 @@ import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.printing.Texts.* - +import dotty.tools.dotc.typer.ImportInfo class ReplPrinter(_ctx: Context) extends RefinedPrinter(_ctx) { val debugPrint = _ctx.settings.YprintDebug.value + private def importSiteMatches(site: Type, owner: Symbol)(using Context): Boolean = + site.exists && (site.termSymbol == owner || site.termSymbol.moduleClass == owner) + + private def isRootImport(owner: Symbol)(using Context): Boolean = + defn.rootImportTypes.exists(r => r.symbol == owner || r.symbol.moduleClass == owner) + + private def importedNameFrom(info: ImportInfo, name: Name)(using Context): Name | Null = + val termName = name.toTermName + if info.forwardMapping.contains(termName) then info.forwardMapping(termName) + else if info.isWildcardImport && !info.excluded.contains(termName) then termName + else null + + private def findImportedName(sym: Symbol, owner: Symbol)(using Context): Name | Null = + if isRootImport(owner) then sym.name.toTermName + else + var c = ctx + while c ne NoContext do + if c.isImportContext && importSiteMatches(c.importInfo.nn.site, owner) then + val result = importedNameFrom(c.importInfo.nn, sym.name) + if result != null then return result + + c = c.outer + null + + private def shortRefNameViaImports(tp: TermRef)(using Context): Name | Null = + if debugPrint then null + else if !tp.symbol.exists then null + else if !tp.prefix.termSymbol.exists then null + else findImportedName(tp.symbol, tp.prefix.termSymbol) + + private def getImportedTypeName(sym: Symbol)(using Context): Name | Null = + if !sym.exists then null + else findImportedName(sym, sym.effectiveOwner) + + override def toTextRef(tp: SingletonType): Text = controlled { + tp match + case tp: TermRef if !debugPrint => + val shortName = shortRefNameViaImports(tp) + if shortName != null then nameString(shortName) + else super.toTextRef(tp) + + case _ => super.toTextRef(tp) + } + + override def toText(tp: Type): Text = tp match + case tp: TypeRef if !debugPrint => + val importedName = getImportedTypeName(tp.symbol) + if importedName != null then nameString(importedName) + else super.toText(tp) + + case _ => super.toText(tp) + + /** Override fullNameString to use shorter names based on imports. + * This is called by RefinedPrinter.toTextRef for packages. + */ + override def fullNameString(sym: Symbol): String = + if debugPrint then super.fullNameString(sym) + else + def findShortestPath(s: Symbol): String = + if s.isRoot || s == NoSymbol || s.owner.isEffectiveRoot then nameString(s) + else + val owner = s.effectiveOwner + if findImportedName(s, owner) != null then nameString(s) + else findShortestPath(owner) + "." + nameString(s) + + findShortestPath(sym) + override def nameString(name: Name): String = if (name.isReplAssignName) name.decode.toString.takeWhile(_ != '$') else super.nameString(name) diff --git a/compiler/src/dotty/tools/dotc/printing/Texts.scala b/compiler/src/dotty/tools/dotc/printing/Texts.scala index 475e2c6900d5..5909f8171843 100644 --- a/compiler/src/dotty/tools/dotc/printing/Texts.scala +++ b/compiler/src/dotty/tools/dotc/printing/Texts.scala @@ -139,7 +139,8 @@ object Texts { def mkString(width: Int, withLineNumbers: Boolean): String = { val sb = new StringBuilder - val numberWidth = if (withLineNumbers) (2 * maxLine.toString.length) + 2 else 0 + // width display can be upto a range "n-n" where 1 <= n <= maxLine+1 + val numberWidth = if (withLineNumbers) (2 * (maxLine + 1).toString.length) + 2 else 0 layout(width - numberWidth).print(sb, numberWidth) sb.toString } diff --git a/compiler/src/dotty/tools/dotc/profile/Profiler.scala b/compiler/src/dotty/tools/dotc/profile/Profiler.scala index ab3e73468385..8b1192871cff 100644 --- a/compiler/src/dotty/tools/dotc/profile/Profiler.scala +++ b/compiler/src/dotty/tools/dotc/profile/Profiler.scala @@ -117,12 +117,12 @@ sealed trait Profiler { protected def beforeImplicitSearch(pt: Type): TracedEventId = TracedEventId.Empty protected def afterImplicitSearch(event: TracedEventId): Unit = () - inline def onMacroSplice[T](macroSym: Symbol)(inline body: T): T = - val event = beforeMacroSplice(macroSym) + inline def onInlineCall[T](inlineSym: Symbol)(inline body: T): T = + val event = beforeInlineCall(inlineSym) try body - finally afterMacroSplice(event) - protected def beforeMacroSplice(macroSym: Symbol): TracedEventId = TracedEventId.Empty - protected def afterMacroSplice(event: TracedEventId): Unit = () + finally afterInlineCall(event) + protected def beforeInlineCall(inlineSym: Symbol): TracedEventId = TracedEventId.Empty + protected def afterInlineCall(event: TracedEventId): Unit = () inline def onCompletion[T](root: Symbol, associatedFile: => AbstractFile)(inline body: T): T = val (event, completionName) = beforeCompletion(root, associatedFile) @@ -178,7 +178,7 @@ private [profile] class RealProfiler(reporter : ProfileReporter)(using Context) enum Category: def name: String = this.toString().toLowerCase() - case Run, Phase, File, TypeCheck, Implicit, Macro, Completion + case Run, Phase, File, TypeCheck, Implicit, Inline, Completion private [profile] val chromeTrace = if ctx.settings.YprofileTrace.isDefault then null @@ -317,8 +317,8 @@ private [profile] class RealProfiler(reporter : ProfileReporter)(using Context) override def beforeImplicitSearch(pt: Type): TracedEventId = traceDurationStart(Category.Implicit, s"?[${symbolName(pt.typeSymbol)}]", colour = "yellow") override def afterImplicitSearch(event: TracedEventId): Unit = traceDurationEnd(Category.Implicit, event, colour = "yellow") - override def beforeMacroSplice(macroSym: Symbol): TracedEventId = traceDurationStart(Category.Macro, s"«${symbolName(macroSym)}»", colour = "olive") - override def afterMacroSplice(event: TracedEventId): Unit = traceDurationEnd(Category.Macro, event, colour = "olive") + override def beforeInlineCall(inlineSym: Symbol): TracedEventId = traceDurationStart(Category.Inline, s"«${symbolName(inlineSym)}»", colour = "olive") + override def afterInlineCall(event: TracedEventId): Unit = traceDurationEnd(Category.Inline, event, colour = "olive") override def beforeCompletion(root: Symbol, associatedFile: => AbstractFile): (TracedEventId, String) = if chromeTrace == null diff --git a/compiler/src/dotty/tools/dotc/quoted/reflect/FromSymbol.scala b/compiler/src/dotty/tools/dotc/quoted/reflect/FromSymbol.scala index cfc09a8ed836..32c978073194 100644 --- a/compiler/src/dotty/tools/dotc/quoted/reflect/FromSymbol.scala +++ b/compiler/src/dotty/tools/dotc/quoted/reflect/FromSymbol.scala @@ -45,8 +45,14 @@ object FromSymbol { case tpd.EmptyTree => tpd.DefDef(sym) } - def valDefFromSym(sym: TermSymbol)(using Context): tpd.ValDef = sym.defTree match { + def valDefFromSym(sym: TermSymbol)(using Context): tpd.ValOrDefDef = sym.defTree match { case tree: tpd.ValDef => tree + case tree: tpd.DefDef => + // `Getters` phase replaces val class members with defs, + // so we may see a defdef here if we are running this on a symbol compiled + // in the same compilation (but before suspension, so that + // the symbol could have reached `Getters`). + tree case tpd.EmptyTree => tpd.ValDef(sym) } diff --git a/compiler/src/dotty/tools/dotc/report.scala b/compiler/src/dotty/tools/dotc/report.scala index 5517bd005077..48db6088ab0b 100644 --- a/compiler/src/dotty/tools/dotc/report.scala +++ b/compiler/src/dotty/tools/dotc/report.scala @@ -1,13 +1,11 @@ package dotty.tools.dotc -import reporting.* -import Diagnostic.* -import util.{SourcePosition, NoSourcePosition, SrcPos} -import core.* -import Contexts.*, Flags.*, Symbols.*, Decorators.* -import config.SourceVersion import ast.* -import config.Feature.sourceVersion +import core.*, Contexts.*, Flags.*, Symbols.*, Decorators.* +import config.Feature.sourceVersion, config.SourceVersion +import reporting.*, Diagnostic.* +import util.{SourcePosition, NoSourcePosition, SrcPos} + import java.lang.System.currentTimeMillis object report: @@ -23,7 +21,7 @@ object report: ctx.reporter.report(warning) def deprecationWarning(msg: Message, pos: SrcPos, origin: String = "")(using Context): Unit = - issueWarning(new DeprecationWarning(msg, pos.sourcePos, origin)) + issueWarning(DeprecationWarning(msg, addInlineds(pos), origin)) def migrationWarning(msg: Message, pos: SrcPos)(using Context): Unit = issueWarning(new MigrationWarning(msg, pos.sourcePos)) @@ -54,6 +52,9 @@ object report: else issueWarning(new FeatureWarning(msg, pos.sourcePos)) end featureWarning + def warning(msg: Message, pos: SrcPos, origin: String)(using Context): Unit = + issueWarning(LintWarning(msg, addInlineds(pos), origin)) + def warning(msg: Message, pos: SrcPos)(using Context): Unit = issueWarning(new Warning(msg, addInlineds(pos))) @@ -125,12 +126,12 @@ object report: private def addInlineds(pos: SrcPos)(using Context): SourcePosition = def recur(pos: SourcePosition, inlineds: List[Trees.Tree[?]]): SourcePosition = inlineds match - case inlined :: inlineds1 => pos.withOuter(recur(inlined.sourcePos, inlineds1)) + case inlined :: inlineds => + val outer = recur(inlined.sourcePos, inlineds) + pos.withOuter(outer) case Nil => pos recur(pos.sourcePos, tpd.enclosingInlineds) - private object messageRendering extends MessageRendering - // Should only be called from Run#enrichErrorMessage. def enrichErrorMessage(errorMessage: String)(using Context): String = if ctx.settings.YnoEnrichErrorMessages.value then errorMessage diff --git a/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala index ce7477f4da70..27135a218d28 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala @@ -11,9 +11,11 @@ import Diagnostic.Error */ class ConsoleReporter( reader: BufferedReader = Console.in, - writer: PrintWriter = new PrintWriter(Console.err, true) + writer: PrintWriter = new PrintWriter(Console.err, true), + echoer: PrintWriter = new PrintWriter(Console.out, true) ) extends ConsoleReporter.AbstractConsoleReporter { override def printMessage(msg: String): Unit = { writer.print(msg + "\n"); writer.flush() } + override def echoMessage(msg: String): Unit = { echoer.println(msg); echoer.flush() } override def flush()(using Context): Unit = writer.flush() override def doReport(dia: Diagnostic)(using Context): Unit = { @@ -29,6 +31,9 @@ object ConsoleReporter { /** Prints the message. */ def printMessage(msg: String): Unit + /** Print the informative message. */ + def echoMessage(msg: String): Unit + /** Prints the message with the given position indication. */ def doReport(dia: Diagnostic)(using Context): Unit = { printMessage(messageAndPos(dia)) diff --git a/compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala b/compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala index 6a2d88f4e82f..e814a0811adf 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Diagnostic.scala @@ -8,9 +8,9 @@ import dotty.tools.dotc.config.Settings.Setting import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.interfaces.Diagnostic.{ERROR, INFO, WARNING} import dotty.tools.dotc.util.SourcePosition +import dotty.tools.dotc.util.chaining.* import java.util.{Collections, Optional, List => JList} -import scala.util.chaining.* import core.Decorators.toMessage object Diagnostic: @@ -36,6 +36,20 @@ object Diagnostic: pos: SourcePosition ) extends Error(msg, pos) + /** A Warning with an origin. The semantics of `origin` depend on the warning. + * For example, an unused import warning has an origin that specifies the unused selector. + * The origin of a deprecation is the deprecated element. + */ + trait OriginWarning(val origin: String): + self: Warning => + object OriginWarning: + val NoOrigin = "..." + + /** Lints are likely to be filtered. Provide extra axes for filtering by `-Wconf`. + */ + class LintWarning(msg: Message, pos: SourcePosition, origin: String = OriginWarning.NoOrigin) + extends Warning(msg, pos), OriginWarning(origin) + class Warning( msg: Message, pos: SourcePosition @@ -73,13 +87,9 @@ object Diagnostic: def enablingOption(using Context): Setting[Boolean] = ctx.settings.unchecked } - class DeprecationWarning( - msg: Message, - pos: SourcePosition, - val origin: String - ) extends ConditionalWarning(msg, pos) { + class DeprecationWarning(msg: Message, pos: SourcePosition, origin: String) + extends ConditionalWarning(msg, pos), OriginWarning(origin): def enablingOption(using Context): Setting[Boolean] = ctx.settings.deprecation - } class MigrationWarning( msg: Message, @@ -104,5 +114,5 @@ class Diagnostic( override def diagnosticRelatedInformation: JList[interfaces.DiagnosticRelatedInformation] = Collections.emptyList() - override def toString: String = s"$getClass at $pos: $message" + override def toString: String = s"$getClass at $pos L${pos.line+1}: $message" end Diagnostic diff --git a/compiler/src/dotty/tools/dotc/reporting/DidYouMean.scala b/compiler/src/dotty/tools/dotc/reporting/DidYouMean.scala index 14e5e7798e05..0302c58a063c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/DidYouMean.scala +++ b/compiler/src/dotty/tools/dotc/reporting/DidYouMean.scala @@ -5,7 +5,8 @@ package reporting import core.* import Contexts.* import Decorators.*, Symbols.*, Names.*, Types.*, Flags.* -import typer.ProtoTypes.{FunProto, SelectionProto} +import typer.ProtoTypes.SelectionProto +import collection.mutable /** A utility object to support "did you mean" hinting */ object DidYouMean: @@ -46,6 +47,8 @@ object DidYouMean: then ctx.outer else nextInteresting(ctx.outer) + val ignoredRootImports = mutable.Set.empty[Symbol] + def recur()(using Context): Unit = if ctx eq NoContext then () // done @@ -54,7 +57,7 @@ object DidYouMean: if imp.isRootImport && !rootImportOK then () // done else imp.importSym.info match - case ImportType(expr) => + case ImportType(expr) if !ignoredRootImports.contains(expr.symbol) => val candidates = memberCandidates(expr.tpe, isType, isApplied) if imp.isWildcardImport then for cand <- candidates if !imp.excluded.contains(cand.name.toTermName) do @@ -67,6 +70,8 @@ object DidYouMean: else if !sel.isUnimport then for cand <- candidates if cand.name.toTermName.show == selStr do acc += Binding(sel.rename.likeSpaced(cand.name), cand, expr.tpe) + if imp.unimported.exists then + ignoredRootImports += imp.unimported case _ => recur()(using nextInteresting(ctx)) else @@ -100,7 +105,7 @@ object DidYouMean: else (dist(j - 1)(i) min dist(j)(i - 1) min dist(j - 1)(i - 1)) + 1 dist(s2.length)(s1.length) - /** List of possible candidate names with their Levenstein distances + /** List of possible candidate names with their Levenshtein distances * to the name `from` of the missing member. * @param maxDist Maximal number of differences to be considered for a hint * A distance qualifies if it is at most `maxDist`, shorter than diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index c3cad8cbfea5..34bd999067f5 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -220,6 +220,25 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case DeprecatedInfixNamedArgumentSyntaxID // errorNumber: 204 case GivenSearchPriorityID // errorNumber: 205 case EnumMayNotBeValueClassesID // errorNumber: 206 + case IllegalUnrollPlacementID // errorNumber: 207 - unused in LTS + case ExtensionHasDefaultID // errorNumber: 208 + case FormatInterpolationErrorID // errorNumber: 209 + case ValueClassCannotExtendAliasOfAnyValID // errorNumber: 210 + case MatchIsNotPartialFunctionID // errorNumber: 211 + case OnlyFullyDependentAppliedConstructorTypeID // errorNumber: 212 + case PointlessAppliedConstructorTypeID // errorNumber: 213 + case IllegalContextBoundsID // errorNumber: 214 + case NamedPatternNotApplicableID // errorNumber: 215 + case UnnecessaryNN // errorNumber: 216 + case ErasedNotPureID // errorNumber: 217 + case IllegalErasedDefID // errorNumber: 218 + case CannotInstantiateQuotedTypeVarID // errorNumber: 219 + case DefaultShadowsGivenID // errorNumber: 220 + case RecurseWithDefaultID // errorNumber: 221 + case EncodedPackageNameID // errorNumber: 222 + case CannotBeIncludedID // errorNumber: 223 + case OverrideClassID // errorNumber: 224 + case InferUnionWarningID // errorNumber: 225 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/Message.scala b/compiler/src/dotty/tools/dotc/reporting/Message.scala index ff9c6823cfc0..4c4563b20900 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Message.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Message.scala @@ -217,7 +217,7 @@ object Message: super.toText(sym) end Printer -end Message +end Message /** A `Message` contains all semantic information necessary to easily * comprehend what caused the message to be logged. Each message can be turned @@ -364,12 +364,14 @@ abstract class Message(val errorId: ErrorMessageID)(using Context) { self => def mapMsg(f: String => String): Message = new Message(errorId): val kind = self.kind def msg(using Context) = f(self.msg) + override def msgPostscript(using Context) = self.msgPostscript def explain(using Context) = self.explain override def canExplain = self.canExplain def appendExplanation(suffix: => String): Message = new Message(errorId): val kind = self.kind def msg(using Context) = self.msg + override def msgPostscript(using Context) = self.msgPostscript def explain(using Context) = self.explain ++ suffix override def canExplain = true @@ -409,3 +411,15 @@ object NoExplanation { if (m.explanation == "") Some(m) else None } + +final class InferUnionWarning(tp: Type)(using Context) + extends Message(ErrorMessageID.InferUnionWarningID): + + def kind: MessageKind = MessageKind.Type + + def msg(using Context): String = + i"""A type argument was inferred to be union type $tp + |This may indicate a programming error. + |""" + + def explain(using Context): String = "" diff --git a/compiler/src/dotty/tools/dotc/reporting/MessageKind.scala b/compiler/src/dotty/tools/dotc/reporting/MessageKind.scala index bb02a08d2e46..e09dd1d6e69e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/MessageKind.scala +++ b/compiler/src/dotty/tools/dotc/reporting/MessageKind.scala @@ -23,6 +23,7 @@ enum MessageKind: case PotentialIssue case UnusedSymbol case Staging + case Interpolation /** Human readable message that will end up being shown to the user. * NOTE: This is only used in the situation where you have multiple words diff --git a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala index 7db5112b6674..7d17f44f82ff 100644 --- a/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala +++ b/compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala @@ -8,6 +8,7 @@ import java.lang.System.{lineSeparator => EOL} import core.Contexts.* import core.Decorators.* +import io.AbstractFile import printing.Highlighting.{Blue, Red, Yellow} import printing.SyntaxHighlighting import Diagnostic.* @@ -21,6 +22,11 @@ trait MessageRendering { import Highlight.* import Offsets.* + /** The maximal number of lines of code that are shown in a message after the + * `^` and error message. + */ + private inline val maxRenderedLinesAfterPoint = 3 + /** Remove ANSI coloring from `str`, useful for getting real length of * strings * @@ -68,9 +74,21 @@ trait MessageRendering { val lines = linesFrom(syntax) val (before, after) = pos.beforeAndAfterPoint + def compress(offsetsAndLines: List[(Int, String)]): List[(Int, String)] = + if offsetsAndLines.isEmpty then offsetsAndLines + else + val compressedLines = + if offsetsAndLines.length > maxRenderedLinesAfterPoint then + offsetsAndLines.take(maxRenderedLinesAfterPoint - 2) + ++ List( + (offsetsAndLines(maxRenderedLinesAfterPoint - 2)._1, "..."), + offsetsAndLines.last) + else offsetsAndLines + compressedLines + ( before.zip(lines).map(render), - after.zip(lines.drop(before.length)).map(render), + compress(after.zip(lines.drop(before.length))).map(render), maxLen ) } @@ -144,7 +162,7 @@ trait MessageRendering { * * @return aligned error message */ - private def errorMsg(pos: SourcePosition, msg: String)(using Context, Level, Offset): String = { + private def errorMsg(pos: SourcePosition, msg: String, addLine: Boolean)(using Context, Level, Offset): String = { val padding = msg.linesIterator.foldLeft(pos.startColumnPadding) { (pad, line) => val lineLength = stripColor(line).length val maxPad = math.max(0, ctx.settings.pageWidth.value - offset - lineLength) - offset @@ -153,14 +171,18 @@ trait MessageRendering { else pad } - msg.linesIterator + val msgStr = msg.linesIterator .map { line => offsetBox + (if line.isEmpty then "" else padding + line) } .mkString(EOL) + if addLine then msgStr ++ s"${EOL}$offsetBox" else msgStr } + // file.path or munge it to normalize for testing + protected def renderPath(file: AbstractFile): String = file.path + /** The source file path, line and column numbers from the given SourcePosition */ protected def posFileStr(pos: SourcePosition): String = - val path = pos.source.file.path + val path = renderPath(pos.source.file) if pos.exists then s"$path:${pos.line + 1}:${pos.column}" else path /** The separator between errors containing the source file and error type @@ -209,20 +231,21 @@ trait MessageRendering { sb.toString } - private def appendFilterHelp(dia: Diagnostic, sb: StringBuilder): Unit = + private def appendFilterHelp(dia: Diagnostic, sb: StringBuilder)(using Context, Level, Offset): Unit = + extension (sb: StringBuilder) def nl: sb.type = sb.append(EOL).append(offsetBox) import dia.msg val hasId = msg.errorId.errorNumber >= 0 val (category, origin) = dia match - case _: UncheckedWarning => ("unchecked", "") + case _: UncheckedWarning => ("unchecked", "") case w: DeprecationWarning => ("deprecation", w.origin) - case _: FeatureWarning => ("feature", "") - case _ => ("", "") + case _: FeatureWarning => ("feature", "") + case _ => ("", "") var entitled = false def addHelp(what: String)(value: String): Unit = if !entitled then - sb.append(EOL).append("Matching filters for @nowarn or -Wconf:") + sb.nl.append("Matching filters for @nowarn or -Wconf:") entitled = true - sb.append(EOL).append(" - ").append(what).append(value) + sb.nl.append(" - ").append(what).append(value) if hasId then addHelp("id=E")(msg.errorId.errorNumber.toString) addHelp("name=")(msg.errorId.productPrefix.stripSuffix("ID")) @@ -249,7 +272,7 @@ trait MessageRendering { if (pos1.exists && pos1.source.file.exists) { val (srcBefore, srcAfter, offset) = sourceLines(pos1) val marker = positionMarker(pos1) - val err = errorMsg(pos1, msg.message) + val err = errorMsg(pos1, msg.message, srcAfter.nonEmpty) sb.append((srcBefore ::: marker :: err :: srcAfter).mkString(EOL)) if inlineStack.nonEmpty then diff --git a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala index eb9756028c27..bafac8cb2517 100644 --- a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala @@ -9,6 +9,7 @@ import dotty.tools.dotc.core.Mode import dotty.tools.dotc.core.Symbols.{NoSymbol, Symbol} import dotty.tools.dotc.reporting.Diagnostic.* import dotty.tools.dotc.reporting.Message.* +import dotty.tools.dotc.rewrites.Rewrites import dotty.tools.dotc.util.NoSourcePosition import java.io.{BufferedReader, PrintWriter} @@ -30,6 +31,10 @@ object Reporter { override def report(dia: Diagnostic)(using Context): Unit = () } + /** A silent reporter for testing */ + class SilentReporter extends Reporter: + def doReport(dia: Diagnostic)(using Context): Unit = () + type ErrorHandler = (Diagnostic, Context) => Unit private val defaultIncompleteHandler: ErrorHandler = @@ -95,6 +100,7 @@ abstract class Reporter extends interfaces.ReporterResult { private var _errorCount = 0 private var _warningCount = 0 + private var _infoCount = 0 /** The number of errors reported by this reporter (ignoring outer reporters) */ def errorCount: Int = _errorCount @@ -112,12 +118,17 @@ abstract class Reporter extends interfaces.ReporterResult { private var warnings: List[Warning] = Nil + private var infos: List[Info] = Nil + /** All errors reported by this reporter (ignoring outer reporters) */ def allErrors: List[Error] = errors /** All warnings reported by this reporter (ignoring outer reporters) */ def allWarnings: List[Warning] = warnings + /** All infos reported by this reporter (ignoring outer reporters) */ + def allInfos: List[Info] = infos + /** Were sticky errors reported? Overridden in StoreReporter. */ def hasStickyErrors: Boolean = false @@ -148,10 +159,14 @@ abstract class Reporter extends interfaces.ReporterResult { unreportedWarnings = unreportedWarnings.updated(key, count + n) /** Issue the diagnostic, ignoring `-Wconf` and `@nowarn` configurations, - * but still honouring `-nowarn`, `-Werror`, and conditional warnings. */ - def issueUnconfigured(dia: Diagnostic)(using Context): Unit = dia match + * but still honouring `-nowarn`, `-Werror`, and conditional warnings. + * + * Avoid forcing elaboration of the message, but if already `forced`, + * then do issue the diagnostic with usual `isHidden` check. + */ + def issueUnconfigured(dia: Diagnostic, forced: Boolean = false)(using Context): Unit = dia match case w: Warning if ctx.settings.silentWarnings.value => - case w: ConditionalWarning if w.isSummarizedConditional => + case w: ConditionalWarning if !forced && w.isSummarizedConditional => val key = w.enablingOption.name addUnreported(key, 1) case _ => @@ -164,6 +179,8 @@ abstract class Reporter extends interfaces.ReporterResult { handleRecursive("error reporting", dia.message, ex) dia match { case w: Warning => + if w.isInstanceOf[LintWarning] then + w.msg.actions.foreach(Rewrites.applyAction) warnings = w :: warnings _warningCount += 1 case e: Error => @@ -171,16 +188,21 @@ abstract class Reporter extends interfaces.ReporterResult { _errorCount += 1 if ctx.typerState.isGlobalCommittable then ctx.base.errorsToBeReported = true - case _: Info => // nothing to do here + case i: Info => + infos = i :: infos + _infoCount += 1 // match error if d is something else } markReported(dia) end issueUnconfigured def issueIfNotSuppressed(dia: Diagnostic)(using Context): Unit = - def toErrorIfFatal(dia: Diagnostic) = dia match + /** When `wconfEmitsFull`, a conditional warning was matched by `-Wconf` and fully emitted: + * `-Werror` must still apply, unlike summarized conditional warnings (no `-deprecation`, etc.). + */ + def toErrorIfFatal(dia: Diagnostic, wconfEmitsFull: Boolean = false) = dia match case w: Warning if ctx.settings.silentWarnings.value => dia - case w: ConditionalWarning if w.isSummarizedConditional => dia + case w: ConditionalWarning if !wconfEmitsFull && w.isSummarizedConditional => dia case w: Warning if ctx.settings.XfatalWarnings.value => w.toError case _ => dia @@ -189,10 +211,11 @@ abstract class Reporter extends interfaces.ReporterResult { dia match case w: Warning => WConf.parsed.action(dia) match case Error => issueUnconfigured(w.toError) - case Warning => issueUnconfigured(toErrorIfFatal(w)) + case Warning => issueUnconfigured(toErrorIfFatal(w, wconfEmitsFull = true), forced = true) case Verbose => issueUnconfigured(toErrorIfFatal(w.setVerbose())) case Info => issueUnconfigured(w.toInfo) case Silent => + case Default => issueUnconfigured(toErrorIfFatal(w)) case _ => issueUnconfigured(dia) // `ctx.run` can be null in test, also in the repl when parsing the first line. The parser runs early, the Run is diff --git a/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala b/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala index 9395788d4cc7..3bb7b64154a6 100644 --- a/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/StoreReporter.scala @@ -4,7 +4,6 @@ package reporting import core.Contexts.* import collection.mutable -import config.Printers.typr import Diagnostic.* /** This class implements a Reporter that stores all messages @@ -22,7 +21,6 @@ class StoreReporter(outer: Reporter | Null = Reporter.NoReporter, fromTyperState protected var infos: mutable.ListBuffer[Diagnostic] | Null = null override def doReport(dia: Diagnostic)(using Context): Unit = { - typr.println(s">>>> StoredError: ${dia.message}") // !!! DEBUG if (infos == null) infos = new mutable.ListBuffer infos.uncheckedNN += dia } @@ -49,6 +47,6 @@ class StoreReporter(outer: Reporter | Null = Reporter.NoReporter, fromTyperState // so that then only when the messages are unbuffered (when the reporter if flushed) // do they go through -Wconf, and possibly then buffered on the Run as a suspended message override def report(dia: Diagnostic)(using Context): Unit = - if fromTyperState then issueUnconfigured(dia) + if fromTyperState then issueUnconfigured(dia, forced = false) else super.report(dia) } diff --git a/compiler/src/dotty/tools/dotc/reporting/WConf.scala b/compiler/src/dotty/tools/dotc/reporting/WConf.scala index 3842067f5390..89d95a6094f1 100644 --- a/compiler/src/dotty/tools/dotc/reporting/WConf.scala +++ b/compiler/src/dotty/tools/dotc/reporting/WConf.scala @@ -10,15 +10,18 @@ import dotty.tools.dotc.interfaces.SourceFile import dotty.tools.dotc.reporting.MessageFilter.SourcePattern import java.util.regex.PatternSyntaxException +import scala.PartialFunction.cond import scala.annotation.internal.sharable import scala.util.matching.Regex enum MessageFilter: - def matches(message: Diagnostic): Boolean = this match + def matches(message: Diagnostic): Boolean = + import Diagnostic.* + this match case Any => true - case Deprecated => message.isInstanceOf[Diagnostic.DeprecationWarning] - case Feature => message.isInstanceOf[Diagnostic.FeatureWarning] - case Unchecked => message.isInstanceOf[Diagnostic.UncheckedWarning] + case Deprecated => message.isInstanceOf[DeprecationWarning] + case Feature => message.isInstanceOf[FeatureWarning] + case Unchecked => message.isInstanceOf[UncheckedWarning] case MessageID(errorId) => message.msg.errorId == errorId case MessagePattern(pattern) => val noHighlight = message.msg.message.replaceAll("\\e\\[[\\d;]*[^\\d;]","") @@ -31,7 +34,8 @@ enum MessageFilter: pattern.findFirstIn(path).nonEmpty case Origin(pattern) => message match - case message: Diagnostic.DeprecationWarning => pattern.findFirstIn(message.origin).nonEmpty + case message: OriginWarning if message.origin != OriginWarning.NoOrigin => + pattern.findFirstIn(message.origin).nonEmpty case _ => false case None => false @@ -42,12 +46,13 @@ enum MessageFilter: case Origin(pattern: Regex) enum Action: - case Error, Warning, Verbose, Info, Silent + case Error, Warning, Verbose, Info, Silent, Default final case class WConf(confs: List[(List[MessageFilter], Action)]): - def action(message: Diagnostic): Action = confs.collectFirst { - case (filters, action) if filters.forall(_.matches(message)) => action - }.getOrElse(Action.Warning) + def action(message: Diagnostic): Action = + confs.collectFirst{ + case (filters, action) if filters.forall(_.matches(message)) => action + }.getOrElse(Action.Default) object WConf: import Action.* @@ -56,12 +61,12 @@ object WConf: private type Conf = (List[MessageFilter], Action) def parseAction(s: String): Either[List[String], Action] = s match - case "error" | "e" => Right(Error) - case "warning" | "w" => Right(Warning) - case "verbose" | "v" => Right(Verbose) - case "info" | "i" => Right(Info) - case "silent" | "s" => Right(Silent) - case _ => Left(List(s"unknown action: `$s`")) + case "error" | "e" => Right(Error) + case "warning" | "w" => Right(Warning) + case "verbose" | "v" => Right(Verbose) + case "info" | "i" => Right(Info) + case "silent" | "s" => Right(Silent) + case _ => Left(List(s"unknown action: `$s`")) private def regex(s: String) = try Right(s.r) @@ -134,11 +139,20 @@ object WConf: if (parseErrorss.nonEmpty) Left(parseErrorss.flatten) else Right(WConf(configs)) -class Suppression(val annotPos: SourcePosition, filters: List[MessageFilter], val start: Int, end: Int, val verbose: Boolean): - private[this] var _used = false - def used: Boolean = _used - def markUsed(): Unit = { _used = true } - +class Suppression(val annotPos: SourcePosition, val filters: List[MessageFilter], val start: Int, val end: Int, val verbose: Boolean): + inline def unusedState = 0 + inline def usedState = 1 + inline def supersededState = 2 + private var _used = unusedState + def used: Boolean = _used == usedState + def superseded: Boolean = _used == supersededState + def markUsed(): Unit = + _used = usedState + def markSuperseded(): Unit = + _used = supersededState def matches(dia: Diagnostic): Boolean = val pos = dia.pos pos.exists && start <= pos.start && pos.end <= end && filters.forall(_.matches(dia)) + + override def toString = s"Suppress in ${annotPos.source} $start..$end [${filters.mkString(", ")}]" +end Suppression diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index f9210845bd54..825229dabffe 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -14,11 +14,12 @@ import printing.Highlighting.* import printing.Formatting import ErrorMessageID.* import ast.Trees +import ast.desugar import config.{Feature, ScalaVersion} import transform.patmat.Space import transform.patmat.SpaceEngine import typer.ErrorReporting.{err, matchReductionAddendum, substitutableTypeSymbolsInScope} -import typer.ProtoTypes.{ViewProto, SelectionProto, FunProto} +import typer.ProtoTypes.{ViewProto, FunProto} import typer.Implicits.* import typer.Inferencing import scala.util.control.NonFatal @@ -33,7 +34,6 @@ import cc.CaptureSet.IdentityCaptRefMap import dotty.tools.dotc.rewrites.Rewrites.ActionPatch import dotty.tools.dotc.util.Spans.Span import dotty.tools.dotc.util.SourcePosition -import scala.jdk.CollectionConverters.* import dotty.tools.dotc.util.SourceFile import DidYouMean.* @@ -60,7 +60,7 @@ trait ShowMatchTrace(tps: Type*)(using Context) extends Message: override def msgPostscript(using Context): String = super.msgPostscript ++ matchReductionAddendum(tps*) -abstract class TypeMismatchMsg(found: Type, expected: Type)(errorId: ErrorMessageID)(using Context) +abstract class TypeMismatchMsg(found: Type, val expected: Type)(errorId: ErrorMessageID)(using Context) extends Message(errorId), ShowMatchTrace(found, expected): def kind = MessageKind.TypeMismatch def explain(using Context) = err.whyNoMatchStr(found, expected) @@ -270,13 +270,18 @@ class MissingIdent(tree: untpd.Ident, treeKind: String, val name: Name, proto: T extends NotFoundMsg(MissingIdentID) { def msg(using Context) = val missing = name.show - val addendum = - didYouMean( - inScopeCandidates(name.isTypeName, isApplied = proto.isInstanceOf[FunProto], rootImportOK = true) - .closestTo(missing), - proto, "") - - i"Not found: $treeKind$name$addendum" + // If we're looking for a term or type X and a type or term X respectively is in scope, don't emit "Not found: X" as that's confusing for beginners + val wrongKind = inScopeCandidates(!name.isTypeName, isApplied = proto.isInstanceOf[FunProto], rootImportOK = true).closestTo(missing, maxDist = 0) + if wrongKind.nonEmpty then + if name.isTypeName then + val extra = if wrongKind.exists(_._2.sym.denot.is(Module)) then f" - did you mean $name.type?" else "" + i"Expected a type, but found a term: $name$extra" + else + i"Expected a term, but found a type: $name" + else + val candidates = inScopeCandidates(name.isTypeName, isApplied = proto.isInstanceOf[FunProto], rootImportOK = true).closestTo(missing) + val addendum = didYouMean(candidates, proto, "") + i"Not found: $treeKind$name$addendum" def explain(using Context) = { i"""|Each identifier in Scala needs a matching declaration. There are two kinds of |identifiers: type identifiers and value identifiers. Value identifiers are introduced @@ -288,13 +293,19 @@ extends NotFoundMsg(MissingIdentID) { | |Possible reasons why no matching declaration was found: | - The declaration or the use is mis-spelt. - | - An import is missing.""" + | - An import is missing. + | - The declaration exists but refers to a type in a context where a term is expected, or vice-versa.""" } } 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. @@ -352,9 +363,29 @@ class TypeMismatch(val found: Type, expected: Type, val inTree: Option[untpd.Tre ++ addenda.dropWhile(_.isEmpty).headOption.getOrElse(importSuggestions) override def explain(using Context) = - val treeStr = inTree.map(x => s"\nTree: ${x.show}").getOrElse("") + 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) @@ -921,11 +952,11 @@ class UncheckedTypePattern(argType: Type, whyNot: String)(using Context) |""" } -class MatchCaseUnreachable()(using Context) +class MatchCaseUnreachable(why: String = "")(using Context) extends Message(MatchCaseUnreachableID) { def kind = MessageKind.MatchCaseUnreachable - def msg(using Context) = "Unreachable case" - def explain(using Context) = "" + override protected def msg(using Context) = "Unreachable case" + override protected def explain(using Context) = why } class MatchCaseOnlyNullWarning()(using Context) @@ -1186,14 +1217,22 @@ extends DeclarationMsg(OverrideErrorID), NoDisambiguation: class ForwardReferenceExtendsOverDefinition(value: Symbol, definition: Symbol)(using Context) extends ReferenceMsg(ForwardReferenceExtendsOverDefinitionID) { - def msg(using Context) = i"${definition.name} is a forward reference extending over the definition of ${value.name}" + extension (sym: Symbol) def srcLine = sym.line + 1 + + def msg(using Context) = + val ref = + if value != definition then i"${definition.name} (defined on line ${definition.srcLine})" + else i"${definition.name}" + i"forward reference to ${ref} extends over the definition of ${value.name} (on line ${value.srcLine})" def explain(using Context) = i"""|${definition.name} is used before you define it, and the definition of ${value.name} |appears between that use and the definition of ${definition.name}. | - |Forward references are allowed only, if there are no value definitions between - |the reference and the referred method definition. + |Forward references are allowed only if there are no value definitions between + |the reference and the definition that is referred to. + |Specifically, any statement between the reference and the definition + |cannot be a variable definition, and if it's a value definition, it must be lazy. | |Define ${definition.name} before it is used, |or move the definition of ${value.name} so it does not appear between @@ -1506,17 +1545,25 @@ class AmbiguousExtensionMethod(tree: untpd.Tree, expansion1: tpd.Tree, expansion |are possible expansions of $tree""" def explain(using Context) = "" -class ReassignmentToVal(name: Name)(using Context) +class ReassignmentToVal(name: Name, pt: Type)(using Context) extends TypeMsg(ReassignmentToValID) { - def msg(using Context) = i"""Reassignment to val $name""" - def explain(using Context) = - i"""|You can not assign a new value to $name as values can't be changed. - |Keep in mind that every statement has a value, so you may e.g. use - | ${hl("val")} $name ${hl("= if (condition) 2 else 5")} - |In case you need a reassignable name, you can declare it as - |variable - | ${hl("var")} $name ${hl("=")} ... - |""" + + def booleanExpected = pt.isRef(defn.BooleanClass) + def booleanNote = + if booleanExpected then + ". Maybe you meant to write an equality test using `==`?" + else "" + def msg(using Context) + = i"""Reassignment to val $name$booleanNote""" + + def explain(using Context) = + if booleanExpected then + i"""An equality test is written "x == y" using `==`. A single `=` stands + |for assigment, where a variable on the left gets a new value on the right. + |This is only permitted if the variable is declared with `var`.""" + else + i"""You can not assign a new value to $name as values can't be changed. + |Reassigment is only permitted if the variable is declared with `var`.""" } class TypeDoesNotTakeParameters(tpe: Type, params: List[untpd.Tree])(using Context) @@ -1793,6 +1840,12 @@ class ValueClassParameterMayNotBeCallByName(valueClass: Symbol, param: Symbol)(u def explain(using Context) = "" } +class ValueClassCannotExtendAliasOfAnyVal(valueClass: Symbol, alias: Symbol)(using Context) + extends SyntaxMsg(ValueClassCannotExtendAliasOfAnyValID) { + def msg(using Context) = i"""A value class cannot extend a type alias ($alias) of ${hl("AnyVal")}""" + def explain(using Context) = "" +} + class SuperCallsNotAllowedInlineable(symbol: Symbol)(using Context) extends SyntaxMsg(SuperCallsNotAllowedInlineableID) { def msg(using Context) = i"Super call not allowed in inlineable $symbol" @@ -2055,8 +2108,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 @@ -2294,13 +2366,17 @@ class JavaSymbolIsNotAValue(symbol: Symbol)(using Context) extends TypeMsg(JavaS } class DoubleDefinition(decl: Symbol, previousDecl: Symbol, base: Symbol)(using Context) -extends NamingMsg(DoubleDefinitionID) { +extends NamingMsg(DoubleDefinitionID): + import Signature.MatchDegree.* + + private def erasedType: Type = + if ctx.erasedTypes then decl.info + else TypeErasure.transformInfo(decl, decl.info) + def msg(using Context) = { def nameAnd = if (decl.name != previousDecl.name) " name and" else "" - def erasedType = if ctx.erasedTypes then i" ${decl.info}" else "" def details(using Context): String = if (decl.isRealMethod && previousDecl.isRealMethod) { - import Signature.MatchDegree.* // compare the signatures when both symbols represent methods decl.signature.matchDegree(previousDecl.signature) match { @@ -2325,9 +2401,15 @@ extends NamingMsg(DoubleDefinitionID) { |Consider adding a @targetName annotation to one of the conflicting definitions |for disambiguation.""" else "" - i"have the same$nameAnd type$erasedType after erasure.$hint" + i"have the same$nameAnd type $erasedType after erasure.$hint" } } + else if decl.is(CaseAccessor) || previousDecl.is(CaseAccessor) then + val selector = """_(\d+)""".r + decl.name.toString match + case selector(n) => + s"${decl.name} is a case element selector and must name the ${n}th element" + case _ => "" else "" def symLocation(sym: Symbol) = { val lineDesc = @@ -2338,7 +2420,7 @@ extends NamingMsg(DoubleDefinitionID) { } val clashDescription = if (decl.owner eq previousDecl.owner) - "Double definition" + "Conflicting definitions" else if ((decl.owner eq base) || (previousDecl eq base)) "Name clash between defined and inherited member" else @@ -2351,8 +2433,62 @@ extends NamingMsg(DoubleDefinitionID) { |""" } + details } - def explain(using Context) = "" -} + 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""" + |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 + | + | ${atPhase(typerPhase)(previousDecl.showDcl)} + | ${atPhase(typerPhase)(decl.showDcl)} + | + |erase to the identical signature + | + | ${erasedType} + | + |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.$givenAddendum + |2. Keep the same names in source but give one definition a distinct + | bytecode-level name via `@targetName`; for example: + | + | @targetName("${decl.name.show}_2") + | ${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 ImportRenamedTwice(ident: untpd.Ident)(using Context) extends SyntaxMsg(ImportRenamedTwiceID) { def msg(using Context) = s"${ident.show} is renamed twice on the same import line." @@ -2474,6 +2610,17 @@ class ExtensionNullifiedByMember(method: Symbol, target: Symbol)(using Context) | |The extension may be invoked as though selected from an arbitrary type if conversions are in play.""" +class ExtensionHasDefault(method: Symbol)(using Context) + extends Message(ExtensionHasDefaultID): + def kind = MessageKind.PotentialIssue + def msg(using Context) = + i"""Extension method ${hl(method.name.toString)} should not have a default argument for its receiver.""" + def explain(using Context) = + i"""The receiver cannot be omitted when an extension method is invoked as a selection. + |A default argument for that parameter would never be used in that case. + |An extension method can be invoked as a regular method, but if that is the intended usage, + |it should not be defined as an extension.""" + class TraitCompanionWithMutableStatic()(using Context) extends SyntaxMsg(TraitCompanionWithMutableStaticID) { def msg(using Context) = i"Companion of traits cannot define mutable @static fields" @@ -3081,7 +3228,7 @@ extends ReferenceMsg(CannotBeAccessedID): case _ => i"none of the overloaded alternatives named $name can" val where = if (ctx.owner.exists) i" from ${ctx.owner.enclosingClass}" else "" - val whyNot = new StringBuffer + val whyNot = new StringBuilder for alt <- alts do val cls = alt.owner.enclosingSubClass val owner = if cls.exists then cls else alt.owner @@ -3089,10 +3236,10 @@ extends ReferenceMsg(CannotBeAccessedID): if alt.is(Protected) then if alt.privateWithin.exists && alt.privateWithin != owner then if owner.is(Final) then alt.privateWithin.showLocated - else alt.privateWithin.showLocated + ", or " + owner.showLocated + " or one of its subclasses" + else s"${alt.privateWithin.showLocated}, or ${owner.showLocated} or one of its subclasses" else if owner.is(Final) then owner.showLocated - else owner.showLocated + " or one of its subclasses" + else s"${owner.showLocated} or one of its subclasses" else alt.privateWithin.orElse(owner).showLocated val accessMod = if alt.is(Protected) then "protected" else "private" @@ -3155,22 +3302,33 @@ extends SyntaxMsg(VolatileOnValID): protected def msg(using Context): String = "values cannot be volatile" protected def explain(using Context): String = "" -class UnusedSymbol(errorText: String)(using Context) -extends Message(UnusedSymbolID) { + +class UnusedSymbol(errorText: String, val actions: List[CodeAction] = Nil)(using Context) +extends Message(UnusedSymbolID): def kind = MessageKind.UnusedSymbol override def msg(using Context) = errorText override def explain(using Context) = "" -} - -object UnusedSymbol { - def imports(using Context): UnusedSymbol = new UnusedSymbol(i"unused import") - def localDefs(using Context): UnusedSymbol = new UnusedSymbol(i"unused local definition") - def explicitParams(using Context): UnusedSymbol = new UnusedSymbol(i"unused explicit parameter") - def implicitParams(using Context): UnusedSymbol = new UnusedSymbol(i"unused implicit parameter") - def privateMembers(using Context): UnusedSymbol = new UnusedSymbol(i"unused private member") - def patVars(using Context): UnusedSymbol = new UnusedSymbol(i"unused pattern variable") -} + override def actions(using Context) = this.actions + +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") + def unsetPrivates(using Context): UnusedSymbol = + UnusedSymbol(i"unset private variable, consider using an immutable val instead") + private def paramAddendum(sym: Symbol)(using Context): String = + if sym.denot.owner.is(ExtensionMethod) then i" in extension ${sym.denot.owner}" + else "" final class QuotedTypeMissing(tpe: Type)(using Context) extends StagingMessage(QuotedTypeMissingID): @@ -3194,3 +3352,74 @@ final class EnumMayNotBeValueClasses(sym: Symbol)(using Context) extends SyntaxM def explain(using Context) = "" end EnumMayNotBeValueClasses + +class BadFormatInterpolation(errorText: String)(using Context) extends Message(FormatInterpolationErrorID): + def kind = MessageKind.Interpolation + protected def msg(using Context) = errorText + protected def explain(using Context) = "" + +class MatchIsNotPartialFunction(using Context) extends SyntaxMsg(MatchIsNotPartialFunctionID): + protected def msg(using Context) = + "match expression in result of block will not be used to synthesize partial function" + protected def explain(using Context) = + i"""A `PartialFunction` can be synthesized from a function literal if its body is just a pattern match. + | + |For example, `collect` takes a `PartialFunction`. + | (1 to 10).collect(i => i match { case n if n % 2 == 0 => n }) + |is equivalent to using a "pattern-matching anonymous function" directly: + | (1 to 10).collect { case n if n % 2 == 0 => n } + |Compare an operation that requires a `Function1` instead: + | (1 to 10).map { case n if n % 2 == 0 => n case n => n + 1 } + | + |As a convenience, the "selector expression" of the match can be an arbitrary expression: + | List("1", "two", "3").collect(x => Try(x.toInt) match { case Success(i) => i }) + |In this example, `isDefinedAt` evaluates the selector expression and any guard expressions + |in the pattern match in order to report whether an input is in the domain of the function. + | + |However, blocks of statements are not supported by this idiom: + | List("1", "two", "3").collect: x => + | val maybe = Try(x.toInt) // statements preceding the match + | maybe match + | case Success(i) if i % 2 == 0 => i // throws MatchError on cases not covered + | + |This restriction is enforced to simplify the evaluation semantics of the partial function. + |Otherwise, it might not be clear what is computed by `isDefinedAt`. + | + |Efficient operations will use `applyOrElse` to avoid computing the match twice, + |but the `apply` body would be executed "per element" in the example.""" + +final class IllegalContextBounds(using Context) extends SyntaxMsg(IllegalContextBoundsID): + override protected def msg(using Context): String = + i"Context bounds are not allowed in this position" + + override protected def explain(using Context): String = "" + +final class NamedPatternNotApplicable(selectorType: Type)(using Context) extends PatternMatchMsg(NamedPatternNotApplicableID): + override protected def msg(using Context): String = + i"Named patterns cannot be used with $selectorType, because it is not a named tuple or case class" + + override protected def explain(using Context): String = "" + +final class DefaultShadowsGiven(name: Name)(using Context) extends TypeMsg(DefaultShadowsGivenID): + override protected def msg(using Context): String = + i"Argument for implicit parameter $name was supplied using a default argument." + override protected def explain(using Context): String = + "Usually the given in scope is intended, but you must specify it after explicit `using`." + +final class RecurseWithDefault(name: Name)(using Context) extends TypeMsg(RecurseWithDefaultID): + override protected def msg(using Context): String = + i"Recursive call used a default argument for parameter $name." + override protected def explain(using Context): String = + "It's more explicit to pass current or modified arguments in a recursion." + +final class EncodedPackageName(name: Name)(using Context) extends SyntaxMsg(EncodedPackageNameID): + override protected def msg(using Context): String = + i"The package name `$name` will be encoded on the classpath, and can lead to undefined behaviour." + override protected def explain(using Context): String = + i"""Tools may not handle directories whose names differ from their corresponding package names. + |For example, `p-q` is encoded as `p$$minusq` when written to the file system. + | + |Package objects derive their names from the file names, so files such as `myfile.test.scala` + |or `myfile-test.scala` can produce encoded names for the generated package objects. + | + |In this case, the name `$name` is encoded as `${name.encode}`.""" diff --git a/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala b/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala index 3c7216625a7c..a702c3e265ac 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/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index 73f69dd6f2ad..3ce77b8c8862 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -24,7 +24,7 @@ import java.io.PrintWriter import scala.collection.mutable import scala.util.hashing.MurmurHash3 -import scala.util.chaining.* +import dotty.tools.dotc.util.chaining.* /** This phase sends a representation of the API of classes to sbt via callbacks. * @@ -61,7 +61,7 @@ class ExtractAPI extends Phase { // definitions, and `PostTyper` does not change definitions). override def runsAfter: Set[String] = Set(transform.PostTyper.name) - override def run(using Context): Unit = { + protected def run(using Context): Unit = { val unit = ctx.compilationUnit val sourceFile = unit.source ctx.withIncCallback: cb => diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala index 2fb5dbbc24f1..6ae7bf7edfa4 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala @@ -66,7 +66,7 @@ class ExtractDependencies extends Phase { // See the scripted test `constants` for an example where this matters. // TODO: Add a `Phase#runsBefore` method ? - override def run(using Context): Unit = { + protected def run(using Context): Unit = { val unit = ctx.compilationUnit val collector = new ExtractDependenciesCollector collector.traverse(unit.tpdTree) diff --git a/compiler/src/dotty/tools/dotc/semanticdb/ConstantOps.scala b/compiler/src/dotty/tools/dotc/semanticdb/ConstantOps.scala index 784b23cfc78c..aea2a08877cc 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/ConstantOps.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/ConstantOps.scala @@ -7,7 +7,7 @@ import dotty.tools.dotc.{semanticdb => s} import core.Contexts.Context import core.Constants.* -object ConstantOps: +private[semanticdb] object ConstantOps: extension (const: Constant) def toSemanticConst(using Context): s.Constant = const.tag match { case UnitTag => s.UnitConstant() diff --git a/compiler/src/dotty/tools/dotc/semanticdb/Descriptor.scala b/compiler/src/dotty/tools/dotc/semanticdb/Descriptor.scala index 2743e37cb79f..2066322fe3d3 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/Descriptor.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/Descriptor.scala @@ -5,7 +5,7 @@ import scala.language.unsafeNulls import java.lang.System.{lineSeparator => EOL} import dotty.tools.dotc.semanticdb.{Descriptor => d} -class DescriptorParser(s: String) { +private[semanticdb] class DescriptorParser(s: String) { var i = s.length def fail() = { val message = "invalid symbol format" @@ -94,14 +94,14 @@ class DescriptorParser(s: String) { } } -object DescriptorParser { +private[semanticdb] object DescriptorParser { def apply(symbol: String): (Descriptor, String) = { val parser = new DescriptorParser(symbol) parser.entryPoint() } } -sealed trait Descriptor { +private[semanticdb] sealed trait Descriptor { def isNone: Boolean = this == d.None def isTerm: Boolean = this.isInstanceOf[d.Term] def isMethod: Boolean = this.isInstanceOf[d.Method] @@ -111,7 +111,8 @@ sealed trait Descriptor { def isTypeParameter: Boolean = this.isInstanceOf[d.TypeParameter] def value: String } -object Descriptor { + +private[semanticdb] object Descriptor { case object None extends Descriptor { def value: String = "" } final case class Term(value: String) extends Descriptor final case class Method(value: String, disambiguator: String) extends Descriptor diff --git a/compiler/src/dotty/tools/dotc/semanticdb/DiagnosticOps.scala b/compiler/src/dotty/tools/dotc/semanticdb/DiagnosticOps.scala index 4bc6e1ecb026..97390d0231a9 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/DiagnosticOps.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/DiagnosticOps.scala @@ -3,10 +3,9 @@ package dotty.tools.dotc.semanticdb import dotty.tools.dotc.reporting.Diagnostic import dotty.tools.dotc.{semanticdb => s} import dotty.tools.dotc.interfaces.Diagnostic.{ERROR, INFO, WARNING} -import dotty.tools.dotc.core.Contexts.Context import scala.annotation.internal.sharable -object DiagnosticOps: +private[semanticdb] object DiagnosticOps: @sharable private val asciiColorCodes = "\u001B\\[[;\\d]*m".r extension (d: Diagnostic) def toSemanticDiagnostic: s.Diagnostic = diff --git a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala index b4a2067e5c43..07cb095c4159 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala @@ -12,7 +12,8 @@ import Contexts.* import Symbols.* import Flags.* import Names.Name -import StdNames.nme +import Types._ +import StdNames.{nme, tpnme} import NameOps.* import Denotations.StaleSymbol import util.Spans.Span @@ -24,7 +25,6 @@ import scala.jdk.CollectionConverters.* import scala.PartialFunction.condOpt import typer.ImportInfo.withRootImports -import dotty.tools.dotc.reporting.Diagnostic.Warning import dotty.tools.dotc.{semanticdb => s} import dotty.tools.io.{AbstractFile, JarArchive} import dotty.tools.dotc.semanticdb.DiagnosticOps.* @@ -47,7 +47,7 @@ import java.nio.file.Path * We need to run this phase after the "CheckUnused.PostInlining" phase * so that we can extract the warnings generated by "-Wunused". */ -class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode) extends Phase: +private[semanticdb] class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode) extends Phase: override val phaseName: String = ExtractSemanticDB.phaseNamePrefix + phaseMode.toString() @@ -63,7 +63,7 @@ class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode) extends private def computeDiagnostics( sourceRoot: String, - warnings: Map[SourceFile, List[Warning]], + warnings: Map[SourceFile, List[dotty.tools.dotc.reporting.Diagnostic]], append: ((Path, List[Diagnostic])) => Unit)(using Context): Boolean = monitor(phaseName) { val unit = ctx.compilationUnit warnings.get(unit.source).foreach { ws => @@ -104,14 +104,14 @@ class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode) extends val appendDiagnostics = phaseMode == ExtractSemanticDB.PhaseMode.AppendDiagnostics val unitContexts = units.map(ctx.fresh.setCompilationUnit(_).withRootImports) if (appendDiagnostics) - val warnings = ctx.reporter.allWarnings.groupBy(w => w.pos.source) + val warningsAndInfos = (ctx.reporter.allWarnings ++ ctx.reporter.allInfos).groupBy(w => w.pos.source) val buf = mutable.ListBuffer.empty[(Path, Seq[Diagnostic])] val units0 = - for unitCtx <- unitContexts if computeDiagnostics(sourceRoot, warnings, buf += _)(using unitCtx) + for unitCtx <- unitContexts if computeDiagnostics(sourceRoot, warningsAndInfos, buf += _)(using unitCtx) yield unitCtx.compilationUnit cancellable { - buf.toList.asJava.parallelStream().forEach { case (out, warnings) => - ExtractSemanticDB.appendDiagnostics(warnings, out) + buf.toList.asJava.parallelStream().forEach { case (out, diagnostics) => + ExtractSemanticDB.appendDiagnostics(diagnostics, out) } } units0 @@ -121,10 +121,10 @@ class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode) extends yield unitCtx.compilationUnit } - def run(using Context): Unit = unsupported("run") + override protected def run(using Context): Unit = unsupported("run") end ExtractSemanticDB -object ExtractSemanticDB: +private[semanticdb] object ExtractSemanticDB: import java.nio.file.Path import java.nio.file.Files import java.nio.file.Paths @@ -320,7 +320,7 @@ object ExtractSemanticDB: registerDefinition(tree.symbol, selectSpan(tree), Set.empty, tree.source) case tree => registerDefinition(tree.symbol, tree.span, Set.empty, tree.source) case tree: NamedDefTree => - if !tree.symbol.isAllOf(ModuleValCreationFlags) then + if tree.symbol.exists && !tree.symbol.isAllOf(ModuleValCreationFlags) then tree match { case tree: ValDef if tree.symbol.isAllOf(EnumValue) => tree.rhs match @@ -379,7 +379,12 @@ object ExtractSemanticDB: traverseAnnotsOfDefinition(ctorSym) ctorParams(tree.constr.termParamss, tree.constr.leadingTypeParams, tree.body) registerDefinition(ctorSym, tree.constr.nameSpan.startPos, Set.empty, tree.source) - case tree: Apply => + case ReflectiveSelectableApply(qual, memberName, sel) => + traverse(sel.qualifier) + val qualTpe = qual.symbol.info + val member = extractRefinement(qualTpe, memberName) + member.foreach(sym => registerUse(sym.symbolName, sel.nameSpan, tree.source)) + case tree: Apply if tree.fun.symbol.exists => @tu lazy val genParamSymbol: Name => String = tree.fun.symbol.funParamSymbol traverse(tree.fun) synth.tryFindSynthetic(tree).foreach(synthetics.addOne) @@ -467,6 +472,33 @@ object ExtractSemanticDB: end traverse + private def extractRefinement(site: Type, memberName: String)(using Context): Option[Symbol] = + def loop(site: Type, owner: Symbol): Option[Symbol] = + site match + case RefinedType(_, name, info) if name.toString() == memberName => + val flags = info match + case _: (ExprType | MethodOrPoly) => Method + case _ => EmptyFlags + Some(newSymbol(owner, name, flags, info)) + case RefinedType(parent, _, _) => loop(parent, owner) + case tp: ExprType => loop(tp.superType, owner) + case tp: TypeProxy => + loop(tp.superType, tp.typeSymbol) + case _ => + None + loop(site, NoSymbol) + + private object ReflectiveSelectableApply: + def unapply(tree: Tree)(using Context): Option[(Tree, String, Select)] = tree match + case Apply( + sel @ Select(Apply(Ident(reflSelectable), List(qual)), fun), + Literal(Constants.Constant(memberName: String)) :: args + ) if reflSelectable == nme.reflectiveSelectable && + (fun == nme.selectDynamic || fun == nme.applyDynamic) => + Some(qual, memberName, sel) + case _ => None + end ReflectiveSelectableApply + private object PatternValDef: def unapply(tree: ValDef)(using Context): Option[(Tree, Tree)] = tree.rhs match diff --git a/compiler/src/dotty/tools/dotc/semanticdb/LinkMode.scala b/compiler/src/dotty/tools/dotc/semanticdb/LinkMode.scala index aac4c1254892..3eb69859e258 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/LinkMode.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/LinkMode.scala @@ -1,4 +1,4 @@ package dotty.tools.dotc.semanticdb -enum LinkMode: +private[semanticdb] enum LinkMode: case SymlinkChildren, HardlinkChildren diff --git a/compiler/src/dotty/tools/dotc/semanticdb/PPrint.scala b/compiler/src/dotty/tools/dotc/semanticdb/PPrint.scala index fdf159836878..f96a48392ea2 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/PPrint.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/PPrint.scala @@ -6,7 +6,8 @@ import scala.collection.mutable import dotty.tools.dotc.semanticdb.Scala3.given import SymbolInformation.Kind.* import dotty.tools.dotc.util.SourceFile -class SymbolInformationPrinter (symtab: PrinterSymtab): + +private[semanticdb] class SymbolInformationPrinter (symtab: PrinterSymtab): val notes = InfoNotes() val infoPrinter = InfoPrinter(notes) @@ -274,7 +275,7 @@ class SymbolInformationPrinter (symtab: PrinterSymtab): end SymbolInformationPrinter extension (info: SymbolInformation) - def prefixBeforeTpe: String = { + private[semanticdb] def prefixBeforeTpe: String = { info.kind match { case LOCAL | FIELD | PARAMETER | SELF_PARAMETER | UNKNOWN_KIND | Unrecognized(_) => ": " @@ -284,25 +285,24 @@ extension (info: SymbolInformation) } } -trait PrinterSymtab: +private[semanticdb] trait PrinterSymtab: def info(symbol: String): Option[SymbolInformation] -object PrinterSymtab: + +private[semanticdb] object PrinterSymtab: def fromTextDocument(doc: TextDocument): PrinterSymtab = val map = doc.symbols.map(info => (info.symbol, info)).toMap new PrinterSymtab { override def info(symbol: String): Option[SymbolInformation] = map.get(symbol) } -def processRange(sb: StringBuilder, range: Range): Unit = +private[semanticdb] def processRange(sb: StringBuilder, range: Range): Unit = sb.append('[') .append(range.startLine).append(':').append(range.startCharacter) .append("..") .append(range.endLine).append(':').append(range.endCharacter) .append("):") - - -class SyntheticPrinter(symtab: PrinterSymtab, source: SourceFile) extends SymbolInformationPrinter(symtab): +private[semanticdb] class SyntheticPrinter(symtab: PrinterSymtab, source: SourceFile) extends SymbolInformationPrinter(symtab): def pprint(synth: Synthetic): String = val sb = new StringBuilder() diff --git a/compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala b/compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala index f49b00089712..052c4f2f27b3 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/Scala3.scala @@ -19,7 +19,7 @@ import java.lang.Character.{isJavaIdentifierPart, isJavaIdentifierStart} import scala.annotation.internal.sharable import scala.annotation.switch -object Scala3: +private[semanticdb] object Scala3: import Symbols.* import core.NameOps.* @@ -257,8 +257,14 @@ object Scala3: /** Is symbol global? Non-global symbols get localN names */ def isGlobal(using Context): Boolean = sym.exists && ( - sym.is(Package) - || !sym.isSelfSym && (sym.is(Param) || sym.owner.isClass) && sym.owner.isGlobal + //packages are always global + sym.is(Package) || + // Non-self parameters for global symbols + !sym.isSelfSym && (sym.is(Param) && sym.owner.isGlobal || + // Topelevel class symbols are global + sym.owner.isClass || + // Needed for refinement symbols, which are accessible in global scope + sym.owner.isType && !sym.owner.info.hiBound.isMatch) && sym.owner.isGlobal ) def isLocalWithinSameName(using Context): Boolean = diff --git a/compiler/src/dotty/tools/dotc/semanticdb/SemanticSymbolBuilder.scala b/compiler/src/dotty/tools/dotc/semanticdb/SemanticSymbolBuilder.scala index 81f5d37f443f..873a16524067 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/SemanticSymbolBuilder.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/SemanticSymbolBuilder.scala @@ -2,17 +2,24 @@ package dotty.tools package dotc package semanticdb -import core.* -import Contexts.* -import Symbols.* -import Flags.* -import Names.Name +import core.Contexts.* +import core.Symbols.* +import core.Flags.* +import core.MissingType +import core.NameOps.* +import core.Names.* +import core.StdNames.tpnme +import Scala3.{*, given} import scala.annotation.tailrec import scala.collection.mutable +import scala.util.control.NonFatal -class SemanticSymbolBuilder: - import Scala3.{_, given} +// The API here is a little tricky because it's used both as a stateful builder with local symbols, +// and for non-local symbols that do not need any state through the companion object. + +private[semanticdb] class SemanticSymbolBuilder: + import SemanticSymbolBuilder.* private var nextLocalIdx: Int = 0 @@ -22,32 +29,32 @@ class SemanticSymbolBuilder: /** The local symbol(s) starting at given offset */ private val symsAtOffset = new mutable.HashMap[Int, Set[Symbol]]().withDefault(_ => Set[Symbol]()) - def symbolName(sym: Symbol)(using Context): String = val b = StringBuilder(20) - addSymName(b, sym) + addSymName(b, sym, Some(this)) b.toString + def symbolName(sym: FakeSymbol)(using Context): String = + val b = StringBuilder(20) sym match case sym: WildcardTypeSymbol => - val b = StringBuilder(20) - addSymName(b, sym.owner) - b.append('['); addName(b, sym.name); b.append(']') - b.toString + addSymName(b, sym.owner, Some(this)) + b.append('[') + addName(b, sym.name) + b.append(']') case sym: TermParamRefSymbol => - val b = StringBuilder(20) - addSymName(b, sym.owner) - b.append('('); addName(b, sym.name); b.append(')') - b.toString + addSymName(b, sym.owner, Some(this)) + b.append('(') + addName(b, sym.name) + b.append(')') case sym: TypeParamRefSymbol => - val b = StringBuilder(20) - addSymName(b, sym.owner) - b.append('['); addName(b, sym.name); b.append(']') - b.toString + addSymName(b, sym.owner, Some(this)) + b.append('[') + addName(b, sym.name) + b.append(']') case sym: RefinementSymbol => - val b = StringBuilder(20) addLocalSymName(b) - b.toString + b.toString def funParamSymbol(sym: Symbol)(using Context): Name => String = if sym.isGlobal then @@ -57,21 +64,157 @@ class SemanticSymbolBuilder: name => locals.keys.find(local => local.isTerm && local.owner == sym && local.name == name) .fold("")(Symbols.LocalPrefix + locals(_)) - private def addName(b: StringBuilder, name: Name): Unit = - val str = name.toString.unescapeUnicode - if str.isJavaIdent then b append str - else b append '`' append str append '`' - private def addLocalSymName(b: StringBuilder): Unit = val idx = nextLocalIdx nextLocalIdx += 1 b.append(Symbols.LocalPrefix).append(idx) + + /** The index of local symbol `sym`. Symbols with the same name and + * the same starting position have the same index. + */ + private def localIdx(sym: Symbol)(using Context): Int = + val startPos = + // assert(sym.span.exists, s"$sym should have a span") + if sym.span.exists then Some(sym.span.start) else None + + @tailrec + def computeLocalIdx(sym: Symbol): Int = locals.get(sym) match + case Some(idx) => idx + case None => + (for { + pos <- startPos + syms <- symsAtOffset.get(pos) + found <- syms.find(_.name == sym.name) + } yield found) match + case Some(other) => computeLocalIdx(other) + case None => + val idx = nextLocalIdx + nextLocalIdx += 1 + locals(sym) = idx + startPos.foreach(pos => symsAtOffset(pos) += sym) + idx + end computeLocalIdx + + computeLocalIdx(sym) + end localIdx + +private[semanticdb] object SemanticSymbolBuilder: + def inverseSymbol(sym: String)(using ctx: Context): List[Symbol] = + def stripBackticks(s: String): String = s.stripPrefix("`").stripSuffix("`") + + import Scala3.StringOps.* + + val defns = ctx.definitions + import defns.* + + def loop(s: String): List[Symbol] = + if !s.isSymbol || s.isRootPackage then RootPackage :: Nil + else if s.isEmptyPackage then EmptyPackageVal :: Nil + else if s.isPackage then + try + val pkg = s.split('/').map(stripBackticks).mkString(".") + requiredPackage(pkg) :: Nil + catch + case NonFatal(_) => + Nil + else + val (desc, parent) = DescriptorParser(s) + val parentSymbol = loop(parent) + + def tryMember(sym: Symbol): List[Symbol] = + sym match + case NoSymbol => + Nil + case owner => + desc match + case Descriptor.None => + Nil + case Descriptor.Type(value) => + val typeSym = owner.info.decl(typeName(value)).symbol + // Semanticdb describes java static members as a reference from type + // while scalac puts static members into synthetic companion class - term + // To avoid issues with resolving static members return type and term in case of Java type + // Example: + // `java/nio/file/Files#exists()` - `exists` is a member of type `Files#` + // however in scalac this method is defined only in `module Files` + if typeSym.is(JavaDefined) then + typeSym :: owner.info.decl(termName(value)).symbol :: Nil + /** + * Looks like decl doesn't work for: + * package a: + * implicit class <> (i: Int): + * def inc = i + 1 + */ + else if typeSym == NoSymbol then + owner.info.member(typeName(value)).symbol :: Nil + else typeSym :: Nil + end if + case Descriptor.Term(value) => + val outSymbol = owner.info.decl(termName(value)).symbol + if outSymbol.exists + then outSymbol :: Nil + else if owner.is(Package) + then + // Fallback for type companion objects, + // e.g. + // ```File.scala + // package a + // type Cow = Int + // object Cow + // ``` + // `ScalaTopLevelMtags` emits `a/Cow.` + // but the symbol we look for is `a/File$package/Cow.` + // (look: tests.pc.CompletionWorkspaceSuite.type-apply) + owner.info.decls + .filter { s => + s.isPackageObject && s.name.stripModuleClassSuffix.show.endsWith("$package") + } + .flatMap(tryMember) + else Nil + end if + case Descriptor.Package(value) => + owner.info.decl(termName(value)).symbol :: Nil + case Descriptor.Parameter(value) => + // TODO - need to check how to implement this properly + // owner.paramSymss.flatten.filter(_.name.containsName(value)) + Nil + case Descriptor.TypeParameter(value) => + // TODO - need to check how to implement this properly + // owner.typeParams.filter(_.name.containsName(value)) + Nil + case Descriptor.Method(value, _) => + owner.info + .decl(termName(value)) + .alternatives + .iterator + .map(_.symbol) + .filter(sym => symbolName(sym) == s) + .toList + end match + end tryMember + + parentSymbol.flatMap(tryMember) + try + val res = loop(sym) + res.filterNot(_ == NoSymbol) + catch case NonFatal(e) => Nil + end inverseSymbol + + private def addName(b: StringBuilder, name: Name): Unit = + val str = name.toString.unescapeUnicode + if str.isJavaIdent then b.append(str) + else b.append('`').append(str).append('`') + /** Add semanticdb name of the given symbol to string builder */ - private def addSymName(b: StringBuilder, sym: Symbol)(using Context): Unit = + private def addSymName(b: StringBuilder, sym: Symbol, sb: Option[SemanticSymbolBuilder])(using Context): Unit = def addOwner(owner: Symbol): Unit = - if !owner.isRoot then addSymName(b, owner) + if !owner.isRoot then + // Skip synthetic refinement class so refinement members get the type alias as owner + // e.g. User#name(). instead of User#``#name(). + if owner.name == tpnme.REFINE_CLASS then addOwner(owner.owner) + else addSymName(b, owner, sb) def addOverloadIdx(initSym: Symbol): Unit = // revert from the compiler-generated overload of the signature polymorphic method @@ -84,69 +227,62 @@ class SemanticSymbolBuilder: decls0 end decls val alts = decls.filter(_.isOneOf(Method | Mutable)).toList.reverse.partition(!_.is(Synthetic)).toList.flatten + def find(filter: Symbol => Boolean) = alts match case notSym :: rest if !filter(notSym) => - val idx = rest.indexWhere(filter).ensuring(_ >= 0) - b.append('+').append(idx + 1) + val idx = rest.indexWhere(filter) + if idx >= 0 then b.append('+').append(idx + 1) case _ => end find + val sig = sym.signature val targetName = sym.targetName find(sym => sym.signature == sig && sym.targetName == targetName) + + @tailrec def addDescriptor(sym: Symbol): Unit = if sym.is(ModuleClass) then addDescriptor(sym.sourceModule) else if sym.is(TypeParam) then - b.append('['); addName(b, sym.name); b.append(']') + b.append('[') + addName(b, sym.name) + b.append(']') else if sym.is(Param) then - b.append('('); addName(b, sym.name); b.append(')') + b.append('(') + addName(b, sym.name) + b.append(')') else if sym.isRoot then b.append(Symbols.RootPackage) else if sym.isEmptyPackage then b.append(Symbols.EmptyPackage) - else if (sym.isScala2PackageObject) then + else if sym.isScala2PackageObject then b.append(Symbols.PackageObjectDescriptor) else + def isScalaMethodOrVar = sym.isOneOf(Method | Mutable) && !sym.is(JavaDefined) + def isJavaMethod = sym.is(Method) && sym.is(JavaDefined) addName(b, sym.name) if sym.is(Package) then b.append('/') else if sym.isType || sym.isAllOf(JavaModule) then b.append('#') - else if sym.isOneOf(Method | Mutable) - && (!sym.is(StableRealizable) || sym.isConstructor) then - b.append('('); addOverloadIdx(sym); b.append(").") + else if (isScalaMethodOrVar || isJavaMethod) + && (!sym.is(StableRealizable) || sym.isConstructor) then + b.append('(') + addOverloadIdx(sym) + b.append(").") else b.append('.') - /** The index of local symbol `sym`. Symbols with the same name and - * the same starting position have the same index. - */ - def localIdx(sym: Symbol)(using Context): Int = - val startPos = - // assert(sym.span.exists, s"$sym should have a span") - if (sym.span.exists) Some(sym.span.start) else None - @tailrec - def computeLocalIdx(sym: Symbol): Int = locals get sym match - case Some(idx) => idx - case None => - (for { - pos <- startPos - syms <- symsAtOffset.get(pos) - found <- syms.find(_.name == sym.name) - } yield found) match - case Some(other) => computeLocalIdx(other) - case None => - val idx = nextLocalIdx - nextLocalIdx += 1 - locals(sym) = idx - startPos.foreach(pos => symsAtOffset(pos) += sym) - idx - end computeLocalIdx - computeLocalIdx(sym) - end localIdx - - if sym.exists then - if sym.isGlobal then - addOwner(sym.owner); addDescriptor(sym) - else - b.append(Symbols.LocalPrefix).append(localIdx(sym)) + sb match + case Some(sb) if sym.exists && !sym.isGlobal => + b.append(Symbols.LocalPrefix).append(sb.localIdx(sym)) + case _ if sym.exists => + addOwner(sym.owner) + addDescriptor(sym) + case _ => + () end addSymName + + def symbolName(sym: Symbol)(using Context): String = + val b = StringBuilder(20) + addSymName(b, sym, None) + b.toString diff --git a/compiler/src/dotty/tools/dotc/semanticdb/SyntheticsExtractor.scala b/compiler/src/dotty/tools/dotc/semanticdb/SyntheticsExtractor.scala index af38315a857e..854df32dfe91 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/SyntheticsExtractor.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/SyntheticsExtractor.scala @@ -8,7 +8,7 @@ import dotty.tools.dotc.core.NameKinds import dotty.tools.dotc.{semanticdb => s} -class SyntheticsExtractor: +private[semanticdb] class SyntheticsExtractor: import Scala3.{_, given} val visited = collection.mutable.HashSet[Tree]() @@ -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/Tools.scala b/compiler/src/dotty/tools/dotc/semanticdb/Tools.scala index ea95e34a57b9..41410b662407 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/Tools.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/Tools.scala @@ -6,7 +6,7 @@ import scala.jdk.CollectionConverters.* import dotty.tools.dotc.util.SourceFile import dotty.tools.dotc.semanticdb.Scala3.given -object Tools: +private[semanticdb] object Tools: /** Converts a Path to a String that is URI encoded, without forcing absolute paths. */ def mkURIstring(path: Path): String = diff --git a/compiler/src/dotty/tools/dotc/semanticdb/TypeOps.scala b/compiler/src/dotty/tools/dotc/semanticdb/TypeOps.scala index 4293ecd6ca43..758df4ebc30d 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/TypeOps.scala @@ -9,15 +9,14 @@ import core.Annotations.Annotation import core.Flags import core.Names.Name import core.StdNames.tpnme -import scala.util.chaining.scalaUtilChainingOps import collection.mutable import dotty.tools.dotc.{semanticdb => s} import Scala3.{FakeSymbol, SemanticSymbol, WildcardTypeSymbol, TypeParamRefSymbol, TermParamRefSymbol, RefinementSymbol} -import dotty.tools.dotc.core.Names.Designator +import dotty.tools.dotc.util.chaining.* -class TypeOps: +private[semanticdb] class TypeOps: import SymbolScopeOps.* import Scala3.given private val paramRefSymtab = mutable.Map[(LambdaType, Name), Symbol]() @@ -96,7 +95,11 @@ class TypeOps: // We try to find the "actual" binder of : `inner`, // and register them to the symbol table with `(, inner) -> ` // instead of `("y", outer) -> ` - if lam.paramNames.contains(sym.name) then + // We must also check for parameter shadowing such as def shadowParam(x: Int) = {val x = true} + // We skip param symbol check if owner is not a LambdaType for proper MatchType paramRef entry in the paramRefSymtab + // for more information: https://github.com/scala/scala3/pull/23161#discussion_r2097755983 + + if (sym.is(Flags.Param) || !sym.owner.info.isInstanceOf[LambdaType]) && lam.paramNames.contains(sym.name) then paramRefSymtab((lam, sym.name)) = sym else enterParamRef(lam.resType) @@ -525,9 +528,7 @@ class TypeOps: case _ => false } - -object SymbolScopeOps: - import Scala3.{_, given} +private[semanticdb] object SymbolScopeOps: extension (syms: List[SemanticSymbol]) def sscope(using linkMode: LinkMode)(using SemanticSymbolBuilder, TypeOps, Context): s.Scope = linkMode match diff --git a/compiler/src/dotty/tools/dotc/semanticdb/generated/Tree.scala b/compiler/src/dotty/tools/dotc/semanticdb/generated/Tree.scala index 6a19494cd65a..afe56978a4af 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[this] 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/semanticdb/package.scala b/compiler/src/dotty/tools/dotc/semanticdb/package.scala new file mode 100644 index 000000000000..4705518aea94 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/semanticdb/package.scala @@ -0,0 +1,49 @@ +package dotty.tools.dotc + +import java.io.ByteArrayOutputStream +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.semanticdb.internal.MD5 +import dotty.tools.dotc.semanticdb.internal.SemanticdbOutputStream + +/** + * Public API of the SemanticDB package. + * + * The `generated` package is NOT a public API, and neither is the `internal` package. + * (However, in practice `internal` is used by the presentation-compiler to avoid depending on a protobuf runtime... see `replaceProtobuf` in project/Build.scala) + */ +package object semanticdb { + /** Compiler pass to write SemanticDB information about code. */ + type ExtractSemanticInfo = ExtractSemanticDB.ExtractSemanticInfo + + /** Compiler pass to write SemanticDB information about compiler diagnostics. */ + type AppendDiagnostics = ExtractSemanticDB.AppendDiagnostics + + /** Serializes a SemanticDB text document as bytes for the given tree, path, and textual source code. */ + def textDocumentBytes(tree: tpd.Tree, path: String, sourceCode: String)(using Context): Array[Byte] = + val extractor = ExtractSemanticDB.Extractor() + extractor.traverse(tree) + val document = TextDocument( + schema = Schema.SEMANTICDB4, + language = Language.SCALA, + uri = path, + text = sourceCode, + md5 = MD5.compute(sourceCode), + symbols = extractor.symbolInfos.toList, + occurrences = extractor.occurrences.toList + ) + val byteStream = new ByteArrayOutputStream() + val out = SemanticdbOutputStream.newInstance(byteStream) + document.writeTo(out) + out.flush() + byteStream.toByteArray.nn + + /** Gets SemanticDB symbols from the given name. */ + def symbolsFromName(sym: String)(using ctx: Context): List[Symbol] = + SemanticSymbolBuilder.inverseSymbol(sym) + + /** Gets the SemanticDB name of the given symbol. */ + def symbolToName(sym: Symbol)(using Context): String = + SemanticSymbolBuilder.symbolName(sym) +} \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 1832eeabca7e..4534af0f0883 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -189,11 +189,19 @@ class CrossStageSafety extends TreeMapWithStages { /** Check level consistency of terms references */ private def checkLevelConsistency(tree: Ident | This)(using Context): Unit = + def isStatic(pre: Type)(using Context): Boolean = pre match + case pre: NamedType => + val sym = pre.currentSymbol + sym.is(Package) || sym.isStaticOwner && isStatic(pre.prefix) + case pre: ThisType => isStatic(pre.tref) + case _ => true new TypeTraverser { def traverse(tp: Type): Unit = tp match case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level != levelOf(tp.symbol) => levelError(tp.symbol, tp, tree.srcPos) + case tp: ThisType if isStatic(tp) => + // static object (OK) case tp: ThisType if level != -1 && level != levelOf(tp.cls) => levelError(tp.cls, tp, tree.srcPos) case tp: AnnotatedType => @@ -201,7 +209,7 @@ class CrossStageSafety extends TreeMapWithStages { case _ if tp.typeSymbol.is(Package) => // OK case _ => - traverseChildren(tp) + traverseChildren(tp) }.traverse(tree.tpe) private def levelError(sym: Symbol, tp: Type, pos: SrcPos)(using Context): tp.type = { diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index a73f884fbac9..509049e131c8 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -76,7 +76,7 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { tp match case tp @ NamedType(NoPrefix, _) if level > levelOf(tp.symbol) => tp.symbol case tp: NamedType if !tp.symbol.isStatic => levelInconsistentRootOfPath(tp.prefix) - case tp: ThisType if level > levelOf(tp.cls) => tp.cls + case tp: ThisType if level > levelOf(tp.cls) && !tp.cls.isRefinementClass => tp.cls case _ => NoSymbol /** Try to heal reference to type `T` used in a higher level than its definition. diff --git a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala index 237db90b315f..91cc02953528 100644 --- a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala +++ b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala @@ -67,7 +67,7 @@ abstract class AccessProxies { /** The name of the accessor for definition with given `name` in given `site` */ def accessorNameOf(name: TermName, site: Symbol)(using Context): TermName - def needsAccessor(sym: Symbol)(using Context): Boolean + def needsAccessor(refTree: RefTree | Apply | TypeApply)(using Context): Boolean def ifNoHost(reference: RefTree)(using Context): Tree = { assert(false, i"no host found for $reference with ${reference.symbol.showLocated} from ${ctx.owner}") @@ -127,7 +127,6 @@ abstract class AccessProxies { * access with a reference to the accessor. * * @param reference The original reference to the non-public symbol - * @param onLHS The reference is on the left-hand side of an assignment */ def useAccessor(reference: RefTree)(using Context): Tree = { val accessed = reference.symbol.asTerm @@ -136,8 +135,13 @@ abstract class AccessProxies { if accessorClass.is(Package) then accessorClass = ctx.owner.topLevelClass val accessorName = accessorNameOf(accessed.name, accessorClass) + val mappedInfo = accessed.info match + // TypeRef pointing to module class seems to not be stable, so we remap that to a TermRef + // see test i22593.scala (and issue #i22593) + case tref @ TypeRef(prefix, _) if tref.symbol.is(Module) => TermRef(prefix, tref.symbol.companionModule) + case other => other val accessorInfo = - accessed.info.ensureMethodic.asSeenFrom(accessorClass.thisType, accessed.owner) + mappedInfo.ensureMethodic.asSeenFrom(accessorClass.thisType, accessed.owner) val accessor = accessorSymbol(accessorClass, accessorName, accessorInfo, accessed) rewire(reference, accessor) } @@ -146,7 +150,7 @@ abstract class AccessProxies { /** Replace tree with a reference to an accessor if needed */ def accessorIfNeeded(tree: Tree)(using Context): Tree = tree match { - case tree: RefTree if needsAccessor(tree.symbol) => + case tree: RefTree if needsAccessor(tree) => if (tree.symbol.isConstructor) { report.error("Implementation restriction: cannot use private constructors in inlineable methods", tree.srcPos) tree // TODO: create a proper accessor for the private constructor diff --git a/compiler/src/dotty/tools/dotc/transform/Bridges.scala b/compiler/src/dotty/tools/dotc/transform/Bridges.scala index 482e5056fad0..9a76a5a6be9e 100644 --- a/compiler/src/dotty/tools/dotc/transform/Bridges.scala +++ b/compiler/src/dotty/tools/dotc/transform/Bridges.scala @@ -90,6 +90,16 @@ class Bridges(root: ClassSymbol, thisPhase: DenotTransformer)(using Context) { // A bridge might introduce a classcast exception. // Example where this was observed: run/i12828a.scala and MapView in stdlib213 report.log(i"suppress bridge in $root for ${member} in ${member.owner} and ${other.showLocated} since member infos ${site.memberInfo(member)} and ${site.memberInfo(other)} do not match") + else if member.is(JavaDefined) + && !member.isPublic(using preErasureCtx) && !member.is(Protected) // opt: public or protected are obviously accessible + && !member.isAccessibleFrom(root.thisType)(using preErasureCtx) + then + // Don't generate a bridge that would call an inaccessible method. + // This can happen with Java package-private methods + // when a Scala class in a different package extends the Java class. + // Scala's private[pkg] methods are compiled as public at the JVM level, + // so they don't have this problem (see #25291). + report.log(i"suppress bridge in $root for inaccessible method ${member.showLocated}") else if !bridgeExists then addBridge(member, other) } diff --git a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala index a85cabdd5460..7ecdf79af984 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckShadowing.scala @@ -49,26 +49,22 @@ class CheckShadowing extends MiniPhase: override def description: String = CheckShadowing.description + override def isEnabled(using Context): Boolean = ctx.settings.Xlint.value.nonEmpty + override def isRunnable(using Context): Boolean = - super.isRunnable && - ctx.settings.Xlint.value.nonEmpty && - !ctx.isJava + super.isRunnable && ctx.settings.Xlint.value.nonEmpty && !ctx.isJava - // Setup before the traversal override def prepareForUnit(tree: tpd.Tree)(using Context): Context = val data = ShadowingData() val fresh = ctx.fresh.setProperty(_key, data) shadowingDataApply(sd => sd.registerRootImports())(using fresh) - // Reporting on traversal's end override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = shadowingDataApply(sd => reportShadowing(sd.getShadowingResult) ) tree - // MiniPhase traversal : - override def prepareForPackageDef(tree: tpd.PackageDef)(using Context): Context = shadowingDataApply(sd => sd.inNewScope()) ctx @@ -91,16 +87,16 @@ class CheckShadowing extends MiniPhase: ) override def prepareForTypeDef(tree: tpd.TypeDef)(using Context): Context = - if tree.symbol.isAliasType then // if alias, the parent is the current symbol - nestedTypeTraverser(tree.symbol).traverse(tree.rhs) - if tree.symbol.is(Param) then // if param, the parent is up - val owner = tree.symbol.owner + val sym = tree.symbol + if sym.isAliasType then // if alias, the parent is the current symbol + nestedTypeTraverser(sym).traverse(tree.rhs) + if sym.is(Param) then // if param, the parent is up + val owner = sym.owner val parent = if (owner.isConstructor) then owner.owner else owner nestedTypeTraverser(parent).traverse(tree.rhs)(using ctx.outer) - shadowingDataApply(sd => sd.registerCandidate(parent, tree)) - else - ctx - + if isValidTypeParamOwner(sym.owner) then + shadowingDataApply(sd => sd.registerCandidate(parent, tree)) + ctx override def transformPackageDef(tree: tpd.PackageDef)(using Context): tpd.Tree = shadowingDataApply(sd => sd.outOfScope()) @@ -115,13 +111,14 @@ class CheckShadowing extends MiniPhase: tree override def transformTypeDef(tree: tpd.TypeDef)(using Context): tpd.Tree = - if tree.symbol.is(Param) && isValidTypeParamOwner(tree.symbol.owner) then // Do not register for constructors the work is done for the Class owned equivalent TypeDef + // Do not register for constructors the work is done for the Class owned equivalent TypeDef + if tree.symbol.is(Param) && isValidTypeParamOwner(tree.symbol.owner) then shadowingDataApply(sd => sd.computeTypeParamShadowsFor(tree.symbol.owner)(using ctx.outer)) - if tree.symbol.isAliasType then // No need to start outer here, because the TypeDef reached here it's already the parent + // No need to start outer here, because the TypeDef reached here it's already the parent + if tree.symbol.isAliasType then shadowingDataApply(sd => sd.computeTypeParamShadowsFor(tree.symbol)(using ctx)) tree - // Helpers : private def isValidTypeParamOwner(owner: Symbol)(using Context): Boolean = !owner.isConstructor && !owner.is(Synthetic) && !owner.is(Exported) @@ -141,7 +138,7 @@ class CheckShadowing extends MiniPhase: override def traverse(tree: tpd.Tree)(using Context): Unit = tree match - case t:tpd.TypeDef => + case t: tpd.TypeDef => val newCtx = shadowingDataApply(sd => sd.registerCandidate(parent, t) ) @@ -157,7 +154,7 @@ class CheckShadowing extends MiniPhase: override def traverse(tree: tpd.Tree)(using Context): Unit = tree match - case t:tpd.Import => + case t: tpd.Import => val newCtx = shadowingDataApply(sd => sd.registerImport(t)) traverseChildren(tree)(using newCtx) case _ => @@ -240,7 +237,7 @@ object CheckShadowing: val declarationScope = ctx.effectiveScope val res = declarationScope.lookup(symbol.name) res match - case s: Symbol if s.isType => Some(s) + case s: Symbol if s.isType && s != symbol => Some(s) case _ => lookForUnitShadowedType(symbol)(using ctx.outer) /** Register if the valDef is a private declaration that shadows an inherited field */ @@ -310,4 +307,3 @@ object CheckShadowing: case class ShadowResult(warnings: List[ShadowWarning]) end CheckShadowing - diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 6e626fc5dd9e..5dd977b30bea 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -1,883 +1,1098 @@ -package dotty.tools.dotc.transform +package dotty.tools.dotc +package transform -import scala.annotation.tailrec -import scala.collection.mutable - -import dotty.tools.uncheckedNN -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Symbols.* -import dotty.tools.dotc.ast.tpd.{Inlined, TreeTraverser} -import dotty.tools.dotc.ast.untpd -import dotty.tools.dotc.ast.untpd.ImportSelector +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.Decorators.{em, i} -import dotty.tools.dotc.core.Denotations.SingleDenotation +import dotty.tools.dotc.core.Decorators.* import dotty.tools.dotc.core.Flags.* -import dotty.tools.dotc.core.Phases.Phase -import dotty.tools.dotc.core.StdNames +import dotty.tools.dotc.core.Names.{Name, SimpleName, DerivedName, TermName, termName} +import dotty.tools.dotc.core.NameKinds.{ + BodyRetainerName, ContextBoundParamName, ContextFunctionParamName, DefaultGetterName, WildcardParamName} +import dotty.tools.dotc.core.NameOps.{isAnonymousFunctionName, isReplWrapperName, isContextFunction, setterName} +import dotty.tools.dotc.core.Scopes.newScope +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.Message -import dotty.tools.dotc.reporting.UnusedSymbol as UnusedSymbolMessage -import dotty.tools.dotc.typer.ImportInfo -import dotty.tools.dotc.util.{Property, SrcPos} -import dotty.tools.dotc.core.Mode -import dotty.tools.dotc.core.Types.{AnnotatedType, ConstantType, NoType, TermRef, Type, TypeTraverser} -import dotty.tools.dotc.core.Flags.flagsString -import dotty.tools.dotc.core.Flags -import dotty.tools.dotc.core.Names.{Name, TermName} -import dotty.tools.dotc.core.NameOps.isReplWrapperName +import dotty.tools.dotc.reporting.{CodeAction, Diagnostic, UnusedSymbol} +import dotty.tools.dotc.rewrites.Rewrites.ActionPatch import dotty.tools.dotc.transform.MegaPhase.MiniPhase -import dotty.tools.dotc.core.Annotations -import dotty.tools.dotc.core.Definitions -import dotty.tools.dotc.core.NameKinds.WildcardParamName -import dotty.tools.dotc.core.Symbols.Symbol -import dotty.tools.dotc.core.StdNames.nme -import dotty.tools.dotc.util.Spans.Span -import scala.math.Ordering +import dotty.tools.dotc.typer.{ImportInfo, Typer, TyperPhase} +import dotty.tools.dotc.typer.Deriving.OriginalTypeClass +import dotty.tools.dotc.typer.Implicits.{ContextualImplicits, RenamedImplicitRef} +import dotty.tools.dotc.util.{Property, Spans, SrcPos}, Spans.Span +import dotty.tools.dotc.util.Chars.{isLineBreakChar, isWhitespace} +import dotty.tools.dotc.util.chaining.* +import java.util.IdentityHashMap -/** - * A compiler phase that checks for unused imports or definitions - * - * Basically, it gathers definition/imports and their usage. If a - * definition/imports does not have any usage, then it is reported. - */ -class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _key: Property.Key[CheckUnused.UnusedData]) extends MiniPhase: - import CheckUnused.* - import UnusedData.* - - private inline def unusedDataApply[U](inline f: UnusedData => U)(using Context): Context = - ctx.property(_key) match - case Some(ud) => f(ud) - case None => () - ctx - - override def phaseName: String = CheckUnused.phaseNamePrefix + suffix +import scala.collection.mutable, mutable.{ArrayBuilder, ListBuffer, Stack} - override def description: String = CheckUnused.description +import CheckUnused.* - override def isRunnable(using Context): Boolean = - super.isRunnable && - ctx.settings.WunusedHas.any && - !ctx.isJava +/** A compiler phase that checks for unused imports or definitions. + */ +class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPhase: - // ========== SETUP ============ + override def phaseName: String = s"checkUnused$suffix" - override def prepareForUnit(tree: tpd.Tree)(using Context): Context = - val data = UnusedData() - tree.getAttachment(_key).foreach(oldData => - data.unusedAggregate = oldData.unusedAggregate - ) - val fresh = ctx.fresh.setProperty(_key, data) - tree.putAttachment(_key, data) - fresh + override def description: String = "check for unused elements" - // ========== END + REPORTING ========== + override def runsAfter = Set: + phaseMode match + case PhaseMode.Aggregate => TyperPhase.name + case PhaseMode.Resolve => Inlining.name + case PhaseMode.Report => PatternMatcher.name - override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = - unusedDataApply { ud => - ud.finishAggregation() - if(phaseMode == PhaseMode.Report) then - ud.unusedAggregate.foreach(reportUnused) - } - tree + override def isEnabled(using Context): Boolean = ctx.settings.WunusedHas.any - // ========== MiniPhase Prepare ========== - override def prepareForOther(tree: tpd.Tree)(using Context): Context = - // A standard tree traverser covers cases not handled by the Mega/MiniPhase - traverser.traverse(tree) - ctx + override def isRunnable(using Context): Boolean = super.isRunnable && ctx.settings.WunusedHas.any && !ctx.isJava - override def prepareForInlined(tree: tpd.Inlined)(using Context): Context = - traverser.traverse(tree.call) - ctx + override def prepareForUnit(tree: Tree)(using Context): Context = + val infos = tree.getAttachment(refInfosKey).getOrElse: + RefInfos().tap(tree.withAttachment(refInfosKey, _)) + ctx.fresh.setProperty(refInfosKey, infos) + override def transformUnit(tree: Tree)(using Context): tree.type = + if phaseMode == PhaseMode.Report then + reportUnused() + tree.removeAttachment(refInfosKey) + tree - override def prepareForIdent(tree: tpd.Ident)(using Context): Context = + override def transformIdent(tree: Ident)(using Context): tree.type = + refInfos.isAssignment = tree.hasAttachment(AssignmentTarget) if tree.symbol.exists then - unusedDataApply { ud => - @tailrec - def loopOnNormalizedPrefixes(prefix: Type, depth: Int): Unit = - // limit to 10 as failsafe for the odd case where there is an infinite cycle - if depth < 10 && prefix.exists then - ud.registerUsed(prefix.classSymbol, None) - loopOnNormalizedPrefixes(prefix.normalizedPrefix, depth + 1) - - loopOnNormalizedPrefixes(tree.typeOpt.normalizedPrefix, depth = 0) - ud.registerUsed(tree.symbol, Some(tree.name)) - } + // if in an inline expansion, resolve at summonInline (synthetic pos) or in an enclosing call site + val resolving = + 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, 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, imports = resolving) else if tree.hasType then - unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) - else - ctx - - override def prepareForSelect(tree: tpd.Select)(using Context): Context = - val name = tree.removeAttachment(OriginalName) - unusedDataApply(_.registerUsed(tree.symbol, name, includeForImport = tree.qualifier.span.isSynthetic)) - - override def prepareForBlock(tree: tpd.Block)(using Context): Context = - pushInBlockTemplatePackageDef(tree) - - override def prepareForTemplate(tree: tpd.Template)(using Context): Context = - pushInBlockTemplatePackageDef(tree) - - override def prepareForPackageDef(tree: tpd.PackageDef)(using Context): Context = - pushInBlockTemplatePackageDef(tree) - - override def prepareForValDef(tree: tpd.ValDef)(using Context): Context = - unusedDataApply{ud => - // do not register the ValDef generated for `object` - traverseAnnotations(tree.symbol) - if !tree.symbol.is(Module) then - ud.registerDef(tree) - if tree.name.startsWith("derived$") && tree.typeOpt != NoType then - ud.registerUsed(tree.typeOpt.typeSymbol, None, isDerived = true) - ud.addIgnoredUsage(tree.symbol) - } - - override def prepareForDefDef(tree: tpd.DefDef)(using Context): Context = - unusedDataApply: ud => - if !tree.symbol.is(Private) then - tree.termParamss.flatten.foreach { p => - ud.addIgnoredParam(p.symbol) - } - ud.registerTrivial(tree) - traverseAnnotations(tree.symbol) - ud.registerDef(tree) - ud.addIgnoredUsage(tree.symbol) - - override def prepareForTypeDef(tree: tpd.TypeDef)(using Context): Context = - unusedDataApply: ud => - traverseAnnotations(tree.symbol) - if !tree.symbol.is(Param) then // Ignore type parameter (as Scala 2) - ud.registerDef(tree) - ud.addIgnoredUsage(tree.symbol) - - override def prepareForBind(tree: tpd.Bind)(using Context): Context = - traverseAnnotations(tree.symbol) - unusedDataApply(_.registerPatVar(tree)) + resolveUsage(tree.tpe.classSymbol, tree.name, tree.tpe.importPrefix.skipPackageObject) + refInfos.isAssignment = false + tree - override def prepareForTypeTree(tree: tpd.TypeTree)(using Context): Context = - if !tree.isInstanceOf[tpd.InferredTypeTree] then typeTraverser(unusedDataApply).traverse(tree.tpe) - ctx + // import x.y; y may be rewritten x.y, also import x.z as y + override def transformSelect(tree: Select)(using Context): tree.type = + refInfos.isAssignment = tree.hasAttachment(AssignmentTarget) + val name = tree.removeAttachment(OriginalName).getOrElse(nme.NO_NAME) + inline def isImportable = tree.qualifier.srcPos.isSynthetic + && tree.qualifier.tpe.match + case ThisType(_) | SuperType(_, _) => false + case qualtpe => qualtpe.isStable + val sym = tree.symbol + .orElse: + tree.typeOpt.resultType.typeSymbol + if tree.srcPos.isSynthetic && sym == defn.TypeTest_unapply then + tree.qualifier.tpe.underlying.finalResultType match + case AppliedType(tycon, args) => + val res = + if tycon.typeSymbol == defn.TypeTestClass then args(1) // T in TypeTest[-S, T] + else if tycon.typeSymbol == defn.TypeableType then args(0) // T in Typeable[T] + else return tree + val target = res.dealias.typeSymbol + resolveUsage(target, target.name, res.importPrefix.skipPackageObject) // case _: T => + case _ => + else if isImportable || name.exists(_ != sym.name) then + if !ignoreTree(tree) then + resolveUsage(sym, name, tree.qualifier.tpe) + else if !ignoreTree(tree) then + refUsage(sym) + refInfos.isAssignment = false + tree - override def prepareForAssign(tree: tpd.Assign)(using Context): Context = - unusedDataApply{ ud => - val sym = tree.lhs.symbol - if sym.exists then - ud.registerSetVar(sym) - } + override def transformLiteral(tree: Literal)(using Context): tree.type = + tree.getAttachment(Typer.AdaptedTree).foreach(transformAllDeep) + tree - // ========== MiniPhase Transform ========== + override def prepareForCaseDef(tree: CaseDef)(using Context): Context = + nowarner.traverse(tree.pat) + ctx - override def transformBlock(tree: tpd.Block)(using Context): tpd.Tree = - popOutBlockTemplatePackageDef() + override def prepareForApply(tree: Apply)(using Context): Context = + // ignore tupling of for assignments, as they are not usages of vars + if tree.hasAttachment(ForArtifact) then + tree match + case Apply(TypeApply(Select(fun, nme.apply), _), args) => + if fun.symbol.is(Module) && defn.isTupleClass(fun.symbol.companionClass) then + args.foreach(_.withAttachment(ForArtifact, ())) + case _ => + else if !tree.tpe.isInstanceOf[MethodOrPoly] then + val f = funPart(tree) + if f.symbol.isConstructor then + f match + case Select(_: New, nme.CONSTRUCTOR) if ctx.outersIterator.exists(_.owner eq f.symbol.owner) => + ignoreArgsOfSelfConstruction(tree, f.symbol) + case _ => + ctx + override def transformApply(tree: Apply)(using Context): tree.type = + // check for multiversal equals + tree match + case Apply(Select(left, nme.Equals | nme.NotEquals), right :: Nil) => + val caneq = defn.CanEqualClass.typeRef.appliedTo(left.tpe.widen :: right.tpe.widen :: Nil) + resolveScoped(caneq) + case tree => + refUsage(tree.tpe.typeSymbol) tree - override def transformTemplate(tree: tpd.Template)(using Context): tpd.Tree = - popOutBlockTemplatePackageDef() + override def transformTypeApply(tree: TypeApply)(using Context): tree.type = + if tree.symbol.exists && tree.symbol.isConstructor then + refUsage(tree.symbol.owner) // redundant with use of resultType in transformSelect of fun tree - override def transformPackageDef(tree: tpd.PackageDef)(using Context): tpd.Tree = - popOutBlockTemplatePackageDef() + override def prepareForAssign(tree: Assign)(using Context): Context = + tree.lhs.putAttachment(AssignmentTarget, ()) // don't take LHS reference as a read + ctx + override def transformAssign(tree: Assign)(using Context): tree.type = + tree.lhs.removeAttachment(AssignmentTarget) tree - override def transformValDef(tree: tpd.ValDef)(using Context): tpd.Tree = - unusedDataApply(_.removeIgnoredUsage(tree.symbol)) + override def prepareForMatch(tree: Match)(using Context): Context = + // allow case.pat against tree.selector (simple var pat only for now) + tree.selector match + case Ident(nm) => tree.cases.foreach(k => allowVariableBindings(List(nm), List(k.pat))) + case _ => + ctx + override def transformMatch(tree: Match)(using Context): tree.type = + if tree.isInstanceOf[InlineMatch] && tree.selector.isEmpty then + val sf = defn.Compiletime_summonFrom + resolveUsage(sf, sf.name, NoPrefix) tree - override def transformDefDef(tree: tpd.DefDef)(using Context): tpd.Tree = - unusedDataApply(_.removeIgnoredUsage(tree.symbol)) + override def transformTypeTree(tree: TypeTree)(using Context): tree.type = + tree.tpe match + case AnnotatedType(_, annot) => transformAllDeep(annot.tree) + case tpt if !tree.isInferred && tpt.typeSymbol.exists => resolveUsage(tpt.typeSymbol, tpt.typeSymbol.name, NoPrefix) + case _ => tree - override def transformTypeDef(tree: tpd.TypeDef)(using Context): tpd.Tree = - unusedDataApply(_.removeIgnoredUsage(tree.symbol)) + override def prepareForInlined(tree: Inlined)(using Context): Context = + if tree.inlinedFromOuterScope then + tree.expansion.putAttachment(InlinedParameter, ()) + ctx + override def transformInlined(tree: Inlined)(using Context): tree.type = + 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 = + register(tree) + ctx - // ---------- MiniPhase HELPERS ----------- + override def prepareForValDef(tree: ValDef)(using Context): Context = + if !tree.symbol.is(Deferred) && tree.rhs.symbol != defn.Predef_undefined then + register(tree) + relax(tree.rhs, tree.tpt.tpe) + if tree.symbol.isAllOf(EnumCase) then ctx.outer else ctx + override def transformValDef(tree: ValDef)(using Context): tree.type = + traverseAnnotations(tree.symbol) + if tree.name.startsWith("derived$") && tree.hasType then + def loop(t: Tree): Unit = t match + case Ident(name) => resolveUsage(t.tpe.typeSymbol, name, t.tpe.underlyingPrefix.skipPackageObject) + case Select(t, _) => loop(t) + case _ => + tree.getAttachment(OriginalTypeClass).foreach(loop) + tree - private def pushInBlockTemplatePackageDef(tree: tpd.Block | tpd.Template | tpd.PackageDef)(using Context): Context = - unusedDataApply { ud => - ud.pushScope(UnusedData.ScopeType.fromTree(tree)) - } + override def prepareForDefDef(tree: DefDef)(using Context): Context = + def trivial = tree.symbol.is(Deferred) || isUnconsuming(tree.rhs) + def nontrivial = tree.symbol.isConstructor || tree.symbol.isAnonymousFunction + def isDefault = tree.symbol.name.is(DefaultGetterName) + if !nontrivial && trivial || isDefault then + refInfos.skip.addOne(tree.symbol) + if tree.symbol.is(Inline) then + refInfos.inliners += 1 + else if !tree.symbol.is(Deferred) && tree.rhs.symbol != defn.Predef_undefined then + register(tree) + relax(tree.rhs, tree.tpt.tpe) ctx + override def transformDefDef(tree: DefDef)(using Context): tree.type = + traverseAnnotations(tree.symbol) + if tree.symbol.is(Inline) then + refInfos.inliners -= 1 + tree - private def popOutBlockTemplatePackageDef()(using Context): Context = - unusedDataApply { ud => - ud.popScope() - } + override def prepareForTypeDef(tree: TypeDef)(using Context): Context = + if tree.symbol.isAllOf(EnumCase) then ctx.outer else ctx + override def transformTypeDef(tree: TypeDef)(using Context): tree.type = + traverseAnnotations(tree.symbol) + if !tree.symbol.is(Param) then // type parameter to do? + register(tree) + tree + + override def prepareForStats(trees: List[Tree])(using Context): Context = + // gather local implicits while ye may + if !ctx.owner.isClass then + if trees.exists(t => t.isDef && t.symbol.is(Given) && t.symbol.isLocalToBlock) then + val scope = newScope.openForMutations + for tree <- trees if tree.isDef && tree.symbol.is(Given) do + scope.enter(tree.symbol.name, tree.symbol) + return ctx.fresh.setScope(scope) ctx - /** - * This traverse is the **main** component of this phase + override def transformOther(tree: Tree)(using Context): tree.type = + tree match + case imp: Import => + register(imp) + transformAllDeep(imp.expr) + for selector <- imp.selectors do + if selector.isGiven then + selector.bound match + case untpd.TypedSplice(bound) => transformAllDeep(bound) + case _ => + case exp: Export => + transformAllDeep(exp.expr) + case AppliedTypeTree(tpt, args) => + transformAllDeep(tpt) + args.foreach(transformAllDeep) + case RefinedTypeTree(tpt, refinements) => + transformAllDeep(tpt) + refinements.foreach(transformAllDeep) + case LambdaTypeTree(tparams, body) => + tparams.foreach(transformAllDeep) + transformAllDeep(body) + case SingletonTypeTree(ref) => + // selftype of object is not a usage + val moduleSelfRef = ctx.owner.is(Module) && ctx.owner == tree.symbol.companionModule.moduleClass + if !moduleSelfRef then + transformAllDeep(ref) + case TypeBoundsTree(lo, hi, alias) => + transformAllDeep(lo) + transformAllDeep(hi) + transformAllDeep(alias) + case tree: NamedArg => transformAllDeep(tree.arg) + case Annotated(arg, annot) => + transformAllDeep(arg) + transformAllDeep(annot) + case Quote(body, tags) => + transformAllDeep(body) + tags.foreach(transformAllDeep) + case Splice(expr) => + transformAllDeep(expr) + case pat @ SplicePattern(body, args) => + transformAllDeep(body) + args.foreach(transformAllDeep) + case MatchTypeTree(bound, selector, cases) => + transformAllDeep(bound) + transformAllDeep(selector) + cases.foreach(transformAllDeep) + case ByNameTypeTree(result) => + transformAllDeep(result) + //case _: InferredTypeTree => // do nothing + //case _ if tree.isType => + case _ => + tree + + private def traverseAnnotations(sym: Symbol)(using Context): Unit = + for annot <- sym.denot.annotations do + transformAllDeep(annot.tree) + + /** If sym is not an enclosing element with respect to the give context, record the reference * - * It traverses the tree and gathers the data in the - * corresponding context property + * Also check that every enclosing element is not a synthetic member + * of the sym's case class companion module. */ - private def traverser = new TreeTraverser: - import tpd.* - import UnusedData.ScopeType + def refUsage(sym: Symbol)(using Context): Unit = + if !refInfos.hasRef(sym) then + val isCase = sym.is(Case) && sym.isClass + if !ctx.outersIterator.exists: outer => + val owner = outer.owner + owner.eq(sym) + || isCase + && owner.exists + && owner.is(Synthetic) + && owner.owner.eq(sym.companionModule.moduleClass) + then + refInfos.addRef(sym) - /* Register every imports, definition and usage */ - override def traverse(tree: tpd.Tree)(using Context): Unit = - val newCtx = if tree.symbol.exists then ctx.withOwner(tree.symbol) else ctx - tree match - case imp: tpd.Import => - unusedDataApply(_.registerImport(imp)) - imp.selectors.filter(_.isGiven).map(_.bound).collect { - case untpd.TypedSplice(tree1) => tree1 - }.foreach(traverse(_)(using newCtx)) - traverseChildren(tree)(using newCtx) - case ident: Ident => - prepareForIdent(ident) - traverseChildren(tree)(using newCtx) - case sel: Select => - prepareForSelect(sel) - traverseChildren(tree)(using newCtx) - case tree: (tpd.Block | tpd.Template | tpd.PackageDef) => - //! DIFFERS FROM MINIPHASE - pushInBlockTemplatePackageDef(tree) - traverseChildren(tree)(using newCtx) - popOutBlockTemplatePackageDef() - case t: tpd.ValDef => - prepareForValDef(t) - traverseChildren(tree)(using newCtx) - transformValDef(t) - case t: tpd.DefDef => - prepareForDefDef(t) - traverseChildren(tree)(using newCtx) - transformDefDef(t) - case t: tpd.TypeDef => - prepareForTypeDef(t) - traverseChildren(tree)(using newCtx) - transformTypeDef(t) - case t: tpd.Bind => - prepareForBind(t) - traverseChildren(tree)(using newCtx) - case t:tpd.Assign => - prepareForAssign(t) - traverseChildren(tree) - case _: tpd.InferredTypeTree => - case t@tpd.RefinedTypeTree(tpt, refinements) => - //! DIFFERS FROM MINIPHASE - typeTraverser(unusedDataApply).traverse(t.tpe) - traverse(tpt)(using newCtx) - case t@tpd.TypeTree() => - //! DIFFERS FROM MINIPHASE - typeTraverser(unusedDataApply).traverse(t.tpe) - traverseChildren(tree)(using newCtx) - case _ => - //! DIFFERS FROM MINIPHASE - traverseChildren(tree)(using newCtx) - end traverse - end traverser - - /** This is a type traverser which catch some special Types not traversed by the term traverser above */ - private def typeTraverser(dt: (UnusedData => Any) => Unit)(using Context) = new TypeTraverser: - override def traverse(tp: Type): Unit = - if tp.typeSymbol.exists then dt(_.registerUsed(tp.typeSymbol, Some(tp.typeSymbol.name))) - tp match - case AnnotatedType(_, annot) => - dt(_.registerUsed(annot.symbol, None)) - traverseChildren(tp) + /** Look up a reference in enclosing contexts to determine whether it was introduced by a definition or import. + * The binding of highest precedence must then be correct. + * + * Unqualified locals and fully qualified globals are neither imported nor in scope; + * 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, imports: Boolean = true)(using Context): Unit = + import PrecedenceLevels.* + val sym = sym0.userSymbol + + def matchingSelector(info: ImportInfo): ImportSelector | Null = + val qtpe = info.site + def hasAltMember(nm: Name) = qtpe.member(nm).hasAltWith: alt => + val sameSym = + alt.symbol == sym + || nm.isTypeName && alt.symbol.isAliasType && alt.info.dealias.typeSymbol == sym + sameSym && alt.symbol.isAccessibleFrom(qtpe) + def hasAltMemberNamed(nm: Name) = qtpe.member(nm).hasAltWith(_.symbol.isAccessibleFrom(qtpe)) + + def loop(sels: List[ImportSelector]): ImportSelector | Null = sels match + case sel :: sels => + val matches = + if sel.isWildcard then + // if name is different from sym.name, it must be a rename on import, not a wildcard selector + !name.exists(_.toTermName != sym.name.toTermName) + // the qualifier must have the target symbol as a member + && hasAltMember(sym.name) + && { + if sel.isGiven then // Further check that the symbol is a given or implicit and conforms to the bound + sym.isOneOf(GivenOrImplicit) + && (sel.bound.isEmpty || sym.info.finalResultType <:< sel.boundTpe) + && (prefix.eq(NoPrefix) || qtpe =:= prefix) + else + !sym.is(Given) // Normal wildcard, check that the symbol is not a given (but can be implicit) + } + else if sel.isUnimport then + val masksMatchingMember = + name != nme.NO_NAME + && sels.exists(x => x.isWildcard && !x.isGiven) + && !name.exists(_.toTermName != sel.name) // import a.b as _, b must match name + && (hasAltMemberNamed(sel.name) || hasAltMemberNamed(sel.name.toTypeName)) + if masksMatchingMember then + refInfos.sels.put(sel, ()) // imprecise due to precedence but errs on the side of false negative + false + else + !name.exists(_.toTermName != sel.rename) // if there is an explicit name, it must match + && (prefix.eq(NoPrefix) || qtpe =:= prefix) + && (hasAltMember(sel.name) || hasAltMember(sel.name.toTypeName)) + if matches then sel else loop(sels) + case nil => null + loop(info.selectors) + end matchingSelector + + def checkMember(ctxsym: Symbol): Boolean = + ctxsym.isClass && sym.owner.isClass + && ctxsym.thisType.baseClasses.contains(sym.owner) + && ctxsym.thisType.member(sym.name).hasAltWith(d => d.containsSym(sym) && !name.exists(_ != d.name)) + + // Avoid spurious NoSymbol and also primary ctors which are never warned about. + // Selections C.this.toString should be already excluded, but backstopped here for eq, etc. + if !sym.exists || sym.isPrimaryConstructor || sym.isEffectiveRoot || defn.topClasses(sym.owner) then return + + // Find the innermost, highest precedence. Contexts have no nesting levels but assume correctness. + // If the sym is an enclosing definition (the owner of a context), it does not count toward usages. + val isLocal = sym.isLocalToBlock + var candidate: Context = NoContext + var importer: ImportSelector | Null = null // non-null for import context + var precedence = NoPrecedence // of current resolution + var enclosed = false // true if sym is owner of an enclosing context + var done = false + val ctxs = ctx.outersIterator + 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 record the reference + if cur.isImportContext then + val sel = matchingSelector(cur.importInfo.nn) + if sel != null then + if cur.importInfo.nn.isRootImport then + if precedence.weakerThan(OtherUnit) then + precedence = OtherUnit + candidate = cur + importer = sel + done = true + else if sel.isWildcard then + if precedence.weakerThan(Wildcard) then + precedence = Wildcard + candidate = cur + importer = sel + else + if precedence.weakerThan(NamedImport) then + precedence = NamedImport + candidate = cur + importer = sel + else if isLocal then + if cur.owner eq sym.owner then + done = true // local def or param + else if checkMember(cur.owner) then + if sym.is(Package) || sym.srcPos.sourcePos.source == ctx.source then + precedence = Definition + candidate = cur + importer = null // ignore import in same scope; we can't check nesting level + done = true + else if precedence.weakerThan(OtherUnit) then + precedence = OtherUnit + candidate = cur + end while + // record usage and possibly an import + if !enclosed then + refUsage(sym) + if imports && candidate != NoContext && candidate.isImportContext && importer != null then + refInfos.sels.put(importer, ()) + end resolveUsage + + /** Simulate implicit search for contextual implicits in lexical scope and mark any definitions or imports as used. + * Avoid cached ctx.implicits because it needs the precise import context that introduces the given. + */ + def resolveScoped(tp: Type)(using Context): Unit = + var done = false + val ctxs = ctx.outersIterator + while !done && ctxs.hasNext do + val cur = ctxs.next() + val implicitRefs: List[ImplicitRef] = + if (cur.isClassDefContext) cur.owner.thisType.implicitMembers + else if (cur.isImportContext) cur.importInfo.nn.importedImplicits + else if (cur.isNonEmptyScopeContext) cur.scope.implicitDecls + else Nil + implicitRefs.find(ref => ref.underlyingRef.widen <:< tp) match + case Some(found: TermRef) => + refUsage(found.denot.symbol) + if cur.isImportContext then + cur.importInfo.nn.selectors.find(sel => sel.isGiven || sel.rename == found.name) match + case Some(sel) => + refInfos.sels.put(sel, ()) + case _ => + return + case Some(found: RenamedImplicitRef) if cur.isImportContext => + refUsage(found.underlyingRef.denot.symbol) + cur.importInfo.nn.selectors.find(sel => sel.rename == found.implicitName) match + case Some(sel) => + refInfos.sels.put(sel, ()) case _ => - traverseChildren(tp) + return + case _ => + end resolveScoped - /** This traverse the annotations of the symbol */ - private def traverseAnnotations(sym: Symbol)(using Context): Unit = - sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree)) - - - /** Do the actual reporting given the result of the anaylsis */ - private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit = - res.warnings.toList.sortBy(_.pos.span.point)(using Ordering[Int]).foreach { s => - s match - case UnusedSymbol(t, _, WarnTypes.Imports) => - report.warning(UnusedSymbolMessage.imports, t) - case UnusedSymbol(t, _, WarnTypes.LocalDefs) => - report.warning(UnusedSymbolMessage.localDefs, t) - case UnusedSymbol(t, _, WarnTypes.ExplicitParams) => - report.warning(UnusedSymbolMessage.explicitParams, t) - case UnusedSymbol(t, _, WarnTypes.ImplicitParams) => - report.warning(UnusedSymbolMessage.implicitParams, t) - case UnusedSymbol(t, _, WarnTypes.PrivateMembers) => - report.warning(UnusedSymbolMessage.privateMembers, t) - case UnusedSymbol(t, _, WarnTypes.PatVars) => - report.warning(UnusedSymbolMessage.patVars, t) - case UnusedSymbol(t, _, WarnTypes.UnsetLocals) => - report.warning("unset local variable, consider using an immutable val instead", t) - case UnusedSymbol(t, _, WarnTypes.UnsetPrivates) => - report.warning("unset private variable, consider using an immutable val instead", t) - } + /** 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: - val phaseNamePrefix: String = "checkUnused" - val description: String = "check for unused elements" enum PhaseMode: - case Aggregate - case Report - - private enum WarnTypes: - case Imports - case LocalDefs - case ExplicitParams - case ImplicitParams - case PrivateMembers - case PatVars - case UnsetLocals - case UnsetPrivates - - /** - * The key used to retrieve the "unused entity" analysis metadata, - * from the compilation `Context` - */ - private val _key = Property.StickyKey[UnusedData] + case Aggregate // new defs are considered + case Resolve // no new defs, only additional references + case Report // report when done + val refInfosKey = Property.StickyKey[RefInfos] + + inline def refInfos(using Context): RefInfos = ctx.property(refInfosKey).get + + /** Attachment holding the name of an Ident as written by the user. */ val OriginalName = Property.StickyKey[Name] - class PostTyper extends CheckUnused(PhaseMode.Aggregate, "PostTyper", _key) + /** Suppress warning in a tree, such as a patvar name allowed by special convention. */ + val NoWarn = Property.StickyKey[Unit] - class PostInlining extends CheckUnused(PhaseMode.Report, "PostInlining", _key) + /** Ignore reference. */ + val Ignore = Property.StickyKey[Unit] - /** - * A stateful class gathering the infos on : - * - imports - * - definitions - * - usage - */ - private class UnusedData: - import collection.mutable.{Set => MutSet, Map => MutMap, Stack => MutStack, ListBuffer => MutList} - import UnusedData.* - - /** The current scope during the tree traversal */ - val currScopeType: MutStack[ScopeType] = MutStack(ScopeType.Other) - - var unusedAggregate: Option[UnusedResult] = None - - /* IMPORTS */ - private val impInScope = MutStack(MutList[ImportSelectorData]()) - /** - * We store the symbol along with their accessibility without import. - * Accessibility to their definition in outer context/scope - * - * See the `isAccessibleAsIdent` extension method below in the file - */ - private val usedInScope = MutStack(MutSet[(Symbol, Option[Name], Boolean)]()) - private val usedInPosition = MutMap.empty[Name, MutSet[Symbol]] - /* unused import collected during traversal */ - private val unusedImport = MutList.empty[ImportSelectorData] - - /* LOCAL DEF OR VAL / Private Def or Val / Pattern variables */ - private val localDefInScope = MutList.empty[tpd.MemberDef] - private val privateDefInScope = MutList.empty[tpd.MemberDef] - private val explicitParamInScope = MutList.empty[tpd.MemberDef] - private val implicitParamInScope = MutList.empty[tpd.MemberDef] - private val patVarsInScope = MutList.empty[tpd.Bind] - - /** All variables sets*/ - private val setVars = MutSet[Symbol]() - - /** All used symbols */ - private val usedDef = MutSet[Symbol]() - /** Do not register as used */ - private val doNotRegister = MutSet[Symbol]() - - /** Trivial definitions, avoid registering params */ - private val trivialDefs = MutSet[Symbol]() - - private val paramsToSkip = MutSet[Symbol]() - - - def finishAggregation(using Context)(): Unit = - val unusedInThisStage = this.getUnused - this.unusedAggregate match { - case None => - this.unusedAggregate = Some(unusedInThisStage) - case Some(prevUnused) => - val intersection = unusedInThisStage.warnings.intersect(prevUnused.warnings) - this.unusedAggregate = Some(UnusedResult(intersection)) - } + /** Tree is LHS of Assign. */ + val AssignmentTarget = Property.StickyKey[Unit] + /** Tree is an inlined parameter. */ + val InlinedParameter = Property.StickyKey[Unit] - /** - * Register a found (used) symbol along with its name - * - * The optional name will be used to target the right import - * as the same element can be imported with different renaming - */ - def registerUsed(sym: Symbol, name: Option[Name], includeForImport: Boolean = true, isDerived: Boolean = false)(using Context): Unit = - if sym.exists && !isConstructorOfSynth(sym) && !doNotRegister(sym) then - if sym.isConstructor then - registerUsed(sym.owner, None, includeForImport) // constructor are "implicitly" imported with the class - else - // If the symbol is accessible in this scope without an import, do not register it for unused import analysis - val includeForImport1 = - includeForImport - && (name.exists(_.toTermName != sym.name.toTermName) || !sym.isAccessibleAsIdent) - - def addIfExists(sym: Symbol): Unit = - if sym.exists then - usedDef += sym - if includeForImport1 then - usedInScope.top += ((sym, name, isDerived)) - addIfExists(sym) - addIfExists(sym.companionModule) - addIfExists(sym.companionClass) - if sym.sourcePos.exists then - for n <- name do - usedInPosition.getOrElseUpdate(n, MutSet.empty) += sym - - /** Register a symbol that should be ignored */ - def addIgnoredUsage(sym: Symbol)(using Context): Unit = - doNotRegister ++= sym.everySymbol - - /** Remove a symbol that shouldn't be ignored anymore */ - def removeIgnoredUsage(sym: Symbol)(using Context): Unit = - doNotRegister --= sym.everySymbol - - def addIgnoredParam(sym: Symbol)(using Context): Unit = - paramsToSkip += sym - - /** Register an import */ - def registerImport(imp: tpd.Import)(using Context): Unit = - if - !tpd.languageImport(imp.expr).nonEmpty + def PostTyper() = CheckUnused(PhaseMode.Aggregate, "PostTyper") + + def PostInlining() = CheckUnused(PhaseMode.Resolve, "PostInlining") + + def PostPatMat() = CheckUnused(PhaseMode.Report, "PostPatMat") + + class RefInfos: + val defs = mutable.Set.empty[(Symbol, SrcPos)] // definitions with tree pos + val pats = mutable.Set.empty[(Symbol, SrcPos)] // pattern variables with tree pos + val refs = mutable.Set.empty[Symbol] // references + 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 = + tree match + case tree if !tree.srcPos.isUserCode => + case imp: Import => + if inliners == 0 + && languageImport(imp.expr).isEmpty && !imp.isGeneratedByEnum - && !isTransparentAndInline(imp) - && currScopeType.top != ScopeType.ReplWrapper // #18383 Do not report top-level import's in the repl as unused + && !ctx.owner.name.isReplWrapperName + then + if imp.isCompiletimeTesting then + isNullified = true + else + imps.put(imp, ()) + case tree: Bind => + if !tree.name.isInstanceOf[DerivedName] && !tree.name.is(WildcardParamName) then + if tree.hasAttachment(NoWarn) then + nowarn.addOne(tree.symbol) + pats.addOne(tree.symbol -> tree.namePos) + case tree: NamedDefTree => + if tree.hasAttachment(PatternVar) then + if !tree.name.isInstanceOf[DerivedName] then + pats.addOne(tree.symbol -> tree.namePos) + else if (tree.symbol ne NoSymbol) + && !tree.name.isWildcard + && !tree.symbol.is(ModuleVal) // track only the ModuleClass using the object symbol, with correct namePos + then + if tree.hasAttachment(NoWarn) then + nowarn.addOne(tree.symbol) + defs.addOne(tree.symbol.userSymbol -> tree.namePos) + case _ => + if tree.symbol ne NoSymbol then + defs.addOne(tree.symbol -> tree.srcPos) // TODO is this a code path + + var inliners = 0 // depth of inline def (not inlined yet) + + // instead of refs.addOne, use refUsage -> addRef to distinguish a read from a write to var + var isAssignment = false + def addRef(sym: Symbol): Unit = + if isAssignment then + asss.addOne(sym) + else + refs.addOne(sym) + def hasRef(sym: Symbol): Boolean = + if isAssignment then + asss(sym) + else + refs(sym) + + // currently compiletime.testing is completely erased, so ignore the unit + var isNullified = false + end RefInfos + + // Names are resolved by definitions and imports, which have four precedence levels: + object PrecedenceLevels: + opaque type Precedence = Int + inline def NoPrecedence: Precedence = 5 + inline def OtherUnit: Precedence = 4 // root import or def from another compilation unit via enclosing package + inline def Wildcard: Precedence = 3 // wildcard import + inline def NamedImport: Precedence = 2 // specific import + inline def Definition: Precedence = 1 // def from this compilation unit + extension (p: Precedence) + inline def weakerThan(q: Precedence): Boolean = p > q + inline def isNone: Boolean = p == NoPrecedence + + def reportUnused()(using Context): Unit = if !refInfos.isNullified then + atPhaseBeforeTransforms: + for (msg, pos, origin) <- warnings do + report.warning(msg, pos, origin) + + type MessageInfo = (UnusedSymbol, SrcPos, String) // string is origin or empty + + def warnings(using Context): Array[MessageInfo] = + val actionable: true = true //ctx.settings.rewrite.value.nonEmpty + val warnings = ArrayBuilder.make[MessageInfo] + def warnAt(pos: SrcPos)(msg: UnusedSymbol, origin: String = Diagnostic.OriginWarning.NoOrigin): Unit = + warnings.addOne((msg, pos, origin)) + val infos = refInfos + + // 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 + if ctx.settings.WunusedHas.locals && sym.is(Mutable) && !infos.asss(sym) then + warnAt(pos)(UnusedSymbol.unsetLocals) + else if ctx.settings.WunusedHas.privates + && sym.is(Mutable) + && (sym.is(Private) || sym.isEffectivelyPrivate) + && !sym.isSetter // tracks sym.underlyingSymbol sibling getter, check setter below + && !isMutated(sym) then - val qualTpe = imp.expr.tpe - - // Put wildcard imports at the end, because they have lower priority within one Import - val reorderdSelectors = - val (wildcardSels, nonWildcardSels) = imp.selectors.partition(_.isWildcard) - nonWildcardSels ::: wildcardSels - - val excludedMembers: mutable.Set[TermName] = mutable.Set.empty - - val newDataInScope = - for sel <- reorderdSelectors yield - val data = new ImportSelectorData(qualTpe, sel) - if shouldSelectorBeReported(imp, sel) || isImportExclusion(sel) || isImportIgnored(imp, sel) then - // Immediately mark the selector as used - data.markUsed() - if isImportExclusion(sel) then - excludedMembers += sel.name - if sel.isWildcard && excludedMembers.nonEmpty then - // mark excluded members for the wildcard import - data.markExcluded(excludedMembers.toSet) - data - impInScope.top.appendAll(newDataInScope) - end registerImport - - /** Register (or not) some `val` or `def` according to the context, scope and flags */ - def registerDef(memDef: tpd.MemberDef)(using Context): Unit = - if memDef.isValidMemberDef && !isDefIgnored(memDef) then - if memDef.isValidParam then - if memDef.symbol.isOneOf(GivenOrImplicit) then - if !paramsToSkip.contains(memDef.symbol) then - implicitParamInScope += memDef - else if !paramsToSkip.contains(memDef.symbol) then - explicitParamInScope += memDef - else if currScopeType.top == ScopeType.Local then - localDefInScope += memDef - else if memDef.shouldReportPrivateDef then - privateDefInScope += memDef - - /** Register pattern variable */ - def registerPatVar(patvar: tpd.Bind)(using Context): Unit = - if !patvar.symbol.isUnusedAnnot then - patVarsInScope += patvar - - /** enter a new scope */ - def pushScope(newScopeType: ScopeType): Unit = - // unused imports : - currScopeType.push(newScopeType) - impInScope.push(MutList()) - usedInScope.push(MutSet()) - - def registerSetVar(sym: Symbol): Unit = - setVars += sym - - /** - * leave the current scope and do : - * - * - If there are imports in this scope check for unused ones - */ - def popScope()(using Context): Unit = - currScopeType.pop() - val usedInfos = usedInScope.pop() - val selDatas = impInScope.pop() - - for usedInfo <- usedInfos do - val (sym, optName, isDerived) = usedInfo - val usedData = selDatas.find { selData => - sym.isInImport(selData, optName, isDerived) - } - usedData match - case Some(data) => - data.markUsed() - case None => - // Propagate the symbol one level up - if usedInScope.nonEmpty then - usedInScope.top += usedInfo - end for // each in `used` - - for selData <- selDatas do - if !selData.isUsed then - unusedImport += selData - end popScope - - /** - * Leave the scope and return a `List` of unused `ImportSelector`s - * - * The given `List` is sorted by line and then column of the position - */ - - def getUnused(using Context): UnusedResult = - popScope() - - def isUsedInPosition(name: Name, span: Span): Boolean = - usedInPosition.get(name) match - case Some(syms) => syms.exists(sym => span.contains(sym.span)) - case None => false - - val sortedImp = - if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then - unusedImport.toList - .map(d => UnusedSymbol(d.selector.srcPos, d.selector.name, WarnTypes.Imports)) - else - Nil - // Partition to extract unset local variables from usedLocalDefs - val (usedLocalDefs, unusedLocalDefs) = - if ctx.settings.WunusedHas.locals then - localDefInScope.toList.partition(d => d.symbol.usedDefContains) - else - (Nil, Nil) - val sortedLocalDefs = - unusedLocalDefs - .filterNot(d => isUsedInPosition(d.symbol.name, d.span)) - .filterNot(d => containsSyntheticSuffix(d.symbol)) - .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.LocalDefs)) - val unsetLocalDefs = usedLocalDefs.filter(isUnsetVarDef).map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.UnsetLocals)).toList - - val sortedExplicitParams = - if ctx.settings.WunusedHas.explicits then - explicitParamInScope.toList - .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => isUsedInPosition(d.symbol.name, d.span)) - .filterNot(d => containsSyntheticSuffix(d.symbol)) - .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.ExplicitParams)) - else - Nil - val sortedImplicitParams = - if ctx.settings.WunusedHas.implicits then - implicitParamInScope.toList - .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => containsSyntheticSuffix(d.symbol)) - .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.ImplicitParams)) - else - Nil - // Partition to extract unset private variables from usedPrivates - val (usedPrivates, unusedPrivates) = - if ctx.settings.WunusedHas.privates then - privateDefInScope.toList.partition(d => d.symbol.usedDefContains) + warnAt(pos)(UnusedSymbol.unsetPrivates) + + def checkPrivate(sym: Symbol, pos: SrcPos) = + if ctx.settings.WunusedHas.privates + && !sym.isPrimaryConstructor + && !sym.isOneOf(SelfName | Synthetic | CaseAccessor) + && !sym.name.is(BodyRetainerName) + && !sym.isSerializationSupport + && !( sym.is(Mutable) + && sym.isSetter // tracks sym.underlyingSymbol sibling getter + && (sym.owner.is(Trait) || sym.owner.isAnonymousClass) + ) + && !infos.nowarn(sym) + then + if sym.is(Mutable) && isMutated(sym) then + warnAt(pos)(UnusedSymbol.privateVars) else - (Nil, Nil) - val sortedPrivateDefs = unusedPrivates.filterNot(d => containsSyntheticSuffix(d.symbol)).map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.PrivateMembers)) - val unsetPrivateDefs = usedPrivates.filter(isUnsetVarDef).map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.UnsetPrivates)) - val sortedPatVars = - if ctx.settings.WunusedHas.patvars then - patVarsInScope.toList - .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => containsSyntheticSuffix(d.symbol)) - .filterNot(d => isUsedInPosition(d.symbol.name, d.span)) - .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.PatVars)) + warnAt(pos)(UnusedSymbol.privateMembers) + + def checkParam(sym: Symbol, pos: SrcPos) = + val m = sym.owner + def allowed = + val dd = defn + m.isDeprecated + || m.is(Synthetic) && !m.isAnonymousFunction + || m.hasAnnotation(defn.UnusedAnnot) // param of unused method + || sym.info.isSingleton + || m.isConstructor && m.owner.thisType.baseClasses.contains(defn.AnnotationClass) + def checkExplicit(): Unit = + // A class param is unused if its param accessor is unused. + // (The class param is not assigned to a field until constructors.) + // A local param accessor warns as a param; a private accessor as a private member. + // Avoid warning for case class elements because they are aliased via unapply (i.e. may be extracted). + if m.isPrimaryConstructor then + val alias = m.owner.info.member(sym.name) + if alias.exists then + val aliasSym = alias.symbol + if aliasSym.isAllOf(PrivateParamAccessor, butNot = CaseAccessor) + && !infos.refs(alias.symbol) + && !usedByDefaultGetter(sym, m) + then + if aliasSym.is(Local) then + if ctx.settings.WunusedHas.explicits then + warnAt(pos)(UnusedSymbol.explicitParams(aliasSym)) + else + if ctx.settings.WunusedHas.privates then + warnAt(pos)(UnusedSymbol.privateMembers) + else if ctx.settings.WunusedHas.explicits + && !sym.is(Synthetic) // param to setter is unused bc there is no field yet + && !(sym.owner.is(ExtensionMethod) && + m.paramSymss.dropWhile(_.exists(_.isTypeParam)).match + case (h :: Nil) :: _ => h == sym // param is the extended receiver + case _ => false + ) + && !sym.name.isInstanceOf[DerivedName] + && !ctx.platform.isMainMethod(m) + && !usedByDefaultGetter(sym, m) + then + warnAt(pos)(UnusedSymbol.explicitParams(sym)) + end checkExplicit + // begin + if !infos.skip(m) + && !m.isEffectivelyOverride + && !allowed + then + checkExplicit() + end checkParam + + // does the param have an alias in a default arg method that is used? + def usedByDefaultGetter(param: Symbol, meth: Symbol): Boolean = + val cls = if meth.isConstructor then meth.enclosingClass.companionModule else meth.enclosingClass + val MethName = meth.name + cls.info.decls.exists: d => + d.name match + case DefaultGetterName(MethName, _) => + d.paramSymss.exists(_.exists(p => p.name == param.name && infos.refs(p))) + case _ => false + + def checkImplicit(sym: Symbol, pos: SrcPos) = + val m = sym.owner + def allowed = + val dd = defn + m.isDeprecated + || m.is(Synthetic) + || m.hasAnnotation(dd.UnusedAnnot) // param of unused method + || sym.owner.name.isContextFunction // a ubiquitous parameter + || sym.info.dealias.typeSymbol.match // more ubiquity + case dd.DummyImplicitClass | dd.SubTypeClass | dd.SameTypeClass => true + case tps => + tps.isMarkerTrait // no members to use; was only if sym.name.is(ContextBoundParamName) + || // but consider NotGiven + tps.hasAnnotation(dd.LanguageFeatureMetaAnnot) + || sym.info.isSingleton // DSL friendly + || sym.info.dealias.isInstanceOf[RefinedType] // can't be expressed as a context bound + if ctx.settings.WunusedHas.implicits + && !infos.skip(m) + && !m.isEffectivelyOverride + && !allowed + then + if m.isPrimaryConstructor then + val alias = m.owner.info.member(sym.name) + if alias.exists then + val aliasSym = alias.symbol + val checking = + aliasSym.isAllOf(PrivateParamAccessor, butNot = CaseAccessor) + || aliasSym.isAllOf(Protected | ParamAccessor, butNot = CaseAccessor) && m.owner.is(Given) + if checking + && !infos.refs(alias.symbol) + && !usedByDefaultGetter(sym, m) + then + warnAt(pos)(UnusedSymbol.implicitParams(aliasSym)) + else if !usedByDefaultGetter(sym, m) then + warnAt(pos)(UnusedSymbol.implicitParams(sym)) + + def checkLocal(sym: Symbol, pos: SrcPos) = + if ctx.settings.WunusedHas.locals + && !sym.isOneOf(InlineProxy | Synthetic) + then + if sym.is(Mutable) && infos.asss(sym) then + warnAt(pos)(UnusedSymbol.localVars) else - Nil - val warnings = - sortedImp ::: - sortedLocalDefs ::: - sortedExplicitParams ::: - sortedImplicitParams ::: - sortedPrivateDefs ::: - sortedPatVars ::: - unsetLocalDefs ::: - unsetPrivateDefs - UnusedResult(warnings.toSet) - end getUnused - //============================ HELPERS ==================================== - - - /** - * Checks if import selects a def that is transparent and inline - */ - private def isTransparentAndInline(imp: tpd.Import)(using Context): Boolean = - imp.selectors.exists { sel => - val qual = imp.expr - val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) - importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) - } - - /** - * Heuristic to detect synthetic suffixes in names of symbols - */ - private def containsSyntheticSuffix(symbol: Symbol)(using Context): Boolean = - symbol.name.mangledString.contains("$") - - /** - * Is the constructor of synthetic package object - * Should be ignored as it is always imported/used in package - * Trigger false negative on used import - * - * Without this check example: - * - * --- WITH PACKAGE : WRONG --- - * {{{ - * package a: - * val x: Int = 0 - * package b: - * import a.* // no warning - * }}} - * --- WITH OBJECT : OK --- - * {{{ - * object a: - * val x: Int = 0 - * object b: - * import a.* // unused warning - * }}} - */ - private def isConstructorOfSynth(sym: Symbol)(using Context): Boolean = - sym.exists && sym.isConstructor && sym.owner.isPackageObject && sym.owner.is(Synthetic) - - /** - * This is used to avoid reporting the parameters of the synthetic main method - * generated by `@main` - */ - private def isSyntheticMainParam(sym: Symbol)(using Context): Boolean = - sym.exists && ctx.platform.isMainMethod(sym.owner) && sym.owner.is(Synthetic) - - /** - * This is used to ignore exclusion imports (i.e. import `qual`.{`member` => _}) - */ - private def isImportExclusion(sel: ImportSelector): Boolean = sel.renamed match - case untpd.Ident(name) => name == StdNames.nme.WILDCARD - case _ => false - - /** - * If -Wunused:strict-no-implicit-warn import and this import selector could potentially import implicit. - * return true - */ - private def shouldSelectorBeReported(imp: tpd.Import, sel: ImportSelector)(using Context): Boolean = - ctx.settings.WunusedHas.strictNoImplicitWarn && ( - sel.isWildcard || - imp.expr.tpe.member(sel.name.toTermName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit)) || - imp.expr.tpe.member(sel.name.toTypeName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit)) - ) - - /** - * Ignore CanEqual imports - */ - private def isImportIgnored(imp: tpd.Import, sel: ImportSelector)(using Context): Boolean = - (sel.isWildcard && sel.isGiven && imp.expr.tpe.allMembers.exists(p => p.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)) && p.symbol.isOneOf(GivenOrImplicit))) || - (imp.expr.tpe.member(sel.name.toTermName).alternatives - .exists(p => p.symbol.isOneOf(GivenOrImplicit) && p.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)))) - - /** - * Ignore definitions of CanEqual given - */ - private def isDefIgnored(memDef: tpd.MemberDef)(using Context): Boolean = - memDef.symbol.isOneOf(GivenOrImplicit) && memDef.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)) - - extension (tree: ImportSelector) - def boundTpe: Type = tree.bound match { - case untpd.TypedSplice(tree1) => tree1.tpe - case _ => NoType - } - - extension (sym: Symbol) - /** is accessible without import in current context */ - private def isAccessibleAsIdent(using Context): Boolean = - ctx.outersIterator.exists{ c => - c.owner == sym.owner - || sym.owner.isClass && c.owner.isClass - && c.owner.thisType.baseClasses.contains(sym.owner) - && c.owner.thisType.member(sym.name).alternatives.contains(sym) - } - - /** Given an import and accessibility, return selector that matches import<->symbol */ - private def isInImport(selData: ImportSelectorData, altName: Option[Name], isDerived: Boolean)(using Context): Boolean = - assert(sym.exists, s"Symbol $sym does not exist") - - val selector = selData.selector - - if !selector.isWildcard then - if altName.exists(explicitName => selector.rename != explicitName.toTermName) then - // if there is an explicit name, it must match - false - else - if isDerived then - // See i15503i.scala, grep for "package foo.test.i17156" - selData.allSymbolsDealiasedForNamed.contains(dealias(sym)) + warnAt(pos)(UnusedSymbol.localDefs) + + def checkPatvars() = + // patvars in for comprehensions share the pos of where the name was introduced + val byPos = infos.pats.groupMap((sym, pos) => sym.srcPos.sourcePos.withSpan(sym.srcPos.span.toSynthetic))(_._1) + for (pos, syms) <- byPos if pos.span.exists && !syms.exists(_.hasAnnotation(defn.UnusedAnnot)) do + if !syms.exists(infos.refs(_)) then + if !syms.exists(v => !v.isLocal && !v.is(Private) || infos.nowarn(v)) then + warnAt(syms.head.namePos)(UnusedSymbol.patVars) + else if syms.exists(_.is(Mutable)) then // check unassigned var + val sym = // recover the original + if syms.size == 1 then syms.head + else infos.pats.find((s, p) => syms.contains(s) && !p.span.isSynthetic).map(_._1).getOrElse(syms.head) + if sym.is(Mutable) && !infos.asss(sym) then + if sym.isLocalToBlock then + warnAt(sym.namePos)(UnusedSymbol.unsetLocals) + else if sym.is(Private) then + warnAt(sym.namePos)(UnusedSymbol.unsetPrivates) + + def checkImports() = + import scala.jdk.CollectionConverters.given + type ImpSel = (Import, ImportSelector) + def isUsed(sel: ImportSelector): Boolean = infos.sels.containsKey(sel) + def warnImport(warnable: ImpSel, actions: List[CodeAction] = Nil): Unit = + val (imp, sel) = warnable + val msg = UnusedSymbol.imports(actions) + // example collection.mutable.{Map as MutMap} + val origin = cpy.Import(imp)(imp.expr, List(sel)).show(using ctx.withoutColors).stripPrefix("import ") + warnAt(sel.srcPos)(msg, origin) + + if !actionable then + for imp <- infos.imps.keySet.nn.asScala; sel <- imp.selectors if !isUsed(sel) do + warnImport(imp -> sel) + else + // If the rest of the line is blank, include it in the final edit position. (Delete trailing whitespace.) + // If for deletion, and the prefix of the line is also blank, then include that, too. (Del blank line.) + // If deleting a blank line and surrounded by blank lines, remove an adjoining blank line. + def editPosAt(srcPos: SrcPos, forDeletion: Boolean): SrcPos = + val start = srcPos.span.start + val end = srcPos.span.end + val content = srcPos.sourcePos.source.content() + val prev = content.lastIndexWhere(c => !isWhitespace(c), end = start - 1) + val emptyLeft = prev < 0 || isLineBreakChar(content(prev)) + val next = content.indexWhere(c => !isWhitespace(c), from = end) + val emptyRight = next < 0 || isLineBreakChar(content(next)) + val deleteLine = emptyLeft && emptyRight && forDeletion + val bump = if (deleteLine) 1 else 0 // todo improve to include offset of next line, endline + 1 + val p0 = srcPos.span + val p1 = if (next >= 0 && emptyRight) p0.withEnd(next + bump) else p0 + val p2 = + if deleteLine then + var newStart = prev + 1 + if srcPos.line > 1 then + val source = srcPos.sourcePos.source + import source.{lineToOffset, lineToOffsetOpt, offsetToLine} + val startLine = offsetToLine(start) + val endLine = offsetToLine(end) + val preceding = lineToOffset(startLine - 1) + lineToOffsetOpt(endLine + 2) match + case Some(succeeding) if lineToOffset(startLine) - preceding == 1 && succeeding - end == 2 => + newStart = preceding + case _ => + p1.withStart(newStart) + else p1 + srcPos.sourcePos.withSpan(p2) + def actionsOf(actions: (SrcPos, String)*): List[CodeAction] = + val patches = actions.map((srcPos, replacement) => ActionPatch(srcPos.sourcePos, replacement)).toList + List(CodeAction(title = "unused import", description = Some("remove import"), patches)) + def replace(editPos: SrcPos)(replacement: String): List[CodeAction] = actionsOf(editPos -> replacement) + def deletion(editPos: SrcPos): List[CodeAction] = actionsOf(editPos -> "") + def textFor(impsel: ImpSel): String = + val (imp, sel) = impsel + val content = imp.srcPos.sourcePos.source.content() + def textAt(pos: SrcPos) = String(content.slice(pos.span.start, pos.span.end)) + val qual = textAt(imp.expr.srcPos) // keep original + val selector = textAt(sel.srcPos) // keep original + s"$qual.$selector" // don't succumb to vagaries of show + // begin actionable + val sortedImps = infos.imps.keySet.nn.asScala + .filter(_.srcPos.span.exists) // extra caution + .toArray + .sortBy(_.srcPos.span.point) // sorted by pos, not sort in place + var index = 0 + while index < sortedImps.length do + val nextImport = sortedImps.indexSatisfying(from = index + 1)(_.isPrimaryClause) // next import statement + if sortedImps.indexSatisfying(from = index, until = nextImport): imp => + imp.selectors.exists(!isUsed(_)) // check if any selector in statement was unused + < nextImport then + // if no usable selectors in the import statement, delete it entirely. + // if there is exactly one usable selector, then replace with just that selector (i.e., format it). + // else for each clause, delete it or format one selector or delete unused selectors. + // To delete a comma separated item, delete start-to-start, but for last item delete a preceding comma. + // Reminder that first clause span includes the keyword, so delete point-to-start instead. + val existing = sortedImps.slice(index, nextImport) + val (keeping, deleting) = existing.iterator.flatMap(imp => imp.selectors.map(imp -> _)).toList + .partition((imp, sel) => isUsed(sel)) + if keeping.isEmpty then + val editPos = existing.head.srcPos.sourcePos.withSpan: + Span(start = existing.head.srcPos.span.start, end = existing.last.srcPos.span.end) + deleting.init.foreach(warnImport(_)) + warnImport(deleting.last, deletion(editPosAt(editPos, forDeletion = true))) + else if keeping.lengthIs == 1 then + val editPos = existing.head.srcPos.sourcePos.withSpan: + Span(start = existing.head.srcPos.span.start, end = existing.last.srcPos.span.end) + deleting.init.foreach(warnImport(_)) + val text = s"import ${textFor(keeping.head)}" + warnImport(deleting.last, replace(editPosAt(editPos, forDeletion = false))(text)) else - selData.allSymbolsForNamed.contains(sym) + val lostClauses = existing.iterator.filter(imp => !keeping.exists((i, _) => imp eq i)).toList + for imp <- lostClauses do + val actions = + if imp == existing.last then + val content = imp.srcPos.sourcePos.source.content() + val prev = existing.lastIndexWhere(i0 => keeping.exists((i, _) => i == i0)) + val comma = content.indexOf(',', from = existing(prev).srcPos.span.end) + val commaPos = imp.srcPos.sourcePos.withSpan: + Span(start = comma, end = existing(prev + 1).srcPos.span.start) + val srcPos = imp.srcPos + val editPos = srcPos.sourcePos.withSpan: // exclude keyword + srcPos.span.withStart(srcPos.span.point) + actionsOf(commaPos -> "", editPos -> "") + else + val impIndex = existing.indexOf(imp) + val editPos = imp.srcPos.sourcePos.withSpan: // exclude keyword + Span(start = imp.srcPos.span.point, end = existing(impIndex + 1).srcPos.span.start) + deletion(editPos) + imp.selectors.init.foreach(sel => warnImport(imp -> sel)) + warnImport(imp -> imp.selectors.last, actions) + val singletons = existing.iterator.filter(imp => keeping.count((i, _) => imp eq i) == 1).toList + var seen = List.empty[Import] + for impsel <- deleting do + val (imp, sel) = impsel + if singletons.contains(imp) then + if seen.contains(imp) then + warnImport(impsel) + else + seen ::= imp + val editPos = imp.srcPos.sourcePos.withSpan: + Span(start = imp.srcPos.span.point, end = imp.srcPos.span.end) // exclude keyword + val text = textFor(keeping.find((i, _) => imp eq i).get) + warnImport(impsel, replace(editPosAt(editPos, forDeletion = false))(text)) + else if !lostClauses.contains(imp) then + val actions = + if sel == imp.selectors.last then + val content = sel.srcPos.sourcePos.source.content() + val prev = imp.selectors.lastIndexWhere(s0 => keeping.exists((_, s) => s == s0)) + val comma = content.indexOf(',', from = imp.selectors(prev).srcPos.span.end) + val commaPos = sel.srcPos.sourcePos.withSpan: + Span(start = comma, end = imp.selectors(prev + 1).srcPos.span.start) + val editPos = sel.srcPos + actionsOf(commaPos -> "", editPos -> "") + else + val selIndex = imp.selectors.indexOf(sel) + val editPos = sel.srcPos.sourcePos.withSpan: + sel.srcPos.span.withEnd(imp.selectors(selIndex + 1).srcPos.span.start) + deletion(editPos) + warnImport(impsel, actions) + end if + index = nextImport + end while + + // begin + for (sym, pos) <- infos.defs.iterator if !sym.hasAnnotation(defn.UnusedAnnot) do + if infos.refs(sym) then + checkUnassigned(sym, pos) + else if sym.isEffectivelyPrivate then + checkPrivate(sym, pos) + else if sym.is(Param, butNot = Given | Implicit) then + checkParam(sym, pos) + else if sym.is(Param) then // Given | Implicit + checkImplicit(sym, pos) + else if sym.isLocalToBlock then + checkLocal(sym, pos) + + if ctx.settings.WunusedHas.patvars then + checkPatvars() + + if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then + checkImports() + + def sortOrder(msgInfo: MessageInfo): Int = + val srcPos = msgInfo._2 + if srcPos.span.exists then srcPos.span.point else 0 + + warnings.result().sortBy(sortOrder) + end warnings + + // Specific exclusions + def ignoreTree(tree: Tree): Boolean = + tree.hasAttachment(ForArtifact) || tree.hasAttachment(Ignore) + + // The RHS of a def is too trivial to warn about unused params, e.g. def f(x: Int) = ??? + def isUnconsuming(rhs: Tree)(using Context): Boolean = + rhs.symbol == defn.Predef_undefined + || rhs.tpe =:= defn.NothingType // compiletime.error + || rhs.isInstanceOf[Literal] // 42 + || rhs.tpe.match + case ConstantType(_) => true + case tp: TermRef => tp.underlying.classSymbol.is(Module) // Scala 2 SingleType + case _ => false + //|| isPurePath(rhs) // a bit strong + || rhs.match + case Block((dd @ DefDef(anonfun, paramss, _, _)) :: Nil, Closure(Nil, Ident(nm), _)) => + anonfun == nm // isAnonymousFunctionName(anonfun) + && paramss.match + case (ValDef(contextual, _, _) :: Nil) :: Nil => + contextual.is(ContextFunctionParamName) + && isUnconsuming(dd.rhs) // rhs was wrapped in a context function + case _ => false + case Block(Nil, Literal(u)) => u.tpe =:= defn.UnitType // def f(x: X) = {} + case This(_) => true + case Ident(_) => rhs.symbol.is(ParamAccessor) + case Typed(rhs, _) => isUnconsuming(rhs) + case _ => false + + def allowVariableBindings(ok: List[Name], args: List[Tree]): Unit = + ok.zip(args).foreach: + case (param, arg @ Bind(p, _)) if param == p => arg.withAttachment(NoWarn, ()) + case _ => + + // NoWarn Binds if the name matches a "canonical" name, e.g. case element name + val nowarner = new TreeTraverser: + def traverse(tree: Tree)(using Context) = tree match + case UnApply(fun, _, args) => + val unapplied = tree.tpe.finalResultType.dealias.typeSymbol + if unapplied.is(CaseClass) then + allowVariableBindings(unapplied.primaryConstructor.info.firstParamNames, args) + else if fun.symbol == defn.PairClass_unapply then + val ok = fun.symbol.info match + case PolyType(tycon, MethodTpe(_, _, AppliedType(_, tprefs))) => + tprefs.collect: + case ref: TypeParamRef => termName(ref.binder.paramNames(ref.paramNum).toString.toLowerCase.nn) + case _ => Nil + allowVariableBindings(ok, args) + else if fun.symbol == defn.TypeTest_unapply then + () // just recurse into args else - // Wildcard - if selData.excludedMembers.contains(altName.getOrElse(sym.name).toTermName) then - // Wildcard with exclusions that match the symbol - false - else if !selData.qualTpe.member(sym.name).hasAltWith(_.symbol == sym) then - // The qualifier does not have the target symbol as a member - false + if unapplied.exists && unapplied.owner == defn.Quotes_reflectModule then + // cheapy search for parameter names via java reflection of Trees + // in lieu of drilling into requiredClass("scala.quoted.runtime.impl.QuotesImpl") + // ...member("reflect")...member(unapplied.name.toTypeName) + // with aliases into requiredModule("dotty.tools.dotc.ast.tpd") + val implName = s"dotty.tools.dotc.ast.Trees$$${unapplied.name}" + try + import scala.language.unsafeNulls + val clz = Class.forName(implName) // TODO improve to use class path or reflect + val ok = clz.getConstructors.head.getParameters.map(p => termName(p.getName)).toList.init + allowVariableBindings(ok, args) + catch case _: ClassNotFoundException => () + args.foreach(traverse) + case tree => traverseChildren(tree) + + // NoWarn members in tree that correspond to refinements; currently uses only names. + def relax(tree: Tree, tpe: Type)(using Context): Unit = + def refinements(tpe: Type, names: List[Name]): List[Name] = + tpe match + case RefinedType(parent, refinedName, refinedInfo) => refinedName :: refinements(parent, names) + case _ => names + val refinedNames = refinements(tpe, Nil) + if !refinedNames.isEmpty then + val names = refinedNames.toSet + val relaxer = new TreeTraverser: + def traverse(tree: Tree)(using Context) = + tree match + case tree: NamedDefTree if names(tree.name) => tree.withAttachment(NoWarn, ()) + case _ => + traverseChildren(tree) + relaxer.traverse(tree) + + def ignoreArgsOfSelfConstruction(tree: Apply, ctor: Symbol)(using Context): Unit = + val pars = ctor.denot.paramSymss.flatten.iterator.filter(_.isTerm) + val args = allTermArguments(tree) + for (par, arg) <- pars.zip(args) do + if arg.symbol.is(ParamAccessor) && arg.symbol.name == par.name && arg.symbol.owner == ctor.owner then + arg.putAttachment(Ignore, ()) + + extension (nm: Name) + inline def exists(p: Name => Boolean): Boolean = nm.ne(nme.NO_NAME) && p(nm) + inline def isWildcard: Boolean = nm == nme.WILDCARD || nm.is(WildcardParamName) + + extension (tp: Type)(using Context) + def importPrefix: Type = tp match + case tp: NamedType => tp.prefix + case tp: ClassInfo => tp.prefix + case tp: TypeProxy => tp.superType.normalizedPrefix + case _ => NoType + def underlyingPrefix: Type = tp match + case tp: NamedType => tp.prefix + case tp: ClassInfo => tp.prefix + case tp: TypeProxy => tp.underlying.underlyingPrefix + case _ => NoType + def skipPackageObject: Type = + if tp.typeSymbol.isPackageObject then tp.underlyingPrefix else tp + def underlying: Type = tp match + case tp: TypeProxy => tp.underlying + case _ => tp + + private val serializationNames: Set[TermName] = + Set("readResolve", "readObject", "readObjectNoData", "writeObject", "writeReplace").map(termName(_)) + + extension (sym: Symbol)(using Context) + def isSerializationSupport: Boolean = + sym.is(Method) && serializationNames(sym.name.toTermName) && sym.owner.isClass + && sym.owner.derivesFrom(defn.JavaSerializableClass) + def isMarkerTrait: Boolean = + sym.info.hiBound.resultType.allMembers.forall: d => + val m = d.symbol + !m.isTerm || m.isSelfSym || m.is(Method) && (m.owner == defn.AnyClass || m.owner == defn.ObjectClass) + def isEffectivelyPrivate: Boolean = + sym.is(Private, butNot = ParamAccessor) + || sym.owner.isAnonymousClass && !sym.isEffectivelyOverride + def isEffectivelyOverride: Boolean = + sym.is(Override) + || + sym.canMatchInheritedSymbols && { // inline allOverriddenSymbols using owner.info or thisType + val owner = sym.owner.asClass + val base = if owner.classInfo.selfInfo != NoType then owner.thisType else owner.info + base.baseClasses.drop(1).iterator.exists: bc => + if bc == defn.PolyFunctionClass then + sym.name == nme.apply else - if selector.isGiven then - // Further check that the symbol is a given or implicit and conforms to the bound - sym.isOneOf(Given | Implicit) - && (selector.bound.isEmpty || sym.info.finalResultType <:< selector.boundTpe) - else - // Normal wildcard, check that the symbol is not a given (but can be implicit) - !sym.is(Given) - end if - end isInImport - - /** Annotated with @unused */ - private def isUnusedAnnot(using Context): Boolean = - sym.annotations.exists(a => a.symbol == ctx.definitions.UnusedAnnot) - - private def shouldNotReportParamOwner(using Context): Boolean = - if sym.exists then - val owner = sym.owner - trivialDefs(owner) || // is a trivial def - owner.isPrimaryConstructor || - owner.annotations.exists ( // @depreacated - _.symbol == ctx.definitions.DeprecatedAnnot - ) || - owner.isAllOf(Synthetic | PrivateLocal) || - owner.is(Accessor) || - owner.isOverriden - else - false - - private def usedDefContains(using Context): Boolean = - sym.everySymbol.exists(usedDef.apply) - - private def everySymbol(using Context): List[Symbol] = - List(sym, sym.companionClass, sym.companionModule, sym.moduleClass).filter(_.exists) - - /** A function is overriden. Either has `override flags` or parent has a matching member (type and name) */ - private def isOverriden(using Context): Boolean = - sym.is(Flags.Override) || (sym.exists && sym.owner.thisType.parents.exists(p => sym.matchingMember(p).exists)) - - end extension - - extension (defdef: tpd.DefDef) - // so trivial that it never consumes params - private def isTrivial(using Context): Boolean = - val rhs = defdef.rhs - rhs.symbol == ctx.definitions.Predef_undefined || - rhs.tpe =:= ctx.definitions.NothingType || - defdef.symbol.is(Deferred) || - (rhs match { - case _: tpd.Literal => true - case _ => rhs.tpe match - case ConstantType(_) => true - case tp: TermRef => - // Detect Scala 2 SingleType - tp.underlying.classSymbol.is(Flags.Module) - case _ => - false - }) - def registerTrivial(using Context): Unit = - if defdef.isTrivial then - trivialDefs += defdef.symbol - - extension (memDef: tpd.MemberDef) - private def isValidMemberDef(using Context): Boolean = - memDef.symbol.exists - && !memDef.symbol.isUnusedAnnot - && !memDef.symbol.isAllOf(Flags.AccessorCreationFlags) - && !memDef.name.isWildcard - && !memDef.symbol.owner.is(ExtensionMethod) - - private def isValidParam(using Context): Boolean = - val sym = memDef.symbol - (sym.is(Param) || sym.isAllOf(PrivateParamAccessor | Local, butNot = CaseAccessor)) && - !isSyntheticMainParam(sym) && - !sym.shouldNotReportParamOwner - - private def shouldReportPrivateDef(using Context): Boolean = - currScopeType.top == ScopeType.Template && !memDef.symbol.isConstructor && memDef.symbol.is(Private, butNot = SelfName | Synthetic | CaseAccessor) - - private def isUnsetVarDef(using Context): Boolean = - val sym = memDef.symbol - sym.is(Mutable) && !setVars(sym) - - extension (imp: tpd.Import) - /** Enum generate an import for its cases (but outside them), which should be ignored */ - def isGeneratedByEnum(using Context): Boolean = - imp.symbol.exists && imp.symbol.owner.is(Flags.Enum, butNot = Flags.Case) - - extension (thisName: Name) - private def isWildcard: Boolean = - thisName == StdNames.nme.WILDCARD || thisName.is(WildcardParamName) - - end UnusedData - - private object UnusedData: - enum ScopeType: - case Local - case Template - case ReplWrapper - case Other - - object ScopeType: - /** return the scope corresponding to the enclosing scope of the given tree */ - def fromTree(tree: tpd.Tree)(using Context): ScopeType = tree match - case tree: tpd.Template => if tree.symbol.name.isReplWrapperName then ReplWrapper else Template - case _:tpd.Block => Local - case _ => Other - - final class ImportSelectorData(val qualTpe: Type, val selector: ImportSelector): - private var myUsed: Boolean = false - var excludedMembers: Set[TermName] = Set.empty - - def markUsed(): Unit = myUsed = true - - def isUsed: Boolean = myUsed - - def markExcluded(excluded: Set[TermName]): Unit = excludedMembers ++= excluded - - private var myAllSymbols: Set[Symbol] | Null = null - - def allSymbolsForNamed(using Context): Set[Symbol] = - if myAllSymbols == null then - val allDenots = qualTpe.member(selector.name).alternatives ::: qualTpe.member(selector.name.toTypeName).alternatives - myAllSymbols = allDenots.map(_.symbol).toSet - myAllSymbols.uncheckedNN - - private var myAllSymbolsDealiased: Set[Symbol] | Null = null - - def allSymbolsDealiasedForNamed(using Context): Set[Symbol] = - if myAllSymbolsDealiased == null then - myAllSymbolsDealiased = allSymbolsForNamed.map(sym => dealias(sym)) - myAllSymbolsDealiased.uncheckedNN - end ImportSelectorData - - case class UnusedSymbol(pos: SrcPos, name: Name, warnType: WarnTypes) - /** A container for the results of the used elements analysis */ - case class UnusedResult(warnings: Set[UnusedSymbol]) - object UnusedResult: - val Empty = UnusedResult(Set.empty) - end UnusedData - - private def dealias(symbol: Symbol)(using Context): Symbol = - if symbol.isType && symbol.asType.denot.isAliasType then - symbol.asType.typeRef.dealias.typeSymbol - else - symbol - + sym.overriddenSymbol(inClass = bc, siteClass = owner).exists + } + // pick the symbol the user wrote for purposes of tracking + inline def userSymbol: Symbol= + if sym.denot.is(ModuleClass) then sym.denot.companionModule else sym + // reconstitute a range pos from the symbol's pos, which is its start + def namePos: SrcPos = + sym.srcPos.sourcePos.withSpan: + val span = sym.span + Span(span.start, span.start + sym.name.toString.length) + + extension (sel: ImportSelector) + def boundTpe: Type = sel.bound match + case untpd.TypedSplice(tree) => tree.tpe + case _ => NoType + + extension (imp: Import)(using Context) + /** Is it the first import clause in a statement? `a.x` in `import a.x, b.{y, z}` */ + def isPrimaryClause: Boolean = + imp.srcPos.span.pointDelta > 0 // primary clause starts at `import` keyword with point at clause proper + + /** Generated import of cases from enum companion. */ + def isGeneratedByEnum: Boolean = + imp.symbol.exists && imp.symbol.owner.is(Enum, butNot = Case) + + /** 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 + def indexSatisfying(from: Int, until: Int = arr.length)(p: A => Boolean): Int = + var i = from + while i < until && !p(arr(i)) do + i += 1 + i end CheckUnused diff --git a/compiler/src/dotty/tools/dotc/transform/CollectNullableFields.scala b/compiler/src/dotty/tools/dotc/transform/CollectNullableFields.scala index 9433f7949163..8c3355d5f56b 100644 --- a/compiler/src/dotty/tools/dotc/transform/CollectNullableFields.scala +++ b/compiler/src/dotty/tools/dotc/transform/CollectNullableFields.scala @@ -60,7 +60,7 @@ class CollectNullableFields extends MiniPhase { val sym = tree.symbol val isNullablePrivateField = sym.isField && - !sym.is(Lazy) && + !sym.isOneOf(Lazy | Erased) && !sym.owner.is(Trait) && sym.initial.isAllOf(PrivateLocal) && // We need `isNullableClassAfterErasure` and not `isNullable` because diff --git a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala index 5740f359cb77..44759ec9a412 100644 --- a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala +++ b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala @@ -106,7 +106,9 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => val moduleRef = ref(clazz.companionModule) val enums = moduleCls.info.decls.filter(member => member.isAllOf(EnumValue)) - for { enumValue <- enums } + val forwarderSyms = scala.collection.mutable.ListBuffer[Symbol]() + + val result = for { enumValue <- enums } yield { def forwarderSym(flags: FlagSet, info: Type): Symbol { type ThisName = TermName } = val sym = newSymbol(clazz, enumValue.name.asTermName, flags, info) @@ -119,8 +121,36 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => // we achieve the right contract with static forwarders instead. DefDef(forwarderSym(EnumValue | Method | JavaStatic, MethodType(Nil, enumValue.info)), body) else - ValDef(forwarderSym(EnumValue | JavaStatic, enumValue.info), body) + val sym = forwarderSym(EnumValue | JavaStatic | Mutable, enumValue.info) + forwarderSyms += sym + ValDef(sym, body) } + + // Store forwarder symbols for later use in companion initialization + if forwarderSyms.nonEmpty then + enumForwarders(clazz) = forwarderSyms.toList + + result + } + + /** Generate assignment to initialize enum forwarders in the companion object, + * so that forwarders are initialized when comapnion object is touched first. + * For each enum value, generates: EnumClass.enumValue = Module.enumValue + * see: https://github.com/scala/scala3/issues/12637 + */ + private def enumForwarderInitializers(moduleCls: Symbol)(using Context): List[Tree] = { + if ctx.settings.scalajs.value then + Nil // Scala.js uses methods, no initialization needed + else + val enumClass = moduleCls.linkedClass + val forwarderSyms = enumForwarders.get(enumClass).getOrElse(Nil) + val enums = moduleCls.info.decls.filter(member => member.isAllOf(EnumValue)).toList + + forwarderSyms.zip(enums).map { case (forwarderSym, enumValue) => + val lhs = ref(forwarderSym) + val rhs = ref(enumValue) + Assign(lhs, rhs) + } } private def isJavaEnumValueImpl(cls: Symbol)(using Context): Boolean = @@ -129,6 +159,7 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => && cls.owner.owner.linkedClass.derivesFromJavaEnum private val enumCaseOrdinals = MutableSymbolMap[Int]() + private val enumForwarders = MutableSymbolMap[List[Symbol]]() private def registerEnumClass(cls: Symbol)(using Context): Unit = cls.children.zipWithIndex.foreach(enumCaseOrdinals.update) @@ -181,10 +212,17 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => ) else if cls.linkedClass.derivesFromJavaEnum then enumCaseOrdinals.clear() // remove simple cases // invariant: companion is visited after cases - templ + // Add initialization code for enum forwarders + val initializers = enumForwarderInitializers(cls) + enumForwarders.remove(cls.linkedClass) // Clear cache after use + if initializers.isEmpty then + templ + else + cpy.Template(templ)(body = templ.body ++ initializers) else templ } override def checkPostCondition(tree: Tree)(using Context): Unit = assert(enumCaseOrdinals.isEmpty, "Java based enum ordinal cache was not cleared") + assert(enumForwarders.isEmpty, "Java based enum forwarder cache was not cleared") } diff --git a/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala b/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala index 0bb54f8c9345..463bb68718ef 100644 --- a/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala +++ b/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala @@ -104,7 +104,7 @@ object ContextFunctionResults: def contextFunctionResultTypeAfter(meth: Symbol, depth: Int)(using Context) = def recur(tp: Type, n: Int): Type = if n == 0 then tp - else tp match + else tp.dealias match case defn.ContextFunctionType(_, resTpe) => recur(resTpe, n - 1) recur(meth.info.finalResultType, depth) diff --git a/compiler/src/dotty/tools/dotc/transform/Dependencies.scala b/compiler/src/dotty/tools/dotc/transform/Dependencies.scala index 733f4601e363..98373ac9c6ad 100644 --- a/compiler/src/dotty/tools/dotc/transform/Dependencies.scala +++ b/compiler/src/dotty/tools/dotc/transform/Dependencies.scala @@ -29,7 +29,7 @@ abstract class Dependencies(root: ast.tpd.Tree, @constructorOnly rootContext: Co def tracked: Iterable[Symbol] = free.keys /** The outermost class that captures all free variables of a function - * that are captured by enclosinh classes (this means that the function could + * that are captured by enclosing classes (this means that the function could * be placed in that class without having to add more environment parameters) */ def logicalOwner: collection.Map[Symbol, Symbol] = logicOwner @@ -44,9 +44,9 @@ abstract class Dependencies(root: ast.tpd.Tree, @constructorOnly rootContext: Co /** A map from local methods and classes to the owners to which they will be lifted as members. * For methods and classes that do not have any dependencies this will be the enclosing package. - * symbols with packages as lifted owners will subsequently represented as static + * Symbols with packages as lifted owners will be subsequently represented as static * members of their toplevel class, unless their enclosing class was already static. - * Note: During tree transform (which runs at phase LambdaLift + 1), liftedOwner + * Note: During tree transform (which runs at phase LambdaLift + 1), logicOwner * is also used to decide whether a method had a term owner before. */ private val logicOwner = new LinkedHashMap[Symbol, Symbol] @@ -74,8 +74,8 @@ abstract class Dependencies(root: ast.tpd.Tree, @constructorOnly rootContext: Co || owner.is(Trait) && isLocal(owner) || sym.isConstructor && isLocal(owner) - /** Set `liftedOwner(sym)` to `owner` if `owner` is more deeply nested - * than the previous value of `liftedowner(sym)`. + /** Set `logicOwner(sym)` to `owner` if `owner` is more deeply nested + * than the previous value of `logicOwner(sym)`. */ private def narrowLogicOwner(sym: Symbol, owner: Symbol)(using Context): Unit = if sym.maybeOwner.isTerm @@ -88,7 +88,7 @@ abstract class Dependencies(root: ast.tpd.Tree, @constructorOnly rootContext: Co /** Mark symbol `sym` as being free in `enclosure`, unless `sym` is defined * in `enclosure` or there is an intermediate class properly containing `enclosure` - * in which `sym` is also free. Also, update `liftedOwner` of `enclosure` so + * in which `sym` is also free. Also, update `logicOwner` of `enclosure` so * that `enclosure` can access `sym`, or its proxy in an intermediate class. * This means: * @@ -136,6 +136,7 @@ abstract class Dependencies(root: ast.tpd.Tree, @constructorOnly rootContext: Co if !enclosure.exists then throw NoPath() if enclosure == sym.enclosure then NoSymbol else + /** is sym a constructor or a term that is nested in a constructor? */ def nestedInConstructor(sym: Symbol): Boolean = sym.isConstructor || sym.isTerm && nestedInConstructor(sym.enclosure) @@ -236,6 +237,10 @@ abstract class Dependencies(root: ast.tpd.Tree, @constructorOnly rootContext: Co captureImplicitThis(tree.tpe) case tree: Select => if isExpr(sym) && isLocal(sym) then markCalled(sym, enclosure) + case tree: New => + val constr = tree.tpe.typeSymbol.primaryConstructor + if constr.exists then + symSet(called, enclosure) += constr case tree: This => narrowTo(tree.symbol.asClass) case tree: MemberDef if isExpr(sym) && sym.owner.isTerm => @@ -278,7 +283,7 @@ abstract class Dependencies(root: ast.tpd.Tree, @constructorOnly rootContext: Co changedFreeVars do () - /** Compute final liftedOwner map by closing over caller dependencies */ + /** Compute final logicOwner map by closing over caller dependencies */ private def computeLogicOwners()(using Context): Unit = while changedLogicOwner = false @@ -290,7 +295,6 @@ abstract class Dependencies(root: ast.tpd.Tree, @constructorOnly rootContext: Co val calleeOwner = normalizedCallee.owner if calleeOwner.isTerm then narrowLogicOwner(caller, logicOwner(normalizedCallee)) else - assert(calleeOwner.is(Trait)) // methods nested inside local trait methods cannot be lifted out // beyond the trait. Note that we can also call a trait method through // a qualifier; in that case no restriction to lifted owner arises. diff --git a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala index b98d7d525089..9adfff7bf35f 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -13,6 +13,7 @@ import Decorators.* import Denotations.*, SymDenotations.* import DenotTransformers.* import NullOpsDecorator.* +import util.Spans.Span object ElimRepeated { val name: String = "elimRepeated" @@ -183,7 +184,7 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase => .get .symbol.asTerm // Generate the method - val forwarderDef = DefDef(forwarderSym, prefss => { + val forwarderDef = DefDef(forwarderSym, prefss => val init :+ (last :+ vararg) = prefss: @unchecked // Can't call `.argTypes` here because the underlying array type is of the // form `Array[? <: SomeType]`, so we need `.argInfos` to get the `TypeBounds`. @@ -191,7 +192,8 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase => ref(sym.termRef) .appliedToArgss(init) .appliedToTermArgs(last :+ wrapArray(vararg, elemtp)) - }) + .withSpan(Span(tree.span.start)) + ).withSpan(tree.span.toSynthetic) Thicket(tree, forwarderDef) else tree diff --git a/compiler/src/dotty/tools/dotc/transform/EmptyPhase.scala b/compiler/src/dotty/tools/dotc/transform/EmptyPhase.scala index 9a287b2dd1d9..706f4cda548a 100644 --- a/compiler/src/dotty/tools/dotc/transform/EmptyPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/EmptyPhase.scala @@ -14,6 +14,6 @@ class EmptyPhase extends Phase: override def isEnabled(using Context) = prev.isEnabled - override def run(using Context) = () + protected def run(using Context) = () end EmptyPhase \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index b8155551c979..8e3d4f1c1085 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -19,6 +19,7 @@ import core.Decorators.* import core.Constants.* import core.Definitions.* import core.Annotations.BodyAnnotation +import core.MissingType import typer.NoChecking import inlines.Inlines import typer.ProtoTypes.* @@ -49,7 +50,7 @@ class Erasure extends Phase with DenotTransformer { override def changesMembers: Boolean = true // the phase adds bridges override def changesParents: Boolean = true // the phase drops Any - def transform(ref: SingleDenotation)(using Context): SingleDenotation = ref match { + def transform(ref: SingleDenotation)(using Context): SingleDenotation = try ref match { case ref: SymDenotation => def isCompacted(symd: SymDenotation) = symd.isAnonymousFunction && { @@ -136,11 +137,14 @@ class Erasure extends Phase with DenotTransformer { ref.symbol, transformInfo(ref.symbol, ref.symbol.info), currentStablePeriod, ref.prefix) case _ => ref.derivedSingleDenotation(ref.symbol, transformInfo(ref.symbol, ref.symbol.info)) - } + } catch case ex: MissingType => + // Handle missing types from dependencies (e.g., JDK version mismatch) + report.error(ex.toMessage, ref.symbol.srcPos) + ref // Keep old denotation on error private val eraser = new Erasure.Typer(this) - def run(using Context): Unit = { + protected def run(using Context): Unit = { val unit = ctx.compilationUnit unit.tpdTree = eraser.typedExpr(unit.tpdTree)(using ctx.fresh.setTyper(eraser).setPhase(this.next)) } @@ -458,39 +462,9 @@ object Erasure { val samParamTypes = sam.paramInfos val samResultType = sam.resultType - /** Can the implementation parameter type `tp` be auto-adapted to a different - * parameter type in the SAM? - * - * For derived value classes, we always need to do the bridging manually. - * For primitives, we cannot rely on auto-adaptation on the JVM because - * the Scala spec requires null to be "unboxed" to the default value of - * the value class, but the adaptation performed by LambdaMetaFactory - * will throw a `NullPointerException` instead. See `lambda-null.scala` - * for test cases. - * - * @see [LambdaMetaFactory](https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html) - */ - def autoAdaptedParam(tp: Type) = - !tp.isErasedValueType && !tp.isPrimitiveValueType - - /** Can the implementation result type be auto-adapted to a different result - * type in the SAM? - * - * For derived value classes, it's the same story as for parameters. - * For non-Unit primitives, we can actually rely on the `LambdaMetaFactory` - * adaptation, because it only needs to box, not unbox, so no special - * handling of null is required. - */ - def autoAdaptedResult = - !implResultType.isErasedValueType && !implReturnsUnit - - def sameClass(tp1: Type, tp2: Type) = tp1.classSymbol == tp2.classSymbol - - val paramAdaptationNeeded = - implParamTypes.lazyZip(samParamTypes).exists((implType, samType) => - !sameClass(implType, samType) && !autoAdaptedParam(implType)) - val resultAdaptationNeeded = - !sameClass(implResultType, samResultType) && !autoAdaptedResult + // Check if bridging is needed using the common function from TypeErasure + val (paramAdaptationNeeded, resultAdaptationNeeded) = + additionalAdaptationNeeded(implParamTypes, implResultType, samParamTypes, samResultType) if paramAdaptationNeeded || resultAdaptationNeeded then // Instead of instantiating `scala.FunctionN`, see if we can instantiate @@ -563,9 +537,9 @@ object Erasure { report.error(msg, tree.srcPos) tree.symbol.getAnnotation(defn.CompileTimeOnlyAnnot) match case Some(annot) => - val message = annot.argumentConstant(0) match - case Some(c) => - c.stringValue.toMessage + val message = annot.argumentConstantString(0) match + case Some(msg) => + msg.toMessage case _ => em"""Reference to ${tree.symbol.showLocated} should not have survived, |it should have been processed and eliminated during expansion of an enclosing macro or term erasure.""" @@ -730,13 +704,10 @@ object Erasure { assignType(untpd.cpy.Select(tree)(qual, tree.name.primitiveArrayOp), qual) def adaptIfSuper(qual: Tree): Tree = qual match { - case Super(thisQual, untpd.EmptyTypeIdent) => - val SuperType(thisType, supType) = qual.tpe: @unchecked - if (sym.owner.is(Flags.Trait)) - cpy.Super(qual)(thisQual, untpd.Ident(sym.owner.asClass.name)) - .withType(SuperType(thisType, sym.owner.typeRef)) - else - qual.withType(SuperType(thisType, thisType.firstParent.typeConstructor)) + case Super(thisQual, untpd.EmptyTypeIdent) if sym.owner.is(Flags.Trait) => + val SuperType(thisType, _) = qual.tpe: @unchecked + cpy.Super(qual)(thisQual, untpd.Ident(sym.owner.asClass.name)) + .withType(SuperType(thisType, sym.owner.typeRef)) case _ => qual } @@ -748,13 +719,8 @@ object Erasure { // Scala classes are always emitted as public, unless the // `private` modifier is used, but a non-private class can never // extend a private class, so such a class will never be a cast target. - !cls.is(Flags.JavaDefined) || { - // We can't rely on `isContainedWith` here because packages are - // not nested from the JVM point of view. - val boundary = cls.accessBoundary(cls.owner)(using preErasureCtx) - (boundary eq defn.RootClass) || - (ctx.owner.enclosingPackageClass eq boundary) - } + !cls.is(Flags.JavaDefined) || + cls.isAccessibleFrom(cls.owner.thisType)(using preErasureCtx) @tailrec def recur(qual: Tree): Tree = diff --git a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala index f62044eae7d2..4f0be72a4197 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala @@ -3,6 +3,7 @@ package dotc package transform import core.* +import core.TypeErasure.erasure import Scopes.newScope import Contexts.*, Symbols.*, Types.*, Flags.*, Decorators.*, StdNames.*, Constants.* import MegaPhase.* @@ -10,6 +11,7 @@ import Names.TypeName import Symbols.* import NullOpsDecorator.* import ast.untpd +import scala.collection.mutable.ListBuffer /** Expand SAM closures that cannot be represented by the JVM as lambdas to anonymous classes. * These fall into five categories @@ -17,7 +19,7 @@ import ast.untpd * 1. Partial function closures, we need to generate isDefinedAt and applyOrElse methods for these. * 2. Closures implementing non-trait classes * 3. Closures implementing classes that inherit from a class other than Object - * (a lambda cannot not be a run-time subtype of such a class) + * (a lambda cannot be a run-time subtype of such a class) * 4. Closures that implement traits which run initialization code. * 5. Closures that get synthesized abstract methods in the transformation pipeline. These methods can be * (1) superaccessors, (2) outer references, (3) accessors for fields. @@ -59,7 +61,7 @@ class ExpandSAMs extends MiniPhase: // A SAM type is allowed to have type aliases refinements (see // SAMType#samParent) which must be converted into type members if // the closure is desugared into a class. - val refinements = collection.mutable.ListBuffer[(TypeName, TypeAlias)]() + val refinements = ListBuffer.empty[(TypeName, TypeAlias)] def collectAndStripRefinements(tp: Type): Type = tp match case RefinedType(parent, name, info: TypeAlias) => val res = collectAndStripRefinements(parent) @@ -68,47 +70,89 @@ class ExpandSAMs extends MiniPhase: case _ => tp val tpe1 = collectAndStripRefinements(tpe) val Seq(samDenot) = tpe1.possibleSamMethods - cpy.Block(tree)(stats, - transformFollowingDeep: - AnonClass(List(tpe1), - List(samDenot.symbol.asTerm.name -> fn.symbol.asTerm), - refinements.toList, - adaptVarargs = true - ) - ) + val implSym = fn.symbol.asTerm + + // Check if SAM param erases to array but impl param doesn't (see #23179). + // E.g. SAM expects Array[AnyRef] (erases to Array[Object]) but impl has + // Array[? >: AnyRef] (erases to Object). In this case we need to create + // a wrapper with the SAM signature that casts arguments before forwarding. + val samInfo = samDenot.info + val implInfo = implSym.info + val needsArrayAdaptation = (samInfo, implInfo) match + case (samMt: MethodType, implMt: MethodType) => + samMt.paramInfos.lazyZip(implMt.paramInfos).exists { (samPt, implPt) => + erasure(samPt).isInstanceOf[JavaArrayType] && !erasure(implPt).isInstanceOf[JavaArrayType] + } + case _ => false + + if needsArrayAdaptation then + // Create a wrapper method with SAM's signature that casts args to impl's types + val wrapperSym = newSymbol( + implSym.owner, implSym.name.asTermName, Synthetic | Method, + samInfo, coord = implSym.span).entered.asTerm + val wrapperDef = DefDef(wrapperSym, paramss => { + val implMt = implInfo.stripPoly.asInstanceOf[MethodType] + val adaptedArgs = paramss.head.lazyZip(implMt.paramInfos).map { (arg, implPt) => + if arg.tpe =:= implPt then arg else arg.cast(implPt) + } + ref(implSym).appliedToTermArgs(adaptedArgs) + }) + cpy.Block(tree)(fn :: wrapperDef :: Nil, + transformFollowingDeep: + AnonClass(List(tpe1), + List(samDenot.symbol.asTerm.name -> wrapperSym), + refinements.toList, + adaptVarargs = true + ) + ) + else + cpy.Block(tree)(stats, + transformFollowingDeep: + AnonClass(List(tpe1), + List(samDenot.symbol.asTerm.name -> implSym), + refinements.toList, + adaptVarargs = true + ) + ) } case _ => tree } - /** A partial function literal: + /** A pattern-matching anonymous function: * * ``` * val x: PartialFunction[A, B] = { case C1 => E1; ...; case Cn => En } * ``` + * or + * ``` + * x => e(x) { case C1 => E1; ...; case Cn => En } + * ``` + * where the expression `e(x)` may be trivially `x` * * which desugars to: * * ``` * val x: PartialFunction[A, B] = { - * def $anonfun(x: A): B = x match { case C1 => E1; ...; case Cn => En } + * def $anonfun(x: A): B = e(x) match { case C1 => E1; ...; case Cn => En } * closure($anonfun: PartialFunction[A, B]) * } * ``` + * where the expression `e(x)` defaults to `x` for a simple block of cases * * is expanded to an anonymous class: * * ``` * val x: PartialFunction[A, B] = { * class $anon extends AbstractPartialFunction[A, B] { - * final def isDefinedAt(x: A): Boolean = x match { + * final def isDefinedAt(x: A): Boolean = e(x) match { * case C1 => true * ... * case Cn => true * case _ => false * } * - * final def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = x match { + * final def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = e(x) match { * case C1 => E1 * ... * case Cn => En @@ -120,16 +164,22 @@ class ExpandSAMs extends MiniPhase: * } * ``` */ - private def toPartialFunction(tree: Block, tpe: Type)(using Context): Tree = { + private def toPartialFunction(tree: Block, tpe: Type)(using Context): Tree = val closureDef(anon @ DefDef(_, List(List(param)), _, _)) = tree: @unchecked // The right hand side from which to construct the partial function. This is always a Match. // If the original rhs is already a Match (possibly in braces), return that. // Otherwise construct a match `x match case _ => rhs` where `x` is the parameter of the closure. - def partialFunRHS(tree: Tree): Match = tree match + def partialFunRHS(tree: Tree): Match = + inline def checkMatch(): Unit = + tree match + case Block(_, m: Match) => report.warning(reporting.MatchIsNotPartialFunction(), m.srcPos) + case _ => + tree match case m: Match => m case Block(Nil, expr) => partialFunRHS(expr) case _ => + checkMatch() Match(ref(param.symbol), CaseDef(untpd.Ident(nme.WILDCARD).withType(param.symbol.info), EmptyTree, tree) :: Nil) @@ -140,7 +190,7 @@ class ExpandSAMs extends MiniPhase: defn.AbstractPartialFunctionClass.typeRef.appliedTo(anonTpe.firstParamTypes.head, anonTpe.resultType), defn.SerializableType) - AnonClass(anonSym.owner, parents, tree.span) { pfSym => + AnonClass(anonSym.owner, parents, tree.span): pfSym => def overrideSym(sym: Symbol) = sym.copy( owner = pfSym, flags = Synthetic | Method | Final | Override, @@ -149,7 +199,8 @@ class ExpandSAMs extends MiniPhase: val isDefinedAtFn = overrideSym(defn.PartialFunction_isDefinedAt) val applyOrElseFn = overrideSym(defn.PartialFunction_applyOrElse) - def translateMatch(tree: Match, pfParam: Symbol, cases: List[CaseDef], defaultValue: Tree)(using Context) = { + def translateMatch(owner: Symbol)(pfParam: Symbol, cases: List[CaseDef], defaultValue: Tree)(using Context) = + val tree: Match = pfRHS val selector = tree.selector val cases1 = if cases.exists(isDefaultCase) then cases else @@ -159,31 +210,27 @@ class ExpandSAMs extends MiniPhase: cases :+ defaultCase cpy.Match(tree)(selector, cases1) .subst(param.symbol :: Nil, pfParam :: Nil) - // Needed because a partial function can be written as: + // Needed because a partial function can be written as: // param => param match { case "foo" if foo(param) => param } // And we need to update all references to 'param' - } + .changeOwner(anonSym, owner) - def isDefinedAtRhs(paramRefss: List[List[Tree]])(using Context) = { + def isDefinedAtRhs(paramRefss: List[List[Tree]])(using Context) = val tru = Literal(Constant(true)) - def translateCase(cdef: CaseDef) = - cpy.CaseDef(cdef)(body = tru).changeOwner(anonSym, isDefinedAtFn) + def translateCase(cdef: CaseDef) = cpy.CaseDef(cdef)(body = tru) val paramRef = paramRefss.head.head val defaultValue = Literal(Constant(false)) - translateMatch(pfRHS, paramRef.symbol, pfRHS.cases.map(translateCase), defaultValue) - } + translateMatch(isDefinedAtFn)(paramRef.symbol, pfRHS.cases.map(translateCase), defaultValue) - def applyOrElseRhs(paramRefss: List[List[Tree]])(using Context) = { + def applyOrElseRhs(paramRefss: List[List[Tree]])(using Context) = val List(paramRef, defaultRef) = paramRefss(1) - def translateCase(cdef: CaseDef) = - cdef.changeOwner(anonSym, applyOrElseFn) val defaultValue = defaultRef.select(nme.apply).appliedTo(paramRef) - translateMatch(pfRHS, paramRef.symbol, pfRHS.cases.map(translateCase), defaultValue) - } + translateMatch(applyOrElseFn)(paramRef.symbol, pfRHS.cases, defaultValue) - val isDefinedAtDef = transformFollowingDeep(DefDef(isDefinedAtFn, isDefinedAtRhs(_)(using ctx.withOwner(isDefinedAtFn)))) - val applyOrElseDef = transformFollowingDeep(DefDef(applyOrElseFn, applyOrElseRhs(_)(using ctx.withOwner(applyOrElseFn)))) + val isDefinedAtDef = transformFollowingDeep: + DefDef(isDefinedAtFn, isDefinedAtRhs(_)(using ctx.withOwner(isDefinedAtFn))) + val applyOrElseDef = transformFollowingDeep: + DefDef(applyOrElseFn, applyOrElseRhs(_)(using ctx.withOwner(applyOrElseFn))) List(isDefinedAtDef, applyOrElseDef) - } - } + end toPartialFunction end ExpandSAMs diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 0db1ddc5750c..eb142f843090 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -3,6 +3,7 @@ package dotc package transform import MegaPhase.* +import ast.tpd.* import core.DenotTransformers.* import core.Symbols.* import core.Contexts.* @@ -15,10 +16,8 @@ import core.Names.* import core.NameOps.* import core.NameKinds.SuperArgName -import dotty.tools.dotc.ast.tpd - -import collection.mutable import scala.annotation.tailrec +import scala.collection.mutable /** This phase adds outer accessors to classes and traits that need them. * Compared to Scala 2.x, it tries to minimize the set of classes @@ -36,7 +35,6 @@ import scala.annotation.tailrec */ class ExplicitOuter extends MiniPhase with InfoTransformer { thisPhase => import ExplicitOuter.* - import ast.tpd.* override def phaseName: String = ExplicitOuter.name @@ -64,7 +62,7 @@ class ExplicitOuter extends MiniPhase with InfoTransformer { thisPhase => * Furthermore, if a parent trait might have an outer accessor, * provide an implementation for the outer accessor by computing the parent's * outer from the parent type prefix. If the trait ends up not having an outer accessor - * after all, the implementation is redundant, but does not harm. + * after all, the implementation is redundant, but does no harm. * The same logic is not done for non-trait parent classes because for them the outer * pointer is passed in the super constructor, which will be implemented later in * a separate phase which needs to run after erasure. However, we make sure here @@ -111,7 +109,7 @@ class ExplicitOuter extends MiniPhase with InfoTransformer { thisPhase => else impl } - override def transformClosure(tree: Closure)(using Context): tpd.Tree = { + override def transformClosure(tree: Closure)(using Context): Tree = { if (tree.tpt ne EmptyTree) { val cls = tree.tpt.asInstanceOf[TypeTree].tpe.classSymbol if (cls.exists && hasOuter(cls.asClass)) @@ -122,7 +120,6 @@ class ExplicitOuter extends MiniPhase with InfoTransformer { thisPhase => } object ExplicitOuter { - import ast.tpd.* val name: String = "explicitOuter" val description: String = "add accessors to outer classes from nested ones" @@ -217,11 +214,12 @@ object ExplicitOuter { * - we need to potentially pass along outer to a parent class or trait */ private def needsOuterAlways(cls: ClassSymbol)(using Context): Boolean = - needsOuterIfReferenced(cls) && - (!hasOnlyLocalInstantiation(cls) || // needs outer because we might not know whether outer is referenced or not - cls.mixins.exists(needsOuterIfReferenced) || // needs outer for parent traits - cls.info.parents.exists(parent => // needs outer to potentially pass along to parent - needsOuterIfReferenced(parent.classSymbol.asClass))) + needsOuterIfReferenced(cls) + && (!hasOnlyLocalInstantiation(cls) // needs outer because we might not know whether outer is referenced or not + || cls.mixins.exists(needsOuterIfReferenced) // needs outer for parent traits + || cls.info.parents.exists: parent => // needs outer to potentially pass along to parent + needsOuterIfReferenced(parent.classSymbol.asClass) + ) /** Class is only instantiated in the compilation unit where it is defined */ private def hasOnlyLocalInstantiation(cls: ClassSymbol)(using Context): Boolean = @@ -301,10 +299,15 @@ object ExplicitOuter { case _ => false def containsOuterRefsAnywhere(tp: Type): Boolean = - tp.existsPart({ + def hasOuterRef(t: Type) = + { t match case t: SingletonType => isOuterRef(t) case _ => false - }, StopAt.Static) + } || { + val t1 = t.dealias + (t1 ne t) && containsOuterRefsAnywhere(t1) + } + tp.existsPart(hasOuterRef, StopAt.Static) def containsOuterRefs(t: Tree): Boolean = t match case _: This | _: Ident => isOuterRef(t.tpe) @@ -322,7 +325,7 @@ object ExplicitOuter { containsOuterRefsAtTopLevel(app.args.head.tpe.dealias) case t: TypeTree if inInline => // Expansions of inline methods must be able to address outer types - containsOuterRefsAnywhere(t.tpe.dealias) + containsOuterRefsAnywhere(t.tpe) case _ => false diff --git a/compiler/src/dotty/tools/dotc/transform/ForwardDepChecks.scala b/compiler/src/dotty/tools/dotc/transform/ForwardDepChecks.scala index bf8a6fa6c7bf..601e3c86cce2 100644 --- a/compiler/src/dotty/tools/dotc/transform/ForwardDepChecks.scala +++ b/compiler/src/dotty/tools/dotc/transform/ForwardDepChecks.scala @@ -120,7 +120,8 @@ class ForwardDepChecks extends MiniPhase: override def transformApply(tree: Apply)(using Context): Apply = if (isSelfConstrCall(tree)) assert(ctx.owner.isConstructor) - checkSelfConstructorCall() + if currentLevel.isInstanceOf[LevelInfo] then // ElimRepeated can introduce RHS of ctor which is not a block + checkSelfConstructorCall() tree override def transformNew(tree: New)(using Context): New = { diff --git a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala index e60561fb04e2..585188987f39 100644 --- a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala +++ b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala @@ -233,6 +233,7 @@ object GenericSignatures { @noinline def jsig(tp0: Type, toplevel: Boolean = false, unboxedVCs: Boolean = true): Unit = { + inline def jsig1(tp0: Type): Unit = jsig(tp0, toplevel = false, unboxedVCs = true) val tp = tp0.dealias tp match { @@ -241,41 +242,51 @@ object GenericSignatures { val erasedUnderlying = fullErasure(ref.underlying.bounds.hi) // don't emit type param name if the param is upper-bounded by a primitive type (including via a value class) if erasedUnderlying.isPrimitiveValueType then - jsig(erasedUnderlying, toplevel, unboxedVCs) + jsig(erasedUnderlying, toplevel = toplevel, unboxedVCs = unboxedVCs) else typeParamSig(ref.paramName.lastPart) + case ref: TermRef if ref.symbol.isGetter => + // If the type of a val is a TermRef to another val, generating the generic signature + // based on the underlying type will produce the type `scala.Function0` + // The reason behind this is that during the `getters` phase, the same symbol will now + // refer to the getter where the type will be now `=> `. + // Since the TermRef originally intended to capture the underlying type of a `val`, + // we recover that information by directly checking the resultType of the getter. + // See `tests/run/i24553.scala` for an example + jsig(ref.info.resultType, toplevel = toplevel, unboxedVCs = unboxedVCs) + case defn.ArrayOf(elemtp) => if (isGenericArrayElement(elemtp, isScala2 = false)) - jsig(defn.ObjectType) + jsig1(defn.ObjectType) else builder.append(ClassfileConstants.ARRAY_TAG) elemtp match - case TypeBounds(lo, hi) => jsig(hi.widenDealias) - case _ => jsig(elemtp) + case TypeBounds(lo, hi) => jsig1(hi.widenDealias) + case _ => jsig1(elemtp) case RefOrAppliedType(sym, pre, args) => if (sym == defn.PairClass && tupleArity(tp) > Definitions.MaxTupleArity) - jsig(defn.TupleXXLClass.typeRef) + jsig1(defn.TupleXXLClass.typeRef) else if (isTypeParameterInSig(sym, sym0)) { assert(!sym.isAliasType, "Unexpected alias type: " + sym) typeParamSig(sym.name.lastPart) } else if (defn.specialErasure.contains(sym)) - jsig(defn.specialErasure(sym).nn.typeRef) + jsig1(defn.specialErasure(sym).nn.typeRef) else if (sym == defn.UnitClass || sym == defn.BoxedUnitModule) - jsig(defn.BoxedUnitClass.typeRef) + jsig1(defn.BoxedUnitClass.typeRef) else if (sym == defn.NothingClass) builder.append("Lscala/runtime/Nothing$;") else if (sym == defn.NullClass) builder.append("Lscala/runtime/Null$;") else if (sym.isPrimitiveValueClass) - if (!unboxedVCs) jsig(defn.ObjectType) - else if (sym == defn.UnitClass) jsig(defn.BoxedUnitClass.typeRef) + if (!unboxedVCs) jsig1(defn.ObjectType) + else if (sym == defn.UnitClass) jsig1(defn.BoxedUnitClass.typeRef) else builder.append(defn.typeTag(sym.info)) else if (sym.isDerivedValueClass) { if (unboxedVCs) { val erasedUnderlying = fullErasure(tp) - jsig(erasedUnderlying, toplevel) + jsig(erasedUnderlying, toplevel = toplevel, unboxedVCs = true) } else classSig(sym, pre, args) } else if (defn.isSyntheticFunctionClass(sym)) { @@ -285,14 +296,14 @@ object GenericSignatures { else if sym.isClass then classSig(sym, pre, args) else - jsig(erasure(tp), toplevel, unboxedVCs) + jsig(erasure(tp), toplevel = toplevel, unboxedVCs = unboxedVCs) case ExprType(restpe) if toplevel => builder.append("()") methodResultSig(restpe) case ExprType(restpe) => - jsig(defn.FunctionType(0).appliedTo(restpe)) + jsig1(defn.FunctionType(0).appliedTo(restpe)) case PolyType(tparams, mtpe: MethodType) => assert(tparams.nonEmpty) @@ -320,7 +331,7 @@ object GenericSignatures { builder.append('(') // TODO: Update once we support varargs params.foreach { tp => - jsig(tp) + jsig1(tp) } builder.append(')') methodResultSig(restpe) @@ -338,7 +349,7 @@ object GenericSignatures { val (reprParents, _) = splitIntersection(parents) val repr = reprParents.find(_.typeSymbol.is(TypeParam)).getOrElse(reprParents.head) - jsig(repr, unboxedVCs = unboxedVCs) + jsig(repr, toplevel = false, unboxedVCs = unboxedVCs) case ci: ClassInfo => val tParams = tp.typeParams diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 72c763ef155a..148b132002ef 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -29,7 +29,7 @@ class Inlining extends MacroTransform { override def changesMembers: Boolean = true - override def run(using Context): Unit = + override protected def run(using Context): Unit = if ctx.compilationUnit.needsInlining || ctx.compilationUnit.hasMacroAnnotations then super.run diff --git a/compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala b/compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala index d05b5044ff5c..2f0911aeca81 100644 --- a/compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala +++ b/compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala @@ -2,14 +2,17 @@ package dotty.tools.dotc package transform import java.io.File +import java.nio.file.{Files, Path} import ast.tpd.* import collection.mutable +import core.Comments.Comment import core.Flags.* import core.Contexts.{Context, ctx, inContext} import core.DenotTransformers.IdentityDenotTransformer import core.Symbols.{defn, Symbol} import core.Constants.Constant +import core.NameKinds.DefaultGetterName import core.NameOps.isContextFunction import core.StdNames.nme import core.Types.* @@ -22,6 +25,7 @@ import localopt.StringInterpolatorOpt import inlines.Inlines import scala.util.matching.Regex import java.util.regex.Pattern +import scala.annotation.internal.sharable /** Implements code coverage by inserting calls to scala.runtime.coverage.Invoker * ("instruments" the source code). @@ -40,32 +44,85 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: private var coverageExcludeClasslikePatterns: List[Pattern] = Nil private var coverageExcludeFilePatterns: List[Pattern] = Nil + private val coverageLocalExclusions: mutable.Map[String, List[Span]] = mutable.Map.empty - override def run(using ctx: Context): Unit = + override def runOn(units: List[CompilationUnit])(using ctx: Context): List[CompilationUnit] = val outputPath = ctx.settings.coverageOutputDir.value - // Ensure the dir exists + // Ensure the dir exists (once per batch, not per unit) val dataDir = File(outputPath) val newlyCreated = dataDir.mkdirs() if !newlyCreated then - // If the directory existed before, let's clean it up. - dataDir.listFiles.nn - .filter(_.nn.getName.nn.startsWith("scoverage")) - .foreach(_.nn.delete()) + // If the directory existed before, clean measurement files. + val files = dataDir.listFiles + if files != null then + files + .filter(_.nn.getName.nn.startsWith("scoverage.measurements.")) + .foreach(_.nn.delete()) end if - // Initialise a coverage object if it does not exist yet - if ctx.base.coverage == null then - ctx.base.coverage = Coverage() + // Deserialize previous coverage once at the start + val coverageFilePath = Serializer.coverageFilePath(outputPath) + val previousCoverage = + if Files.exists(coverageFilePath) then + Serializer.deserialize(coverageFilePath, ctx.settings.sourceroot.value) + else Coverage() + // Initialize coverage patterns once coverageExcludeClasslikePatterns = ctx.settings.coverageExcludeClasslikes.value.map(_.r.pattern) coverageExcludeFilePatterns = ctx.settings.coverageExcludeFiles.value.map(_.r.pattern) - ctx.base.coverage.nn.removeStatementsFromFile(ctx.compilationUnit.source.file.absolute.jpath) - super.run + // Initialize coverage object + ctx.base.coverage = Coverage() + ctx.base.coverage.nn.setNextStatementId(previousCoverage.nextStatementId()) + + // Process each unit to extract local coverage exclusions from comments + units.foreach { unit => + val excludedSpans = mutable.ListBuffer[Span]() + var currentStartingComment: Option[Comment] = None + + unit.comments.foreach { + case comment if InstrumentCoverage.scoverageLocalOff.matches(comment.raw) && currentStartingComment.isEmpty => + currentStartingComment = Some(comment) + case comment if InstrumentCoverage.scoverageLocalOn.matches(comment.raw) => + currentStartingComment.foreach { start => + currentStartingComment = None + excludedSpans += start.span.withEnd(comment.span.end) + } + case _ => + } + + currentStartingComment.headOption.foreach { start => + excludedSpans += start.span.withEnd(unit.source.length - 1) + } + + if excludedSpans.nonEmpty then + coverageLocalExclusions(unit.source.file.path) = excludedSpans.toList + } + + // Run the transformation on all units + val result = super.runOn(units) + + // Serialize once at the end with merged coverage + val mergedCoverage = Coverage() + val currentFiles = units.map(_.source.file.absolute.jpath) + + // Add statements from previous coverage that aren't from recompiled files + // and whose source files still exist + previousCoverage.statements + .filterNot(stmt => + val source = stmt.location.sourcePath + currentFiles.contains(source) || !Files.exists(source) + ) + .foreach(mergedCoverage.addStatement) + + // Add all new statements from this compilation + ctx.base.coverage.nn.statements.foreach(mergedCoverage.addStatement) + + Serializer.serialize(mergedCoverage, outputPath, ctx.settings.sourceroot.value) - Serializer.serialize(ctx.base.coverage.nn, outputPath, ctx.settings.sourceroot.value) + result private def isClassIncluded(sym: Symbol)(using Context): Boolean = val fqn = sym.fullName.toText(ctx.printerFn(ctx)).show @@ -79,6 +136,11 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: _.matcher(normalizedPath).nn.matches ) + private def isTreeExcluded(tree: Tree)(using Context): Boolean = + val sourceFile = ctx.source.file.path + coverageLocalExclusions.get(sourceFile).exists: excludedSpans => + excludedSpans.exists(_.contains(tree.span)) + override protected def newTransformer(using Context) = CoverageTransformer(ctx.settings.coverageOutputDir.value) @@ -123,7 +185,8 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: desc = sourceFile.content.slice(pos.start, pos.end).mkString, symbolName = tree.symbol.name.toSimpleName.show, treeName = tree.getClass.getSimpleName.nn, - branch + branch, + ignored = isTreeExcluded(tree) ) ctx.base.coverage.nn.addStatement(statement) id @@ -145,6 +208,40 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: val span = pos.span.toSynthetic invokeCall(statementId, span) + private def erasedParamStatuses(app: Apply)(using Context): List[Boolean] = + app.fun.tpe.widen match + case mt: MethodType if mt.hasErasedParams => mt.erasedParams + case _ => Nil + + private def transformApplyArgs(trees: List[Tree], erasedArgs: List[Boolean] = Nil)(using Context): List[Tree] = + if allConstArgs(trees) then trees + else if erasedArgs.isEmpty then transform(trees) + else trees.lazyZip(erasedArgs).map { (arg, isErased) => + if isErased then arg else transform(arg) + }.toList + + 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, erasedParamStatuses(a)) + ) + 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 +255,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, erasedParamStatuses(tree))) + 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 +270,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), transformApplyArgs(tree.args, erasedParamStatuses(tree))) InstrumentedParts.notCovered(transformed) private def tryInstrument(tree: Ident)(using Context): InstrumentedParts = @@ -187,9 +285,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 +305,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 +327,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,9 +388,11 @@ 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) + val rhs = if tree.symbol.isEffectivelyErased then tree.rhs else transform(tree.rhs) cpy.ValDef(tree)(rhs = rhs) case tree: DefDef => @@ -290,8 +400,10 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: case tree: PackageDef => if isFileIncluded(tree.srcPos.sourcePos.source) && isClassIncluded(tree.symbol) then - // only transform the statements of the package - cpy.PackageDef(tree)(tree.pid, transform(tree.stats)) + // Use transformStats (not transform) to process statements with updated context. + // This ensures language imports like `scala.language.unsafeNulls` are properly + // processed for subsequent statements in the package. + cpy.PackageDef(tree)(tree.pid, transformStats(tree.stats, tree.symbol)) else tree @@ -323,13 +435,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 _ => @@ -390,8 +502,7 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: private def transformTemplateParents(parents: List[Tree])(using Context): List[Tree] = def transformParent(parent: Tree): Tree = parent match case tree: Apply => - // only instrument the args, not the constructor call - cpy.Apply(tree)(tree.fun, tree.args.mapConserve(transform)) + cpy.Apply(tree)(tree.fun, transformApplyArgs(tree.args, erasedParamStatuses(tree))) case tree: TypeApply => // args are types, instrument the fun with transformParent cpy.TypeApply(tree)(transformParent(tree.fun), tree.args) @@ -515,6 +626,7 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: !sym.isOneOf(ExcludeMethodFlags) && !isCompilerIntrinsicMethod(sym) && !(sym.isClassConstructor && isSecondaryCtorDelegateCall) + && !sym.name.is(DefaultGetterName) // https://github.com/scala/scala3/issues/20255 && (tree.typeOpt match case AppliedType(tycon: NamedType, _) => /* If the last expression in a block is a context function, we'll try to @@ -551,6 +663,7 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: && sym.info.isParameterless && !isCompilerIntrinsicMethod(sym) && !sym.info.typeSymbol.name.isContextFunction // exclude context functions like in canInstrumentApply + && !sym.name.is(DefaultGetterName) // https://github.com/scala/scala3/issues/20255 /** Does sym refer to a "compiler intrinsic" method, which only exist during compilation, * like Any.isInstanceOf? @@ -559,15 +672,16 @@ 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 + @sharable val scoverageLocalOn: Regex = """^\s*//\s*\$COVERAGE-ON\$""".r + @sharable val scoverageLocalOff: Regex = """^\s*//\s*\$COVERAGE-OFF\$""".r /** * An instrumented Tree, in 3 parts. diff --git a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala index 3fbdf1fa5427..12045be7419a 100644 --- a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -34,8 +34,8 @@ object LambdaLift: val liftedDefs: HashMap[Symbol, ListBuffer[Tree]] = new HashMap val deps = new Dependencies(ctx.compilationUnit.tpdTree, ctx.withPhase(thisPhase)): - def isExpr(sym: Symbol)(using Context): Boolean = sym.is(Method) - def enclosure(using Context) = ctx.owner.enclosingMethod + def isExpr(sym: Symbol)(using Context): Boolean = sym.is(Method) || sym.hasAnnotation(defn.ScalaStaticAnnot) + def enclosure(using Context) = ctx.owner.enclosingMethodOrStatic override def process(tree: Tree)(using Context): Unit = super.process(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index e6cae7010ade..6cfd0439cf4f 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -9,7 +9,7 @@ import core.Contexts.* import core.Decorators.* import core.DenotTransformers.IdentityDenotTransformer import core.Flags.* -import core.NameKinds.{ExpandedName, LazyBitMapName, LazyLocalInitName, LazyLocalName} +import core.NameKinds.{ExpandedName, LazyBitMapName, LazyLocalInitName, LazyLocalName, LazyVarHandleName} import core.StdNames.nme import core.Symbols.* import core.Types.* @@ -27,8 +27,10 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { * The map contains the list of the offset trees. */ class OffsetInfo(var defs: List[Tree], var ord: Int = 0) + class VarHandleInfo(var defs: List[Tree]) private val appendOffsetDefs = mutable.Map.empty[Symbol, OffsetInfo] + private val appendVarHandleDefs = mutable.Map.empty[Symbol, VarHandleInfo] override def phaseName: String = LazyVals.name @@ -108,12 +110,19 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { */ override def transformTemplate(template: Template)(using Context): Tree = { val cls = ctx.owner.asClass - appendOffsetDefs.get(cls) match { - case None => template - case Some(data) => - data.defs.foreach(defin => defin.symbol.addAnnotation(Annotation(defn.ScalaStaticAnnot, defin.symbol.span))) - cpy.Template(template)(body = addInFront(data.defs, template.body)) - } + if !selectImpl.useVarHandles then + appendOffsetDefs.get(cls) match { + case None => template + case Some(data) => + data.defs.foreach(defin => defin.symbol.addAnnotation(Annotation(defn.ScalaStaticAnnot, defin.symbol.span))) + cpy.Template(template)(body = addInFront(data.defs, template.body)) + } + else + appendVarHandleDefs.get(cls) match { + case None => template + case Some(data) => + cpy.Template(template)(body = addInFront(data.defs, template.body)) + } } private def addInFront(prefix: List[Tree], stats: List[Tree]) = stats match { @@ -273,6 +282,43 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { } } + private object selectImpl { + inline def apply[T](usingOffset: => T, usingVarHandle: => T)(using Context): T = + if useVarHandles(using ctx) then usingVarHandle else usingOffset + + private def checkVarHandlesAllowed(using Context) = { + val useLegacyImpl = ctx.settings.YlegacyLazyVals.value + val useFutureImpl = ctx.settings.YfutureLazyVals.value + + if useLegacyImpl && useFutureImpl then + report.error(em"Both ${ctx.settings.YlegacyLazyVals.name} and ${ctx.settings.YfutureLazyVals.name} cannot be set simultaneously.") + false + else if useLegacyImpl || !useFutureImpl then + false + else { + val releaseVersion = ctx.settings.javaOutputVersion.value + if releaseVersion.nonEmpty && releaseVersion.toInt >= 9 then + true + else + report.error(em"${ctx.settings.YfutureLazyVals.name} is set, but it requires explicit ${ctx.settings.javaOutputVersion.name} set to minimum required version 9 or higher.") + false + } + } + + // Check if environment allows VarHandles, cached per run + private var canUseVarHandles: Boolean = compiletime.uninitialized + private var cachedContext: Context = compiletime.uninitialized + def useVarHandles(using Context) = { + if ctx eq cachedContext then + canUseVarHandles + else + canUseVarHandles = checkVarHandlesAllowed + cachedContext = ctx + canUseVarHandles + } + } + + /** * Create a threadsafe lazy accessor and function that computes the field's value. `Evaluating` and * `NullValue` are represented by `object`s and `Waiting` by a class that allows awaiting the completion @@ -327,20 +373,30 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { * @param memberDef the transformed lazy field member definition * @param claz the class containing this lazy val field * @param target the target synthetic field - * @param offset the offset of the field in the storage allocation of the class + * @param offsetOrVarHandle the offset of the field in the storage allocation or its VarHandle * @param thiz a reference to the transformed class */ def mkThreadSafeDef(memberDef: ValOrDefDef, claz: ClassSymbol, target: Symbol, - offset: Tree, + offsetOrVarHandle: Tree, thiz: Tree)(using Context): (DefDef, DefDef) = { + def varHandle = offsetOrVarHandle.ensuring(selectImpl.useVarHandles, "Unexpected VarHandle usage") + def offset = offsetOrVarHandle.ensuring(!selectImpl.useVarHandles, "Unexpected Offset usage") + val tp = memberDef.tpe.widenDealias.resultType.widenDealias val waiting = ref(defn.LazyValsWaitingState) val controlState = ref(defn.LazyValsControlState) val evaluating = Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.evaluating) val nullValue = Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.nullValue) - val objCasFlag = Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.objCas) + val casFlag = selectImpl( + usingOffset = Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.objCas), + usingVarHandle = typer.Applications.retypeSignaturePolymorphicFn( // must be retyped to avoid wrapping into Array[Object] + Select(varHandle, lazyNme.compareAndSet), + MethodType(List(defn.ObjectType,defn.ObjectType,defn.ObjectType), defn.BooleanType) + ) + ) + val accessorMethodSymbol = memberDef.symbol.asTerm val lazyInitMethodName = LazyLocalInitName.fresh(memberDef.name.asTermName) val lazyInitMethodSymbol = newSymbol(claz, lazyInitMethodName, Synthetic | Method | Private, MethodType(Nil)(_ => Nil, _ => defn.ObjectType)) @@ -382,12 +438,18 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { val lockRel = { val lockSymb = newSymbol(lazyInitMethodSymbol, lazyNme.lock, Synthetic, waiting.typeOpt) Block(ValDef(lockSymb, ref(target).cast(waiting.typeOpt)) - :: objCasFlag.appliedTo(thiz, offset, ref(lockSymb), ref(resSymb)) :: Nil, + :: selectImpl( + usingOffset = casFlag.appliedTo(thiz, offset, ref(lockSymb), ref(resSymb)), + usingVarHandle = casFlag.appliedTo(thiz, ref(lockSymb), ref(resSymb)) + ) :: Nil, ref(lockSymb).select(lazyNme.RLazyVals.waitingRelease).ensureApplied) } // finally block val fin = If( - objCasFlag.appliedTo(thiz, offset, evaluating, ref(resSymb)).select(nme.UNARY_!).appliedToNone, + selectImpl( + usingOffset = casFlag.appliedTo(thiz, offset, evaluating, ref(resSymb)), + usingVarHandle = casFlag.appliedTo(thiz, evaluating, ref(resSymb)) + ).select(nme.UNARY_!).appliedToNone, lockRel, unitLiteral ) @@ -408,7 +470,10 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { ) // if CAS(_, null, Evaluating) If( - objCasFlag.appliedTo(thiz, offset, nullLiteral, evaluating), + selectImpl( + usingOffset = casFlag.appliedTo(thiz, offset, nullLiteral, evaluating), + usingVarHandle = casFlag.appliedTo(thiz, nullLiteral, evaluating) + ), Block(ValDef(resSymb, nullLiteral) :: ValDef(resSymbNullable, nullLiteral) :: evaluate :: Nil, // var result: AnyRef = null Return(ref(resSymbNullable), lazyInitMethodSymbol)), unitLiteral @@ -424,7 +489,10 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { ref(current).select(defn.Object_eq).appliedTo(evaluating), // if is Evaluating then CAS(_, Evaluating, new Waiting) Block( - objCasFlag.appliedTo(thiz, offset, ref(current), Select(New(waiting), StdNames.nme.CONSTRUCTOR).ensureApplied) :: Nil, + selectImpl( + usingOffset = casFlag.appliedTo(thiz, offset, ref(current), Select(New(waiting), StdNames.nme.CONSTRUCTOR).ensureApplied), + usingVarHandle = casFlag.appliedTo(thiz, ref(current), Select(New(waiting), StdNames.nme.CONSTRUCTOR).ensureApplied) + ) :: Nil, unitLiteral ), // if not Evaluating @@ -470,23 +538,43 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.getOffsetStatic) val containerTree = ValDef(containerSymbol, nullLiteral) - // create an offset for this lazy val - val offsetSymbol: TermSymbol = appendOffsetDefs.get(claz) match - case Some(info) => - newSymbol(claz, offsetName(info.defs.size), Synthetic, defn.LongType).enteredAfter(this) - case None => - newSymbol(claz, offsetName(0), Synthetic, defn.LongType).enteredAfter(this) - offsetSymbol.nn.addAnnotation(Annotation(defn.ScalaStaticAnnot, offsetSymbol.nn.span)) - val fieldTree = thizClass.select(lazyNme.RLazyVals.getDeclaredField).appliedTo(Literal(Constant(containerName.mangledString))) - val offsetTree = ValDef(offsetSymbol.nn, getOffset.appliedTo(fieldTree)) - val offsetInfo = appendOffsetDefs.getOrElseUpdate(claz, new OffsetInfo(Nil)) - offsetInfo.defs = offsetTree :: offsetInfo.defs - val offset = ref(offsetSymbol.nn) + val offsetOrVarHandle = selectImpl( + usingOffset = { + // create an offset for this lazy val + val offsetSymbol: TermSymbol = appendOffsetDefs.get(claz) match + case Some(info) => + newSymbol(claz, offsetName(info.defs.size), Synthetic, defn.LongType).enteredAfter(this) + case None => + newSymbol(claz, offsetName(0), Synthetic, defn.LongType).enteredAfter(this) + offsetSymbol.nn.addAnnotation(Annotation(defn.ScalaStaticAnnot, offsetSymbol.nn.span)) + val fieldTree = thizClass.select(lazyNme.RLazyVals.getDeclaredField).appliedTo(Literal(Constant(containerName.mangledString))) + val offsetTree = ValDef(offsetSymbol.nn, getOffset.appliedTo(fieldTree)) + val offsetInfo = appendOffsetDefs.getOrElseUpdate(claz, new OffsetInfo(Nil)) + offsetInfo.defs = offsetTree :: offsetInfo.defs + ref(offsetSymbol.nn) + }, + usingVarHandle = { + // create a VarHandle for this lazy val + val varHandleSymbol: TermSymbol = newSymbol(claz, LazyVarHandleName(containerName), Private | Synthetic, defn.VarHandleClass.typeRef).enteredAfter(this) + varHandleSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot, varHandleSymbol.span)) + val getVarHandle = + ref(defn.MethodHandlesClass).select(defn.MethodHandles_lookup).appliedToNone + .select(defn.MethodHandlesLookup_FindVarHandle).appliedTo( + thizClass, Literal(Constant(containerName.mangledString)), Literal(Constant(defn.ObjectType)) + ) + val varHandleTree = ValDef(varHandleSymbol, getVarHandle) + val varHandle = ref(varHandleSymbol) + + val varHandleInfo = appendVarHandleDefs.getOrElseUpdate(claz, new VarHandleInfo(Nil)) + varHandleInfo.defs = varHandleTree :: varHandleInfo.defs + varHandle + } + ) val swapOver = This(claz) - val (accessorDef, initMethodDef) = mkThreadSafeDef(x, claz, containerSymbol, offset, swapOver) + val (accessorDef, initMethodDef) = mkThreadSafeDef(x, claz, containerSymbol, offsetOrVarHandle, swapOver) Thicket(containerTree, accessorDef, initMethodDef) } @@ -686,5 +774,6 @@ object LazyVals { val current: TermName = "current".toTermName val lock: TermName = "lock".toTermName val discard: TermName = "discard".toTermName + val compareAndSet: TermName = "compareAndSet".toTermName } } diff --git a/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala b/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala index 887a962f7a65..0cc1eb5c00ca 100644 --- a/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala @@ -13,7 +13,7 @@ abstract class MacroTransform extends Phase { import ast.tpd.* - override def run(using Context): Unit = { + override protected def run(using Context): Unit = { val unit = ctx.compilationUnit unit.tpdTree = atPhase(transformPhase)(newTransformer.transform(unit.tpdTree)) } diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index 3076e5fc183c..1f5831536d84 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -132,7 +132,7 @@ object MegaPhase { protected def singletonGroup: MegaPhase = new MegaPhase(Array(this)) - override def run(using Context): Unit = + protected def run(using Context): Unit = singletonGroup.run } } @@ -482,7 +482,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { def transformSpecificTrees[T <: Tree](trees: List[T], start: Int)(using Context): List[T] = transformTrees(trees, start).asInstanceOf[List[T]] - override def run(using Context): Unit = + protected def run(using Context): Unit = ctx.compilationUnit.tpdTree = atPhase(miniPhases.last.next)(transformUnit(ctx.compilationUnit.tpdTree)) diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index 6df4bebde132..a92cdaf21149 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -6,7 +6,6 @@ import core.* import MegaPhase.* import Contexts.* import Flags.* - import Symbols.* import SymDenotations.* import Types.* @@ -17,6 +16,7 @@ import Names.* import NameKinds.* import NameOps.* import ast.Trees.* +import dotty.tools.dotc.core.Phases.erasurePhase object Mixin { val name: String = "mixin" @@ -93,7 +93,7 @@ object Mixin { * def x_=(y: T) = () * * 4.5 (done in `mixinForwarders`) For every method - * ` def f[Ts](ps1)...(psN): U` imn M` that needs to be disambiguated: + * ` def f[Ts](ps1)...(psN): U` in M` that needs to be disambiguated: * * def f[Ts](ps1)...(psN): U = super[M].f[Ts](ps1)...(psN) * @@ -113,6 +113,15 @@ object Mixin { class Mixin extends MiniPhase with SymTransformer { thisPhase => import ast.tpd.* + /** Infos before erasure of generated mixin trees. + * + * These will be used to generate Java generic signatures. + * Normally we use the types before erasure; we cannot do that + * for mixin trees since they are created after erasure, and therefore + * their type history does not have anything recorded for before erasure. + */ + val mixinGenericInfos = MutableSymbolMap[Type]() + override def phaseName: String = Mixin.name override def description: String = Mixin.description @@ -248,7 +257,7 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => case Some((_, _, args)) => args.iterator case _ => Iterator.empty def nextArgument() = - if argsIt.hasNext then argsIt.next + if argsIt.hasNext then argsIt.next() else assert( impl.parents.forall(_.tpe.typeSymbol != mixin), @@ -277,7 +286,12 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => else Underscore(getter.info.resultType) // transformFollowing call is needed to make memoize & lazy vals run - transformFollowing(DefDef(mkForwarderSym(getter.asTerm), rhs)) + val forwarder = mkForwarderSym(getter.asTerm) + val erased = atPhase(erasurePhase) { + cls.thisType.memberInfo(getter) + } + mixinGenericInfos(forwarder) = erased + transformFollowing(DefDef(forwarder, rhs)) } else if wasOneOf(getter, ParamAccessor) then // mixin parameter field is defined by an override; evaluate the argument and throw it away @@ -293,11 +307,27 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => yield transformFollowing(DefDef(mkForwarderSym(setter.asTerm), unitLiteral.withSpan(cls.span))) def mixinForwarders(mixin: ClassSymbol): List[Tree] = - for (meth <- mixin.info.decls.toList if needsMixinForwarder(meth)) - yield { + for meth <- mixin.info.decls.filter(needsMixinForwarder) + yield util.Stats.record("mixin forwarders") - transformFollowing(DefDef(mkForwarderSym(meth.asTerm, Bridge), forwarderRhsFn(meth))) + transformFollowing(DefDef(mkMixinForwarderSym(meth.asTerm), forwarderRhsFn(meth))) + + def mkMixinForwarderSym(target: TermSymbol): TermSymbol = + val sym = mkForwarderSym(target, extraFlags = Bridge | MixedIn) + val (infoBeforeErasure, isDifferentThanInfoNow) = atPhase(erasurePhase) { + val beforeErasure = cls.thisType.memberInfo(target) + (beforeErasure, !(beforeErasure =:= sym.info)) } + if isDifferentThanInfoNow then + // The info before erasure would not have been the same as the info now. + // We want to store it for the backend to compute the generic Java signature. + // However, we must still avoid doing that if erasing that signature would + // not give the same erased type. If it doesn't, we'll just give a completely + // incorrect Java signature. (This could be improved by generating dedicated + // bridges, but we don't go that far; scalac doesn't either.) + if TypeErasure.transformInfo(target, infoBeforeErasure) =:= sym.info then + mixinGenericInfos(sym) = infoBeforeErasure + sym cpy.Template(impl)( constr = diff --git a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala index 1b2d3e79c9a4..1d6e5e1d9b10 100644 --- a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala @@ -2,8 +2,9 @@ package dotty.tools.dotc package transform import core.* -import Symbols.*, Types.*, Contexts.*, DenotTransformers.*, Flags.* +import Decorators.*, Symbols.*, Types.*, Contexts.*, DenotTransformers.*, Flags.* import util.Spans.* +import util.chaining.* import StdNames.*, NameOps.* import typer.Nullables @@ -18,15 +19,21 @@ class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(using Context) { map(n => getClassIfDefined("org.junit." + n)). filter(_.exists) - def mkForwarderSym(member: TermSymbol, extraFlags: FlagSet = EmptyFlags): TermSymbol = { - val res = member.copy( + def mkForwarderSym(member: TermSymbol, extraFlags: FlagSet = EmptyFlags): TermSymbol = + member.copy( owner = cls, name = member.name.stripScala2LocalSuffix, flags = member.flags &~ Deferred &~ Module | Synthetic | extraFlags, - info = cls.thisType.memberInfo(member)).enteredAfter(thisPhase).asTerm - res.addAnnotations(member.annotations.filter(_.symbol != defn.TailrecAnnot)) - res - } + info = cls.thisType.memberInfo(member) + ) + .enteredAfter(thisPhase) + .asTerm.tap: forwarder => + forwarder.addAnnotations(member.annotations.filter(_.symbol != defn.TailrecAnnot)) + val paramSymss = forwarder.paramSymss // compute once; different from rawParamss + forwarder.setParamss(paramSymss) + atPhaseBeforeTransforms: + for (src, dst) <- member.paramSymss.flatten.filter(!_.isType).zip(paramSymss.flatten) do + dst.addAnnotations(src.annotations) def superRef(target: Symbol, span: Span = cls.span): Tree = { val sup = if (target.isConstructor && !target.owner.is(Trait)) @@ -47,32 +54,39 @@ class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(using Context) { cls.info.nonPrivateMember(sym.name).hasAltWith(_.symbol == sym) } - /** Does `method` need a forwarder to in class `cls` - * Method needs a forwarder in those cases: + /** Does `method` need a forwarder in class `cls`? + * Method needs a forwarder in these cases: * - there's a class defining a method with same signature * - there are multiple traits defining method with same signature */ - def needsMixinForwarder(meth: Symbol): Boolean = { + def needsMixinForwarder(meth: Symbol): Boolean = lazy val competingMethods = competingMethodsIterator(meth).toList - def needsDisambiguation = competingMethods.exists(x=> !x.is(Deferred)) // multiple implementations are available + def needsDisambiguation = competingMethods.exists(!_.is(Deferred)) // multiple implementations are available def hasNonInterfaceDefinition = competingMethods.exists(!_.owner.is(Trait)) // there is a definition originating from class // JUnit 4 won't recognize annotated default methods, so always generate a forwarder for them. def generateJUnitForwarder: Boolean = - meth.annotations.nonEmpty && JUnit4Annotations.exists(annot => meth.hasAnnotation(annot)) && + meth.annotations.nonEmpty && JUnit4Annotations.exists(meth.hasAnnotation) && ctx.settings.mixinForwarderChoices.isAtLeastJunit // Similarly, Java serialization won't take into account a readResolve/writeReplace default method. def generateSerializationForwarder: Boolean = (meth.name == nme.readResolve || meth.name == nme.writeReplace) && meth.info.paramNamess.flatten.isEmpty - !meth.isConstructor && - meth.is(Method, butNot = PrivateOrAccessorOrDeferred) && - (ctx.settings.mixinForwarderChoices.isTruthy || meth.owner.is(Scala2x) || needsDisambiguation || hasNonInterfaceDefinition || - generateJUnitForwarder || generateSerializationForwarder) && - isInImplementingClass(meth) - } + !meth.isConstructor + && meth.is(Method, butNot = PrivateOrAccessorOrDeferred) + && (!meth.is(JavaDefined) || !meth.owner.is(Sealed) || meth.owner.children.contains(cls)) + && ( + ctx.settings.mixinForwarderChoices.isTruthy + || meth.owner.is(Scala2x) + || needsDisambiguation + || hasNonInterfaceDefinition + || generateJUnitForwarder + || generateSerializationForwarder + ) + && isInImplementingClass(meth) + end needsMixinForwarder final val PrivateOrAccessor: FlagSet = Private | Accessor final val PrivateOrAccessorOrDeferred: FlagSet = Private | Accessor | Deferred diff --git a/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala b/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala index 95975ad9e6b8..7a1e55a37324 100644 --- a/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala +++ b/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -12,13 +12,16 @@ import SymDenotations.SymDenotation import Names.Name import StdNames.nme import NameOps.* +import NameKinds.LazyVarHandleName import ast.* import MegaPhase.* -/** Move static methods from companion to the class itself */ +/** Move static methods from companion to the class itself. Also create the static constructor. + * VarHandles generated by the compiler for lazy vals are left in the original class. + */ class MoveStatics extends MiniPhase with SymTransformer { import ast.tpd.* @@ -28,7 +31,7 @@ class MoveStatics extends MiniPhase with SymTransformer { def transformSym(sym: SymDenotation)(using Context): SymDenotation = if (sym.hasAnnotation(defn.ScalaStaticAnnot) && sym.owner.is(Flags.Module) && sym.owner.companionClass.exists && - (sym.is(Flags.Method) || !(sym.is(Flags.Mutable) && sym.owner.companionClass.is(Flags.Trait)))) { + (sym.is(Flags.Method) || !(sym.is(Flags.Mutable) && sym.owner.companionClass.is(Flags.Trait)) && !sym.symbol.name.is(LazyVarHandleName))) { sym.owner.asClass.delete(sym.symbol) sym.owner.companionClass.asClass.enter(sym.symbol) sym.copySymDenotation(owner = sym.owner.companionClass) @@ -65,7 +68,7 @@ class MoveStatics extends MiniPhase with SymTransformer { val moduleTmpl = module.rhs.asInstanceOf[Template] val companionTmpl = companion.rhs.asInstanceOf[Template] val (staticDefs, remainingDefs) = moduleTmpl.body.partition { - case memberDef: MemberDef => memberDef.symbol.isScalaStatic + case memberDef: MemberDef => memberDef.symbol.isScalaStatic && !memberDef.symbol.name.is(LazyVarHandleName) case _ => false } diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index a52502364c4d..5bebdcf1b80a 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -399,14 +399,15 @@ object PatternMatcher { // begin patternPlan swapBind(tree) match { case Typed(pat, tpt) => - val isTrusted = pat match { + def isTrusted(pattern: Tree): Boolean = pattern match { case UnApply(extractor, _, _) => extractor.symbol.is(Synthetic) && extractor.symbol.owner.linkedClass.is(Case) && !hasExplicitTypeArgs(extractor) + case Bind(_, body) => isTrusted(body) case _ => false } - TestPlan(TypeTest(tpt, isTrusted), scrutinee, tree.span, + TestPlan(TypeTest(tpt, isTrusted(pat)), scrutinee, tree.span, letAbstract(ref(scrutinee).cast(tpt.tpe)) { casted => nonNull += casted patternPlan(casted, pat, onSuccess) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 69f64ef4bf57..f33266196c4e 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -86,7 +86,7 @@ class PickleQuotes extends MacroTransform { assert(Inlines.inInlineMethod) case _ => - override def run(using Context): Unit = + override protected def run(using Context): Unit = if (ctx.compilationUnit.needsStaging) super.run protected def newTransformer(using Context): Transformer = new Transformer { @@ -139,7 +139,7 @@ class PickleQuotes extends MacroTransform { /** Get the holeContents of the transformed tree */ def getContents() = - val res = holeContents.result + val res = holeContents.result() holeContents.clear() res end HoleContentExtractor diff --git a/compiler/src/dotty/tools/dotc/transform/Pickler.scala b/compiler/src/dotty/tools/dotc/transform/Pickler.scala index 61781dde13db..42d0a89bc183 100644 --- a/compiler/src/dotty/tools/dotc/transform/Pickler.scala +++ b/compiler/src/dotty/tools/dotc/transform/Pickler.scala @@ -75,7 +75,7 @@ class Pickler extends Phase { private def useExecutor(using Context) = Pickler.ParallelPickling && !ctx.settings.YtestPickler.value - override def run(using Context): Unit = { + protected def run(using Context): Unit = { val unit = ctx.compilationUnit pickling.println(i"unpickling in run ${ctx.runId}") diff --git a/compiler/src/dotty/tools/dotc/transform/PostInlining.scala b/compiler/src/dotty/tools/dotc/transform/PostInlining.scala index 26c956d85d18..b6fd62b1d0f5 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostInlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostInlining.scala @@ -17,7 +17,7 @@ class PostInlining extends MacroTransform, IdentityDenotTransformer: override def changesMembers = true - override def run(using Context): Unit = + override protected def run(using Context): Unit = if ctx.compilationUnit.needsMirrorSupport then super.run lazy val synthMbr: SyntheticMembers = new SyntheticMembers(thisPhase) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 72a3ff7324e8..6d5175bf17a0 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -193,7 +193,11 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ++ sym.annotations) else if sym.is(Param) then + // @unused is getter/setter but we want it on ordinary method params + // @param should be consulted only for fields + val unusing = sym.getAnnotation(defn.UnusedAnnot) sym.keepAnnotationsCarrying(thisPhase, Set(defn.ParamMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots) + unusing.foreach(sym.addAnnotation) else if sym.is(ParamAccessor) then sym.keepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot)) else diff --git a/compiler/src/dotty/tools/dotc/transform/PreRecheck.scala b/compiler/src/dotty/tools/dotc/transform/PreRecheck.scala index db9e28d7aad7..e43c3dd113af 100644 --- a/compiler/src/dotty/tools/dotc/transform/PreRecheck.scala +++ b/compiler/src/dotty/tools/dotc/transform/PreRecheck.scala @@ -14,6 +14,6 @@ abstract class PreRecheck extends Phase, DenotTransformer: override def changesBaseTypes: Boolean = true - def run(using Context): Unit = () + override protected def run(using Context): Unit = () override def isCheckable = false diff --git a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala index a779f39800e4..feba9e856c04 100644 --- a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala @@ -68,7 +68,7 @@ class ProtectedAccessors extends MiniPhase { private class Accessors extends AccessProxies { val insert: Insert = new Insert { def accessorNameOf(name: TermName, site: Symbol)(using Context): TermName = ProtectedAccessorName(name) - def needsAccessor(sym: Symbol)(using Context) = ProtectedAccessors.needsAccessor(sym) + def needsAccessor(refTree: RefTree | Apply | TypeApply)(using Context) = ProtectedAccessors.needsAccessor(refTree.symbol) override def ifNoHost(reference: RefTree)(using Context): Tree = { val curCls = ctx.owner.enclosingClass diff --git a/compiler/src/dotty/tools/dotc/transform/Recheck.scala b/compiler/src/dotty/tools/dotc/transform/Recheck.scala index 2eebc2347eec..24cf77dead31 100644 --- a/compiler/src/dotty/tools/dotc/transform/Recheck.scala +++ b/compiler/src/dotty/tools/dotc/transform/Recheck.scala @@ -134,7 +134,7 @@ abstract class Recheck extends Phase, SymTransformer: sym.copySymDenotation(initFlags = sym.flags &~ Recheck.ResetPrivate | Private) else sym - def run(using Context): Unit = + override protected def run(using Context): Unit = val rechecker = newRechecker() rechecker.checkUnit(ctx.compilationUnit) rechecker.reset() @@ -149,7 +149,7 @@ abstract class Recheck extends Phase, SymTransformer: * can be retrieved with `knownType` */ private val keepAllTypes = inContext(ictx) { - ictx.settings.Xprint.value.containsPhase(thisPhase) + ictx.settings.Vprint.value.containsPhase(thisPhase) } /** Should type of `tree` be kept in an attachment so that it can be retrieved with diff --git a/compiler/src/dotty/tools/dotc/transform/SetRootTree.scala b/compiler/src/dotty/tools/dotc/transform/SetRootTree.scala index f62b1f5f01f2..6a6cecd00ec5 100644 --- a/compiler/src/dotty/tools/dotc/transform/SetRootTree.scala +++ b/compiler/src/dotty/tools/dotc/transform/SetRootTree.scala @@ -17,6 +17,7 @@ class SetRootTree extends Phase { // Check no needed. Does not transform trees override def isCheckable: Boolean = false + // public accessibility, not protected as in Phase, because this is called from Namer override def run(using Context): Unit = { val tree = ctx.compilationUnit.tpdTree traverser.traverse(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeTuples.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeTuples.scala index 264659930505..37623997aef5 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeTuples.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeTuples.scala @@ -41,7 +41,8 @@ class SpecializeTuples extends MiniPhase: qual.tpe.widenDealias match case AppliedType(tycon, args) if defn.isSpecializableTuple(tycon.classSymbol, args) => val argIdx = if name == nme._1 then 0 else 1 - Select(qual, name.specializedName(args(argIdx) :: Nil)) + // Keep the specialized accessor for performance, then adapt back to the original expected type. + Select(qual, name.specializedName(args(argIdx) :: Nil)).ensureConforms(tree.tpe) case _ => tree case _ => tree diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 7a1992b0ba18..a52b78682ac1 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -54,7 +54,7 @@ object Splicer { inContext(sliceContext) { val oldContextClassLoader = Thread.currentThread().getContextClassLoader Thread.currentThread().setContextClassLoader(classLoader) - try ctx.profiler.onMacroSplice(owner){ + try { val interpreter = new SpliceInterpreter(splicePos, classLoader) // Some parts of the macro are evaluated during the unpickling performed in quotedExprToTree diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index e9cc877ca770..db99e4f1d28c 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -76,7 +76,7 @@ class Splicing extends MacroTransform: override def phaseName: String = Splicing.name - override def run(using Context): Unit = + override protected def run(using Context): Unit = if ctx.compilationUnit.needsStaging then super.run diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index a48718b2d60a..802845e3fa6a 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -76,7 +76,7 @@ class Staging extends MacroTransform { } end checkPostCondition - override def run(using Context): Unit = + override protected def run(using Context): Unit = if (ctx.compilationUnit.needsStaging) super.run protected def newTransformer(using Context): Transformer = new Transformer { diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 70f82434a5a6..841ed63449f0 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -15,6 +15,7 @@ import util.Property import util.Spans.Span import config.Printers.derive import NullOpsDecorator.* +import scala.runtime.Statics object SyntheticMembers { @@ -67,7 +68,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { myCaseSymbols = defn.caseClassSynthesized myCaseModuleSymbols = myCaseSymbols.filter(_ ne defn.Any_equals) myEnumValueSymbols = List(defn.Product_productPrefix) - myNonJavaEnumValueSymbols = myEnumValueSymbols :+ defn.Any_toString :+ defn.Enum_ordinal + myNonJavaEnumValueSymbols = myEnumValueSymbols :+ defn.Any_toString :+ defn.Enum_ordinal :+ defn.Any_hashCode } def valueSymbols(using Context): List[Symbol] = { initSymbols; myValueSymbols } @@ -95,6 +96,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { val isSimpleEnumValue = isEnumValue && !clazz.owner.isAllOf(EnumCase) val isJavaEnumValue = isEnumValue && clazz.derivesFrom(defn.JavaEnumClass) val isNonJavaEnumValue = isEnumValue && !isJavaEnumValue + val ownName = clazz.name.stripModuleClassSuffix.toString val symbolsToSynthesize: List[Symbol] = if clazz.is(Case) then @@ -108,6 +110,12 @@ class SyntheticMembers(thisPhase: DenotTransformer) { def syntheticDefIfMissing(sym: Symbol): List[Tree] = if (existingDef(sym, clazz).exists) Nil else syntheticDef(sym) :: Nil + def identifierRef: Tree = + if isSimpleEnumValue then // owner is `def $new(_$ordinal: Int, $name: String) = new MyEnum { ... }` + ref(clazz.owner.paramSymss.head.find(_.name == nme.nameDollar).get) + else // assume owner is `val Foo = new MyEnum { def ordinal = 0 }` + Literal(Constant(clazz.owner.name.toString)) + def syntheticDef(sym: Symbol): Tree = { val synthetic = sym.copy( owner = clazz, @@ -118,8 +126,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { def forwardToRuntime(vrefs: List[Tree]): Tree = ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToTermArgs(This(clazz) :: vrefs) - def ownName: Tree = - Literal(Constant(clazz.name.stripModuleClassSuffix.toString)) + def ownNameLit: Tree = Literal(Constant(ownName)) def nameRef: Tree = if isJavaEnumValue then @@ -128,12 +135,6 @@ class SyntheticMembers(thisPhase: DenotTransformer) { else identifierRef - def identifierRef: Tree = - if isSimpleEnumValue then // owner is `def $new(_$ordinal: Int, $name: String) = new MyEnum { ... }` - ref(clazz.owner.paramSymss.head.find(_.name == nme.nameDollar).get) - else // assume owner is `val Foo = new MyEnum { def ordinal = 0 }` - Literal(Constant(clazz.owner.name.toString)) - def ordinalRef: Tree = if isSimpleEnumValue then // owner is `def $new(_$ordinal: Int, $name: String) = new MyEnum { ... }` ref(clazz.owner.paramSymss.head.find(_.name == nme.ordinalDollar_).get) @@ -146,7 +147,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { Literal(Constant(candidate.get)) def toStringBody(vrefss: List[List[Tree]]): Tree = - if (clazz.is(ModuleClass)) ownName + if (clazz.is(ModuleClass)) ownNameLit else if (isNonJavaEnumValue) identifierRef else forwardToRuntime(vrefss.head) @@ -159,7 +160,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { case nme.ordinal => ordinalRef case nme.productArity => Literal(Constant(accessors.length)) case nme.productPrefix if isEnumValue => nameRef - case nme.productPrefix => ownName + case nme.productPrefix => ownNameLit case nme.productElement => productElementBody(accessors.length, vrefss.head.head) case nme.productElementName => productElementNameBody(accessors.length, vrefss.head.head) } @@ -289,50 +290,56 @@ class SyntheticMembers(thisPhase: DenotTransformer) { * class C(x: T) extends AnyVal * ``` * - * gets the `hashCode` method: - * + * gets the `hashCode` method. If the value is primitive: * ``` * def hashCode: Int = x.hashCode() * ``` + * otherwise, the null-safe variant: + * ``` + * def hashCode: Int = java.util.Objects.hashCode(x) + * ``` */ def valueHashCodeBody(using Context): Tree = { assert(accessors.nonEmpty) - ref(accessors.head).select(nme.hashCode_).ensureApplied + val accessor = accessors.head + val tp = accessor.info.finalResultType + if (tp.classSymbol.isPrimitiveValueClass) + ref(accessor).select(nme.hashCode_).ensureApplied + else + ref(defn.Objects_hashCode).appliedTo(ref(accessor)) } - /** The class - * - * ``` - * case object C - * ``` + /** + * A `case object C` or a `case class C()` without parameters gets the `hashCode` method + * ``` + * def hashCode: Int = "C".hashCode // constant folded + * ``` * - * gets the `hashCode` method: - * - * ``` - * def hashCode: Int = "C".hashCode // constant folded - * ``` - * - * The class - * - * ``` - * case class C(x: T, y: U) - * ``` + * Otherwise, if none of the parameters are primitive types: + * ``` + * def hashCode: Int = MurmurHash3.productHash( + * this, + * Statics.mix(0xcafebabe, "C".hashCode), // constant folded + * ignorePrefix = true) + * ``` * - * if none of `T` or `U` are primitive types, gets the `hashCode` method: - * - * ``` - * def hashCode: Int = ScalaRunTime._hashCode(this) - * ``` + * The implementation used to invoke `ScalaRunTime._hashCode`, but that implementation mixes in the result + * of `productPrefix`, which causes scala/bug#13033. By setting `ignorePrefix = true` and mixing in the case + * name into the seed, the bug can be fixed and the generated code works with the unchanged Scala library. * - * else if either `T` or `U` are primitive, gets the `hashCode` method implemented by [[caseHashCodeBody]] + * For case classes with primitive paramters, see [[caseHashCodeBody]]. */ def chooseHashcode(using Context) = - if (clazz.is(ModuleClass)) - Literal(Constant(clazz.name.stripModuleClassSuffix.toString.hashCode)) + if (isNonJavaEnumValue) identifierRef.select(nme.hashCode_).appliedToTermArgs(Nil) + else if (accessors.isEmpty) Literal(Constant(ownName.hashCode)) else if (accessors.exists(_.info.finalResultType.classSymbol.isPrimitiveValueClass)) caseHashCodeBody else - ref(defn.ScalaRuntime__hashCode).appliedTo(This(clazz)) + ref(defn.MurmurHash3Module).select(defn.MurmurHash3_productHash).appliedTo( + This(clazz), + Literal(Constant(Statics.mix(0xcafebabe, ownName.hashCode))), + Literal(Constant(true)) + ) /** The class * @@ -345,7 +352,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { * ``` * def hashCode: Int = { * var acc: Int = 0xcafebabe - * acc = Statics.mix(acc, this.productPrefix.hashCode()); + * acc = Statics.mix(acc, "C".hashCode); * acc = Statics.mix(acc, x); * acc = Statics.mix(acc, Statics.this.anyHash(y)); * Statics.finalizeHash(acc, 2) @@ -356,7 +363,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { val acc = newSymbol(ctx.owner, nme.acc, Mutable | Synthetic, defn.IntType, coord = ctx.owner.span) val accDef = ValDef(acc, Literal(Constant(0xcafebabe))) val mixPrefix = Assign(ref(acc), - ref(defn.staticsMethod("mix")).appliedTo(ref(acc), This(clazz).select(defn.Product_productPrefix).select(defn.Any_hashCode).appliedToNone)) + ref(defn.staticsMethod("mix")).appliedTo(ref(acc), Literal(Constant(ownName.hashCode)))) val mixes = for (accessor <- accessors) yield Assign(ref(acc), ref(defn.staticsMethod("mix")).appliedTo(ref(acc), hashImpl(accessor))) val finish = ref(defn.staticsMethod("finalizeHash")).appliedTo(ref(acc), Literal(Constant(accessors.size))) diff --git a/compiler/src/dotty/tools/dotc/transform/TailRec.scala b/compiler/src/dotty/tools/dotc/transform/TailRec.scala index 158b72f7abdb..b2be8d4651f0 100644 --- a/compiler/src/dotty/tools/dotc/transform/TailRec.scala +++ b/compiler/src/dotty/tools/dotc/transform/TailRec.scala @@ -4,9 +4,9 @@ package transform import ast.{TreeTypeMap, tpd} import config.Printers.tailrec import core.* -import Contexts.*, Flags.*, Symbols.*, Decorators.em +import Contexts.*, Flags.*, Symbols.*, Decorators.* import Constants.Constant -import NameKinds.{TailLabelName, TailLocalName, TailTempName} +import NameKinds.{DefaultGetterName, TailLabelName, TailLocalName, TailTempName} import StdNames.nme import reporting.* import transform.MegaPhase.MiniPhase @@ -194,7 +194,8 @@ class TailRec extends MiniPhase { def isInfiniteRecCall(tree: Tree): Boolean = { def tailArgOrPureExpr(stat: Tree): Boolean = stat match { case stat: ValDef if stat.name.is(TailTempName) || !stat.symbol.is(Mutable) => tailArgOrPureExpr(stat.rhs) - case Assign(lhs: Ident, rhs) if lhs.symbol.name.is(TailLocalName) => tailArgOrPureExpr(rhs) + case Assign(lhs: Ident, rhs) if lhs.symbol.name.is(TailLocalName) => + tailArgOrPureExpr(rhs) || varForRewrittenThis.exists(_ == lhs.symbol && rhs.tpe.isStable) case Assign(lhs: Ident, rhs: Ident) => lhs.symbol == rhs.symbol case stat: Ident if stat.symbol.name.is(TailLocalName) => true case _ => tpd.isPureExpr(stat) @@ -322,7 +323,14 @@ class TailRec extends MiniPhase { method.matches(calledMethod) && enclosingClass.appliedRef.widen <:< prefix.tpe.widenDealias - if (isRecursiveCall) + if isRecursiveCall then + if ctx.settings.Whas.recurseWithDefault then + tree.args.find(_.symbol.name.is(DefaultGetterName)) match + case Some(arg) => + val DefaultGetterName(_, index) = arg.symbol.name: @unchecked + report.warning(RecurseWithDefault(calledMethod.info.firstParamNames(index)), tree.srcPos) + case _ => + if (inTailPosition) { tailrec.println("Rewriting tail recursive call: " + tree.span) rewrote = true @@ -343,6 +351,12 @@ class TailRec extends MiniPhase { case prefix: This if prefix.symbol == enclosingClass => // Avoid assigning `this = this` assignParamPairs + case prefix + if prefix.symbol.is(Module) + && prefix.symbol.moduleClass == enclosingClass + && isPurePath(prefix) => + // Avoid assigning `this = MyObject` + assignParamPairs case _ => (getVarForRewrittenThis(), noTailTransform(prefix)) :: assignParamPairs diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 33d1ea20df14..dbfd62f610ae 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -102,7 +102,7 @@ class TreeChecker extends Phase with SymTransformer { def phaseName: String = "Ycheck" - def run(using Context): Unit = + protected def run(using Context): Unit = if (ctx.settings.YtestPickler.value && ctx.phase.prev.isInstanceOf[Pickler]) report.echo("Skipping Ycheck after pickling with -Ytest-pickler, the returned tree contains stale symbols") else if (ctx.phase.prev.isCheckable) @@ -214,7 +214,7 @@ object TreeChecker { private val everDefinedSyms = MutableSymbolMap[untpd.Tree]() // don't check value classes after typer, as the constraint about constructors doesn't hold after transform - override def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(using Context): Unit = () + override def checkDerivedValueClass(cdef: untpd.TypeDef, clazz: Symbol, stats: List[Tree])(using Context): Unit = () def withDefinedSyms[T](trees: List[untpd.Tree])(op: => T)(using Context): T = { var locally = List.empty[Symbol] @@ -699,8 +699,13 @@ object TreeChecker { // Check that we only add the captured type `T` instead of a more complex type like `List[T]`. // If we have `F[T]` with captured `F` and `T`, we should list `F` and `T` separately in the args. + def isAllowedTypeArg(tp: Type): Boolean = tp.dealias match + case _: TypeRef | _: TermRef | _: ThisType => true + case tp: AndType => isAllowedTypeArg(tp.tp1) && isAllowedTypeArg(tp.tp2) + case _ => false + for arg <- args do - assert(arg.isTerm || arg.tpe.isInstanceOf[TypeRef], "Expected TypeRef in Hole type args but got: " + arg.tpe) + assert(arg.isTerm || isAllowedTypeArg(arg.tpe), "Unexpected type arg in Hole: " + arg.tpe) // Check result type of the hole if isTerm then assert(tree1.typeOpt <:< pt) @@ -781,31 +786,35 @@ object TreeChecker { val phases = ctx.base.allPhases.toList val treeChecker = new LocalChecker(previousPhases(phases)) + + def reportMalformedMacroTree(msg: String | Null, err: Throwable) = + val stack = + if !ctx.settings.Ydebug.value then "\nstacktrace available when compiling with `-Ydebug`" + else if err.getStackTrace == null then " no stacktrace" + else err.getStackTrace.nn.mkString(" ", " \n", "") + report.error( + em"""Malformed tree was found while expanding macro with -Xcheck-macros. + |The tree does not conform to the compiler's tree invariants. + | + |Macro was: + |${scala.quoted.runtime.impl.QuotesImpl.showDecompiledTree(original)} + | + |The macro returned: + |${scala.quoted.runtime.impl.QuotesImpl.showDecompiledTree(expansion)} + | + |Error: + |$msg + |$stack + |""", + original + ) try treeChecker.typed(expansion)(using checkingCtx) catch case err: java.lang.AssertionError => - val stack = - if !ctx.settings.Ydebug.value then "\nstacktrace available when compiling with `-Ydebug`" - else if err.getStackTrace == null then " no stacktrace" - else err.getStackTrace.nn.mkString(" ", " \n", "") - - report.error( - s"""Malformed tree was found while expanding macro with -Xcheck-macros. - |The tree does not conform to the compiler's tree invariants. - | - |Macro was: - |${scala.quoted.runtime.impl.QuotesImpl.showDecompiledTree(original)} - | - |The macro returned: - |${scala.quoted.runtime.impl.QuotesImpl.showDecompiledTree(expansion)} - | - |Error: - |${err.getMessage} - |$stack - |""", - original - ) + reportMalformedMacroTree(err.getMessage(), err) + case err: UnhandledError => + reportMalformedMacroTree(err.diagnostic.message, err) private[TreeChecker] def previousPhases(phases: List[Phase])(using Context): List[Phase] = phases match { case (phase: MegaPhase) :: phases1 => diff --git a/compiler/src/dotty/tools/dotc/transform/TryCatchPatterns.scala b/compiler/src/dotty/tools/dotc/transform/TryCatchPatterns.scala index 095c6af60766..6243f8ac1cad 100644 --- a/compiler/src/dotty/tools/dotc/transform/TryCatchPatterns.scala +++ b/compiler/src/dotty/tools/dotc/transform/TryCatchPatterns.scala @@ -71,7 +71,7 @@ class TryCatchPatterns extends MiniPhase { case _ => isDefaultCase(cdef) } - private def isSimpleThrowable(tp: Type)(using Context): Boolean = tp.stripped match { + private def isSimpleThrowable(tp: Type)(using Context): Boolean = tp.strippedDealias match { case tp @ TypeRef(pre, _) => (pre == NoPrefix || pre.typeSymbol.isStatic) && // Does not require outer class check !tp.symbol.is(Flags.Trait) && // Traits not supported by JVM diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 4591b4d75533..677c8adc5829 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -108,9 +108,10 @@ object TypeTestsCasts { // // If we perform widening, we will get X = Nothing, and we don't have // Ident[X] <:< Ident[Int] any more. - TypeComparer.constrainPatternType(P1, X, forceInvariantRefinement = true) + val forceInvariantRefinement = !tycon.classSymbol.is(Trait) + TypeComparer.constrainPatternType(P1, X, forceInvariantRefinement = forceInvariantRefinement) debug.println( - TypeComparer.explained(_.constrainPatternType(P1, X, forceInvariantRefinement = true)) + TypeComparer.explained(_.constrainPatternType(P1, X, forceInvariantRefinement = forceInvariantRefinement)) ) } @@ -199,12 +200,11 @@ object TypeTestsCasts { def testCls = effectiveClass(testType.widen) def unboxedTestCls = effectiveClass(unboxedTestType.widen) - def unreachable(why: => String)(using Context): Boolean = { - if (flagUnrelated) - if (inMatch) report.error(em"this case is unreachable since $why", expr.srcPos) + def unreachable(why: => String)(using Context): false = + if flagUnrelated then + if inMatch then report.error(MatchCaseUnreachable(why), expr.srcPos) else report.warning(em"this will always yield false since $why", expr.srcPos) false - } /** Are `foundCls` and `testCls` classes that allow checks * whether a test would be always false? @@ -218,8 +218,9 @@ object TypeTestsCasts { !(!testCls.isPrimitiveValueClass && foundCls.isPrimitiveValueClass) && // foundCls can be `Boolean`, while testCls is `Integer` // it can happen in `(3: Boolean | Int).isInstanceOf[Int]` - !foundCls.isDerivedValueClass && !testCls.isDerivedValueClass + !foundCls.isDerivedValueClass && !testCls.isDerivedValueClass && // we don't have the logic to handle derived value classes + !ctx.platform.typeMightBeSubtypeAtRuntime(foundCls, testCls) /** Check whether a runtime test that a value of `foundCls` can be a `testCls` * can be true in some cases. Issues a warning or an error otherwise. @@ -242,12 +243,7 @@ object TypeTestsCasts { else true end check - val foundEffectiveClass = effectiveClass(expr.tpe.widen) - - if foundEffectiveClass.isPrimitiveValueClass && !testCls.isPrimitiveValueClass then - report.error(em"cannot test if value of $exprType is a reference of $testCls", tree.srcPos) - false - else foundClasses.exists(check) + foundClasses.exists(check) end checkSensical val tp = if expr.tpe.isPrimitiveValueType then defn.boxedType(expr.tpe) else expr.tpe @@ -266,7 +262,7 @@ object TypeTestsCasts { } else if (testCls.isPrimitiveValueClass) foundClsSyms match - case List(cls) if cls.isPrimitiveValueClass => + case List(cls) if cls.isPrimitiveValueClass && !ctx.platform.typeMightBeSubtypeAtRuntime(testCls, cls) => constant(expr, Literal(Constant(foundClsSyms.head == testCls))) case _ => transformIsInstanceOf( @@ -362,7 +358,7 @@ object TypeTestsCasts { val isTrusted = tree.hasAttachment(PatternMatcher.TrustedTypeTestKey) checkTypePattern(expr.tpe, argType, expr.srcPos, isTrusted) transformTypeTest(expr, argType, - flagUnrelated = enclosingInlineds.isEmpty) // if test comes from inlined code, dont't flag it even if it always false + flagUnrelated = enclosingInlineds.isEmpty) // if test comes from inlined code, don't flag it even if it always false } else if (sym.isTypeCast) transformAsInstanceOf(erasure(tree.args.head.tpe)) diff --git a/compiler/src/dotty/tools/dotc/transform/WInferUnion.scala b/compiler/src/dotty/tools/dotc/transform/WInferUnion.scala new file mode 100644 index 000000000000..de1bf23c4eb2 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/WInferUnion.scala @@ -0,0 +1,37 @@ +package dotty.tools.dotc.transform + +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.ast.tpd.InferredTypeTree +import dotty.tools.dotc.core.Contexts.{Context, ctx} +import dotty.tools.dotc.core.Decorators.em +import dotty.tools.dotc.core.Types.OrType +import dotty.tools.dotc.report +import dotty.tools.dotc.reporting.ErrorMessageID +import dotty.tools.dotc.reporting.InferUnionWarning +import dotty.tools.dotc.transform.MegaPhase.MiniPhase + +class WInferUnion extends MiniPhase { + + override def phaseName: String = WInferUnion.name + + override def description: String = WInferUnion.description + + override def isEnabled(using Context): Boolean = ctx.settings.Whas.inferUnion + + override def transformTypeApply(tree: tpd.TypeApply)(using Context): tpd.Tree = + val inferredOrTypes = tree.args.find: tpt => + tpt.isInstanceOf[InferredTypeTree] + && tpt.tpe.stripTypeVar.match + case tp: OrType if tp.isSoft => true + case _ => false + inferredOrTypes.foreach: tpt => + report.warning( + InferUnionWarning(tpt.tpe.stripTypeVar), + tpt.srcPos + ) + tree +} + +object WInferUnion: + val name = "Winfer-union" + val description = "check for type arguments inferred as union types" diff --git a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala index 1365bc36e958..3f731ec67140 100644 --- a/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala +++ b/compiler/src/dotty/tools/dotc/transform/YCheckPositions.scala @@ -17,7 +17,7 @@ class YCheckPositions extends Phase { override def description: String = YCheckPositions.description - override def run(using Context): Unit = () // YCheck only + protected def run(using Context): Unit = () // YCheck only override def checkPostCondition(tree: Tree)(using Context): Unit = tree match { diff --git a/compiler/src/dotty/tools/dotc/transform/init/Checker.scala b/compiler/src/dotty/tools/dotc/transform/init/Checker.scala index 92f55f30edff..3c520438bab2 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Checker.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Checker.scala @@ -53,7 +53,7 @@ class Checker extends Phase: units0 end runOn - def run(using Context): Unit = unsupported("run") + override protected def run(using Context): Unit = unsupported("run") class InitTreeTraverser extends TreeTraverser: private val classes: mutable.ArrayBuffer[ClassSymbol] = new mutable.ArrayBuffer diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 4548dccb598f..a9115251e14f 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -378,7 +378,7 @@ object Semantic: // ----- Checker State ----------------------------------- /** The state that threads through the interpreter */ - type Contextual[T] = (Context, Trace, Promoted, Cache.Data, Reporter) ?=> T + type Contextual[T] = (Context, Trace, Promoted, Cache.Data, Reporter, TreeCache.CacheData) ?=> T // ----- Error Handling ----------------------------------- @@ -443,6 +443,43 @@ object Semantic: inline def reporter(using r: Reporter): Reporter = r +// ----- Cache for Trees ----------------------------- + + object TreeCache: + class CacheData: + private val emptyTrees = mutable.Set[ValOrDefDef]() + private val templatesToSkip = mutable.Set[Template]() + + def checkTemplateBodyValidity(tpl: Template, className: String)(using Context): Unit = + if (templatesToSkip.contains(tpl)) + throw new TastyTreeException(className) + + val errorCount = ctx.reporter.errorCount + tpl.forceFields() + + if (ctx.reporter.errorCount > errorCount) + templatesToSkip.add(tpl) + throw new TastyTreeException(className) + + extension (tree: ValOrDefDef) + def getRhs(using Context): Tree = + def getTree: Tree = + val errorCount = ctx.reporter.errorCount + val rhs = tree.rhs + + if (ctx.reporter.errorCount > errorCount) + emptyTrees.add(tree) + report.warning("Ignoring analyses of " + tree.name + " due to error in reading TASTy.") + EmptyTree + else + rhs + + if (emptyTrees.contains(tree)) EmptyTree + else getTree + end TreeCache + + inline def treeCache(using t: TreeCache.CacheData): TreeCache.CacheData = t + // ----- Operations on domains ----------------------------- extension (a: Value) def join(b: Value): Value = @@ -562,7 +599,7 @@ object Semantic: case ref: Ref => val target = if needResolve then resolve(ref.klass, field) else field if target.is(Flags.Lazy) then - val rhs = target.defTree.asInstanceOf[ValDef].rhs + val rhs = target.defTree.asInstanceOf[ValDef].getRhs eval(rhs, ref, target.owner.asClass, cacheResult = true) else if target.exists then val obj = ref.objekt @@ -577,7 +614,7 @@ object Semantic: // return `Hot` here, errors are reported in checking `ThisRef` Hot else if target.hasSource then - val rhs = target.defTree.asInstanceOf[ValOrDefDef].rhs + val rhs = target.defTree.asInstanceOf[ValOrDefDef].getRhs eval(rhs, ref, target.owner.asClass, cacheResult = true) else val error = CallUnknown(field)(trace) @@ -622,6 +659,8 @@ object Semantic: val methodType = atPhaseBeforeTransforms { meth.info.stripPoly } var allArgsHot = true val allParamTypes = methodType.paramInfoss.flatten.map(_.repeatedToSingle) + if(allParamTypes.size != args.size) + report.warning("[Internal error] Number of parameters do not match number of arguments in " + meth.name) val errors = allParamTypes.zip(args).flatMap { (info, arg) => val tryReporter = Reporter.errorsIn { arg.promote } allArgsHot = allArgsHot && tryReporter.errors.isEmpty @@ -701,7 +740,7 @@ object Semantic: else reporter.reportAll(tryReporter.errors) extendTrace(ddef) { - eval(ddef.rhs, ref, cls, cacheResult = true) + eval(ddef.getRhs, ref, cls, cacheResult = true) } else if ref.canIgnoreMethodCall(target) then Hot @@ -768,7 +807,7 @@ object Semantic: val tpl = cls.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template] extendTrace(cls.defTree) { init(tpl, ref, cls) } else - val initCall = ddef.rhs match + val initCall = ddef.getRhs match case Block(call :: _, _) => call case call => call extendTrace(ddef) { eval(initCall, ref, cls) } @@ -787,7 +826,7 @@ object Semantic: extendTrace(cls.defTree) { eval(tpl, ref, cls, cacheResult = true) } ref else - extendTrace(ddef) { eval(ddef.rhs, ref, cls, cacheResult = true) } + extendTrace(ddef) { eval(ddef.getRhs, ref, cls, cacheResult = true) } else if ref.canIgnoreMethodCall(ctor) then Hot else @@ -897,8 +936,7 @@ object Semantic: case Cold => Cold - case ref: Ref => eval(vdef.rhs, ref, enclosingClass, cacheResult = sym.is(Flags.Lazy)) - + case ref: Ref => eval(vdef.getRhs, ref, enclosingClass, cacheResult = sym.is(Flags.Lazy)) case _ => report.error("[Internal error] unexpected this value when accessing local variable, sym = " + sym.show + ", thisValue = " + thisValue2.show + Trace.show, Trace.position) Hot @@ -1105,7 +1143,7 @@ object Semantic: * * The class to be checked must be an instantiable concrete class. */ - private def checkClass(classSym: ClassSymbol)(using Cache.Data, Context): Unit = + private def checkClass(classSym: ClassSymbol)(using Cache.Data, Context, TreeCache.CacheData): Unit = val thisRef = ThisRef(classSym) val tpl = classSym.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template] @@ -1140,8 +1178,12 @@ object Semantic: */ def checkClasses(classes: List[ClassSymbol])(using Context): Unit = given Cache.Data() + given TreeCache.CacheData() for classSym <- classes if isConcreteClass(classSym) do - checkClass(classSym) + try + checkClass(classSym) + catch + case TastyTreeException(className) => report.warning("Skipping the analysis of " + classSym.show + " due to an error reading the body of " + className + "'s TASTy.") // ----- Semantic definition -------------------------------- type ArgInfo = TraceValue[Value] @@ -1298,7 +1340,7 @@ object Semantic: } case closureDef(ddef) => - Fun(ddef.rhs, thisV, klass) + Fun(ddef.getRhs, thisV, klass) case PolyFun(body) => Fun(body, thisV, klass) @@ -1353,7 +1395,7 @@ object Semantic: case vdef : ValDef => // local val definition - eval(vdef.rhs, thisV, klass) + eval(vdef.getRhs, thisV, klass) case ddef : DefDef => // local method @@ -1475,6 +1517,8 @@ object Semantic: * @param klass The class to which the template belongs. */ def init(tpl: Template, thisV: Ref, klass: ClassSymbol): Contextual[Value] = log("init " + klass.show, printer, (_: Value).show) { + treeCache.checkTemplateBodyValidity(tpl, klass.show) + val paramsMap = tpl.constr.termParamss.flatten.map { vdef => vdef.name -> thisV.objekt.field(vdef.symbol) }.toMap @@ -1571,8 +1615,8 @@ object Semantic: // class body if thisV.isThisRef || !thisV.asInstanceOf[Warm].isPopulatingParams then tpl.body.foreach { - case vdef : ValDef if !vdef.symbol.is(Flags.Lazy) && !vdef.rhs.isEmpty => - val res = eval(vdef.rhs, thisV, klass) + case vdef : ValDef if !vdef.symbol.is(Flags.Lazy) && !vdef.getRhs.isEmpty => + val res = eval(vdef.getRhs, thisV, klass) // TODO: Improve promotion to avoid handling enum initialization specially // // The failing case is tests/init/pos/i12544.scala due to promotion failure. diff --git a/compiler/src/dotty/tools/dotc/transform/init/Util.scala b/compiler/src/dotty/tools/dotc/transform/init/Util.scala index ba2216504aef..a4741e276188 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Util.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Util.scala @@ -15,6 +15,9 @@ import config.Printers.init as printer import Trace.* object Util: + /** Exception used for errors encountered when reading TASTy. */ + case class TastyTreeException(msg: String) extends RuntimeException(msg) + /** Utility definition used for better error-reporting of argument errors */ case class TraceValue[T](value: T, trace: Trace) @@ -39,6 +42,8 @@ object Util: case Apply(fn, args) => val argTps = fn.tpe.widen match case mt: MethodType => mt.paramInfos + if (args.size != argTps.size) + report.warning("[Internal error] Number of arguments do not match number of argument types in " + tree.symbol.name) val normArgs: List[Arg] = args.zip(argTps).map { case (arg, _: ExprType) => ByNameArg(arg) case (arg, _) => arg diff --git a/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala b/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala index 9e40792895c0..4ad630b031c7 100644 --- a/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala @@ -3,18 +3,16 @@ package transform.localopt import scala.annotation.tailrec import scala.collection.mutable.ListBuffer -import scala.util.chaining.* import scala.util.matching.Regex.Match - -import PartialFunction.cond - import dotty.tools.dotc.ast.tpd.{Match => _, *} import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.core.Phases.typerPhase +import dotty.tools.dotc.reporting.BadFormatInterpolation import dotty.tools.dotc.util.Spans.Span +import dotty.tools.dotc.util.chaining.* /** Formatter string checker. */ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List[Tree])(using Context): @@ -30,8 +28,9 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List def argType(argi: Int, types: Type*): Type = require(argi < argc, s"$argi out of range picking from $types") val tpe = argTypes(argi) - types.find(t => argConformsTo(argi, tpe, t)) - .orElse(types.find(t => argConvertsTo(argi, tpe, t))) + types.find(t => t != defn.AnyType && argConformsTo(argi, tpe, t)) + .orElse(types.find(t => t != defn.AnyType && argConvertsTo(argi, tpe, t))) + .orElse(types.find(t => t == defn.AnyType && argConformsTo(argi, tpe, t))) .getOrElse { report.argError(s"Found: ${tpe.show}, Required: ${types.map(_.show).mkString(", ")}", argi) actuals += args(argi) @@ -64,53 +63,60 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List /** For N part strings and N-1 args to interpolate, normalize parts and check arg types. * - * Returns normalized part strings and args, where args correcpond to conversions in tail of parts. + * Returns normalized part strings and args, where args correspond to conversions in tail of parts. */ def checked: (List[String], List[Tree]) = val amended = ListBuffer.empty[String] val convert = ListBuffer.empty[Conversion] + def checkPart(part: String, n: Int): Unit = + val matches = formatPattern.findAllMatchIn(part) + + def insertStringConversion(): Unit = + amended += "%s" + part + val cv = Conversion.stringXn(n) + cv.accepts(argType(n-1, defn.AnyType)) + convert += cv + cv.lintToString(argTypes(n-1)) + + def errorLeading(op: Conversion) = op.errorAt(Spec): + s"conversions must follow a splice; ${Conversion.literalHelp}" + + def accept(op: Conversion): Unit = + if !op.isLeading then errorLeading(op) + op.accepts(argType(n-1, op.acceptableVariants*)) + amended += part + convert += op + op.lintToString(argTypes(n-1)) + + // after the first part, a leading specifier is required for the interpolated arg; %s is supplied if needed + if n == 0 then amended += part + else if !matches.hasNext then insertStringConversion() + else + val cv = Conversion(matches.next(), n) + if cv.isLiteral then insertStringConversion() + else if cv.isIndexed then + if cv.index.getOrElse(-1) == n then accept(cv) else insertStringConversion() + else if !cv.isError then accept(cv) + + // any remaining conversions in this part must be either literals or indexed + while matches.hasNext do + val cv = Conversion(matches.next(), n) + if n == 0 && cv.hasFlag('<') then cv.badFlag('<', "No last arg") + else if !cv.isLiteral && !cv.isIndexed then errorLeading(cv) + end checkPart + @tailrec - def loop(remaining: List[String], n: Int): Unit = - remaining match - case part0 :: more => - def badPart(t: Throwable): String = "".tap(_ => report.partError(t.getMessage.nn, index = n, offset = 0)) - val part = try StringContext.processEscapes(part0) catch badPart - val matches = formatPattern.findAllMatchIn(part) - - def insertStringConversion(): Unit = - amended += "%s" + part - convert += Conversion(formatPattern.findAllMatchIn("%s").next(), n) // improve - argType(n-1, defn.AnyType) - def errorLeading(op: Conversion) = op.errorAt(Spec)(s"conversions must follow a splice; ${Conversion.literalHelp}") - def accept(op: Conversion): Unit = - if !op.isLeading then errorLeading(op) - op.accepts(argType(n-1, op.acceptableVariants*)) - amended += part - convert += op - - // after the first part, a leading specifier is required for the interpolated arg; %s is supplied if needed - if n == 0 then amended += part - else if !matches.hasNext then insertStringConversion() - else - val cv = Conversion(matches.next(), n) - if cv.isLiteral then insertStringConversion() - else if cv.isIndexed then - if cv.index.getOrElse(-1) == n then accept(cv) else insertStringConversion() - else if !cv.isError then accept(cv) - - // any remaining conversions in this part must be either literals or indexed - while matches.hasNext do - val cv = Conversion(matches.next(), n) - if n == 0 && cv.hasFlag('<') then cv.badFlag('<', "No last arg") - else if !cv.isLiteral && !cv.isIndexed then errorLeading(cv) - - loop(more, n + 1) - case Nil => () - end loop + def loop(remaining: List[String], n: Int): Unit = remaining match + case part0 :: remaining => + def badPart(t: Throwable): String = "".tap(_ => report.partError(t.getMessage.nn, index = n, offset = 0)) + val part = try StringContext.processEscapes(part0) catch badPart + checkPart(part, n) + loop(remaining, n + 1) + 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) @@ -125,10 +131,8 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List def intOf(g: SpecGroup): Option[Int] = group(g).map(_.toInt) extension (inline value: Boolean) - inline def or(inline body: => Unit): Boolean = value || { body ; false } - inline def orElse(inline body: => Unit): Boolean = value || { body ; true } - inline def and(inline body: => Unit): Boolean = value && { body ; true } - inline def but(inline body: => Unit): Boolean = value && { body ; false } + inline infix def or(inline body: => Unit): Boolean = value || { body; false } + inline infix def and(inline body: => Unit): Boolean = value && { body; true } enum Kind: case StringXn, HashXn, BooleanXn, CharacterXn, IntegralXn, FloatingPointXn, DateTimeXn, LiteralXn, ErrorXn @@ -147,9 +151,10 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List // the conversion char is the head of the op string (but see DateTimeXn) val cc: Char = kind match - case ErrorXn => if op.isEmpty then '?' else op(0) - case DateTimeXn => if op.length > 1 then op(1) else '?' - case _ => op(0) + case ErrorXn => if op.isEmpty then '?' else op(0) + case DateTimeXn => if op.length <= 1 then '?' else op(1) + case StringXn => if op.isEmpty then 's' else op(0) // accommodate the default %s + case _ => op(0) def isIndexed: Boolean = index.nonEmpty || hasFlag('<') def isError: Boolean = kind == ErrorXn @@ -209,18 +214,40 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List // is the specifier OK with the given arg def accepts(arg: Type): Boolean = kind match - case BooleanXn => arg == defn.BooleanType orElse warningAt(CC)("Boolean format is null test for non-Boolean") - case IntegralXn => - arg == BigIntType || !cond(cc) { - case 'o' | 'x' | 'X' if hasAnyFlag("+ (") => "+ (".filter(hasFlag).foreach(bad => badFlag(bad, s"only use '$bad' for BigInt conversions to o, x, X")) ; true - } + case BooleanXn if arg != defn.BooleanType => + warningAt(CC): + """non-Boolean value formats as "true" for non-null references and boxed primitives, otherwise "false"""" + true + case IntegralXn if arg != BigIntType => + cc match + case 'o' | 'x' | 'X' if hasAnyFlag("+ (") => + "+ (".filter(hasFlag).foreach: bad => + badFlag(bad, s"only use '$bad' for BigInt conversions to o, x, X") + false case _ => true + case _ => true + + def lintToString(arg: Type): Unit = + def checkIsStringify(tp: Type): Boolean = tp.widen match + case OrType(tp1, tp2) => + checkIsStringify(tp1) || checkIsStringify(tp2) + case tp => + !(tp =:= defn.StringType) + && { + tp =:= defn.UnitType + && { warningAt(CC)("interpolated Unit value"); true } + || + !tp.isPrimitiveValueType + && { warningAt(CC)("interpolation uses toString"); true } + } + if ctx.settings.Whas.toStringInterpolated && kind == StringXn then + checkIsStringify(arg): Unit // what arg type if any does the conversion accept def acceptableVariants: List[Type] = kind match case StringXn => if hasFlag('#') then FormattableType :: Nil else defn.AnyType :: Nil - case BooleanXn => defn.BooleanType :: defn.NullType :: Nil + case BooleanXn => defn.BooleanType :: defn.NullType :: defn.AnyType :: Nil // warn if not boolean case HashXn => defn.AnyType :: Nil case CharacterXn => defn.CharType :: defn.ByteType :: defn.ShortType :: defn.IntType :: Nil case IntegralXn => defn.IntType :: defn.LongType :: defn.ByteType :: defn.ShortType :: BigIntType :: Nil @@ -249,25 +276,30 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List object Conversion: def apply(m: Match, i: Int): Conversion = - def kindOf(cc: Char) = cc match - case 's' | 'S' => StringXn - case 'h' | 'H' => HashXn - case 'b' | 'B' => BooleanXn - case 'c' | 'C' => CharacterXn - case 'd' | 'o' | - 'x' | 'X' => IntegralXn - case 'e' | 'E' | - 'f' | - 'g' | 'G' | - 'a' | 'A' => FloatingPointXn - case 't' | 'T' => DateTimeXn - case '%' | 'n' => LiteralXn - case _ => ErrorXn - end kindOf m.group(CC) match - case Some(cc) => new Conversion(m, i, kindOf(cc(0))).tap(_.verify) - case None => new Conversion(m, i, ErrorXn).tap(_.errorAt(Spec)(s"Missing conversion operator in '${m.matched}'; $literalHelp")) + case Some(cc) => + val xn = cc(0) match + case 's' | 'S' => StringXn + case 'h' | 'H' => HashXn + case 'b' | 'B' => BooleanXn + case 'c' | 'C' => CharacterXn + case 'd' | 'o' | + 'x' | 'X' => IntegralXn + case 'e' | 'E' | + 'f' | + 'g' | 'G' | + 'a' | 'A' => FloatingPointXn + case 't' | 'T' => DateTimeXn + case '%' | 'n' => LiteralXn + case _ => ErrorXn + new Conversion(m, i, xn) + .tap(_.verify) + case None => + new Conversion(m, i, ErrorXn) + .tap(_.errorAt(Spec)(s"Missing conversion operator in '${m.matched}'; $literalHelp")) end apply + // construct a default %s conversion + def stringXn(i: Int): Conversion = new Conversion(formatPattern.findAllMatchIn("%").next(), i, StringXn) val literalHelp = "use %% for literal %, %n for newline" end Conversion @@ -277,10 +309,15 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List val pos = partsElems(index).sourcePos val bgn = pos.span.start + offset val fin = if end < 0 then pos.span.end else pos.span.start + end - pos.withSpan(Span(bgn, fin, bgn)) + pos.withSpan(Span(start = bgn, end = fin, point = bgn)) extension (r: report.type) - def argError(message: String, index: Int): Unit = r.error(message, args(index).srcPos).tap(_ => reported = true) - def partError(message: String, index: Int, offset: Int, end: Int = -1): Unit = r.error(message, partPosAt(index, offset, end)).tap(_ => reported = true) - def partWarning(message: String, index: Int, offset: Int, end: Int = -1): Unit = r.warning(message, partPosAt(index, offset, end)).tap(_ => reported = true) + def argError(message: String, index: Int): Unit = + r.error(BadFormatInterpolation(message), args(index).srcPos) + .tap(_ => reported = true) + def partError(message: String, index: Int, offset: Int, end: Int = -1): Unit = + r.error(BadFormatInterpolation(message), partPosAt(index, offset, end)) + .tap(_ => reported = true) + def partWarning(message: String, index: Int, offset: Int, end: Int): Unit = + r.warning(BadFormatInterpolation(message), partPosAt(index, offset, end)) 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 7743054f5487..8afeb80ed85e 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 _ => @@ -96,16 +99,33 @@ class StringInterpolatorOpt extends MiniPhase: def mkConcat(strs: List[Literal], elems: List[Tree]): Tree = val stri = strs.iterator val elemi = elems.iterator - var result: Tree = stri.next + var result: Tree = stri.next() def concat(tree: Tree): Unit = result = result.select(defn.String_+).appliedTo(tree).withSpan(tree.span) while elemi.hasNext do - concat(elemi.next) - val str = stri.next + val elem = elemi.next() + lintToString(elem) + concat(elem) + val str = stri.next() if !str.const.stringValue.isEmpty then concat(str) result end mkConcat + def lintToString(t: Tree): Unit = + def checkIsStringify(tp: Type): Boolean = tp.widen match + case OrType(tp1, tp2) => + checkIsStringify(tp1) || checkIsStringify(tp2) + case tp => + !(tp =:= defn.StringType) + && { + tp =:= defn.UnitType + && { report.warning(bfi"interpolated Unit value", t.srcPos); true } + || + !tp.isPrimitiveValueType + && { report.warning(bfi"interpolation uses toString", t.srcPos); true } + } + if ctx.settings.Whas.toStringInterpolated then + checkIsStringify(t.tpe): Unit val sym = tree.symbol // Test names first to avoid loading scala.StringContext if not used, and common names first val isInterpolatedMethod = @@ -116,10 +136,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) @@ -168,3 +216,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 9cf94877328b..49c8a93c8425 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -45,11 +45,15 @@ import SpaceEngine.* * */ +/** A key to be used in a context property that caches the results of isSubspace checks */ +private val IsSubspaceCacheKey = new Property.Key[mutable.HashMap[(Space, Space), Boolean]] + +/** A key to track which case classes are currently being expanded in simplify, to prevent infinite recursion */ +private val ExpandingCaseClassesKey = new Property.Key[mutable.Set[Symbol]] + /** space definition */ sealed trait Space extends Showable: - @sharable private val isSubspaceCache = mutable.HashMap.empty[Space, Boolean] - def isSubspace(b: Space)(using Context): Boolean = val a = this val a2 = a.simplify @@ -57,7 +61,8 @@ sealed trait Space extends Showable: if (a ne a2) || (b ne b2) then a2.isSubspace(b2) else if a == Empty then true else if b == Empty then false - else isSubspaceCache.getOrElseUpdate(b, computeIsSubspace(a, b)) + else + ctx.property(IsSubspaceCacheKey).get.getOrElseUpdate((a, b), computeIsSubspace(a, b)) @sharable private var mySimplified: Space | Null = null @@ -128,10 +133,39 @@ object SpaceEngine { else if spaces2.corresponds(spaces)(_ eq _) then space else Or(spaces2) case typ: Typ => if decompose(typ).isEmpty then Empty - else space + else + val cls = typ.tp.classSymbol + ctx.property(ExpandingCaseClassesKey) match + case Some(expanding) + if cls.is(CaseClass) && !cls.isOneOf(AbstractOrTrait) && !expanding.contains(cls) => + expanding += cls + try + expandCaseClass(typ.tp) match + case null => space + case prod => if prod.nn.simplify == Empty then Empty else space + finally expanding -= cls + case _ => space case _ => space }) + /** Try to expand a case class type into a Prod space with its field types. + * Returns null if the expansion is not possible (no companion, custom unapply, etc). */ + private def expandCaseClass(tp: Type)(using Context): Prod | Null = + val cls = tp.classSymbol + val companion = cls.companionModule + if !companion.exists then return null + val companionRef = companion.termRef + val unapplyDenot = companionRef.member(nme.unapply) + if !unapplyDenot.exists + || unapplyDenot.hasAltWith(!_.symbol.is(Synthetic)) + || companionRef.member(nme.unapplySeq).exists + then return null + val fun = TermRef(companionRef, nme.unapply, unapplyDenot) + val arity = productArity(tp) + if arity <= 0 then return null + val sig = signature(fun, tp, arity) + Prod(tp, fun, sig.map(Typ(_, false))) + /** Remove a space if it's a subspace of remaining spaces * * Note: `dedup` will return the same result if the sequence >= 10 @@ -167,6 +201,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) @@ -181,7 +223,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 (Typ(tp1, _), Prod(tp2, fun, ss)) => @@ -460,8 +502,8 @@ object SpaceEngine { val inArray = tycon.isRef(defn.ArrayClass) || tp.translucentSuperType.isRef(defn.ArrayClass) val args2 = if isTyped && !inArray then args.map(_ => WildcardType) - else args.map(arg => erase(arg, inArray = inArray, isValue = false)) - tp.derivedAppliedType(erase(tycon, inArray, isValue = false), args2) + else args.map(arg => erase(arg, inArray = inArray, isValue = false, isTyped = false)) + tp.derivedAppliedType(erase(tycon, inArray = inArray, isValue = false, isTyped = false), args2) case tp @ OrType(tp1, tp2) => OrType(erase(tp1, inArray, isValue), erase(tp2, inArray, isValue), tp.isSoft) @@ -642,54 +684,45 @@ object SpaceEngine { // we get // <== refineUsingParent(NatT, class Succ, []) = Succ[NatT] // <== isSub(Succ[NatT] <:< Succ[Succ[]]) = false - def getAppliedClass(tp: Type): (Type, List[Type]) = tp match - case tp @ AppliedType(_: HKTypeLambda, _) => (tp, Nil) - case tp @ AppliedType(tycon: TypeRef, _) if tycon.symbol.isClass => (tp, tp.args) + def getAppliedClass(tp: Type): Type = tp match + case tp @ AppliedType(_: HKTypeLambda, _) => tp + case tp @ AppliedType(tycon: TypeRef, _) if tycon.symbol.isClass => tp case tp @ AppliedType(tycon: TypeProxy, _) => getAppliedClass(tycon.superType.applyIfParameterized(tp.args)) - case tp => (tp, Nil) - val (tp, typeArgs) = getAppliedClass(tpOriginal) - // This function is needed to get the arguments of the types that will be applied to the class. - // This is necessary because if the arguments of the types contain Nothing, - // then this can affect whether the class will be taken into account during the exhaustiveness check - def getTypeArgs(parent: Symbol, child: Symbol, typeArgs: List[Type]): List[Type] = - val superType = child.typeRef.superType - if typeArgs.exists(_.isBottomType) && superType.isInstanceOf[ClassInfo] then - val parentClass = superType.asInstanceOf[ClassInfo].declaredParents.find(_.classSymbol == parent).get - val paramTypeMap = Map.from(parentClass.argTypes.map(_.typeSymbol).zip(typeArgs)) - val substArgs = child.typeRef.typeParamSymbols.map(param => paramTypeMap.getOrElse(param, WildcardType)) - substArgs - else Nil - def getChildren(sym: Symbol, typeArgs: List[Type]): List[Symbol] = + case tp => tp + val tp = getAppliedClass(tpOriginal) + def getChildren(sym: Symbol): List[Symbol] = sym.children.flatMap { child => if child eq sym then List(sym) // i3145: sealed trait Baz, val x = new Baz {}, Baz.children returns Baz... else if tp.classSymbol == defn.TupleClass || tp.classSymbol == defn.NonEmptyTupleClass then List(child) // TupleN and TupleXXL classes are used for Tuple, but they aren't Tuple's children - else if (child.is(Private) || child.is(Sealed)) && child.isOneOf(AbstractOrTrait) then - getChildren(child, getTypeArgs(sym, child, typeArgs)) - else - val childSubstTypes = child.typeRef.applyIfParameterized(getTypeArgs(sym, child, typeArgs)) - // if a class contains a field of type Nothing, - // then it can be ignored in pattern matching, because it is impossible to obtain an instance of it - val existFieldWithBottomType = childSubstTypes.fields.exists(_.info.isBottomType) - if existFieldWithBottomType then Nil else List(child) + else if child.is(Sealed) && child.isOneOf(AbstractOrTrait) then getChildren(child) + else List(child) } - val children = trace(i"getChildren($tp)")(getChildren(tp.classSymbol, typeArgs)) + val children = trace(i"getChildren($tp)")(getChildren(tp.classSymbol)) val parts = children.map { sym => val sym1 = if (sym.is(ModuleClass)) sym.sourceModule else sym val refined = trace(i"refineUsingParent($tp, $sym1, $mixins)")(TypeOps.refineUsingParent(tp, sym1, mixins)) + def containsUninhabitedField(tp: Type): Boolean = + !tp.typeSymbol.is(ModuleClass) && tp.fields.exists { field => + !field.symbol.flags.is(Lazy) && field.info.dealias.isBottomType + } + def inhabited(tp: Type): Boolean = tp.dealias match case AndType(tp1, tp2) => !TypeComparer.provablyDisjoint(tp1, tp2) case OrType(tp1, tp2) => inhabited(tp1) || inhabited(tp2) case tp: RefinedType => inhabited(tp.parent) - case tp: TypeRef => inhabited(tp.prefix) - case _ => true + case tp: TypeRef => !containsUninhabitedField(tp) && inhabited(tp.prefix) + case tp: AppliedType => !containsUninhabitedField(tp) && inhabited(tp.tycon) + case _ => !containsUninhabitedField(tp) if inhabited(refined) then refined else NoType }.filter(_.exists) parts + case tref: TypeRef if tref.isUpperBoundedAbstract => + rec(tref.info.hiBound, mixins) case _ => ListOfNoType end rec @@ -698,14 +731,16 @@ object SpaceEngine { extension (tp: Type) def isDecomposableToChildren(using Context): Boolean = - val sym = tp.typeSymbol // e.g. Foo[List[Int]] = type Foo (i19275) val cls = tp.classSymbol // e.g. Foo[List[Int]] = class List tp.hasSimpleKind // can't decompose higher-kinded types && cls.is(Sealed) && cls.isOneOf(AbstractOrTrait) // ignore sealed non-abstract classes && !cls.hasAnonymousChild // can't name anonymous classes as counter-examples && cls.children.nonEmpty // can't decompose without children - && !sym.isOpaqueAlias // can't instantiate subclasses to conform to an opaque type (i19275) + + extension (tref: TypeRef) + def isUpperBoundedAbstract(using Context): Boolean = + tref.symbol.isAbstractOrAliasType && !tref.info.hiBound.isNothingType val ListOfNoType = List(NoType) val ListOfTypNoType = ListOfNoType.map(Typ(_, decomposed = true)) @@ -831,7 +866,11 @@ object SpaceEngine { classSym.is(Case) && { if seen.add(classSym) then productSelectorTypes(tpw, sel.srcPos).exists(isCheckable(_)) else true // recursive case class: return true and other members can still fail the check - } + } || + (tpw.isInstanceOf[TypeRef] && { + val tref = tpw.asInstanceOf[TypeRef] + tref.isUpperBoundedAbstract && isCheckable(tref.info.hiBound) + }) !sel.tpe.hasAnnotation(defn.UncheckedAnnot) && { @@ -941,9 +980,16 @@ object SpaceEngine { } def checkMatch(m: Match)(using Context): Unit = - checkMatchExhaustivityOnly(m) - if reachabilityCheckable(m.selector) then checkReachability(m) + inContext(ctx.withProperty(IsSubspaceCacheKey, Some(mutable.HashMap.empty))) { + checkMatchExhaustivityOnly(m) + if reachabilityCheckable(m.selector) then checkReachability(m) + } def checkMatchExhaustivityOnly(m: Match)(using Context): Unit = - if exhaustivityCheckable(m.selector) then checkExhaustivity(m) + inContext(ctx.withProperty(IsSubspaceCacheKey, Some(mutable.HashMap.empty))) { + if exhaustivityCheckable(m.selector) then + inContext(ctx.withProperty(ExpandingCaseClassesKey, Some(mutable.Set.empty))) { + checkExhaustivity(m) + } + } } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 1e6fbd7bed17..74e45a93cd7a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -3,7 +3,7 @@ package dotc package typer import core.* -import ast.{Trees, tpd, untpd, desugar} +import ast.{Trees, tpd, untpd, desugar, TreeTypeMap} import util.Stats.record import util.{SrcPos, NoSourcePosition} import Contexts.* @@ -26,6 +26,7 @@ import config.Feature import collection.mutable import config.Printers.{overload, typr, unapp} +import inlines.Inlines import TypeApplications.* import Annotations.Annotation @@ -124,8 +125,11 @@ object Applications { } def productSelectorTypes(tp: Type, errorPos: SrcPos)(using Context): List[Type] = { - val sels = for (n <- Iterator.from(0)) yield extractorMemberType(tp, nme.selectorName(n), errorPos) - sels.takeWhile(_.exists).toList + if tp.isError then + Nil + else + val sels = for (n <- Iterator.from(0)) yield extractorMemberType(tp, nme.selectorName(n), errorPos) + sels.takeWhile(_.exists).toList } def tupleComponentTypes(tp: Type)(using Context): List[Type] = @@ -242,7 +246,7 @@ object Applications { * parameter list, or EmptyTree if none was found. * @param fn the tree referring to the function part of this call * @param n the index of the parameter in the parameter list of the call - * @param testOnly true iff we just to find out whether a getter exists + * @param testOnly true iff we just want to find out whether a getter exists */ def findDefaultGetter(fn: Tree, n: Int, testOnly: Boolean)(using Context): Tree = def reifyPrefix(pre: Type): Tree = pre match @@ -322,21 +326,36 @@ object Applications { /** Splice new method reference `meth` into existing application `app` */ private def spliceMeth(meth: Tree, app: Tree)(using Context): Tree = app match { - case Apply(fn, args) => - // Constructors always have one leading non-implicit parameter list. - // Empty list is inserted for constructors where the first parameter list is implicit. - // - // Therefore, we need to ignore the first empty argument list. - // This is needed for the test tests/neg/i12344.scala - // - // see NamerOps.normalizeIfConstructor + case Apply(fn, args0) => + def duplicateByNameArg(arg: Tree, formal: Type): Tree = { + if formal.isByName then + // Default getter calls reuse by-name argument trees. If that argument contains + // local definitions, reusing the same tree would duplicate the same symbols. + // Use a fresh copy of those symbols here. + // see https://github.com/scala/scala3/issues/2939 + new TreeTypeMap(oldOwners = ctx.owner :: Nil, newOwners = ctx.owner :: Nil).transform(arg) + else arg + } + + val args = fn.tpe.widen match + case mt: MethodType => args0.zipWithConserve(mt.paramInfos)(duplicateByNameArg) + case _ => args0 + + // Constructors written with a leading implicit parameter list are normalized + // to have one leading non-implicit parameter list. See NamerOps.normalizeIfConstructor. + // However, a default getter for the implicit parameter will not reflect the augmented signature. + // If leading empty args is detected for this case, but the default arg getter isNullaryMethod, + // then the empty args are supplied as usual: $lessinit$greater$default$1() // if args == Nil && !fn.isInstanceOf[Apply] && app.tpe.isImplicitMethod && fn.symbol.isConstructor - then meth - else spliceMeth(meth, fn).appliedToArgs(args) + && !meth.tpe.widen.isNullaryMethod + then + meth + else + spliceMeth(meth, fn).appliedToArgs(args) case TypeApply(fn, targs) => // Note: It is important that the type arguments `targs` are passed in new trees // instead of being spliced in literally. Otherwise, a type argument to a default @@ -479,7 +498,7 @@ trait Applications extends Compatibility { case tp => args.size } - !isAnnotConstr(methRef.symbol) && + !isJavaAnnotConstr(methRef.symbol) && args.size < requiredArgNum(funType) } @@ -551,71 +570,136 @@ trait Applications extends Compatibility { def infoStr = if methType.isErroneous then "" else i": $methType" i"${err.refStr(methRef)}$infoStr" - /** Re-order arguments to correctly align named arguments */ + private type TreeList[T <: Untyped] = List[Trees.Tree[T]] + + /** Re-order arguments to correctly align named arguments + * Issue errors in the following situations: + * + * - "positional after named argument" if a positional argument follows a named + * argument and one of the following is true: + * + * - There is a formal argument before the argument position + * that has not yet been instantiated with a previous actual argument, + * (either named or positional), or + * - The formal parameter at the argument position is also mentioned + * in a subsequent named parameter. + * - "parameter already instantiated" if two named arguments have the same name or deprecated alias. + * - "does not have parameter" if a named parameter does not mention a formal + * parameter name. + */ def reorder[T <: Untyped](args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = { - /** @param pnames The list of parameter names that are missing arguments + extension [A](list: List[A]) inline def dropOne = if list.isEmpty then list else list.tail // aka list.drop(1) + + extension (dna: Annotation) + def deprecatedName: Name = + dna.argumentConstantString(0).map(_.toTermName).getOrElse(nme.NO_NAME) + def since: String = + val version = dna.argumentConstantString(1).filter(!_.isEmpty) + version.map(v => s" (since $v)").getOrElse("") + + val deprecatedNames: Map[Name, Annotation] = + val sym = methRef.symbol + val paramss = + if sym.hasAnnotation(defn.MappedAlternativeAnnot) then sym.rawParamss + else sym.paramSymss + paramss.find(_.exists(_.isTerm)) match + case Some(ps) if ps.exists(_.hasAnnotation(defn.DeprecatedNameAnnot)) => + ps.flatMap: p => + p.getAnnotation(defn.DeprecatedNameAnnot).map(p.name -> _) + .toMap + case _ => Map.empty + + extension (name: Name) + def isMatchedBy(usage: Name): Boolean = + name == usage + || deprecatedNames.get(name).exists(_.deprecatedName == usage) + def checkDeprecationOf(usage: Name, pos: SrcPos): Unit = if !ctx.isAfterTyper then + import report.deprecationWarning + for dna <- deprecatedNames.get(name) do + dna.deprecatedName match + case `name` | nme.NO_NAME if name == usage => + deprecationWarning(em"naming parameter $usage is deprecated${dna.since}", pos) + case `usage` => + deprecationWarning(em"the parameter name $usage is deprecated${dna.since}: use $name instead", pos) + case _ => + def alternative: Name = + deprecatedNames.get(name).map(_.deprecatedName).getOrElse(nme.NO_NAME) + + /** Reorder the suffix of named args per a list of required names. + * + * @param pnames The list of parameter names that are missing arguments * @param args The list of arguments that are not yet passed, or that are waiting to be dropped * @param nameToArg A map from as yet unseen names to named arguments - * @param toDrop A set of names that have already be passed as named arguments + * @param toDrop A set of names that have already been passed as named arguments + * @param missingArgs true if args were already missing, so error on positional * * For a well-typed application we have the invariants * * 1. `(args diff toDrop)` can be reordered to match `pnames` * 2. For every `(name -> arg)` in `nameToArg`, `arg` is an element of `args` + * + * Recurse over the parameter names looking for named args to use. + * If there are no more parameters or no args fit, process the next arg: + * a named arg may be previously used, or not yet used, or badly named. */ - def handleNamed(pnames: List[Name], args: List[Trees.Tree[T]], - nameToArg: Map[Name, Trees.NamedArg[T]], toDrop: Set[Name]): List[Trees.Tree[T]] = pnames match { - case pname :: pnames1 if nameToArg contains pname => - // there is a named argument for this parameter; pick it - nameToArg(pname) :: handleNamed(pnames1, args, nameToArg - pname, toDrop + pname) + def handleNamed(pnames: List[Name], args: TreeList[T], + nameToArg: Map[Name, Trees.NamedArg[T]], toDrop: Set[Name]): TreeList[T] = pnames match { + case pname :: pnames if nameToArg.contains(pname) => + val arg = nameToArg(pname) // use the named argument for this parameter + pname.checkDeprecationOf(pname, arg.srcPos) + arg :: handleNamed(pnames, args, nameToArg - pname, toDrop + pname) + case pname :: pnames if nameToArg.contains(pname.alternative) => + val alt = pname.alternative + val arg = nameToArg(alt) // use the named argument for this parameter + pname.checkDeprecationOf(alt, arg.srcPos) + arg :: handleNamed(pnames, args, nameToArg - alt, toDrop + alt) case _ => - def pnamesRest = if (pnames.isEmpty) pnames else pnames.tail - args match { - case (arg @ NamedArg(aname, _)) :: args1 => - if (toDrop contains aname) // argument is already passed - handleNamed(pnames, args1, nameToArg, toDrop - aname) - else if ((nameToArg contains aname) && pnames.nonEmpty) // argument is missing, pass an empty tree - genericEmptyTree :: handleNamed(pnames.tail, args, nameToArg, toDrop) - else { // name not (or no longer) available for named arg - def msg = - if (methodType.paramNames contains aname) - em"parameter $aname of $methString is already instantiated" - else - em"$methString does not have a parameter $aname" - fail(msg, arg.asInstanceOf[Arg]) - arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop) - } - case arg :: args1 => - arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop) // unnamed argument; pick it - case Nil => // no more args, continue to pick up any preceding named args - if (pnames.isEmpty) Nil - else handleNamed(pnamesRest, args, nameToArg, toDrop) - } + args match + case allArgs @ (arg @ NamedArg(aname, _)) :: args => + if toDrop.contains(aname) then + // named argument was already picked (using aname), skip it + handleNamed(pnames, args, nameToArg, toDrop - aname) + else if pnames.nonEmpty && nameToArg.contains(aname) then + // argument for pname is missing, pass an empty tree; arg may be used later, so keep it + genericEmptyTree :: handleNamed(pnames.tail, allArgs, nameToArg, toDrop) + else // name not (or no longer) available for named arg + def msg = + if methodType.paramNames.exists(nm => nm == aname || nm.alternative == aname) then + em"parameter $aname of $methString is already instantiated" + else + em"$methString does not have a parameter $aname" + fail(msg, arg.asInstanceOf[Arg]) + arg :: handleNamed(pnames.dropOne, args, nameToArg, toDrop) + case arg :: args => + if toDrop.nonEmpty then + report.error(i"positional after named argument", arg.srcPos) + arg :: handleNamed(pnames.dropOne, args, nameToArg, toDrop) // unnamed argument; pick it + case nil => // no more args, continue to pick up any preceding named args + if pnames.isEmpty then nil + else handleNamed(pnames.dropOne, args = nil, nameToArg, toDrop) } - def handlePositional(pnames: List[Name], args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = - args match { - case (arg: NamedArg @unchecked) :: _ => - val nameAssocs = for (case arg @ NamedArg(name, _) <- args) yield (name, arg) - handleNamed(pnames, args, nameAssocs.toMap, Set()) - case arg :: args1 => - arg :: handlePositional(if (pnames.isEmpty) Nil else pnames.tail, args1) - case Nil => Nil - } + // Skip prefix of positional args, then handleNamed + def handlePositional(pnames: List[Name], args: TreeList[T]): TreeList[T] = + args match + case (arg @ NamedArg(name, _)) :: args if !pnames.isEmpty && pnames.head.isMatchedBy(name) => + pnames.head.checkDeprecationOf(name, arg.srcPos) + arg :: handlePositional(pnames.tail, args) + case (_: NamedArg) :: _ => + val nameAssocs = args.collect { case arg @ NamedArg(name, _) => name -> arg } + handleNamed(pnames, args, nameAssocs.toMap, toDrop = Set.empty) + case arg :: args => + arg :: handlePositional(pnames.dropOne, args) + case nil => nil handlePositional(methodType.paramNames, args) - } + } // end reorder /** Is `sym` a constructor of a Java-defined annotation? */ def isJavaAnnotConstr(sym: Symbol): Boolean = sym.is(JavaDefined) && sym.isConstructor && sym.owner.is(JavaAnnotation) - - /** Is `sym` a constructor of an annotation? */ - def isAnnotConstr(sym: Symbol): Boolean = - sym.isConstructor && sym.owner.isAnnotation - /** Match re-ordered arguments against formal parameters * @param n The position of the first parameter in formals in `methType`. */ @@ -676,6 +760,11 @@ trait Applications extends Compatibility { def implicitArg = implicitArgTree(formal, appPos.span) if !defaultArg.isEmpty then + if methodType.isImplicitMethod && ctx.mode.is(Mode.ImplicitsEnabled) + && !inferImplicitArg(formal, appPos.span).tpe.isError + then + report.warning(DefaultShadowsGiven(methodType.paramNames(n)), appPos) + defaultArg.tpe.widen match case _: MethodOrPoly if testOnly => matchArgs(args1, formals1, n + 1) case _ => matchArgs(args1, addTyped(treeToArg(defaultArg)), n + 1) @@ -756,7 +845,71 @@ trait Applications extends Compatibility { case SAMType(samMeth, samParent) => argtpe <:< samMeth.toFunctionType(isJava = samParent.classSymbol.is(JavaDefined)) case _ => false - isCompatible(argtpe, formal) + // For overload resolution, allow arguments with wildcard type parameters to match + // formal parameters. This handles cases where isCompatible fails because the + // argument type has wildcards in positions where the formal type has concrete types + // or type variables. + // + // In the example below, the non-varargs overload (1) should be preferred. + // While Class[? <: Object] is not a subtype of Class[T], + // for applicability we check if the wildcard's bounds are compatible with the formal. + // + // ```scala + // def blub[T](a: Class[? <: T]): String = "a" // (1) + // def blub[T](a: Class[T], ints: Int*): String = "b" (2) + // blub(classOf[Object]) // classOf[Object]: Class[? <: Object] + // ``` + // + // The case where argtpe has wildcard type isn't handled by isCompatible: + // When (where Class is invariant): (1) + // - formal = Class[? <: String] + // - argtpe = Class[String] + // isCompatible returns true because Class[String] <:< Class[? <: String] (String is a valid "some T <: String"). + // On the other hand, when (2) + // - formal = Class[String] + // - argtpe = Class[? <: String] + // isCompatible returns false because Class[? <: String] <:< Class[String] doesn't hold. + // + // However, for overload resolution, we want to check applicability: + // "could this work with some type instantiation?" (yes, if ? = String) + def wildcardArgOK = + argtpe match + case at @ AppliedType(tycon1, args1) if at.hasWildcardArg => + formal match + case AppliedType(tycon2, args2) + if tycon1 =:= tycon2 && args1.length == args2.length => + // We need to handle all 4 cases, in addition to + // `case (TypeBounds(_, hi), formal)` case where arg has wildcard, and formal is concrete: + // because if argtpe has a wildcard type, and formal has concrete at the same position, + // isComptible immediately returns false: + // In the example below, argtpe = Codec[?, String, ? <: Format] + // and formal = Codec[L, H, F <: Format]. + // isCompatible immediately returns false, because of the first type arguments. + // wildcardArgOK needs to handle all combinations of TypeBounds on argtpe and formal. + // + // ```scala + // trait Format + // trait Codec[L, H, F <: Format] // invariant + // + // def decode[L, H](codec: Codec[L, H, ? <: Format]): Int = 1 + // def decode[L, H](other: String): Int = 2 + // decode(null: Codec[?, String, ? <: Format]) // should return 1 + // ``` + args1.lazyZip(args2).forall { + case (arg: TypeBounds, formal: TypeBounds) => + (formal.lo relaxed_<:< arg.lo) && + (arg.hi relaxed_<:< formal.hi) + case (TypeBounds(_, hi), formal) => + hi relaxed_<:< formal + case (arg, formal: TypeBounds) => + (formal.lo relaxed_<:< arg) && (arg relaxed_<:< formal.hi) + case (a, b) => + a =:= b + } + case _ => false + case _ => false + + isCompatible(argtpe, formal) || wildcardArgOK // Only allow SAM-conversion to PartialFunction if implicit conversions // are enabled. This is necessary to avoid ambiguity between an overload // taking a PartialFunction and one taking a Function1 because @@ -843,7 +996,7 @@ trait Applications extends Compatibility { def makeVarArg(n: Int, elemFormal: Type): Unit = { val args = typedArgBuf.takeRight(n).toList typedArgBuf.dropRightInPlace(n) - val elemtpt = TypeTree(elemFormal) + val elemtpt = TypeTree(elemFormal.normalizedTupleType) typedArgBuf += seqToRepeated(SeqLiteral(args, elemtpt)) } @@ -899,14 +1052,34 @@ trait Applications extends Compatibility { isPureExpr(arg) || arg.isInstanceOf[RefTree | Apply | TypeApply] && arg.symbol.name.is(DefaultGetterName) - val result: Tree = { + def defaultsAddendum(args: List[Tree]): Unit = + def usesDefault(arg: Tree): Boolean = arg match + case TypeApply(Select(_, name), _) => name.is(DefaultGetterName) + case Apply(Select(_, name), _) => name.is(DefaultGetterName) + case Apply(fun, _) => usesDefault(fun) + case _ => false + ctx.reporter.mapBufferedMessages{ + case dia: Diagnostic.Error => + dia.msg match + case msg: TypeMismatch + if msg.inTree.exists{ t => + args.exists(arg => arg.span == t.span && usesDefault(arg)) + } => + val noteText = i"Error occurred in an application involving default arguments." + val explained = i"Expanded application: ${cpy.Apply(app)(normalizedFun, args)}" + Diagnostic.Error(msg.append(s"\n$noteText").appendExplanation(s"\n\n$explained"), dia.pos) + case msg => dia + case dia => dia + } + val result: Tree = { var typedArgs = typedArgBuf.toList def app0 = cpy.Apply(app)(normalizedFun, typedArgs) // needs to be a `def` because typedArgs can change later val app1 = - if !success then app0.withType(UnspecifiedErrorType) + if !success then + if typedArgs.exists(_.tpe.isError) then + defaultsAddendum(typedArgs) + app0.withType(UnspecifiedErrorType) else { - if isAnnotConstr(methRef.symbol) && !isJavaAnnotConstr(methRef.symbol) then - typedArgs if !sameSeq(args, orderedArgs) && !isJavaAnnotConstr(methRef.symbol) && !typedArgs.forall(isSafeArg) @@ -923,8 +1096,8 @@ trait Applications extends Compatibility { // with all non-explicit default parameters at the end in declaration order. val orderedArgDefs = { // Indices of original typed arguments that are lifted by liftArgs - val impureArgIndices = typedArgBuf.zipWithIndex.collect { - case (arg, idx) if !lifter.noLift(arg) => idx + val impureArgIndices = typedArgBuf.lazyZip(methodType.paramInfos).zipWithIndex.collect { + case ((arg, tp), idx) if lifter.wouldLift(arg, tp) => idx } def position(arg: Trees.Tree[T]) = { val i = args.indexOf(arg) @@ -1111,9 +1284,8 @@ trait Applications extends Compatibility { // // summonFrom { // case given A[t] => - // summonFrom + // summonFrom: // case given `t` => ... - // } // } // // the second `summonFrom` should expand only once the first `summonFrom` is @@ -1282,14 +1454,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 @@ -1347,7 +1516,7 @@ trait Applications extends Compatibility { val widened = TypeComparer.dropTransparentTraits( tree.tpe.parents.reduceLeft(TypeComparer.andType(_, _)), pt) - if widened <:< pt then Typed(tree, TypeTree(widened)) + if widened <:< pt then Typed(tree, TypeTree(widened, inferred = true)) else tree else tree @@ -1583,7 +1752,7 @@ trait Applications extends Compatibility { for (argType <- argTypes) assert(!isBounds(argType), unapplyApp.tpe.show) val bunchedArgs = argTypes match { case argType :: Nil => - if (args.lengthCompare(1) > 0 && Feature.autoTuplingEnabled && defn.isTupleNType(argType)) untpd.Tuple(args) :: Nil + if (args.lengthCompare(1) > 0 && Feature.autoTuplingEnabled && defn.isTupleNType(argType.normalizedTupleType)) untpd.Tuple(args) :: Nil else args case _ => args } @@ -1766,13 +1935,15 @@ trait Applications extends Compatibility { tp1 match case tp1: MethodType => // (1) tp1.paramInfos.isEmpty && tp2.isInstanceOf[LambdaType] - || { + || ( + !alt1.symbol.is(ExtensionMethod) || alt2.symbol.is(ExtensionMethod) + ) && ( if tp1.isVarArgsMethod then tp2.isVarArgsMethod && isApplicableMethodRef(alt2, tp1.paramInfos.map(_.repeatedToSingle), WildcardType, ArgMatch.Compatible) else isApplicableMethodRef(alt2, tp1.paramInfos, WildcardType, ArgMatch.Compatible) - } + ) case tp1: PolyType => // (2) inContext(ctx.fresh.setExploreTyperState()) { // Fully define the PolyType parameters so that the infos of the @@ -2248,10 +2419,12 @@ trait Applications extends Compatibility { deepPt match case pt @ FunProto(_, PolyProto(targs, resType)) => // try to narrow further with snd argument list and following type params + pretypeArgs(candidates, pt) resolveMapped(candidates, skipParamClause(pt.typedArgs().tpes, targs.tpes), resType) case pt @ FunProto(_, resType: FunOrPolyProto) => // try to narrow further with snd argument list + pretypeArgs(candidates, pt) resolveMapped(candidates, skipParamClause(pt.typedArgs().tpes, Nil), resType) case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index bf99def353b0..db76a2c354df 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -274,11 +274,16 @@ object Checking { */ def checkInfo(tp: Type): Type = tp match { case tp @ TypeAlias(alias) => - tp.derivedAlias(checkPart(alias, "alias")) + val lo1 = atVariance(-1)(checkPart(alias, "alias")) + val hi1 = checkUpper(alias, "alias") + if lo1 eq hi1 then + tp.derivedAlias(lo1) + else + tp.derivedTypeBounds(lo1, hi1) case tp @ MatchAlias(alias) => - tp.derivedAlias(checkUpper(alias, "match")) + tp.derivedAlias(atVariance(0)(checkUpper(alias, "match"))) case tp @ TypeBounds(lo, hi) => - tp.derivedTypeBounds(checkPart(lo, "lower bound"), checkUpper(hi, "upper bound")) + tp.derivedTypeBounds(atVariance(-1)(checkPart(lo, "lower bound")), checkUpper(hi, "upper bound")) case _ => tp } @@ -299,12 +304,12 @@ object Checking { case tp: TermRef => this(tp.info) mapOver(tp) - case tp @ AppliedType(tycon, args) => - tp.derivedAppliedType(this(tycon), args.mapConserve(this(_, nestedCycleOK, nestedCycleOK))) case tp @ RefinedType(parent, name, rinfo) => tp.derivedRefinedType(this(parent), name, this(rinfo, nestedCycleOK, nestedCycleOK)) case tp: RecType => tp.rebind(this(tp.parent)) + case tp: LazyRef => + tp case tp @ TypeRef(pre, _) => try { // A prefix is interesting if it might contain (transitively) a reference @@ -337,14 +342,17 @@ object Checking { if isInteresting(pre) then CyclicReference.trace(i"explore ${tp.symbol} for cyclic references"): - val pre1 = this(pre, false, false) + val pre1 = atVariance(variance max 0)(this(pre, false, false)) if locked.contains(tp) || tp.symbol.infoOrCompleter.isInstanceOf[NoCompleter] + && tp.symbol == sym then throw CyclicReference(tp.symbol) locked += tp try - if tp.symbol.isOpaqueAlias then + if tp.symbol.infoOrCompleter.isInstanceOf[NoCompleter] then + ; // skip checking info (and avoid forcing the symbol with .isOpaqueAlias/etc) + else if tp.symbol.isOpaqueAlias then checkInfo(TypeAlias(tp.translucentSuperType)) else if !tp.symbol.isClass then checkInfo(tp.info) @@ -362,6 +370,16 @@ object Checking { } case _ => mapOver(tp) } + + override def mapArg(arg: Type, tparam: ParamInfo): Type = + val varianceDiff = variance != tparam.paramVarianceSign + atVariance(variance * tparam.paramVarianceSign): + // Using tests/pos/i22257.scala as an example, + // if we consider FP's lower-bound of Fixed[Node] + // than `Node` is a type argument in contravariant + // position, while the type parameter is covariant. + val nestedCycleOK1 = nestedCycleOK || variance != 0 && varianceDiff + this(arg, nestedCycleOK, nestedCycleOK1) } /** Under -Yrequire-targetName, if `sym` has an operator name, check that it has a @@ -702,7 +720,7 @@ object Checking { } /** Verify classes extending AnyVal meet the requirements */ - def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(using Context): Unit = { + def checkDerivedValueClass(cdef: untpd.TypeDef, clazz: Symbol, stats: List[Tree])(using Context): Unit = { def checkValueClassMember(stat: Tree) = stat match { case _: TypeDef if stat.symbol.isClass => report.error(ValueClassesMayNotDefineInner(clazz, stat.symbol), stat.srcPos) @@ -715,6 +733,14 @@ object Checking { case _ => report.error(ValueClassesMayNotContainInitalization(clazz), stat.srcPos) } + inline def checkParentIsNotAnyValAlias(): Unit = + cdef.rhs match { + case impl: Template => + val parent = impl.parents.head + if parent.symbol.isAliasType && parent.typeOpt.dealias =:= defn.AnyValType then + report.error(ValueClassCannotExtendAliasOfAnyVal(clazz, parent.symbol), cdef.srcPos) + case _ => () + } // We don't check synthesised enum anonymous classes that are generated from // enum extending a value class type (AnyVal or an alias of it) // The error message 'EnumMayNotBeValueClassesID' will take care of generating the error message (See #22236) @@ -729,6 +755,9 @@ object Checking { report.error(ValueClassesMayNotBeAbstract(clazz), clazz.srcPos) if (!clazz.isStatic) report.error(ValueClassesMayNotBeContainted(clazz), clazz.srcPos) + + checkParentIsNotAnyValAlias() + if (isDerivedValueClass(underlyingOfValueClass(clazz.asClass).classSymbol)) report.error(ValueClassesMayNotWrapAnotherValueClass(clazz), clazz.srcPos) else { @@ -737,6 +766,8 @@ object Checking { } clParamAccessors match { case param :: params => + if (defn.isContextFunctionType(param.info)) + report.error("value classes are not allowed for context function types", param.srcPos) if (param.is(Mutable)) report.error(ValueClassParameterMayNotBeAVar(clazz, param), param.srcPos) if (param.info.isInstanceOf[ExprType]) @@ -943,6 +974,8 @@ trait Checking { def recur(pat: Tree, pt: Type): Boolean = !sourceVersion.isAtLeast(`3.2`) || pt.hasAnnotation(defn.UncheckedAnnot) + || pat.tpe.isErroneous // avoid spurious warning + || pt.isErroneous || { patmatch.println(i"check irrefutable $pat: ${pat.tpe} against $pt") pat match @@ -1144,7 +1177,7 @@ trait Checking { typr.println(i"check no double declarations $cls") def checkDecl(decl: Symbol): Unit = { - for (other <- seen(decl.name) if !decl.isAbsent() && !other.isAbsent()) { + for (other <- seen(decl.name) if decl.name != nme.ERROR && !decl.isAbsent() && !other.isAbsent()) { typr.println(i"conflict? $decl $other") def javaFieldMethodPair = decl.is(JavaDefined) && other.is(JavaDefined) && @@ -1208,8 +1241,8 @@ trait Checking { else tpt /** Verify classes extending AnyVal meet the requirements */ - def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(using Context): Unit = - Checking.checkDerivedValueClass(clazz, stats) + def checkDerivedValueClass(cdef: untpd.TypeDef, clazz: Symbol, stats: List[Tree])(using Context): Unit = + Checking.checkDerivedValueClass(cdef, clazz, stats) /** Check that case classes are not inherited by case classes. */ @@ -1580,7 +1613,7 @@ trait NoChecking extends ReChecking { override def checkNoTargetNameConflict(stats: List[Tree])(using Context): Unit = () override def checkParentCall(call: Tree, caller: ClassSymbol)(using Context): Unit = () override def checkSimpleKinded(tpt: Tree)(using Context): Tree = tpt - override def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(using Context): Unit = () + override def checkDerivedValueClass(cdef: untpd.TypeDef, clazz: Symbol, stats: List[Tree])(using Context): Unit = () override def checkCaseInheritance(parentSym: Symbol, caseCls: ClassSymbol, pos: SrcPos)(using Context): Unit = () override def checkNoForwardDependencies(vparams: List[ValDef])(using Context): Unit = () override def checkMembersOK(tp: Type, pos: SrcPos)(using Context): Type = tp diff --git a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala index ed96e3cacd4b..523a15fb770f 100644 --- a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala @@ -37,6 +37,11 @@ class CrossVersionChecks extends MiniPhase: for annot <- sym.annotations if annot.symbol.isExperimental do Feature.checkExperimentalDef(annot.symbol, annot.tree) + private def checkDeprecatedAnnots(sym: Symbol)(using Context): Unit = + if sym.exists then + for annot <- sym.annotations if annot.symbol.isDeprecated do + checkDeprecatedRef(annot.symbol, annot.tree.srcPos) + /** If @migration is present (indicating that the symbol has changed semantics between versions), * emit a warning. */ @@ -74,26 +79,30 @@ class CrossVersionChecks extends MiniPhase: for parent <- parents psym = parent.tpe.classSymbol annot <- psym.getAnnotation(defn.DeprecatedInheritanceAnnot) - if !skipWarning(psym) + if !skipDeprecation(psym) do val msg = annot.argumentConstantString(0).map(msg => s": $msg").getOrElse("") val since = annot.argumentConstantString(1).map(version => s" (since: $version)").getOrElse("") - report.deprecationWarning(em"inheritance from $psym is deprecated$since$msg", parent.srcPos, origin=psym.showFullName) + val composed = em"inheritance from $psym is deprecated$since$msg" + report.deprecationWarning(composed, parent.srcPos, origin = psym.showFullName) } override def transformValDef(tree: ValDef)(using Context): ValDef = checkDeprecatedOvers(tree) checkExperimentalAnnots(tree.symbol) + checkDeprecatedAnnots(tree.symbol) tree override def transformDefDef(tree: DefDef)(using Context): DefDef = checkDeprecatedOvers(tree) checkExperimentalAnnots(tree.symbol) + checkDeprecatedAnnots(tree.symbol) tree override def transformTypeDef(tree: TypeDef)(using Context): TypeDef = // TODO do we need to check checkDeprecatedOvers(tree)? checkExperimentalAnnots(tree.symbol) + checkDeprecatedAnnots(tree.symbol) tree override def transformTemplate(tree: tpd.Template)(using Context): tpd.Tree = @@ -166,16 +175,19 @@ object CrossVersionChecks: * Also check for deprecation of the companion class for synthetic methods in the companion module. */ private[CrossVersionChecks] def checkDeprecatedRef(sym: Symbol, pos: SrcPos)(using Context): Unit = - def maybeWarn(annotee: Symbol, annot: Annotation) = if !skipWarning(sym) then + def warn(annotee: Symbol, annot: Annotation) = val message = annot.argumentConstantString(0).filter(!_.isEmpty).map(": " + _).getOrElse("") val since = annot.argumentConstantString(1).filter(!_.isEmpty).map(" since " + _).getOrElse("") - report.deprecationWarning(em"${annotee.showLocated} is deprecated${since}${message}", pos, origin=annotee.showFullName) + val composed = em"${annotee.showLocated} is deprecated${since}${message}" + report.deprecationWarning(composed, pos, origin = annotee.showFullName) sym.getAnnotation(defn.DeprecatedAnnot) match - case Some(annot) => maybeWarn(sym, annot) + case Some(annot) => if !skipDeprecation(sym) then warn(sym, annot) case _ => if sym.isAllOf(SyntheticMethod) then val companion = sym.owner.companionClass - if companion.is(CaseClass) then companion.getAnnotation(defn.DeprecatedAnnot).foreach(maybeWarn(companion, _)) + if companion.is(CaseClass) then + for annot <- companion.getAnnotation(defn.DeprecatedAnnot) if !skipDeprecation(sym) do + warn(companion, annot) /** Decide whether the deprecation of `sym` should be ignored in this context. * @@ -195,7 +207,7 @@ object CrossVersionChecks: * class (or its companion) is either the deprecated case class * or the case class of the deprecated element. */ - private def skipWarning(sym: Symbol)(using Context): Boolean = + def skipDeprecation(sym: Symbol)(using Context): Boolean = // is the owner an enum or its companion and also the owner of sym def isEnumOwner(owner: Symbol)(using Context) = @@ -223,4 +235,4 @@ object CrossVersionChecks: owner.is(Synthetic) && symIsCaseOrMember siteIsSyntheticCaseClassMember || siteIsEnclosedByDeprecatedElement - end skipWarning + end skipDeprecation diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index cc940ee38e41..febb12cf1007 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -10,7 +10,7 @@ import Contexts.*, Symbols.*, Types.*, SymDenotations.*, Names.*, NameOps.*, Fla import ProtoTypes.*, ContextOps.* import util.Spans.* import util.SrcPos -import collection.mutable +import collection.mutable.ListBuffer import ErrorReporting.errorTree /** A typer mixin that implements type class derivation functionality */ @@ -25,8 +25,8 @@ trait Deriving { */ class Deriver(cls: ClassSymbol, codePos: SrcPos)(using Context) { - /** A buffer for synthesized symbols for type class instances */ - private var synthetics = new mutable.ListBuffer[Symbol] + /** A buffer for synthesized symbols for type class instances, with what user asked to synthesize. */ + private val synthetics = ListBuffer.empty[(tpd.Tree, Symbol)] /** A version of Type#underlyingClassRef that works also for higher-kinded types */ private def underlyingClassRef(tp: Type): Type = tp match { @@ -41,7 +41,7 @@ trait Deriving { * an instance with the same name does not exist already. * @param reportErrors Report an error if an instance with the same name exists already */ - private def addDerivedInstance(clsName: Name, info: Type, pos: SrcPos): Unit = { + private def addDerivedInstance(derived: tpd.Tree, clsName: Name, info: Type, pos: SrcPos): Unit = { val instanceName = "derived$".concat(clsName) if (ctx.denotNamed(instanceName).exists) report.error(em"duplicate type class derivation for $clsName", pos) @@ -50,9 +50,8 @@ trait Deriving { // derived instance will have too low a priority to be selected over a freshly // derived instance at the summoning site. val flags = if info.isInstanceOf[MethodOrPoly] then GivenMethod else Given | Lazy - synthetics += - newSymbol(ctx.owner, instanceName, flags, info, coord = pos.span) - .entered + val sym = newSymbol(ctx.owner, instanceName, flags, info, coord = pos.span).entered + synthetics += derived -> sym } /** Check derived type tree `derived` for the following well-formedness conditions: @@ -77,7 +76,8 @@ trait Deriving { * that have the same name but different prefixes through selective aliasing. */ private def processDerivedInstance(derived: untpd.Tree): Unit = { - val originalTypeClassType = typedAheadType(derived, AnyTypeConstructorProto).tpe + val originalTypeClassTree = typedAheadType(derived, AnyTypeConstructorProto) + val originalTypeClassType = originalTypeClassTree.tpe val underlyingClassType = underlyingClassRef(originalTypeClassType) val typeClassType = checkClassType( underlyingClassType.orElse(originalTypeClassType), @@ -100,7 +100,7 @@ trait Deriving { val derivedInfo = if derivedParams.isEmpty then monoInfo else PolyType.fromParams(derivedParams, monoInfo) - addDerivedInstance(originalTypeClassType.typeSymbol.name, derivedInfo, derived.srcPos) + addDerivedInstance(originalTypeClassTree, originalTypeClassType.typeSymbol.name, derivedInfo, derived.srcPos) } def deriveSingleParameter: Unit = { @@ -312,7 +312,7 @@ trait Deriving { else tpd.ValDef(sym.asTerm, typeclassInstance(sym)(Nil)) } - synthetics.map(syntheticDef).toList + synthetics.map((t, s) => syntheticDef(s).withAttachment(Deriving.OriginalTypeClass, t)).toList } def finalize(stat: tpd.TypeDef): tpd.Tree = { @@ -321,3 +321,8 @@ trait Deriving { } } } +object Deriving: + import dotty.tools.dotc.util.Property + + /** Attachment holding the name of a type class as written by the user. */ + val OriginalTypeClass = Property.StickyKey[tpd.Tree] diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 14cc7bf963a6..afc9e2c08f46 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -43,9 +43,9 @@ object DynamicUnapply { def unapply(tree: tpd.Tree): Option[List[tpd.Tree]] = tree match case TypeApply(Select(qual, name), _) if name == nme.asInstanceOfPM => unapply(qual) - case Apply(Apply(Select(selectable, fname), Literal(Constant(name)) :: ctag :: Nil), _ :: implicits) + case Apply(Apply(Select(selectable, fname), Literal(Constant(name)) :: restArgs), _ :: implicits) if fname == nme.applyDynamic && (name == "unapply" || name == "unapplySeq") => - Some(selectable :: ctag :: implicits) + Some(selectable :: (restArgs ::: implicits)) case _ => None } @@ -136,8 +136,11 @@ trait Dynamic { typedDynamicAssign(qual, name, sel.span, Nil) case TypeApply(sel @ Select(qual, name), targs) if !isDynamicMethod(name) => typedDynamicAssign(qual, name, sel.span, targs) - case _ => - errorTree(tree, ReassignmentToVal(tree.lhs.symbol.name)) + case lhs => + val name = lhs match + case nt: NameTree => nt.name + case _ => lhs.symbol.name + errorTree(tree, ReassignmentToVal(name, pt)) } } diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 33ee68cc9986..5539555da9c3 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -180,12 +180,21 @@ object ErrorReporting { // use normalized types if that also shows an error, and both sides stripped // the same number of context functions. Use original types otherwise. - def missingElse = tree match + def moreNotes = tree match case If(_, _, elsep @ Literal(Constant(()))) if elsep.span.isSynthetic => - "\nMaybe you are missing an else part for the conditional?" - case _ => "" - - errorTree(tree, TypeMismatch(treeTp, expectedTp, Some(tree), implicitFailure.whyNoConversion, missingElse)) + "\n\nMaybe you are missing an else part for the conditional?" :: Nil + case Literal(Constant(())) if tree.span.isZeroExtent => + ctx.tree match + case Block(stats, EmptyTree) if stats.nonEmpty => + def after = stats.last match + case stat: MemberDef => i" after the definition of `${stat.name}`" + case _ => "" + i"\n\nMaybe the enclosing block is missing a final expression$after?" :: Nil + case _ => + Nil + case _ => + Nil + errorTree(tree, TypeMismatch(treeTp, expectedTp, Some(tree), implicitFailure.whyNoConversion, moreNotes.mkString)) } /** A subtype log explaining why `found` does not conform to `expected` */ diff --git a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala index 26d03db4b7dc..783d43fd5961 100644 --- a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -33,18 +33,29 @@ abstract class Lifter { /** The corresponding lifter for pass-by-name arguments */ protected def exprLifter: Lifter = NoLift + private def lifterFor(paramType: Type): Lifter = + if paramType.isInstanceOf[ExprType] then exprLifter else this + + /** Returns true if the argument would be lifted for the given parameter type. */ + def wouldLift(arg: tpd.Tree, paramType: Type)(using Context): Boolean = + !paramType.hasAnnotation(defn.InlineParamAnnot) && !lifterFor(paramType).noLift(arg) + /** The flags of a lifted definition */ protected def liftedFlags: FlagSet = EmptyFlags /** The tree of a lifted definition */ protected def liftedDef(sym: TermSymbol, rhs: Tree)(using Context): MemberDef = ValDef(sym, rhs) + /** Type assigned to a lifted temporary symbol. */ + protected def liftedExprType(expr: Tree)(using Context): Type = + expr.tpe.widen.deskolemized + private def lift(defs: mutable.ListBuffer[Tree], expr: Tree, prefix: TermName = EmptyTermName)(using Context): Tree = if (noLift(expr)) expr else { val name = UniqueName.fresh(prefix) // don't instantiate here, as the type params could be further constrained, see tests/pos/pickleinf.scala - var liftedType = expr.tpe.widen.deskolemized + var liftedType = liftedExprType(expr) if (liftedFlags.is(Method)) liftedType = ExprType(liftedType) val lifted = newSymbol(ctx.owner, name, liftedFlags | Synthetic, liftedType, coord = spanCoord(expr.span), // Lifted definitions will be added to a local block, so they need to be @@ -90,8 +101,7 @@ abstract class Lifter { args.lazyZip(mt.paramNames).lazyZip(mt.paramInfos).map { (arg, name, tp) => if tp.hasAnnotation(defn.InlineParamAnnot) then arg else - val lifter = if (tp.isInstanceOf[ExprType]) exprLifter else this - lifter.liftArg(defs, arg, if (name.firstPart contains '$') EmptyTermName else name) + lifterFor(tp).liftArg(defs, arg, if name.firstPart.contains('$') then EmptyTermName else name) } case _ => args.mapConserve(liftArg(defs, _)) @@ -173,7 +183,6 @@ object LiftImpure extends LiftImpure /** Lift all impure or complex arguments */ class LiftComplex extends Lifter { def noLift(expr: tpd.Tree)(using Context): Boolean = tpd.isPurePath(expr) - override def exprLifter: Lifter = LiftToDefs } object LiftComplex extends LiftComplex @@ -207,6 +216,16 @@ object LiftCoverage extends LiftImpure { override def noLift(expr: tpd.Tree)(using Context) = if liftingArgs then noLiftArg(expr) else super.noLift(expr) + /** Preserve singleton precision for lifted coverage temps when the underlying value is a + * compile-time constant (same notion ConstFold uses), so constant re-folding after lifting + * still matches the original inferred singleton type. Everything else uses the base widen. + */ + override protected def liftedExprType(expr: tpd.Tree)(using Context): Type = + val dealiased = expr.tpe.dealias.deskolemized + dealiased.widenTermRefExpr.normalized.simplified match + case _: ConstantType => dealiased + case _ => super.liftedExprType(expr) + def liftForCoverage(defs: mutable.ListBuffer[tpd.Tree], tree: tpd.Apply)(using Context) = { val liftedFun = liftApp(defs, tree.fun) val liftedArgs = liftArgs(defs, tree.fun.tpe, tree.args)(using liftingArgsContext) @@ -214,12 +233,6 @@ object LiftCoverage extends LiftImpure { } } -/** Lift all impure or complex arguments to `def`s */ -object LiftToDefs extends LiftComplex { - override def liftedFlags: FlagSet = Method - override def liftedDef(sym: TermSymbol, rhs: tpd.Tree)(using Context): tpd.DefDef = tpd.DefDef(sym, rhs) -} - /** Lifter for eta expansion */ object EtaExpansion extends LiftImpure { import tpd.* diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 69237c2aa493..60fbe218d904 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -413,6 +413,7 @@ object Implicits: /** A failed search */ case class SearchFailure(tree: Tree) extends SearchResult { + require(tree.tpe.isInstanceOf[SearchFailureType], s"unexpected type for ${tree}") final def isAmbiguous: Boolean = tree.tpe.isInstanceOf[AmbiguousImplicits | TooUnspecific] final def reason: SearchFailureType = tree.tpe.asInstanceOf[SearchFailureType] } @@ -790,7 +791,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)) @@ -802,6 +803,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) @@ -1250,11 +1255,15 @@ trait Implicits: val history = ctx.searchHistory.nest(cand, pt) val typingCtx = nestedContext().setNewTyperState().setFreshGADTBounds.setSearchHistory(history) + val alreadyStoppedInlining = ctx.base.stopInlining val result = typedImplicit(cand, pt, argument, span)(using typingCtx) result match case res: SearchSuccess => ctx.searchHistory.defineBynameImplicit(wideProto, res) case _ => + if !alreadyStoppedInlining && ctx.base.stopInlining then + // a call overflowed as part of the expansion when typing the implicit + ctx.base.stopInlining = false // Since the search failed, the local typerstate will be discarded // without being committed, but type variables local to that state // might still appear in an error message, so we run `gc()` here to @@ -1291,7 +1300,7 @@ trait Implicits: // Fall back: if both results are extension method applications, // compare the extension methods instead of their wrappers. def stripExtension(alt: SearchSuccess) = methPart(stripApply(alt.tree)).tpe - (stripExtension(alt1), stripExtension(alt2)) match + ((stripExtension(alt1), stripExtension(alt2)) : @unchecked) match case (ref1: TermRef, ref2: TermRef) => // ref1 and ref2 might refer to type variables owned by // alt1.tstate and alt2.tstate respectively, to compare the diff --git a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala index 1c30f44c29cf..20a48edba757 100644 --- a/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala +++ b/compiler/src/dotty/tools/dotc/typer/ImportSuggestions.scala @@ -68,7 +68,7 @@ trait ImportSuggestions: && !(root.name == nme.raw.BAR && ctx.settings.scalajs.value && root == JSDefinitions.jsdefn.PseudoUnionModule) } - def nestedRoots(site: Type)(using Context): List[Symbol] = + def nestedRoots(site: Type, parentSymbols: Set[Symbol])(using Context): List[Symbol] = val seenNames = mutable.Set[Name]() site.baseClasses.flatMap { bc => bc.info.decls.filter { dcl => @@ -78,34 +78,37 @@ trait ImportSuggestions: } } - def rootsStrictlyIn(ref: Type)(using Context): List[TermRef] = + def rootsStrictlyIn(ref: Type, parentSymbols: Set[Symbol] = Set())(using Context): List[TermRef] = val site = ref.widen val refSym = site.typeSymbol - val nested = - if refSym.is(Package) then - if refSym == defn.EmptyPackageClass // Don't search the empty package - || refSym == defn.JavaPackageClass // As an optimization, don't search java... - || refSym == defn.JavaLangPackageClass // ... or java.lang. - then Nil - else refSym.info.decls.filter(lookInside) - else if refSym.infoOrCompleter.isInstanceOf[StubInfo] then - Nil // Don't chase roots that do not exist - else - if !refSym.is(Touched) then - refSym.ensureCompleted() // JavaDefined is reliably known only after completion - if refSym.is(JavaDefined) then Nil - else nestedRoots(site) - nested - .map(mbr => TermRef(ref, mbr.asTerm)) - .flatMap(rootsIn) - .toList + if parentSymbols.contains(refSym) then Nil + else + val nested = + if refSym.is(Package) then + if refSym == defn.EmptyPackageClass // Don't search the empty package + || refSym == defn.JavaPackageClass // As an optimization, don't search java... + || refSym == defn.JavaLangPackageClass // ... or java.lang. + then Nil + else refSym.info.decls.filter(lookInside) + else if refSym.infoOrCompleter.isInstanceOf[StubInfo] then + Nil // Don't chase roots that do not exist + else + if !refSym.is(Touched) then + refSym.ensureCompleted() // JavaDefined is reliably known only after completion + if refSym.is(JavaDefined) then Nil + else nestedRoots(site, parentSymbols) + val newParentSymbols = parentSymbols + refSym + nested + .map(mbr => TermRef(ref, mbr.asTerm)) + .flatMap(rootsIn(_, newParentSymbols)) + .toList - def rootsIn(ref: TermRef)(using Context): List[TermRef] = + def rootsIn(ref: TermRef, parentSymbols: Set[Symbol] = Set())(using Context): List[TermRef] = if seen.contains(ref) then Nil else implicitsDetailed.println(i"search for suggestions in ${ref.symbol.fullName}") seen += ref - ref :: rootsStrictlyIn(ref) + ref :: rootsStrictlyIn(ref, parentSymbols) def rootsOnPath(tp: Type)(using Context): List[TermRef] = tp match case ref: TermRef => rootsIn(ref) ::: rootsOnPath(ref.prefix) diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 7acc704fd00d..ffef8e0638cd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -442,10 +442,15 @@ object Inferencing { } } } - val res = patternBindings.toList.map { (boundSym, _) => + val res = patternBindings.toList.map { (boundSym, origin) => // substitute bounds of pattern bound variables to deal with possible F-bounds for (wildCard, param) <- patternBindings do boundSym.info = boundSym.info.substParam(param, wildCard.typeRef) + + // also substitute in any GADT bounds + // e.g. in i22879, replace the `T` in `X <: Iterable[T]` with the pattern bound `T$1` + ctx.gadtState.replace(origin, boundSym.typeRef) + boundSym } @@ -604,7 +609,6 @@ trait Inferencing { this: Typer => // This is needed because it could establish singleton type upper bounds. See i2998.scala. val tp = tree.tpe.widen - val vs = variances(tp, pt) // Avoid interpolating variables occurring in tree's type if typerstate has unreported errors. // Reason: The errors might reflect unsatisfiable constraints. In that @@ -628,135 +632,138 @@ trait Inferencing { this: Typer => // val y: List[List[String]] = List(List(1)) if state.reporter.hasUnreportedErrors then return tree - def constraint = state.constraint - - trace(i"interpolateTypeVars($tree: ${tree.tpe}, $pt, $qualifying)", typr, (_: Any) => i"$qualifying\n$constraint\n${ctx.gadt}") { - //println(i"$constraint") - //println(i"${ctx.gadt}") - - /** Values of this type report type variables to instantiate with variance indication: - * +1 variable appears covariantly, can be instantiated from lower bound - * -1 variable appears contravariantly, can be instantiated from upper bound - * 0 variable does not appear at all, can be instantiated from either bound - */ - type ToInstantiate = List[(TypeVar, Int)] - - val toInstantiate: ToInstantiate = - val buf = new mutable.ListBuffer[(TypeVar, Int)] - for tvar <- qualifying do - if !tvar.isInstantiated && constraint.contains(tvar) && tvar.nestingLevel >= ctx.nestingLevel then - constrainIfDependentParamRef(tvar, tree) - if !tvar.isInstantiated then - // isInstantiated needs to be checked again, since previous interpolations could already have - // instantiated `tvar` through unification. - val v = vs.computedVariance(tvar) - if v == null then buf += ((tvar, 0)) - else if v.intValue != 0 then buf += ((tvar, v.intValue)) - else comparing(cmp => - if !cmp.levelOK(tvar.nestingLevel, ctx.nestingLevel) then - // Invariant: The type of a tree whose enclosing scope is level - // N only contains type variables of level <= N. - typr.println(i"instantiate nonvariant $tvar of level ${tvar.nestingLevel} to a type variable of level <= ${ctx.nestingLevel}, $constraint") - cmp.atLevel(ctx.nestingLevel, tvar.origin) - else - typr.println(i"no interpolation for nonvariant $tvar in $state") - ) - // constrainIfDependentParamRef could also have instantiated tvars added to buf before the check - buf.filterNot(_._1.isInstantiated).toList - end toInstantiate - - def typeVarsIn(xs: ToInstantiate): TypeVars = - xs.foldLeft(SimpleIdentitySet.empty: TypeVars)((tvs, tvi) => tvs + tvi._1) - - /** Filter list of proposed instantiations so that they don't constrain further - * the current constraint. - */ - def filterByDeps(tvs0: ToInstantiate): ToInstantiate = - val excluded = // ignore dependencies from other variables that are being instantiated - typeVarsIn(tvs0) - def step(tvs: ToInstantiate): ToInstantiate = tvs match - case tvs @ (hd @ (tvar, v)) :: tvs1 => - def aboveOK = !constraint.dependsOn(tvar, excluded, co = true) - def belowOK = !constraint.dependsOn(tvar, excluded, co = false) - if v == 0 && !aboveOK then - step((tvar, 1) :: tvs1) - else if v == 0 && !belowOK then - step((tvar, -1) :: tvs1) - else if v == -1 && !aboveOK || v == 1 && !belowOK then - typr.println(i"drop $tvar, $v in $tp, $pt, qualifying = ${qualifying.toList}, tvs0 = ${tvs0.toList}%, %, excluded = ${excluded.toList}, $constraint") - step(tvs1) - else // no conflict, keep the instantiation proposal - tvs.derivedCons(hd, step(tvs1)) - case Nil => - Nil - val tvs1 = step(tvs0) - if tvs1 eq tvs0 then tvs1 - else filterByDeps(tvs1) // filter again with smaller excluded set - end filterByDeps - - /** Instantiate all type variables in `tvs` in the indicated directions, - * as described in the doc comment of `ToInstantiate`. - * If a type variable A is instantiated from below, and there is another - * type variable B in `buf` that is known to be smaller than A, wait and - * instantiate all other type variables before trying to instantiate A again. - * Dually, wait instantiating a type variable from above as long as it has - * upper bounds in `buf`. - * - * This is done to avoid loss of precision when forming unions. An example - * is in i7558.scala: - * - * type Tr[+V1, +O1 <: V1] - * extension [V2, O2 <: V2](tr: Tr[V2, O2]) def sl: Tr[V2, O2] = ??? - * def as[V3, O3 <: V3](tr: Tr[V3, O3]) : Tr[V3, O3] = tr.sl - * - * Here we interpolate at some point V2 and O2 given the constraint - * - * V2 >: V3, O2 >: O3, O2 <: V2 - * - * where O3 and V3 are type refs with O3 <: V3. - * If we interpolate V2 first to V3 | O2, the widenUnion algorithm will - * instantiate O2 to V3, leading to the final constraint - * - * V2 := V3, O2 := V3 - * - * But if we instantiate O2 first to O3, and V2 next to V3, we get the - * more flexible instantiation - * - * V2 := V3, O2 := O3 - */ - def doInstantiate(tvs: ToInstantiate): Unit = - - /** Try to instantiate `tvs`, return any suspended type variables */ - def tryInstantiate(tvs: ToInstantiate): ToInstantiate = tvs match - case (hd @ (tvar, v)) :: tvs1 => - val fromBelow = v == 1 || (v == 0 && tvar.hasLowerBound) - typr.println( - i"interpolate${if v == 0 then " non-occurring" else ""} $tvar in $state in $tree: $tp, fromBelow = $fromBelow, $constraint") - if tvar.isInstantiated then - tryInstantiate(tvs1) - else - val suspend = tvs1.exists{ (following, _) => - if fromBelow - then constraint.isLess(following.origin, tvar.origin) - else constraint.isLess(tvar.origin, following.origin) - } - if suspend then - typr.println(i"suspended: $hd") - hd :: tryInstantiate(tvs1) - else - tvar.instantiate(fromBelow) - tryInstantiate(tvs1) - case Nil => Nil - if tvs.nonEmpty then doInstantiate(tryInstantiate(tvs)) - end doInstantiate - - doInstantiate(filterByDeps(toInstantiate)) - } + instantiateTypeVars(tp, pt, qualifying, tree) } end if tree end interpolateTypeVars + def instantiateTypeVars(tp: Type, pt: Type, qualifying: List[TypeVar], tree: Tree = EmptyTree)(using Context): Unit = + trace(i"instantiateTypeVars($tp, $pt, $qualifying, $tree)", typr): + val state = ctx.typerState + def constraint = state.constraint + + val vs = variances(tp, pt) + + /** Values of this type report type variables to instantiate with variance indication: + * +1 variable appears covariantly, can be instantiated from lower bound + * -1 variable appears contravariantly, can be instantiated from upper bound + * 0 variable does not appear at all, can be instantiated from either bound + */ + type ToInstantiate = List[(TypeVar, Int)] + + val toInstantiate: ToInstantiate = + val buf = new mutable.ListBuffer[(TypeVar, Int)] + for tvar <- qualifying do + if !tvar.isInstantiated && constraint.contains(tvar) && tvar.nestingLevel >= ctx.nestingLevel then + constrainIfDependentParamRef(tvar, tree) + if !tvar.isInstantiated then + // isInstantiated needs to be checked again, since previous interpolations could already have + // instantiated `tvar` through unification. + val v = vs.computedVariance(tvar) + if v == null then buf += ((tvar, 0)) + else if v.intValue != 0 then buf += ((tvar, v.intValue)) + else comparing(cmp => + if !cmp.levelOK(tvar.nestingLevel, ctx.nestingLevel) then + // Invariant: The type of a tree whose enclosing scope is level + // N only contains type variables of level <= N. + typr.println(i"instantiate nonvariant $tvar of level ${tvar.nestingLevel} to a type variable of level <= ${ctx.nestingLevel}, $constraint") + cmp.atLevel(ctx.nestingLevel, tvar.origin) + else + typr.println(i"no interpolation for nonvariant $tvar in $state") + ) + // constrainIfDependentParamRef could also have instantiated tvars added to buf before the check + buf.filterNot(_._1.isInstantiated).toList + end toInstantiate + + def typeVarsIn(xs: ToInstantiate): TypeVars = + xs.foldLeft(SimpleIdentitySet.empty: TypeVars)((tvs, tvi) => tvs + tvi._1) + + /** Filter list of proposed instantiations so that they don't constrain further + * the current constraint. + */ + def filterByDeps(tvs0: ToInstantiate): ToInstantiate = + val excluded = // ignore dependencies from other variables that are being instantiated + typeVarsIn(tvs0) + def step(tvs: ToInstantiate): ToInstantiate = tvs match + case tvs @ (hd @ (tvar, v)) :: tvs1 => + def aboveOK = !constraint.dependsOn(tvar, excluded, co = true) + def belowOK = !constraint.dependsOn(tvar, excluded, co = false) + if v == 0 && !aboveOK then + step((tvar, 1) :: tvs1) + else if v == 0 && !belowOK then + step((tvar, -1) :: tvs1) + else if v == -1 && !aboveOK || v == 1 && !belowOK then + typr.println(i"drop $tvar, $v in $tp, $pt, qualifying = ${qualifying.toList}, tvs0 = ${tvs0.toList}%, %, excluded = ${excluded.toList}, $constraint") + step(tvs1) + else // no conflict, keep the instantiation proposal + tvs.derivedCons(hd, step(tvs1)) + case Nil => + Nil + val tvs1 = step(tvs0) + if tvs1 eq tvs0 then tvs1 + else filterByDeps(tvs1) // filter again with smaller excluded set + end filterByDeps + + /** Instantiate all type variables in `tvs` in the indicated directions, + * as described in the doc comment of `ToInstantiate`. + * If a type variable A is instantiated from below, and there is another + * type variable B in `buf` that is known to be smaller than A, wait and + * instantiate all other type variables before trying to instantiate A again. + * Dually, wait instantiating a type variable from above as long as it has + * upper bounds in `buf`. + * + * This is done to avoid loss of precision when forming unions. An example + * is in i7558.scala: + * + * type Tr[+V1, +O1 <: V1] + * extension [V2, O2 <: V2](tr: Tr[V2, O2]) def sl: Tr[V2, O2] = ??? + * def as[V3, O3 <: V3](tr: Tr[V3, O3]) : Tr[V3, O3] = tr.sl + * + * Here we interpolate at some point V2 and O2 given the constraint + * + * V2 >: V3, O2 >: O3, O2 <: V2 + * + * where O3 and V3 are type refs with O3 <: V3. + * If we interpolate V2 first to V3 | O2, the widenUnion algorithm will + * instantiate O2 to V3, leading to the final constraint + * + * V2 := V3, O2 := V3 + * + * But if we instantiate O2 first to O3, and V2 next to V3, we get the + * more flexible instantiation + * + * V2 := V3, O2 := O3 + */ + def doInstantiate(tvs: ToInstantiate): Unit = + + /** Try to instantiate `tvs`, return any suspended type variables */ + def tryInstantiate(tvs: ToInstantiate): ToInstantiate = tvs match + case (hd @ (tvar, v)) :: tvs1 => + val fromBelow = v == 1 || (v == 0 && tvar.hasLowerBound) + typr.println( + i"interpolate${if v == 0 then " non-occurring" else ""} $tvar in $state in $tree: $tp, fromBelow = $fromBelow, $constraint") + if tvar.isInstantiated then + tryInstantiate(tvs1) + else + val suspend = tvs1.exists{ (following, _) => + if fromBelow + then constraint.isLess(following.origin, tvar.origin) + else constraint.isLess(tvar.origin, following.origin) + } + if suspend then + typr.println(i"suspended: $hd") + hd :: tryInstantiate(tvs1) + else + tvar.instantiate(fromBelow) + tryInstantiate(tvs1) + case Nil => Nil + if tvs.nonEmpty then doInstantiate(tryInstantiate(tvs)) + end doInstantiate + + doInstantiate(filterByDeps(toInstantiate)) + end instantiateTypeVars + /** If `tvar` represents a parameter of a dependent method type in the current `call` * approximate it from below with the type of the actual argument. Skolemize that * type if necessary to make it a Singleton. diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 0471bb6a432a..72dd64bb2774 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -12,7 +12,7 @@ import ast.desugar, ast.desugar.* import ProtoTypes.* import util.Spans.* import util.Property -import collection.mutable +import collection.mutable, mutable.ListBuffer import tpd.tpes import Variances.alwaysInvariant import config.{Config, Feature} @@ -131,7 +131,7 @@ class Namer { typer: Typer => * The logic here is very subtle and fragile due to the fact that * we are not allowed to force anything. */ - def checkNoConflict(name: Name, isPrivate: Boolean, span: Span)(using Context): Name = + def checkNoConflict(name: Name, span: Span)(using Context): Name = val owner = ctx.owner var conflictsDetected = false @@ -166,7 +166,7 @@ class Namer { typer: Typer => def preExisting = ctx.effectiveScope.lookup(name) if (!owner.isClass || name.isTypeName) && preExisting.exists then conflict(preExisting) - else if owner.isPackageObject && !isPrivate && name != nme.CONSTRUCTOR then + else if owner.isPackageObject && name != nme.CONSTRUCTOR then checkNoConflictIn(owner.owner) for pkgObj <- pkgObjs(owner.owner) if pkgObj != owner do checkNoConflictIn(pkgObj) @@ -242,7 +242,7 @@ class Namer { typer: Typer => tree match { case tree: TypeDef if tree.isClassDef => val flags = checkFlags(tree.mods.flags) - val name = checkNoConflict(tree.name, flags.is(Private), tree.span).asTypeName + val name = checkNoConflict(tree.name, tree.span).asTypeName val cls = createOrRefine[ClassSymbol](tree, name, flags, ctx.owner, cls => adjustIfModule(new ClassCompleter(cls, tree)(ctx), tree), @@ -251,7 +251,7 @@ class Namer { typer: Typer => cls case tree: MemberDef => var flags = checkFlags(tree.mods.flags) - val name = checkNoConflict(tree.name, flags.is(Private), tree.span) + val name = checkNoConflict(tree.name, tree.span) tree match case tree: ValOrDefDef => if tree.isInstanceOf[ValDef] && !flags.is(Param) && name.endsWith("_=") then @@ -694,7 +694,13 @@ class Namer { typer: Typer => enterSymbol(classConstructorCompanion(classSym.asClass)) else for moduleSym <- companionVals do - if moduleSym.is(Module) && !moduleSym.isDefinedInCurrentRun then + // by not going through `.lastKnownDenotation` (instead using `.current`), + // we guarantee that the `moduleSym` will be brought forward to the current run, + // rendering `moduleSym.isDefinedInCurrentRun` as always true. + // We want to regenerate the companion instead of bringing it forward, + // as even if we are able to bring forward the object symbol, + // we might not be able to do the same with its stale module class symbol (see `tests/pos/i20449`) + if moduleSym.lastKnownDenotation.is(Module) && !moduleSym.isDefinedInCurrentRun then val companion = if needsConstructorProxies(classSym) then classConstructorCompanion(classSym.asClass) @@ -1083,7 +1089,8 @@ class Namer { typer: Typer => end typeSig } - class ClassCompleter(cls: ClassSymbol, original: TypeDef)(ictx: Context) extends Completer(original)(ictx) { + class ClassCompleter(cls: ClassSymbol, original: TypeDef)(ictx: Context) + extends Completer(original)(ictx), CompleterWithCleanup { withDecls(newScope(using ictx)) protected implicit val completerCtx: Context = localContext(cls) @@ -1109,7 +1116,7 @@ class Namer { typer: Typer => * extension method. */ private def exportForwarders(exp: Export, pathMethod: Symbol)(using Context): List[tpd.MemberDef] = - val buf = new mutable.ListBuffer[tpd.MemberDef] + val buf = ListBuffer.empty[tpd.MemberDef] val Export(expr, selectors) = exp if expr.isEmpty then report.error(em"Export selector must have prefix and `.`", exp.srcPos) @@ -1130,11 +1137,26 @@ class Namer { typer: Typer => def canForward(mbr: SingleDenotation, alias: TermName): CanForward = { import CanForward.* val sym = mbr.symbol + /** + * The export selects a member of the current class (issue #22147). + * Assumes that cls.classInfo.selfType.derivesFrom(sym.owner) is true. + */ + def isCurrentClassMember: Boolean = expr match + case id: (Ident | This) => // Access through self type or this + /* Given the usage context below, where cls's self type is a subtype of sym.owner, + it suffices to check if symbol is the same class. */ + cls == id.symbol + case _ => false if !sym.isAccessibleFrom(pathType) then No("is not accessible") else if sym.isConstructor || sym.is(ModuleClass) || sym.is(Bridge) || sym.is(ConstructorProxy) || sym.isAllOf(JavaModule) then Skip - else if cls.derivesFrom(sym.owner) && (sym.owner == cls || !sym.is(Deferred)) then + // if the cls is a subclass or mixes in the owner of the symbol + // and either + // * the symbols owner is the cls itself + // * the symbol is not a deferred symbol + // * the symbol is a member of the current class (#22147) + else if cls.classInfo.selfType.derivesFrom(sym.owner) && (sym.owner == cls || !sym.is(Deferred) || isCurrentClassMember) then No(i"is already a member of $cls") else if pathMethod.exists && mbr.isType then No("is a type, so it cannot be exported as extension method") @@ -1155,25 +1177,26 @@ class Namer { typer: Typer => Yes } - def foreachDefaultGetterOf(sym: TermSymbol, op: TermSymbol => Unit): Unit = + def foreachDefaultGetterOf(sym: TermSymbol, alias: TermName)(op: (TermSymbol, TermName) => Unit): Unit = var n = 0 - val methodName = - if sym.name == nme.apply && sym.is(Synthetic) && sym.owner.companionClass.is(Case) then - // The synthesized `apply` methods of case classes use the constructor's default getters - nme.CONSTRUCTOR - else sym.name + // The synthesized `apply` methods of case classes use the constructor's default getters + val useConstructor = sym.name == nme.apply && sym.is(Synthetic) && sym.owner.companionClass.is(Case) + val methodName = if useConstructor then nme.CONSTRUCTOR else sym.name + val aliasedName = if useConstructor then nme.CONSTRUCTOR else alias + val useAliased = !useConstructor && methodName != aliasedName for params <- sym.paramSymss; param <- params do if param.isTerm then if param.is(HasDefault) then val getterName = DefaultGetterName(methodName, n) val getter = pathType.member(getterName).symbol assert(getter.exists, i"$path does not have a default getter named $getterName") - op(getter.asTerm) + val targetName = if useAliased then DefaultGetterName(aliasedName, n) else getterName + op(getter.asTerm, targetName) n += 1 /** Add a forwarder with name `alias` or its type name equivalent to `mbr`, - * provided `mbr` is accessible and of the right implicit/non-implicit kind. - */ + * provided `mbr` is accessible and of the right implicit/non-implicit kind. + */ def addForwarder(alias: TermName, mbr: SingleDenotation, span: Span): Unit = def adaptForwarderParams(acc: List[List[tpd.Tree]], tp: Type, prefss: List[List[tpd.Tree]]) @@ -1196,7 +1219,7 @@ class Namer { typer: Typer => val hasDefaults = sym.hasDefaultParams // compute here to ensure HasDefaultParams and NoDefaultParams flags are set val forwarder = if mbr.isType then - val forwarderName = checkNoConflict(alias.toTypeName, isPrivate = false, span) + val forwarderName = checkNoConflict(alias.toTypeName, span) var target = pathType.select(sym) if target.typeParams.nonEmpty then target = target.EtaExpand(target.typeParams) @@ -1250,7 +1273,7 @@ class Namer { typer: Typer => (EmptyFlags, mbrInfo) var mbrFlags = MandatoryExportTermFlags | maybeStable | (sym.flags & RetainedExportTermFlags) if pathMethod.exists then mbrFlags |= ExtensionMethod - val forwarderName = checkNoConflict(alias, isPrivate = false, span) + val forwarderName = checkNoConflict(alias, span) newSymbol(cls, forwarderName, mbrFlags, mbrInfo, coord = span) forwarder.info = avoidPrivateLeaks(forwarder) @@ -1274,7 +1297,7 @@ class Namer { typer: Typer => val ddef = tpd.DefDef(forwarder.asTerm, prefss => { val forwarderCtx = ctx.withOwner(forwarder) val (pathRefss, methRefss) = prefss.splitAt(extensionParamsCount(path.tpe.widen)) - val ref = path.appliedToArgss(pathRefss).select(sym.asTerm) + val ref = path.appliedToArgss(pathRefss).select(sym.asTerm).withSpan(span.focus) val rhs = ref.appliedToArgss(adaptForwarderParams(Nil, sym.info, methRefss)) .etaExpandCFT(using forwarderCtx) if forwarder.isInlineMethod then @@ -1289,9 +1312,8 @@ class Namer { typer: Typer => }) buf += ddef.withSpan(span) if hasDefaults then - foreachDefaultGetterOf(sym.asTerm, - getter => addForwarder( - getter.name.asTermName, getter.asSeenFrom(path.tpe), span)) + foreachDefaultGetterOf(sym.asTerm, alias): (getter, getterName) => + addForwarder(getterName, getter.asSeenFrom(path.tpe), span) // adding annotations and flags at the parameter level // TODO: This probably needs to be filtered to avoid adding some annotation @@ -1346,13 +1368,13 @@ class Namer { typer: Typer => addWildcardForwardersNamed(alias, span) def addForwarders(sels: List[untpd.ImportSelector], seen: List[TermName]): Unit = sels match - case sel :: sels1 => + case sel :: sels => if sel.isWildcard then addWildcardForwarders(seen, sel.span) else if sel.rename != nme.WILDCARD then addForwardersNamed(sel.name, sel.rename, sel.span) - addForwarders(sels1, sel.name :: seen) + addForwarders(sels, sel.name :: seen) case _ => /** Avoid a clash of export forwarder `forwarder` with other forwarders in `forwarders`. @@ -1418,12 +1440,17 @@ class Namer { typer: Typer => forwarders end exportForwarders - /** Add forwarders as required by the export statements in this class */ - private def processExports(using Context): Unit = + /** Add forwarders as required by the export statements in this class. + * @return true if forwarders were added + */ + private def processExports(using Context): Boolean = + + var exported = false def processExport(exp: Export, pathSym: Symbol)(using Context): Unit = for forwarder <- exportForwarders(exp, pathSym) do forwarder.symbol.entered + exported = true def exportPathSym(path: Tree, ext: ExtMethods)(using Context): Symbol = def fail(msg: String): Symbol = @@ -1466,6 +1493,8 @@ class Namer { typer: Typer => // import contexts for nothing. if hasExport(rest) then process(rest) + // was a forwarder entered for an export + exported end processExports /** Ensure constructor is completed so that any parameter accessors @@ -1667,9 +1696,11 @@ class Namer { typer: Typer => cls.invalidateMemberCaches() // we might have checked for a member when parents were not known yet. cls.setNoInitsFlags(parentsKind(parents), untpd.bodyKind(rest)) cls.setStableConstructor() - processExports(using localCtx) defn.patchStdLibClass(cls) addConstructorProxies(cls) + if processExports(using localCtx) then + addConstructorProxies(cls) + cleanup() } } @@ -1677,7 +1708,7 @@ class Namer { typer: Typer => private enum CanForward: case Yes case No(whyNot: String) - case Skip // for members that have never forwarders + case Skip // for members that never have forwarders class SuspendCompleter extends LazyType, SymbolLoaders.SecondCompleter { @@ -1977,9 +2008,15 @@ class Namer { typer: Typer => // `default-getter-variance.scala`. AnnotatedType(defaultTp, Annotation(defn.UncheckedVarianceAnnot, sym.span)) else + inline def isJavaEnumValue = tp match + case tp: TermRef => tp.termSymbol.isAllOf(JavaDefined | Enum) + case _ => false // don't strip @uncheckedVariance annot for default getters TypeOps.simplify(tp.widenTermRefExpr, - if defaultTp.exists then TypeOps.SimplifyKeepUnchecked() else null) match + if defaultTp.exists then TypeOps.SimplifyKeepUnchecked() else null) + match + // For final members pointing to Java enum values, preserve the singleton type. See i24750. + case _ if sym.is(Final) && isJavaEnumValue => tp case ctp: ConstantType if sym.isInlineVal => ctp case tp => TypeComparer.widenInferred(tp, pt, widenUnions = true) diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 9694822bc0d5..904f366549d4 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -69,13 +69,6 @@ object ProtoTypes { |constraint was: ${ctx.typerState.constraint} |constraint now: ${newctx.typerState.constraint}""") if result && (ctx.typerState.constraint ne newctx.typerState.constraint) then - // Remove all type lambdas and tvars introduced by testCompat - for tvar <- newctx.typerState.ownedVars do - inContext(newctx): - if !tvar.isInstantiated then - tvar.instantiate(fromBelow = false) // any direction - - // commit any remaining changes in typer state newctx.typerState.commit() result case _ => testCompat @@ -398,21 +391,28 @@ object ProtoTypes { * - t2 is a ascription (t22: T) and t1 is at the outside of t22 * - t2 is a closure (...) => t22 and t1 is at the outside of t22 */ - def hasInnerErrors(t: Tree)(using Context): Boolean = t match - case Typed(expr, tpe) => hasInnerErrors(expr) - case closureDef(mdef) => hasInnerErrors(mdef.rhs) + def hasInnerErrors(t: Tree, argType: Type)(using Context): Boolean = t match + case Typed(expr, tpe) => hasInnerErrors(expr, argType) + case closureDef(mdef) => hasInnerErrors(mdef.rhs, argType) case _ => t.existsSubTree { t1 => if t1.typeOpt.isError && t.span.toSynthetic != t1.span.toSynthetic && t.typeOpt != t1.typeOpt then typr.println(i"error subtree $t1 of $t with ${t1.typeOpt}, spans = ${t1.span}, ${t.span}") - true + t1.typeOpt match + case errorType: ErrorType if errorType.msg.isInstanceOf[TypeMismatchMsg] => + // if error is caused by an argument type mismatch, + // then return false to try to find an extension. + // see i20335.scala for test case. + val typeMismtachMsg = errorType.msg.asInstanceOf[TypeMismatchMsg] + argType != typeMismtachMsg.expected + case _ => true else false } - private def cacheTypedArg(arg: untpd.Tree, typerFn: untpd.Tree => Tree, force: Boolean)(using Context): Tree = { + private def cacheTypedArg(arg: untpd.Tree, typerFn: untpd.Tree => Tree, force: Boolean, argType: Type)(using Context): Tree = { var targ = state.typedArg(arg) if (targ == null) untpd.functionWithUnknownParamType(arg) match { @@ -430,7 +430,7 @@ object ProtoTypes { targ = typerFn(arg) // TODO: investigate why flow typing is not working on `targ` if ctx.reporter.hasUnreportedErrors then - if hasInnerErrors(targ.nn) then + if hasInnerErrors(targ.nn, argType) then state.errorArgs += arg else state.typedArg = state.typedArg.updated(arg, targ.nn) @@ -458,7 +458,7 @@ object ProtoTypes { val protoTyperState = ctx.typerState val oldConstraint = protoTyperState.constraint val args1 = args.mapWithIndexConserve((arg, idx) => - cacheTypedArg(arg, arg => typer.typed(norm(arg, idx)), force = false)) + cacheTypedArg(arg, arg => typer.typed(norm(arg, idx)), force = false, NoType)) val newConstraint = protoTyperState.constraint if !args1.exists(arg => isUndefined(arg.tpe)) then state.typedArgs = args1 @@ -505,7 +505,8 @@ object ProtoTypes { val locked = ctx.typerState.ownedVars val targ = cacheTypedArg(arg, typer.typedUnadapted(_, wideFormal, locked)(using argCtx), - force = true) + force = true, + wideFormal) val targ1 = typer.adapt(targ, wideFormal, locked) if wideFormal eq formal then targ1 else checkNoWildcardCaptureForCBN(targ1) @@ -857,6 +858,13 @@ object ProtoTypes { } } + def containsTypeParamRef(tp: Type)(using Context): Boolean = + tp.stripped match { + case tr: TypeParamRef => true + case AppliedType(_, args) => args.exists(containsTypeParamRef) + case _ => false + } + /** Approximate occurrences of parameter types and uninstantiated typevars * by wildcard types. */ @@ -864,7 +872,9 @@ object ProtoTypes { tp match { case tp: NamedType => // default case, inlined for speed val isPatternBoundTypeRef = tp.isInstanceOf[TypeRef] && tp.symbol.isPatternBound + val isImplicitTermRef = tp.isInstanceOf[TermRef] && tp.symbol.maybeOwner.isAnonymousFunction && tp.symbol.isParamOrAccessor && tp.symbol.is(Flags.Given) && containsTypeParamRef(tp.superType) if (isPatternBoundTypeRef) WildcardType(tp.underlying.bounds) + else if (isImplicitTermRef) wildApprox(tp.underlying, theMap, seen, internal) else if (tp.symbol.isStatic || (tp.prefix `eq` NoPrefix)) tp else tp.derivedSelect(wildApprox(tp.prefix, theMap, seen, internal)) case tp @ AppliedType(tycon, args) => @@ -957,6 +967,15 @@ object ProtoTypes { paramInfos = tl.paramInfos.mapConserve(wildApprox(_, theMap, seen, internal1).bounds), resType = wildApprox(tl.resType, theMap, seen, internal1) ) + case tp @ AnnotatedType(parent, _) => + // This case avoids approximating types in the annotation tree, which can + // cause the type assigner to fail. + // See #22893 and tests/pos/annot-default-arg-22874.scala. + val parentApprox = wildApprox(parent, theMap, seen, internal) + if tp.isRefining then + WildcardType(TypeBounds.upper(parentApprox)) + else + parentApprox case _ => (if (theMap != null && seen.eq(theMap.seen)) theMap else new WildApproxMap(seen, internal)) .mapOver(tp) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 87768e8a2026..3936899eb21d 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -19,6 +19,7 @@ import config.Feature.{warnOnMigration, migrateTo3, sourceVersion} import config.SourceVersion.{`3.0`, `future`} import config.Printers.refcheck import reporting.* +import Annotations.Annotation import Constants.Constant object RefChecks { @@ -311,6 +312,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 => @@ -340,6 +350,8 @@ object RefChecks { next == other || isInheritedAccessor(next, other) } + def isMarkedOverride(sym: Symbol) = sym.isAnyOverride || sym.hasAnnotation(defn.UncheckedOverrideAnnot) + /* Check that all conditions for overriding `other` by `member` * of class `clazz` are met. */ @@ -399,12 +411,17 @@ object RefChecks { if trueMatch && noErrorType then emitOverrideError(overrideErrorMsg(msg, compareTypes)) - def overrideDeprecation(what: String, member: Symbol, other: Symbol, fix: String): Unit = - report.deprecationWarning( - em"overriding $what${infoStringWithLocation(other)} is deprecated;\n ${infoString(member)} should be $fix.", - if member.owner == clazz then member.srcPos else clazz.srcPos, - origin = other.showFullName - ) + def overrideDeprecation(annot: Annotation, member: Symbol, other: Symbol): Unit = + if !CrossVersionChecks.skipDeprecation(member) then + val message = + annot.argumentConstantString(0).filter(!_.isEmpty) + .getOrElse(s"${infoString(member)} should be removed or renamed.") + val since = annot.argumentConstantString(1).filter(!_.isEmpty).map(" since " + _).getOrElse("") + val composed = + em"""overriding ${infoStringWithLocation(other)} is deprecated$since; + | $message""" + val pos = if member.owner == clazz then member.srcPos else clazz.srcPos + report.deprecationWarning(msg = composed, pos, origin = other.showFullName) def autoOverride(sym: Symbol) = sym.is(Synthetic) && ( @@ -479,7 +496,7 @@ object RefChecks { else if !other.is(Deferred) && !member.is(Deferred) && !other.name.is(DefaultGetterName) - && !member.isAnyOverride + && !isMarkedOverride(member) then // Exclusion for default getters, fixes SI-5178. We cannot assign the Override flag to // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket. @@ -507,9 +524,9 @@ object RefChecks { overrideError("needs `override` modifier") else if (other.is(AbsOverride) && other.isIncompleteIn(clazz) && !member.is(AbsOverride)) overrideError("needs `abstract override` modifiers") - else if member.is(Override) && other.is(Mutable) then + else if isMarkedOverride(member) && other.is(Mutable) then overrideError("cannot override a mutable variable") - else if (member.isAnyOverride && + else if (isMarkedOverride(member) && !(member.owner.thisType.baseClasses exists (_ isSubClass other.owner)) && !member.is(Deferred) && !other.is(Deferred) && intersectionIsEmpty(member.extendedOverriddenSymbols, other.extendedOverriddenSymbols)) @@ -550,7 +567,7 @@ object RefChecks { else if !other.isExperimental && member.hasAnnotation(defn.ExperimentalAnnot) then // (1.12) overrideError("may not override non-experimental member") else if other.hasAnnotation(defn.DeprecatedOverridingAnnot) then - overrideDeprecation("", member, other, "removed or renamed") + other.getAnnotation(defn.DeprecatedOverridingAnnot).foreach(overrideDeprecation(_, member, other)) end checkOverride val checker = if makeOverridingPairsChecker == null then OverridingPairsChecker(clazz, self) else makeOverridingPairsChecker(clazz, self) @@ -630,18 +647,40 @@ object RefChecks { // to consolidate getters and setters. val grouped = missing.groupBy(_.underlyingSymbol.name) + def isDuplicateSetter(sym: Symbol): Boolean = + sym.isSetter && { + val field = sym.accessedFieldOrGetter + grouped.getOrElse(field.name, Nil).contains(field) + } + val missingMethods = grouped.toList flatMap { case (name, syms) => lastOverrides(syms) - .filterConserve(!_.isSetter) + .filterConserve(!isDuplicateSetter(_)) // Avoid reporting override error for both `x` and setter `x_=` .distinctBy(_.signature) // Avoid duplication for similar definitions (#19731) } + /** Replace synthetic parameter names (x$0, x$1, ...) with + * dollar-free versions (x0, x1, ...) so that stub implementations + * are directly usable as valid Scala identifiers. + */ + def replaceSyntheticParamNames(tp: Type): Type = tp match + case mt: MethodType if mt.allParamNamesSynthetic => + val newNames = mt.paramNames.zipWithIndex.map((_, i) => termName("x" + i)) + mt.derivedLambdaType(newNames, mt.paramInfos, replaceSyntheticParamNames(mt.resType)) + case mt: MethodType => + mt.derivedLambdaType(mt.paramNames, mt.paramInfos, replaceSyntheticParamNames(mt.resType)) + case pt: PolyType => + pt.derivedLambdaType(pt.paramNames, pt.paramInfos, replaceSyntheticParamNames(pt.resType)) + case _ => tp + def stubImplementations: List[String] = { // Grouping missing methods by the declaring class val regrouped = missingMethods.groupBy(_.owner).toList def membersStrings(members: List[Symbol]) = - members.sortBy(_.name.toString).map(_.asSeenFrom(clazz.thisType).showDcl + " = ???") + members.sortBy(_.name.toString).map: sym => + val denot = sym.asSeenFrom(clazz.thisType) + denot.mapInfo(replaceSyntheticParamNames).showDcl + " = ???" if (regrouped.tail.isEmpty) membersStrings(regrouped.head._2) @@ -666,7 +705,7 @@ object RefChecks { for (member <- missingMethods) { def showDclAndLocation(sym: Symbol) = - s"${sym.showDcl} in ${sym.owner.showLocated}" + s"${sym.mapInfo(replaceSyntheticParamNames).showDcl} in ${sym.owner.showLocated}" def undefined(msg: String) = abstractClassError(false, s"${showDclAndLocation(member)} is not defined $msg") val underlying = member.underlyingSymbol @@ -837,7 +876,7 @@ object RefChecks { checkNoAbstractDecls(clazz) if (abstractErrors.nonEmpty) - report.error(abstractErrorMessage, clazz.srcPos) + report.error(abstractErrorMessage, clazzNamePos) checkMemberTypesOK() checkCaseClassInheritanceInvariant() @@ -1033,14 +1072,27 @@ object RefChecks { end checkUnaryMethods /** Check that an extension method is not hidden, i.e., that it is callable as an extension method. + * + * For example, it is not possible to define a type-safe extension `contains` for `Set`, + * since for any parameter type, the existing `contains` method will compile and would be used. * * An extension method is hidden if it does not offer a parameter that is not subsumed * by the corresponding parameter of the member with the same name (or of all alternatives of an overload). * - * This check is suppressed if this method is an override. + * This check is suppressed if the method is an override of a deferred member + * (because the type of the receiver in the override may be a concrete type arg). * - * For example, it is not possible to define a type-safe extension `contains` for `Set`, - * since for any parameter type, the existing `contains` method will compile and would be used. + * If the receiver is an opaque alias, use its upper bound if it is not also opaque. + * (Member lookup would dealias, and the check must approximate an arbitrary use site.) + * + * If the extension method is parameterless, it is always hidden by a member of the same name. + * (Either the member is parameterless, or the reference is taken as the eta-expansion of the member.) + * + * This check is in lieu of a more expensive use-site check that an application failed to use an extension. + * That check would account for accessibility and opacity. As a limitation, this check considers + * only public members for which corresponding method parameters are either both opaque types or both not. + * It is intended to warn if the receiver type from a third-party library has been augmented with a member + * that nullifies an existing extension. * * If the member has a leading implicit parameter list, then the extension method must also have * a leading implicit parameter list. The reason is that if the implicit arguments are inferred, @@ -1051,46 +1103,59 @@ object RefChecks { * If the member does not have a leading implicit parameter list, then the argument cannot be explicitly * supplied with `using`, as typechecking would fail. But the extension method may have leading implicit * parameters, which are necessarily supplied implicitly in the application. The first non-implicit - * parameters of the extension method must be distinguishable from the member parameters, as described. - * - * If the extension method is nullary, it is always hidden by a member of the same name. - * (Either the member is nullary, or the reference is taken as the eta-expansion of the member.) - * - * This check is in lieu of a more expensive use-site check that an application failed to use an extension. - * That check would account for accessibility and opacity. As a limitation, this check considers - * only public members, a target receiver that is not an alias, and corresponding method parameters - * that are either both opaque types or both not. + * parameters of the extension method must be distinguishable from the member parameters, as described above. */ def checkExtensionMethods(sym: Symbol)(using Context): Unit = - if sym.is(Extension) && !sym.nextOverriddenSymbol.exists then + if sym.is(Extension) then atPhase(typerPhase): extension (tp: Type) - def strippedResultType = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).resultType - def firstExplicitParamTypes = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).firstParamTypes + def explicit = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true) def hasImplicitParams = tp.stripPoly match { case mt: MethodType => mt.isImplicitMethod case _ => false } - val target = sym.info.firstExplicitParamTypes.head // required for extension method, the putative receiver - val methTp = sym.info.strippedResultType // skip leading implicits and the "receiver" parameter - def hidden = - target.nonPrivateMember(sym.name) - .filterWithPredicate: - member => - member.symbol.isPublic && { - val memberIsImplicit = member.info.hasImplicitParams - val paramTps = - if memberIsImplicit then methTp.stripPoly.firstParamTypes - else methTp.firstExplicitParamTypes - - paramTps.isEmpty || memberIsImplicit && !methTp.hasImplicitParams || { - val memberParamTps = member.info.stripPoly.firstParamTypes - !memberParamTps.isEmpty - && memberParamTps.lengthCompare(paramTps) == 0 - && memberParamTps.lazyZip(paramTps).forall: (m, x) => - m.typeSymbol.denot.isOpaqueAlias == x.typeSymbol.denot.isOpaqueAlias - && (x frozen_<:< m) - } - } - .exists - if !target.typeSymbol.denot.isAliasType && !target.typeSymbol.denot.isOpaqueAlias && hidden - then report.warning(ExtensionNullifiedByMember(sym, target.typeSymbol), sym.srcPos) + def isParamLess = tp.stripPoly match { case mt: MethodType => false case _ => true } + val explicitInfo = sym.info.explicit // consider explicit value params + def memberHidesMethod(member: Denotation): Boolean = + val methTp = explicitInfo.resultType // skip leading implicits and the "receiver" parameter + if methTp.isParamLess then + return true // extension without parens is always hidden by a member of same name + val memberIsImplicit = member.info.hasImplicitParams + inline def paramsCorrespond = + val paramTps = + if memberIsImplicit then methTp.stripPoly.firstParamTypes + else methTp.explicit.firstParamTypes + val memberParamTps = member.info.stripPoly.firstParamTypes + memberParamTps.corresponds(paramTps): (m, x) => + m.typeSymbol.denot.isOpaqueAlias == x.typeSymbol.denot.isOpaqueAlias + && (x frozen_<:< m) + memberIsImplicit && !methTp.hasImplicitParams || paramsCorrespond + def targetOfHiddenExtension: Symbol = + val receiver = + explicitInfo.firstParamTypes.head // required for extension method, the putative receiver + .dealiasKeepOpaques + .typeSymbol + val target = + if receiver.isOpaqueAlias then + val hi = receiver.info.hiBound.dealiasKeepOpaques + if hi.typeSymbol.isOpaqueAlias then NoType + else hi.typeSymbol.info // use upper bound if not also opaque + else + receiver.info + if target.exists then + val member = target.nonPrivateMember(sym.name) + .filterWithPredicate: member => + member.symbol.isPublic && memberHidesMethod(member) + if member.exists then receiver else NoSymbol // report the receiver not the target type where member was found + else NoSymbol + if sym.is(HasDefaultParams) then + val getterDenot = + val receiverName = explicitInfo.firstParamNames.head + val num = sym.info.paramNamess.flatten.indexWhere(_ == receiverName) + val getterName = DefaultGetterName(sym.name.toTermName, num = num) + sym.owner.info.member(getterName) + if getterDenot.exists + then report.warning(ExtensionHasDefault(sym), getterDenot.symbol.srcPos) + if !sym.nextOverriddenSymbol.exists then + val target = targetOfHiddenExtension + if target.exists then + report.warning(ExtensionNullifiedByMember(sym, target), sym.srcPos) end checkExtensionMethods /** Verify that references in the user-defined `@implicitNotFound` message are valid. diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index dca270f683c9..f64e2ee00de6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -18,6 +18,7 @@ import annotation.{tailrec, constructorOnly} import ast.tpd import ast.tpd.* import Synthesizer.* +import TypeComparer.{fullLowerBound, fullUpperBound} /** Synthesize terms for special classes */ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): @@ -51,7 +52,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): if defn.SpecialClassTagClasses.contains(sym) then classTagModul.select(sym.name.toTermName).withSpan(span) else - val ctype = escapeJavaArray(erasure(tp)) + val ctype = escapeJavaArray(erasure(tp.normalizedTupleType)) if ctype.exists then classTagModul.select(nme.apply) .appliedToType(tp) @@ -231,6 +232,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): withNoErrors(success(Literal(Constant(())))) case n: TermRef => withNoErrors(success(ref(n))) + case ts: ThisType => + withNoErrors(success(This(ts.cls))) case tp => EmptyTreeNoError case _ => @@ -281,8 +284,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): loop(formal) private def checkRefinement(formal: Type, name: TypeName, expected: Type, span: Span)(using Context): Unit = - val actual = formal.lookupRefined(name) - if actual.exists && !(expected =:= actual) + val actual = formal.findMember(name, NoPrefix).info + if actual.exists && !(actual.bounds.contains(expected)) then report.error( em"$name mismatch, expected: $expected, found: $actual.", ctx.source.atSpan(span)) @@ -440,6 +443,8 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): case MirrorSource.Singleton(_, tref) => val singleton = tref.termSymbol // prefer alias name over the original name val singletonPath = tpd.singleton(tref).withSpan(span) + checkRefinement(formal, tpnme.MirroredElemTypes, defn.EmptyTupleModule.termRef, span) + checkRefinement(formal, tpnme.MirroredElemLabels, defn.EmptyTupleModule.termRef, span) if tref.classSymbol.is(Scala2x) then // could be Scala 3 alias of Scala 2 case object. val mirrorType = formal.constrained_& { mirrorCore(defn.Mirror_SingletonProxyClass, mirroredType, mirroredType, singleton.name) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index ba9975923df0..8adbd061d8b0 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -81,7 +81,7 @@ trait TypeAssigner { defn.FromJavaObjectType else tpe match case tpe: NamedType => - val tpe1 = TypeOps.makePackageObjPrefixExplicit(tpe) + val tpe1 = tpe.makePackageObjPrefixExplicit if tpe1 ne tpe then accessibleType(tpe1, superAccess) else @@ -157,7 +157,7 @@ trait TypeAssigner { else qualType.findMember(name, pre) - if reallyExists(mbr) then qualType.select(name, mbr) + if reallyExists(mbr) && NamedType.validPrefix(qualType) then qualType.select(name, mbr) else if qualType.isErroneous || name.toTermName == nme.ERROR then UnspecifiedErrorType else NoType end selectionType diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index d46c85b38e9d..7acb641ef70d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -31,6 +31,7 @@ import EtaExpansion.etaExpand import TypeComparer.CompareResult import inlines.{Inlines, PrepareInlineable} import util.Spans.* +import util.chaining.* import util.common.* import util.{Property, SimpleIdentityMap, SrcPos} import Applications.{tupleComponentTypes, wrapDefs, defaultArgument} @@ -53,6 +54,7 @@ import config.Config import transform.CheckUnused.OriginalName import scala.annotation.constructorOnly +import scala.util.chaining.scalaUtilChainingOps import dotty.tools.dotc.rewrites.Rewrites object Typer { @@ -77,6 +79,9 @@ object Typer { /** Indicates that an expression is explicitly ascribed to [[Unit]] type. */ val AscribedToUnit = new Property.StickyKey[Unit] + /** Tree adaptation lost fidelity; this attachment preserves the original tree. */ + val AdaptedTree = new Property.StickyKey[tpd.Tree] + /** An attachment on a Select node with an `apply` field indicating that the `apply` * was inserted by the Typer. */ @@ -98,11 +103,18 @@ object Typer { */ private[typer] val HiddenSearchFailure = new Property.Key[List[SearchFailure]] + + /** An attachment on a Typed node. Indicates that the Typed node was synthetically + * inserted by the Typer phase. We might want to remove it for the purpose of inlining, + * but only if it was not manually inserted by the user. + */ + private[typer] val InsertedTyped = new Property.Key[Unit] + /** Is tree a compiler-generated `.apply` node that refers to the * apply of a function class? */ private[typer] def isSyntheticApply(tree: tpd.Tree): Boolean = tree match { - case tree: tpd.Select => tree.hasAttachment(InsertedApply) + case _: (tpd.Select | tpd.Apply) => tree.hasAttachment(InsertedApply) case TypeApply(fn, targs) => isSyntheticApply(fn) && targs.forall(_.isInstanceOf[tpd.InferredTypeTree]) case _ => false } @@ -296,8 +308,17 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer imp.importSym.info match case ImportType(expr) => val pre = expr.tpe - val denot0 = pre.memberBasedOnFlags(name, required, excluded) - .accessibleFrom(pre)(using refctx) + def findMember(excluded: FlagSet) = + if ctx.isJava then + ctx.javaFindMember(name, pre, lookInCompanion = true, required, excluded) + else + pre.memberBasedOnFlags(name, required, excluded) + .accessibleFrom(pre)(using refctx) + val denot0 = findMember(excluded) + var accessibleDenot = denot0.accessibleFrom(pre)(using refctx) + if !accessibleDenot.exists && denot0.hasAltWith(_.symbol.is(Private)) then + accessibleDenot = + findMember(excluded | Private).accessibleFrom(pre)(using refctx) // Pass refctx so that any errors are reported in the context of the // reference instead of the context of the import scope if denot0.exists then @@ -315,7 +336,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // so we ignore that import. if reallyExists(denot) && !isScalaJsPseudoUnion then if unimported.isEmpty || !unimported.contains(pre.termSymbol) then - return pre.select(name, denot) + return pre.select(name, denot).makePackageObjPrefixExplicit case _ => if imp.importSym.isCompleting then report.warning(i"cyclic ${imp.importSym}, ignored", pos) @@ -475,7 +496,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer defDenot.symbol.owner else curOwner - effectiveOwner.thisType.select(name, defDenot) + effectiveOwner.thisType.select(name, defDenot).makePackageObjPrefixExplicit } if !curOwner.is(Package) || isDefinedInCurrentUnit(defDenot) then result = checkNewOrShadowed(found, Definition) // no need to go further out, we found highest prec entry @@ -629,32 +650,40 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val checkedType = checkNotShadowed(ownType) val tree1 = checkedType match case checkedType: NamedType if !prefixIsElidable(checkedType) => - ref(checkedType).withSpan(tree.span) + ref(checkedType).withSpan(tree.span).withAttachmentsFrom(tree) case _ => tree.withType(checkedType) val tree2 = toNotNullTermRef(tree1, pt) checkLegalValue(tree2, pt) tree2 - def isLocalExtensionMethodRef: Boolean = rawType match - case rawType: TermRef => - rawType.denot.hasAltWith(_.symbol.is(ExtensionMethod)) - && !pt.isExtensionApplyProto - && { + // extensionParam + def leadParamOf(m: SymDenotation): Symbol = + def leadParam(paramss: List[List[Symbol]]): Symbol = paramss match + case (param :: _) :: paramss if param.isType => leadParam(paramss) + case _ :: (param :: Nil) :: _ if m.name.isRightAssocOperatorName => param + case (param :: Nil) :: _ => param + case _ => NoSymbol + leadParam(m.rawParamss) + + val localExtensionSelection: untpd.Tree = + var select: untpd.Tree = EmptyTree + if ctx.mode.is(Mode.InExtensionMethod) then + rawType match + case rawType: TermRef + if rawType.denot.hasAltWith(_.symbol.is(ExtensionMethod)) && !pt.isExtensionApplyProto => val xmethod = ctx.owner.enclosingExtensionMethod - rawType.denot.hasAltWith { alt => - alt.symbol.is(ExtensionMethod) - && alt.symbol.extensionParam.span == xmethod.extensionParam.span - } - } - case _ => - false + val xparam = leadParamOf(xmethod) + if rawType.denot.hasAltWith: alt => + alt.symbol.is(ExtensionMethod) + && alt.symbol.extensionParam.span == xparam.span // forces alt.symbol (which might be xmethod) + then + select = untpd.cpy.Select(tree)(untpd.ref(xparam), name) + case _ => + select - if ctx.mode.is(Mode.InExtensionMethod) && isLocalExtensionMethodRef then - val xmethod = ctx.owner.enclosingExtensionMethod - val qualifier = untpd.ref(xmethod.extensionParam.termRef) - val selection = untpd.cpy.Select(tree)(qualifier, name) - typed(selection, pt) + if !localExtensionSelection.isEmpty then + typed(localExtensionSelection, pt) else if rawType.exists then val ref = setType(ensureAccessible(rawType, superAccess = false, tree.srcPos)) if ref.symbol.name != name then @@ -727,10 +756,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // Otherwise, map combinations of A *: B *: .... EmptyTuple with nesting levels <= 22 // to the Tuple class of the right arity and select from that one def trySmallGenericTuple(qual: Tree, withCast: Boolean) = - if qual.tpe.isSmallGenericTuple then + val tp = qual.tpe.widenTermRefExpr + val tpNormalized = tp.normalizedTupleType + if tp ne tpNormalized then if withCast then - val elems = qual.tpe.widenTermRefExpr.tupleElementTypes.getOrElse(Nil) - typedSelectWithAdapt(tree, pt, qual.cast(defn.tupleType(elems))) + typedSelectWithAdapt(tree, pt, qual.cast(tpNormalized)) else typedSelectWithAdapt(tree, pt, qual) else EmptyTree @@ -867,7 +897,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer record("typedNumber") val digits = tree.digits val target = pt.dealias - def lit(value: Any) = Literal(Constant(value)).withSpan(tree.span) + def lit(value: Any) = Literal(Constant(value)).withSpan(tree.span).withAttachmentsFrom(tree) try { // Special case primitive numeric types if (target.isRef(defn.IntClass) || @@ -960,7 +990,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer typedAhead(parent, tree => inferTypeParams(typedType(tree), pt)) val anon = tpnme.ANON_CLASS val clsDef = TypeDef(anon, templ1).withFlags(Final | Synthetic) - typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(anon), Nil)), pt) + typed( + cpy.Block(tree)( + clsDef :: Nil, + New(Ident(anon), Nil).withSpan(tree.span)), + pt) case _ => var tpt1 = typedType(tree.tpt) val tsym = tpt1.tpe.underlyingClassRef(refinementOK = false).typeSymbol @@ -973,7 +1007,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer report.error(WildcardOnTypeArgumentNotAllowedOnNew(), targ.srcPos) case _ => } - assignType(cpy.New(tree)(tpt1), tpt1) } @@ -1137,8 +1170,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def lhs1 = adapt(lhsCore, AssignProto, locked) def reassignmentToVal = - report.error(ReassignmentToVal(lhsCore.symbol.name), tree.srcPos) - cpy.Assign(tree)(lhsCore, typed(tree.rhs, lhs1.tpe.widen)).withType(defn.UnitType) + val adapted = lhs1 + val name = lhs match + case nt: NameTree => nt.name // respects import rename + case _ => adapted.symbol.name // other LHS such as X.x += y + report.error(ReassignmentToVal(name, pt), tree.srcPos) + cpy.Assign(tree)(lhsCore, typed(tree.rhs, adapted.tpe.widen)).withType(defn.UnitType) def canAssign(sym: Symbol) = sym.is(Mutable, butNot = Accessor) || @@ -1215,7 +1252,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer typedDynamicAssign(tree, pt) case tpe => reassignmentToVal - } + } } def typedBlockStats(stats: List[untpd.Tree])(using Context): (List[tpd.Tree], Context) = @@ -1387,11 +1424,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case defn.ErasedFunctionOf(mt @ MethodTpe(_, formals, restpe)) if formals.length == defaultArity => (formals, untpd.DependentTypeTree(syms => restpe.substParams(mt, syms.map(_.termRef)))) case SAMType(mt @ MethodTpe(_, formals, restpe), _) => - (formals, - if (mt.isResultDependent) - untpd.DependentTypeTree(syms => restpe.substParams(mt, syms.map(_.termRef))) - else - typeTree(restpe)) + val tree = + if (mt.isResultDependent) { + if (formals.length != defaultArity) + typeTree(WildcardType) + else + untpd.DependentTypeTree(syms => restpe.substParams(mt, syms.map(_.termRef))) + } else + typeTree(restpe) + (formals, tree) case _ => (List.tabulate(defaultArity)(alwaysWildcardType), untpd.TypeTree()) } @@ -1439,11 +1480,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else typedFunctionValue(tree, pt) def typedFunctionType(tree: untpd.Function, pt: Type)(using Context): Tree = { - val untpd.Function(args, body) = tree - body match - case untpd.CapturesAndResult(refs, result) => + val untpd.Function(args, result) = tree + result match + case untpd.CapturesAndResult(refs, result1) => return typedUnadapted(untpd.makeRetaining( - cpy.Function(tree)(args, result), refs, tpnme.retains), pt) + cpy.Function(tree)(args, result1), refs, tpnme.retains), pt) case _ => var (funFlags, erasedParams) = tree match { case tree: untpd.FunctionWithMods => (tree.mods.flags, tree.erasedParams) @@ -1455,22 +1496,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val isImpure = funFlags.is(Impure) /** Typechecks dependent function type with given parameters `params` */ - def typedDependent(params: List[untpd.ValDef])(using Context): Tree = - val fixThis = new untpd.UntypedTreeMap: - // pretype all references of this in outer context, - // so that they do not refer to the refined type being constructed - override def transform(tree: untpd.Tree)(using Context): untpd.Tree = tree match - case This(id) => untpd.TypedSplice(typedExpr(tree)(using ctx.outer)) - case _ => super.transform(tree) - + def typedDependent(params: List[untpd.ValDef], result: untpd.Tree)(using Context): Tree = val params1 = if funFlags.is(Given) then params.map(_.withAddedFlags(Given)) else params - val params2 = params1.map(fixThis.transformSub) - val params3 = params2.zipWithConserve(erasedParams) { (arg, isErased) => + val params2 = params1.zipWithConserve(erasedParams): (arg, isErased) => if isErased then arg.withAddedFlags(Erased) else arg - } - val appDef0 = untpd.DefDef(nme.apply, List(params3), body, EmptyTree).withSpan(tree.span) + val appDef0 = untpd.DefDef(nme.apply, List(params2), result, EmptyTree).withSpan(tree.span) index(appDef0 :: Nil) val appDef = typed(appDef0).asInstanceOf[DefDef] val mt = appDef.symbol.info.asInstanceOf[MethodType] @@ -1478,14 +1510,14 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer report.error(em"$mt is an illegal function type because it has inter-parameter dependencies", tree.srcPos) // Restart typechecking if there are erased classes that we want to mark erased if mt.erasedParams.zip(mt.paramInfos.map(_.isErasedClass)).exists((paramErased, classErased) => classErased && !paramErased) then - val newParams = params3.zipWithConserve(mt.paramInfos.map(_.isErasedClass)) { (arg, isErasedClass) => + val newParams = params2.zipWithConserve(mt.paramInfos.map(_.isErasedClass)) { (arg, isErasedClass) => if isErasedClass then arg.withAddedFlags(Erased) else arg } - return typedDependent(newParams) + return typedDependent(newParams, result) val core = if mt.hasErasedParams then TypeTree(defn.ErasedFunctionClass.typeRef) else - val resTpt = TypeTree(mt.nonDependentResultApprox).withSpan(body.span) + val resTpt = TypeTree(mt.nonDependentResultApprox).withSpan(result.span) val paramTpts = appDef.termParamss.head.map(p => TypeTree(p.tpt.tpe).withSpan(p.tpt.span)) val funSym = defn.FunctionSymbol(numArgs, isContextual, isImpure) val tycon = TypeTree(funSym.typeRef) @@ -1495,19 +1527,28 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer args match { case ValDef(_, _, _) :: _ => - typedDependent(args.asInstanceOf[List[untpd.ValDef]])( + val fixThis = new untpd.UntypedTreeMap: + // pretype all references of this so that they do not refer to the + // refined type being constructed + override def transform(tree: untpd.Tree)(using Context): untpd.Tree = tree match + case This(id) => untpd.TypedSplice(typedExpr(tree)) + case _ => super.transform(tree) + + val untpd.Function(fixedArgs: List[untpd.ValDef] @unchecked, fixedResult) = + fixThis.transform(tree): @unchecked + typedDependent(fixedArgs, fixedResult)( using ctx.fresh.setOwner(newRefinedClassSymbol(tree.span)).setNewScope) case _ => if erasedParams.contains(true) then typedFunctionType(desugar.makeFunctionWithValDefs(tree, pt), pt) else val funSym = defn.FunctionSymbol(numArgs, isContextual, isImpure) - val result = typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funSym.typeRef), args :+ body), pt) + val funTpt = typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funSym.typeRef), args :+ result), pt) // if there are any erased classes, we need to re-do the typecheck. - result match + funTpt match case r: AppliedTypeTree if r.args.exists(_.tpe.isErasedClass) => typedFunctionType(desugar.makeFunctionWithValDefs(tree, pt), pt) - case _ => result + case _ => funTpt } } @@ -1698,7 +1739,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case mt: MethodType => pt.findFunctionType match { case SAMType(samMeth, samParent) - if !defn.isFunctionNType(samParent) && mt <:< samMeth => + if !ctx.erasedTypes && !defn.isFunctionNType(samParent) + && mt <:< samMeth && !mt.isImplicitMethod => if defn.isContextFunctionType(mt.resultType) then report.error( em"""Implementation restriction: cannot convert this expression to `$samParent` @@ -1759,7 +1801,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val sel1 = typedExpr(tree.selector) val rawSelectorTpe = fullyDefinedType(sel1.tpe, "pattern selector", tree.srcPos) val selType = rawSelectorTpe match - case c: ConstantType if tree.isInline => c + case c: ConstantType => c case otherTpe => otherTpe.widen /** Does `tree` has the same shape as the given match type? @@ -2423,7 +2465,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // wrt to operand order for `&`, we include the explicit subtype test here. // See also #5649. then body1.tpe - else pt & body1.tpe + else body1.tpe & pt val sym = newPatternBoundSymbol(name, symTp, tree.span) if (pt == defn.ImplicitScrutineeTypeRef || tree.mods.is(Given)) sym.setFlag(Given) if (ctx.mode.is(Mode.InPatternAlternative)) @@ -2483,31 +2525,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def registerNowarn(tree: Tree, mdef: untpd.Tree)(using Context): Unit = val annot = Annotations.Annotation(tree) - def argPos = annot.argument(0).getOrElse(tree).sourcePos - var verbose = false - val filters = annot.argumentConstantString(0) match - case None => annot.argument(0) match - case Some(t: Select) if t.name.is(DefaultGetterName) => - // default argument used for `@nowarn` and `@nowarn()` - List(MessageFilter.Any) - case _ => - report.warning(s"filter needs to be a compile-time constant string", argPos) - List(MessageFilter.None) - case Some("") => - List(MessageFilter.Any) - case Some("verbose") | Some("v") => - verbose = true - List(MessageFilter.Any) - case Some(s) => - WConf.parseFilters(s).left.map(parseErrors => - report.warning (s"Invalid message filter\n${parseErrors.mkString ("\n")}", argPos) - List(MessageFilter.None) - ).merge - val range = mdef.sourcePos - val sup = Suppression(tree.sourcePos, filters, range.start, range.end, verbose) - // invalid suppressions, don't report as unused - if filters == List(MessageFilter.None) then sup.markUsed() - ctx.run.nn.suppressions.addSuppression(sup) + val argPos = annot.argument(0).getOrElse(tree).sourcePos + val conf = annot.argumentConstantString(0).getOrElse: + annot.argument(0) match + case Some(t: Select) if t.name.is(DefaultGetterName) => + "" // default argument used for `@nowarn` and `@nowarn()` + case _ => + report.warning(s"filter needs to be a compile-time constant string", argPos) + "none" // not a -Wconf filter, mapped to MessageFilter.None by registerNowarn + ctx.run.nn.suppressions.registerNowarn(tree.sourcePos, mdef.span)(conf, argPos) def typedValDef(vdef: untpd.ValDef, sym: Symbol)(using Context): Tree = ctx.profiler.onTypedDef(sym) { val ValDef(name, tpt, _) = vdef @@ -2521,7 +2547,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym) postProcessInfo(vdef1, sym) - vdef1.setDefTree + if (!ctx.isAfterInlining) vdef1.setDefTree val nnInfo = rhs1.notNullInfo vdef1.withNotNullInfo(if sym.is(Lazy) then nnInfo.retractedInfo else nnInfo) } @@ -2549,6 +2575,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer completeAnnotations(ddef, sym) val paramss1 = paramss.nestedMapConserve(typed(_)).asInstanceOf[List[ParamClause]] for case ValDefs(vparams) <- paramss1 do + if !ctx.isAfterTyper && !sym.is(Synthetic) then + vparams.foldLeft(mutable.Set.empty[Name]): (seen, p) => + if seen(p.name) then + report.error(em"parameter name must be distinct from deprecated name", p.srcPos) + for annot <- p.symbol.getAnnotation(defn.DeprecatedNameAnnot) do + val nm = annot.argumentConstantString(0).map(_.toTermName).getOrElse(nme.NO_NAME) + if seen(nm) then + report.error(em"deprecated parameter name must be distinct from other names", annot.tree.srcPos) + seen.addOne(nm) + seen.addOne(p.name) checkNoForwardDependencies(vparams) if (sym.isOneOf(GivenOrImplicit)) checkImplicitConversionDefOK(sym) val tpt1 = checkSimpleKinded(typedType(tpt)) @@ -2581,7 +2617,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if sym.is(ExtensionMethod) then rhsCtx.addMode(Mode.InExtensionMethod) val rhs1 = PrepareInlineable.dropInlineIfError(sym, if sym.isScala2Macro then typedScala2MacroBody(ddef.rhs)(using rhsCtx) - else typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(using rhsCtx)) + else + typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(using rhsCtx)) match + case typed @ Typed(outer, _) if typed.hasAttachment(InsertedTyped) => outer + case other => other if sym.isInlineMethod then if StagingLevel.level > 0 then @@ -2596,6 +2635,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if sym.isConstructor then if sym.is(Inline) then report.error("constructors cannot be `inline`", ddef) + + if sym.targetName != sym.name then + report.error(em"@targetName annotation may not be used on a constructor", ddef.srcPos) + if sym.isPrimaryConstructor then if sym.owner.is(Case) then for @@ -2605,17 +2648,21 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if defn.isContextFunctionType(param.tpt.tpe) then report.error("case class element cannot be a context function", param.srcPos) else - if sym.targetName != sym.name then - report.error(em"@targetName annotation may not be used on a constructor", ddef.srcPos) - for params <- paramss1; param <- params do checkRefsLegal(param, sym.owner, (name, sym) => sym.is(TypeParam), "secondary constructor") def checkThisConstrCall(tree: Tree): Unit = tree match case app: Apply if untpd.isSelfConstrCall(app) => - if (sym.span.exists && app.symbol.span.exists && sym.span.start <= app.symbol.span.start) + if !sym.is(Synthetic) + && sym.span.exists + && app.symbol.span.exists + && sym.span.start <= app.symbol.span.start + && tree.span.exists && !tree.span.isSynthetic + then report.error("secondary constructor must call a preceding constructor", app.srcPos) - case Block(call :: _, _) => checkThisConstrCall(call) + case Block(call :: _, expr) => + checkThisConstrCall(call) + checkThisConstrCall(expr) case _ => checkThisConstrCall(rhs1) @@ -2647,7 +2694,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if !sym.is(Module) && !sym.isConstructor && sym.info.finalResultType.isErasedClass then sym.setFlag(Erased) mdef.ensureHasSym(sym) - mdef.setDefTree + if (!ctx.isAfterInlining) mdef.setDefTree + else mdef def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree = ctx.profiler.onTypedDef(sym) { val TypeDef(name, rhs) = tdef @@ -2792,7 +2840,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer checkNonCyclicInherited(cls.thisType, cls.info.parents, cls.info.decls, cdef.srcPos) // check value class constraints - checkDerivedValueClass(cls, body1) + checkDerivedValueClass(cdef, cls, body1) // check PolyFunction constraints (no erased functions!) if parents1.exists(_.tpe.classSymbol eq defn.PolyFunctionClass) then @@ -2909,12 +2957,19 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer inContext(ctx.packageContext(tree, pkg)) { // If it exists, complete the class containing the top-level definitions // before typing any statement in the package to avoid cycles as in i13669.scala - val topLevelClassName = desugar.packageObjectName(ctx.source).moduleClassName - pkg.moduleClass.info.decls.lookup(topLevelClassName).ensureCompleted() + val packageObjectName = desugar.packageObjectName(ctx.source) + val topLevelClassSymbol = pkg.moduleClass.info.decls.lookup(packageObjectName.moduleClassName) + topLevelClassSymbol.ensureCompleted() var stats1 = typedStats(tree.stats, pkg.moduleClass)._1 if (!ctx.isAfterTyper) stats1 = stats1 ++ typedBlockStats(MainProxies.proxies(stats1))._1 cpy.PackageDef(tree)(pid1, stats1).withType(pkg.termRef) + .tap: _ => + if !ctx.isAfterTyper + && pkg != defn.EmptyPackageVal + && !topLevelClassSymbol.info.decls.filter(sym => !sym.isConstructor && !sym.is(Synthetic)).isEmpty + then + desugar.checkSimplePackageName(packageObjectName, tree.span, ctx.source, isPackageObject = true) } case _ => // Package will not exist if a duplicate type has already been entered, see `tests/neg/1708.scala` @@ -3057,7 +3112,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def typedTuple(tree: untpd.Tuple, pt: Type)(using Context): Tree = { val arity = tree.trees.length if (arity <= Definitions.MaxTupleArity) - typed(desugar.smallTuple(tree).withSpan(tree.span), pt) + typed(desugar.smallTuple(tree).withSpan(tree.span).withAttachmentsFrom(tree), pt) else { val pts = pt.tupleElementTypes match @@ -3156,7 +3211,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tree: untpd.Typed => typedTyped(tree, pt) case tree: untpd.NamedArg => typedNamedArg(tree, pt) case tree: untpd.Assign => typedAssign(tree, pt) - case tree: untpd.Block => typedBlock(desugar.block(tree), pt)(using ctx.fresh.setNewScope) + case tree: untpd.Block => typedBlock(desugar.block(tree), pt)(using ctx.fresh.setNewScope.setTree(tree)) case tree: untpd.If => typedIf(tree, pt) case tree: untpd.Function => typedFunction(tree, pt) case tree: untpd.Closure => typedClosure(tree, pt) @@ -3286,6 +3341,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val ifun = desugar.makeContextualFunction(paramTypes, 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` */ @@ -3353,7 +3418,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer traverse(xtree :: rest) case stat :: rest => val stat1 = typed(stat)(using ctx.exprContext(stat, exprOwner)) - if !checkInterestingResultInStatement(stat1) then checkStatementPurity(stat1)(stat, exprOwner) + if !checkInterestingResultInStatement(stat1) then checkStatementPurity(stat1)(stat, exprOwner, isUnitExpr = false) buf += stat1 traverse(rest)(using stat1.nullableContext) case nil => @@ -3633,8 +3698,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val remembered = // report AmbiguousReferences as priority, otherwise last error (errs.filter(_.msg.isInstanceOf[AmbiguousReference]) ++ errs).take(1) for err <- remembered do + val tree = if app.isEmpty then qual else app rememberSearchFailure(qual, - SearchFailure(app.withType(FailedExtension(app, selectionProto, err.msg)))) + SearchFailure(tree.withType(FailedExtension(tree, selectionProto, err.msg)))) catch case ex: TypeError => nestedFailure(ex) // try an implicit conversion or given extension @@ -3724,73 +3790,64 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val allDenots = ref.denot.alternatives if pt.isExtensionApplyProto then allDenots.filter(_.symbol.is(ExtensionMethod)) else allDenots + def altRef(alt: SingleDenotation) = TermRef(ref.prefix, ref.name, alt) + val alts = altDenots.map(altRef) typr.println(i"adapt overloaded $ref with alternatives ${altDenots map (_.info)}%\n\n %") /** Search for an alternative that does not take parameters. - * If there is one, return it, otherwise emit an error. + * If there is one, return it, otherwise return the error tree. */ - def tryParameterless(alts: List[TermRef])(error: => tpd.Tree): Tree = + def tryParameterless(error: => tpd.Tree): Tree = alts.filter(_.info.isParameterless) match - case alt :: Nil => readaptSimplified(tree.withType(alt)) - case _ => - if altDenots.exists(_.info.paramInfoss == ListOfNil) then - typed(untpd.Apply(untpd.TypedSplice(tree), Nil), pt, locked) - else - error - - def altRef(alt: SingleDenotation) = TermRef(ref.prefix, ref.name, alt) - val alts = altDenots.map(altRef) + case alt :: Nil => readaptSimplified(tree.withType(alt)) + case _ => + altDenots.find(_.info.paramInfoss == ListOfNil) match + case Some(alt) => readaptSimplified(tree.withType(altRef(alt))) + case _ => error resolveOverloaded(alts, pt) match - case alt :: Nil => - readaptSimplified(tree.withType(alt)) - case Nil => - // If no alternative matches, there are still two ways to recover: - // 1. If context is an application, try to insert an apply or implicit - // 2. If context is not an application, pick a alternative that does - // not take parameters. - - def errorNoMatch = errorTree(tree, NoMatchingOverload(altDenots, pt)) - - pt match - case pt: FunOrPolyProto if pt.applyKind != ApplyKind.Using => - // insert apply or convert qualifier, but only for a regular application - tryInsertApplyOrImplicit(tree, pt, locked)(errorNoMatch) - case _ => - tryParameterless(alts)(errorNoMatch) - - case ambiAlts => - // If there are ambiguous alternatives, and: - // 1. the types aren't erroneous - // 2. the expected type is not a function type - // 3. there exist a parameterless alternative - // - // Then, pick the parameterless alternative. - // See tests/pos/i10715-scala and tests/pos/i10715-java. - - /** Constructs an "ambiguous overload" error */ - def errorAmbiguous = - val remainingDenots = altDenots.filter(denot => ambiAlts.contains(altRef(denot))) - val addendum = - if ambiAlts.exists(!_.symbol.exists) then - i"""| - | - |Note: Overloaded definitions introduced by refinements cannot be resolved""" - else "" - errorTree(tree, AmbiguousOverload(tree, remainingDenots, pt, addendum)) - end errorAmbiguous - - if tree.tpe.isErroneous || pt.isErroneous then - tree.withType(UnspecifiedErrorType) - else - pt match - case _: FunProto => - errorAmbiguous - case _ => - tryParameterless(alts)(errorAmbiguous) - - end match + case alt :: Nil => + readaptSimplified(tree.withType(alt)) + case Nil => + // If no alternative matches, there are still two ways to recover: + // 1. If context is an application, try to insert an apply or implicit + // 2. If context is not an application, pick an alternative that does + // not take parameters. + def errorNoMatch = errorTree(tree, NoMatchingOverload(altDenots, pt)) + + pt match + case pt: FunOrPolyProto if pt.applyKind != ApplyKind.Using => + // insert apply or convert qualifier, but only for a regular application + tryInsertApplyOrImplicit(tree, pt, locked)(errorNoMatch) + case _ => + tryParameterless(errorNoMatch) + case ambiAlts => + // If there are ambiguous alternatives, and: + // 1. the types aren't erroneous + // 2. the expected type is not a function type + // 3. there exists a parameterless alternative + // + // Then, pick the parameterless alternative. See tests/pos/i10715-* + + /** Constructs an "ambiguous overload" error */ + def errorAmbiguous = + val remainingDenots = altDenots.filter(denot => ambiAlts.contains(altRef(denot))) + val addendum = + if ambiAlts.exists(!_.symbol.exists) then + i"""| + | + |Note: Overloaded definitions introduced by refinements cannot be resolved""" + else "" + errorTree(tree, AmbiguousOverload(tree, remainingDenots, pt, addendum)) + + pt match + case pt if tree.tpe.isErroneous || pt.isErroneous => + tree.withType(UnspecifiedErrorType) + case _: FunProto => + errorAmbiguous + case _ => + tryParameterless(errorAmbiguous) end adaptOverloaded def adaptToArgs(wtp: Type, pt: FunProto): Tree = wtp match { @@ -3818,9 +3875,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer readapt(tree.appliedToNone) // insert () to primary constructors else errorTree(tree, em"Missing arguments for $methodStr") - case _ => tryInsertApplyOrImplicit(tree, pt, locked) { - errorTree(tree, MethodDoesNotTakeParameters(tree)) - } + case _ => + tryInsertApplyOrImplicit(tree, pt, locked): + errorTree(tree, MethodDoesNotTakeParameters(tree)) } def adaptNoArgsImplicitMethod(wtp: MethodType): Tree = { @@ -3853,10 +3910,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer implicitArgs(formals2, argIndex + 1, pt) val arg = inferImplicitArg(formal, tree.span.endPos) + + def tryConstrainResult(pt: Type): Boolean = + try constrainResult(tree.symbol, wtp, pt) + catch case _: TyperState.BadTyperStateAssertion => false + arg.tpe match case failed: AmbiguousImplicits => val pt1 = pt.deepenProtoTrans - if (pt1 `ne` pt) && (pt1 ne sharpenedPt) && constrainResult(tree.symbol, wtp, pt1) + if (pt1 `ne` pt) && (pt1 ne sharpenedPt) && tryConstrainResult(pt1) then implicitArgs(formals, argIndex, pt1) else arg :: implicitArgs(formals1, argIndex + 1, pt1) case failed: SearchFailureType => @@ -3982,44 +4044,55 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } /** A synthetic apply should be eta-expanded if it is the apply of an implicit function - * class, and the expected type is a function type. This rule is needed so we can pass - * an implicit function to a regular function type. So the following is OK - * - * val f: implicit A => B = ??? - * val g: A => B = f - * - * and the last line expands to - * - * val g: A => B = (x$0: A) => f.apply(x$0) - * - * One could be tempted not to eta expand the rhs, but that would violate the invariant - * that expressions of implicit function types are always implicit closures, which is - * exploited by ShortcutImplicits. - * - * On the other hand, the following would give an error if there is no implicit - * instance of A available. - * - * val x: AnyRef = f - * - * That's intentional, we want to fail here, otherwise some unsuccessful implicit searches - * would go undetected. - * - * Examples for these cases are found in run/implicitFuns.scala and neg/i2006.scala. - */ + * class, and the expected type is a function type. This rule is needed so we can pass + * an implicit function to a regular function type. So the following is OK + * + * val f: implicit A => B = ??? + * val g: A => B = f + * + * and the last line expands to + * + * val g: A => B = (x$0: A) => f.apply(x$0) + * + * One could be tempted not to eta expand the rhs, but that would violate the invariant + * that expressions of implicit function types are always implicit closures, which is + * exploited by ShortcutImplicits. + * + * On the other hand, the following would give an error if there is no implicit + * instance of A available. + * + * val x: AnyRef = f + * + * That's intentional, we want to fail here, otherwise some unsuccessful implicit searches + * would go undetected. + * + * Examples for these cases are found in run/implicitFuns.scala and neg/i2006.scala. + */ def adaptNoArgsUnappliedMethod(wtp: MethodType, functionExpected: Boolean, arity: Int): Tree = { /** Is reference to this symbol `f` automatically expanded to `f()`? */ def isAutoApplied(sym: Symbol): Boolean = lazy val msg = MissingEmptyArgumentList(sym.show, tree) - - sym.isConstructor - || sym.matchNullaryLoosely - || Feature.warnOnMigration(msg, tree.srcPos, version = `3.0`) - && { - msg.actions - .headOption - .foreach(Rewrites.applyAction) - true + def applyAction(): Unit = + msg.actions + .headOption + .foreach(Rewrites.applyAction) + def warnScala2(): Unit = + if { + var isScala2 = sym.owner.is(Scala2x) + val isJavaEtc = + sym.allOverriddenSymbols.exists: m => + if m.owner.is(Scala2x) then + isScala2 = true + m.is(JavaDefined) || m == defn.Object_clone || m.owner == defn.AnyClass + isScala2 && !isJavaEtc } + then + report.warning(msg, tree.srcPos) + applyAction() + sym.isConstructor + || sym.matchNullaryLoosely.tap(if _ then warnScala2()) + || Feature.warnOnMigration(msg, tree.srcPos, version = `3.0`).tap(if _ then applyAction()) + end isAutoApplied /** If this is a selection prototype of the form `.apply(...): R`, return the nested * function prototype `(...)R`. Otherwise `pt`. @@ -4204,12 +4277,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer /** Adapt an expression of constant type to a different constant type `tpe`. */ def adaptConstant(tree: Tree, tpe: ConstantType): Tree = { - def lit = Literal(tpe.value).withSpan(tree.span) + def lit = Literal(tpe.value).withSpan(tree.span).withAttachment(AdaptedTree, tree) tree match { case Literal(c) => lit case tree @ Block(stats, expr) => tpd.cpy.Block(tree)(stats, adaptConstant(expr, tpe)) case tree => - if (isIdempotentExpr(tree)) lit // See discussion in phase Literalize why we demand isIdempotentExpr + if isIdempotentExpr(tree) then lit // See discussion in phase FirstTransform why we demand isIdempotentExpr else Block(tree :: Nil, lit) } } @@ -4244,14 +4317,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // so will take the code path that decides on inlining val tree1 = adapt(tree, WildcardType, locked) checkStatementPurity(tree1)(tree, ctx.owner, isUnitExpr = true) - - if ctx.settings.Whas.valueDiscard - && !ctx.isAfterTyper - && !tree.isInstanceOf[Inlined] - && !isThisTypeResult(tree) - && !tree.hasAttachment(AscribedToUnit) then - report.warning(ValueDiscarding(tree.tpe), tree.srcPos) - + checkValueDiscard(tree) return tpd.Block(tree1 :: Nil, unitLiteral) } @@ -4352,7 +4418,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => } - /** If `tree` is a constructor proxy reference, convert it to a `new` expression, + /** If `tree` is a constructor proxy reference, convert it to a `new` expression; + * check if it is a reference to an exported type/companion pair; * otherwise return EmptyTree. */ def newExpr(tree: Tree): Tree = @@ -4368,13 +4435,24 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer cpy.Ident(qual)(qual.symbol.name.sourceModuleName.toTypeName) case _ => errorTree(tree, em"cannot convert from $tree to an instance creation expression") - val tycon = ctorResultType.underlyingClassRef(refinementOK = false) + val tycon = + val exported = + if qual.symbol.isAllOf(SyntheticMethod | Exported) then + qual.symbol.owner.info.memberBasedOnFlags(qual.symbol.name.toTypeName, required = Exported) + else NoDenotation + if exported.exists then + qual.tpe match + case tp: NamedType => + exported.symbol.typeRef.asSeenFrom(tp.prefix, exported.symbol.owner) + case _ => + exported.symbol.typeRef + else ctorResultType.underlyingClassRef(refinementOK = false) typed( untpd.Select( untpd.New(untpd.TypedSplice(tpt.withType(tycon))), nme.CONSTRUCTOR), - pt) - .showing(i"convert creator $tree -> $result", typr) + pt + ).showing(i"convert creator $tree -> $result", typr) /** If `tree` is a constructor proxy reference, return the type it constructs, * otherwise return NoType. @@ -4390,9 +4468,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => NoType case _ => NoType + // begin adapt1 tree match { case _: MemberDef | _: PackageDef | _: Import | _: WithoutTypeOrPos[?] | _: Closure => tree - case _ => tree.tpe.widen match { + case _ => + tree.tpe.widen match case tp: FlexType => ensureReported(tp) tree @@ -4454,7 +4534,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if (ctx.mode is Mode.Type) adaptType(tree.tpe) else adaptNoArgs(wtp) } - } } } @@ -4588,11 +4667,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else false } - private def checkStatementPurity(tree: tpd.Tree)(original: untpd.Tree, exprOwner: Symbol, isUnitExpr: Boolean = false)(using Context): Unit = + private def checkStatementPurity(tree: tpd.Tree)(original: untpd.Tree, exprOwner: Symbol, isUnitExpr: Boolean)(using Context): Unit = if !tree.tpe.isErroneous && !ctx.isAfterTyper - && !tree.isInstanceOf[Inlined] - && isPureExpr(tree) + && tree.match + case Inlined(_, Nil, Literal(k)) if k.tag == UnitTag => false // e.g., assert(2 + 2 == 4) + case tree => isPureExpr(tree) && !isSelfOrSuperConstrCall(tree) then tree match case closureDef(meth) @@ -4606,13 +4686,29 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // sometimes we do not have the original anymore and use the transformed tree instead. // But taken together, the two criteria are quite accurate. missingArgs(tree, tree.tpe.widen) - case _ if tree.hasAttachment(AscribedToUnit) => - // The tree was ascribed to `Unit` explicitly to silence the warning. - () - case _ if isUnitExpr => - report.warning(PureUnitExpression(original, tree.tpe), original.srcPos) - case _ => - report.warning(PureExpressionInStatementPosition(original, exprOwner), original.srcPos) + case tree => + withDiscardWarnable(tree): warnable => + val msg = + if isUnitExpr then PureUnitExpression(original, warnable.tpe) + else PureExpressionInStatementPosition(original, exprOwner) + report.warning(msg, original.srcPos) + + private def checkValueDiscard(tree: tpd.Tree)(using Context): Unit = + if ctx.settings.Whas.valueDiscard && !ctx.isAfterTyper then + withDiscardWarnable(tree): warnable => + report.warning(ValueDiscarding(warnable.tpe), tree.srcPos) + + // Check if the tree was ascribed to `Unit` explicitly to silence the warning. + private def withDiscardWarnable(tree: tpd.Tree)(op: (tpd.Tree) => Unit)(using Context): Unit = + def exempt(tree: tpd.Tree): Boolean = isThisTypeResult(tree) || isAscribedToUnit(tree) + def recur(tree: tpd.Tree): Unit = tree match + case tree: Inlined => + if !exempt(tree.call) then + recur(tree.expansion) + case tree => + if !exempt(tree) then + op(tree) + recur(tree) /** Types the body Scala 2 macro declaration `def f = macro ` */ protected def typedScala2MacroBody(call: untpd.Tree)(using Context): Tree = diff --git a/compiler/src/dotty/tools/dotc/typer/TyperPhase.scala b/compiler/src/dotty/tools/dotc/typer/TyperPhase.scala index 95c507d935b1..ba16e6cdfa87 100644 --- a/compiler/src/dotty/tools/dotc/typer/TyperPhase.scala +++ b/compiler/src/dotty/tools/dotc/typer/TyperPhase.scala @@ -110,7 +110,7 @@ class TyperPhase(addRootImports: Boolean = true) extends Phase { ctx.run.nn.checkSuspendedUnits(newUnits) newUnits - def run(using Context): Unit = unsupported("run") + protected def run(using Context): Unit = unsupported("run") } object TyperPhase { diff --git a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala index b02220b3f393..f16bae9d1a67 100644 --- a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -164,7 +164,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/dotc/util/Chars.scala b/compiler/src/dotty/tools/dotc/util/Chars.scala index cde1a63f5293..d92058b98842 100644 --- a/compiler/src/dotty/tools/dotc/util/Chars.scala +++ b/compiler/src/dotty/tools/dotc/util/Chars.scala @@ -3,7 +3,8 @@ package dotty.tools.dotc.util import scala.annotation.switch import Character.{LETTER_NUMBER, LOWERCASE_LETTER, OTHER_LETTER, TITLECASE_LETTER, UPPERCASE_LETTER} import Character.{MATH_SYMBOL, OTHER_SYMBOL} -import Character.{isJavaIdentifierPart, isUnicodeIdentifierStart, isUnicodeIdentifierPart} +import Character.{isISOControl as isControl, isJavaIdentifierPart, isUnicodeIdentifierStart, isUnicodeIdentifierPart} +import java.lang.StringBuilder /** Contains constants and classifier methods for characters */ object Chars: @@ -50,7 +51,7 @@ object Chars: } /** Is character a whitespace character (but not a new line)? */ - def isWhitespace(c: Char): Boolean = + inline def isWhitespace(c: Char): Boolean = c == ' ' || c == '\t' || c == CR /** Can character form part of a doc comment variable $xxx? */ @@ -110,3 +111,59 @@ object Chars: /** Would the character be encoded by `NameTransformer.encode`? */ def willBeEncoded(c: Char): Boolean = !isJavaIdentifierPart(c) + + private inline def requiresFormat(c: Char): Boolean = (c: @switch) match + case '\b' | '\t' | '\n' | '\f' | '\r' | '"' | '\'' | '\\' => true + case c => isControl(c) + + def escapedString(text: String, quoted: Boolean): String = + inline def doBuild: String = + val b = StringBuilder(text.length + 16) + if quoted then + b.append('"') + var i = 0 + while i < text.length do + escapedChar(b, text.charAt(i)) + i += 1 + if quoted then + b.append('"') + b.toString + var i = 0 + while i < text.length do + if requiresFormat(text.charAt(i)) then return doBuild + i += 1 + if quoted then "\"" + text + "\"" + else text + + def escapedChar(ch: Char): String = + if requiresFormat(ch) then + val b = StringBuilder().append('\'').nn + escapedChar(b, ch) + b.append('\'').toString + else + "'" + ch + "'" + + private def escapedChar(b: StringBuilder, c: Char): Unit = + inline def quadNibble(x: Int, i: Int): Unit = + if i < 4 then + quadNibble(x >> 4, i + 1) + val n = x & 0xF + val c = if (n < 10) '0' + n else 'a' + (n - 10) + b.append(c.toChar) + val replace = (c: @switch) match + case '\b' => "\\b" + case '\t' => "\\t" + case '\n' => "\\n" + case '\f' => "\\f" + case '\r' => "\\r" + case '"' => "\\\"" + case '\'' => "\\\'" + case '\\' => "\\\\" + case c => + if isControl(c) then + b.append("\\u") + quadNibble(c.toInt, 0) + else + b.append(c) + return + b.append(replace) diff --git a/compiler/src/dotty/tools/dotc/util/ReusableInstance.scala b/compiler/src/dotty/tools/dotc/util/ReusableInstance.scala index ec88b5880745..cca7af0d2fa3 100644 --- a/compiler/src/dotty/tools/dotc/util/ReusableInstance.scala +++ b/compiler/src/dotty/tools/dotc/util/ReusableInstance.scala @@ -1,7 +1,7 @@ package dotty.tools.dotc.util import scala.collection.mutable.ArrayBuffer -import scala.util.chaining.* +import dotty.tools.dotc.util.chaining.* /** A wrapper for a list of cached instances of a type `T`. * The wrapper is recursion-reentrant: several instances are kept, so diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index 9d76d2d8b192..2e0e3b00f565 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -107,7 +107,7 @@ object Signatures { def isEnclosingApply(tree: tpd.Tree, span: Span)(using Context): Boolean = tree match - case apply @ Apply(fun, _) => !fun.span.contains(span) && isValid(apply) + case apply @ Apply(fun, _) => !fun.span.contains(span) && !fun.span.isZeroExtent && isValid(apply) case unapply @ UnApply(fun, _, _) => !fun.span.contains(span) && !ctx.definitions.isFunctionNType(tree.tpe) // we want to show tuples in unapply case typeTree @ AppliedTypeTree(fun, _) => !fun.span.contains(span) && isValid(typeTree) @@ -162,7 +162,9 @@ object Signatures { val typeName = fun.symbol.name.show val typeParams = fun.symbol.typeRef.typeParams.map(_.paramName.show).map(TypeParam.apply(_)) val denot = fun.denot.asSingleDenotation - val activeParameter = findCurrentParamIndex(types, span, typeParams.length - 1) + val activeParameter = + val index = findCurrentParamIndex(types, span, typeParams.length - 1) + if index == -1 then 0 else index val signature = Signature(typeName, List(typeParams), Some(typeName) , None, Some(denot)) (activeParameter, 0, List(signature)) @@ -225,7 +227,7 @@ object Signatures { val alternativeIndex = bestAlternative(alternatives, params, paramssListIndex) (alternativeIndex, alternatives) - if alternativeIndex < alternatives.length then + if alternativeIndex < alternatives.length && alternatives(alternativeIndex).symbol.paramSymss.nonEmpty then val alternativeSymbol = alternatives(alternativeIndex).symbol val safeParamssListIndex = paramssListIndex min (alternativeSymbol.paramSymss.length - 1) val previousArgs = alternativeSymbol.paramSymss.take(safeParamssListIndex).foldLeft(0)(_ + _.length) @@ -244,13 +246,18 @@ object Signatures { findCurrentParamIndex(untpdArgs, span, alternativeSymbol.paramSymss(safeParamssListIndex).length - 1) val pre = treeQualifier(fun) - val alternativesWithTypes = alternatives.map(_.asSeenFrom(pre.tpe.widenTermRefExpr)) + val alternativesWithTypes = alternatives.map(_.asSeenFrom(pre.tpe)) val alternativeSignatures = alternativesWithTypes .flatMap(toApplySignature(_, findOutermostCurriedApply(untpdPath), safeParamssListIndex)) + val totalParamCount = alternativeSymbol.paramSymss.foldLeft(0)(_ + _.length) val finalParamIndex = - if currentParamsIndex == -1 then -1 - else previousArgs + currentParamsIndex + val index = if currentParamsIndex == -1 then previousArgs + else previousArgs + currentParamsIndex + // Ensure activeParameter is non-negative (LSP requirement) + // For empty parameter lists, allow index to equal totalParamCount + // so presentation compiler can skip highlighting + index max 0 (finalParamIndex, alternativeIndex, alternativeSignatures) else (0, 0, Nil) @@ -317,7 +324,9 @@ object Signatures { val paramTypes = extractParamTypess(resultType, denot, patterns.size).flatten.map(stripAllAnnots) val paramNames = extractParamNamess(resultType, denot).flatten - val activeParameter = findCurrentParamIndex(patterns, span, paramTypes.length - 1) + val activeParameter = + val index = findCurrentParamIndex(patterns, span, paramTypes.length - 1) + if index == -1 then 0 else index val unapplySignature = toUnapplySignature(denot.asSingleDenotation, paramNames, paramTypes).toList (activeParameter, 0, unapplySignature) @@ -379,7 +388,7 @@ object Signatures { case other => other.stripAnnots /** - * Checks if tree is valid for signatureHelp. Skipped trees are either tuple or function applies + * Checks if tree is valid for signatureHelp. Skips tuple apply trees * * @param tree tree to validate */ @@ -389,12 +398,7 @@ object Signatures { && tree.symbol.exists && ctx.definitions.isTupleClass(tree.symbol.owner.companionClass) - val isFunctionNApply = - tree.symbol.name == nme.apply - && tree.symbol.exists - && ctx.definitions.isFunctionSymbol(tree.symbol.owner) - - !isTupleApply && !isFunctionNApply + !isTupleApply /** * Get unapply method result type omiting unknown types and another method calls. diff --git a/compiler/src/dotty/tools/dotc/util/SourceFile.scala b/compiler/src/dotty/tools/dotc/util/SourceFile.scala index 246255ae3802..8b87bc8f6326 100644 --- a/compiler/src/dotty/tools/dotc/util/SourceFile.scala +++ b/compiler/src/dotty/tools/dotc/util/SourceFile.scala @@ -13,7 +13,7 @@ import Chars.* import scala.annotation.internal.sharable import scala.collection.mutable import scala.collection.mutable.ArrayBuffer -import scala.util.chaining.given +import dotty.tools.dotc.util.chaining.* import java.io.File.separator import java.net.URI @@ -228,12 +228,11 @@ object SourceFile { * It relies on SourceFile#virtual implementation to create the virtual file. */ def virtual(uri: URI, content: String): SourceFile = - val path = Paths.get(uri).toString - SourceFile.virtual(path, content) + SourceFile(new VirtualFile(Paths.get(uri), content.getBytes(StandardCharsets.UTF_8)), content.toCharArray) /** Returns the relative path of `source` within the `reference` path * - * It returns the absolute path of `source` if it is not contained in `reference`. + * It returns the current path under `source.file.jpath` if it is not contained in `reference`. */ def relativePath(source: SourceFile, reference: String): String = { val file = source.file @@ -264,7 +263,7 @@ object SourceFile { val path = refPath.relativize(sourcePath) path.iterator.asScala.mkString("/") else - sourcePath.toString + jpath.toString } /** Return true if file is a script: @@ -275,12 +274,11 @@ object SourceFile { def apply(file: AbstractFile | Null, codec: Codec): SourceFile = // Files.exists is slow on Java 8 (https://rules.sonarsource.com/java/tag/performance/RSPEC-3725), - // so cope with failure; also deal with path prefix "Not a directory". + // so cope with failure. val chars = try new String(file.toByteArray, codec.charSet).toCharArray catch - case _: NoSuchFileException => Array.empty[Char] - case fse: FileSystemException if fse.getMessage.endsWith("Not a directory") => Array.empty[Char] + case _: FileSystemException => Array.empty[Char] if isScript(file, chars) then ScriptSourceFile(file, chars) diff --git a/compiler/src/dotty/tools/dotc/util/Spans.scala b/compiler/src/dotty/tools/dotc/util/Spans.scala index e1487408f36b..33346ad6da17 100644 --- a/compiler/src/dotty/tools/dotc/util/Spans.scala +++ b/compiler/src/dotty/tools/dotc/util/Spans.scala @@ -42,23 +42,26 @@ object Spans { /** The start of this span. */ def start: Int = { - assert(exists) + assert(exists, "start of NoSpan") (coords & StartEndMask).toInt } /** The end of this span */ def end: Int = { - assert(exists) + assert(exists, "end of NoSpan") ((coords >>> StartEndBits) & StartEndMask).toInt } /** The point of this span, returns start for synthetic spans */ def point: Int = { - assert(exists) + assert(exists, "point of NoSpan") val poff = pointDelta if (poff == SyntheticPointDelta) start else start + poff } + def pointMayBeIncorrect = + pointDelta == 0 && end - start >= SyntheticPointDelta + /** The difference between point and start in this span */ def pointDelta: Int = (coords >>> (StartEndBits * 2)).toInt diff --git a/compiler/src/dotty/tools/dotc/util/StackTraceOps.scala b/compiler/src/dotty/tools/dotc/util/StackTraceOps.scala index f991005f0c43..bd5c031a65e0 100644 --- a/compiler/src/dotty/tools/dotc/util/StackTraceOps.scala +++ b/compiler/src/dotty/tools/dotc/util/StackTraceOps.scala @@ -15,7 +15,7 @@ package dotty.tools.dotc.util import scala.language.unsafeNulls import collection.mutable, mutable.ListBuffer -import scala.util.chaining.given +import dotty.tools.dotc.util.chaining.* import java.lang.System.lineSeparator object StackTraceOps: diff --git a/compiler/src/dotty/tools/dotc/util/chaining.scala b/compiler/src/dotty/tools/dotc/util/chaining.scala new file mode 100644 index 000000000000..0c61ab6e73e9 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/util/chaining.scala @@ -0,0 +1,8 @@ + +package dotty.tools.dotc.util + +object chaining: + + extension [A](x: A) + inline def tap(inline f: A => Unit): x.type = { f(x): Unit; x } + inline def pipe[B](inline f: A => B): B = f(x) diff --git a/compiler/src/dotty/tools/io/AbstractFile.scala b/compiler/src/dotty/tools/io/AbstractFile.scala index f34fe6f40b9c..4ba935e8fbd6 100644 --- a/compiler/src/dotty/tools/io/AbstractFile.scala +++ b/compiler/src/dotty/tools/io/AbstractFile.scala @@ -116,7 +116,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] { } /** Returns the underlying Path if any and null otherwise. */ - def jpath: JPath + def jpath: JPath | Null /** An underlying source, if known. Mostly, a zip/jar file. */ def underlyingSource: Option[AbstractFile] = None diff --git a/compiler/src/dotty/tools/io/Jar.scala b/compiler/src/dotty/tools/io/Jar.scala index b4c2024bb487..6076638bff45 100644 --- a/compiler/src/dotty/tools/io/Jar.scala +++ b/compiler/src/dotty/tools/io/Jar.scala @@ -50,7 +50,7 @@ class Jar(file: File) { def mainClass: Option[String] = manifest.map(_(Name.MAIN_CLASS)) /** The manifest-defined classpath String if available. */ def classPathString: Option[String] = - for (m <- manifest ; cp <- m.attrs.get(Name.CLASS_PATH)) yield cp + for (m <- manifest ; cp <- m.attrs.get(Name.CLASS_PATH) if !cp.trim().isEmpty()) yield cp def classPathElements: List[String] = classPathString match { case Some(s) => s.split("\\s+").toList case _ => Nil diff --git a/compiler/src/dotty/tools/io/Path.scala b/compiler/src/dotty/tools/io/Path.scala index c8420c5e381d..2c01b2ae8378 100644 --- a/compiler/src/dotty/tools/io/Path.scala +++ b/compiler/src/dotty/tools/io/Path.scala @@ -13,7 +13,6 @@ import java.net.{URI, URL} import java.nio.file.attribute.{BasicFileAttributes, FileTime} import java.io.IOException import scala.jdk.CollectionConverters.* -import scala.util.Random.alphanumeric /** An abstraction for filesystem paths. The differences between * Path, File, and Directory are primarily to communicate intent. @@ -46,8 +45,6 @@ object Path { else name.substring(i + 1).toLowerCase } - def onlyDirs(xs: Iterator[Path]): Iterator[Directory] = xs.filter(_.isDirectory).map(_.toDirectory) - def onlyDirs(xs: List[Path]): List[Directory] = xs.filter(_.isDirectory).map(_.toDirectory) def onlyFiles(xs: Iterator[Path]): Iterator[File] = xs.filter(_.isFile).map(_.toFile) def roots: List[Path] = FileSystems.getDefault.getRootDirectories.iterator().asScala.map(Path.apply).toList @@ -59,8 +56,6 @@ object Path { else new Path(jpath) } catch { case ex: SecurityException => new Path(jpath) } - /** Avoiding any shell/path issues by only using alphanumerics. */ - private[io] def randomPrefix: String = alphanumeric take 6 mkString "" private[io] def fail(msg: String): Nothing = throw FileOperationException(msg) } import Path.* @@ -82,11 +77,6 @@ class Path private[io] (val jpath: JPath) { def toURI: URI = jpath.toUri() def toURL: URL = toURI.toURL() - /** If this path is absolute, returns it: otherwise, returns an absolute - * path made up of root / this. - */ - def toAbsoluteWithRoot(root: Path): Path = if (isAbsolute) this else root.toAbsolute / this - /** Creates a new Path with the specified path appended. Assumes * the type of the new component implies the type of the result. */ diff --git a/compiler/src/dotty/tools/io/VirtualFile.scala b/compiler/src/dotty/tools/io/VirtualFile.scala index 9d290a9b0e6a..e87faa0b86f9 100644 --- a/compiler/src/dotty/tools/io/VirtualFile.scala +++ b/compiler/src/dotty/tools/io/VirtualFile.scala @@ -40,15 +40,34 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF this.content = content } + /** + * Initializes this instance with the specified path + * and a name taken from the last path element. + * + * @param path the path of the virtual file to be created + * @param content the initial contents of the virtual file + * @return the created virtual file + */ + def this(path: JPath, content: Array[Byte]) = { + this(path.getFileName().toString(), path.toString()) + this.content = content + this.jpath_ = path + } + private var content = Array.emptyByteArray + private var jpath_ : JPath = null + def absolute: AbstractFile = this - /** Returns null. */ - def jpath: JPath = null + /** Returns path, which might be a non-existing file or null. */ + def jpath: JPath = jpath_ override def sizeOption: Option[Int] = Some(content.length) + /** Always returns true, even if jpath is a non-existing file. */ + override def exists: Boolean = true + def input : InputStream = new ByteArrayInputStream(content) override def output: OutputStream = { diff --git a/compiler/src/dotty/tools/package.scala b/compiler/src/dotty/tools/package.scala index f90355b1fa8e..815b906bfad4 100644 --- a/compiler/src/dotty/tools/package.scala +++ b/compiler/src/dotty/tools/package.scala @@ -16,8 +16,6 @@ package object tools { */ transparent inline def uncheckedNN: T = x.asInstanceOf[T] - inline def toOption: Option[T] = - if x == null then None else Some(x.asInstanceOf[T]) end extension /** Nullable eq and ne. */ diff --git a/compiler/src/dotty/tools/repl/JLineTerminal.scala b/compiler/src/dotty/tools/repl/JLineTerminal.scala index e4ac1626525e..929236b414c1 100644 --- a/compiler/src/dotty/tools/repl/JLineTerminal.scala +++ b/compiler/src/dotty/tools/repl/JLineTerminal.scala @@ -37,7 +37,7 @@ class JLineTerminal extends java.io.Closeable { else str protected def promptStr = "scala" private def prompt(using Context) = blue(s"\n$promptStr> ") - private def newLinePrompt(using Context) = blue(" | ") + private def newLinePrompt(using Context) = " " /** Blockingly read line from `System.in` * @@ -82,6 +82,10 @@ class JLineTerminal extends java.io.Closeable { def close(): Unit = terminal.close() + /** Register a signal handler and return the previous handler */ + def handle(signal: org.jline.terminal.Terminal.Signal, handler: org.jline.terminal.Terminal.SignalHandler): org.jline.terminal.Terminal.SignalHandler = + terminal.handle(signal, handler) + /** Provide syntax highlighting */ private class Highlighter(using Context) extends reader.Highlighter { def highlight(reader: LineReader, buffer: String): AttributedString = { diff --git a/compiler/src/dotty/tools/repl/ParseResult.scala b/compiler/src/dotty/tools/repl/ParseResult.scala index 6c9f95d4dca2..2c87a3b10ec8 100644 --- a/compiler/src/dotty/tools/repl/ParseResult.scala +++ b/compiler/src/dotty/tools/repl/ParseResult.scala @@ -52,6 +52,27 @@ object Load { val command: String = ":load" } +/** `:require` is a deprecated alias for :jar` + */ +case class Require(path: String) extends Command +object Require { + val command: String = ":require" +} + +/** `:jar ` adds a jar to the classpath + */ +case class JarCmd(path: String) extends Command +object JarCmd { + val command: String = ":jar" +} + +/** `:kind ` display the kind of a type. see also :help kind + */ +case class KindOf(expr: String) extends Command +object KindOf { + val command: String = ":kind" +} + /** To find out the type of an expression you may simply do: * * ``` @@ -93,6 +114,12 @@ object Reset { val command: String = ":reset" } +/** `:sh ` run a shell command (result is implicitly => List[String]) */ +case class Sh(expr: String) extends Command +object Sh { + val command: String = ":sh" +} + /** Toggle automatic printing of results */ case object Silent extends Command: val command: String = ":silent" @@ -123,7 +150,9 @@ case object Help extends Command { object ParseResult { - @sharable private val CommandExtract = """(:[\S]+)\s*(.*)""".r + // Commands must start with ":" followed by a letter (e.g. :help, :load) + // This ensures operators like :: or :+ are treated as Scala code, not commands + @sharable private val CommandExtract = """(:[a-zA-Z]\S*)\s*(.*)""".r private def parseStats(using Context): List[untpd.Tree] = { val parser = new Parser(ctx.source) @@ -138,10 +167,14 @@ object ParseResult { Help.command -> (_ => Help), Reset.command -> (arg => Reset(arg)), Imports.command -> (_ => Imports), + JarCmd.command -> (arg => JarCmd(arg)), + KindOf.command -> (arg => KindOf(arg)), Load.command -> (arg => Load(arg)), + Require.command -> (arg => Require(arg)), TypeOf.command -> (arg => TypeOf(arg)), DocOf.command -> (arg => DocOf(arg)), Settings.command -> (arg => Settings(arg)), + Sh.command -> (arg => Sh(arg)), Silent.command -> (_ => Silent), ) diff --git a/compiler/src/dotty/tools/repl/ReplCompiler.scala b/compiler/src/dotty/tools/repl/ReplCompiler.scala index 5c8dcc2616b7..e34bb229a727 100644 --- a/compiler/src/dotty/tools/repl/ReplCompiler.scala +++ b/compiler/src/dotty/tools/repl/ReplCompiler.scala @@ -11,16 +11,16 @@ import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.StdNames.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.reporting.Diagnostic -import dotty.tools.dotc.transform.PostTyper +import dotty.tools.dotc.transform.{CheckUnused, CheckShadowing, PostTyper} import dotty.tools.dotc.typer.ImportInfo.{withRootImports, RootRef} import dotty.tools.dotc.typer.TyperPhase import dotty.tools.dotc.util.Spans.* import dotty.tools.dotc.util.{ParsedComment, Property, SourceFile} import dotty.tools.dotc.{CompilationUnit, Compiler, Run} import dotty.tools.repl.results.* +import dotty.tools.dotc.util.chaining.* import scala.collection.mutable -import scala.util.chaining.given /** This subclass of `Compiler` is adapted for use in the REPL. * @@ -36,6 +36,7 @@ class ReplCompiler extends Compiler: List(Parser()), List(ReplPhase()), List(TyperPhase(addRootImports = false)), + List(CheckUnused.PostTyper(), CheckShadowing()), List(CollectTopLevelImports()), List(PostTyper()), ) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index d7ba2d3ce193..0d356bc704a0 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -7,6 +7,7 @@ import java.nio.charset.StandardCharsets import dotty.tools.dotc.ast.Trees.* import dotty.tools.dotc.ast.{tpd, untpd} +import dotty.tools.dotc.classpath.ClassPathFactory import dotty.tools.dotc.config.CommandLineParser.tokenize import dotty.tools.dotc.config.Properties.{javaVersion, javaVmName, simpleVersionString} import dotty.tools.dotc.core.Contexts.* @@ -21,6 +22,7 @@ import dotty.tools.dotc.core.NameOps.* import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.StdNames.* import dotty.tools.dotc.core.Symbols.{Symbol, defn} +import dotty.tools.dotc.core.SymbolLoaders import dotty.tools.dotc.interfaces import dotty.tools.dotc.interactive.Completion import dotty.tools.dotc.printing.SyntaxHighlighting @@ -38,6 +40,7 @@ import org.jline.reader.* import scala.annotation.tailrec import scala.collection.mutable import scala.jdk.CollectionConverters.* +import scala.tools.asm.ClassReader import scala.util.control.NonFatal import scala.util.Using @@ -139,7 +142,9 @@ class ReplDriver(settings: Array[String], * * Possible reason for unsuccessful run are raised flags in CLI like --help or --version */ - final def tryRunning = if shouldStart then runUntilQuit() + final def tryRunning = if shouldStart then + if rootCtx.settings.replQuitAfterInit.value(using rootCtx) then initialState + else runUntilQuit() /** Run REPL with `state` until `:quit` command found * @@ -190,16 +195,44 @@ class ReplDriver(settings: Array[String], val line = terminal.readLine(completer) ParseResult(line) } catch { - case _: EndOfFileException | - _: UserInterruptException => // Ctrl+D or Ctrl+C + case _: EndOfFileException => // Ctrl+D Quit + case _: UserInterruptException => // Ctrl+C at prompt - clear and continue + SigKill } } @tailrec def loop(using state: State)(): State = { + val res = readLine() if (res == Quit) state - else loop(using interpret(res))() + // Ctrl-C pressed at prompt - just continue with same state (line is cleared by JLine) + else if (res == SigKill) loop(using state)() + else { + // Set up interrupt handler for command execution + var firstCtrlCEntered = false + val thread = Thread.currentThread() + val previousSignalHandler = terminal.handle( + org.jline.terminal.Terminal.Signal.INT, + (sig: org.jline.terminal.Terminal.Signal) => { + if (!firstCtrlCEntered) { + firstCtrlCEntered = true + thread.interrupt() + out.println("\nInterrupting running thread, Ctrl-C again to terminate the REPL Process") + } else { + out.println("\nTerminating REPL Process...") + System.exit(130) // Standard exit code for SIGINT + } + } + ) + + val newState = + try interpret(res) + // Restore previous handler + finally terminal.handle(org.jline.terminal.Terminal.Signal.INT, previousSignalHandler) + + loop(using newState)() + } } try runBody { loop() } @@ -511,6 +544,65 @@ class ReplDriver(settings: Array[String], state } + case Require(path) => + out.println(":require is no longer supported, but has been replaced with :jar. Please use :jar") + state + + case JarCmd(path) => + val jarFile = AbstractFile.getDirectory(path) + if (jarFile == null) + out.println(s"""Cannot add "$path" to classpath.""") + state + else + def flatten(f: AbstractFile): Iterator[AbstractFile] = + if (f.isClassContainer) f.iterator().flatMap(flatten) + else Iterator(f) + + def tryClassLoad(classFile: AbstractFile): Option[String] = { + val input = classFile.input + try { + val reader = new ClassReader(input) + val clsName = reader.getClassName.replace('/', '.') + rendering.myClassLoader.loadClass(clsName) + Some(clsName) + } catch + case _: ClassNotFoundException => None + finally { + input.close() + } + } + + try { + val entries = flatten(jarFile) + + val existingClass = entries.filter(_.extension == "class").find(tryClassLoad(_).isDefined) + if (existingClass.nonEmpty) + out.println(s"The path '$path' cannot be loaded, it contains a classfile that already exists on the classpath: ${existingClass.get}") + else inContext(state.context): + val jarClassPath = ClassPathFactory.newClassPath(jarFile) + val prevOutputDir = ctx.settings.outputDir.value + + // add to compiler class path + ctx.platform.addToClassPath(jarClassPath) + SymbolLoaders.mergeNewEntries(defn.RootClass, ClassPath.RootPackage, jarClassPath, ctx.platform.classPath) + + // new class loader with previous output dir and specified jar + val prevClassLoader = rendering.classLoader() + val jarClassLoader = fromURLsParallelCapable( + jarClassPath.asURLs, prevClassLoader) + rendering.myClassLoader = new AbstractFileClassLoader( + prevOutputDir, jarClassLoader) + + out.println(s"Added '$path' to classpath.") + } catch { + case e: Throwable => + out.println(s"Failed to load '$path' to classpath: ${e.getMessage}") + } + state + + case KindOf(expr) => + out.println(s"""The :kind command is not currently supported.""") + state case TypeOf(expr) => expr match { case "" => out.println(s":type ") @@ -533,6 +625,10 @@ class ReplDriver(settings: Array[String], } state + case Sh(expr) => + out.println(s"""The :sh command is deprecated. Use `import scala.sys.process._` and `"command".!` instead.""") + state + case Settings(arg) => arg match case "" => given ctx: Context = state.context @@ -561,6 +657,7 @@ class ReplDriver(settings: Array[String], private object ReplConsoleReporter extends ConsoleReporter.AbstractConsoleReporter { override def posFileStr(pos: SourcePosition) = "" // omit file paths override def printMessage(msg: String): Unit = out.println(msg) + override def echoMessage(msg: String): Unit = printMessage(msg) override def flush()(using Context): Unit = out.flush() } diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index d4f2bdd0991b..e98d3343cd44 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -454,7 +454,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler withDefaultPos(tpd.ref(tp).asInstanceOf[tpd.RefTree]) def apply(sym: Symbol): Ref = assert(sym.isTerm, s"expected a term symbol, but received $sym") - val refTree = tpd.ref(sym) match + val refTree = tpd.generalisedRef(sym) match case t @ tpd.This(ident) => // not a RefTree, so we need to work around this - issue #19732 // ident in `This` can be a TypeIdent of sym, so we manually prepare the ref here, // knowing that the owner is actually `This`. @@ -522,7 +522,22 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def overloaded(qualifier: Term, name: String, targs: List[TypeRepr], args: List[Term], returnType: TypeRepr): Term = withDefaultPos(tpd.applyOverloaded(qualifier, name.toTermName, args, targs, returnType)) def copy(original: Tree)(qualifier: Term, name: String): Select = - tpd.cpy.Select(original)(qualifier, name.toTermName) + original match + case original: tpd.Select if original.name.toString == name => + // Name unchanged: preserve original Name object and use tpd.cpy.Select + // which correctly handles qualifier type changes via derivedSelect + tpd.cpy.Select(original)(qualifier, original.name) + case _ => + // Name changed: resolve the name and create a fresh Select with proper type + val simpleName = name.toTermName + val resolvedName = + val member = qualifier.tpe.member(simpleName) + if member.exists then simpleName + else + val unmangled = simpleName.unmangle(NameKinds.Scala2MethodNameKinds) + if (unmangled ne simpleName) && qualifier.tpe.member(unmangled).exists then unmangled + else simpleName + tpd.Select(qualifier, resolvedName) def unapply(x: Select): (Term, String) = (x.qualifier, x.name.toString) end Select @@ -805,7 +820,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object BlockTypeTest extends TypeTest[Tree, Block]: def unapply(x: Tree): Option[Block & x.type] = x match - case x: (tpd.Block & x.type) => Some(x) + case x: (tpd.Block & x.type) if x.isTerm => Some(x) case _ => None end BlockTypeTest @@ -1390,7 +1405,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object TypeBlockTypeTest extends TypeTest[Tree, TypeBlock]: def unapply(x: Tree): Option[TypeBlock & x.type] = x match - case tpt: (tpd.Block & x.type) => Some(tpt) + case tpt: (tpd.Block & x.type) if x.isType => Some(tpt) case _ => None end TypeBlockTypeTest @@ -1527,6 +1542,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler withDefaultPos(tpd.Bind(sym, pattern)) def copy(original: Tree)(name: String, pattern: Tree): Bind = withDefaultPos(tpd.cpy.Bind(original)(name.toTermName, pattern)) + .withSpan(original.span) def unapply(pattern: Bind): (String, Tree) = (pattern.name.toString, pattern.pattern) end Bind @@ -1795,7 +1811,48 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def termSymbol: Symbol = self.termSymbol def isSingleton: Boolean = self.isSingleton def memberType(member: Symbol): TypeRepr = - member.info.asSeenFrom(self, member.owner) + // This check only fails if no owner of the passed member symbol is related to the `self` type. + // Ideally we would just check if the symbol is a member of + // the type (with self.derivesFrom(member.owner)), but: + // A) the children of enums are not members of those enums, which is unintuitive + // B) this check was added late, risking major hard to fix regressions + // (and failed dotty compilation tests) + // Additionally, it can be useful to be able to learn a type of a symbol using some indirect prefix, + // even if that symbol is not a direct member of that prefix, but a nested one. + def isTypeRelatedToThePassedMember = + import scala.util.boundary + // the symbol for `self` will not exist for LambdaTypes, + // which don't seem to be supported by asSeenFrom anyway + // (but also don't seem to cause crashes, and return no-op member.info, + // which we might not be able to access otherwise, so we need to allow this). + val isLambdaType = self.isInstanceOf[LambdaType] + isLambdaType || boundary { + var checked: Symbol = member + while(checked.exists) { + if self.derivesFrom(checked) + || self.typeSymbol.declarations.contains(checked) + || self.typeSymbol.companionClass.declarations.contains(checked) then + boundary.break(true) + checked = checked.owner + } + boundary.break(false) + } + + xCheckMacroAssert(isTypeRelatedToThePassedMember, s"$member is not a member of ${self.show}") + + // we replace thisTypes here to avoid resolving otherwise unstable prefixes into Nothing + val memberInfo = + if self.typeSymbol.isClassDef then + member.info.substThis(self.classSymbol.asClass, self) + else + member.info + + memberInfo.asSeenFrom(self, member.owner) match + case dotc.core.Types.ClassInfo(prefix, sym, _, _, _) => + // We do not want to expose ClassInfo in the reflect API, instead we change it to a TypeRef, + // see issue #22395 + prefix.select(sym) + case other => other def baseClasses: List[Symbol] = self.baseClasses def baseType(cls: Symbol): TypeRepr = self.baseType(cls) def derivesFrom(cls: Symbol): Boolean = self.derivesFrom(cls) @@ -1821,6 +1878,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def typeArgs: List[TypeRepr] = self match case AppliedType(_, args) => args + case AnnotatedType(parent, _) => parent.typeArgs case _ => List.empty end extension end TypeReprMethods @@ -2411,7 +2469,12 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler end StringConstantTypeTest object StringConstant extends StringConstantModule: - def apply(x: String): StringConstant = dotc.core.Constants.Constant(x) + def apply(x: String): StringConstant = + require(x != null, "value of StringConstant cannot be `null`") + // A `null` constant must be represented as a `NullConstant`, c.f. a + // constant with `tag == NullTag`, which is not a `StringConstant`. + // See issue 23008. + dotc.core.Constants.Constant(x) def unapply(constant: StringConstant): Some[String] = Some(constant.stringValue) end StringConstant @@ -2448,7 +2511,17 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object ClassOfConstant extends ClassOfConstantModule: def apply(x: TypeRepr): ClassOfConstant = - // TODO check that the type is a valid class when creating this constant or let Ycheck do it? + // We only check if the supplied TypeRepr is valid if it contains an Array, + // as so far only that Array could cause issues + def correctTypeApplicationForArray(typeRepr: TypeRepr): Boolean = + val isArray = typeRepr.typeSymbol != dotc.core.Symbols.defn.ArrayClass + typeRepr match + case AppliedType(_, targs) if !targs.isEmpty => true + case _ => isArray + xCheckMacroAssert( + correctTypeApplicationForArray(x), + "Illegal empty Array type constructor. Please supply a type parameter." + ) dotc.core.Constants.Constant(x) def unapply(constant: ClassOfConstant): Some[TypeRepr] = Some(constant.typeValue) end ClassOfConstant @@ -2734,7 +2807,33 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def memberTypes: List[Symbol] = self.typeRef.decls.filter(_.isType) def typeMembers: List[Symbol] = - lookupPrefix.typeMembers.map(_.symbol).toList + // lookupPrefix.typeMembers currently returns a Set wrapped into a unsorted Seq, + // so we try to sort that here (see discussion: https://github.com/scala/scala3/issues/22472), + // without adding too much of a performance hit. + // It first sorts by parents, then for type params by their positioning, then for members + // derived from declarations it sorts them by their name lexicographically + val parentsMap = lookupPrefix.sortedParents.map(_.typeSymbol).zipWithIndex.toList.toMap + val unsortedTypeMembers = lookupPrefix.typeMembers.map(_.symbol).filter(_.exists).toList + unsortedTypeMembers.sortWith { + case (typeA, typeB) => + val msg = "Unknown type member found. Please consider reporting the issue to the compiler. " + assert(parentsMap.contains(typeA.owner), msg) + assert(parentsMap.contains(typeB.owner), msg) + val parentPlacementA = parentsMap(typeA.owner) + val parentPlacementB = parentsMap(typeB.owner) + if (parentPlacementA == parentPlacementB) then + if typeA.isTypeParam && typeB.isTypeParam then + // put type params at the beginning (and sort them by declaration order) + val pl = typeA.owner + val typeParamPositionMap = pl.typeParams.map(_.asInstanceOf[Symbol]).zipWithIndex.toMap + typeParamPositionMap(typeA) < typeParamPositionMap(typeB) + else if typeA.isTypeParam then true + else if typeB.isTypeParam then false + else + // sort by name lexicographically + typeA.name.toString().compareTo(typeB.name.toString()) < 0 + else parentPlacementA < parentPlacementB + }.map(_.asInstanceOf[Symbol]) def declarations: List[Symbol] = self.typeRef.info.decls.toList diff --git a/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala b/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala index 726f2daf1788..a66dabc542e3 100644 --- a/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala +++ b/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala @@ -1,6 +1,7 @@ package scala.quoted package runtime.impl.printers +import scala.collection.mutable import scala.quoted.* object Extractors { @@ -67,6 +68,12 @@ object Extractors { import quotes.reflect.* private val sb: StringBuilder = new StringBuilder + private var recTypeCounter = 0 + private val recTypeIds = mutable.Map.empty[RecursiveType, Int] + + private def nextRecTypeId(): Int = + recTypeCounter += 1 + recTypeCounter def result(): String = sb.result() @@ -226,9 +233,14 @@ object Extractors { case SuperType(thistpe, supertpe) => this += "SuperType(" += thistpe += ", " += supertpe += ")" case RecursiveThis(binder) => - this += "RecursiveThis(" += binder += ")" - case RecursiveType(underlying) => - this += "RecursiveType(" += underlying += ")" + val id = recTypeIds.getOrElse(binder, -1) + if (id == -1) + this += "RecursiveThis(" += binder += ")" + else + this += "RecursiveThis( + val id = recTypeIds.getOrElseUpdate(rt, nextRecTypeId()) + this += "RecursiveType(rec" += id += " => " += underlying += ")" case MethodType(argNames, argTypes, resType) => this += "MethodType(" ++= argNames += ", " ++= argTypes += ", " += resType += ")" case PolyType(argNames, argBounds, resType) => diff --git a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala index 017ee7eecb7e..dbcf8a3a9bdf 100644 --- a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala +++ b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala @@ -1,7 +1,12 @@ package scala.quoted package runtime.impl.printers +import dotty.tools.dotc.util.Chars + import scala.annotation.switch +import scala.collection.mutable + +import java.lang.StringBuilder /** Printer for fully elaborated representation of the source code */ object SourceCode { @@ -97,7 +102,7 @@ object SourceCode { this += lineBreak() += "}" } - def result(): String = sb.result() + def result(): String = sb.toString private def lineBreak(): String = "\n" + (" " * indent) private def doubleLineBreak(): String = "\n\n" + (" " * indent) @@ -438,7 +443,7 @@ object SourceCode { case _ => inParens { printTree(term) - this += (if (dotty.tools.dotc.util.Chars.isOperatorPart(sb.last)) " : " else ": ") + this += (if Chars.isOperatorPart(sb.charAt(sb.length - 1)) then " : " else ": ") def printTypeOrAnnots(tpe: TypeRepr): Unit = tpe match { case AnnotatedType(tp, annot) if tp == term.tpe => printAnnotation(annot) @@ -957,9 +962,6 @@ object SourceCode { } - inline private val qc = '\'' - inline private val qSc = '"' - def printConstant(const: Constant): this.type = const match { case UnitConstant() => this += highlightLiteral("()") case NullConstant() => this += highlightLiteral("null") @@ -970,8 +972,8 @@ object SourceCode { case LongConstant(v) => this += highlightLiteral(v.toString + "L") case FloatConstant(v) => this += highlightLiteral(v.toString + "f") case DoubleConstant(v) => this += highlightLiteral(v.toString) - case CharConstant(v) => this += highlightString(s"${qc}${escapedChar(v)}${qc}") - case StringConstant(v) => this += highlightString(s"${qSc}${escapedString(v)}${qSc}") + case CharConstant(v) => this += highlightString(Chars.escapedChar(v)) + case StringConstant(v) => this += highlightString(Chars.escapedString(v, quoted = true)) case ClassOfConstant(v) => this += "classOf" inSquare(printType(v)) @@ -1444,22 +1446,8 @@ object SourceCode { private def +=(x: Char): this.type = { sb.append(x); this } private def +=(x: String): this.type = { sb.append(x); this } - private def escapedChar(ch: Char): String = (ch: @switch) match { - case '\b' => "\\b" - case '\t' => "\\t" - case '\n' => "\\n" - case '\f' => "\\f" - case '\r' => "\\r" - case '"' => "\\\"" - case '\'' => "\\\'" - case '\\' => "\\\\" - case _ => if ch.isControl then f"${"\\"}u${ch.toInt}%04x" else String.valueOf(ch).nn - } - - private def escapedString(str: String): String = str flatMap escapedChar - - private[this] val names = collection.mutable.Map.empty[Symbol, String] - private[this] val namesIndex = collection.mutable.Map.empty[String, Int] + private val names = collection.mutable.Map.empty[Symbol, String] + private val namesIndex = collection.mutable.Map.empty[String, Int] private def splicedName(sym: Symbol): Option[String] = { if sym.owner.isClassDef then None diff --git a/compiler/test-resources/jars/MyLibrary.scala b/compiler/test-resources/jars/MyLibrary.scala new file mode 100644 index 000000000000..bd752d2df864 --- /dev/null +++ b/compiler/test-resources/jars/MyLibrary.scala @@ -0,0 +1,8 @@ +/** + * JAR used for testing repl :jar + * Generated using: mkdir out; scalac -d out MyLibrary.scala; jar cf mylibrary.jar -C out . + */ +package mylibrary + +object Utils: + def greet(name: String): String = s"Hello, $name!" diff --git a/compiler/test-resources/jars/MyLibrary2.scala b/compiler/test-resources/jars/MyLibrary2.scala new file mode 100644 index 000000000000..df5a944c0bcf --- /dev/null +++ b/compiler/test-resources/jars/MyLibrary2.scala @@ -0,0 +1,9 @@ +/** + * JAR used for testing repl :jar + * Generated using: mkdir out2; scalac -d out MyLibrary2.scala; jar cf mylibrary2.jar -C out2 . + */ +package mylibrary2 + +object Utils2: + def greet(name: String): String = s"Greetings, $name!" + diff --git a/compiler/test-resources/jars/mylibrary.jar b/compiler/test-resources/jars/mylibrary.jar new file mode 100644 index 000000000000..4537e10ee491 Binary files /dev/null and b/compiler/test-resources/jars/mylibrary.jar differ diff --git a/compiler/test-resources/jars/mylibrary2.jar b/compiler/test-resources/jars/mylibrary2.jar new file mode 100644 index 000000000000..1d6584bdf545 Binary files /dev/null and b/compiler/test-resources/jars/mylibrary2.jar differ diff --git a/compiler/test-resources/repl-macros/i15104c b/compiler/test-resources/repl-macros/i15104c index 9f2eb0a4442a..23bb3a4857de 100644 --- a/compiler/test-resources/repl-macros/i15104c +++ b/compiler/test-resources/repl-macros/i15104c @@ -1,6 +1,6 @@ scala> import scala.quoted._ scala> def macroImpl(using Quotes) = Expr(1) -def macroImpl(using x$1: scala.quoted.Quotes): scala.quoted.Expr[Int] +def macroImpl(using x$1: Quotes): Expr[Int] scala> inline def foo = ${ macroImpl } def foo: Int scala> foo diff --git a/compiler/test-resources/repl-macros/i5551 b/compiler/test-resources/repl-macros/i5551 index 0a07e971440d..fe4e73ffa5d5 100644 --- a/compiler/test-resources/repl-macros/i5551 +++ b/compiler/test-resources/repl-macros/i5551 @@ -1,8 +1,6 @@ scala> import scala.quoted._ scala> def assertImpl(expr: Expr[Boolean])(using q: Quotes) = '{ if !($expr) then throw new AssertionError("failed assertion")} -def assertImpl - (expr: scala.quoted.Expr[Boolean]) - (using q: scala.quoted.Quotes): scala.quoted.Expr[Unit] +def assertImpl(expr: Expr[Boolean])(using q: Quotes): Expr[Unit] scala> inline def assert(expr: => Boolean): Unit = ${ assertImpl('{expr}) } def assert(expr: => Boolean): Unit diff --git a/compiler/test-resources/repl/i10355 b/compiler/test-resources/repl/i10355 index 9fa17f9511a1..16502e4bb79b 100644 --- a/compiler/test-resources/repl/i10355 +++ b/compiler/test-resources/repl/i10355 @@ -1,9 +1,5 @@ scala> import scala.quoted._ scala> def foo(expr: Expr[Any])(using Quotes) = expr match { case '{ $x: t } => '{ $x: Any } } -def foo - (expr: scala.quoted.Expr[Any]) - (using x$2: scala.quoted.Quotes): scala.quoted.Expr[Any] +def foo(expr: Expr[Any])(using x$2: Quotes): Expr[Any] scala> def bar(expr: Expr[Any])(using Quotes) = expr match { case '{ $x: t } => '{ val a: t = ??? ; ???} } -def bar - (expr: scala.quoted.Expr[Any]) - (using x$2: scala.quoted.Quotes): scala.quoted.Expr[Nothing] +def bar(expr: Expr[Any])(using x$2: Quotes): Expr[Nothing] diff --git a/compiler/test-resources/repl/i11146 b/compiler/test-resources/repl/i11146 index 1a6b7d414eeb..afdb68ad37d9 100644 --- a/compiler/test-resources/repl/i11146 +++ b/compiler/test-resources/repl/i11146 @@ -22,4 +22,4 @@ scala> object fs2 { class Stream[T] { override def toString = "fs2.Stream(..)" } scala> import fs2.Stream scala> Stream(1) -val res1: fs2.Stream[Int] = fs2.Stream(..) +val res1: Stream[Int] = fs2.Stream(..) diff --git a/compiler/test-resources/repl/i15493 b/compiler/test-resources/repl/i15493 index 670cf8ebcbd2..c0952b821b0f 100644 --- a/compiler/test-resources/repl/i15493 +++ b/compiler/test-resources/repl/i15493 @@ -143,7 +143,7 @@ scala> res33.toString val res34: String = Outer$Foo@17 scala> Vector.unapplySeq(Vector(2)) -val res35: scala.collection.SeqFactory.UnapplySeqWrapper[Int] = scala.collection.SeqFactory$UnapplySeqWrapper@df507bfd +val res35: collection.SeqFactory.UnapplySeqWrapper[Int] = scala.collection.SeqFactory$UnapplySeqWrapper@df507bfd scala> new scala.concurrent.duration.DurationInt(5) -val res36: scala.concurrent.duration.package.DurationInt = scala.concurrent.duration.package$DurationInt@5 +val res36: concurrent.duration.package.DurationInt = scala.concurrent.duration.package$DurationInt@5 diff --git a/compiler/test-resources/repl/i21655 b/compiler/test-resources/repl/i21655 new file mode 100644 index 000000000000..57b09bfad32d --- /dev/null +++ b/compiler/test-resources/repl/i21655 @@ -0,0 +1,2 @@ +scala>:kind +The :kind command is not currently supported. \ No newline at end of file diff --git a/compiler/test-resources/repl/i21657 b/compiler/test-resources/repl/i21657 new file mode 100644 index 000000000000..fa2eec3ac891 --- /dev/null +++ b/compiler/test-resources/repl/i21657 @@ -0,0 +1,2 @@ +scala>:sh +The :sh command is deprecated. Use `import scala.sys.process._` and `"command".!` instead. \ No newline at end of file diff --git a/compiler/test-resources/repl/import b/compiler/test-resources/repl/import index 1bba01cb5443..313532d17d1d 100644 --- a/compiler/test-resources/repl/import +++ b/compiler/test-resources/repl/import @@ -1,9 +1,9 @@ scala> import collection.mutable._ scala> val buf = new ListBuffer[Int] -val buf: scala.collection.mutable.ListBuffer[Int] = ListBuffer() +val buf: ListBuffer[Int] = ListBuffer() scala> buf += 22 -val res0: scala.collection.mutable.ListBuffer[Int] = ListBuffer(22) +val res0: ListBuffer[Int] = ListBuffer(22) scala> buf ++= List(1, 2, 3) -val res1: scala.collection.mutable.ListBuffer[Int] = ListBuffer(22, 1, 2, 3) +val res1: ListBuffer[Int] = ListBuffer(22, 1, 2, 3) scala> buf.toList val res2: List[Int] = List(22, 1, 2, 3) diff --git a/compiler/test-resources/repl/importFromObj b/compiler/test-resources/repl/importFromObj index 281025af45f5..128268e019b8 100644 --- a/compiler/test-resources/repl/importFromObj +++ b/compiler/test-resources/repl/importFromObj @@ -1,6 +1,6 @@ scala> import scala.collection.mutable scala> val buf = mutable.ListBuffer[Int]() -val buf: scala.collection.mutable.ListBuffer[Int] = ListBuffer() +val buf: mutable.ListBuffer[Int] = ListBuffer() scala> object o { val xs = List(1, 2, 3) } // defined object o scala> import o._ @@ -14,7 +14,7 @@ scala> buf += xs | longer explanation available when compiling with `-explain` 1 error found scala> buf ++= xs -val res0: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3) +val res0: mutable.ListBuffer[Int] = ListBuffer(1, 2, 3) scala> import util.foobar -- [E008] Not Found Error: ----------------------------------------------------- 1 | import util.foobar diff --git a/compiler/test-resources/repl/jar-command b/compiler/test-resources/repl/jar-command new file mode 100644 index 000000000000..b0ded22d3654 --- /dev/null +++ b/compiler/test-resources/repl/jar-command @@ -0,0 +1,13 @@ +scala> val z = 1 +val z: Int = 1 + +scala>:jar sbt-test/source-dependencies/canon/actual/a.jar +Added 'sbt-test/source-dependencies/canon/actual/a.jar' to classpath. + +scala> import A.x + +scala> x +val res0: Int = 3 + +scala> z +val res1: Int = 1 diff --git a/compiler/test-resources/repl/jar-errors b/compiler/test-resources/repl/jar-errors new file mode 100644 index 000000000000..7d5720fcb233 --- /dev/null +++ b/compiler/test-resources/repl/jar-errors @@ -0,0 +1,11 @@ +scala>:jar path/does/not/exist +Cannot add "path/does/not/exist" to classpath. + +scala>:jar sbt-test/source-dependencies/canon/actual/a.jar +Added 'sbt-test/source-dependencies/canon/actual/a.jar' to classpath. + +scala>:jar sbt-test/source-dependencies/canon/actual/a.jar +The path 'sbt-test/source-dependencies/canon/actual/a.jar' cannot be loaded, it contains a classfile that already exists on the classpath: sbt-test/source-dependencies/canon/actual/a.jar(A.class) + +scala>:require sbt-test/source-dependencies/canon/actual/a.jar +:require is no longer supported, but has been replaced with :jar. Please use :jar \ No newline at end of file diff --git a/compiler/test-resources/repl/jar-multiple b/compiler/test-resources/repl/jar-multiple new file mode 100644 index 000000000000..453ccc40dbf6 --- /dev/null +++ b/compiler/test-resources/repl/jar-multiple @@ -0,0 +1,32 @@ +scala> val z = 1 +val z: Int = 1 + +scala>:jar compiler/test-resources/jars/mylibrary.jar +Added 'compiler/test-resources/jars/mylibrary.jar' to classpath. + +scala> import mylibrary.Utils + +scala> Utils.greet("Alice") +val res0: String = Hello, Alice! + +scala>:jar compiler/test-resources/jars/mylibrary2.jar +Added 'compiler/test-resources/jars/mylibrary2.jar' to classpath. + +scala> import mylibrary2.Utils2 + +scala> Utils2.greet("Alice") +val res1: String = Greetings, Alice! + +scala> Utils.greet("Alice") +val res2: String = Hello, Alice! + +scala> import mylibrary.Utils.greet + +scala> greet("Tom") +val res3: String = Hello, Tom! + +scala> Utils.greet("Alice") +val res4: String = Hello, Alice! + +scala> z +val res5: Int = 1 diff --git a/compiler/test-resources/type-printer/vals b/compiler/test-resources/type-printer/vals index 21cd311cf284..99ee2f57d9e6 100644 --- a/compiler/test-resources/type-printer/vals +++ b/compiler/test-resources/type-printer/vals @@ -5,6 +5,6 @@ var y: Int = 2 scala> val xs = List(1) val xs: List[Int] = List(1) scala> scala.util.Try(1) -val res0: scala.util.Try[Int] = Success(1) +val res0: util.Try[Int] = Success(1) scala> Map(1 -> "one") val res1: Map[Int, String] = Map(1 -> one) diff --git a/compiler/test/dotc/comptest.scala b/compiler/test/dotc/comptest.scala index fb53f561a94d..2e0184cddb8f 100644 --- a/compiler/test/dotc/comptest.scala +++ b/compiler/test/dotc/comptest.scala @@ -7,7 +7,7 @@ import scala.concurrent.duration._ object comptest extends ParallelTesting { def maxDuration = 3.seconds - def numberOfSlaves = 5 + def numberOfWorkers = 5 def safeMode = false def isInteractive = true def testFilter = Nil diff --git a/compiler/test/dotc/neg-init-global-scala2-library-tasty.excludelist b/compiler/test/dotc/neg-init-global-scala2-library-tasty.excludelist new file mode 100644 index 000000000000..03b020db64d9 --- /dev/null +++ b/compiler/test/dotc/neg-init-global-scala2-library-tasty.excludelist @@ -0,0 +1,21 @@ +## See #18882 +patmat.scala +t9312.scala +unapplySeq-implicit-arg.scala +unapplySeq-implicit-arg2.scala +unapplySeq-implicit-arg3.scala +ScalaCheck.scala +mutable-read8.scala +TypeCast.scala +global-cycle8.scala +global-cycle6.scala +i12544b.scala +t9360.scala +mutable-array.scala +patmat-unapplySeq2.scala +line-spacing.scala +global-list.scala +t5366.scala +mutable-read7.scala +t9115.scala +Color.scala \ No newline at end of file diff --git a/compiler/test/dotc/neg-scala2-library-tasty.excludelist b/compiler/test/dotc/neg-scala2-library-tasty.excludelist new file mode 100644 index 000000000000..d46a021ddd50 --- /dev/null +++ b/compiler/test/dotc/neg-scala2-library-tasty.excludelist @@ -0,0 +1,2 @@ +i8752.scala +f-interpolator-neg.scala # Additional: A pure expression does nothing in statement position diff --git a/compiler/test/dotc/patmat-exhaustivity-scala2-library-tasty.excludelist b/compiler/test/dotc/patmat-exhaustivity-scala2-library-tasty.excludelist new file mode 100644 index 000000000000..6f1717d532fd --- /dev/null +++ b/compiler/test/dotc/patmat-exhaustivity-scala2-library-tasty.excludelist @@ -0,0 +1,4 @@ +t7746.scala # order of exhaustivity suggestions differs +t4408.scala # order of exhaustivity suggestions differs +patmat-ortype.scala # order of exhaustivity suggestions differs +i13003.scala # order of exhaustivity suggestions differs diff --git a/compiler/test/dotc/pos-from-tasty.blacklist b/compiler/test/dotc/pos-from-tasty.excludelist similarity index 100% rename from compiler/test/dotc/pos-from-tasty.blacklist rename to compiler/test/dotc/pos-from-tasty.excludelist diff --git a/compiler/test/dotc/pos-init-global-scala2-library-tasty.excludelist b/compiler/test/dotc/pos-init-global-scala2-library-tasty.excludelist new file mode 100644 index 000000000000..eb60fc3b7c14 --- /dev/null +++ b/compiler/test/dotc/pos-init-global-scala2-library-tasty.excludelist @@ -0,0 +1,5 @@ +## See #18882 +patmat.scala +patmat-interpolator.scala +unapplySeq-implicit-arg-pos.scala +global-cycle11.scala diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.excludelist similarity index 74% rename from compiler/test/dotc/pos-test-pickling.blacklist rename to compiler/test/dotc/pos-test-pickling.excludelist index 1cb73da4c2eb..2f444ac51ca8 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.excludelist @@ -24,16 +24,23 @@ t5031_2.scala i16997.scala i7414.scala i17588.scala +i8300.scala i9804.scala i13433.scala i16649-irrefutable.scala strict-pattern-bindings-3.0-migration.scala +i17186b.scala +i11982a.scala i17255 i17735.scala +i24134 # Tree is huge and blows stack for printing Text i7034.scala +# Causes cyclic reference by interacting with compiler stdlib types +stdlib + # Stale symbol: package object scala seqtype-cycle @@ -57,8 +64,15 @@ i6505.scala i15158.scala i15155.scala i15827.scala +i17149.scala +tuple-fold.scala +mt-redux-norm.perspective.scala i18211.scala +10867.scala +named-tuples1.scala i20897.scala +i20512.scala +i22645b.scala # Opaque type i5720.scala @@ -95,7 +109,7 @@ i13842.scala # Position change under captureChecking boxmap-paper.scala -# Function types print differnt after unpickling since test mispredicts Feature.preFundsEnabled +# Function types print different after unpickling since test mispredicts Feature.preFundsEnabled caps-universal.scala # GADT cast applied to singleton type difference @@ -103,6 +117,7 @@ i4176-gadt.scala # GADT difference i13974a.scala +i15867.scala java-inherited-type1 @@ -115,6 +130,19 @@ i19955a.scala i19955b.scala i20053b.scala +# alias types at different levels of dereferencing +parsercombinators-givens.scala +parsercombinators-givens-2.scala +parsercombinators-ctx-bounds.scala +parsercombinators-this.scala +parsercombinators-arrow.scala +parsercombinators-new-syntax.scala +hylolib-deferred-given +hylolib-cb +hylolib + +# typecheckErrors method unpickling +i21415.scala # LTS specific i21390.TrieMap.scala diff --git a/compiler/test/dotc/run-from-tasty.blacklist b/compiler/test/dotc/run-from-tasty.excludelist similarity index 100% rename from compiler/test/dotc/run-from-tasty.blacklist rename to compiler/test/dotc/run-from-tasty.excludelist diff --git a/compiler/test/dotc/run-macros-scala2-library-tasty.excludelist b/compiler/test/dotc/run-macros-scala2-library-tasty.excludelist new file mode 100644 index 000000000000..6fdfccf7646c --- /dev/null +++ b/compiler/test/dotc/run-macros-scala2-library-tasty.excludelist @@ -0,0 +1,5 @@ +# Checkfile differences for equivalent type +tasty-extractors-1 +tasty-extractors-2 +tasty-extractors-types +type-print diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.excludelist similarity index 92% rename from compiler/test/dotc/run-test-pickling.blacklist rename to compiler/test/dotc/run-test-pickling.excludelist index 32fa44d0cb20..b1a0b6fb67b5 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.excludelist @@ -4,6 +4,7 @@ derive-generic.scala eff-dependent.scala enum-java +enum-java-scala i5257.scala i7212 i7868.scala @@ -27,7 +28,6 @@ tuple-zip.scala tuples1.scala tuples1a.scala tuples1b.scala -typeCheckErrors.scala typeclass-derivation-doc-example.scala typeclass-derivation1.scala typeclass-derivation2.scala @@ -45,8 +45,11 @@ t6138-2 i12656.scala trait-static-forwarder i17255 +named-tuples-strawman-2.scala +25-1 +25-2 # typecheckErrors method unpickling typeCheckErrors.scala i18150.scala - +i22968.scala diff --git a/compiler/test/dotty/Properties.scala b/compiler/test/dotty/Properties.scala index cc47303d5468..4830f1d7b24c 100644 --- a/compiler/test/dotty/Properties.scala +++ b/compiler/test/dotty/Properties.scala @@ -19,7 +19,6 @@ object Properties { /** Are we running on the CI? */ val isRunByCI: Boolean = sys.env.isDefinedAt("DOTTY_CI_RUN") - || sys.env.isDefinedAt("DRONE") // TODO remove this when we drop Drone val testCache: Path = sys.env.get("DOTTY_TEST_CACHE").map(Paths.get(_)).getOrElse { diff --git a/compiler/test/dotty/tools/AnnotationsTests.scala b/compiler/test/dotty/tools/AnnotationsTests.scala index 3998bf7c93c0..68d56241e4c1 100644 --- a/compiler/test/dotty/tools/AnnotationsTests.scala +++ b/compiler/test/dotty/tools/AnnotationsTests.scala @@ -4,17 +4,20 @@ import vulpix.TestConfiguration import org.junit.Test -import dotc.ast.Trees._ -import dotc.core.Decorators._ -import dotc.core.Contexts._ -import dotc.core.Phases._ -import dotc.core.Types._ -import dotc.core.Symbols._ +import dotc.ast.Trees.* +import dotc.ast.tpd +import dotc.core.Decorators.* +import dotc.core.Contexts.* +import dotc.core.Constants.* +import dotc.core.Phases.* +import dotc.core.Types.* +import dotc.core.Symbols.* import java.io.File -import java.nio.file._ +import java.nio.file.* class AnnotationsTest: + def augmentedClassPath(path: Path) = s"${path}${File.pathSeparator}${TestConfiguration.basicClasspath}" @Test def annotTreeNotErased: Unit = withJavaCompiled( @@ -95,3 +98,31 @@ class AnnotationsTest: val term: TermSymbol = requiredClass("java.lang.invoke.MethodHandle").requiredMethod("invokeExact") assert(term.hasAnnotation(defn.NativeAnnot), i"${term.annotations}") } + + @Test def `inlined annot arg is retrieved`: Unit = + val javaSources = + VirtualJavaSource("Param.java", + """|import java.lang.annotation.*; + |@Inherited @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) + |public @interface Param { String[] value(); }""".stripMargin) + :: Nil + val scalaSources = + """object Bar { inline transparent def bar() = Array("a", "b", "c") }""" + :: "@Param(Bar.bar()) class Foo" + :: Nil + withJavaCompiled(javaSources*): javaOutputDir => + inCompilerContext(augmentedClassPath(javaOutputDir), scalaSources = scalaSources*): + val cls = requiredClass("Foo") + val param = requiredClass("Param") + val annots = cls.annotations + assert(annots.size == 2, i"$annots") // includes SourceFile + val annot = annots.find(_.symbol == param).getOrElse(assert(false, s"Missing $param")) + assert(annot.arguments.size == 1, i"${annot.arguments}") + extension (t: tpd.Tree) + def stripNamedArg = tpd.stripNamedArg(t) + def stripInlined = tpd.stripInlined(t) + annot.arguments(0).stripNamedArg.stripInlined match + case Apply(Apply(_, List(Typed(SeqLiteral(ks, _), _))), _) => + assert(ks.length == 3, i"$ks") + assert(ks.map(_.tpe).collect { case ConstantType(Constant(s: String)) => s } == List("a", "b", "c")) + case bad => assert(false, s"Bad arg $bad") diff --git a/compiler/test/dotty/tools/TestSources.scala b/compiler/test/dotty/tools/TestSources.scala index 68a260de81e4..15cb0c741ede 100644 --- a/compiler/test/dotty/tools/TestSources.scala +++ b/compiler/test/dotty/tools/TestSources.scala @@ -11,28 +11,27 @@ object TestSources { // pos tests lists - def posFromTastyBlacklistFile: String = "compiler/test/dotc/pos-from-tasty.blacklist" - def posTestPicklingBlacklistFile: String = "compiler/test/dotc/pos-test-pickling.blacklist" + def posFromTastyExcludelistFile: String = "compiler/test/dotc/pos-from-tasty.excludelist" + def posTestPicklingExcludelistFile: String = "compiler/test/dotc/pos-test-pickling.excludelist" def posTestRecheckExcludesFile: String = "compiler/test/dotc/pos-test-recheck.excludes" def posLazyValsAllowlistFile: String = "compiler/test/dotc/pos-lazy-vals-tests.allowlist" def posLintingAllowlistFile: String = "compiler/test/dotc/pos-linting.allowlist" - def posFromTastyBlacklisted: List[String] = loadList(posFromTastyBlacklistFile) - def posTestPicklingBlacklisted: List[String] = loadList(posTestPicklingBlacklistFile) + def posFromTastyExcludelisted: List[String] = loadList(posFromTastyExcludelistFile) + def posTestPicklingExcludelisted: List[String] = loadList(posTestPicklingExcludelistFile) def posTestRecheckExcluded: List[String] = loadList(posTestRecheckExcludesFile) def posLazyValsAllowlist: List[String] = loadList(posLazyValsAllowlistFile) def posLintingAllowlist: List[String] = loadList(posLintingAllowlistFile) // run tests lists - def runFromTastyBlacklistFile: String = "compiler/test/dotc/run-from-tasty.blacklist" - def runTestPicklingBlacklistFile: String = "compiler/test/dotc/run-test-pickling.blacklist" + def runFromTastyExcludelistFile: String = "compiler/test/dotc/run-from-tasty.excludelist" + def runTestPicklingExcludelistFile: String = "compiler/test/dotc/run-test-pickling.excludelist" def runTestRecheckExcludesFile: String = "compiler/test/dotc/run-test-recheck.excludes" def runLazyValsAllowlistFile: String = "compiler/test/dotc/run-lazy-vals-tests.allowlist" - - def runFromTastyBlacklisted: List[String] = loadList(runFromTastyBlacklistFile) - def runTestPicklingBlacklisted: List[String] = loadList(runTestPicklingBlacklistFile) + def runFromTastyExcludelisted: List[String] = loadList(runFromTastyExcludelistFile) + def runTestPicklingExcludelisted: List[String] = loadList(runTestPicklingExcludelistFile) def runTestRecheckExcluded: List[String] = loadList(runTestRecheckExcludesFile) def runLazyValsAllowlist: List[String] = loadList(runLazyValsAllowlistFile) diff --git a/compiler/test/dotty/tools/backend/jvm/ClassfileParserTest.scala b/compiler/test/dotty/tools/backend/jvm/ClassfileParserTest.scala new file mode 100644 index 000000000000..9263103f1712 --- /dev/null +++ b/compiler/test/dotty/tools/backend/jvm/ClassfileParserTest.scala @@ -0,0 +1,57 @@ +package dotty.tools.backend.jvm + +// painful to do Java reflection stuff without this +import scala.language.unsafeNulls + +import org.junit.Assert.assertEquals +import org.junit.Test + +import java.lang.reflect.Member + +class ClassfileParserTest { + @Test + def noConstantPoolLag(): Unit = { + def constNames(ms: List[Member]) = ms.collect { + case f if f.getName.startsWith("CONSTANT_") => f.getName + }.sorted + + val toDotc = Map( + "CONSTANT_INTERFACE_METHODREF" -> "CONSTANT_INTFMETHODREF", + "CONSTANT_INVOKE_DYNAMIC" -> "CONSTANT_INVOKEDYNAMIC", + "CONSTANT_METHOD_HANDLE" -> "CONSTANT_METHODHANDLE", + "CONSTANT_METHOD_TYPE" -> "CONSTANT_METHODTYPE", + "CONSTANT_NAME_AND_TYPE" -> "CONSTANT_NAMEANDTYPE", + ).withDefault(x => x) + + val asmConsts = constNames(Class.forName("scala.tools.asm.Symbol").getDeclaredFields.toList) + .map(_.stripSuffix("_TAG")) + .map(toDotc) + .::("CONSTANT_UNICODE") + .sorted + // in the Scala 2 version of this test, we also use Java reflection to get the constant + // names out of ClassfileConstants. in Dotty, the constants are `inline val`s, invisible + // to Java reflection, so we hardcode them here + assertEquals(asmConsts, List( + // do not add to this list without also making the corresponding change + // in ClassfileConstants! that would defeat the purpose of the test + "CONSTANT_CLASS", + "CONSTANT_DOUBLE", + "CONSTANT_DYNAMIC", + "CONSTANT_FIELDREF", + "CONSTANT_FLOAT", + "CONSTANT_INTEGER", + "CONSTANT_INTFMETHODREF", + "CONSTANT_INVOKEDYNAMIC", + "CONSTANT_LONG", + "CONSTANT_METHODHANDLE", + "CONSTANT_METHODREF", + "CONSTANT_METHODTYPE", + "CONSTANT_MODULE", + "CONSTANT_NAMEANDTYPE", + "CONSTANT_PACKAGE", + "CONSTANT_STRING", + "CONSTANT_UNICODE", + "CONSTANT_UTF8", + )) + } +} diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala index 6b7af09d4a2d..e72752dac277 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala @@ -83,7 +83,7 @@ trait DottyBytecodeTest { import scala.collection.mutable.ListBuffer def files(dir: AbstractFile): List[(String, Array[Byte])] = { val res = ListBuffer.empty[(String, Array[Byte])] - for (f <- dir.iterator) { + for (f <- dir.iterator()) { if (!f.isDirectory) res += ((f.name, f.toByteArray)) else if (f.name != "." && f.name != "..") res ++= files(f) } diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala index 5388e65d33b7..f5cee3b32a47 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala @@ -1192,21 +1192,11 @@ class DottyBytecodeTests extends DottyBytecodeTest { Field(GETSTATIC, "scala/package$", "MODULE$", "Lscala/package$;"), Invoke(INVOKEVIRTUAL, "scala/package$", "Nil", "()Lscala/collection/immutable/Nil$;", false), VarOp(ALOAD, 2), - VarOp(ASTORE, 7), - Op(DUP), - Jump(IFNONNULL, Label(31)), - Op(POP), - VarOp(ALOAD, 7), - Jump(IFNULL, Label(36)), - Jump(GOTO, Label(40)), - Label(31), - VarOp(ALOAD, 7), - Invoke(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false), - Jump(IFEQ, Label(40)), - Label(36), + Invoke(INVOKESTATIC, "java/util/Objects", "equals", "(Ljava/lang/Object;Ljava/lang/Object;)Z", false), + Jump(IFEQ, Label(28)), IntOp(BIPUSH, 20), Op(IRETURN), - Label(40), + Label(28), TypeOp(NEW, "scala/MatchError"), Op(DUP), VarOp(ALOAD, 2), @@ -1987,6 +1977,67 @@ class DottyBytecodeTests extends DottyBytecodeTest { assertEquals(expected, clsNode.interfaces.asScala) } } + + // Test for https://github.com/scala/scala3/issues/24997 + // Automatic untupling should not introduce extra CHECKCAST instructions for unused tuple elements + @Test def i24997 = { + val source = + """|class Wrapper[A](value: A): + | def use[B](f: A => B): B = f(value) + | + |class Test: + | def withCase: Int = + | val w = Wrapper(("42", "Answer")) + | w.use { case (s, _) => s.length } + | + | def withoutCase: Int = + | val w = Wrapper(("42", "Answer")) + | w.use { (s, _) => s.length } + |""".stripMargin + + checkBCode(source) { dir => + val clsIn = dir.lookupName("Test.class", directory = false).input + val clsNode = loadClassNode(clsIn) + + // Get instructions for both methods + def getCheckCastCount(methodPrefix: String): Int = { + clsNode.methods.asScala.filter(_.name.startsWith(methodPrefix)).map { method => + instructionsFromMethod(method).count { + case TypeOp(Opcodes.CHECKCAST, _) => true + case _ => false + } + }.sum + } + + val withCaseCasts = getCheckCastCount("withCase") + val withoutCaseCasts = getCheckCastCount("withoutCase") + + // Both methods should have the same number of CHECKCAST instructions + // (specifically, they should NOT have extra ones for unused wildcard elements) + assertEquals(s"withCase should have 1 CHECKCAST", 1, withCaseCasts) + assertEquals(s"withoutCase should have 1 CHECKCAST", 1, withoutCaseCasts) + } + } + + @Test def i24997_placeholder = { + // Regression test for https://github.com/scala/scala3/pull/25085 + // Ensure that placeholder syntax `_ + _` works correctly when tupled. + // The previous fix accidentally removed the binding for the used synthetic parameters. + val source = + """|class Test: + | def use(f: ((Int, Int)) => Int): Int = f((1, 2)) + | + | def test: Int = + | use { _ + _ } + |""".stripMargin + checkBCode(source) { dir => + // The main verification is that it compiles. + // We can also check that `test` method exists. + val clsIn = dir.lookupName("Test.class", directory = false).input + val clsNode = loadClassNode(clsIn) + assert(clsNode.methods.asScala.exists(_.name == "test")) + } + } } object invocationReceiversTestCode { diff --git a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala index fcbc738f2934..b490d55bb43f 100644 --- a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala @@ -785,4 +785,36 @@ class InlineBytecodeTests extends DottyBytecodeTest { } } + @Test def inline_match_scrutinee_with_side_effect = { + val source = """class Test: + | inline def inlineTest(): Int = + | inline { + | println("scrutinee") + | (1, 2) + | } match + | case (e1, e2) => e1 + e2 + | + | def test: Int = inlineTest() + """.stripMargin + + checkBCode(source) { dir => + val clsIn = dir.lookupName("Test.class", directory = false).input + val clsNode = loadClassNode(clsIn) + + val fun = getMethod(clsNode, "test") + val instructions = instructionsFromMethod(fun) + val expected = List( + Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), + Ldc(LDC, "scrutinee"), + Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false), + Op(ICONST_3), + Op(IRETURN), + ) + + assert(instructions == expected, + "`i was not properly inlined in `test`\n" + diffInstructions(instructions, expected)) + + } + } + } diff --git a/compiler/test/dotty/tools/backend/jvm/SourcePositionsTest.scala b/compiler/test/dotty/tools/backend/jvm/SourcePositionsTest.scala index 7bb52260c366..877214abca2a 100644 --- a/compiler/test/dotty/tools/backend/jvm/SourcePositionsTest.scala +++ b/compiler/test/dotty/tools/backend/jvm/SourcePositionsTest.scala @@ -114,3 +114,26 @@ class SourcePositionsTest extends DottyBytecodeTest: assertEquals(expected, lineNumbers) } } + + @Test def i23413: Unit = + val code = + """ + |trait Test: + | def mult(x: Int): Int + | + | mult(3) + | mult(4) + | mult(5) + | + """.stripMargin + checkBCode(code): dir => + val testClass = loadClassNode(dir.lookupName("Test.class", directory = false).input, skipDebugInfo = false) + val testMethod = getMethod(testClass, "$init$") + val lineNumbers = instructionsFromMethod(testMethod).collect {case ln: LineNumber => ln} + val expected = List( + LineNumber(5, Label(0)), // mult(3) + LineNumber(6, Label(6)), // mult(4) + LineNumber(7, Label(12)), // mult(5) + ) + assertEquals(expected, lineNumbers) + end i23413 diff --git a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala index ce46b669e2c8..ef2135c6adfe 100644 --- a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala @@ -4,9 +4,7 @@ package dotc import scala.language.unsafeNulls -import org.junit.{ Test, BeforeClass, AfterClass } -import org.junit.Assert._ -import org.junit.Assume._ +import org.junit.{AfterClass, Ignore, Test} import org.junit.experimental.categories.Category import scala.concurrent.duration._ @@ -96,7 +94,7 @@ class BootstrappedOnlyCompilationTests { // Negative tests ------------------------------------------------------------ @Test def negMacros: Unit = { - implicit val testGroup: TestGroup = TestGroup("compileNegWithCompiler") + given TestGroup = TestGroup("negMacros") compileFilesInDir("tests/neg-macros", defaultOptions.and("-Xcheck-macros")) .checkExpectedErrors() } @@ -204,7 +202,7 @@ object BootstrappedOnlyCompilationTests extends ParallelTesting { // Test suite configuration -------------------------------------------------- def maxDuration = 100.seconds - def numberOfSlaves = Runtime.getRuntime().availableProcessors() + def numberOfWorkers = Runtime.getRuntime().availableProcessors() def safeMode = Properties.testsSafeMode def isInteractive = SummaryReport.isInteractive def testFilter = Properties.testsFilter diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index f10e159beb86..b6264fdc2056 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -69,6 +69,7 @@ class CompilationTests { compileFile("tests/rewrites/i17399.scala", unindentOptions.and("-rewrite")), compileFile("tests/rewrites/i20002.scala", defaultOptions.and("-indent", "-rewrite")), compileFile("tests/rewrites/i21382.scala", defaultOptions.and("-indent", "-rewrite")), + compileFile("tests/rewrites/unused.scala", defaultOptions.and("-rewrite", "-Wunused:all")), ).checkRewrites() } @@ -137,6 +138,7 @@ class CompilationTests { "tests/neg-custom-args/toplevel-samesource/nested/S.scala"), defaultOptions), compileFile("tests/neg/i7575.scala", defaultOptions.withoutLanguageFeatures.and("-language:_")), + compileFile("tests/neg-custom-args/i20491/Test.scala", defaultOptions.withClasspath("tests/neg-custom-args/i20491/cp")), ).checkExpectedErrors() } @@ -170,8 +172,8 @@ class CompilationTests { @Test def pickling: Unit = { implicit val testGroup: TestGroup = TestGroup("testPickling") aggregateTests( - compileFilesInDir("tests/pos", picklingOptions, FileFilter.exclude(TestSources.posTestPicklingBlacklisted)), - compileFilesInDir("tests/run", picklingOptions, FileFilter.exclude(TestSources.runTestPicklingBlacklisted)) + compileFilesInDir("tests/pos", picklingOptions, FileFilter.exclude(TestSources.posTestPicklingExcludelisted)), + compileFilesInDir("tests/run", picklingOptions, FileFilter.exclude(TestSources.runTestPicklingExcludelisted)) ).checkCompile() } @@ -234,6 +236,54 @@ class CompilationTests { tests.foreach(_.delete()) } + + /* This tests for errors in the program's TASTy trees. + * The test consists of three files: (a) v1/A, (b) v1/B, and (c) v0/A. (a) and (b) are + * compatible, but (b) and (c) are not. If (b) and (c) are compiled together, there should be + * 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 classA0 = defaultOutputDir + tastyErrorGroup + "/A/v0/A" + val classA1 = defaultOutputDir + tastyErrorGroup + "/A/v1/A" + val classB1 = defaultOutputDir + tastyErrorGroup + "/B/v1/B" + + val tests = List( + compileFile("tests/init/tasty-error/val-or-defdef/v1/A.scala", tastyErrorOptions)(tastyErrorGroup), + compileFile("tests/init/tasty-error/val-or-defdef/v1/B.scala", tastyErrorOptions.withClasspath(classA1))(tastyErrorGroup), + compileFile("tests/init/tasty-error/val-or-defdef/v0/A.scala", tastyErrorOptions)(tastyErrorGroup), + ).map(_.keepOutput.checkCompile()) + + compileFile("tests/init/tasty-error/val-or-defdef/Main.scala", tastyErrorOptions.withClasspath(classA0).withClasspath(classB1))(tastyErrorGroup).checkExpectedErrors() + + tests.foreach(_.delete()) + } + + /* This tests for errors in the program's TASTy trees. + * The test consists of five files: Main, C, v1/A, v1/B, and v0/A. The files v1/A, v1/B, and v0/A all depend on C. v1/A and v1/B are + * compatible, but v1/B and v0/A are not. If v1/B and v0/A are compiled together, there should be + * 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 classC = defaultOutputDir + tastyErrorGroup + "/C/typedef/C" + val classA0 = defaultOutputDir + tastyErrorGroup + "/A/v0/A" + val classA1 = defaultOutputDir + tastyErrorGroup + "/A/v1/A" + val classB1 = defaultOutputDir + tastyErrorGroup + "/B/v1/B" + + val tests = List( + compileFile("tests/init/tasty-error/typedef/C.scala", tastyErrorOptions)(tastyErrorGroup), + compileFile("tests/init/tasty-error/typedef/v1/A.scala", tastyErrorOptions.withClasspath(classC))(tastyErrorGroup), + compileFile("tests/init/tasty-error/typedef/v1/B.scala", tastyErrorOptions.withClasspath(classC).withClasspath(classA1))(tastyErrorGroup), + compileFile("tests/init/tasty-error/typedef/v0/A.scala", tastyErrorOptions.withClasspath(classC))(tastyErrorGroup), + ).map(_.keepOutput.checkCompile()) + + compileFile("tests/init/tasty-error/typedef/Main.scala", tastyErrorOptions.withClasspath(classC).withClasspath(classA0).withClasspath(classB1))(tastyErrorGroup).checkExpectedErrors() + + tests.foreach(_.delete()) + } } // parallel backend tests @@ -273,7 +323,7 @@ object CompilationTests extends ParallelTesting { // Test suite configuration -------------------------------------------------- def maxDuration = 45.seconds - def numberOfSlaves = Runtime.getRuntime().availableProcessors() + def numberOfWorkers = Runtime.getRuntime().availableProcessors() def safeMode = Properties.testsSafeMode def isInteractive = SummaryReport.isInteractive def testFilter = Properties.testsFilter diff --git a/compiler/test/dotty/tools/dotc/FromTastyTests.scala b/compiler/test/dotty/tools/dotc/FromTastyTests.scala index 1d46cbbce95c..fa1d9acff316 100644 --- a/compiler/test/dotty/tools/dotc/FromTastyTests.scala +++ b/compiler/test/dotty/tools/dotc/FromTastyTests.scala @@ -23,7 +23,7 @@ class FromTastyTests { implicit val testGroup: TestGroup = TestGroup("posTestFromTasty") compileTastyInDir(s"tests${JFile.separator}pos", defaultOptions, - fromTastyFilter = FileFilter.exclude(TestSources.posFromTastyBlacklisted) + fromTastyFilter = FileFilter.exclude(TestSources.posFromTastyExcludelisted) ).checkCompile() } @@ -35,7 +35,7 @@ class FromTastyTests { implicit val testGroup: TestGroup = TestGroup("runTestFromTasty") compileTastyInDir(s"tests${JFile.separator}run", defaultOptions, - fromTastyFilter = FileFilter.exclude(TestSources.runFromTastyBlacklisted) + fromTastyFilter = FileFilter.exclude(TestSources.runFromTastyExcludelisted) ).checkRuns() } } @@ -44,7 +44,7 @@ object FromTastyTests extends ParallelTesting { // Test suite configuration -------------------------------------------------- def maxDuration = 30.seconds - def numberOfSlaves = Runtime.getRuntime().availableProcessors() + def numberOfWorkers = Runtime.getRuntime().availableProcessors() def safeMode = Properties.testsSafeMode def isInteractive = SummaryReport.isInteractive def testFilter = Properties.testsFilter diff --git a/compiler/test/dotty/tools/dotc/IdempotencyTests.scala b/compiler/test/dotty/tools/dotc/IdempotencyTests.scala index b515ebb05f96..031e6d0cc7e0 100644 --- a/compiler/test/dotty/tools/dotc/IdempotencyTests.scala +++ b/compiler/test/dotty/tools/dotc/IdempotencyTests.scala @@ -72,7 +72,7 @@ object IdempotencyTests extends ParallelTesting { // Test suite configuration -------------------------------------------------- def maxDuration = 30.seconds - def numberOfSlaves = 5 + def numberOfWorkers = 5 def safeMode = Properties.testsSafeMode def isInteractive = SummaryReport.isInteractive def testFilter = Properties.testsFilter diff --git a/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala b/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala index 50e07f388dc4..c24dc079bfc1 100644 --- a/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala +++ b/compiler/test/dotty/tools/dotc/TastyBootstrapTests.scala @@ -110,7 +110,7 @@ object TastyBootstrapTests extends ParallelTesting { // Test suite configuration -------------------------------------------------- def maxDuration = 45.seconds - def numberOfSlaves = Runtime.getRuntime.availableProcessors() + def numberOfWorkers = Runtime.getRuntime.availableProcessors() def safeMode = Properties.testsSafeMode def isInteractive = SummaryReport.isInteractive def testFilter = Properties.testsFilter diff --git a/compiler/test/dotty/tools/dotc/classpath/MultiReleaseJarTest.scala b/compiler/test/dotty/tools/dotc/classpath/MultiReleaseJarTest.scala index 4375e14fddd8..9199f474dc92 100644 --- a/compiler/test/dotty/tools/dotc/classpath/MultiReleaseJarTest.scala +++ b/compiler/test/dotty/tools/dotc/classpath/MultiReleaseJarTest.scala @@ -15,11 +15,13 @@ import org.junit.Test import scala.jdk.CollectionConverters._ import scala.util.Properties +import org.junit.Assume.assumeTrue + class MultiReleaseJarTest extends dotty.tools.backend.jvm.DottyBytecodeTest { @Test def mrJar(): Unit = { - if (!Properties.isJavaAtLeast("9")) { println("skipping mrJar() on old JDK"); return } + assumeTrue("Multi-release JARs require JDK 9+", Properties.isJavaAtLeast("9")) // The test fails if the same jar file gets reused. This might be a caching problem in our classpath implementation @@ -61,8 +63,7 @@ class MultiReleaseJarTest extends dotty.tools.backend.jvm.DottyBytecodeTest { assertEquals(Set("foo1", "bar1"), apiMethods(jar1, "8")) assertEquals(Set("foo1", "foo2", "bar1"), apiMethods(jar2, "9")) - if Properties.isJavaAtLeast("10") then - assertEquals(Set("foo1", "foo2", "bar1", "bar2"), apiMethods(jar3, "10")) + assertEquals(Set("foo1", "foo2", "bar1", "bar2"), apiMethods(jar3, "10")) } finally List(jar1, jar2, jar3).forall(path => try Files.deleteIfExists(path) @@ -72,7 +73,7 @@ class MultiReleaseJarTest extends dotty.tools.backend.jvm.DottyBytecodeTest { @Test def ctSymTest(): Unit = { - if (!Properties.isJavaAtLeast("9")) { println("skipping mrJar() on old JDK"); return } + assumeTrue("ct.sym cross-compilation requires JDK 9+", Properties.isJavaAtLeast("9")) def classExists(className: String, release: String): Boolean = { given ctx: Context = initCtx.fresh diff --git a/compiler/test/dotty/tools/dotc/config/PropertiesTest.scala b/compiler/test/dotty/tools/dotc/config/PropertiesTest.scala new file mode 100644 index 000000000000..e45ac1f3983f --- /dev/null +++ b/compiler/test/dotty/tools/dotc/config/PropertiesTest.scala @@ -0,0 +1,45 @@ +package dotty.tools.dotc.config + +import org.junit.Before +import org.junit.Test +import org.junit.Assert._ +import scala.language.unsafeNulls + +class PropertiesTest { + final val TestProperty = "dotty.tools.dotc.config.PropertiesTest.__test_property__" + + @Before + def beforeEach(): Unit = { + Properties.clearProp(TestProperty) + } + + @Test + def testPropOrNone(): Unit = { + assertEquals(Properties.propOrNone(TestProperty), None) + + Properties.setProp(TestProperty, "foo") + + assertEquals(Properties.propOrNone(TestProperty), Some("foo")) + } + + @Test + def testPropOrElse(): Unit = { + assertEquals(Properties.propOrElse(TestProperty, "bar"), "bar") + + Properties.setProp(TestProperty, "foo") + + var done = false + assertEquals(Properties.propOrElse(TestProperty, { done = true; "bar" }), "foo") + assertFalse("Does not evaluate alt if not needed", done) + } + + @Test + def testEnvOrElse(): Unit = { + assertEquals(Properties.envOrElse("_PropertiesTest_NOT_DEFINED", "test"), "test") + + var done = false + val envName = System.getenv().keySet().iterator().next() + assertNotEquals(Properties.envOrElse(envName, {done = true; "bar"}), "bar") + assertFalse("Does not evaluate alt if not needed", done) + } +} diff --git a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala index d6ef59e9f00a..1078731e3232 100644 --- a/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala +++ b/compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala @@ -85,7 +85,7 @@ class ScalaSettingsTests: val feat = new Diagnostic.FeatureWarning(msg, util.NoSourcePosition) assertEquals(Action.Error, sut.action(feat)) val warn = new Diagnostic.Warning(msg, util.NoSourcePosition) - assertEquals(Action.Warning, sut.action(warn)) + assertEquals(Action.Default, sut.action(warn)) val nowr = new Diagnostic.Warning("This is a problem.".toMessage, util.NoSourcePosition) assertEquals(Action.Silent, sut.action(nowr)) @@ -136,7 +136,7 @@ class ScalaSettingsTests: ) ) ) - assertEquals(result, Right(reporting.Action.Warning)) + assertEquals(Right(reporting.Action.Default), result) @Test def `WConf src filter silences warnings from a matching path for real file`: Unit = val result = Using.resource(Files.createTempFile("myfile", ".scala").nn) { file => @@ -166,7 +166,7 @@ class ScalaSettingsTests: ) ) }(Files.deleteIfExists(_)) - assertEquals(result, Right(reporting.Action.Warning)) + assertEquals(Right(reporting.Action.Default), result) @Test def `WConf src filter reports an error on an invalid regex`: Unit = val result = wconfSrcFilterTest( @@ -199,4 +199,11 @@ class ScalaSettingsTests: ) assertEquals(result, Right(reporting.Action.Error)) + @Test def `illegal source versions are not accepted when parsing the settings`: Unit = + for source <- SourceVersion.illegalInSettings do + val settings = new ScalaSettings + val result = settings.processArguments(List("-source", source.toString()), true) + assertEquals(0, result.warnings.length) + assertEquals(1, result.errors.length) + end ScalaSettingsTests diff --git a/compiler/test/dotty/tools/dotc/core/TypesTest.scala b/compiler/test/dotty/tools/dotc/core/TypesTest.scala new file mode 100644 index 000000000000..00336dca2b2c --- /dev/null +++ b/compiler/test/dotty/tools/dotc/core/TypesTest.scala @@ -0,0 +1,18 @@ +package dotty.tools.dotc.core + +import dotty.tools.DottyTest +import dotty.tools.dotc.core.Symbols.defn +import dotty.tools.dotc.core.TypeOps + +import org.junit.Test +import org.junit.Assert.assertEquals + +class TypesTest extends DottyTest: + + @Test def tuple3TypeSize = + val tpe = defn.TupleType(3).nn.appliedTo(List(defn.IntType, defn.BooleanType, defn.DoubleType)) + assertEquals(3, tpe.typeSize) + + @Test def tuple3ConsTypeSize = + val tpe = TypeOps.nestedPairs(List(defn.IntType, defn.BooleanType, defn.DoubleType)) + assertEquals(3, tpe.typeSize) \ No newline at end of file diff --git a/compiler/test/dotty/tools/dotc/core/tasty/TastyUnpickerTest.scala b/compiler/test/dotty/tools/dotc/core/tasty/TastyUnpickerTest.scala new file mode 100644 index 000000000000..4c8d53868c07 --- /dev/null +++ b/compiler/test/dotty/tools/dotc/core/tasty/TastyUnpickerTest.scala @@ -0,0 +1,55 @@ +package dotty.tools.dotc.core.tasty + +import dotty.tools.io.VirtualFile +import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.ast.* +import dotty.tools.dotc.ast.tpd.* +import dotty.tools.tasty.UnpickleException +import org.junit.Test +import org.junit.Assert.* + +class TastyUnpickerTest { + private def test(bytes: Int*): Unit = + val base = new ContextBase + given Context = base.initialCtx.fresh + val bytesArray = bytes.map(_.toByte).toArray + val dotty = new DottyUnpickler(bytes = bytesArray) + dotty.enter(roots = Set.empty) + dotty.rootTrees.foreach(t => t.foreachSubTree(identity)) + + // this used to go into an infinite loop as we could 'goto' a negative number when looking for a section end + @Test def regressionInfiniteLoop(): Unit = + assertThrows(classOf[UnpickleException], () => test( + 0x5c, 0xa1, 0xab, 0x1f, 0x9c, 0x88, 0x80, 0xb8, 0x53, 0x63, 0x61, 0x6c, 0x61, 0x20, 0x33, 0x2e, 0x38, 0x2e, 0x32, 0x2d, + 0x52, 0x43, 0x31, 0x2d, 0x62, 0x69, 0x6e, 0x2d, 0x53, 0x4e, 0x41, 0xb0, 0xac, 0xb7, 0xa9, 0x54, 0x2d, 0x6e, 0x6f, 0x6e, + 0x62, 0x9a, 0x90, 0x8b, 0x8c, 0x74, 0x72, 0x0a, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x6f, 0x6c, 0x77, 0x2e, + 0x64, 0x6f, 0x74, 0x3c, 0x00, 0xd5, 0x86, 0x0a, 0xd0, 0x5b, 0x65, 0xe8, 0x00, 0xd4, 0x9d, 0xda, 0xab, 0x1f, 0x3b, 0x18, + 0x02, 0xb6, 0x01, 0x84, 0x41, 0x53, 0x54, 0x73, 0x01, 0x85, 0x7a, 0x63, 0x61, 0x6c, 0x61, 0x01, 0x8a, 0x61, 0x6e, 0x6e, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x02, 0x82, 0x81, 0x82, 0x01, 0x8c, 0x65, 0x78, 0x70, 0x65, 0x72, 0x69, 0x93, + 0x9a, 0x91, 0x8b, 0x61, 0x6c, 0x01, 0x88, 0x6c, 0x5d, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x01, 0x8f, 0x63, 0x61, 0x70, + 0x74, 0x75, 0x72, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x01, 0x86, 0x75, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, + 0x01, 0x86, 0x3c, 0x69, 0x6e, 0x69, 0x74, 0x3e, 0x01, 0x8a, 0x41, 0x6e, 0x6e, 0x6f, 0x69, 0x61, 0x74, 0x74, 0x6f, 0x6e, + 0x02, 0x82, 0x83, 0x89, 0x3f, 0x82, 0x88, 0x8a, 0x01, 0x90, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x41, 0x6e, 0x6e, 0x6e, + 0x69, 0x0b, 0x00, 0x6f, 0x6f, 0x61, 0x01, 0x84, 0x55, 0x6e, 0x69, 0x74, 0x01, 0x8a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x46, 0x69, 0x6c, 0x65, 0x01, 0x88, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x02, 0x82, 0x83, 0x8f, 0x02, 0x82, + 0x90, 0x8e, 0x01, 0x84, 0x6a, 0x61, 0x76, 0x61, 0x01, 0x84, 0x6c, 0x61, 0x6e, 0x67, 0x02, 0x82, 0x92, 0x93, 0x01, 0x86, + 0x53, 0x5d, 0x72, 0x69, 0x6c, 0x67, 0x02, 0x82, 0x94, 0x95, 0x3f, 0x83, 0x88, 0x91, 0x96, 0x01, 0xa9, 0x6c, 0x69, 0x62, + 0x72, 0x61, 0x72, 0x79, 0x2f, 0x73, 0x72, 0x63, 0x2f, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x75, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x2e, 0x73, 0x63, 0x61, 0x6c, 0x61, 0x02, 0x82, + 0x83, 0x84, 0x3f, 0x83, 0x88, 0x99, 0x96, 0x01, 0x9e, 0x75, 0x6e, 0x64, 0xa3, 0x8d, 0xdf, 0x8d, 0x9a, 0x89, 0x96, 0x9a, + 0x77, 0x20, 0x61, 0x73, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x53, 0x49, 0x50, 0x2d, 0x34, 0x36, 0x01, + 0x89, 0x50, 0x6f, 0xd7, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x01, 0x88, 0x41, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x01, 0x8a, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x81, 0x74, 0x6d, 0x72, 0x80, 0xd5, 0x84, 0x83, 0x72, 0xdd, 0x2e, 0x47, + 0xe1, 0x13, 0x24, 0x38, 0x40, 0x40, 0x81, 0x4b, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x0f, 0x0f, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, + 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x9e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x85, 0x02, 0x03, 0x04, 0x81, 0x98, + ) ) +} diff --git a/compiler/test/dotty/tools/dotc/coverage/CoverageTests.scala b/compiler/test/dotty/tools/dotc/coverage/CoverageTests.scala index 17aa49f23768..2c083a332ead 100644 --- a/compiler/test/dotty/tools/dotc/coverage/CoverageTests.scala +++ b/compiler/test/dotty/tools/dotc/coverage/CoverageTests.scala @@ -17,6 +17,7 @@ import scala.language.unsafeNulls import scala.collection.mutable.Buffer import dotty.tools.dotc.util.DiffUtil +import java.nio.charset.StandardCharsets import java.util.stream.Collectors @Category(Array(classOf[BootstrappedOnlyTests])) @@ -119,12 +120,77 @@ class CoverageTests: ) } + @Test + def checkIncrementalCoverage(): Unit = + val target = Files.createTempDirectory("coverage") + val sourceRoot = target.resolve("src") + Files.createDirectory(sourceRoot) + val sourceFile1 = sourceRoot.resolve("file1.scala") + Files.write(sourceFile1, "def file1() = 1".getBytes(StandardCharsets.UTF_8)) + + val coverageOut = target.resolve("coverage-out") + Files.createDirectory(coverageOut) + val options = defaultOptions.and("-Ycheck:instrumentCoverage", "-coverage-out", coverageOut.toString, "-sourceroot", sourceRoot.toString) + compileFile(sourceFile1.toString, options).checkCompile() + + val scoverageFile = coverageOut.resolve("scoverage.coverage") + assert(Files.exists(scoverageFile), s"Expected scoverage file to exist at $scoverageFile") + + locally { + val coverage = Serializer.deserialize(scoverageFile, sourceRoot.toString()) + val filesWithCoverage = coverage.statements.map(_.location.sourcePath.getFileName.toString).toSet + assertEquals(Set("file1.scala"), filesWithCoverage) + } + + val sourceFile2 = sourceRoot.resolve("file2.scala") + Files.write(sourceFile2, "def file2() = 2".getBytes(StandardCharsets.UTF_8)) + + compileFile(sourceFile2.toString, options).checkCompile() + locally { + val coverage = Serializer.deserialize(scoverageFile, sourceRoot.toString()) + val filesWithCoverage = coverage.statements.map(_.location.sourcePath.getFileName.toString).toSet + assertEquals(Set("file1.scala", "file2.scala"), filesWithCoverage) + } + + @Test + def `deleted source files should not be kept in incremental coverage`(): Unit = + val target = Files.createTempDirectory("coverage") + val sourceRoot = target.resolve("src") + Files.createDirectory(sourceRoot) + val sourceFile1 = sourceRoot.resolve("file1.scala") + Files.write(sourceFile1, "def file1() = 1".getBytes(StandardCharsets.UTF_8)) + + val coverageOut = target.resolve("coverage-out") + Files.createDirectory(coverageOut) + val options = defaultOptions.and("-Ycheck:instrumentCoverage", "-coverage-out", coverageOut.toString, "-sourceroot", sourceRoot.toString) + compileFile(sourceFile1.toString, options).checkCompile() + + val scoverageFile = coverageOut.resolve("scoverage.coverage") + assert(Files.exists(scoverageFile), s"Expected scoverage file to exist at $scoverageFile") + + locally { + val coverage = Serializer.deserialize(scoverageFile, sourceRoot.toString()) + val filesWithCoverage = coverage.statements.map(_.location.sourcePath.getFileName.toString).toSet + assertEquals(Set("file1.scala"), filesWithCoverage) + } + + val sourceFile2 = sourceRoot.resolve("file2.scala") + Files.write(sourceFile2, "def file2() = 2".getBytes(StandardCharsets.UTF_8)) + + Files.delete(sourceFile1) + + compileFile(sourceFile2.toString, options).checkCompile() + locally { + val coverage = Serializer.deserialize(scoverageFile, sourceRoot.toString()) + val filesWithCoverage = coverage.statements.map(_.location.sourcePath.getFileName.toString).toSet + assertEquals(Set("file2.scala"), filesWithCoverage) + } object CoverageTests extends ParallelTesting: import scala.concurrent.duration.* def maxDuration = 30.seconds - def numberOfSlaves = 1 + def numberOfWorkers = 1 def safeMode = Properties.testsSafeMode def testFilter = Properties.testsFilter diff --git a/compiler/test/dotty/tools/dotc/parsing/ScannerTest.scala b/compiler/test/dotty/tools/dotc/parsing/ScannerTest.scala index 659cd27e62f4..9f78d0778b41 100644 --- a/compiler/test/dotty/tools/dotc/parsing/ScannerTest.scala +++ b/compiler/test/dotty/tools/dotc/parsing/ScannerTest.scala @@ -10,7 +10,7 @@ import org.junit.Test class ScannerTest extends DottyTest { - val blackList = List( + val excluded = List( "/scaladoc/scala/tools/nsc/doc/html/page/Index.scala", "/scaladoc/scala/tools/nsc/doc/html/page/Template.scala" ) @@ -33,13 +33,13 @@ class ScannerTest extends DottyTest { def scanDir(path: String): Unit = scanDir(Directory(path)) def scanDir(dir: Directory): Unit = { - if (blackList exists (dir.jpath.toString endsWith _)) - println(s"blacklisted package: ${dir.toAbsolute.jpath}") + if (excluded exists (dir.jpath.toString endsWith _)) + println(s"excluded package: ${dir.toAbsolute.jpath}") else for (f <- dir.files) if (f.name.endsWith(".scala")) - if (blackList exists (f.jpath.toString endsWith _)) - println(s"blacklisted file: ${f.toAbsolute.jpath}") + if (excluded exists (f.jpath.toString endsWith _)) + println(s"excluded file: ${f.toAbsolute.jpath}") else scan(new PlainFile(f)) for (d <- dir.dirs) diff --git a/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala b/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala index cfcade9be5ab..d33ec9b31e0e 100644 --- a/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala +++ b/compiler/test/dotty/tools/dotc/printing/PrintingTest.scala @@ -1,6 +1,7 @@ package dotty package tools package dotc +package printing import scala.language.unsafeNulls @@ -27,10 +28,10 @@ class PrintingTest { def options(phase: String, flags: List[String]) = val outDir = ParallelTesting.defaultOutputDir + "printing" + File.pathSeparator File(outDir).mkdirs() - List(s"-Xprint:$phase", "-color:never", "-nowarn", "-d", outDir, "-classpath", TestConfiguration.basicClasspath) ::: flags + List(s"-Vprint:$phase", "-color:never", "-nowarn", "-d", outDir, "-classpath", TestConfiguration.basicClasspath) ::: flags private def compileFile(path: JPath, phase: String): Boolean = { - val baseFilePath = path.toString.stripSuffix(".scala") + val baseFilePath = path.toString.stripSuffix(".scala").stripSuffix(".java") val checkFilePath = baseFilePath + ".check" val flagsFilePath = baseFilePath + ".flags" val byteStream = new ByteArrayOutputStream() @@ -51,9 +52,10 @@ class PrintingTest { FileDiff.checkAndDumpOrUpdate(path.toString, actualLines.toIndexedSeq, checkFilePath) } - def testIn(testsDir: String, phase: String) = + def testIn(testsDir: String, phase: String, exclude: io.Path => Boolean = _ => false) = val res = Directory(testsDir).list.toList .filter(f => f.extension == "scala") + .filterNot(exclude) .map { f => compileFile(f.jpath, phase) } val failed = res.filter(!_) @@ -66,6 +68,8 @@ class PrintingTest { end testIn + private val isAtLeastJDK9 = scala.util.Properties.isJavaAtLeast("9") + @Test def printing: Unit = testIn("tests/printing", "typer") @@ -73,5 +77,9 @@ class PrintingTest { def untypedPrinting: Unit = testIn("tests/printing/untyped", "parser") @Test - def transformedPrinting: Unit = testIn("tests/printing/transformed", "repeatableAnnotations") + def transformedPrinting: Unit = testIn("tests/printing/transformed", "repeatableAnnotations", + exclude = path => + if isAtLeastJDK9 then false // test all + else Seq("lazy-vals-new-varhandle.scala").contains(path.name) + ) } diff --git a/compiler/test/dotty/tools/dotc/reporting/CodeActionTest.scala b/compiler/test/dotty/tools/dotc/reporting/CodeActionTest.scala index 870da08dcfba..519df1f40785 100644 --- a/compiler/test/dotty/tools/dotc/reporting/CodeActionTest.scala +++ b/compiler/test/dotty/tools/dotc/reporting/CodeActionTest.scala @@ -4,6 +4,7 @@ import dotty.tools.DottyTest import dotty.tools.dotc.rewrites.Rewrites import dotty.tools.dotc.rewrites.Rewrites.ActionPatch import dotty.tools.dotc.util.SourceFile +import dotty.tools.dotc.core.Contexts._ import scala.annotation.tailrec import scala.jdk.CollectionConverters.* @@ -136,14 +137,138 @@ class CodeActionTest extends DottyTest: afterPhase = "patternMatcher" ) + @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 = val rep = new StoreReporter(null) with UniqueMessagePositions with HideNonSensicalMessages initialCtx.setReporter(rep).withoutColors - private def checkCodeAction(code: String, title: String, expected: String, afterPhase: String = "typer") = - ctx = newContext + private def checkCodeAction(code: String, title: String, expected: String, afterPhase: String = "typer", ctxx: Context = newContext) = + ctx = ctxx val source = SourceFile.virtual("test", code).content val runCtx = checkCompile(afterPhase, code) { (_, _) => () } val diagnostics = runCtx.reporter.removeBufferedMessages diff --git a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala index f68bc951a1df..f88af43a4a22 100644 --- a/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala +++ b/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala @@ -1,12 +1,19 @@ -package dotty.tools +package dotty +package tools package dotc package reporting -import scala.language.unsafeNulls -import java.io.{BufferedReader, FileInputStream, FileOutputStream, FileReader, PrintStream, PrintWriter, StringReader, StringWriter, File as JFile} +import java.io.{File as JFile, *} +import java.nio.file.Files.readAllLines import java.text.SimpleDateFormat import java.util.Date + +import core.Contexts.* import core.Decorators.* +import interfaces.Diagnostic.{ERROR, WARNING} +import io.AbstractFile +import util.SourcePosition +import Diagnostic.* import scala.collection.mutable import scala.jdk.CollectionConverters.* @@ -29,13 +36,17 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M final def messages: Iterator[String] = _messageBuf.iterator protected final val _consoleBuf = new StringWriter - protected final val _consoleReporter = new ConsoleReporter(null, new PrintWriter(_consoleBuf)) + protected final val _consoleReporter = new TestConsoleReporter(new PrintWriter(_consoleBuf)): + override protected def renderPath(file: AbstractFile): String = TestReporter.renderPath(file) + final def consoleOutput: String = _consoleBuf.toString private var _skip: Boolean = false final def setSkip(): Unit = _skip = true final def skipped: Boolean = _skip + override protected def renderPath(file: AbstractFile): String = TestReporter.renderPath(file) + protected final def inlineInfo(pos: SourcePosition)(using Context): String = if (pos.exists) { if (pos.outer.exists) @@ -69,13 +80,24 @@ extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with M case _ => "" } - if dia.level >= WARNING then + if _consoleReporter.isDiagnosticRelevant(dia) then _consoleReporter.doReport(dia) _diagnosticBuf.append(dia) printMessageAndPos(dia, extra) } } +class TestConsoleReporter(writer: PrintWriter) extends ConsoleReporter(scala.Console.in, writer) { + // Report even info-level diagnostics if they've been explicitly enabled by -Ylog + def isDiagnosticRelevant(dia: Diagnostic)(using Context): Boolean = + dia.level >= WARNING || ctx.settings.Ylog.value.containsPhase(ctx.phase) + + /** Print the message with the given position indication. */ + override def doReport(dia: Diagnostic)(using Context): Unit = + if isDiagnosticRelevant(dia) then printMessage(messageAndPos(dia)) + else echoMessage(messageAndPos(dia)) +} + object TestReporter { private val testLogsDirName: String = "testlogs" private val failedTestsFileName: String = "last-failed.log" @@ -112,7 +134,7 @@ object TestReporter { def logPath: String = { initLog() - outFile.getCanonicalPath + outFile.getCanonicalPath.nn } def reporter(ps: PrintStream, logLevel: Int): TestReporter = @@ -149,10 +171,16 @@ object TestReporter { Properties.rerunFailed && failedTestsFile.exists() && failedTestsFile.isFile - )(java.nio.file.Files.readAllLines(failedTestsFile.toPath).asScala.toList) + )(readAllLines(failedTestsFile.toPath).nn.asScala.toList) def writeFailedTests(tests: List[String]): Unit = initLog() tests.foreach(failed => failedTestsWriter.println(failed)) failedTestsWriter.flush() + + def renderPath(file: AbstractFile): String = + if JFile.separatorChar == '\\' then + file.path.replace('\\', '/').nn + else + file.path } diff --git a/compiler/test/dotty/tools/dotc/sbt/ProgressCallbackTest.scala b/compiler/test/dotty/tools/dotc/sbt/ProgressCallbackTest.scala index 489dc0f1759c..afe1b54665bd 100644 --- a/compiler/test/dotty/tools/dotc/sbt/ProgressCallbackTest.scala +++ b/compiler/test/dotty/tools/dotc/sbt/ProgressCallbackTest.scala @@ -7,7 +7,6 @@ import dotty.tools.dotc.sbt.ProgressCallbackTest.* import org.junit.Assert.* import org.junit.Test -import dotty.tools.toOption import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Contexts.ctx import dotty.tools.dotc.CompilationUnit diff --git a/compiler/test/dotty/tools/dotc/semanticdb/DocumentPrinter.scala b/compiler/test/dotty/tools/dotc/semanticdb/DocumentPrinter.scala new file mode 100644 index 000000000000..22e98dd2c564 --- /dev/null +++ b/compiler/test/dotty/tools/dotc/semanticdb/DocumentPrinter.scala @@ -0,0 +1,46 @@ +package dotty.tools.dotc.semanticdb + +import dotty.tools.dotc.semanticdb.Scala3.{*, given} +import dotty.tools.dotc.util.SourceFile + +object DocumentPrinter: + /** Pretty-prints a SemanticDB `TextDocument` provided in its serialized form. Intended for testing and debugging. */ + def textDocumentPrettyPrint(bytes: Array[Byte]): String = + DocumentPrinter.printTextDocument(TextDocument.parseFrom(bytes)) + + /** Pretty-prints a text document with symbol occurrences next to each resolved identifier. + * + * Useful for testing purposes to ensure that SymbolOccurrence values make sense and are correct. + * Example output (NOTE, slightly modified to avoid "unclosed comment" errors): + * {{{ + * class Example *example/Example#* { + * val a *example/Example#a.* : String *scala/Predef.String#* = "1" + * } + * }}} + **/ + private def printTextDocument(doc: TextDocument): String = + val symtab = doc.symbols.iterator.map(info => info.symbol -> info).toMap + val sb = StringBuilder(1000) + val sourceFile = SourceFile.virtual(doc.uri, doc.text) + var offset = 0 + for occ <- doc.occurrences.sorted do + val range = occ.range.get + val end = math.max( + offset, + sourceFile.lineToOffset(range.endLine) + range.endCharacter + ) + val isPrimaryConstructor = + symtab.get(occ.symbol).exists(_.isPrimary) + if !occ.symbol.isPackage && !isPrimaryConstructor then + assert(end <= doc.text.length, + s"doc is only ${doc.text.length} - offset=$offset, end=$end , symbol=${occ.symbol} in source ${sourceFile.name}") + sb.append(doc.text.substring(offset, end)) + sb.append("/*") + .append(if (occ.role.isDefinition) "<-" else "->") + .append(occ.symbol.replace("/", "::")) + .append("*/") + offset = end + assert(offset <= doc.text.length, s"absurd offset = $offset when doc is length ${doc.text.length}") + sb.append(doc.text.substring(offset)) + sb.toString + end printTextDocument diff --git a/compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala b/compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala index de8c2c11f9c2..dd0d89372bed 100644 --- a/compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala +++ b/compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala @@ -1,27 +1,25 @@ package dotty.tools.dotc.semanticdb import scala.language.unsafeNulls - import java.net.URLClassLoader import java.util.regex.Pattern -import java.io.File -import java.nio.file._ +import java.io.{ByteArrayOutputStream, File} +import java.nio.file.* import java.nio.charset.StandardCharsets import java.util.stream.Collectors import java.util.Comparator import scala.util.control.NonFatal import scala.collection.mutable -import scala.jdk.CollectionConverters._ - +import scala.jdk.CollectionConverters.* import javax.tools.ToolProvider - -import org.junit.Assert._ +import org.junit.Assert.* import org.junit.Test import org.junit.experimental.categories.Category - import dotty.BootstrappedOnlyTests import dotty.tools.dotc.Main +import dotty.tools.dotc.semanticdb import dotty.tools.dotc.semanticdb.Scala3.given +import dotty.tools.dotc.semanticdb.internal.SemanticdbOutputStream import dotty.tools.dotc.util.SourceFile @main def updateExpect = @@ -138,12 +136,13 @@ class SemanticdbTests: "-feature", "-deprecation", // "-Ydebug-flags", - // "-Xprint:extractSemanticDB", + // "-Vprint:extractSemanticDB", "-sourceroot", expectSrc.toString, "-classpath", target.toString, "-Xignore-scala2-macros", "-usejavacp", - "-Wunused:all" + "-Wunused:all", + "-Yreporter:dotty.tools.dotc.reporting.Reporter$SilentReporter", ) ++ inputFiles().map(_.toString) val exit = Main.process(args) assertFalse(s"dotc errors: ${exit.errorCount}", exit.hasErrors) @@ -152,41 +151,11 @@ class SemanticdbTests: end SemanticdbTests object SemanticdbTests: - /** Prettyprint a text document with symbol occurrences next to each resolved identifier. - * - * Useful for testing purposes to ensure that SymbolOccurrence values make sense and are correct. - * Example output (NOTE, slightly modified to avoid "unclosed comment" errors): - * {{{ - * class Example *example/Example#* { - * val a *example/Example#a.* : String *scala/Predef.String#* = "1" - * } - * }}} - **/ def printTextDocument(doc: TextDocument): String = - val symtab = doc.symbols.iterator.map(info => info.symbol -> info).toMap - val sb = StringBuilder(1000) - val sourceFile = SourceFile.virtual(doc.uri, doc.text) - var offset = 0 - for occ <- doc.occurrences.sorted do - val range = occ.range.get - val end = math.max( - offset, - sourceFile.lineToOffset(range.endLine) + range.endCharacter - ) - val isPrimaryConstructor = - symtab.get(occ.symbol).exists(_.isPrimary) - if !occ.symbol.isPackage && !isPrimaryConstructor then - assert(end <= doc.text.length, - s"doc is only ${doc.text.length} - offset=$offset, end=$end , symbol=${occ.symbol} in source ${sourceFile.name}") - sb.append(doc.text.substring(offset, end)) - sb.append("/*") - .append(if (occ.role.isDefinition) "<-" else "->") - .append(occ.symbol.replace("/", "::")) - .append("*/") - offset = end - assert(offset <= doc.text.length, s"absurd offset = $offset when doc is length ${doc.text.length}") - sb.append(doc.text.substring(offset)) - sb.toString + val byteStream = new ByteArrayOutputStream() + val out = SemanticdbOutputStream.newInstance(byteStream) + doc.writeTo(out) + out.flush() + DocumentPrinter.textDocumentPrettyPrint(byteStream.toByteArray.nn) end printTextDocument - end SemanticdbTests diff --git a/compiler/test/dotty/tools/dotc/typer/DivergenceChecker.scala b/compiler/test/dotty/tools/dotc/typer/DivergenceChecker.scala index 520b3d2de57e..da3482a43d27 100644 --- a/compiler/test/dotty/tools/dotc/typer/DivergenceChecker.scala +++ b/compiler/test/dotty/tools/dotc/typer/DivergenceChecker.scala @@ -50,8 +50,8 @@ class DivergenceCheckerTests extends DottyTest { 1, 1, 1, - 3, - 5 + 4, + 6 ) tpes.lazyZip(expectedSizes).lazyZip(expectedCoveringSets).foreach { diff --git a/compiler/test/dotty/tools/dotc/typer/NamerRecompileTest.scala b/compiler/test/dotty/tools/dotc/typer/NamerRecompileTest.scala new file mode 100644 index 000000000000..b08ac4403c05 --- /dev/null +++ b/compiler/test/dotty/tools/dotc/typer/NamerRecompileTest.scala @@ -0,0 +1,84 @@ +package dotty.tools.dotc.typer + +import scala.language.unsafeNulls + +import dotty.tools.dotc.Main +import dotty.tools.dotc.interfaces.Diagnostic.ERROR +import dotty.tools.dotc.reporting.TestReporter +import dotty.tools.io.{Directory, File} +import dotty.tools.vulpix.TestConfiguration + +import org.junit.Test +import org.junit.Assert.{assertFalse, assertTrue} + +/** Regression test for https://github.com/scala/scala3/issues/23043 + * + * A compiler plugin that collects per-symbol information will naturally write + * files into the class output directory using the symbol's full name split on + * dots as the path, e.g. + * + * val parts = symbol.fullName.split('.') + * val dir = parts.init.foldLeft(outputDir)(_.subdirectoryNamed(_)) + * dir.fileNamed(parts.last + ".myext") + * + * For an object `testpkg.Scope` this creates a directory `testpkg/Scope/` + * inside the class output. The classpath scanner then treats that directory as + * a package and on the next compilation (with the output dir on the classpath, + * as sbt does) the compiler throws: + * + * TypeError: package testpkg contains object and package with same name: Scope + * + * The fix skips entering a directory as a package when it conflicts with an + * existing symbol and the directory contains no class/tasty files or + * sub-packages. + */ +class NamerRecompileTest: + + @Test def spuriousPackageDirectoryDoesNotConflictWithObject(): Unit = + Directory.inTempDirectory { tmp => + val srcFile = tmp./(File("Test.scala")).toAbsolute + srcFile.writeAll( + """package testpkg + | + |object Scope: + | enum MyEnum: + | case A + | case B(x: Int) + |""".stripMargin + ) + + val out = tmp./("out") + out.createDirectory() + val outPath = out.toAbsolute.toString + + val baseOptions = TestConfiguration.defaultOptions + .and("-d", outPath) + + // First compilation: should succeed + val options1 = baseOptions.and(srcFile.toString) + val reporter1 = TestReporter.reporter(System.out, logLevel = ERROR) + Main.process(options1.all, reporter1) + assertFalse("First compilation failed.", reporter1.hasErrors) + + // Simulate a compiler plugin writing per-symbol data into the output + // directory. A plugin that does: + // val parts = "testpkg.Scope".split('.') // Array("testpkg", "Scope") + // val dir = parts.init.foldLeft(out)(_.subdirectoryNamed(_)) + // dir.fileNamed(parts.last + ".sir") + // will create testpkg/Scope/ which the classpath scanner sees as a package. + val pluginDir = out./("testpkg")./("Scope") + pluginDir.createDirectory() + val pluginFile = pluginDir./(File("MyEnum.sir")).toAbsolute + pluginFile.writeAll("plugin data") + assertTrue("Plugin directory should exist", pluginDir.isDirectory) + + // Second compilation with output on classpath (as sbt does): + // should still succeed despite the plugin-created directory + val options2 = baseOptions.withClasspath(outPath).and(srcFile.toString) + val reporter2 = TestReporter.reporter(System.out, logLevel = ERROR) + Main.process(options2.all, reporter2) + assertFalse( + "Second compilation failed with spurious package conflict (i23043).", + reporter2.hasErrors + ) + } diff --git a/compiler/test/dotty/tools/io/ZipArchiveTest.scala b/compiler/test/dotty/tools/io/ZipArchiveTest.scala index 4959eb2986fd..a36f9d3fcd53 100644 --- a/compiler/test/dotty/tools/io/ZipArchiveTest.scala +++ b/compiler/test/dotty/tools/io/ZipArchiveTest.scala @@ -6,7 +6,6 @@ import java.io.IOException import java.net.{URI, URL, URLClassLoader} import java.nio.file.{Files, Path, Paths} import java.util.jar.{Attributes, Manifest, JarEntry, JarOutputStream} -import java.lang.invoke.{MethodHandles, MethodType} import org.junit.Assert._ import org.junit.Test @@ -45,30 +44,11 @@ class ZipArchiveTest { } } - private val bootClassLoader: ClassLoader = { - if (!util.Properties.isJavaAtLeast("9")) null - else { - try { - MethodHandles - .lookup() - .findStatic( - classOf[ClassLoader], - "getPlatformClassLoader", - MethodType.methodType(classOf[ClassLoader]) - ) - .invoke() - .asInstanceOf[ClassLoader] - } catch { - case _: Throwable => - null - } - } - } - private def classLoader(location: URI): ClassLoader = - new URLClassLoader(Array(location.toURL), bootClassLoader) + new URLClassLoader(Array(location.toURL), /*parent*/ null) - private def manifestAt(location: URI): URL = classLoader(location).getResource("META-INF/MANIFEST.MF") + private def manifestAt(location: URI): URL = + classLoader(location).getResource("META-INF/MANIFEST.MF") // ZipArchive.fromManifestURL(URL) @@ -76,7 +56,7 @@ class ZipArchiveTest { val jar = createTestJar() val archive = new ManifestResources(manifestAt(jar.toUri)) try { - val it = archive.iterator + val it = archive.iterator() assertTrue(it.hasNext) val f = it.next() assertFalse(it.hasNext) diff --git a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala index 9fe5ffcf072e..1bc751402bb6 100644 --- a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala +++ b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala @@ -77,7 +77,7 @@ class ReplCompilerTests extends ReplTest: assertEquals(1, summon[State].imports.size) run("""mutable.Map("one" -> 1)""") assertEquals( - "val res0: scala.collection.mutable.Map[String, Int] = HashMap(one -> 1)", + "val res0: mutable.Map[String, Int] = HashMap(one -> 1)", storedOutput().trim ) } @@ -400,6 +400,39 @@ class ReplCompilerTests extends ReplTest: assertTrue(all.head.startsWith("-- [E103] Syntax Error")) assertTrue(all.exists(_.trim().startsWith("| Illegal start of statement: this modifier is not allowed here"))) + @Test def `i22844 regression colon eol`: Unit = initially: + run: + """|println: + | "hello, world" + |""".stripMargin // outdent, but this test does not exercise the bug + assertEquals(List("hello, world"), lines()) + + // i24142: Colon operator at line beginning should be treated as Scala code, not REPL command + @Test def `i24142 cons operator`: Unit = initially: + run("::(1, Nil)") + assertEquals("val res0: ::[Int] = List(1)", storedOutput().trim) + + @Test def `i24142 symbolic identifier starting with colon`: Unit = initially: + // Test a custom identifier starting with : + run("val `:test` = 42") + assertEquals("val :test: Int = 42", storedOutput().trim) + + @Test def `i24142 colon as infix`: Unit = initially: + run("1 :: 2 :: Nil") + assertEquals("val res0: List[Int] = List(1, 2)", storedOutput().trim) + + @Test def `i24142 commands still work`: Unit = initially: + run(":help") + assertTrue(storedOutput().contains("The REPL has several commands available")) + + @Test def `i24142 abbreviated commands still work`: Unit = initially: + run(":he") + assertTrue(storedOutput().contains("The REPL has several commands available")) + + + @Test def `i22844b regression colon arrow eol`: Unit = contextually: + assertTrue(ParseResult.isIncomplete("List(42).map: x =>")) + object ReplCompilerTests: private val pattern = Pattern.compile("\\r[\\n]?|\\n"); @@ -412,7 +445,7 @@ object ReplCompilerTests: end ReplCompilerTests -class ReplXPrintTyperTests extends ReplTest(ReplTest.defaultOptions :+ "-Xprint:typer"): +class ReplXPrintTyperTests extends ReplTest(ReplTest.defaultOptions :+ "-Vprint:typer"): @Test def i9111 = initially { run("""|enum E { | case A diff --git a/compiler/test/dotty/tools/repl/ReplTest.scala b/compiler/test/dotty/tools/repl/ReplTest.scala index 8fbf635c9a17..b630ef0c2b82 100644 --- a/compiler/test/dotty/tools/repl/ReplTest.scala +++ b/compiler/test/dotty/tools/repl/ReplTest.scala @@ -94,10 +94,7 @@ extends ReplDriver(options, new PrintStream(out, true, StandardCharsets.UTF_8.na FileDiff.dump(checkFile.toPath.toString, actualOutput) println(s"Wrote updated script file to $checkFile") else - println("expected =========>") - println(expectedOutput.mkString(EOL)) - println("actual ===========>") - println(actualOutput.mkString(EOL)) + println(dotc.util.DiffUtil.mkColoredHorizontalLineDiff(actualOutput.mkString(EOL), expectedOutput.mkString(EOL))) fail(s"Error in script $name, expected output did not match actual") end if diff --git a/compiler/test/dotty/tools/repl/ShadowingTests.scala b/compiler/test/dotty/tools/repl/ShadowingTests.scala index 98aa58a62a15..2a067815c318 100644 --- a/compiler/test/dotty/tools/repl/ShadowingTests.scala +++ b/compiler/test/dotty/tools/repl/ShadowingTests.scala @@ -152,7 +152,7 @@ class ShadowingTests extends ReplTest(options = ShadowingTests.options): | |scala> import util.Try |scala> new Try - |val res0: util.Try = you've gotta try! + |val res0: Try = you've gotta try! |""".stripMargin.linesIterator.toList ) end ShadowingTests diff --git a/compiler/test/dotty/tools/repl/TabcompleteTests.scala b/compiler/test/dotty/tools/repl/TabcompleteTests.scala index 963d680b69bc..d8518e9f41dc 100644 --- a/compiler/test/dotty/tools/repl/TabcompleteTests.scala +++ b/compiler/test/dotty/tools/repl/TabcompleteTests.scala @@ -216,10 +216,14 @@ class TabcompleteTests extends ReplTest { ":exit", ":help", ":imports", + ":jar", + ":kind", ":load", ":quit", + ":require", ":reset", ":settings", + ":sh", ":silent", ":type" ), diff --git a/compiler/test/dotty/tools/scripting/BashExitCodeTests.scala b/compiler/test/dotty/tools/scripting/BashExitCodeTests.scala index f2e64b5fad62..772744c8aba9 100644 --- a/compiler/test/dotty/tools/scripting/BashExitCodeTests.scala +++ b/compiler/test/dotty/tools/scripting/BashExitCodeTests.scala @@ -62,6 +62,9 @@ class BashExitCodeTests: @Test def xPluginList = scala("-Xplugin-list")(0) @Test def vPhases = scala("-Vphases")(0) + @Test def replEval = + scala("--repl-quit-after-init", "--repl-init-script", "\"val i = 2 * 2; val j = i + 2\"")(0) + /** A utility for running two commands in a row, like you do in bash. */ extension (inline u1: Unit) inline def & (inline u2: Unit): Unit = { u1; u2 } end BashExitCodeTests diff --git a/compiler/test/dotty/tools/utils.scala b/compiler/test/dotty/tools/utils.scala index a8c480088e08..765d8dd4d123 100644 --- a/compiler/test/dotty/tools/utils.scala +++ b/compiler/test/dotty/tools/utils.scala @@ -36,7 +36,7 @@ extension (str: String) def dropExtension = private def withFile[T](file: File)(action: Source => T): T = resource(Source.fromFile(file, UTF_8.name))(action) -def readLines(f: File): List[String] = withFile(f)(_.getLines.toList) +def readLines(f: File): List[String] = withFile(f)(_.getLines().toList) def readFile(f: File): String = withFile(f)(_.mkString) private object Unthrown extends ControlThrowable @@ -87,8 +87,11 @@ private val toolArg = raw"(?://|/\*| \*) ?(?i:(${ToolName.values.mkString("|")}) // ================================================================================================ /** Directive to specify to vulpix the options to pass to Dotty */ -private val directiveOptionsArg = raw"//> using options (.*)".r.unanchored -private val directiveJavacOptions = raw"//> using javacOpt (.*)".r.unanchored +private val directiveOptionsArg = raw"//> using option(?:s)? (.*)".r +private val directiveJavacOptions = raw"//> using javacOpt (.*)".r +private val directiveTargetOptions = raw"//> using target.platform (jvm|scala-js)".r +private val directiveUnsupported = raw"//> using (scala) (.*)".r +private val directiveUnknown = raw"//> using (.*)".r // Inspect the lines for compiler options of the form // `//> using options args`, `// scalajs: args`, `/* scalajs: args`, ` * scalajs: args` etc. @@ -101,10 +104,13 @@ def toolArgsParse(lines: List[String], filename: Option[String]): List[(String,S case toolArg(name, args) => List((name, args)) case _ => Nil } ++ - lines.flatMap { + lines.flatMap { case directiveOptionsArg(args) => List(("scalac", args)) case directiveJavacOptions(args) => List(("javac", args)) - case _ => Nil + case directiveTargetOptions(platform) => List(("target", platform)) + case directiveUnsupported(name, args) => Nil + case directiveUnknown(rest) => sys.error(s"Unknown directive: `//> using ${CommandLineParser.tokenize(rest).headOption.getOrElse("''")}`${filename.fold("")(f => s" in file $f")}") + case _ => Nil } import org.junit.Test diff --git a/compiler/test/dotty/tools/vulpix/FileDiff.scala b/compiler/test/dotty/tools/vulpix/FileDiff.scala index 5e882be6425a..0c5ae40cd3f8 100644 --- a/compiler/test/dotty/tools/vulpix/FileDiff.scala +++ b/compiler/test/dotty/tools/vulpix/FileDiff.scala @@ -1,9 +1,8 @@ package dotty.tools.vulpix +import scala.jdk.CollectionConverters.* import scala.language.unsafeNulls - -import scala.io.Source -import scala.util.Using +import scala.util.Properties.{javaSpecVersion, versionNumberString} import java.io.File import java.lang.System.{lineSeparator => EOL} @@ -20,24 +19,44 @@ object FileDiff { | > mv $actualFile $expectFile """.stripMargin - def check(sourceTitle: String, outputLines: Seq[String], checkFile: String): Option[String] = { - val checkLines = - if (!(new File(checkFile)).exists) Nil - else Using(Source.fromFile(checkFile, StandardCharsets.UTF_8.name))(_.getLines().toList).get + //at scala.quoted.runtime.impl.QuotesImpl$reflect$ClassDef$.module(QuotesImpl.scala:257) + private val frame = """\s+at [^(]+\([^:]+:(\d+)\)""".r - if (!matches(outputLines, checkLines)) Some( - s"""|Output from '$sourceTitle' did not match check file. Actual output: - |${outputLines.mkString(EOL)} - |""".stripMargin + "\n") + def check(sourceTitle: String, outputLines: Seq[String], checkFile: String): Option[String] = { + val path = Paths.get(checkFile) + if Files.exists(path) then + var stacked = false + val expected = Files.readAllLines(path).asScala + val actuals = + if javaSpecVersion == "25" && versionNumberString.startsWith("3.8.") then + outputLines.filter(!_.startsWith("WARNING:")) // ignore Unsafe warnings due to lazy vals + else + outputLines + val matched = + expected.corresponds(actuals): (expected, actual) => + matches(actual, expected) && { + val framed = expected.endsWith(")") && frame.matches(expected) + if framed then + stacked = true + !framed + } + if stacked then + Some(s"Check file $checkFile includes a stack trace, which is brittle!") + else if matched then + None + else + Some(s"""|Output from '$sourceTitle' did not match check file. Actual output: + |${outputLines.mkString(EOL)} + |""".stripMargin + "\n") else None } def matches(actual: String, expect: String): Boolean = { - val actual1 = actual.stripLineEnd - val expect1 = expect.stripLineEnd + val actual1 = actual.stripLineEnd + val expect1 = expect.stripLineEnd + def matchesWindowsPath = File.separatorChar == '\\' && actual1.replace('\\', '/') == expect1 - // handle check file path mismatch on windows - actual1 == expect1 || File.separatorChar == '\\' && actual1.replace('\\', '/') == expect1 + actual1 == expect1 || matchesWindowsPath // handle path mismatch on windows } def matches(actual: Seq[String], expect: Seq[String]): Boolean = { diff --git a/compiler/test/dotty/tools/vulpix/FileFilter.scala b/compiler/test/dotty/tools/vulpix/FileFilter.scala index b2aef8af038e..b59b4d4f209d 100644 --- a/compiler/test/dotty/tools/vulpix/FileFilter.scala +++ b/compiler/test/dotty/tools/vulpix/FileFilter.scala @@ -11,13 +11,13 @@ object FileFilter { exclude(file :: files.toList) def exclude(files: List[String]): FileFilter = new FileFilter { - private val blackList = files.toSet - def accept(file: String): Boolean = !blackList.contains(file) + private val excluded = files.toSet + def accept(file: String): Boolean = !excluded.contains(file) } def include(files: List[String]): FileFilter = new FileFilter { - private val whiteList = files.toSet - def accept(file: String): Boolean = whiteList.contains(file) + private val included = files.toSet + def accept(file: String): Boolean = included.contains(file) } object NoFilter extends FileFilter { diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index f701a348d233..d781d7788ee2 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -4,35 +4,27 @@ package vulpix import scala.language.unsafeNulls -import java.io.{File => JFile, IOException, PrintStream, ByteArrayOutputStream} -import java.lang.System.{lineSeparator => EOL} -import java.net.URL +import java.io.{File as JFile, PrintStream} import java.nio.file.StandardCopyOption.REPLACE_EXISTING -import java.nio.file.{Files, NoSuchFileException, Path, Paths} +import java.nio.file.{Files, NoSuchFileException, Paths} import java.nio.charset.{Charset, StandardCharsets} -import java.text.SimpleDateFormat import java.util.{HashMap, Timer, TimerTask} -import java.util.concurrent.{ExecutionException, TimeUnit, TimeoutException, Executors => JExecutors} +import java.util.concurrent.{TimeUnit, TimeoutException, Executors => JExecutors} import scala.collection.mutable import scala.io.{Codec, Source} import scala.jdk.CollectionConverters.* import scala.util.{Random, Try, Failure => TryFailure, Success => TrySuccess, Using} -import scala.util.control.NonFatal -import scala.util.matching.Regex import scala.collection.mutable.ListBuffer import dotc.{Compiler, Driver} import dotc.core.Contexts.* -import dotc.decompiler import dotc.report -import dotc.interfaces.Diagnostic.ERROR +import dotc.interfaces.Diagnostic.{ERROR, WARNING} import dotc.reporting.{Reporter, TestReporter} import dotc.reporting.Diagnostic -import dotc.config.Config -import dotc.util.{DiffUtil, SourceFile, SourcePosition, Spans, NoSourcePosition} +import dotc.util.{SourceFile, SourcePosition, Spans, NoSourcePosition} import io.AbstractFile -import dotty.tools.vulpix.TestConfiguration.defaultOptions /** A parallel testing suite whose goal is to integrate nicely with JUnit * @@ -40,8 +32,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 @@ -156,6 +148,11 @@ trait ParallelTesting extends RunnerOrchestration { self => } } } + + final override def toString: String = sourceFiles match { + case Array(f) => f.getPath + case _ => outDir.getPath.stripPrefix(defaultOutputDir).stripPrefix(name).stripPrefix("/") + } } /** A group of files that may all be compiled together, with the same flags @@ -170,8 +167,6 @@ trait ParallelTesting extends RunnerOrchestration { self => decompilation: Boolean = false ) extends TestSource { def sourceFiles: Array[JFile] = files.filter(isSourceFile) - - override def toString() = sourceFiles match { case Array(f) => f.getPath case _ => outDir.getPath } } /** A test source whose files will be compiled separately according to their @@ -195,7 +190,7 @@ trait ParallelTesting extends RunnerOrchestration { self => val compiler = groupSuffixParts.collectFirst { case Compiler(c) => c }.getOrElse("") Group(ordinal, compiler) - dir.listFiles + listFilesOrEmpty(dir) .filter(isSourceFile) .groupBy(groupFor) .toList @@ -205,7 +200,8 @@ trait ParallelTesting extends RunnerOrchestration { self => def sourceFiles = compilationGroups.map(_._2).flatten.toArray } - protected def shouldSkipTestSource(testSource: TestSource): Boolean = false + protected def shouldSkipTestSource(testSource: TestSource): Boolean = + testSource.sourceFiles.length == 0 protected def shouldReRun(testSource: TestSource): Boolean = failedTests.forall(rerun => testSource match { @@ -486,7 +482,7 @@ trait ParallelTesting extends RunnerOrchestration { self => import scala.util.Properties.* def flattenFiles(f: JFile): Array[JFile] = - if (f.isDirectory) f.listFiles.flatMap(flattenFiles) + if (f.isDirectory) listFilesOrEmpty(f).flatMap(flattenFiles) else Array(f) val files: Array[JFile] = files0.flatMap(flattenFiles) @@ -508,12 +504,6 @@ trait ParallelTesting extends RunnerOrchestration { self => .and("-d", targetDir.getPath) .withClasspath(targetDir.getPath) - def waitForJudiciously(process: Process): Int = - try process.waitFor() - catch case _: InterruptedException => - try if process.waitFor(5L, TimeUnit.MINUTES) then process.exitValue() else -2 - finally Thread.currentThread.interrupt() - def compileWithJavac(fs: Array[String]) = if (fs.nonEmpty) { val fullArgs = Array( "-encoding", StandardCharsets.UTF_8.name, @@ -522,7 +512,7 @@ trait ParallelTesting extends RunnerOrchestration { self => val process = Runtime.getRuntime.exec("javac" +: fullArgs) val output = Source.fromInputStream(process.getErrorStream).mkString - if waitForJudiciously(process) != 0 then Some(output) + if process.waitFor() != 0 then Some(output) else None } else None @@ -670,7 +660,7 @@ trait ParallelTesting extends RunnerOrchestration { self => checkFile(testSource).foreach(diffTest(testSource, _, reporterOutputLines(reporters), reporters, logger)) private def reporterOutputLines(reporters: Seq[TestReporter]): List[String] = - reporters.flatMap(_.consoleOutput.split("\n")).toList + reporters.flatMap(_.consoleOutput.linesIterator).toList private[ParallelTesting] def executeTestSuite(): this.type = { assert(testSourcesCompleted == 0, "not allowed to re-use a `CompileRun`") @@ -701,11 +691,7 @@ trait ParallelTesting extends RunnerOrchestration { self => for fut <- eventualResults do try fut.get() - catch - case ee: ExecutionException if ee.getCause.isInstanceOf[InterruptedException] => - System.err.println("Interrupted (probably running after shutdown)") - ee.printStackTrace() - case ex: Exception => + catch case ex: Exception => System.err.println(ex.getMessage) ex.printStackTrace() @@ -729,7 +715,7 @@ trait ParallelTesting extends RunnerOrchestration { self => /** Returns all files in directory or the file if not a directory */ private def flattenFiles(f: JFile): Array[JFile] = - if (f.isDirectory) f.listFiles.flatMap(flattenFiles) + if (f.isDirectory) listFilesOrEmpty(f).flatMap(flattenFiles) else Array(f) } @@ -743,13 +729,14 @@ trait ParallelTesting extends RunnerOrchestration { self => diffCheckfile(testSource, reporters, logger) override def maybeFailureMessage(testSource: TestSource, reporters: Seq[TestReporter]): Option[String] = - lazy val (map, expCount) = getWarnMapAndExpectedCount(testSource.sourceFiles.toIndexedSeq) + lazy val (expected, expCount) = getWarnMapAndExpectedCount(testSource.sourceFiles.toIndexedSeq) lazy val obtCount = reporters.foldLeft(0)(_ + _.warningCount) - lazy val (expected, unexpected) = getMissingExpectedWarnings(map, reporters.iterator.flatMap(_.diagnostics)) - lazy val diagnostics = reporters.flatMap(_.diagnostics.toSeq.sortBy(_.pos.line).map(e => s" at ${e.pos.line + 1}: ${e.message}")) - def showLines(title: String, lines: Seq[String]) = if lines.isEmpty then "" else title + lines.mkString("\n", "\n", "") - def hasMissingAnnotations = expected.nonEmpty || unexpected.nonEmpty - def showDiagnostics = showLines("-> following the diagnostics:", diagnostics) + lazy val diagnostics = reporters.flatMap(_.diagnostics.toSeq.sortBy(_.pos.line)) + lazy val (unfulfilled, unexpected) = getMissingExpectedWarnings(expected, diagnostics.iterator.filter(_.level >= WARNING)) + lazy val messages = diagnostics.map(d => s" at ${d.pos.line + 1}: ${d.message}") + def showLines(title: String, lines: Seq[String]) = if lines.isEmpty then "" else lines.mkString(s"$title\n", "\n", "") + def hasMissingAnnotations = unfulfilled.nonEmpty || unexpected.nonEmpty + def showDiagnostics = showLines("-> following the diagnostics:", messages) Option: if reporters.exists(_.errorCount > 0) then s"""Compilation failed for: ${testSource.title} @@ -758,58 +745,63 @@ trait ParallelTesting extends RunnerOrchestration { self => else if expCount != obtCount then s"""|Wrong number of warnings encountered when compiling $testSource |expected: $expCount, actual: $obtCount - |${showLines("Unfulfilled expectations:", expected)} + |${showLines("Unfulfilled expectations:", unfulfilled)} |${showLines("Unexpected warnings:", unexpected)} |$showDiagnostics |""".stripMargin.trim.linesIterator.mkString("\n", "\n", "") - else if hasMissingAnnotations then s"\nWarnings found on incorrect row numbers when compiling $testSource\n$showDiagnostics" - else if !map.isEmpty then s"\nExpected warnings(s) have {=}: $map" + else if hasMissingAnnotations then + s"""|Warnings found on incorrect row numbers when compiling $testSource + |${showLines("Unfulfilled expectations:", unfulfilled)} + |${showLines("Unexpected warnings:", unexpected)} + |$showDiagnostics + |""".stripMargin.trim.linesIterator.mkString("\n", "\n", "") + else if !expected.isEmpty then s"\nExpected warnings(s) have {=}: $expected" else null end maybeFailureMessage def getWarnMapAndExpectedCount(files: Seq[JFile]): (HashMap[String, Integer], Int) = - val comment = raw"//( *)(nopos-)?warn".r - val map = new HashMap[String, Integer]() + val comment = raw"//(?: *)(nopos-)?warn".r + val map = HashMap[String, Integer]() var count = 0 def bump(key: String): Unit = map.get(key) match case null => map.put(key, 1) case n => map.put(key, n+1) count += 1 - files.filter(isSourceFile).foreach { file => - Using(Source.fromFile(file, StandardCharsets.UTF_8.name)) { source => - source.getLines.zipWithIndex.foreach { case (line, lineNbr) => - comment.findAllMatchIn(line).foreach { m => - m.group(2) match - case "nopos-" => - bump("nopos") - case _ => bump(s"${file.getPath}:${lineNbr+1}") - } - } - }.get - } + for file <- files if isSourceFile(file) do + Using.resource(Source.fromFile(file, StandardCharsets.UTF_8.name)) { source => + source.getLines().zipWithIndex.foreach: (line, lineNbr) => + comment.findAllMatchIn(line).foreach: + case comment("nopos-") => bump("nopos") + case _ => bump(s"${file.getPath}:${lineNbr+1}") + } + end for (map, count) - def getMissingExpectedWarnings(map: HashMap[String, Integer], reporterWarnings: Iterator[Diagnostic]): (List[String], List[String]) = - val unexpected, unpositioned = ListBuffer.empty[String] + // return unfulfilled expected warnings and unexpected diagnostics + def getMissingExpectedWarnings(expected: HashMap[String, Integer], reporterWarnings: Iterator[Diagnostic]): (List[String], List[String]) = + val unexpected = ListBuffer.empty[String] def relativize(path: String): String = path.split(JFile.separatorChar).dropWhile(_ != "tests").mkString(JFile.separator) def seenAt(key: String): Boolean = - map.get(key) match + expected.get(key) match case null => false - case 1 => map.remove(key) ; true - case n => map.put(key, n - 1) ; true + case 1 => expected.remove(key); true + case n => expected.put(key, n - 1); true def sawDiagnostic(d: Diagnostic): Unit = val srcpos = d.pos.nonInlined if srcpos.exists then val key = s"${relativize(srcpos.source.file.toString())}:${srcpos.line + 1}" if !seenAt(key) then unexpected += key else - if(!seenAt("nopos")) unpositioned += relativize(srcpos.source.file.toString()) + if !seenAt("nopos") then unexpected += relativize(srcpos.source.file.toString) reporterWarnings.foreach(sawDiagnostic) - (map.asScala.keys.toList, (unexpected ++ unpositioned).toList) + val splitter = raw"(?:[^:]*):(\d+)".r + val unfulfilled = expected.asScala.keys.toList.sortBy { case splitter(n) => n.toInt case _ => -1 } + (unfulfilled, unexpected.toList) end getMissingExpectedWarnings + end WarnTest private final class RewriteTest(testSources: List[TestSource], checkFiles: Map[JFile, JFile], times: Int, threadLimit: Option[Int], suppressAllOutput: Boolean)(implicit summaryReport: SummaryReporting) extends Test(testSources, times, threadLimit, suppressAllOutput) { @@ -893,7 +885,12 @@ trait ParallelTesting extends RunnerOrchestration { self => |${unexpected.mkString("Unexpected errors:\n", "\n", "")} |$showErrors |""".stripMargin.trim.linesIterator.mkString("\n", "\n", "") - else if hasMissingAnnotations then s"\nErrors found on incorrect row numbers when compiling $testSource\n$showErrors" + else if hasMissingAnnotations then + s"""|Errors found on incorrect row numbers when compiling $testSource + |$showErrors + |${expected.mkString("Unfulfilled expectations:\n", "\n", "")} + |${unexpected.mkString("Unexpected errors:\n", "\n", "")} + |""".stripMargin.trim.linesIterator.mkString("\n", "\n", "") else if !errorMap.isEmpty then s"\nExpected error(s) have {=}: $errorMap" else null } @@ -920,7 +917,7 @@ trait ParallelTesting extends RunnerOrchestration { self => expectedErrors += 1 files.filter(isSourceFile).foreach { file => Using(Source.fromFile(file, StandardCharsets.UTF_8.name)) { source => - source.getLines.zipWithIndex.foreach { case (line, lineNbr) => + source.getLines().zipWithIndex.foreach { case (line, lineNbr) => comment.findAllMatchIn(line).foreach { m => m.group(2) match case prefix if m.group(1).isEmpty => @@ -936,29 +933,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 @@ -1220,7 +1221,7 @@ trait ParallelTesting extends RunnerOrchestration { self => private def copyToDir(dir: JFile, file: JFile): JFile = { val target = Paths.get(dir.getPath, file.getName) Files.copy(file.toPath, target, REPLACE_EXISTING) - if (file.isDirectory) file.listFiles.map(copyToDir(target.toFile, _)) + if (file.isDirectory) listFilesOrEmpty(file).foreach(f => copyToDir(target.toFile, f)) target.toFile } @@ -1258,20 +1259,20 @@ trait ParallelTesting extends RunnerOrchestration { self => * This behaviour is mainly needed for the tests that test the test suite. */ def expectFailure: CompilationTest = - new CompilationTest(targets, times, shouldDelete, threadLimit, true, shouldSuppressOutput) + new CompilationTest(targets, times, shouldDelete, threadLimit, shouldFail = true, shouldSuppressOutput) /** Builds a `CompilationTest` where all output is suppressed * * This behaviour is mainly needed for the tests that test the test suite. */ def suppressAllOutput: CompilationTest = - new CompilationTest(targets, times, shouldDelete, threadLimit, shouldFail, true) + new CompilationTest(targets, times, shouldDelete, threadLimit, shouldFail, shouldSuppressOutput = true) /** Delete all output files generated by this `CompilationTest` */ def delete(): Unit = targets.foreach(t => delete(t.outDir)) private def delete(file: JFile): Unit = { - if (file.isDirectory) file.listFiles.foreach(delete) + if (file.isDirectory) listFilesOrEmpty(file).foreach(delete) try Files.delete(file.toPath) catch { case _: NoSuchFileException => // already deleted, everything's fine @@ -1329,7 +1330,7 @@ trait ParallelTesting extends RunnerOrchestration { self => /** Separates directories from files and returns them as `(dirs, files)` */ private def compilationTargets(sourceDir: JFile, fileFilter: FileFilter = FileFilter.NoFilter): (List[JFile], List[JFile]) = - sourceDir.listFiles.foldLeft((List.empty[JFile], List.empty[JFile])) { case ((dirs, files), f) => + listFilesOrEmpty(sourceDir).foldLeft((List.empty[JFile], List.empty[JFile])) { case ((dirs, files), f) => if (!fileFilter.accept(f.getName)) (dirs, files) else if (f.isDirectory) (f :: dirs, files) else if (isSourceFile(f)) (dirs, f :: files) @@ -1366,14 +1367,14 @@ trait ParallelTesting extends RunnerOrchestration { self => * By default, files are compiled in alphabetical order. An optional seed * can be used for randomization. */ - def compileDir(f: String, flags: TestFlags, randomOrder: Option[Int] = None, recursive: Boolean = true)(implicit testGroup: TestGroup): CompilationTest = { + def compileDir(f: String, flags: TestFlags, randomOrder: Option[Int] = None, recursive: Boolean = true)(using testGroup: TestGroup): CompilationTest = { val outDir = defaultOutputDir + testGroup + JFile.separator val sourceDir = new JFile(f) checkRequirements(f, sourceDir, outDir) def flatten(f: JFile): Array[JFile] = if (f.isDirectory) { - val files = f.listFiles + val files = listFilesOrEmpty(f) if (recursive) files.flatMap(flatten) else files } else Array(f) @@ -1499,7 +1500,7 @@ trait ParallelTesting extends RunnerOrchestration { self => | | sbt "testCompilation --from-tasty $file" | - |This tests can be disabled by adding `${file.getName}` to `compiler${JFile.separator}test${JFile.separator}dotc${JFile.separator}$runOrPos-$listName.blacklist` + |This tests can be disabled by adding `${file.getName}` to `compiler${JFile.separator}test${JFile.separator}dotc${JFile.separator}$runOrPos-$listName.excludelist` | |""".stripMargin } @@ -1571,12 +1572,21 @@ trait ParallelTesting extends RunnerOrchestration { self => flags.options.sliding(2).collectFirst { case Array("-encoding", encoding) => Charset.forName(encoding) }.getOrElse(StandardCharsets.UTF_8) -} -object ParallelTesting { + +object ParallelTesting: def defaultOutputDir: String = "out"+JFile.separator + /** Like `File.listFiles()` but returns an empty array when the result is null. + * listFiles() can return null on I/O error or when the directory was deleted + * (e.g. by another thread during parallel test cleanup on Windows). + */ + def listFilesOrEmpty(dir: JFile): Array[JFile] = { + val a = dir.listFiles + if (a == null) Array.empty else a + } + def isSourceFile(f: JFile): Boolean = { val name = f.getName name.endsWith(".scala") || name.endsWith(".java") @@ -1585,4 +1595,14 @@ object ParallelTesting { def isTastyFile(f: JFile): Boolean = f.getName.endsWith(".tasty") -} + 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/compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala b/compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala index 8e9a27e766b4..c788419defd8 100644 --- a/compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala +++ b/compiler/test/dotty/tools/vulpix/RunnerOrchestration.scala @@ -15,7 +15,7 @@ import scala.concurrent.{ Await, Future } import scala.concurrent.ExecutionContext.Implicits.global import scala.collection.mutable -/** Vulpix spawns JVM subprocesses (`numberOfSlaves`) in order to run tests +/** Vulpix spawns JVM subprocesses (`numberOfWorkers`) in order to run tests * without compromising the main JVM * * These need to be orchestrated in a safe manner with a simple protocol. This @@ -37,7 +37,7 @@ import scala.collection.mutable trait RunnerOrchestration { /** The maximum amount of active runners, which contain a child JVM */ - def numberOfSlaves: Int + def numberOfWorkers: Int /** The maximum duration the child process is allowed to consume before * getting destroyed @@ -176,7 +176,7 @@ trait RunnerOrchestration { private val busyRunners = mutable.Set.empty[Runner] private def getRunner(): Runner = synchronized { - while (freeRunners.isEmpty && busyRunners.size >= numberOfSlaves) wait() + while (freeRunners.isEmpty && busyRunners.size >= numberOfWorkers) wait() val runner = if (freeRunners.isEmpty) new Runner(createProcess) diff --git a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala index 21094799d8a9..b4786f198aec 100644 --- a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala +++ b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala @@ -30,6 +30,10 @@ object TestConfiguration { Properties.dottyLibrary )) + val silenceOptions = Array( + "-Wconf:id=E222:s", // name=EncodedPackageName don't warn about file names with hyphens + ) + val withCompilerClasspath = mkClasspath(List( Properties.scalaLibrary, Properties.scalaAsm, @@ -63,7 +67,7 @@ object TestConfiguration { val yCheckOptions = Array("-Ycheck:all") - val commonOptions = Array("-indent") ++ checkOptions ++ noCheckOptions ++ yCheckOptions + val commonOptions = Array("-indent") ++ checkOptions ++ noCheckOptions ++ yCheckOptions ++ silenceOptions val defaultOptions = TestFlags(basicClasspath, commonOptions) val unindentOptions = TestFlags(basicClasspath, Array("-no-indent") ++ checkOptions ++ noCheckOptions ++ yCheckOptions) val withCompilerOptions = diff --git a/compiler/test/dotty/tools/vulpix/TestGroup.scala b/compiler/test/dotty/tools/vulpix/TestGroup.scala index 3b9a5134f62e..b087086e134e 100644 --- a/compiler/test/dotty/tools/vulpix/TestGroup.scala +++ b/compiler/test/dotty/tools/vulpix/TestGroup.scala @@ -2,14 +2,15 @@ package dotty.tools.vulpix /** Test groups are used to ensure that the output of tests do not overlap. * - * It can be used to disambiguate ouputs of tests that test the same file but with different options as shown in the following example. + * A test group can be used to disambiguate outputs of tests that test the same file + * but with different options as shown in the following example. + * * compileFilesInDir("tests/pos", defaultOptions)(TestGroup("compileStdLib")) // will output in ./out/compileStdLib/... * compileFilesInDir("tests/pos", defaultOptimised)(TestGroup("optimised/testOptimised")) // will output in ./out/optimised/testOptimised/... */ -class TestGroup(val name: String) extends AnyVal { - override def toString: String = name -} -object TestGroup { - def apply(name: String): TestGroup = new TestGroup(name) -} +opaque type TestGroup = String + +object TestGroup: + inline def apply(inline name: String): TestGroup = name + extension (inline group: TestGroup) inline def name: String = group diff --git a/compiler/test/dotty/tools/vulpix/VulpixMetaTests.scala b/compiler/test/dotty/tools/vulpix/VulpixMetaTests.scala index 0044ab8a94e5..df075e4fd634 100644 --- a/compiler/test/dotty/tools/vulpix/VulpixMetaTests.scala +++ b/compiler/test/dotty/tools/vulpix/VulpixMetaTests.scala @@ -25,7 +25,7 @@ class VulpixMetaTests { object VulpixMetaTests extends ParallelTesting { def maxDuration = 1.seconds // Ensure maximum reproducibility. - def numberOfSlaves = 1 + def numberOfWorkers = 1 def safeMode = false // Don't fork a new VM after each run test def isInteractive = false // Don't beautify output for interactive use. def testFilter = Nil // Run all the tests. diff --git a/compiler/test/dotty/tools/vulpix/VulpixUnitTests.scala b/compiler/test/dotty/tools/vulpix/VulpixUnitTests.scala index baf61c845d96..1a9393d40d22 100644 --- a/compiler/test/dotty/tools/vulpix/VulpixUnitTests.scala +++ b/compiler/test/dotty/tools/vulpix/VulpixUnitTests.scala @@ -1,115 +1,106 @@ package dotty.tools package vulpix -import scala.language.unsafeNulls - -import java.io.{File => JFile} -import org.junit.Assert._ -import org.junit.{ Test, AfterClass } - -import scala.concurrent.duration._ -import scala.util.control.NonFatal +import org.junit.{Test as test, AfterClass as tearDown} /** Unit tests for the Vulpix test suite */ -class VulpixUnitTests { - import VulpixUnitTests._ - import TestConfiguration._ +class VulpixUnitTests: + import VulpixUnitTests.* + import TestConfiguration.* - implicit val _: SummaryReporting = new NoSummaryReport + given SummaryReporting = new NoSummaryReport - implicit def testGroup: TestGroup = TestGroup("VulpixTests") + given TestGroup = TestGroup("VulpixTests") - // To fail with something else than an AssertionError - def fail(): Unit = throw new Exception("didn't fail properly") - - @Test def missingFile: Unit = - try { + @test def missingFile: Unit = + assertThrows[IllegalArgumentException](_ => true): compileFile("tests/vulpix-tests/unit/i-dont-exist.scala", defaultOptions).expectFailure.checkExpectedErrors() - fail() - } catch { - case _: IllegalArgumentException => // pass! - } - @Test def pos1Error: Unit = + @test def pos1Error: Unit = compileFile("tests/vulpix-tests/unit/posFail1Error.scala", defaultOptions).expectFailure.checkCompile() - @Test def negMissingAnnot: Unit = - compileFile("tests/vulpix-tests/unit/negMissingAnnot.scala", defaultOptions).expectFailure.checkExpectedErrors() - - @Test def negAnnotWrongLine: Unit = - compileFile("tests/vulpix-tests/unit/negAnnotWrongLine.scala", defaultOptions).expectFailure.checkExpectedErrors() - - @Test def negTooManyAnnots: Unit = - compileFile("tests/vulpix-tests/unit/negTooManyAnnots.scala", defaultOptions).expectFailure.checkExpectedErrors() - - @Test def negNoPositionAnnot: Unit = - compileFile("tests/vulpix-tests/unit/negNoPositionAnnots.scala", defaultOptions).expectFailure.checkExpectedErrors() - - @Test def negAnyPositionAnnot: Unit = - compileFile("tests/vulpix-tests/unit/negAnyPositionAnnots.scala", defaultOptions).checkExpectedErrors() - - @Test def runCompileFail: Unit = + @test def negMissingAnnot: Unit = + compileFile("tests/vulpix-tests/unit/negMissingAnnot.scala", defaultOptions) + .suppressAllOutput + .expectFailure + .checkExpectedErrors() + + @test def negAnnotWrongLine: Unit = + compileFile("tests/vulpix-tests/unit/negAnnotWrongLine.scala", defaultOptions) + .suppressAllOutput + .expectFailure + .checkExpectedErrors() + + @test def negTooManyAnnots: Unit = + compileFile("tests/vulpix-tests/unit/negTooManyAnnots.scala", defaultOptions) + .suppressAllOutput + .expectFailure + .checkExpectedErrors() + + @test def negNoPositionAnnot: Unit = + compileFile("tests/vulpix-tests/unit/negNoPositionAnnots.scala", defaultOptions) + .suppressAllOutput + .expectFailure + .checkExpectedErrors() + + @test def negAnyPositionAnnot: Unit = + compileFile("tests/vulpix-tests/unit/negAnyPositionAnnots.scala", defaultOptions) + .suppressAllOutput + .checkExpectedErrors() + + @test def runCompileFail: Unit = compileFile("tests/vulpix-tests/unit/posFail1Error.scala", defaultOptions).expectFailure.checkRuns() - @Test def runWrongOutput1: Unit = + @test def runWrongOutput1: Unit = compileFile("tests/vulpix-tests/unit/runWrongOutput1.scala", defaultOptions).expectFailure.checkRuns() - @Test def runWrongOutput2: Unit = + @test def runWrongOutput2: Unit = compileFile("tests/vulpix-tests/unit/runWrongOutput2.scala", defaultOptions).expectFailure.checkRuns() - @Test def runDiffOutput1: Unit = + @test def runDiffOutput1: Unit = compileFile("tests/vulpix-tests/unit/runDiffOutput1.scala", defaultOptions).expectFailure.checkRuns() - @Test def runStackOverflow: Unit = + @test def runStackOverflow: Unit = compileFile("tests/vulpix-tests/unit/stackOverflow.scala", defaultOptions).expectFailure.checkRuns() - @Test def runOutRedirects: Unit = + @test def runOutRedirects: Unit = compileFile("tests/vulpix-tests/unit/i2147.scala", defaultOptions).expectFailure.checkRuns() - @Test def infiteNonRec: Unit = + @test def infiniteNonRec: Unit = compileFile("tests/vulpix-tests/unit/infinite.scala", defaultOptions).expectFailure.checkRuns() - @Test def infiteTailRec: Unit = + @test def infiniteTailRec: Unit = compileFile("tests/vulpix-tests/unit/infiniteTail.scala", defaultOptions).expectFailure.checkRuns() - @Test def infiniteAlloc: Unit = + @test def infiniteAlloc: Unit = compileFile("tests/vulpix-tests/unit/infiniteAlloc.scala", defaultOptions).expectFailure.checkRuns() - @Test def deadlock: Unit = + @test def deadlock: Unit = compileFile("tests/vulpix-tests/unit/deadlock.scala", defaultOptions).expectFailure.checkRuns() - @Test def badJava: Unit = - try { - compileFile("tests/vulpix-tests/unit/BadJava.java", defaultOptions).suppressAllOutput.checkCompile() - fail() - } catch { - case ae: AssertionError => assertTrue(ae.getMessage.contains("java compilation failed")) - } + @test def badJava: Unit = + assertThrows[AssertionError](_.getMessage.nn.contains("java compilation failed")): + compileFile("tests/vulpix-tests/unit/BadJava.java", defaultOptions) + .suppressAllOutput + .checkCompile() - @Test def runTimeout: Unit = { + @test def runTimeout: Unit = val fileName = s"tests/vulpix-tests/unit/timeout.scala" - try { - compileFile(fileName, defaultOptions).checkRuns() - fail() - } catch { - case ae: AssertionError => - val expect = """(?m).*test '.+' timed out.*""" - val actual = ae.getMessage.linesIterator.toList.last - assert(actual.matches(expect), "actual = " + actual) - } - } -} - - -object VulpixUnitTests extends ParallelTesting { + val expect = """(?m).*test '.+' timed out.*""" + assertThrows[AssertionError](_.getMessage.nn.linesIterator.toList.last.matches(expect)): + compileFile(fileName, defaultOptions) + .suppressAllOutput + .checkRuns() + +object VulpixUnitTests extends ParallelTesting: + import scala.concurrent.duration.* def maxDuration = 3.seconds - def numberOfSlaves = 5 + def numberOfWorkers = 5 def safeMode = sys.env.get("SAFEMODE").isDefined - def isInteractive = !sys.env.contains("DRONE") + def isInteractive = !sys.env.contains("DOTTY_CI_RUN") def testFilter = Nil def updateCheckFiles: Boolean = false def failedTests = None - @AfterClass - def tearDown() = this.cleanup() -} \ No newline at end of file + @tearDown + def tearDown() = cleanup() diff --git a/dist/bin/common b/dist/bin/common index 6fb828352d52..e0db0edef3bb 100755 --- a/dist/bin/common +++ b/dist/bin/common @@ -4,16 +4,22 @@ # * Credits: This script is based on the script generated by sbt-pack. # *--------------------------------------------------------------------------*/ -# save terminal settings -saved_stty=$(stty -g 2>/dev/null) -# clear on error so we don't later try to restore them -if [[ ! $? ]]; then - saved_stty="" +if [ -e /usr/bin/tty -a "`tty`" != "not a tty" -a ! -p /dev/stdin ]; then + isterminal=1 + # save terminal settings + saved_stty=$(stty -g 2>/dev/null) + # clear on error so we don't later try to restore them + if [[ ! $? ]]; then + saved_stty="" + isterminal=0 + fi +else + isterminal=0 fi # restore stty settings (echo in particular) function restoreSttySettings() { - stty $saved_stty + [ $isterminal -eq 1 ] && stty $saved_stty saved_stty="" } @@ -59,7 +65,7 @@ if [[ ${cygwin-} || ${mingw-} || ${msys-} ]]; then CYGPATHCMD=`which cygpath 2>/dev/null` case "$TERM" in rxvt* | xterm* | cygwin*) - stty -icanon min 1 -echo + [ $isterminal -eq 1 ] && stty -icanon min 1 -echo JAVA_OPTS="$JAVA_OPTS -Djline.terminal=unix" ;; esac diff --git a/docs/_docs/contributing/architecture/lifecycle.md b/docs/_docs/contributing/architecture/lifecycle.md index 2cf58f477da3..2705139aa872 100644 --- a/docs/_docs/contributing/architecture/lifecycle.md +++ b/docs/_docs/contributing/architecture/lifecycle.md @@ -15,7 +15,7 @@ Customisation is provided by extending the [Compiler] class, which maintains an list of [phases][Phases], and how to [run][Run] them. Each interaction with a compiler creates a new run, which is a complete iteration of the compiler's phases over a list of input sources. Each run has the capability to create new definitions or -invalidate older ones, and `dotc` can [track these changes over time](../architecture/time.md). +invalidate older ones, and `dotc` can [track these changes over time](time.md). #### Runs During a run, the input sources are converted to [compilation units][CompilationUnit] (i.e. the abstraction of @@ -32,12 +32,12 @@ the compiler are: - `erasure`, which retypes a more simplified program into one that has the same types as the JVM. - `genBCode`, the JVM backend, which converts erased compiler trees into Java bytecode format. -[You can read more about phases here](../architecture/phases.md#phase-categories). +[You can read more about phases here](phases.md#phase-categories). #### Drivers The core compiler also requires a lot of state to be initialised before use, such as [settings][ScalaSettings] -and the [Context](../architecture/context.md). For convenience, the [Driver] class contains high level functions for +and the [Context](context.md). For convenience, the [Driver] class contains high level functions for configuring the compiler and invoking it programatically. The object [Main] inherits from `Driver` and is invoked by the `scalac` script. @@ -84,7 +84,7 @@ tools // contains helpers and the `scala` generic runner [dotty.tools]: https://github.com/lampepfl/dotty/tree/master/compiler/src/dotty/tools [ScalaSettings]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala [syntax]: https://docs.scala-lang.org/scala3/reference/syntax.html -[Main]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/Main.scala -[Driver]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/Driver.scala -[Compiler]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/Compiler.scala -[Run]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/Run.scala \ No newline at end of file +[Main]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/Main.scala +[Driver]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/Driver.scala +[Compiler]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/Compiler.scala +[Run]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/Run.scala diff --git a/docs/_docs/contributing/architecture/phases.md b/docs/_docs/contributing/architecture/phases.md index e5c4b1487a8c..f406b061d564 100644 --- a/docs/_docs/contributing/architecture/phases.md +++ b/docs/_docs/contributing/architecture/phases.md @@ -85,24 +85,24 @@ suitable for the runtime system, with two sub-groupings: ### `backendPhases` These map the transformed trees to Java classfiles or SJSIR files. -[CompilationUnit]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/CompilationUnit.scala -[Compiler]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/Compiler.scala -[Phase]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/core/Phases.scala -[MiniPhase]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala -[Run]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/Run.scala -[parser]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/parsing/ParserPhase.scala -[typer]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/TyperPhase.scala -[posttyper]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/PostTyper.scala -[prepjsinterop]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala -[pickler]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/Pickler.scala -[inlining]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/Inlining.scala -[postInlining]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/PostInlining.scala -[staging]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/Staging.scala -[pickleQuotes]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala -[refchecks]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/RefChecks.scala -[initChecker]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/init/Checker.scala -[firstTransform]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala -[patternMatcher]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala -[erasure]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/Erasure.scala -[Mirror]: https://github.com/lampepfl/dotty/blob/master/library/src/scala/deriving/Mirror.scala -[PCP]: ../../reference/metaprogramming/macros.md#the-phase-consistency-principle +[CompilationUnit]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/CompilationUnit.scala +[Compiler]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/Compiler.scala +[Phase]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/core/Phases.scala +[MiniPhase]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +[Run]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/Run.scala +[parser]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/parsing/ParserPhase.scala +[typer]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/typer/TyperPhase.scala +[posttyper]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +[prepjsinterop]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala +[pickler]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/transform/Pickler.scala +[inlining]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/transform/Inlining.scala +[postInlining]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/transform/PostInlining.scala +[staging]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/transform/Staging.scala +[pickleQuotes]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +[refchecks]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +[initChecker]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/transform/init/Checker.scala +[firstTransform]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +[patternMatcher]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +[erasure]: https://github.com/scala/scala3/blob/master/compiler/src/dotty/tools/dotc/transform/Erasure.scala +[Mirror]: https://github.com/scala/scala3/blob/master/library/src/scala/deriving/Mirror.scala +[PCP]: ../../reference/metaprogramming/macros.md#level-consistency diff --git a/docs/_docs/contributing/debugging/ide-debugging.md b/docs/_docs/contributing/debugging/ide-debugging.md index af817826565a..cca5d4849e5e 100644 --- a/docs/_docs/contributing/debugging/ide-debugging.md +++ b/docs/_docs/contributing/debugging/ide-debugging.md @@ -140,7 +140,7 @@ And concatenate the output into the classpath argument, which should already con In the `args` you can add any additional compiler option you want. -For instance you can add `-Xprint:all` to print all the generated trees after each mega phase. +For instance you can add `-Vprint:all` to print all the generated trees after each mega phase. Run `scalac -help` to get an overview of the available compiler options. diff --git a/docs/_docs/contributing/debugging/inspection.md b/docs/_docs/contributing/debugging/inspection.md index a80c3d3462ae..483f9db4474e 100644 --- a/docs/_docs/contributing/debugging/inspection.md +++ b/docs/_docs/contributing/debugging/inspection.md @@ -61,9 +61,9 @@ Sometimes you may want to stop the compiler after a certain phase, for example t knock-on errors from occurring from a bug in an earlier phase. Use the flag `-Ystop-after:` to prevent any phases executing afterwards. -> e.g. `-Xprint:` where `phase` is a miniphase, will print after +> e.g. `-Vprint:` where `phase` is a miniphase, will print after > the whole phase group is complete, which may be several miniphases after `phase`. -> Instead you can use `-Ystop-after: -Xprint:` to stop +> Instead you can use `-Ystop-after: -Vprint:` to stop > immediately after the miniphase and see the trees that you intended. ## Printing TASTy of a Class diff --git a/docs/_docs/contributing/debugging/other-debugging.md b/docs/_docs/contributing/debugging/other-debugging.md index 50be43db51ab..3879f3d8b53d 100644 --- a/docs/_docs/contributing/debugging/other-debugging.md +++ b/docs/_docs/contributing/debugging/other-debugging.md @@ -137,19 +137,19 @@ assertPositioned(tree.reporting(s"Tree is: $result")) To print out the trees you are compiling after the FrontEnd (scanner, parser, namer, typer) phases: ```shell -scalac -Xprint:typer ../issues/Playground.scala +scalac -Vprint:typer ../issues/Playground.scala ``` To print out the trees after Frontend and CollectSuperCalls phases: ```shell -scalac -Xprint:typer,collectSuperCalls ../issues/Playground.scala +scalac -Vprint:typer,collectSuperCalls ../issues/Playground.scala ``` To print out the trees after all phases: ```shell -scalac -Xprint:all ../issues/Playground.scala +scalac -Vprint:all ../issues/Playground.scala ``` To find out the list of all the phases and their names, check out [this](https://github.com/lampepfl/dotty/blob/10526a7d0aa8910729b6036ee51942e05b71abf6/compiler/src/dotty/tools/dotc/Compiler.scala#L34) line in `Compiler.scala`. Each `Phase` object has `phaseName` defined on it, this is the phase name. @@ -219,7 +219,7 @@ And is to be used as: scalac -Yprint-pos ../issues/Playground.scala ``` -If used, all the trees output with `show` or via `-Xprint:typer` will also have positions attached to them, e.g.: +If used, all the trees output with `show` or via `-Vprint:typer` will also have positions attached to them, e.g.: ```scala package @ { @@ -247,7 +247,7 @@ package @ { Every [Positioned](https://github.com/lampepfl/dotty/blob/10526a7d0aa8910729b6036ee51942e05b71abf6/compiler/src/dotty/tools/dotc/ast/Positioned.scala) (a parent class of `Tree`) object has a `uniqueId` field. It is an integer that is unique for that tree and doesn't change from compile run to compile run. You can output these IDs from any printer (such as the ones used by `.show` and `-Xprint`) via `-Yshow-tree-ids` flag, e.g.: ```shell -scalac -Xprint:typer -Yshow-tree-ids ../issues/Playground.scala +scalac -Vprint:typer -Yshow-tree-ids ../issues/Playground.scala ``` Gives: diff --git a/docs/_docs/contributing/getting-started.md b/docs/_docs/contributing/getting-started.md index 0de0ee687dcc..3775622a5c54 100644 --- a/docs/_docs/contributing/getting-started.md +++ b/docs/_docs/contributing/getting-started.md @@ -129,13 +129,12 @@ For more information, see the [scaladoc section](./scaladoc.md). The main development discussion channels are: - [github.com/lampepfl/dotty/discussions](https://github.com/lampepfl/dotty/discussions) - [contributors.scala-lang.org](https://contributors.scala-lang.org) -- [gitter.im/scala/contributors](https://gitter.im/scala/contributors) [git]: https://git-scm.com [Metals]: https://scalameta.org/metals/ [vs-code]: https://code.visualstudio.com [lampepfl/dotty]: https://github.com/lampepfl/dotty -[sbt-download]: https://www.scala-sbt.org/download.html +[sbt-download]: https://www.scala-sbt.org/download/ [java8]: https://www.oracle.com/java/technologies/javase-jdk8-downloads.html [java11]: https://www.oracle.com/java/technologies/javase-jdk11-downloads.html [adopt]: https://adoptopenjdk.net/ diff --git a/docs/_docs/contributing/issues/cause.md b/docs/_docs/contributing/issues/cause.md index e23f6d1f747f..1cd529c3618d 100644 --- a/docs/_docs/contributing/issues/cause.md +++ b/docs/_docs/contributing/issues/cause.md @@ -14,14 +14,14 @@ In this section, you will be able to answer questions such as: ## What phase generated a particular tree? -As described in the [compiler lifecycle](../architecture/lifecycle.md#phases-2), +As described in the [compiler lifecycle](../architecture/lifecycle.md#phases), each phase transforms the trees and types that represent your code in a certain way. -To print the code as it is transformed through the compiler, use the compiler flag `-Xprint:all`. +To print the code as it is transformed through the compiler, use the compiler flag `-Vprint:all`. After each phase group is completed, you will see the resulting trees representing the code. -> It is recommended to test `-Xprint:all` on a single, small file, otherwise a lot of unnecessary +> It is recommended to test `-Vprint:all` on a single, small file, otherwise a lot of unnecessary > output will be generated. ### Trace a Tree Creation Site @@ -31,7 +31,7 @@ your search to the code of that phase. For example if you found a problematic tr `posttyper`, the problem most likely appears in the code of [PostTyper]. We can trace the exact point the tree was generated by looking for its unique ID, and then generating a stack trace at its creation: -1. Run the compiler with `-Xprint:posttyper` and `-Yshow-tree-ids` flags. +1. Run the compiler with `-Vprint:posttyper` and `-Yshow-tree-ids` flags. This will only print the trees of the `posttyper` phase. This time you should see the tree in question be printed alongside its ID. You'll see something like `println#223("Hello World"#37)`. 2. Copy the ID of the desired tree. @@ -43,7 +43,7 @@ Do not use a conditional breakpoint, the time overhead is very significant for a ### Enhanced Tree Printing -As seen above `-Xprint:` can be enhanced with further configuration flags, found in +As seen above `-Vprint:` can be enhanced with further configuration flags, found in [ScalaSettings]. For example, you can additionally print the type of a tree with `-Xprint-types`. ## Increasing Logging Output diff --git a/docs/_docs/contributing/issues/reproduce.md b/docs/_docs/contributing/issues/reproduce.md index ca5da324a867..cb26e7eb764e 100644 --- a/docs/_docs/contributing/issues/reproduce.md +++ b/docs/_docs/contributing/issues/reproduce.md @@ -39,9 +39,9 @@ $ scalac Here are some useful debugging ``: -* `-Xprint:PHASE1,PHASE2,...` or `-Xprint:all`: prints the `AST` after each +* `-Vprint:PHASE1,PHASE2,...` or `-Vprint:all`: prints the `AST` after each specified phase. Phase names can be found by examining the - `dotty.tools.dotc.transform.*` classes for their `phaseName` field e.g., `-Xprint:erasure`. + `dotty.tools.dotc.transform.*` classes for their `phaseName` field e.g., `-Vprint:erasure`. You can discover all phases in the `dotty.tools.dotc.Compiler` class * `-Ylog:PHASE1,PHASE2,...` or `-Ylog:all`: enables `ctx.log("")` logging for the specified phase. @@ -142,7 +142,7 @@ $ (rm -rv out || true) && mkdir out # clean up compiler output, create `out` dir scalac # Invoke the compiler task defined by the Dotty sbt project -d $here/out # All the artefacts go to the `out` folder created earlier - # -Xprint:typer # Useful debug flags, commented out and ready for quick usage. Should you need one, you can quickly access it by uncommenting it. + # -Vprint:typer # Useful debug flags, commented out and ready for quick usage. Should you need one, you can quickly access it by uncommenting it. # -Ydebug-error # -Yprint-debug # -Yprint-debug-owners diff --git a/docs/_docs/contributing/scaladoc.md b/docs/_docs/contributing/scaladoc.md index 05bcc0166fb6..4a56b469fd92 100644 --- a/docs/_docs/contributing/scaladoc.md +++ b/docs/_docs/contributing/scaladoc.md @@ -46,21 +46,15 @@ More information about specific scaladoc flags you can find inside [Usage docs]( ## Developing -For every PR named with prefix `scaladoc/`, we build documentation for scaladoc and Dotty. For example, for -PR `scaladoc/update-docs` you can find them at: - -- -- -- - -Note that these correspond to the contents of `output` directory - that's -precisely what they are. - -You can also find the result of building the same sites for latest `master` at: - -- -- -- +For every PR named with prefix `scaladoc/`, we build documentation for scaladoc and Dotty. +These can be build locally using: +```sh +sbt scaladoc/generateSelfDocumentation +sbt scaladoc/generateTestcasesDocumentation +sbt scaladoc/generateScalaDocumentation +sbt scaladoc/generateReferenceDocumentation +``` +Note that these correspond to the contents of `output` directory. You can run the Scaladoc tool from the sbt prompt as a user would run it with `scaladoc/run`, passing `-d ` and some `.tasty` files as @@ -71,7 +65,7 @@ input on the command line. From the sbt prompt, `scaladoc/test` runs the tests. You can also run specific signature tests with `testOnly`, -for example `scaladoc/test *scaladoc.signatures.MarkdownCode`. +for example `scaladoc/testOnly *scaladoc.signatures.MarkdownCode`. Most tests rely on comparing signatures (of classes, methods, objects etc.) extracted from the generated documentation to signatures found in source files (extracted using Scalameta). Such tests are defined using [SignatureTest](test/dotty/tools/scaladoc/signatures/SignatureTest.scala) class @@ -140,4 +134,4 @@ Tasty Reflect is the dedicated way in Scala 3 of accessing this information. ## Credits -- [Flatart](https://www.iconfinder.com/Flatart) - Gitter icon +- Flatart - Gitter icon diff --git a/docs/_docs/contributing/setting-up-your-ide.md b/docs/_docs/contributing/setting-up-your-ide.md index 3779ce1c3403..690b42a37a51 100644 --- a/docs/_docs/contributing/setting-up-your-ide.md +++ b/docs/_docs/contributing/setting-up-your-ide.md @@ -42,7 +42,15 @@ want to make sure you do two things: + val enableBspAllProjects = true, ``` -2. Run `sbt publishLocal` to get the needed presentation compiler jars. +2. Run in sbt shell `sbt> scala3-bootstrapped/compile` and then `sbt> scala3-bootstrapped/publishLocalBin` + to get the required presentation compiler jars. + + If any step fails due to random errors, try removing `./out/` directory and running `sbt> clean` + + This step has to be repeated every time compiler version has been bumped. + + + By default Metals uses Bloop build server, however you can also use sbt directly. You can achieve this with the `Metals: Switch Build Server` command diff --git a/docs/_docs/internals/dotty-internals-1-notes.md b/docs/_docs/internals/dotty-internals-1-notes.md index 899fb49438b3..ea1a9d3f0669 100644 --- a/docs/_docs/internals/dotty-internals-1-notes.md +++ b/docs/_docs/internals/dotty-internals-1-notes.md @@ -3,7 +3,7 @@ layout: doc-page title: Dotty Internals 1: Trees & Symbols (Meeting Notes) --- -These are meeting notes for the [Dotty Internals 1: Trees & Symbols](https://www.youtube.com/watch?v=yYd-zuDd3S8) talk by [Dmitry Petrashko](http://twitter.com/darkdimius) on Mar 21, 2017. +These are meeting notes for the [Dotty Internals 1: Trees & Symbols](https://www.youtube.com/watch?v=yYd-zuDd3S8) talk by [Dmitry Petrashko](https://twitter.com/darkdimius) on Mar 21, 2017. # Entry point `dotc/Compiler.scala` diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index ae6d85c7deae..2188f4ee0d90 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -240,6 +240,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/changed-features/overload-resolution.md b/docs/_docs/reference/changed-features/overload-resolution.md index 621515c2a7f8..470e9e078e62 100644 --- a/docs/_docs/reference/changed-features/overload-resolution.md +++ b/docs/_docs/reference/changed-features/overload-resolution.md @@ -46,6 +46,10 @@ This change is motivated by the new language feature [extension methods](../contextual/extension-methods.md), where emerges the need to do overload resolution based on additional argument blocks. +Note that extension methods do not compete with member methods when extension syntax is used. +Contrarily, since extension methods can be called using ordinary method call syntax, +in the case of ambiguity, the member method will be preferred. + ## Parameter Types of Function Values The handling of function values with missing parameter types has been improved. We can now diff --git a/docs/_docs/reference/contextual/context-functions-spec.md b/docs/_docs/reference/contextual/context-functions-spec.md index 385ee3901fd8..95276f054092 100644 --- a/docs/_docs/reference/contextual/context-functions-spec.md +++ b/docs/_docs/reference/contextual/context-functions-spec.md @@ -72,7 +72,7 @@ documentation](../dropped-features/limit22.md). See the section on Expressiveness from [Simplicitly: foundations and applications of implicit function -types](https://dl.acm.org/citation.cfm?id=3158130). +types](https://dl.acm.org/doi/10.1145/3158130). ## Type Checking diff --git a/docs/_docs/reference/contextual/using-clauses.md b/docs/_docs/reference/contextual/using-clauses.md index 9177a2f47dc9..b5bb03a0c2a9 100644 --- a/docs/_docs/reference/contextual/using-clauses.md +++ b/docs/_docs/reference/contextual/using-clauses.md @@ -130,6 +130,74 @@ f(global)(using ctx)(using sym, kind) But `f(global)(using sym, kind)` would give a type error. +## Explicit `using` Clauses + +Missing arguments to an application can be supplied by either implicit search or default arguments. + +For an explicit application, with either explicit parameters or implicit parameters supplied with explicit `using`, +missing arguments are filled in by defaults. Additionally, any remaining implicit parameters are supplied, +if an implicit value is available. + +For an implicit application of context parameters, where arguments were not supplied explicitly, implicit values +are supplied if they are available, and default arguments used for any that are missing. + +In other words, for a context parameter with a default argument, whether an implicit value is supplied +or the default arg depends on whether the application has explicit `using`. + +As an illustration, + +```scala + +def f(using p: Boolean, i: Int, s: String = "hello, world") = s"p=$p, i=$i, s=$s" + +given Boolean = true +given Int = 42 +given String = "contextual" + +f // all args are supplied implicitly from scope, since implicit values are available + +f(using p = false, i = 27, s = "explicit") // all args are explicit + +f(using s = "partial") // one arg is explicit, others are implicit + +f(using i = 27, s = "partial") // only one arg is supplied implicitly + +f(using p = false, i = 27) // for explicit using, the missing arg is supplied from the default arg + +f(using p = false) // one default arg and one implicit value + +``` + +For the last two cases, where a default arg shadows an implicit value, the compiler warns: + +``` +Argument for implicit parameter s was supplied using a default argument. +``` + +Class constructors are handled the same way, except that they are inherently mixed, +since classes always have an explicit parameter list (which may be empty). + +For example, + +```scala + +class C(using p: Boolean, i: Int, s: String = "hello, world"): + override def toString = s"p=$p, i=$i, s=$s" + +new C // same as new C(summon[Boolean], summon[Int], summon[String])() +``` + +Observe that the explicit parameter list is appended to the signature. + +```scala + +C() // same as new C, where the implicit arguments are inserted before the explicit contructor proxy parens +C(using p = false, i = 27, s = "explicit") // explicit using, with empty parens supplied as for new C +C(using s = "partial")() // the trailing parens are required when args are previously inferred + +``` + +(Scala 2 `implicit` parameter lists are always trailing, even for constructors.) ## Summoning Instances diff --git a/docs/_docs/reference/dropped-features/limit22.md b/docs/_docs/reference/dropped-features/limit22.md index e72aeadbe2ca..9b91295a3f8e 100644 --- a/docs/_docs/reference/dropped-features/limit22.md +++ b/docs/_docs/reference/dropped-features/limit22.md @@ -11,7 +11,7 @@ maximal number of fields in tuple types have been dropped. [`scala.Function22`](https://www.scala-lang.org/api/current/scala/Function22.html) are erased to a new trait [`scala.runtime.FunctionXXL`](https://scala-lang.org/api/3.x/scala/runtime/FunctionXXL.html). * Tuples can also have an arbitrary number of fields. Tuples beyond [`scala.Tuple22`](https://www.scala-lang.org/api/current/scala/Tuple22.html) - are erased to a new class [`scala.runtime.TupleXXL`](https://scala-lang.org/api/3.x/scala/runtime/TupleXXL.html) (which extends the trait [`scala.Product`](https://scala-lang.org/api/3.x/scala/Product.md)). Furthermore, they support generic + are erased to a new class [`scala.runtime.TupleXXL`](https://scala-lang.org/api/3.x/scala/runtime/TupleXXL.html) (which extends the trait [`scala.Product`](https://scala-lang.org/api/3.x/scala/Product.html)). Furthermore, they support generic operation such as concatenation and indexing. Both of these are implemented using arrays. diff --git a/docs/_docs/reference/dropped-features/this-qualifier.md b/docs/_docs/reference/dropped-features/this-qualifier.md index f75d19356696..541c91e5cdfa 100644 --- a/docs/_docs/reference/dropped-features/this-qualifier.md +++ b/docs/_docs/reference/dropped-features/this-qualifier.md @@ -29,3 +29,15 @@ This can cause problems if a program tries to access the missing private field v // [C] needed if `field` is to be accessed through reflection val retained = field * field ``` + +Class parameters are normally inferred object-private, +so that members introduced by explicitly declaring them `val` or `var` are exempt from the rule described here. + +In particular, the following field is not excluded from variance checking: +```scala + class C[-T](private val t: T) // error +``` +And in contrast to the private field shown above, this field is not eliminated: +```scala + class C(private val c: Int) +``` diff --git a/docs/_docs/reference/dropped-features/type-projection.md b/docs/_docs/reference/dropped-features/type-projection.md index 08b5ffb34eca..9b9f643ceb6e 100644 --- a/docs/_docs/reference/dropped-features/type-projection.md +++ b/docs/_docs/reference/dropped-features/type-projection.md @@ -4,15 +4,18 @@ title: "Dropped: General Type Projection" nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/type-projection.html --- -Scala so far allowed general type projection `T#A` where `T` is an arbitrary type -and `A` names a type member of `T`. +Scala 2 allowed general type projection `T#A` where `T` is an arbitrary type and `A` names a type member of `T`. +This turns out to be [unsound](https://github.com/scala/scala3/issues/1050) (at least when combined with other Scala 3 features). -Scala 3 disallows this if `T` is an abstract type (class types and type aliases -are fine). This change was made because unrestricted type projection -is [unsound](https://github.com/lampepfl/dotty/issues/1050). - -This restriction rules out the [type-level encoding of a combinator -calculus](https://michid.wordpress.com/2010/01/29/scala-type-level-encoding-of-the-ski-calculus/). +To remedy this, Scala 3 only allows type projection if `T` is a concrete type (any type which is not abstract), an example for such a type would be a class type (`class T`). +A type is abstract if it is: +* An abstract type member (`type T` without `= SomeType`) +* A type parameter (`[T]`) +* An alias to an abstract type (`type T = SomeAbstractType`). +There are no restriction on `A` apart from the fact it has to be a member type of `T`, for example a subclass (`class T { class A }`). To rewrite code using type projections on abstract types, consider using path-dependent types or implicit parameters. + +This restriction rules out the [type-level encoding of a combinator +calculus](https://michid.wordpress.com/2010/01/29/scala-type-level-encoding-of-the-ski-calculus/). \ No newline at end of file diff --git a/docs/_docs/reference/experimental/canthrow.md b/docs/_docs/reference/experimental/canthrow.md index cd0da72f3d4b..c105170af4a1 100644 --- a/docs/_docs/reference/experimental/canthrow.md +++ b/docs/_docs/reference/experimental/canthrow.md @@ -38,7 +38,7 @@ Ugh! No wonder checked exceptions in Java are not very popular. ## Monadic Effects -So the dilemma is that exceptions are easy to use only as long as we forget static type checking. This has caused many people working with Scala to abandon exceptions altogether and to use an error monad like [`Either`](https://scala-lang.org/api/3.x/scala/util/Either.html) instead. This can work in many situations but is not without its downsides either. It makes code a lot more complicated and harder to refactor. It means one is quickly confronted with the problem how to work with several monads. In general, dealing with one monad at a time in Scala is straightforward but dealing with several monads together is much less pleasant since monads don't compose. A great number of techniques have been proposed, implemented, and promoted to deal with this, from monad transformers, to free monads, to tagless final. But none of these techniques is universally liked; each introduces a complicated DSL that's hard to understand for non-experts, introduces runtime overheads, and makes debugging difficult. In the end, quite a few developers prefer to work instead with a single "super-monad" like [`ZIO`](https://zio.dev/version-1.x/datatypes/core/zio) that has error propagation built in alongside other aspects. This one-size fits all approach can work very nicely, even though (or is it because?) it represents an all-encompassing framework. +So the dilemma is that exceptions are easy to use only as long as we forget static type checking. This has caused many people working with Scala to abandon exceptions altogether and to use an error monad like [`Either`](https://scala-lang.org/api/3.x/scala/util/Either.html) instead. This can work in many situations but is not without its downsides either. It makes code a lot more complicated and harder to refactor. It means one is quickly confronted with the problem how to work with several monads. In general, dealing with one monad at a time in Scala is straightforward but dealing with several monads together is much less pleasant since monads don't compose. A great number of techniques have been proposed, implemented, and promoted to deal with this, from monad transformers, to free monads, to tagless final. But none of these techniques is universally liked; each introduces a complicated DSL that's hard to understand for non-experts, introduces runtime overheads, and makes debugging difficult. In the end, quite a few developers prefer to work instead with a single "super-monad" like [`ZIO`](https://zio.dev/overview/summary#zio) that has error propagation built in alongside other aspects. This one-size fits all approach can work very nicely, even though (or is it because?) it represents an all-encompassing framework. However, a programming language is not a framework; it has to cater also for those applications that do not fit the framework's use cases. So there's still a strong motivation for getting exception checking right. @@ -124,7 +124,7 @@ catch ... Note that the right-hand side of the synthesized given is `compiletime.erasedValue`. This is OK since this given is erased; it will not be executed at runtime. -**Note 1:** The [`saferExceptions`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$experimental$$saferExceptions$.html) feature is designed to work only with checked exceptions. An exception type is _checked_ if it is a subtype of +**Note 1:** The [`saferExceptions`](https://scala-lang.org/api/3.x/scala/language$$experimental$$saferExceptions$.html) feature is designed to work only with checked exceptions. An exception type is _checked_ if it is a subtype of `Exception` but not of `RuntimeException`. The signature of `CanThrow` still admits `RuntimeException`s since `RuntimeException` is a proper subtype of its bound, `Exception`. But no capabilities will be generated for `RuntimeException`s. Furthermore, `throws` clauses also may not refer to `RuntimeException`s. @@ -204,7 +204,7 @@ So the takeaway is that the effects as capabilities model naturally provides for ## Gradual Typing Via Imports -Another advantage is that the model allows a gradual migration from current unchecked exceptions to safer exceptions. Imagine for a moment that [`experimental.saferExceptions`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$experimental$$saferExceptions$.html) is turned on everywhere. There would be lots of code that breaks since functions have not yet been properly annotated with `throws`. But it's easy to create an escape hatch that lets us ignore the breakages for a while: simply add the import +Another advantage is that the model allows a gradual migration from current unchecked exceptions to safer exceptions. Imagine for a moment that [`experimental.saferExceptions`](https://scala-lang.org/api/3.x/scala/language$$experimental$$saferExceptions$.html) is turned on everywhere. There would be lots of code that breaks since functions have not yet been properly annotated with `throws`. But it's easy to create an escape hatch that lets us ignore the breakages for a while: simply add the import ```scala import scala.unsafeExceptions.canThrowAny ``` diff --git a/docs/_docs/reference/experimental/cc.md b/docs/_docs/reference/experimental/cc.md index 162a4661e10c..ff7c01704b78 100644 --- a/docs/_docs/reference/experimental/cc.md +++ b/docs/_docs/reference/experimental/cc.md @@ -656,7 +656,7 @@ TBD The following options are relevant for capture checking. - - **-Xprint:cc** Prints the program with capturing types as inferred by capture checking. + - **-Vprint:cc** Prints the program with capturing types as inferred by capture checking. - **-Ycc-debug** Gives more detailed, implementation-oriented information about capture checking, as described in the next section. The implementation supporting capture checking with these options is currently in branch `cc-experiment` on dotty.epfl.ch. diff --git a/docs/_docs/reference/experimental/erased-defs.md b/docs/_docs/reference/experimental/erased-defs.md index d266cd6c9d19..5ddb81873151 100644 --- a/docs/_docs/reference/experimental/erased-defs.md +++ b/docs/_docs/reference/experimental/erased-defs.md @@ -4,8 +4,8 @@ title: "Erased Definitions" nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/erased-defs.html --- -`erased` is a modifier that expresses that some definition or expression is erased by the compiler instead of being represented in the compiled output. It is not yet part of the Scala language standard. To enable `erased`, turn on the language feature -[`experimental.erasedDefinitions`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$experimental$$erasedDefinitions$.html). This can be done with a language import +`erased` is a modifier that expresses that some value or parameter is erased by the compiler instead of being represented in the compiled output. It is not yet part of the Scala language standard. To enable `erased`, turn on the language feature +[`experimental.erasedDefinitions`](https://scala-lang.org/api/3.x/scala/language$$experimental$$erasedDefinitions$.html). This can be done with a language import ```scala import scala.language.experimental.erasedDefinitions ``` diff --git a/docs/_docs/reference/experimental/explicit-nulls.md b/docs/_docs/reference/experimental/explicit-nulls.md index 47b21d3a5e23..4f1630f62d9b 100644 --- a/docs/_docs/reference/experimental/explicit-nulls.md +++ b/docs/_docs/reference/experimental/explicit-nulls.md @@ -64,7 +64,7 @@ So far, we have found the following useful: Don't use `.nn` on mutable variables directly, because it may introduce an unknown type into the type of the variable. -- An [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html) language feature. +- An [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/language$$unsafeNulls$.html) language feature. When imported, `T | Null` can be used as `T`, similar to regular Scala (without explicit nulls). @@ -453,10 +453,10 @@ We don't support: ### UnsafeNulls -It is difficult to work with many nullable values, we introduce a language feature [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html). +It is difficult to work with many nullable values, we introduce a language feature [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/language$$unsafeNulls$.html). Inside this "unsafe" scope, all `T | Null` values can be used as `T`. -Users can import [`scala.language.unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html) to create such scopes, or use `-language:unsafeNulls` to enable this feature globally (for migration purpose only). +Users can import [`scala.language.unsafeNulls`](https://scala-lang.org/api/3.x/scala/language$$unsafeNulls$.html) to create such scopes, or use `-language:unsafeNulls` to enable this feature globally (for migration purpose only). Assume `T` is a reference type (a subtype of `AnyRef`), the following unsafe operation rules are applied in this unsafe-nulls scope: @@ -475,7 +475,7 @@ can be used as `T2` if `T1` is a subtype of `T2` using regular subtyping rules Addtionally, `null` can be used as `AnyRef` (`Object`), which means you can select `.eq` or `.toString` on it. -The program in [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html) will have a **similar** semantic as regular Scala, but not **equivalent**. +The program in [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/language$$unsafeNulls$.html) will have a **similar** semantic as regular Scala, but not **equivalent**. For example, the following code cannot be compiled even using unsafe nulls. Because of the Java interoperation, the type of the get method becomes `T | Null`. @@ -488,7 +488,7 @@ Since the compiler doesn’t know whether `T` is a reference type, it is unable to `T`. A `.nn` need to be inserted after `xs.get(0)` by user manually to fix the error, which strips the `Null` from its type. -The intention of this [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html) is to give users a better migration path for explicit nulls. +The intention of this [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/language$$unsafeNulls$.html) is to give users a better migration path for explicit nulls. Projects for Scala 2 or regular Scala 3 can try this by adding `-Yexplicit-nulls -language:unsafeNulls` to the compile options. A small number of manual modifications are expected. To migrate to the full explicit nulls feature in the future, `-language:unsafeNulls` can be dropped and add @@ -519,9 +519,9 @@ class C[T >: Null <: String] // define a type bound with unsafe conflict bound val n = nullOf[String] // apply a type bound unsafely ``` -Without the [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html), all these unsafe operations will not be type-checked. +Without the [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/language$$unsafeNulls$.html), all these unsafe operations will not be type-checked. -[`unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html) also works for extension methods and implicit search. +[`unsafeNulls`](https://scala-lang.org/api/3.x/scala/language$$unsafeNulls$.html) also works for extension methods and implicit search. ```scala import scala.language.unsafeNulls diff --git a/docs/_docs/reference/language-versions/source-compatibility.md b/docs/_docs/reference/language-versions/source-compatibility.md index 145c4a84d11b..914dffbd3a41 100644 --- a/docs/_docs/reference/language-versions/source-compatibility.md +++ b/docs/_docs/reference/language-versions/source-compatibility.md @@ -6,9 +6,9 @@ movedTo: https://docs.scala-lang.org/scala3/reference/language-versions/source-c Scala 3 does NOT guarantee source compatibility between different minor language versions (e.g. some syntax valid in 3.x might get deprecated and then phased out in 3.y for y > x). There are also some syntax structures that were valid in Scala 2 but are not anymore in Scala 3. However the compiler provides a possibility to specify the desired version of syntax used in a particular file or globally for a run of the compiler to make migration between versions easier. -The default Scala language syntax version currently supported by the Dotty compiler is [`3.2`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2$.html). There are also other language versions that can be specified instead: +The default Scala language syntax version currently supported by the Dotty compiler is [`3.2`](https://scala-lang.org/api/3.x/scala/language$$3/2$.html). There are also other language versions that can be specified instead: -- [`3.0-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/0-migration$.html): Same as +- [`3.0-migration`](https://scala-lang.org/api/3.x/scala/language$$3/0$minusmigration$$.html): Same as `3.0` and `3.1`, but with a Scala 2 compatibility mode that helps moving Scala 2.13 sources over to Scala 3. In particular, it - flags some Scala 2 constructs that are disallowed in Scala 3 as migration warnings instead of hard errors, @@ -16,16 +16,16 @@ The default Scala language syntax version currently supported by the Dotty compi - gives some additional warnings where the semantics has changed between Scala 2.13 and 3.0 - in conjunction with `-rewrite`, offer code rewrites from Scala 2.13 to 3.0. -- [`3.0`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/0$.html), [`3.1`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/1$.html): the default set of features included in scala versions `3.0.0` to `3.1.3`. -- [`3.2-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2-migration$.html): the same as `3.2`, but in conjunction with `-rewrite`, offer code rewrites from Scala `3.0/3.1` to `3.2`. -- [`3.2`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2$.html): the same as `3.0` and `3.1`, but in addition: +- [`3.0`](https://scala-lang.org/api/3.x/scala/language$$3/0$.html), [`3.1`](https://scala-lang.org/api/3.x/scala/language$$3/1$.html): the default set of features included in scala versions `3.0.0` to `3.1.3`. +- [`3.2-migration`](https://scala-lang.org/api/3.x/scala/language$$3/2$minusmigration$$.html): the same as `3.2`, but in conjunction with `-rewrite`, offer code rewrites from Scala `3.0/3.1` to `3.2`. +- [`3.2`](https://scala-lang.org/api/3.x/scala/language$$3/2$.html): the same as `3.0` and `3.1`, but in addition: - [stricter pattern bindings](https://docs.scala-lang.org/scala3/reference/changed-features/pattern-bindings.html) are now enabled (part of `future` in earlier `3.x` releases), producing warnings for refutable patterns. These warnings can be silenced to achieve the same runtime behavior, but in `future` they become errors and refutable patterns will not compile. - [Nonlocal returns](https://docs.scala-lang.org/scala3/reference/dropped-features/nonlocal-returns.html) now produce a warning upon usage (they are still an error under `future`). -- [`3.3-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/3-migration$.html): the same as `3.3` -- [`3.3`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/3$.html): the same as `3.2`, but in addition: +- [`3.3-migration`](https://scala-lang.org/api/3.x/scala/language$$3/3$minusmigration$$.html): the same as `3.3` +- [`3.3`](https://scala-lang.org/api/3.x/scala/language$$3/3$.html): the same as `3.2`, but in addition: - [Fewer braces syntax](https://docs.scala-lang.org/scala3/reference/other-new-features/indentation.html#optional-braces-for-method-arguments-1) is enabled by default. -- [`future-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future-migration$.html): Same as `future` but with additional helpers to migrate from `3.3`. Similarly to the helpers available under `3.0-migration`, these include migration warnings and optional rewrites. -- [`future`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future$.html): A preview of changes that will be introduced in `3.x` versions after `3.3`. +- [`future-migration`](https://scala-lang.org/api/3.x/scala/language$$future$minusmigration$$.html): Same as `future` but with additional helpers to migrate from `3.3`. Similarly to the helpers available under `3.0-migration`, these include migration warnings and optional rewrites. +- [`future`](https://scala-lang.org/api/3.x/scala/language$$future$.html): A preview of changes that will be introduced in `3.x` versions after `3.3`. Some Scala 2 specific idioms are dropped in this version. The feature set supported by this version may grow over time as features become stabilised for preview. There are two ways to specify a language version : diff --git a/docs/_docs/reference/other-new-features/creator-applications.md b/docs/_docs/reference/other-new-features/creator-applications.md index 8b1de02b2f25..f3b58b87bc48 100644 --- a/docs/_docs/reference/other-new-features/creator-applications.md +++ b/docs/_docs/reference/other-new-features/creator-applications.md @@ -39,8 +39,11 @@ The precise rules are as follows: - the class has a companion object (which might have been generated in step 1), and - that companion object does not already define a member named `apply`. - Each generated `apply` method forwards to one constructor of the class. It has the - same type and value parameters as the constructor. + Each generated `apply` method forwards to one constructor of the class. + It has the same type and value parameters as the constructor, + as well as the same access restriction as the class. + If the class is `protected`, then either the companion object must be `protected` + or the `apply` method will be made `protected`. Constructor proxy companions cannot be used as values by themselves. A proxy companion object must be selected with `apply` (or be applied to arguments, in which case the `apply` is implicitly diff --git a/docs/_docs/reference/other-new-features/indentation.md b/docs/_docs/reference/other-new-features/indentation.md index 9963d1ee7577..103de5d6797a 100644 --- a/docs/_docs/reference/other-new-features/indentation.md +++ b/docs/_docs/reference/other-new-features/indentation.md @@ -189,7 +189,7 @@ Define for an arbitrary sequence of tokens or non-terminals `TS`: ```ebnf :<<< TS >>> ::= ‘{’ TS ‘}’ - | + | TS ``` Then the grammar changes as follows: ```ebnf diff --git a/docs/_docs/reference/other-new-features/opaques-details.md b/docs/_docs/reference/other-new-features/opaques-details.md index d285ec8e8325..e6c2ff80649e 100644 --- a/docs/_docs/reference/other-new-features/opaques-details.md +++ b/docs/_docs/reference/other-new-features/opaques-details.md @@ -109,6 +109,45 @@ object obj: ``` The opaque type alias `A` is transparent in its scope, which includes the definition of `x`, but not the definitions of `obj` and `y`. +## Opaque Types in Transparent Inline Methods + +Additional care is required if an opaque type is returned from a transparent inline method, located inside a context where that opaque type is defined. +Since the typechecking and type inference of the body of the method is done from the perspective of that context, the returned types might contain dealiased opaque types. Generally, this means that calls to those transparent methods will return a `DECLARED & ACTUAL`, where `DECLARED` is the return type defined in the method declaration, and `ACTUAL` is the type returned after the inlining, which might include dealiased opaque types. + +API designers can ensure that the correct type is returned by explicitly annotating it inside of the method body with `: ExpectedType` or by explicitly passing type parameters to the method being returned. Explicitly annotating like this will help for the outermost transparent inline method calls, but will not affect the nested calls, as, from the perspective of the new context into which we are inlining, those might still have to be dealiased to avoid compilation errors: + +```scala +object Time: + opaque type Time = String + opaque type Seconds <: Time = String + + // opaque type aliases have to be dealiased in nested calls, + // otherwise the resulting program might not be typed correctly + // in the below methods this will be typed as Seconds & String despite + // the explicit type declaration + transparent inline def sec(n: Double): Seconds = + s"${n}s": Seconds + + transparent inline def testInference(): List[Time] = + List(sec(5)) // infers List[String] and returns List[Time] & List[String], not List[Seconds] + transparent inline def testGuarded(): List[Time] = + List(sec(5)): List[Seconds] // returns List[Seconds] + transparent inline def testExplicitTime(): List[Time] = + List[Seconds](sec(5)) // returns List[Seconds] + transparent inline def testExplicitString(): List[Time] = + List[String](sec(5)) // returns List[Time] & List[String] + +end Time + +@main def main() = + val t1: List[String] = Time.testInference() // returns List[Time.Time] & List[String] + val t2: List[Time.Seconds] = Time.testGuarded() // returns List[Time.Seconds] + val t3: List[Time.Seconds] = Time.testExplicitTime() // returns List[Time.Seconds] + val t4: List[String] = Time.testExplicitString() // returns List[Time.Time] & List[String] +``` + +Be careful especially if what is being inlined depends on the type of those nested transparent calls. +``` ## Relationship to SIP 35 diff --git a/docs/_docs/reference/other-new-features/open-classes.md b/docs/_docs/reference/other-new-features/open-classes.md index 10af6ead669e..06e7c48c793c 100644 --- a/docs/_docs/reference/other-new-features/open-classes.md +++ b/docs/_docs/reference/other-new-features/open-classes.md @@ -31,7 +31,7 @@ Classes that are not open can still be extended, but only if at least one of two - The extending class is in the same source file as the extended class. In this case, the extension is usually an internal implementation matter. - - The language feature [`adhocExtensions`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$adhocExtensions$.html) is enabled for the extending class. This is typically enabled by an import clause in the source file of the extension: + - The language feature [`adhocExtensions`](https://scala-lang.org/api/3.x/scala/language$$adhocExtensions$.html) is enabled for the extending class. This is typically enabled by an import clause in the source file of the extension: ```scala import scala.language.adhocExtensions ``` @@ -73,7 +73,7 @@ to mock classes in tests, or to apply temporary patches that add features or fix ## Relationship with `sealed` -A class that is neither `abstract` nor `open` is similar to a `sealed` class: it can still be extended, but only in the same source file. The difference is what happens if an extension of the class is attempted in another source file. For a `sealed` class, this is an error, whereas for a simple non-open class, this is still permitted provided the [`adhocExtensions`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$adhocExtensions$.html) feature is enabled, and it gives a warning otherwise. +A class that is neither `abstract` nor `open` is similar to a `sealed` class: it can still be extended, but only in the same source file. The difference is what happens if an extension of the class is attempted in another source file. For a `sealed` class, this is an error, whereas for a simple non-open class, this is still permitted provided the [`adhocExtensions`](https://scala-lang.org/api/3.x/scala/language$$adhocExtensions$.html) feature is enabled, and it gives a warning otherwise. ## Migration diff --git a/docs/_docs/reference/other-new-features/type-test.md b/docs/_docs/reference/other-new-features/type-test.md index ec7a87230753..fb2a2e584711 100644 --- a/docs/_docs/reference/other-new-features/type-test.md +++ b/docs/_docs/reference/other-new-features/type-test.md @@ -63,7 +63,7 @@ We could create a type test at call site where the type test can be performed wi val tt: TypeTest[Any, String] = new TypeTest[Any, String]: def unapply(s: Any): Option[s.type & String] = s match - case s: String => Some(s) + case q: (s.type & String) => Some(q) case _ => None f[AnyRef, String]("acb")(using tt) diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index be64abb07119..f3bb516de8b2 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -240,6 +240,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/docs/_docs/release-notes-0.1.2.md b/docs/_docs/release-notes-0.1.2.md index 98f359c83a15..63ce6504f62a 100644 --- a/docs/_docs/release-notes-0.1.2.md +++ b/docs/_docs/release-notes-0.1.2.md @@ -84,7 +84,7 @@ This release ships with the following features: [7]: reference/contextual/by-name-context-parameters.html [8]: https://infoscience.epfl.ch/record/228518 [9]: http://docs.scala-lang.org/sips/pending/static-members.html -[10]: http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html +[10]: https://docs.scala-lang.org/scala3/reference/changed-features/lazy-vals-init.html [11]: http://magarciaepfl.github.io/scala/ [12]: https://github.com/lampepfl/dotty/commit/b2215ed23311b2c99ea638f9d7fcad9737dba588 [13]: https://github.com/lampepfl/dotty/pull/187 diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md index 4b1293258495..5d4e205aace9 100644 --- a/docs/_spec/03-types.md +++ b/docs/_spec/03-types.md @@ -1071,7 +1071,7 @@ The following properties hold about ´⌈X⌉´ (we have paper proofs for those) The "lower-bound rule" states that ´S <: T´ if ´T = q.X´ and ´q.X´ is a non-class type designator and ´S <: L´ where ´L´ is the lower bound of the underlying type definition of ´q.X´". That rule is known to break transitivy of subtyping in Scala already. -Second, we define the relation ´⋔´ on *classes* (including traits and hidden classes of objects) as: +Second, we define the relation ´⋔´ on *classes* (including traits, hidden classes of objects, and enum terms) as: - ´C ⋔ D´ if `´C ∉´ baseClasses´(D)´` and ´D´ is `final` - ´C ⋔ D´ if `´D ∉´ baseClasses´(C)´` and ´C´ is `final` diff --git a/docs/_spec/08-pattern-matching.md b/docs/_spec/08-pattern-matching.md index 4a34ae8631c4..c13aa5d44b89 100644 --- a/docs/_spec/08-pattern-matching.md +++ b/docs/_spec/08-pattern-matching.md @@ -616,7 +616,7 @@ new scala.PartialFunction[´S´, ´T´] { def apply(´x´: ´S´): ´T´ = x match { case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´ } - def isDefinedAt(´x´: ´S´): Boolean = { + def isDefinedAt(´x´: ´S´): Boolean = x match { case ´p_1´ => true ... case ´p_n´ => true case _ => false } @@ -626,6 +626,22 @@ new scala.PartialFunction[´S´, ´T´] { Here, ´x´ is a fresh name and ´T´ is the least upper bound of the types of all ´b_i´. The final default case in the `isDefinedAt` method is omitted if one of the patterns ´p_1, ..., p_n´ is already a variable or wildcard pattern. +As a convenience, the partial function may be written using function literal notation: + +```scala +(´x: S´) => e(´x´) match { + case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´ +} +``` +where the selector expression is used for matches in the expansion. +The body of the function must consist solely of the match expression. + +This syntax permits annotating the selector: + +```scala +(´x: S´) => (e(´x´): @unchecked) match { ... } +``` + ###### Example Here's an example which uses `foldLeft` to compute the scalar product of two vectors: diff --git a/docs/_spec/TODOreference/experimental/cc.md b/docs/_spec/TODOreference/experimental/cc.md index 878bc0a64ed6..c2011fbcbc88 100644 --- a/docs/_spec/TODOreference/experimental/cc.md +++ b/docs/_spec/TODOreference/experimental/cc.md @@ -655,7 +655,7 @@ TBD The following options are relevant for capture checking. - **-Ycc** Enables capture checking. - - **-Xprint:cc** Prints the program with capturing types as inferred by capture checking. + - **-Vprint:cc** Prints the program with capturing types as inferred by capture checking. - **-Ycc-debug** Gives more detailed, implementation-oriented information about capture checking, as described in the next section. The implementation supporting capture checking with these options is currently in branch `cc-experiment` on dotty.epfl.ch. diff --git a/docs/_spec/TODOreference/other-new-features/type-test.md b/docs/_spec/TODOreference/other-new-features/type-test.md index ec7a87230753..fb2a2e584711 100644 --- a/docs/_spec/TODOreference/other-new-features/type-test.md +++ b/docs/_spec/TODOreference/other-new-features/type-test.md @@ -63,7 +63,7 @@ We could create a type test at call site where the type test can be performed wi val tt: TypeTest[Any, String] = new TypeTest[Any, String]: def unapply(s: Any): Option[s.type & String] = s match - case s: String => Some(s) + case q: (s.type & String) => Some(q) case _ => None f[AnyRef, String]("acb")(using tt) diff --git a/language-server/test/dotty/tools/languageserver/CompletionTest.scala b/language-server/test/dotty/tools/languageserver/CompletionTest.scala index b71552fe5e92..f804b537ddab 100644 --- a/language-server/test/dotty/tools/languageserver/CompletionTest.scala +++ b/language-server/test/dotty/tools/languageserver/CompletionTest.scala @@ -1485,8 +1485,8 @@ class CompletionTest { | 1 match { case foo if fo${m1} => } | 1 match { case foo => fo${m2} } """ - .completion(m1, Set(("foo", Field, "Int"))) - .completion(m2, Set(("foo", Field, "Int"))) + .completion(m1, Set(("foo", Field, "(1 : Int)"))) + .completion(m2, Set(("foo", Field, "(1 : Int)"))) } @Test def patternGuardCompletionsUnApply: Unit = { diff --git a/language-server/test/dotty/tools/languageserver/DefinitionTest.scala b/language-server/test/dotty/tools/languageserver/DefinitionTest.scala index ddacd0c868e0..d6de3d971e2b 100644 --- a/language-server/test/dotty/tools/languageserver/DefinitionTest.scala +++ b/language-server/test/dotty/tools/languageserver/DefinitionTest.scala @@ -378,4 +378,38 @@ class DefinitionTest { .definition(m3 to m4, Nil) .definition(m5 to m6, Nil) .definition(m7 to m8, Nil) + + @Test def typeParam: Unit = { + code"""|class Foo[${m1}T${m2}]: + | def test: ${m3}T${m4}""" + .definition(m3 to m4, List(m1 to m2)) + } + + @Test def enumTypeParam: Unit = { + code"""|enum Test[${m1}T${m2}]: + | case EnumCase(value: ${m3}T${m4})""" + .definition(m3 to m4, List(m1 to m2)) + } + + @Test def extMethodTypeParam: Unit = { + code"""extension [${m1}T${m2}](string: String) def xxxx(y: ${m3}T${m4}) = ???""" + .definition(m3 to m4, List(m1 to m2)) + } + + @Test def typeParamCovariant: Unit = { + code"""|class Foo[+${m1}T${m2}]: + | def test: ${m3}T${m4}""" + .definition(m3 to m4, List(m1 to m2)) + } + + @Test def enumTypeParamCovariant: Unit = { + code"""|enum Test[+${m1}T${m2}]: + | case EnumCase(value: ${m3}T${m4})""" + .definition(m3 to m4, List(m1 to m2)) + } + + @Test def extMethodTypeParamCovariant: Unit = { + code"""extension [+${m1}T${m2}](string: String) def xxxx(y: ${m3}T${m4}) = ???""" + .definition(m3 to m4, List(m1 to m2)) + } } diff --git a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala index 433b2665c4c1..fbc39ee616a1 100644 --- a/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala +++ b/language-server/test/dotty/tools/languageserver/SignatureHelpTest.scala @@ -124,7 +124,7 @@ class SignatureHelpTest { |object O { | Foo(5).method($m1) |}""" - .signatureHelp(m1, List(testSig), Some(0), -1) + .signatureHelp(m1, List(testSig), Some(0), 0) } @Test def unapplyBooleanReturn: Unit = { @@ -136,7 +136,7 @@ class SignatureHelpTest { | case s @ Even(${m1}) => println(s"s has an even number of characters") | case s => println(s"s has an odd number of characters") """ - .signatureHelp(m1, Nil, Some(0), -1) + .signatureHelp(m1, Nil, Some(0), 0) } @Test def unapplyCustomClass: Unit = { diff --git a/library/src/scala/annotation/newMain.scala b/library/src/scala/annotation/newMain.scala index 552e4225a648..686f3bf14b54 100644 --- a/library/src/scala/annotation/newMain.scala +++ b/library/src/scala/annotation/newMain.scala @@ -253,6 +253,7 @@ final class newMain extends MainAnnotation[FromString, Any]: case (arg +: t, "") => recurse(t, arg, acc) case (arg +: t, l) if l.length + 1 + arg.length <= maxLength => recurse(t, s"$l $arg", acc) case (arg +: t, l) => recurse(t, arg, acc :+ l) + case (_, _) => acc } recurse(argsUsage, "", Vector()).toList diff --git a/library/src/scala/deriving/Mirror.scala b/library/src/scala/deriving/Mirror.scala index 57453a516567..a7477cf0fb2d 100644 --- a/library/src/scala/deriving/Mirror.scala +++ b/library/src/scala/deriving/Mirror.scala @@ -52,7 +52,7 @@ object Mirror { extension [T](p: ProductOf[T]) /** Create a new instance of type `T` with elements taken from product `a`. */ - def fromProductTyped[A <: scala.Product, Elems <: p.MirroredElemTypes](a: A)(using m: ProductOf[A] { type MirroredElemTypes = Elems }): T = + def fromProductTyped[A <: scala.Product, Elems <: p.MirroredElemTypes](a: A)(using ProductOf[A] { type MirroredElemTypes = Elems }): T = p.fromProduct(a) /** Create a new instance of type `T` with elements taken from tuple `t`. */ diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 873d5e1e993e..c2609eea143c 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -1,7 +1,6 @@ package scala.quoted -import scala.annotation.experimental -import scala.annotation.implicitNotFound +import scala.annotation.{experimental, implicitNotFound, unused} import scala.reflect.TypeTest /** Current Quotes in scope @@ -879,10 +878,14 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * If `sym` refers to a class member `foo` in class `C`, * returns a tree representing `C.this.foo`. * + * If `sym` refers to an object member `foo` in object C, itself in prefix + * `pre` (which might include `.this`, if it contains a class), + * returns `pre.C.foo`. + * * If `sym` refers to a local definition `foo`, returns * a tree representing `foo`. * - * @note In both cases, the constructed tree should only + * @note In all cases, the constructed tree should only * be spliced into the places where such accesses make sense. * For example, it is incorrect to have `C.this.foo` outside * the class body of `C`, or have `foo` outside the lexical @@ -3553,7 +3556,10 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Methods of the module object `val StringConstant` */ trait StringConstantModule { this: StringConstant.type => - /** Create a constant String value */ + /** Create a constant String value + * + * @throw `IllegalArgumentException` if the argument is `null` + */ def apply(x: String): StringConstant /** Match String value constant and extract its value */ def unapply(constant: StringConstant): Some[String] @@ -3939,11 +3945,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Tree of this definition * - * If this symbol `isClassDef` it will return `a `ClassDef`, - * if this symbol `isTypeDef` it will return `a `TypeDef`, - * if this symbol `isValDef` it will return `a `ValDef`, - * if this symbol `isDefDef` it will return `a `DefDef` - * if this symbol `isBind` it will return `a `Bind`, + * If this symbol `isClassDef` it will return a `ClassDef`, + * if this symbol `isTypeDef` it will return a `TypeDef`, + * if this symbol `isDefDef` it will return a `DefDef`, + * if this symbol `isBind` it will return a `Bind`, + * if this symbol `isValDef` it will return a `ValDef`, + * or a `DefDef` (as the compiler can replace val class members with defs during compilation), * else will throw * * **Warning**: avoid using this method in macros. @@ -4797,7 +4804,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => foldTree(foldTree(foldTree(x, cond)(owner), thenp)(owner), elsep)(owner) case While(cond, body) => foldTree(foldTree(x, cond)(owner), body)(owner) - case Closure(meth, tpt) => + case Closure(meth, _) => foldTree(x, meth)(owner) case Match(selector, cases) => foldTrees(foldTree(x, selector)(owner), cases)(owner) @@ -4875,7 +4882,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => def traverseTree(tree: Tree)(owner: Symbol): Unit = traverseTreeChildren(tree)(owner) - def foldTree(x: Unit, tree: Tree)(owner: Symbol): Unit = traverseTree(tree)(owner) + def foldTree(@unused x: Unit, tree: Tree)(owner: Symbol): Unit = traverseTree(tree)(owner) protected def traverseTreeChildren(tree: Tree)(owner: Symbol): Unit = foldOverTree((), tree)(owner) diff --git a/library/src/scala/runtime/Arrays.scala b/library/src/scala/runtime/Arrays.scala index 2d98caea4df8..97ff589c6610 100644 --- a/library/src/scala/runtime/Arrays.scala +++ b/library/src/scala/runtime/Arrays.scala @@ -1,5 +1,6 @@ package scala.runtime +import scala.annotation.unused import scala.reflect.ClassTag import java.lang.{reflect => jlr} @@ -26,6 +27,6 @@ object Arrays { /** Create an array of a reference type T. */ - def newArray[Arr](componentType: Class[_], returnType: Class[Arr], dimensions: Array[Int]): Arr = + def newArray[Arr](componentType: Class[_], @unused returnType: Class[Arr], dimensions: Array[Int]): Arr = jlr.Array.newInstance(componentType, dimensions: _*).asInstanceOf[Arr] } diff --git a/library/src/scala/runtime/LazyVals.scala b/library/src/scala/runtime/LazyVals.scala index 33ccea4325a5..5dbb24458bad 100644 --- a/library/src/scala/runtime/LazyVals.scala +++ b/library/src/scala/runtime/LazyVals.scala @@ -27,17 +27,18 @@ object LazyVals { private[this] val base: Int = { val processors = java.lang.Runtime.getRuntime.nn.availableProcessors() - 8 * processors * processors + val rawSize = 8 * processors * processors + //find the next power of 2 + 1 << (32 - Integer.numberOfLeadingZeros(rawSize - 1)) } + + private val mask: Int = base - 1 private[this] val monitors: Array[Object] = Array.tabulate(base)(_ => new Object) private def getMonitor(obj: Object, fieldId: Int = 0) = { - var id = (java.lang.System.identityHashCode(obj) + fieldId) % base - - if (id < 0) id += base - monitors(id) + monitors((java.lang.System.identityHashCode(obj) + fieldId) & mask) } private final val LAZY_VAL_MASK = 3L @@ -96,13 +97,13 @@ object LazyVals { println(s"CAS($t, $offset, $e, $v, $ord)") val mask = ~(LAZY_VAL_MASK << ord * BITS_PER_LAZY_VAL) val n = (e & mask) | (v.toLong << (ord * BITS_PER_LAZY_VAL)) - unsafe.compareAndSwapLong(t, offset, e, n) + unsafe.compareAndSwapLong(t, offset, e, n): @nowarn("cat=deprecation") } def objCAS(t: Object, offset: Long, exp: Object, n: Object): Boolean = { if (debug) println(s"objCAS($t, $exp, $n)") - unsafe.compareAndSwapObject(t, offset, exp, n) + unsafe.compareAndSwapObject(t, offset, exp, n): @nowarn("cat=deprecation") } def setFlag(t: Object, offset: Long, v: Int, ord: Int): Unit = { @@ -147,7 +148,7 @@ object LazyVals { def get(t: Object, off: Long): Long = { if (debug) println(s"get($t, $off)") - unsafe.getLongVolatile(t, off) + unsafe.getLongVolatile(t, off): @nowarn("cat=deprecation") } // kept for backward compatibility diff --git a/library/src/scala/runtime/stdLibPatches/language.scala b/library/src/scala/runtime/stdLibPatches/language.scala index 358b5d1a26b0..952caa927de2 100644 --- a/library/src/scala/runtime/stdLibPatches/language.scala +++ b/library/src/scala/runtime/stdLibPatches/language.scala @@ -64,7 +64,7 @@ object language: /** Adds support for clause interleaving: * Methods can now have as many type clauses as they like, this allows to have type bounds depend on terms: `def f(x: Int)[A <: x.type]: A` * - * @see [[http://dotty.epfl.ch/docs/reference/other-new-features/explicit-nulls.html]] + * @see [[https://docs.scala-lang.org/sips/47.html]] */ @compileTimeOnly("`clauseInterleaving` can only be used at compile time in import statements") object clauseInterleaving @@ -94,7 +94,7 @@ object language: /** Experimental support for automatic conversions of arguments, without requiring * a langauge import `import scala.language.implicitConversions`. * - * @see [[https://dotty.epfl.ch/docs/reference/experimental/into-modifier]] + * @see [[https://docs.scala-lang.org/scala3/reference/experimental/into-modifier.html]] */ @compileTimeOnly("`into` can only be used at compile time in import statements") object into @@ -161,7 +161,7 @@ object language: /** Unsafe Nulls fot Explicit Nulls * Inside the "unsafe" scope, `Null` is considered as a subtype of all reference types. * - * @see [[http://dotty.epfl.ch/docs/reference/other-new-features/explicit-nulls.html]] + * @see [[https://nightly.scala-lang.org/docs/reference/experimental/explicit-nulls.html]] */ @compileTimeOnly("`unsafeNulls` can only be used at compile time in import statements") object unsafeNulls diff --git a/pkgs/chocolatey/scala.nuspec b/pkgs/chocolatey/scala.nuspec index 83033fe4b349..ad4e3864b431 100644 --- a/pkgs/chocolatey/scala.nuspec +++ b/pkgs/chocolatey/scala.nuspec @@ -13,7 +13,7 @@ https://github.com/scala/scala3 https://scala-lang.org/ https://github.com/scala/scala3/issues - © 2002-2025, LAMP/EPFL + © 2002-2026, LAMP/EPFL https://cdn.jsdelivr.net/gh/scala/scala3@a046b0014ffd9536144d67a48f8759901b96d12f/pkgs/chocolatey/icon.svg https://github.com/scala/scala3/blob/main/LICENSE true diff --git a/presentation-compiler-testcases/src/tests/macros/metals7460.scala b/presentation-compiler-testcases/src/tests/macros/metals7460.scala new file mode 100644 index 000000000000..0b9b1ca494b2 --- /dev/null +++ b/presentation-compiler-testcases/src/tests/macros/metals7460.scala @@ -0,0 +1,20 @@ +package tests.macros + +import scala.quoted.* + +object Macros7460 { + + transparent inline def foo: String = + ${ fooImpl } + + private def fooImpl(using Quotes): Expr[String] = + Expr("foo...") + + transparent inline def bar: String = + ${ barImpl } + + private def barImpl(using Quotes): Expr[String] = + quotes.reflect.Position.ofMacroExpansion.sourceFile.getJPath.get // this line is the culprit + Expr("bar...") + +} diff --git a/presentation-compiler/src/main/dotty/tools/pc/ApplyArgsExtractor.scala b/presentation-compiler/src/main/dotty/tools/pc/ApplyArgsExtractor.scala new file mode 100644 index 000000000000..094cd66ea17c --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/pc/ApplyArgsExtractor.scala @@ -0,0 +1,269 @@ +package dotty.tools.pc + +import scala.annotation.tailrec +import scala.util.Try + +import dotty.tools.dotc.ast.Trees.ValDef +import dotty.tools.dotc.ast.tpd.* +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Denotations.MultiDenotation +import dotty.tools.dotc.core.Denotations.SingleDenotation +import dotty.tools.dotc.core.Flags +import dotty.tools.dotc.core.Flags.Method +import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.StdNames.* +import dotty.tools.dotc.core.Symbols.NoSymbol +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Symbols.defn +import dotty.tools.dotc.core.SymDenotations.NoDenotation +import dotty.tools.dotc.core.Types.* +import dotty.tools.dotc.util.Spans.Span +import dotty.tools.pc.IndexedContext +import dotty.tools.pc.utils.InteractiveEnrichments.* + +object ApplyExtractor: + def unapply(path: List[Tree])(using Context): Option[Apply] = + path match + case ValDef(_, _, _) :: Block(_, app: Apply) :: _ + if !app.fun.isInfix => Some(app) + case rest => + def getApplyForContextFunctionParam(path: List[Tree]): Option[Apply] = + path match + // fun(arg@@) + case (app: Apply) :: _ => Some(app) + // fun(arg@@), where fun(argn: Context ?=> SomeType) + // recursively matched for multiple context arguments, e.g. Context1 ?=> Context2 ?=> SomeType + case (_: DefDef) :: Block(List(_), _: Closure) :: rest => + getApplyForContextFunctionParam(rest) + case _ => None + for + app <- getApplyForContextFunctionParam(rest) + if !app.fun.isInfix + yield app + end match + +object ApplyArgsExtractor: + def getArgsAndParams( + optIndexedContext: Option[IndexedContext], + apply: Apply, + span: Span + )(using Context): List[(List[Tree], List[ParamSymbol])] = + def collectArgss(a: Apply): List[List[Tree]] = + def stripContextFuntionArgument(argument: Tree): List[Tree] = + argument match + case Block(List(d: DefDef), _: Closure) => + d.rhs match + case app: Apply => + app.args + case b @ Block(List(_: DefDef), _: Closure) => + stripContextFuntionArgument(b) + case _ => Nil + case v => List(v) + val args = a.args.flatMap(stripContextFuntionArgument) + a.fun match + case app: Apply => collectArgss(app) :+ args + case _ => List(args) + + val method = apply.fun + + val argss = collectArgss(apply) + + def fallbackFindApply(sym: Symbol) = + sym.info.member(nme.apply) match + case NoDenotation => Nil + case den => List(den.symbol) + + // fallback for when multiple overloaded methods match the supplied args + def fallbackFindMatchingMethods() = + def matchingMethodsSymbols( + indexedContext: IndexedContext, + method: Tree + ): List[Symbol] = + method match + case Ident(name) => indexedContext.findSymbol(name).getOrElse(Nil) + case Select(This(_), name) => indexedContext.findSymbol(name).getOrElse(Nil) + case sel @ Select(from, name) => + val symbol = from.symbol + val ownerSymbol = + if symbol.is(Method) && symbol.owner.isClass then + Some(symbol.owner) + else Try(symbol.info.classSymbol).toOption + ownerSymbol.map(sym => sym.info.member(name)).collect { + case single: SingleDenotation => List(single.symbol) + case multi: MultiDenotation => multi.allSymbols + }.getOrElse(Nil) + case Apply(fun, _) => matchingMethodsSymbols(indexedContext, fun) + case _ => Nil + + val matchingMethods = + for + indexedContext <- optIndexedContext.toList + potentialMatch <- matchingMethodsSymbols(indexedContext, method) + if potentialMatch.is(Flags.Method) && + potentialMatch.vparamss.length >= argss.length && + Try(potentialMatch.isAccessibleFrom(apply.symbol.info)).toOption + .getOrElse(false) && + potentialMatch.vparamss + .zip(argss) + .reverse + .zipWithIndex + .forall { case (pair, index) => + FuzzyArgMatcher(potentialMatch.tparams) + .doMatch(allArgsProvided = index != 0, span) + .tupled(pair) + } + yield potentialMatch + + matchingMethods + end fallbackFindMatchingMethods + + val matchingMethods: List[Symbol] = + if method.symbol.paramSymss.nonEmpty then + val allArgsAreSupplied = + val vparamss = method.symbol.vparamss + vparamss.length == argss.length && vparamss + .zip(argss) + .lastOption + .exists { case (baseParams, baseArgs) => + baseArgs.length == baseParams.length + } + // ``` + // m(arg : Int) + // m(arg : Int, anotherArg : Int) + // m(a@@) + // ``` + // complier will choose the first `m`, so we need to manually look for the other one + if allArgsAreSupplied then + val foundPotential = fallbackFindMatchingMethods() + if foundPotential.contains(method.symbol) then foundPotential + else method.symbol :: foundPotential + else List(method.symbol) + else if method.symbol.is(Method) || method.symbol == NoSymbol then + fallbackFindMatchingMethods() + else fallbackFindApply(method.symbol) + end if + + matchingMethods.map { methodSym => + val vparamss = methodSym.vparamss + + // get params and args we are interested in + // e.g. + // in the following case, the interesting args and params are + // - params: [apple, banana] + // - args: [apple, b] + // ``` + // def curry(x: Int)(apple: String, banana: String) = ??? + // curry(1)(apple = "test", b@@) + // ``` + val (baseParams0, baseArgs) = + vparamss.zip(argss).lastOption.getOrElse((Nil, Nil)) + + val baseParams: List[ParamSymbol] = + def defaultBaseParams = baseParams0.map(JustSymbol(_)) + + @tailrec + def getRefinedParams(refinedType: Type, level: Int): List[ParamSymbol] = + if level > 0 then + val resultTypeOpt = + refinedType match + case RefinedType(AppliedType(_, args), _, _) => args.lastOption + case AppliedType(_, args) => args.lastOption + case _ => None + resultTypeOpt match + case Some(resultType) => getRefinedParams(resultType, level - 1) + case _ => defaultBaseParams + else + refinedType match + case RefinedType(AppliedType(_, args), _, MethodType(ri)) => + baseParams0.zip(ri).zip(args).map { case ((sym, name), arg) => + RefinedSymbol(sym, name, arg) + } + case _ => defaultBaseParams + + // finds param refinements for lambda expressions + // val hello: (x: Int, y: Int) => Unit = (x, _) => println(x) + @tailrec + def refineParams(method: Tree, level: Int): List[ParamSymbol] = + method match + case Select(Apply(f, _), _) => refineParams(f, level + 1) + case Select(h, name) => + // for Select(foo, name = apply) we want `foo.symbol` + if name == nme.apply then getRefinedParams(h.symbol.info, level) + else getRefinedParams(method.symbol.info, level) + case Apply(f, _) => + refineParams(f, level + 1) + case _ => getRefinedParams(method.symbol.info, level) + refineParams(method, 0) + + end baseParams + + (baseArgs, baseParams) + } + + extension (method: Symbol) + def vparamss(using Context) = method.filteredParamss(_.isTerm) + def tparams(using Context) = method.filteredParamss(_.isType).flatten + def filteredParamss(f: Symbol => Boolean)(using Context) = + method.paramSymss.filter(params => params.forall(f)) +sealed trait ParamSymbol: + def name: Name + def info: Type + def symbol: Symbol + def nameBackticked(using Context) = name.decoded.backticked + +case class JustSymbol(symbol: Symbol)(using Context) extends ParamSymbol: + def name: Name = symbol.name + def info: Type = symbol.info + +case class RefinedSymbol(symbol: Symbol, name: Name, info: Type) + extends ParamSymbol + +class FuzzyArgMatcher(tparams: List[Symbol])(using Context): + + /** A heuristic for checking if the passed arguments match the method's + * arguments' types. For non-polymorphic methods we use the subtype relation + * (`<:<`) and for polymorphic methods we use a heuristic. We check the args + * types not the result type. + */ + def doMatch( + allArgsProvided: Boolean, + span: Span + )(expectedArgs: List[Symbol], actualArgs: List[Tree]) = + (expectedArgs.length == actualArgs.length || + (!allArgsProvided && expectedArgs.length >= actualArgs.length)) && + actualArgs.zipWithIndex.forall { + case (arg: Ident, _) if arg.span.contains(span) => true + case (NamedArg(name, arg), _) => + expectedArgs.exists { expected => + expected.name == name && (!arg.hasType || arg.typeOpt.unfold + .fuzzyArg_<:<(expected.info)) + } + case (arg, i) => + !arg.hasType || arg.typeOpt.unfold.fuzzyArg_<:<(expectedArgs(i).info) + } + + extension (arg: Type) + def fuzzyArg_<:<(expected: Type) = + if tparams.isEmpty then arg <:< expected + else arg <:< substituteTypeParams(expected) + def unfold = + arg match + case arg: TermRef => arg.underlying + case e => e + + private def substituteTypeParams(t: Type): Type = + t match + case e if tparams.exists(_ == e.typeSymbol) => + val matchingParam = tparams.find(_ == e.typeSymbol).get + matchingParam.info match + case b @ TypeBounds(_, _) => WildcardType(b) + case _ => WildcardType + case o @ OrType(e1, e2) => + OrType(substituteTypeParams(e1), substituteTypeParams(e2), o.isSoft) + case AndType(e1, e2) => + AndType(substituteTypeParams(e1), substituteTypeParams(e2)) + case AppliedType(et, eparams) => + AppliedType(et, eparams.map(substituteTypeParams)) + case _ => t + +end FuzzyArgMatcher diff --git a/presentation-compiler/src/main/dotty/tools/pc/AutoImports.scala b/presentation-compiler/src/main/dotty/tools/pc/AutoImports.scala index 1b44dce8c642..8025b5aaf43a 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/AutoImports.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/AutoImports.scala @@ -20,9 +20,7 @@ import org.eclipse.lsp4j as l object AutoImports: object AutoImport: - def renameConfigMap(config: PresentationCompilerConfig)(using - Context - ): Map[Symbol, String] = + def renameConfigMap(config: PresentationCompilerConfig)(using Context): Map[Symbol, String] = config.symbolPrefixes().nn.asScala.flatMap { (from, to) => val pkg = SemanticdbSymbols.inverseSemanticdbSymbol(from) val rename = to.stripSuffix(".").stripSuffix("#") @@ -30,7 +28,6 @@ object AutoImports: .filter(_ != NoSymbol) .map((_, rename)) }.toMap - end AutoImport sealed trait SymbolIdent: def value: String @@ -40,7 +37,7 @@ object AutoImports: case class Select(qual: SymbolIdent, name: String) extends SymbolIdent: def value: String = s"${qual.value}.$name" - def direct(name: String): SymbolIdent = Direct(name) + def direct(name: String)(using Context): SymbolIdent = Direct(name) def fullIdent(symbol: Symbol)(using Context): SymbolIdent = val symbols = symbol.ownersIterator.toList @@ -70,21 +67,21 @@ object AutoImports: importSel: Option[ImportSel] ): - def name: String = ident.value + def name(using Context): String = ident.value object SymbolImport: def simple(sym: Symbol)(using Context): SymbolImport = SymbolImport(sym, SymbolIdent.direct(sym.nameBackticked), None) - /** - * Returns AutoImportsGenerator + /** Returns AutoImportsGenerator * - * @param pos A source position where the autoImport is invoked - * @param text Source text of the file - * @param tree A typed tree of the file - * @param indexedContext A context of the position where the autoImport is invoked - * @param config A presentation compiler config, this is used for renames + * @param pos A source position where the autoImport is invoked + * @param text Source text of the file + * @param tree A typed tree of the file + * @param indexedContext A context of the position where the autoImport is + * invoked + * @param config A presentation compiler config, this is used for renames */ def generator( pos: SourcePosition, @@ -130,14 +127,15 @@ object AutoImports: def nameOnly(edit: l.TextEdit): AutoImportEdits = AutoImportEdits(Some(edit), None) - /** - * AutoImportsGenerator generates TextEdits of auto-imports - * for the given symbols. + /** AutoImportsGenerator generates TextEdits of auto-imports for the given + * symbols. * - * @param pos A source position where the autoImport is invoked - * @param importPosition A position to insert new imports - * @param indexedContext A context of the position where the autoImport is invoked - * @param renames A function that returns the name of the given symbol which is renamed on import statement. + * @param pos A source position where the autoImport is invoked + * @param importPosition A position to insert new imports + * @param indexedContext A context of the position where the autoImport is + * invoked + * @param renames A function that returns the name of the given symbol which + * is renamed on import statement. */ class AutoImportsGenerator( val pos: SourcePosition, @@ -151,8 +149,7 @@ object AutoImports: def forSymbol(symbol: Symbol): Option[List[l.TextEdit]] = editsForSymbol(symbol).map(_.edits) - /** - * @param symbol A missing symbol to auto-import + /** @param symbol A missing symbol to auto-import */ def editsForSymbol(symbol: Symbol): Option[AutoImportEdits] = val symbolImport = inferSymbolImport(symbol) @@ -160,13 +157,11 @@ object AutoImports: case SymbolIdent.Direct(_) => None case other => Some(new l.TextEdit(pos.toLsp, other.value)) - val importEdit = symbolImport.importSel.flatMap(sel => renderImports(List(sel))) if nameEdit.isDefined || importEdit.isDefined then Some(AutoImportEdits(nameEdit, importEdit)) else None - end editsForSymbol def inferSymbolImport(symbol: Symbol): SymbolImport = indexedContext.lookupSym(symbol) match @@ -186,13 +181,16 @@ object AutoImports: ownerImport.ident, symbol.nameBackticked(false) ), - ownerImport.importSel, + ownerImport.importSel ) else - ( - SymbolIdent.direct(symbol.nameBackticked), - Some(ImportSel.Direct(symbol)), - ) + renames(symbol) match + case Some(rename) => (SymbolIdent.direct(rename), None) + case None => + ( + SymbolIdent.direct(symbol.nameBackticked), + Some(ImportSel.Direct(symbol)) + ) end val SymbolImport( @@ -222,10 +220,15 @@ object AutoImports: ), importSel ) + case None => + val reverse = symbol.ownersIterator.toList.reverse + val fullName = reverse.drop(1).foldLeft(SymbolIdent.direct(reverse.head.nameBackticked)) { + case (acc, sym) => SymbolIdent.Select(acc, sym.nameBackticked(false)) + } SymbolImport( symbol, - SymbolIdent.direct(symbol.fullNameBackticked), + SymbolIdent.Direct(symbol.fullNameBackticked), None ) end match @@ -252,7 +255,6 @@ object AutoImports: val topPadding = if importPosition.padTop then "\n" else "" - val formatted = imports .map { case ImportSel.Direct(sym) => importName(sym) @@ -267,15 +269,17 @@ object AutoImports: end renderImports private def importName(sym: Symbol): String = - if indexedContext.importContext.toplevelClashes(sym) then + if indexedContext.toplevelClashes(sym, inImportScope = true) then s"_root_.${sym.fullNameBackticked(false)}" else sym.ownersIterator.zipWithIndex.foldLeft((List.empty[String], false)) { case ((acc, isDone), (sym, idx)) => - if(isDone || sym.isEmptyPackage || sym.isRoot) (acc, true) - else indexedContext.rename(sym) match - case Some(renamed) => (renamed :: acc, true) - case None if !sym.isPackageObject => (sym.nameBackticked(false) :: acc, false) - case None => (acc, false) + if isDone || sym.isEmptyPackage || sym.isRoot then (acc, true) + else + indexedContext.rename(sym) match + // we can't import first part + case Some(renamed) if idx != 0 => (renamed :: acc, true) + case _ if !sym.isPackageObject => (sym.nameBackticked(false) :: acc, false) + case _ => (acc, false) }._1.mkString(".") end AutoImportsGenerator @@ -319,7 +323,6 @@ object AutoImports: }.headOption case _ => None - def skipUsingDirectivesOffset(firstObjectPos: Int = firstMemberDefinitionStart(tree).getOrElse(0)): Int = val firstObjectLine = pos.source.offsetToLine(firstObjectPos) @@ -388,8 +391,8 @@ object AutoImports: val scriptPos = if path.isAmmoniteGeneratedFile || - path.isScalaCLIGeneratedFile || - path.isWorksheet + path.isScalaCLIGeneratedFile || + path.isWorksheet then forScript(path) else None diff --git a/presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala index e35556ad11c9..ce868ca9463b 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala @@ -1,14 +1,17 @@ package dotty.tools.pc import java.nio.file.Paths +import java.util.Optional import scala.collection.mutable import scala.jdk.CollectionConverters.* -import scala.meta.internal.metals.ReportContext import scala.meta.internal.pc.AutoImportsResultImpl import scala.meta.pc.* +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.ast.tpd.* +import dotty.tools.dotc.core.Flags.Method +import dotty.tools.dotc.core.StdNames.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.interactive.Interactive import dotty.tools.dotc.interactive.InteractiveDriver @@ -41,12 +44,19 @@ final class AutoImportsProvider( val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) val path = Interactive.pathTo(newctx.compilationUnit.tpdTree, pos.span)(using newctx) - - val indexedContext = IndexedContext( - Interactive.contextOfPath(path)(using newctx) - ) + val indexedContext = IndexedContext(pos, path, newctx) import indexedContext.ctx + def correctInTreeContext(sym: Symbol) = path match + case (_: Ident) :: (sel: Select) :: _ => + sym.info.allMembers.exists(_.name == sel.name) + case (_: Ident) :: (_: Apply) :: _ if !sym.is(Method) => + def applyInObject = + sym.companionModule.info.allMembers.exists(_.name == nme.apply) + def applyInClass = sym.info.allMembers.exists(_.name == nme.apply) + applyInClass || applyInObject + case _ => true + val isSeen = mutable.Set.empty[String] val symbols = List.newBuilder[Symbol] def visit(sym: Symbol): Boolean = @@ -63,7 +73,7 @@ final class AutoImportsProvider( if isExtension then search.searchMethods(name, buildTargetIdentifier, visitor) else search.search(name, buildTargetIdentifier, visitor) - val results = symbols.result.filter(isExactMatch(_, name)) + val results = symbols.result().filter(isExactMatch(_, name)) if results.nonEmpty then val correctedPos = @@ -83,20 +93,34 @@ final class AutoImportsProvider( text, tree, unit.comments, - indexedContext.importContext, + indexedContext, config ) (sym: Symbol) => generator.forSymbol(sym) end match - end mkEdit - - for - sym <- results - edits <- mkEdit(sym) - yield AutoImportsResultImpl( - sym.owner.showFullName, - edits.asJava - ) + + val all = + for + sym <- results + edits <- mkEdit(sym) + yield ( + AutoImportsResultImpl( + sym.owner.showFullName, + edits.asJava, + Optional.of(SemanticdbSymbols.symbolName(sym)).nn + ), + sym + ) + + all match + case (onlyResult, _) :: Nil => List(onlyResult) + case Nil => Nil + case moreResults => + val moreExact = moreResults.filter { case (_, sym) => + correctInTreeContext(sym) + } + if moreExact.nonEmpty then moreExact.map(_._1) + else moreResults.map(_._1) else List.empty end if end autoImports diff --git a/presentation-compiler/src/main/dotty/tools/pc/CachingDriver.scala b/presentation-compiler/src/main/dotty/tools/pc/CachingDriver.scala index f5715c2780a9..5d375ca2f96c 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/CachingDriver.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/CachingDriver.scala @@ -1,35 +1,48 @@ package dotty.tools.pc +import java.io.File import java.net.URI +import java.nio.file.Path import java.util as ju +import scala.compiletime.uninitialized +import scala.jdk.CollectionConverters.* +import scala.meta.pc.SemanticdbFileManager +import scala.meta.pc.SourcePathMode + +import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.interactive.InteractiveDriver +import dotty.tools.dotc.interactive.LogicalPackage +import dotty.tools.dotc.interactive.LogicalPackagesProvider +import dotty.tools.dotc.interactive.ParsedLogicalPackage import dotty.tools.dotc.reporting.Diagnostic import dotty.tools.dotc.util.SourceFile -import scala.compiletime.uninitialized - -/** - * CachingDriver is a wrapper class that provides a compilation cache for InteractiveDriver. - * CachingDriver skips running compilation if - * - the target URI of `run` is the same as the previous target URI - * - the content didn't change since the last compilation. +/** CachingDriver is a wrapper class that provides a compilation cache for + * InteractiveDriver. CachingDriver skips running compilation if + * - the target URI of `run` is the same as the previous target URI + * - the content didn't change since the last compilation. * - * This compilation cache enables Metals to skip compilation and re-use - * the typed tree under the situation like developers - * sequentially hover on the symbols in the same file without any changes. + * This compilation cache enables Metals to skip compilation and re-use the + * typed tree under the situation like developers sequentially hover on the + * symbols in the same file without any changes. * - * Note: we decided to cache only if the target URI is the same as in the previous run - * because of `InteractiveDriver.currentCtx` that should return the context that - * refers to the last compiled source file. - * It would be ideal if we could update currentCtx even when we skip the compilation, - * but we struggled to do that. See the discussion https://github.com/scalameta/metals/pull/4225#discussion_r941138403 - * To avoid the complexity related to currentCtx, - * we decided to cache only when the target URI only if the same as the previous run. + * Note: we decided to cache only if the target URI is the same as in the + * previous run because of `InteractiveDriver.currentCtx` that should return + * the context that refers to the last compiled source file. It would be ideal + * if we could update currentCtx even when we skip the compilation, but we + * struggled to do that. See the discussion + * https://github.com/scalameta/metals/pull/4225#discussion_r941138403 To avoid + * the complexity related to currentCtx, we decided to cache only when the + * target URI only if the same as the previous run. */ -class CachingDriver(override val settings: List[String]) extends InteractiveDriver(settings): +class CachingDriver private ( + override val settings: List[String], + precomputedSourcePackages: Option[LogicalPackage] +) extends InteractiveDriver(settings, precomputedSourcePackages): - @volatile private var lastCompiledURI: URI = uninitialized + private var lastCompiledURI: URI = uninitialized + private var previousDiags = List.empty[Diagnostic] private def alreadyCompiled(uri: URI, content: Array[Char]): Boolean = compilationUnits.get(uri) match @@ -40,17 +53,25 @@ class CachingDriver(override val settings: List[String]) extends InteractiveDriv case _ => false override def run(uri: URI, source: SourceFile): List[Diagnostic] = - val diags = - if alreadyCompiled(uri, source.content) then Nil - else super.run(uri, source) + if !alreadyCompiled(uri, source.content) then previousDiags = super.run(uri, source) lastCompiledURI = uri - diags - - override def run(uri: URI, sourceCode: String): List[Diagnostic] = - val diags = - if alreadyCompiled(uri, sourceCode.toCharArray().nn) then Nil - else super.run(uri, sourceCode) - lastCompiledURI = uri - diags + previousDiags end CachingDriver + +object CachingDriver: + def apply( + settings: List[String], + sourcePath: ju.function.Supplier[ju.List[Path]], + semanticdbFileManager: SemanticdbFileManager, + sourcePathMode: SourcePathMode + ): CachingDriver = + val precomputedSourcePackages = sourcePathMode match + case SourcePathMode.DISABLED => None + case SourcePathMode.PRUNED | SourcePathMode.FULL => + val sourcePathFiles = sourcePath.get().nn.asScala.toSeq + val logicalSourcePath = sourcePathFiles.mkString(File.pathSeparator.nn) + if sourcePathFiles.nonEmpty then Some(new LogicalPackagesProvider(logicalSourcePath).root) else None + case SourcePathMode.MBT => + Some(ParsedLogicalPackage.fromMbtIndex(semanticdbFileManager.listAllPackages().nn)) + new CachingDriver(settings, precomputedSourcePackages) diff --git a/presentation-compiler/src/main/dotty/tools/pc/CompilerSearchVisitor.scala b/presentation-compiler/src/main/dotty/tools/pc/CompilerSearchVisitor.scala index 9fb84ee1f513..d05c96188be2 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/CompilerSearchVisitor.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/CompilerSearchVisitor.scala @@ -4,8 +4,8 @@ import java.util.logging.Level import java.util.logging.Logger import scala.meta.internal.metals.Report -import scala.meta.internal.metals.ReportContext import scala.meta.pc.* +import scala.meta.pc.reports.ReportContext import scala.util.control.NonFatal import dotty.tools.dotc.core.Contexts.* @@ -27,22 +27,23 @@ class CompilerSearchVisitor( owner.is(Flags.Implicit) && owner.isStatic && owner.isPublic - private def isAccessible(sym: Symbol): Boolean = try - (sym != NoSymbol && sym.isAccessibleFrom(ctx.owner.info) && sym.isStatic) || isAccessibleImplicitClass(sym) - catch - case err: AssertionError => - logger.log(Level.WARNING, err.getMessage()) - false - case NonFatal(e) => - reports.incognito.create( - Report( - "is_public", - s"""Symbol: $sym""".stripMargin, - e + private def isAccessible(sym: Symbol): Boolean = + try + (sym != NoSymbol && sym.isAccessibleFrom(ctx.owner.info) && sym.isStatic) || isAccessibleImplicitClass(sym) + catch + case err: AssertionError => + logger.log(Level.WARNING, err.getMessage()) + false + case NonFatal(e) => + reports.incognito.nn.create(() => + Report( + "is_public", + s"""Symbol: $sym""".stripMargin, + e + ) ) - ) - logger.log(Level.SEVERE, e.getMessage(), e) - false + logger.log(Level.SEVERE, e.getMessage(), e) + false private def toSymbols( pkg: String, @@ -92,11 +93,12 @@ class CompilerSearchVisitor( range: org.eclipse.lsp4j.Range ): Int = val gsym = SemanticdbSymbols.inverseSemanticdbSymbol(symbol).headOption - val matching = for - sym0 <- gsym.toList - sym <- if sym0.companion.is(Flags.Synthetic) then List(sym0, sym0.companion) else List(sym0) - if isAccessible(sym) - yield visitSymbol(sym) + val matching = + for + sym0 <- gsym.toList + sym <- if sym0.companion.is(Flags.Synthetic) then List(sym0, sym0.companion) else List(sym0) + if isAccessible(sym) + yield visitSymbol(sym) matching.size def shouldVisitPackage(pkg: String): Boolean = diff --git a/presentation-compiler/src/main/dotty/tools/pc/CompletionItemResolver.scala b/presentation-compiler/src/main/dotty/tools/pc/CompletionItemResolver.scala index 291ffe1fec30..595c5f1b0c65 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/CompletionItemResolver.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/CompletionItemResolver.scala @@ -41,14 +41,10 @@ object CompletionItemResolver extends ItemResolver: case _ => item end match - case _ => item end match - end resolve - private def fullDocstring(gsym: Symbol, search: SymbolSearch)(using - Context - ): String = + private def fullDocstring(gsym: Symbol, search: SymbolSearch)(using Context): String = def docs(gsym: Symbol): String = search.symbolDocumentation(gsym).fold("")(_.docstring().nn) val gsymDoc = docs(gsym) @@ -62,7 +58,7 @@ object CompletionItemResolver extends ItemResolver: if companion == NoSymbol || gsym.is(JavaDefined) then if gsymDoc.isEmpty() then if gsym.isAliasType then - fullDocstring(gsym.info.deepDealias.typeSymbol, search) + fullDocstring(gsym.info.deepDealiasAndSimplify.typeSymbol, search) else if gsym.is(Method) then gsym.info.finalResultType match case tr @ TermRef(_, sym) => @@ -75,7 +71,7 @@ object CompletionItemResolver extends ItemResolver: else gsymDoc else val companionDoc = docs(companion) - if companionDoc.isEmpty() then gsymDoc + if companionDoc.isEmpty() || companionDoc == gsymDoc then gsymDoc else if gsymDoc.isEmpty() then companionDoc else List( @@ -86,7 +82,5 @@ object CompletionItemResolver extends ItemResolver: |${gsymDoc} |""".stripMargin ).sorted.mkString("\n") - end if - end fullDocstring end CompletionItemResolver diff --git a/presentation-compiler/src/main/dotty/tools/pc/ConvertToNamedArgumentsProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/ConvertToNamedArgumentsProvider.scala index d4f945760cc0..847605baf81c 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/ConvertToNamedArgumentsProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/ConvertToNamedArgumentsProvider.scala @@ -53,9 +53,7 @@ final class ConvertToNamedArgumentsProvider( Some(fun, argss ++ args) case _ => None - def edits(tree: Option[tpd.Tree])(using - Context - ): Either[String, List[l.TextEdit]] = + def edits(tree: Option[tpd.Tree])(using Context): Either[String, List[l.TextEdit]] = def makeTextEdits(fun: tpd.Tree, args: List[tpd.Tree]) = if fun.symbol.is(Flags.JavaDefined) then Left(CodeActionErrorMessages.ConvertToNamedArguments.IsJavaObject) @@ -70,7 +68,6 @@ final class ConvertToNamedArgumentsProvider( new l.TextEdit(position, s"$param = ") } ) - tree match case Some(t) => t match @@ -80,8 +77,6 @@ final class ConvertToNamedArgumentsProvider( makeTextEdits(fun, args) case _ => Right(Nil) case _ => Right(Nil) - end match - end edits edits(tree)(using newctx) end convertToNamedArguments end ConvertToNamedArgumentsProvider diff --git a/presentation-compiler/src/main/dotty/tools/pc/DiagnosticProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/DiagnosticProvider.scala new file mode 100644 index 000000000000..8202e9ff2506 --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/pc/DiagnosticProvider.scala @@ -0,0 +1,76 @@ +package dotty.tools.pc + +import scala.jdk.CollectionConverters.* +import scala.meta.pc.VirtualFileParams + +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.interactive.InteractiveDriver +import dotty.tools.dotc.interfaces.Diagnostic as DiagnosticInterfaces +import dotty.tools.dotc.reporting.CodeAction +import dotty.tools.dotc.reporting.Diagnostic +import dotty.tools.dotc.reporting.ErrorMessageID +import dotty.tools.dotc.rewrites.Rewrites.ActionPatch +import dotty.tools.pc.utils.InteractiveEnrichments.toLsp + +import org.eclipse.lsp4j + +class DiagnosticProvider(driver: InteractiveDriver, params: VirtualFileParams): + def diagnostics(): List[lsp4j.Diagnostic] = + if params.shouldReturnDiagnostics then + val diags = driver.run(params.uri().nn, params.text().nn) + given Context = driver.currentCtx + diags.flatMap(toLsp) + else Nil + + private def toLsp(diag: Diagnostic)(using Context): Option[lsp4j.Diagnostic] = + Option.when(diag.pos.exists): + val explanation = + if Diagnostic.shouldExplain(diag) + then + "\n\n# Explanation (enabled by `-explain`)\n\n" + diag.msg.explanation + else "" + val lspDiag = lsp4j.Diagnostic( + diag.pos.toLsp, + diag.msg.message + explanation, + toDiagnosticSeverity(diag.level), + "presentation compiler" + ) + lspDiag.setCode(diag.msg.errorId.errorNumber) + + val actions = diag.msg.actions.map(toLspScalaAction).asJava + + lspDiag.setData(actions) + if diag.msg.errorId == ErrorMessageID.UnusedSymbolID then + lspDiag.setTags(List(lsp4j.DiagnosticTag.Unnecessary).asJava) + + lspDiag + + private def toLspScalaAction(action: CodeAction): lsp4j.CodeAction = + val lspAction = lsp4j.CodeAction(action.title) + lspAction.setKind(lsp4j.CodeActionKind.QuickFix) + lspAction.setIsPreferred(true) + val edits = action.patches + .groupBy(_.srcPos.source.path) + .map((path, actions) => path -> (actions.map(toLspTextEdit).asJava)) + .asJava + + val workspaceEdit = lsp4j.WorkspaceEdit(edits) + lspAction.setEdit(workspaceEdit) + lspAction + + private def toLspTextEdit(actionPatch: ActionPatch): lsp4j.TextEdit = + val startPos = lsp4j.Position( + actionPatch.srcPos.startLine, + actionPatch.srcPos.startColumn + ) + val endPos = + lsp4j.Position(actionPatch.srcPos.endLine, actionPatch.srcPos.endColumn) + val range = lsp4j.Range(startPos, endPos) + lsp4j.TextEdit(range, actionPatch.replacement) + + private def toDiagnosticSeverity(severity: Int): lsp4j.DiagnosticSeverity = + severity match + case DiagnosticInterfaces.ERROR => lsp4j.DiagnosticSeverity.Error + case DiagnosticInterfaces.WARNING => lsp4j.DiagnosticSeverity.Warning + case DiagnosticInterfaces.INFO => lsp4j.DiagnosticSeverity.Information + case _ => lsp4j.DiagnosticSeverity.Information diff --git a/presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala index c72a0602f1ce..77e489bf9524 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/ExtractMethodProvider.scala @@ -2,11 +2,11 @@ package dotty.tools.pc import java.nio.file.Paths -import scala.meta.internal.metals.ReportContext import scala.meta.internal.pc.ExtractMethodUtils import scala.meta.pc.OffsetParams import scala.meta.pc.RangeParams import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import scala.meta as m import dotty.tools.dotc.ast.Trees.* @@ -48,12 +48,11 @@ final class ExtractMethodProvider( val pos = driver.sourcePosition(range).startPos val path = Interactive.pathTo(driver.openedTrees(uri), pos)(using driver.currentCtx) - given locatedCtx: Context = - val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) - Interactive.contextOfPath(path)(using newctx) - val indexedCtx = IndexedContext(locatedCtx) + val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) + val indexedContext = IndexedContext(pos, path, newctx) + import indexedContext.ctx val printer = - ShortenedTypePrinter(search, IncludeDefaultParam.Never)(using indexedCtx) + ShortenedTypePrinter(search, IncludeDefaultParam.Never)(using indexedContext) def prettyPrint(tpe: Type) = def prettyPrintReturnType(tpe: Type): String = tpe match @@ -64,13 +63,11 @@ final class ExtractMethodProvider( params match case p :: Nil => prettyPrintReturnType(p) case _ => s"(${params.map(prettyPrintReturnType).mkString(", ")})" - if tpe.paramInfoss.isEmpty then prettyPrintReturnType(tpe) else val params = tpe.paramInfoss.map(printParams).mkString(" => ") s"$params => ${prettyPrintReturnType(tpe)}" - end prettyPrint def extractFromBlock(t: tpd.Tree): List[tpd.Tree] = t match @@ -114,7 +111,7 @@ final class ExtractMethodProvider( ( methodParams.sortBy(_.decodedName), - typeParams.toList.sortBy(_.decodedName), + typeParams.toList.sortBy(_.decodedName) ) end localRefs val optEnclosing = @@ -137,12 +134,12 @@ final class ExtractMethodProvider( val extractedPos = head.sourcePos.withEnd(expr.sourcePos.end) val exprType = prettyPrint(expr.typeOpt.widen) val name = - genName(indexedCtx.scopeSymbols.map(_.decodedName).toSet, "newMethod") + genName(indexedContext.scopeSymbols.map(_.decodedName).toSet, "newMethod") val (allMethodParams, typeParams) = localRefs(extracted, stat.sourcePos, extractedPos) val (methodParams, implicitParams) = allMethodParams.partition(!_.isOneOf(Flags.GivenOrImplicit)) - def toParamText(params: List[Symbol]) = - params.map(sym => s"${sym.decodedName}: ${prettyPrint(sym.info)}") + def toParamText(params: List[Symbol]) = + params.map(sym => s"${sym.decodedName}: ${prettyPrint(sym.info)}") .mkString(", ") val methodParamsText = toParamText(methodParams) val implicitParamsText = if implicitParams.nonEmpty then s"(given ${toParamText(implicitParams)})" else "" diff --git a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala index a667b038c894..fc010446785b 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/HoverProvider.scala @@ -3,18 +3,21 @@ package dotty.tools.pc import java.util as ju import scala.meta.internal.metals.Report -import scala.meta.internal.metals.ReportContext import scala.meta.internal.pc.ScalaHover import scala.meta.pc.ContentType import scala.meta.pc.HoverSignature import scala.meta.pc.OffsetParams import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.ast.tpd.* +import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.core.Constants.* import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Decorators.* import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Names.* +import dotty.tools.dotc.core.StdNames import dotty.tools.dotc.core.StdNames.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* @@ -47,7 +50,8 @@ object HoverProvider: val path = unit .map(unit => Interactive.pathTo(unit.tpdTree, pos.span)) .getOrElse(Interactive.pathTo(driver.openedTrees(uri), pos)) - val indexedContext = IndexedContext(ctx) + val indexedContext = IndexedContext(pos, path, ctx) + import indexedContext.ctx def typeFromPath(path: List[Tree]) = if path.isEmpty then NoType else path.head.typeOpt @@ -57,8 +61,79 @@ object HoverProvider: // For expression we need to find all enclosing applies to get the exact generic type val enclosing = path.expandRangeToEnclosingApply(pos) + lazy val printer = ShortenedTypePrinter(search, IncludeDefaultParam.Include)( + using indexedContext + ) + + def hoverSignature( + symbol: Symbol, + symbolTpes: Seq[(Symbol, Type, Option[String])], + tpe: Type + ): ju.Optional[HoverSignature] = { + val exprTpw = tpe.widenTermRefExpr.deepDealiasAndSimplify + val hoverString = + tpw match + // https://github.com/scala/scala3/issues/8891 + case tpw: ImportType => + printer.hoverSymbol(symbol, symbol.paramRef) + case _ => + val (innerTpe, sym) = + if symbol.isType then (symbol.typeRef, symbol) + else enclosing.head.seenFrom(symbol) + + val finalTpe = + if innerTpe != NoType then innerTpe + else tpw + + printer.hoverSymbol(sym, finalTpe.deepDealiasAndSimplify) + end match + end hoverString + + val docString = symbolTpes + .flatMap(symTpe => search.symbolDocumentation(symTpe._1, contentType)) + .map(_.docstring()) + .mkString("\n") + + val expresionTypeOpt = + if symbol.name == StdNames.nme.??? then + InferExpectedType(search, driver, params).infer() + else printer.expressionType(exprTpw) + expresionTypeOpt match + case Some(expressionType) => + val forceExpressionType = + !pos.span.isZeroExtent || ( + !hoverString.endsWith(expressionType) && + !symbol.isType && + !symbol.is(Module) && + !symbol.flags.isAllOf(EnumCase) + ) + ju.Optional.of( + new ScalaHover( + expressionType = Some(expressionType), + symbolSignature = Some(hoverString), + docstring = Some(docString), + forceExpressionType = forceExpressionType, + contextInfo = printer.getUsedRenamesInfo, + contentType = contentType + ) + ).nn + case _ => + ju.Optional.empty().nn + } + if tp.isError || tpw == NoType || tpw.isError || path.isEmpty then + val untpdPath = Interactive.resolveTypedOrUntypedPath(enclosing, pos)(using ctx) + val derivesClauseSymbolOpt = untpdPath match + /* In case of `class X derives TC@@` we shouldn't add `[]` + */ + case Ident(_) :: (templ: untpd.DerivingTemplate) :: _ => + templ.derived.find(_.sourcePos.contains(pos)).collect { + case ident if ident.tpe != null => ident.symbol -> ident.tpe.nn + } + case _ => + None + def report = val posId = if path.isEmpty || !path.head.sourcePos.exists @@ -79,22 +154,25 @@ object HoverProvider: |- ${path.map(_.toString()).mkString("\n- ")} |trees: |- ${unit - .map(u => List(u.tpdTree)) - .getOrElse(driver.openedTrees(uri).map(_.tree)) - .map(_.toString()).mkString("\n- ")} + .map(u => List(u.tpdTree)) + .getOrElse(driver.openedTrees(uri).map(_.tree)) + .map(_.toString()).mkString("\n- ")} |""".stripMargin, s"$uri::$posId" ) - end report - reportContext.unsanitized.create(report, ifVerbose = true) - ju.Optional.empty().nn + + derivesClauseSymbolOpt match + case Some((symbol, tpe)) => + hoverSignature(symbol, List((symbol, tpe, None)), tpe) + case None => + reportContext.unsanitized.nn.create(() => report, /*ifVerbose =*/ true) + ju.Optional.empty().nn else val skipCheckOnName = !pos.isPoint // don't check isHoveringOnName for RangeHover - val printerCtx = Interactive.contextOfPath(path) val printer = ShortenedTypePrinter(search, IncludeDefaultParam.Include)( - using IndexedContext(printerCtx) + using indexedContext ) MetalsInteractive.enclosingSymbolsWithExpressionType( enclosing, @@ -104,56 +182,26 @@ object HoverProvider: ) match case Nil => fallbackToDynamics(path, printer, contentType) - case (symbol, tpe) :: _ + case (symbol, tpe, _) :: _ if symbol.name == nme.selectDynamic || symbol.name == nme.applyDynamic => fallbackToDynamics(path, printer, contentType) - case symbolTpes @ ((symbol, tpe) :: _) => - val exprTpw = tpe.widenTermRefExpr.deepDealias - val hoverString = - tpw match - // https://github.com/lampepfl/dotty/issues/8891 - case tpw: ImportType => - printer.hoverSymbol(symbol, symbol.paramRef) - case _ => - val (tpe, sym) = - if symbol.isType then (symbol.typeRef, symbol) - else enclosing.head.seenFrom(symbol) - - val finalTpe = - if tpe != NoType then tpe - else tpw - - printer.hoverSymbol(sym, finalTpe.deepDealias) - end match - end hoverString - - val docString = symbolTpes - .flatMap(symTpe => search.symbolDocumentation(symTpe._1, contentType)) - .map(_.docstring()) - .mkString("\n") + case symbolTpes @ ((symbol, tpe, None) :: _) => + hoverSignature(symbol, symbolTpes, tpe) + case (_, tpe, Some(namedTupleArg)) :: _ => + val exprTpw = tpe.widenTermRefExpr.deepDealiasAndSimplify printer.expressionType(exprTpw) match - case Some(expressionType) => - val forceExpressionType = - !pos.span.isZeroExtent || ( - !hoverString.endsWith(expressionType) && - !symbol.isType && - !symbol.is(Module) && - !symbol.flags.isAllOf(EnumCase) - ) + case Some(tpe) => ju.Optional.of( new ScalaHover( - expressionType = Some(expressionType), - symbolSignature = Some(hoverString), - docstring = Some(docString), - forceExpressionType = forceExpressionType, + expressionType = Some(tpe), + symbolSignature = Some(s"$namedTupleArg: $tpe"), + docstring = None, + forceExpressionType = false, contextInfo = printer.getUsedRenamesInfo, contentType = contentType ) ).nn - case _ => - ju.Optional.empty().nn - end match - end match + case _ => ju.Optional.empty().nn end if end hover @@ -165,23 +213,30 @@ object HoverProvider: printer: ShortenedTypePrinter, contentType: ContentType )(using Context): ju.Optional[HoverSignature] = path match - case SelectDynamicExtractor(sel, n, name) => + case SelectDynamicExtractor(sel, n, name, rest) => def findRefinement(tp: Type): Option[HoverSignature] = tp match case RefinedType(_, refName, tpe) if name == refName.toString() => + val resultType = + rest match + case Select(_, asInstanceOf) :: TypeApply(_, List(tpe)) :: _ if asInstanceOf == nme.asInstanceOfPM => + tpe.tpe.widenTermRefExpr.deepDealiasAndSimplify + case _ if n == nme.selectDynamic => tpe.resultType + case _ => tpe + val tpeString = - if n == nme.selectDynamic then s": ${printer.tpe(tpe.resultType)}" - else printer.tpe(tpe) + if n == nme.selectDynamic then s": ${printer.tpe(resultType)}" + else printer.tpe(resultType) val valOrDef = if n == nme.selectDynamic && !tpe.isInstanceOf[ExprType] - then "val" - else "def" + then "val " + else "def " Some( new ScalaHover( expressionType = Some(tpeString), - symbolSignature = Some(s"$valOrDef $name$tpeString"), + symbolSignature = Some(s"$valOrDef$name$tpeString"), contextInfo = printer.getUsedRenamesInfo, contentType = contentType ) @@ -190,12 +245,21 @@ object HoverProvider: findRefinement(parent) case _ => None - val refTpe = sel.typeOpt.widen.deepDealias match - case r: RefinedType => Some(r) - case t: (TermRef | TypeProxy) => Some(t.termSymbol.info.deepDealias) - case _ => None + def extractRefinements(t: Type): List[Type] = t match + case r: RefinedType => List(r) + case t: (TypeRef | AppliedType) => + // deepDealiasAndSimplify can succeed with no progress, so we have to avoid infinite loops + val t1 = t.deepDealiasAndSimplify + if t1 == t then Nil + else extractRefinements(t1) + case t: TermRef => extractRefinements(t.widen) + case t: TypeProxy => List(t.termSymbol.info.deepDealiasAndSimplify) + case AndType(l, r) => List(extractRefinements(l), extractRefinements(r)).flatten + case _ => Nil + + val refTpe: List[Type] = extractRefinements(sel.typeOpt) - refTpe.flatMap(findRefinement).asJava + refTpe.flatMap(findRefinement).headOption.asJava case _ => ju.Optional.empty().nn @@ -208,17 +272,15 @@ object SelectDynamicExtractor: case Select(_, _) :: Apply( Select(Apply(reflSel, List(sel)), n), List(Literal(Constant(name: String))) - ) :: _ + ) :: rest if (n == nme.selectDynamic || n == nme.applyDynamic) && nme.reflectiveSelectable == reflSel.symbol.name => - Some(sel, n, name) + Some(sel, n, name, rest) // tests `selectable`, `selectable2` and `selectable-full` in HoverScala3TypeSuite case Select(_, _) :: Apply( Select(sel, n), List(Literal(Constant(name: String))) - ) :: _ if n == nme.selectDynamic || n == nme.applyDynamic => - Some(sel, n, name) + ) :: rest if n == nme.selectDynamic || n == nme.applyDynamic => + Some(sel, n, name, rest) case _ => None end match - end unapply -end SelectDynamicExtractor diff --git a/presentation-compiler/src/main/dotty/tools/pc/IndexedContext.scala b/presentation-compiler/src/main/dotty/tools/pc/IndexedContext.scala index 7c2c34cf5ebb..48b52ac8e43c 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/IndexedContext.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/IndexedContext.scala @@ -1,67 +1,54 @@ package dotty.tools.pc import scala.annotation.tailrec +import scala.meta.pc.OffsetParams import scala.util.control.NonFatal +import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Denotations.PreDenotation +import dotty.tools.dotc.core.Denotations.SingleDenotation import dotty.tools.dotc.core.Flags.* -import dotty.tools.dotc.core.NameOps.moduleClassName +import dotty.tools.dotc.core.NameOps.* import dotty.tools.dotc.core.Names.* +import dotty.tools.dotc.core.Phases import dotty.tools.dotc.core.Scopes.EmptyScope import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* +import dotty.tools.dotc.interactive.Completion +import dotty.tools.dotc.interactive.Completion.CompletionResult import dotty.tools.dotc.interactive.Interactive +import dotty.tools.dotc.interactive.InteractiveDriver import dotty.tools.dotc.typer.ImportInfo +import dotty.tools.dotc.util.SourcePosition import dotty.tools.pc.IndexedContext.Result import dotty.tools.pc.utils.InteractiveEnrichments.* sealed trait IndexedContext: given ctx: Context + def scopeContext: CompletionResult def scopeSymbols: List[Symbol] - def names: IndexedContext.Names def rename(sym: Symbol): Option[String] - def outer: IndexedContext - - def findSymbol(name: String): Option[List[Symbol]] - - final def findSymbol(name: Name): Option[List[Symbol]] = - findSymbol(name.decoded) - - final def lookupSym(sym: Symbol): Result = - findSymbol(sym.decodedName) match - case Some(symbols) if symbols.exists(_ == sym) => - Result.InScope - case Some(symbols) - if symbols.exists(s => isNotConflictingWithDefault(s, sym) || isTypeAliasOf(s, sym) || isTermAliasOf(s, sym)) => - Result.InScope - // when all the conflicting symbols came from an old version of the file + def findSymbol(name: Name, fromPrefix: Option[Type] = None): Option[List[Symbol]] + def findSymbolInLocalScope(name: String): Option[List[Symbol]] + + final def lookupSym(sym: Symbol, fromPrefix: Option[Type] = None): Result = + def all(symbol: Symbol): Set[Symbol] = + Set(symbol, symbol.companionModule, symbol.companionClass, symbol.companion).filter(_ != NoSymbol) + val isRelated = all(sym) ++ all(sym.dealiasType) + findSymbol(sym.name, fromPrefix) match + case Some(symbols) if symbols.exists(isRelated) => Result.InScope + case Some(symbols) if symbols.exists(isTermAliasOf(_, sym)) => Result.InScope + case Some(symbols) if symbols.map(_.dealiasType).exists(isRelated) => Result.InScope case Some(symbols) if symbols.nonEmpty && symbols.forall(_.isStale) => Result.Missing case Some(symbols) if symbols.exists(rename(_).isEmpty) => Result.Conflict + case Some(symbols) => Result.InScope case _ => Result.Missing - end lookupSym - - /** - * Scala by default imports following packages: - * https://scala-lang.org/files/archive/spec/3.4/02-identifiers-names-and-scopes.html - * import java.lang.* - * { - * import scala.* - * { - * import Predef.* - * { /* source */ } - * } - * } - * - * This check is necessary for proper scope resolution, because when we compare symbols from - * index including the underlying type like scala.collection.immutable.List it actually - * is in current scope in form of type forwarder imported from Predef. - */ - private def isNotConflictingWithDefault(sym: Symbol, queriedSym: Symbol): Boolean = - sym.info.widenDealias =:= queriedSym.info.widenDealias && (Interactive.isImportedByDefault(sym)) final def hasRename(sym: Symbol, as: String): Boolean = rename(sym) match - case Some(v) => v == as + case Some(v) => + v == as case None => false // detects import scope aliases like @@ -74,73 +61,98 @@ sealed trait IndexedContext: case _ => false ) - private def isTypeAliasOf(alias: Symbol, queriedSym: Symbol): Boolean = - alias.isAliasType && alias.info.deepDealias.typeSymbol == queriedSym - - final def isEmpty: Boolean = this match - case IndexedContext.Empty => true - case _ => false - - final def importContext: IndexedContext = - this match - case IndexedContext.Empty => this - case _ if ctx.owner.is(Package) => this - case _ => outer.importContext - @tailrec - final def toplevelClashes(sym: Symbol): Boolean = + final def toplevelClashes(sym: Symbol, inImportScope: Boolean): Boolean = if sym == NoSymbol || sym.owner == NoSymbol || sym.owner.isRoot then - lookupSym(sym) match - case IndexedContext.Result.Conflict => true + val possibleConflictingSymbols = findSymbolInLocalScope(sym.name.show) + // if it's import scope we only care about toplevel conflicts, not any clashes inside objects etc. + val symbolClashes = if inImportScope then + // It's toplevel if it's parent is a package + possibleConflictingSymbols.filter(_.exists(_.owner.is(Package))) + else + possibleConflictingSymbols + symbolClashes match + case Some(symbols) if !symbols.contains(sym) => true case _ => false - else toplevelClashes(sym.owner) + else toplevelClashes(sym.owner, inImportScope) end IndexedContext object IndexedContext: - def apply(ctx: Context): IndexedContext = - ctx match + def apply(pos: SourcePosition, tpdPath: List[tpd.Tree], driverCtx: Context): IndexedContext = + driverCtx match case NoContext => Empty - case _ => LazyWrapper(using ctx) + case _ => + val typerCtx: Context = Interactive + .contextOfPath(tpdPath)(using driverCtx).withPhase(Phases.typerPhase(using driverCtx)) + LazyWrapper(pos, tpdPath)(using typerCtx) case object Empty extends IndexedContext: given ctx: Context = NoContext - def findSymbol(name: String): Option[List[Symbol]] = None + def scopeContext: CompletionResult = CompletionResult(Map.empty, Map.empty) + def findSymbol(name: Name, fromPrefix: Option[Type]): Option[List[Symbol]] = None + def findSymbolInLocalScope(name: String): Option[List[Symbol]] = None def scopeSymbols: List[Symbol] = List.empty - val names: Names = Names(Map.empty, Map.empty) def rename(sym: Symbol): Option[String] = None - def outer: IndexedContext = this - - class LazyWrapper(using val ctx: Context) extends IndexedContext: - val outer: IndexedContext = IndexedContext(ctx.outer) - val names: Names = extractNames(ctx) - def findSymbol(name: String): Option[List[Symbol]] = - names.symbols - .get(name) - .map(_.toList) - .orElse(outer.findSymbol(name)) + class LazyWrapper(pos: SourcePosition, tpdPath: List[tpd.Tree])(using val ctx: Context) extends IndexedContext: + + val scopeContext: CompletionResult = Completion.scopeContext(pos, tpdPath, ctx) + val names: Map[String, Seq[SingleDenotation]] = scopeContext.names.toList.groupBy(_._1.show).map { + case (name, denotations) => + val denots = denotations.flatMap(_._2.denots) + val nonRoot = denots.filter(!_.symbol.owner.isRoot) + val (importedByDefault, conflictingValue) = + denots.partition(denot => Interactive.isImportedByDefault(denot.symbol)) + if importedByDefault.nonEmpty && conflictingValue.nonEmpty then + name.trim.nn -> conflictingValue + else + name.trim.nn -> nonRoot + } + val renames = scopeContext.renames + + def defaultScopes(name: Name): Option[List[Symbol]] = + List(defn.ScalaPredefModuleClass, defn.ScalaPackageClass, defn.JavaLangPackageClass) + .map(_.membersNamed(name)) + .collect { case denot if denot.exists => denot.first.symbol } + .toList match + case Nil => None + case list => Some(list) + + override def findSymbolInLocalScope(name: String): Option[List[Symbol]] = + names.get(name).map(_.map(_.symbol).toList).filter(_.nonEmpty) + def findSymbol(name: Name, fromPrefix: Option[Type]): Option[List[Symbol]] = + names + .get(name.show) + .map { denots => + def skipThisType(tp: Type): Type = tp match + case ThisType(prefix) => skipThisType(prefix) + case _ => tp + + val filteredDenots = fromPrefix match + case Some(prefix) => + val target = skipThisType(prefix) + denots.filter { denot => + denot.prefix == NoPrefix || + (denot.prefix match + case tref: TermRef => + tref.termSymbol.info <:< target + case otherPrefix => + otherPrefix <:< target) + } + case None => denots + + filteredDenots.map(_.symbol).toList + } + .orElse(defaultScopes(name)).filter(_.nonEmpty) def scopeSymbols: List[Symbol] = - val acc = Set.newBuilder[Symbol] - (this :: outers).foreach { ref => - acc ++= ref.names.symbols.values.flatten - } - acc.result.toList + names.values.flatten.map(_.symbol).toList def rename(sym: Symbol): Option[String] = - names.renames - .get(sym) - .orElse(outer.rename(sym)) - - private def outers: List[IndexedContext] = - val builder = List.newBuilder[IndexedContext] - var curr = outer - while !curr.isEmpty do - builder += curr - curr = curr.outer - builder.result + renames.get(sym).orElse(renames.get(sym.companion)).map(_.decoded) + end LazyWrapper enum Result: @@ -149,97 +161,4 @@ object IndexedContext: case InScope | Conflict => true case Missing => false - case class Names( - symbols: Map[String, List[Symbol]], - renames: Map[Symbol, String] - ) - - private def extractNames(ctx: Context): Names = - def isAccessibleFromSafe(sym: Symbol, site: Type): Boolean = - try sym.isAccessibleFrom(site, superAccess = false) - catch - case NonFatal(e) => - false - - def accessibleSymbols(site: Type, tpe: Type)(using - Context - ): List[Symbol] = - tpe.decls.toList.filter(sym => isAccessibleFromSafe(sym, site)) - - def accesibleMembers(site: Type)(using Context): List[Symbol] = - site.allMembers - .filter(denot => - try isAccessibleFromSafe(denot.symbol, site) - catch - case NonFatal(e) => - false - ) - .map(_.symbol) - .toList - - def allAccessibleSymbols( - tpe: Type, - filter: Symbol => Boolean = _ => true - )(using Context): List[Symbol] = - val initial = accessibleSymbols(tpe, tpe).filter(filter) - val fromPackageObjects = - initial - .filter(_.isPackageObject) - .flatMap(sym => accessibleSymbols(tpe, sym.thisType)) - initial ++ fromPackageObjects - - def fromImport(site: Type, name: Name)(using Context): List[Symbol] = - List( - site.member(name.toTypeName), - site.member(name.toTermName), - site.member(name.moduleClassName), - ) - .flatMap(_.alternatives) - .map(_.symbol) - - def fromImportInfo( - imp: ImportInfo - )(using Context): List[(Symbol, Option[TermName])] = - val excludedNames = imp.excluded.map(_.decoded) - - if imp.isWildcardImport then - allAccessibleSymbols( - imp.site, - sym => !excludedNames.contains(sym.name.decoded) - ).map((_, None)) - else - imp.forwardMapping.toList.flatMap { (name, rename) => - val isRename = name != rename - if !isRename && !excludedNames.contains(name.decoded) then - fromImport(imp.site, name).map((_, None)) - else if isRename then - fromImport(imp.site, name).map((_, Some(rename))) - else Nil - } - end if - end fromImportInfo - - given Context = ctx - val (symbols, renames) = - if ctx.isImportContext then - val (syms, renames) = - fromImportInfo(ctx.importInfo.nn) - .map((sym, rename) => (sym, rename.map(r => sym -> r.decoded))) - .unzip - (syms, renames.flatten.toMap) - else if ctx.owner.isClass then - val site = ctx.owner.thisType - (accesibleMembers(site), Map.empty) - else if ctx.scope != EmptyScope then (ctx.scope.toList, Map.empty) - else (List.empty, Map.empty) - - val initial = Map.empty[String, List[Symbol]] - val values = - symbols.foldLeft(initial) { (acc, sym) => - val name = sym.decodedName - val syms = acc.getOrElse(name, List.empty) - acc.updated(name, sym :: syms) - } - Names(values, renames) - end extractNames end IndexedContext diff --git a/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala b/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala index d121c8059286..e547c4a6cd7c 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala @@ -1,15 +1,15 @@ package dotty.tools.pc -import dotty.tools.dotc.ast.tpd +import scala.meta.pc.OffsetParams +import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext + import dotty.tools.dotc.ast.tpd.* -import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.StdNames -import dotty.tools.dotc.core.Symbols import dotty.tools.dotc.core.Symbols.defn import dotty.tools.dotc.core.Types.* -import dotty.tools.dotc.core.Types.Type import dotty.tools.dotc.interactive.Interactive import dotty.tools.dotc.interactive.InteractiveDriver import dotty.tools.dotc.typer.Applications.unapplyArgs @@ -21,10 +21,6 @@ import dotty.tools.pc.printer.ShortenedTypePrinter import dotty.tools.pc.printer.ShortenedTypePrinter.IncludeDefaultParam import dotty.tools.pc.utils.InteractiveEnrichments.* -import scala.meta.internal.metals.ReportContext -import scala.meta.pc.OffsetParams -import scala.meta.pc.SymbolSearch - class InferExpectedType( search: SymbolSearch, driver: InteractiveDriver, @@ -44,25 +40,23 @@ class InferExpectedType( case Some(unit) => val path = Interactive.pathTo(driver.openedTrees(uri), pos)(using ctx) - val newctx = ctx.fresh.setCompilationUnit(unit) + val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) val tpdPath = - Interactive.pathTo(newctx.compilationUnit.tpdTree, pos.span)(using - newctx - ) - val locatedCtx = - Interactive.contextOfPath(tpdPath)(using newctx) - val indexedCtx = IndexedContext(locatedCtx) + Interactive.pathTo(newctx.compilationUnit.tpdTree, pos.span)(using newctx) + val indexedContext = IndexedContext(pos, tpdPath, newctx) + import indexedContext.ctx val printer = - ShortenedTypePrinter(search, IncludeDefaultParam.ResolveLater)(using indexedCtx) - InterCompletionType.inferType(path)(using newctx).map{ + ShortenedTypePrinter(search, IncludeDefaultParam.ResolveLater)(using indexedContext) + InferCompletionType.inferType(path)(using newctx).map { tpe => printer.tpe(tpe) } case None => None -object InterCompletionType: +object InferCompletionType: def inferType(path: List[Tree])(using Context): Option[Type] = path match - case (lit: Literal) :: Select(Literal(_), _) :: Apply(Select(Literal(_), _), List(s: Select)) :: rest if s.symbol == defn.Predef_undefined => inferType(rest, lit.span) + case (lit: Literal) :: Select(Literal(_), _) :: Apply(Select(Literal(_), _), List(s: Select)) :: rest + if s.symbol == defn.Predef_undefined => inferType(rest, lit.span) case ident :: rest => inferType(rest, ident.span) case _ => None @@ -74,15 +68,18 @@ object InterCompletionType: case Bind(_, body) :: rest if body.span.contains(span) => inferType(rest, span) case Alternative(_) :: rest => inferType(rest, span) case Try(block, _, _) :: rest if block.span.contains(span) => inferType(rest, span) - case CaseDef(_, _, body) :: Try(_, cases, _) :: rest if body.span.contains(span) && cases.exists(_.span.contains(span)) => inferType(rest, span) + case CaseDef(_, _, body) :: Try(_, cases, _) :: rest + if body.span.contains(span) && cases.exists(_.span.contains(span)) => inferType(rest, span) case If(cond, _, _) :: rest if !cond.span.contains(span) => inferType(rest, span) - case If(cond, _, _) :: rest if cond.span.contains(span) => Some(Symbols.defn.BooleanType) - case CaseDef(_, _, body) :: Match(_, cases) :: rest if body.span.contains(span) && cases.exists(_.span.contains(span)) => + case If(cond, _, _) :: rest if cond.span.contains(span) => Some(defn.BooleanType) + case CaseDef(_, _, body) :: Match(_, cases) :: rest + if body.span.contains(span) && cases.exists(_.span.contains(span)) => inferType(rest, span) case NamedArg(_, arg) :: rest if arg.span.contains(span) => inferType(rest, span) // x match // case @@ - case CaseDef(pat, _, _) :: Match(sel, cases) :: rest if pat.span.contains(span) && cases.exists(_.span.contains(span)) && !sel.tpe.isErroneous => + case CaseDef(pat, _, _) :: Match(sel, cases) :: rest + if pat.span.contains(span) && cases.exists(_.span.contains(span)) && !sel.tpe.isErroneous => sel.tpe match case tpe: TermRef => Some(tpe.symbol.info).filterNot(_.isErroneous) case tpe => Some(tpe) @@ -97,39 +94,7 @@ object InterCompletionType: if ind < 0 then None else unapplyArgs(fun.tpe.finalResultType, fun, pats, NoSourcePosition).lift(ind) // f(@@) - case (app: Apply) :: rest => - val param = - for { - ind <- app.args.zipWithIndex.collectFirst { - case (arg, id) if arg.span.contains(span) => id - } - params <- app.symbol.paramSymss.find(!_.exists(_.isTypeParam)) - param <- params.get(ind) - } yield param.info - param match - // def f[T](a: T): T = ??? - // f[Int](@@) - // val _: Int = f(@@) - case Some(t : TypeRef) if t.symbol.is(Flags.TypeParam) => - for { - (typeParams, args) <- - app match - case Apply(TypeApply(fun, args), _) => - val typeParams = fun.symbol.paramSymss.headOption.filter(_.forall(_.isTypeParam)) - typeParams.map((_, args.map(_.tpe))) - // val f: (j: "a") => Int - // f(@@) - case Apply(Select(v, StdNames.nme.apply), _) => - v.symbol.info match - case AppliedType(des, args) => - Some((des.typeSymbol.typeParams, args)) - case _ => None - case _ => None - ind = typeParams.indexOf(t.symbol) - tpe <- args.get(ind) - if !tpe.isErroneous - } yield tpe - case Some(tpe) => Some(tpe) - case _ => None + case ApplyExtractor(app) => + val idx = app.args.indexWhere(_.span.contains(span)) + if idx < 0 then None else app.fun.tpe.widenTermRefExpr.paramInfoss.flatten.lift(idx) case _ => None - diff --git a/presentation-compiler/src/main/dotty/tools/pc/InferredMethodProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/InferredMethodProvider.scala new file mode 100644 index 000000000000..dabff4a714e0 --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/pc/InferredMethodProvider.scala @@ -0,0 +1,383 @@ +package dotty.tools.pc + +import java.nio.file.Paths + +import scala.annotation.tailrec +import scala.meta.pc.OffsetParams +import scala.meta.pc.PresentationCompilerConfig +import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext + +import dotty.tools.dotc.ast.tpd.* +import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.Symbols.* +import dotty.tools.dotc.core.Symbols.defn +import dotty.tools.dotc.core.Types.* +import dotty.tools.dotc.interactive.Interactive +import dotty.tools.dotc.interactive.InteractiveDriver +import dotty.tools.dotc.util.SourceFile +import dotty.tools.dotc.util.SourcePosition +import dotty.tools.pc.printer.ShortenedTypePrinter +import dotty.tools.pc.printer.ShortenedTypePrinter.IncludeDefaultParam +import dotty.tools.pc.utils.InteractiveEnrichments.* + +import org.eclipse.lsp4j.TextEdit +import org.eclipse.lsp4j as l + +/** Tries to calculate edits needed to create a method that will fix missing + * symbol in all the places that it is possible such as: + * - apply inside method invocation `method(.., nonExistent(param), ...)` and + * `method(.., nonExistent, ...)` + * - method in val definition `val value: DefinedType = nonExistent(param)` + * and `val value: DefinedType = nonExistent` + * - simple method call `nonExistent(param)` and `nonExistent` + * - method call inside a container `container.nonExistent(param)` and + * `container.nonExistent` + * + * @param params position and actual source + * @param driver Scala 3 interactive compiler driver + * @param config presentation compiler configuration + * @param symbolSearch symbol search + */ +final class InferredMethodProvider( + params: OffsetParams, + driver: InteractiveDriver, + config: PresentationCompilerConfig, + symbolSearch: SymbolSearch +)(using ReportContext): + + case class AdjustTypeOpts( + text: String, + adjustedEndPos: l.Position + ) + + def inferredMethodEdits( + adjustOpt: Option[AdjustTypeOpts] = None + ): List[TextEdit] = + val uri = params.uri().nn + val filePath = Paths.get(uri).nn + + val sourceText = adjustOpt.map(_.text).getOrElse(params.text().nn) + val source = + SourceFile.virtual(filePath.toString(), sourceText) + driver.run(uri, source) + val unit = driver.currentCtx.run.nn.units.head + val pos = driver.sourcePosition(params) + val path = + Interactive.pathTo(driver.openedTrees(uri), pos)(using driver.currentCtx) + + val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) + val indexedContext = IndexedContext(pos, path, newctx) + import indexedContext.ctx + + val autoImportsGen = AutoImports.generator( + pos, + sourceText, + unit.tpdTree, + unit.comments, + indexedContext, + config + ) + + val printer = ShortenedTypePrinter( + symbolSearch, + includeDefaultParam = IncludeDefaultParam.ResolveLater, + isTextEdit = true + )(using indexedContext) + + def imports: List[TextEdit] = + printer.imports(autoImportsGen) + + def printType(tpe: Type): String = + printer.tpe(tpe) + + def printName(name: Name): String = + printer.nameString(name) + + def printParams(params: List[Type], startIndex: Int = 0): String = + params.zipWithIndex + .map { case (p, index) => + s"arg${index + startIndex}: ${printType(p)}" + } + .mkString(", ") + + def printSignature( + methodName: Name, + params: List[List[Type]], + retTypeOpt: Option[Type] + ): String = + val retTypeString = retTypeOpt match + case Some(retType) => + val printRetType = printType(retType) + if retType.isAny then "" + else s": $printRetType" + case _ => "" + + val (paramsString, _) = params.foldLeft(("", 0)) { + case ((acc, startIdx), paramList) => + val printed = s"(${printParams(paramList, startIdx)})" + (acc + printed, startIdx + paramList.size) + } + + s"def ${printName(methodName)}$paramsString$retTypeString = ???" + + @tailrec + def countIndent(text: String, index: Int, acc: Int): Int = + if index > 0 && text(index) != '\n' then countIndent(text, index - 1, acc + 1) + else acc + + def indentation(text: String, pos: Int): String = + if pos > 0 then + val isSpace = text(pos) == ' ' + val isTab = text(pos) == '\t' + val indent = countIndent(params.text().nn, pos, 0) + + if isSpace then " " * indent else if isTab then "\t" * indent else "" + else "" + + def insertPosition() = + val blockOrTemplateIndex = + path.tail.indexWhere { + case _: Block | _: Template => true + case _ => false + } + path(blockOrTemplateIndex).sourcePos + + /** Returns the position to insert the method signature for a container. If + * the container has an empty body, the position is the end of the + * container. If the container has a non-empty body, the position is the + * end of the last element in the body. + * + * @param container the container to insert the method signature for + * @return the position to insert the method signature for the container + * and a boolean indicating if the container has an empty body + */ + def insertPositionFor(container: Tree): Option[(SourcePosition, Boolean)] = + val typeSymbol = container.tpe.widenDealias.typeSymbol + if typeSymbol.exists then + val trees = driver.openedTrees(params.uri().nn) + val include = Interactive.Include.definitions | Interactive.Include.local + Interactive.findTreesMatching(trees, include, typeSymbol).headOption match + case Some(srcTree) => + srcTree.tree match + case classDef: TypeDef if classDef.rhs.isInstanceOf[Template] => + val template = classDef.rhs.asInstanceOf[Template] + val (pos, hasEmptyBody) = template.body.lastOption match + case Some(last) => (last.sourcePos, false) + case None => (classDef.sourcePos, true) + Some((pos, hasEmptyBody)) + case _ => None + case None => None + else None + + /** Extracts type information for a specific parameter in a method + * signature. If the parameter is a function type, extracts both the + * function's argument types and return type. Otherwise, extracts just + * the parameter type. + * + * @param methodType the method type to analyze + * @param argIndex the index of the parameter to extract information for + * @return + * a tuple of (argument types, return type) where: + * - argument types: Some(List[Type]) if parameter is a function, None + * otherwise + * - return type: Some(Type) representing either the function's return + * type or the parameter type itself + */ + def extractParameterTypeInfo(methodType: Type, argIndex: Int): (Option[List[Type]], Option[Type]) = + methodType match + case m @ MethodType(param) => + val expectedFunctionType = m.paramInfos(argIndex) + if defn.isFunctionType(expectedFunctionType) then + expectedFunctionType match + case defn.FunctionOf(argTypes, retType, _) => + (Some(argTypes), Some(retType)) + case _ => + (None, Some(expectedFunctionType)) + else + (None, Some(m.paramInfos(argIndex))) + case _ => (None, None) + + def signatureEdits(signature: String): List[TextEdit] = + val pos = insertPosition() + val indent = indentation(params.text().nn, pos.start - 1) + val lspPos = pos.toLsp + lspPos.setEnd(lspPos.getStart()) + + List( + TextEdit( + lspPos, + s"$signature\n$indent" + ) + ) ::: imports + + def signatureEditsForContainer(signature: String, container: Tree): List[TextEdit] = + insertPositionFor(container) match + case Some((pos, hasEmptyBody)) => + val lspPos = pos.toLsp + lspPos.setStart(lspPos.getEnd()) + val indent = indentation(params.text().nn, pos.start - 1) + + if hasEmptyBody then + List( + TextEdit( + lspPos, + s":\n $indent$signature" + ) + ) ::: imports + else + List( + TextEdit( + lspPos, + s"\n$indent$signature" + ) + ) ::: imports + case None => + extensionMethodEdits(signature, container) + + def extensionMethodEdits(signature: String, container: Tree): List[TextEdit] = + val containerTypeStr = printType(container.tpe.widenDealias) + + val pos = insertPosition() + val indent = indentation(params.text().nn, pos.start - 1) + val extensionSignature = s"extension (x: $containerTypeStr)\n $indent$signature" + + val lspPos = pos.toLsp + lspPos.setEnd(lspPos.getStart()) + + List( + TextEdit( + lspPos, + s"$extensionSignature\n$indent" + ) + ) ::: imports + + path match + /** ``` + * outerArgs + * --------------------------- + * method(..., errorMethod(args), ...) + * ``` + */ + case (id @ Ident(errorMethod)) :: + (apply @ Apply(func, args)) :: + Apply(method, outerArgs) :: + _ if id.symbol == NoSymbol && func == id && method != apply => + + val argTypes = args.map(_.typeOpt.widenDealias) + + val argIndex = outerArgs.indexOf(apply) + val (allArgTypes, retTypeOpt) = + extractParameterTypeInfo(method.tpe.widenDealias, argIndex) match + case (Some(argTypes2), retTypeOpt) => (List(argTypes, argTypes2), retTypeOpt) + case (None, retTypeOpt) => (List(argTypes), retTypeOpt) + + val signature = printSignature(errorMethod, allArgTypes, retTypeOpt) + + signatureEdits(signature) + + /** ``` + * outerArgs + * --------------------- + * method(..., errorMethod, ...) + * ``` + */ + case (id @ Ident(errorMethod)) :: + Apply(method, outerArgs) :: + _ if id.symbol == NoSymbol && method != id => + + val argIndex = outerArgs.indexOf(id) + + val (argTypes, retTypeOpt) = extractParameterTypeInfo(method.tpe.widenDealias, argIndex) + + val allArgTypes = argTypes match + case Some(argTypes) => List(argTypes) + case None => Nil + + val signature = printSignature(errorMethod, allArgTypes, retTypeOpt) + + signatureEdits(signature) + + /** ``` + * outerArgs + * --------------------- + * method(..., errorMethod, ...) + * ``` + */ + case (id @ Ident(errorMethod)) :: + (apply @ Apply(func, args)) :: + ValDef(_, tpt, body) :: + _ if id.symbol == NoSymbol && func == id && apply == body => + + val retType = tpt.tpe.widenDealias + val argTypes = args.map(_.typeOpt.widenDealias) + + val signature = printSignature(errorMethod, List(argTypes), Some(retType)) + signatureEdits(signature) + + /** ``` + * tpt body + * ----------- ----------- + * val value: DefinedType = errorMethod + * + * ``` + */ + case (id @ Ident(errorMethod)) :: + ValDef(_, tpt, body) :: + _ if id.symbol == NoSymbol && id == body => + + val retType = tpt.tpe.widenDealias + + val signature = printSignature(errorMethod, Nil, Some(retType)) + signatureEdits(signature) + + /** ``` + * errorMethod(args) + * ``` + */ + case (id @ Ident(errorMethod)) :: + (apply @ Apply(func, args)) :: + _ if id.symbol == NoSymbol && func == id => + + val argTypes = args.map(_.typeOpt.widenDealias) + + val signature = printSignature(errorMethod, List(argTypes), None) + signatureEdits(signature) + + /** ``` + * errorMethod + * ``` + */ + case (id @ Ident(errorMethod)) :: + _ if id.symbol == NoSymbol => + + val signature = printSignature(errorMethod, Nil, None) + signatureEdits(signature) + + /** ``` + * container.errorMethod(args) + * ``` + */ + case (select @ Select(container, errorMethod)) :: + (apply @ Apply(func, args)) :: + _ if select.symbol == NoSymbol && func == select => + + val argTypes = args.map(_.typeOpt.widenDealias) + val signature = printSignature(errorMethod, List(argTypes), None) + signatureEditsForContainer(signature, container) + + /** ``` + * container.errorMethod + * ``` + */ + case (select @ Select(container, errorMethod)) :: + _ if select.symbol == NoSymbol => + + val signature = printSignature(errorMethod, Nil, None) + signatureEditsForContainer(signature, container) + + case _ => Nil + + end inferredMethodEdits +end InferredMethodProvider diff --git a/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala index d8cdbcd8fe69..5378043e96ae 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/InferredTypeProvider.scala @@ -3,15 +3,16 @@ package dotty.tools.pc import java.nio.file.Paths import scala.annotation.tailrec -import scala.meta.internal.metals.ReportContext import scala.meta.pc.OffsetParams import scala.meta.pc.PresentationCompilerConfig import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import scala.meta as m import dotty.tools.dotc.ast.Trees.* import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.NameOps.* import dotty.tools.dotc.core.Names.* import dotty.tools.dotc.core.Symbols.* @@ -29,22 +30,21 @@ import dotty.tools.pc.utils.InteractiveEnrichments.* import org.eclipse.lsp4j.TextEdit import org.eclipse.lsp4j as l -/** - * Tries to calculate edits needed to insert the inferred type annotation - * in all the places that it is possible such as: - * - value or variable declaration - * - methods - * - pattern matches - * - for comprehensions - * - lambdas +/** Tries to calculate edits needed to insert the inferred type annotation in + * all the places that it is possible such as: + * - value or variable declaration + * - methods + * - pattern matches + * - for comprehensions + * - lambdas * - * The provider will not check if the type does not exist, since there is no way to - * get that data from the presentation compiler. The actual check is being done via - * scalameta parser in InsertInferredType code action. + * The provider will not check if the type does not exist, since there is no + * way to get that data from the presentation compiler. The actual check is + * being done via scalameta parser in InsertInferredType code action. * - * @param params position and actual source - * @param driver Scala 3 interactive compiler driver - * @param config presentation compielr configuration + * @param params position and actual source + * @param driver Scala 3 interactive compiler driver + * @param config presentation compielr configuration */ final class InferredTypeProvider( params: OffsetParams, @@ -71,11 +71,11 @@ final class InferredTypeProvider( driver.run(uri, source) val unit = driver.currentCtx.run.nn.units.head val pos = driver.sourcePosition(params) + val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) val path = - Interactive.pathTo(driver.openedTrees(uri), pos)(using driver.currentCtx) - - given locatedCtx: Context = driver.localContext(params) - val indexedCtx = IndexedContext(locatedCtx) + Interactive.pathTo(newctx.compilationUnit.tpdTree, pos.span)(using newctx) + val indexedCtx = IndexedContext(pos, path, newctx) + import indexedCtx.ctx val autoImportsGen = AutoImports.generator( pos, sourceText, @@ -89,19 +89,25 @@ final class InferredTypeProvider( sourceText.substring(0, nameEnd).nn + sourceText.substring(tptEnd + 1, sourceText.length()) + def widenJavaEnumSingleton(tpe: Type)(using Context): Type = tpe match + case tp: TermRef if tp.termSymbol.isAllOf(Enum | JavaDefined) => + tp.widen + case _ => tpe + def optDealias(tpe: Type): Type = def isInScope(tpe: Type): Boolean = tpe match case tref: TypeRef => indexedCtx.lookupSym( - tref.currentSymbol + tref.currentSymbol, + Some(tref.prefix) ) == IndexedContext.Result.InScope case AppliedType(tycon, args) => isInScope(tycon) && args.forall(isInScope) case _ => true - if isInScope(tpe) - then tpe - else tpe.deepDealias + val widened = widenJavaEnumSingleton(tpe) + if isInScope(widened) then widened + else widened.deepDealiasAndSimplify val printer = ShortenedTypePrinter( symbolSearch, @@ -112,8 +118,8 @@ final class InferredTypeProvider( def imports: List[TextEdit] = printer.imports(autoImportsGen) - def printType(tpe: Type): String = - printer.tpe(tpe) + def printTypeAscription(tpe: Type, spaceBefore: Boolean = false): String = + (if spaceBefore then " : " else ": ") + printer.tpe(tpe) path.headOption match /* `val a = 1` or `var b = 2` @@ -124,7 +130,7 @@ final class InferredTypeProvider( * turns into * `.map((a: Int) => a + a)` */ - case Some(vl @ ValDef(sym, tpt, rhs)) => + case Some(vl @ ValDef(name, tpt, rhs)) => val isParam = path match case head :: next :: _ if next.symbol.isAnonymousFunction => true case head :: (b @ Block(stats, expr)) :: next :: _ @@ -136,9 +142,10 @@ final class InferredTypeProvider( val endPos = findNamePos(sourceText, vl, keywordOffset).endPos.toLsp adjustOpt.foreach(adjust => endPos.setEnd(adjust.adjustedEndPos)) + val spaceBefore = name.isOperatorName new TextEdit( endPos, - ": " + printType(optDealias(tpt.typeOpt)) + { + printTypeAscription(optDealias(tpt.typeOpt), spaceBefore) + { if withParens then ")" else "" } ) @@ -181,8 +188,7 @@ final class InferredTypeProvider( typeNameEdit ::: imports rhs match - case t: Tree[?] - if t.typeOpt.isErroneous && retryType && !tpt.sourcePos.span.isZeroExtent => + case t: Tree[?] if !tpt.sourcePos.span.isZeroExtent => inferredTypeEdits( Some( AdjustTypeOpts( @@ -192,12 +198,11 @@ final class InferredTypeProvider( ) ) case _ => simpleType - end match /* `def a[T](param : Int) = param` * turns into * `def a[T](param : Int): Int = param` */ - case Some(df @ DefDef(name, _, tpt, rhs)) => + case Some(df @ DefDef(name, paramss, tpt, rhs)) => def typeNameEdit = /* NOTE: In Scala 3.1.3, `List((1,2)).map((<>,b) => ...)` * turns into `List((1,2)).map((:Inta,b) => ...)`, @@ -208,10 +213,12 @@ final class InferredTypeProvider( if tpt.endPos.end > df.namePos.end then tpt.endPos.toLsp else df.namePos.endPos.toLsp + val spaceBefore = name.isOperatorName && paramss.isEmpty + adjustOpt.foreach(adjust => end.setEnd(adjust.adjustedEndPos)) new TextEdit( end, - ": " + printType(optDealias(tpt.typeOpt)) + printTypeAscription(optDealias(tpt.typeOpt), spaceBefore) ) end typeNameEdit @@ -220,8 +227,7 @@ final class InferredTypeProvider( while i >= 0 && sourceText(i) != ':' do i -= 1 i rhs match - case t: Tree[?] - if t.typeOpt.isErroneous && retryType && !tpt.sourcePos.span.isZeroExtent => + case t: Tree[?] if !tpt.sourcePos.span.isZeroExtent => inferredTypeEdits( Some( AdjustTypeOpts( @@ -239,9 +245,10 @@ final class InferredTypeProvider( */ case Some(bind @ Bind(name, body)) => def baseEdit(withParens: Boolean) = + val spaceBefore = name.isOperatorName new TextEdit( bind.endPos.toLsp, - ": " + printType(optDealias(body.typeOpt)) + { + printTypeAscription(optDealias(body.typeOpt), spaceBefore) + { if withParens then ")" else "" } ) @@ -272,9 +279,10 @@ final class InferredTypeProvider( * `for(t: Int <- 0 to 10)` */ case Some(i @ Ident(name)) => + val spaceBefore = name.isOperatorName val typeNameEdit = new TextEdit( i.endPos.toLsp, - ": " + printType(optDealias(i.typeOpt.widen)) + printTypeAscription(optDealias(i.typeOpt.widen), spaceBefore) ) typeNameEdit :: imports @@ -287,9 +295,7 @@ final class InferredTypeProvider( text: String, tree: untpd.NamedDefTree, kewordOffset: Int - )(using - Context - ): SourcePosition = + )(using Context): SourcePosition = val realName = tree.name.stripModuleClassSuffix.toString.toList // `NamedDefTree.namePos` is incorrect for bacticked names diff --git a/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala b/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala index 0e978049d177..a1bee972558b 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala @@ -3,14 +3,25 @@ package pc import scala.annotation.tailrec +import dotty.tools.dotc.core.Constants.* +import dotty.tools.dotc.core.StdNames.* +import dotty.tools.pc.utils.InteractiveEnrichments.* + import dotc.* -import ast.*, tpd.* -import core.*, Contexts.*, Flags.*, Names.*, Symbols.*, Types.* +import ast.* +import tpd.* +import core.* +import Contexts.* +import Flags.* +import Names.* +import Symbols.* +import Types.* import interactive.* import util.* import util.SourcePosition object MetalsInteractive: + type NamedTupleArg = String def contextOfStat( stats: List[Tree], @@ -32,18 +43,17 @@ object MetalsInteractive: case _ :: rest => contextOfStat(rest, stat, exprOwner, ctx) - /** - * Check if the given `sourcePos` is on the name of enclosing tree. - * ``` - * // For example, if the postion is on `foo`, returns true - * def foo(x: Int) = { ... } + /** Check if the given `sourcePos` is on the name of enclosing tree. + * ``` + * // For example, if the postion is on `foo`, returns true + * def foo(x: Int) = { ... } * ^ * - * // On the other hand, it points to non-name position, return false. - * def foo(x: Int) = { ... } + * // On the other hand, it points to non-name position, return false. + * def foo(x: Int) = { ... } * ^ - * ``` - * @param path - path to the position given by `Interactive.pathTo` + * ``` + * @param path - path to the position given by `Interactive.pathTo` */ def isOnName( path: List[Tree], @@ -65,14 +75,11 @@ object MetalsInteractive: case _: Import => true case app: (Apply | TypeApply) => contains(app.fun) case _ => false - end contains - val enclosing = path .dropWhile(t => !t.symbol.exists && !t.isInstanceOf[NamedArg]) .headOption .getOrElse(EmptyTree) contains(enclosing) - end isOnName private lazy val isForName: Set[Name] = Set[Name]( StdNames.nme.map, @@ -96,13 +103,12 @@ object MetalsInteractive: pos: SourcePosition, indexed: IndexedContext, skipCheckOnName: Boolean = false - ): List[Symbol] = + )(using Context): List[Symbol] = enclosingSymbolsWithExpressionType(path, pos, indexed, skipCheckOnName) - .map(_._1) + .map(_._1.sourceSymbol) - /** - * Returns the list of tuple enclosing symbol and - * the symbol's expression type if possible. + /** Returns the list of tuple enclosing symbol and the symbol's expression + * type if possible. */ @tailrec def enclosingSymbolsWithExpressionType( @@ -110,7 +116,7 @@ object MetalsInteractive: pos: SourcePosition, indexed: IndexedContext, skipCheckOnName: Boolean = false - ): List[(Symbol, Type)] = + ): List[(Symbol, Type, Option[String])] = import indexed.ctx path match // For a named arg, find the target `DefDef` and jump to the param @@ -118,59 +124,59 @@ object MetalsInteractive: val funSym = fn.symbol if funSym.is(Synthetic) && funSym.owner.is(CaseClass) then val sym = funSym.owner.info.member(name).symbol - List((sym, sym.info)) + List((sym, sym.info, None)) else val paramSymbol = for param <- funSym.paramSymss.flatten.find(_.name == name) yield param val sym = paramSymbol.getOrElse(fn.symbol) - List((sym, sym.info)) + List((sym, sym.info, None)) case (_: untpd.ImportSelector) :: (imp: Import) :: _ => importedSymbols(imp, _.span.contains(pos.span)).map(sym => - (sym, sym.info) + (sym, sym.info, None) ) - case (imp: Import) :: _ => + case (imp: ImportOrExport) :: _ => importedSymbols(imp, _.span.contains(pos.span)).map(sym => - (sym, sym.info) + (sym, sym.info, None) ) // wildcard param case head :: _ if (head.symbol.is(Param) && head.symbol.is(Synthetic)) => - List((head.symbol, head.typeOpt)) + List((head.symbol, head.typeOpt, None)) case (head @ Select(target, name)) :: _ if head.symbol.is(Synthetic) && name == StdNames.nme.apply => val sym = target.symbol if sym.is(Synthetic) && sym.is(Module) then - List((sym.companionClass, sym.companionClass.info)) - else List((target.symbol, target.typeOpt)) + List((sym.companionClass, sym.companionClass.info, None)) + else List((target.symbol, target.typeOpt, None)) // L@@ft(...) case (head @ ApplySelect(select)) :: _ if select.qualifier.sourcePos.contains(pos) && select.name == StdNames.nme.apply => - List((head.symbol, head.typeOpt)) + List((head.symbol, head.typeOpt, None)) // for Inlined we don't have a symbol, but it's needed to show proper type case (head @ Inlined(call, bindings, expansion)) :: _ => - List((call.symbol, head.typeOpt)) + List((call.symbol, head.typeOpt, None)) // for comprehension case (head @ ApplySelect(select)) :: _ if isForSynthetic(head) => // If the cursor is on the qualifier, return the symbol for it // `for { x <- List(1).head@@Option }` returns the symbol of `headOption` if select.qualifier.sourcePos.contains(pos) then - List((select.qualifier.symbol, select.qualifier.typeOpt)) + List((select.qualifier.symbol, select.qualifier.typeOpt, None)) // Otherwise, returns the symbol of for synthetics such as "withFilter" - else List((head.symbol, head.typeOpt)) + else List((head.symbol, head.typeOpt, None)) // f@@oo.bar case Select(target, _) :: _ if target.span.isSourceDerived && target.sourcePos.contains(pos) => - List((target.symbol, target.typeOpt)) + List((target.symbol, target.typeOpt, None)) /* In some cases type might be represented by TypeTree, however it's possible * that the type tree will not be marked properly as synthetic even if it doesn't @@ -185,7 +191,7 @@ object MetalsInteractive: */ case (tpt: TypeTree) :: parent :: _ if tpt.span != parent.span && !tpt.symbol.is(Synthetic) => - List((tpt.symbol, tpt.typeOpt)) + List((tpt.symbol, tpt.typeOpt, None)) /* TypeTest class https://dotty.epfl.ch/docs/reference/other-new-features/type-test.html * compiler automatically adds unapply if possible, we need to find the type symbol @@ -195,21 +201,23 @@ object MetalsInteractive: pat match case UnApply(fun, _, pats) => val tpeSym = pats.head.typeOpt.typeSymbol - List((tpeSym, tpeSym.info)) + List((tpeSym, tpeSym.info, None)) case _ => Nil + case head :: (sel @ Select(_, name)) :: _ + if head.sourcePos.encloses(sel.sourcePos) && (name == StdNames.nme.apply || name == StdNames.nme.unapply) => + val optObjectSymbol = List(head.symbol).filter(sym => !(sym.is(Synthetic) && sym.is(Module))) + val classSymbol = head.symbol.companionClass + val optApplySymbol = List(sel.symbol).filter(sym => !sym.is(Synthetic)) + val symbols = optObjectSymbol ++ (classSymbol :: optApplySymbol) + symbols.collect: + case sym if sym.exists => (sym, sym.info, None) + case path @ head :: tail => if head.symbol.is(Exported) then val sym = head.symbol.sourceSymbol - List((sym, sym.info)) - else if head.symbol.is(Synthetic) then - enclosingSymbolsWithExpressionType( - tail, - pos, - indexed, - skipCheckOnName - ) + List((sym, sym.info, None)) else if head.symbol != NoSymbol then if skipCheckOnName || MetalsInteractive.isOnName( @@ -217,7 +225,14 @@ object MetalsInteractive: pos, indexed.ctx.source ) - then List((head.symbol, head.typeOpt)) + then List((head.symbol, head.typeOpt, None)) + else if head.symbol.is(Synthetic) then + enclosingSymbolsWithExpressionType( + tail, + pos, + indexed, + skipCheckOnName + ) /* Type tree for List(1) has an Int type variable, which has span * but doesn't exist in code. * https://github.com/lampepfl/dotty/issues/15937 @@ -234,7 +249,7 @@ object MetalsInteractive: indexed, skipCheckOnName ) - else recovered.map(sym => (sym, sym.info)) + else recovered.map(sym => (sym, sym.info, None)) end if case Nil => Nil end match @@ -247,7 +262,6 @@ object MetalsInteractive: indexed: IndexedContext ): List[Symbol] = import indexed.ctx - tree match case select: Select => select.qualifier.typeOpt @@ -256,7 +270,6 @@ object MetalsInteractive: .filter(_ != NoSymbol) case ident: Ident => indexed.findSymbol(ident.name).toList.flatten case _ => Nil - end recoverError object ApplySelect: def unapply(tree: Tree): Option[Select] = Option(tree).collect { @@ -264,7 +277,6 @@ object MetalsInteractive: case Apply(select: Select, _) => select case Apply(TypeApply(select: Select, _), _) => select } - end ApplySelect object TreeApply: def unapply(tree: Tree): Option[(Tree, List[Tree])] = diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala b/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala index 1ebfd405768e..70b78d124030 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala @@ -2,7 +2,6 @@ package dotty.tools.pc import java.nio.file.Paths -import dotty.tools.pc.PcSymbolSearch.* import scala.meta.internal.metals.CompilerOffsetParams import scala.meta.pc.OffsetParams import scala.meta.pc.VirtualFileParams @@ -27,13 +26,14 @@ import dotty.tools.dotc.interactive.InteractiveDriver import dotty.tools.dotc.util.SourceFile import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.util.Spans.Span +import dotty.tools.pc.PcSymbolSearch.* import dotty.tools.pc.utils.InteractiveEnrichments.* trait PcCollector[T]: self: WithCompilationUnit => def collect( parent: Option[Tree] - )(tree: Tree| EndMarker, pos: SourcePosition, symbol: Option[Symbol]): T + )(tree: Tree | EndMarker, pos: SourcePosition, symbol: Option[Symbol]): T def allowZeroExtentImplicits: Boolean = false @@ -50,9 +50,9 @@ trait PcCollector[T]: lazy val soughtNames: Set[Name] = sought.map(_.name) /* - * For comprehensions have two owners, one for the enumerators and one for - * yield. This is a heuristic to find that out. - */ + * For comprehensions have two owners, one for the enumerators and one for + * yield. This is a heuristic to find that out. + */ def isForComprehensionOwner(named: NameTree) = soughtNames(named.name) && scala.util @@ -63,7 +63,8 @@ trait PcCollector[T]: o.span.exists && o.span.point == named.symbol.owner.span.point ) - def soughtOrOverride(sym: Symbol) = + def soughtOrOverride(sym0: Symbol) = + val sym = if sym0.is(Flags.Exported) then sym0.sourceSymbol else sym0 sought(sym) || sym.allOverriddenSymbols.exists(sought(_)) def soughtTreeFilter(tree: Tree): Boolean = @@ -76,7 +77,7 @@ trait PcCollector[T]: case df: NamedDefTree if soughtOrOverride(df.symbol) && !df.symbol.isSetter => true - case imp: Import if owners(imp.expr.symbol) => true + case imp: ImportOrExport if owners(imp.expr.symbol) => true case _ => false def soughtFilter(f: Symbol => Boolean): Boolean = @@ -109,23 +110,27 @@ trait PcCollector[T]: ) = this.collect(parent)(tree, pos, symbol) tree match - /** - * All indentifiers such as: - * val a = <> + /** All indentifiers such as: + * ``` + * val a = <> + * ``` */ case ident: Ident if ident.isCorrectSpan && filter(ident) => // symbols will differ for params in different ext methods, but source pos will be the same - if soughtFilter(_.sourcePos == ident.symbol.sourcePos) + val symbol = if ident.symbol.is(Flags.Exported) then ident.symbol.sourceSymbol else ident.symbol + if soughtFilter(_.sourcePos == symbol.sourcePos) then occurrences + collect( ident, - ident.sourcePos + ident.sourcePos, + Some(symbol) ) else occurrences - /** - * Workaround for missing symbol in: - * class A[T](a: T) - * val x = new <>(1) + + /** Workaround for missing symbol in: + * ``` + * class A[T](a: T) val x = new <>(1) + * ``` */ case sel @ Select(New(t), _) if sel.isCorrectSpan && @@ -135,23 +140,27 @@ trait PcCollector[T]: occurrences + collect( sel, namePos(t), - Some(sel.symbol.owner), + Some(sel.symbol.owner) ) else occurrences - /** - * All select statements such as: - * val a = hello.<> + + /** All select statements such as: + * ``` + * val a = hello.<> + * ``` */ case sel: Select - if sel.isCorrectSpan && filter(sel) && - !sel.isForComprehensionMethod => + if sel.isCorrectSpan && filter(sel) && + !sel.isForComprehensionMethod => occurrences + collect( sel, pos.withSpan(selectNameSpan(sel)) ) /* all definitions: - * def <> = ??? - * class <> = ??? + * ``` + * def <> = ??? + * class <> = ??? + * ``` * etc. */ case df: NamedDefTree @@ -160,7 +169,7 @@ trait PcCollector[T]: def collectEndMarker = EndMarker.getPosition(df, pos, sourceText).map: collect(EndMarker(df.symbol), _) - val annots = collectTrees(df.mods.annotations) + val annots = collectTrees(df.symbol.annotations.map(_.tree)) val traverser = new PcCollector.DeepFolderWithParent[Set[T]]( collectNamesWithParent @@ -175,8 +184,10 @@ trait PcCollector[T]: } /* Named parameters don't have symbol so we need to check the owner - * foo(<> = "abc") - * User(<> = "abc") + * ``` + * foo(<> = "abc") + * User(<> = "abc") + * ``` * etc. */ case apply: Apply => @@ -210,13 +221,13 @@ trait PcCollector[T]: } occurrences ++ named - /** - * For traversing annotations: - * @<>("") - * def params() = ??? + /** ``` + * @<>("") + * def params() = ??? + * ``` */ - case mdf: MemberDef if mdf.mods.annotations.nonEmpty => - val trees = collectTrees(mdf.mods.annotations) + case mdf: MemberDef if mdf.symbol.annotations.nonEmpty => + val trees = collectTrees(mdf.symbol.annotations.map(_.tree)) val traverser = new PcCollector.DeepFolderWithParent[Set[T]]( collectNamesWithParent @@ -224,11 +235,10 @@ trait PcCollector[T]: trees.foldLeft(occurrences) { case (set, tree) => traverser(set, tree) } - /** - * For traversing import selectors: - * import scala.util.<> + + /** For traversing import selectors: import scala.util.<> */ - case imp: Import if filter(imp) => + case imp: ImportOrExport if filter(imp) => imp.selectors .collect { case sel: ImportSelector @@ -294,7 +304,6 @@ object PcCollector: private val traverser = WithParentTraverser[X](f) def apply(x: X, tree: Tree)(using Context) = traverser.traverse(x, tree, None) -end PcCollector case class ExtensionParamOccurence( name: Name, @@ -306,37 +315,39 @@ case class ExtensionParamOccurence( case class EndMarker(symbol: Symbol) object EndMarker: - /** - * Matches end marker line from start to the name's beginning. - * E.g. - * end /* some comment */ - */ + /** Matches end marker line from start to the name's beginning. E.g. end + * ``` + * end /* + * some comment */ + * ``` + */ private val endMarkerRegex = """.*end(/\*.*\*/|\s)+""".r def getPosition(df: NamedDefTree, pos: SourcePosition, sourceText: String)( implicit ct: Context ): Option[SourcePosition] = - val name = df.name.toString() - val endMarkerLine = - sourceText.slice(df.span.start, df.span.end).split('\n').last - val index = endMarkerLine.length() - name.length() - if index < 0 then None - else - val (possiblyEndMarker, possiblyEndMarkerName) = - endMarkerLine.splitAt(index) - Option.when( - possiblyEndMarkerName == name && - endMarkerRegex.matches(possiblyEndMarker) - )( - pos - .withStart(df.span.end - name.length()) - .withEnd(df.span.end) - ) - end getPosition -end EndMarker + val name = df.name.toString().stripSuffix("$") + val lines = sourceText.slice(df.span.start, df.span.end).split('\n') + + if lines.nonEmpty then + val endMarkerLine = lines.last + val index = endMarkerLine.length() - name.length() + if index < 0 then None + else + val (possiblyEndMarker, possiblyEndMarkerName) = + endMarkerLine.splitAt(index) + Option.when( + possiblyEndMarkerName == name && + endMarkerRegex.matches(possiblyEndMarker) + )( + pos + .withStart(df.span.end - name.length()) + .withEnd(df.span.end) + ) + else None abstract class WithSymbolSearchCollector[T]( driver: InteractiveDriver, - params: OffsetParams, + params: OffsetParams ) extends WithCompilationUnit(driver, params) with PcSymbolSearch with PcCollector[T]: @@ -347,7 +358,7 @@ abstract class WithSymbolSearchCollector[T]( abstract class SimpleCollector[T]( driver: InteractiveDriver, - params: VirtualFileParams, + params: VirtualFileParams ) extends WithCompilationUnit(driver, params) with PcCollector[T]: def result(): List[T] = resultAllOccurences().toList diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcConvertToNamedLambdaParameters.scala b/presentation-compiler/src/main/dotty/tools/pc/PcConvertToNamedLambdaParameters.scala new file mode 100644 index 000000000000..342a32d1824c --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/pc/PcConvertToNamedLambdaParameters.scala @@ -0,0 +1,151 @@ +package dotty.tools.pc + +import java.nio.file.Paths +import java.util as ju + +import scala.jdk.CollectionConverters.* +import scala.meta.pc.OffsetParams + +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Flags +import dotty.tools.dotc.interactive.Interactive +import dotty.tools.dotc.interactive.InteractiveDriver +import dotty.tools.dotc.util.SourceFile +import dotty.tools.dotc.util.SourcePosition +import dotty.tools.pc.utils.InteractiveEnrichments.* +import dotty.tools.pc.utils.TermNameInference.* + +import org.eclipse.lsp4j as l + +/** Facilitates the code action that converts a wildcard lambda to a lambda with + * named parameters e.g. + * + * List(1, 2).map(<<_>> + 1) => List(1, 2).map(i => i + 1) + */ +final class PcConvertToNamedLambdaParameters( + driver: InteractiveDriver, + params: OffsetParams +): + import PcConvertToNamedLambdaParameters.* + + def convertToNamedLambdaParameters: ju.List[l.TextEdit] = + val uri = params.uri.nn + val filePath = Paths.get(uri) + driver.run( + uri, + SourceFile.virtual(filePath.toString, params.text.nn) + ) + given ctx: Context = driver.currentCtx + val pos = driver.sourcePosition(params) + val trees = driver.openedTrees(uri) + val treeList = Interactive.pathTo(trees, pos) + // Extractor for a lambda function (needs context, so has to be defined here) + val LambdaExtractor = Lambda(using ctx) + // select the most inner wildcard lambda + val firstLambda = treeList.collectFirst { + case LambdaExtractor(params, rhsFn) if params.forall(isWildcardParam) => + params -> rhsFn + } + + firstLambda match + case Some((params, lambda)) => + // avoid names that are either defined or referenced in the lambda + val namesToAvoid = allDefAndRefNamesInTree(lambda) + // compute parameter names based on the type of the parameter + val computedParamNames: List[String] = + params.foldLeft(List.empty[String]) { (acc, param) => + val name = singleLetterNameStream(param.tpe.typeSymbol.name.toString()) + .find(n => !namesToAvoid.contains(n) && !acc.contains(n)) + acc ++ name.toList + } + if computedParamNames.size == params.size then + val paramReferenceEdits = params.zip(computedParamNames).flatMap { (param, paramName) => + val paramReferencePosition = findParamReferencePosition(param, lambda) + paramReferencePosition.toList.map { pos => + val position = pos.toLsp + val range = new l.Range( + position.getStart(), + position.getEnd() + ) + new l.TextEdit(range, paramName) + } + } + val paramNamesStr = computedParamNames.mkString(", ") + val paramDefsStr = + if params.size == 1 then paramNamesStr + else s"($paramNamesStr)" + val defRange = new l.Range( + lambda.sourcePos.toLsp.getStart(), + lambda.sourcePos.toLsp.getStart() + ) + val paramDefinitionEdits = List( + new l.TextEdit(defRange, s"$paramDefsStr => ") + ) + (paramDefinitionEdits ++ paramReferenceEdits).asJava + else + List.empty.asJava + case _ => + List.empty.asJava + +end PcConvertToNamedLambdaParameters + +object PcConvertToNamedLambdaParameters: + val codeActionId = "ConvertToNamedLambdaParameters" + + class Lambda(using Context): + def unapply(tree: tpd.Block): Option[(List[tpd.ValDef], tpd.Tree)] = tree match + case tpd.Block( + (ddef @ tpd.DefDef(_, tpd.ValDefs(params) :: Nil, _, body: tpd.Tree)) :: Nil, + tpd.Closure(_, meth, _) + ) + if ddef.symbol == meth.symbol => + params match + case List(param) => + // lambdas with multiple wildcard parameters are represented as a single parameter function and a block with wildcard valdefs + Some(multipleUnderscoresFromBody(param, body)) + case _ => Some(params -> body) + case _ => None + + private def multipleUnderscoresFromBody( + param: tpd.ValDef, + body: tpd.Tree + )(using Context): (List[tpd.ValDef], tpd.Tree) = body match + case tpd.Block(defs, expr) if param.symbol.is(Flags.Synthetic) => + val wildcardParamDefs = defs.collect { + case valdef: tpd.ValDef if isWildcardParam(valdef) => valdef + } + if wildcardParamDefs.size == defs.size then wildcardParamDefs -> expr + else List(param) -> body + case _ => List(param) -> body + + def isWildcardParam(param: tpd.ValDef)(using Context): Boolean = + param.name.toString.startsWith("_$") + + def findParamReferencePosition(param: tpd.ValDef, lambda: tpd.Tree)(using Context): Option[SourcePosition] = + var pos: Option[SourcePosition] = None + object FindParamReference extends tpd.TreeTraverser: + override def traverse(tree: tpd.Tree)(using Context): Unit = + tree match + case ident @ tpd.Ident(_) if ident.symbol == param.symbol => + pos = Some(tree.sourcePos) + case _ => + traverseChildren(tree) + FindParamReference.traverse(lambda) + pos + + def allDefAndRefNamesInTree(tree: tpd.Tree)(using Context): List[String] = + object FindDefinitionsAndRefs extends tpd.TreeAccumulator[List[String]]: + override def apply(x: List[String], tree: tpd.Tree)(using Context): List[String] = + tree match + case tpd.DefDef(name, _, _, _) => + super.foldOver(x :+ name.toString, tree) + case tpd.ValDef(name, _, _) => + super.foldOver(x :+ name.toString, tree) + case tpd.Ident(name) => + super.foldOver(x :+ name.toString, tree) + case _ => + super.foldOver(x, tree) + FindDefinitionsAndRefs.foldOver(Nil, tree) + +end PcConvertToNamedLambdaParameters diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala index 3b2284bef1d0..bb3469fb832a 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala @@ -1,5 +1,6 @@ package dotty.tools.pc +import java.net.URI import java.nio.file.Paths import java.util.ArrayList @@ -16,6 +17,7 @@ import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Flags.{Exported, ModuleClass} import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.interactive.Interactive +import dotty.tools.dotc.interactive.Interactive.Include import dotty.tools.dotc.interactive.InteractiveDriver import dotty.tools.dotc.util.SourceFile import dotty.tools.dotc.util.SourcePosition @@ -48,127 +50,120 @@ class PcDefinitionProvider( val path = Interactive.pathTo(driver.openedTrees(uri), pos)(using driver.currentCtx) - given ctx: Context = driver.localContext(params) - val indexedContext = IndexedContext(ctx) + val unit = driver.currentCtx.run.nn.units.head + val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) + val indexedContext = IndexedContext(pos, path, newctx) + import indexedContext.ctx val result = - if findTypeDef then findTypeDefinitions(path, pos, indexedContext) - else findDefinitions(path, pos, indexedContext) + if findTypeDef then findTypeDefinitions(path, pos, indexedContext, uri) + else findDefinitions(path, pos, indexedContext, uri) - if result.locations().nn.isEmpty() then fallbackToUntyped(pos)(using ctx) + if result.locations().nn.isEmpty() then fallbackToUntyped(pos, uri)(using ctx) else result end definitions - /** - * Some nodes might disapear from the typed tree, since they are mostly - * used as syntactic sugar. In those cases we check the untyped tree - * and try to get the symbol from there, which might actually be there, - * because these are the same nodes that go through the typer. + /** Some nodes might disapear from the typed tree, since they are mostly used + * as syntactic sugar. In those cases we check the untyped tree and try to + * get the symbol from there, which might actually be there, because these + * are the same nodes that go through the typer. * - * This will happen for: - * - `.. derives Show` - * @param unit compilation unit of the file - * @param pos cursor position - * @return definition result + * This will happen for: + * - `.. derives Show` + * @param unit compilation unit of the file + * @param pos cursor position + * @return definition result */ - private def fallbackToUntyped(pos: SourcePosition)( - using ctx: Context + private def fallbackToUntyped(pos: SourcePosition, uri: URI)( + using ctx: Context ) = lazy val untpdPath = NavigateAST .untypedPath(pos.span) .collect { case t: untpd.Tree => t } - definitionsForSymbol(untpdPath.headOption.map(_.symbol).toList, pos) - end fallbackToUntyped + definitionsForSymbols(untpdPath.headOption.map(_.symbol).toList, uri, pos) private def findDefinitions( path: List[Tree], pos: SourcePosition, - indexed: IndexedContext + indexed: IndexedContext, + uri: URI ): DefinitionResult = import indexed.ctx - definitionsForSymbol( + definitionsForSymbols( MetalsInteractive.enclosingSymbols(path, pos, indexed), + uri, pos ) - end findDefinitions private def findTypeDefinitions( path: List[Tree], pos: SourcePosition, - indexed: IndexedContext + indexed: IndexedContext, + uri: URI ): DefinitionResult = import indexed.ctx val enclosing = path.expandRangeToEnclosingApply(pos) val typeSymbols = MetalsInteractive .enclosingSymbolsWithExpressionType(enclosing, pos, indexed) - .map { case (_, tpe) => + .map { case (_, tpe, _) => tpe.typeSymbol } typeSymbols match case Nil => path.headOption match case Some(value: Literal) => - definitionsForSymbol(List(value.typeOpt.widen.typeSymbol), pos) + definitionsForSymbols(List(value.typeOpt.widen.typeSymbol), uri, pos) case _ => DefinitionResultImpl.empty case _ => - definitionsForSymbol(typeSymbols, pos) - - end findTypeDefinitions + definitionsForSymbols(typeSymbols, uri, pos) - private def definitionsForSymbol( + private def definitionsForSymbols( symbols: List[Symbol], + uri: URI, pos: SourcePosition )(using ctx: Context): DefinitionResult = - symbols match - case symbols @ (sym :: other) => - val isLocal = sym.source == pos.source - if isLocal then - val (exportedDefs, otherDefs) = - Interactive.findDefinitions(List(sym), driver, false, false) - .filter(_.source == sym.source) - .partition(_.tree.symbol.is(Exported)) - - otherDefs.headOption.orElse(exportedDefs.headOption) match - case Some(srcTree) => - val pos = srcTree.namePos - if pos.exists then - val loc = new Location(params.uri().toString(), pos.toLsp) - DefinitionResultImpl( - SemanticdbSymbols.symbolName(sym), - List(loc).asJava, - ) - else DefinitionResultImpl.empty - case None => - DefinitionResultImpl.empty - else - val res = new ArrayList[Location]() - semanticSymbolsSorted(symbols) - .foreach { sym => - res.addAll(search.definition(sym, params.uri())) - } - DefinitionResultImpl( - SemanticdbSymbols.symbolName(sym), - res - ) - end if + semanticSymbolsSorted(symbols) match case Nil => DefinitionResultImpl.empty - end match - end definitionsForSymbol + case syms @ ((_, headSym) :: tail) => + val locations = syms.flatMap: + case (sym, semanticdbSymbol) => + locationsForSymbol(sym, semanticdbSymbol, uri, pos) + DefinitionResultImpl(headSym, locations.asJava) + + private def locationsForSymbol( + symbol: Symbol, + semanticdbSymbol: String, + uri: URI, + pos: SourcePosition + )(using ctx: Context): List[Location] = + val isLocal = symbol.source == pos.source + if isLocal then + val trees = driver.openedTrees(uri) + val include = Include.definitions | Include.local + val (exportedDefs, otherDefs) = + Interactive.findTreesMatching(trees, include, symbol) + .partition(_.tree.symbol.is(Exported)) + otherDefs.headOption.orElse(exportedDefs.headOption).collect: + case srcTree if srcTree.namePos.exists => + new Location(params.uri().toString(), srcTree.namePos.toLsp) + .toList + else search.definition(semanticdbSymbol, uri).nn.asScala.toList def semanticSymbolsSorted( syms: List[Symbol] - )(using ctx: Context): List[String] = + )(using ctx: Context): List[(Symbol, String)] = syms - .map { sym => - // in case of having the same type and teerm symbol - // term comes first - // used only for ordering symbols that come from `Import` - val termFlag = - if sym.is(ModuleClass) then sym.sourceModule.isTerm - else sym.isTerm - (termFlag, SemanticdbSymbols.symbolName(sym)) + .collect { + case sym if sym.exists => + // in case of having the same type and teerm symbol + // term comes first + // used only for ordering symbols that come from `Import` + val termFlag = + if sym.is(ModuleClass) then sym.sourceModule.isTerm + else sym.isTerm + (termFlag, sym.sourceSymbol, SemanticdbSymbols.symbolName(sym)) } - .sorted - .map(_._2) + .sortBy { case (termFlag, _, name) => (termFlag, name) } + .map(_.tail) end PcDefinitionProvider diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala index 9c0e6bcfa9d8..9d11546f3912 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala @@ -1,22 +1,22 @@ package dotty.tools.pc - import java.nio.file.Paths import scala.annotation.tailrec - -import scala.meta.internal.metals.ReportContext -import dotty.tools.pc.utils.InteractiveEnrichments.* -import dotty.tools.pc.printer.ShortenedTypePrinter +import scala.meta.internal.pc.InlayHintOrigin import scala.meta.internal.pc.InlayHints import scala.meta.internal.pc.LabelPart import scala.meta.internal.pc.LabelPart.* import scala.meta.pc.InlayHintsParams import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Flags +import dotty.tools.dotc.core.NameKinds.DefaultGetterName +import dotty.tools.dotc.core.NameOps.fieldName +import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.StdNames.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* @@ -25,15 +25,17 @@ import dotty.tools.dotc.interactive.InteractiveDriver import dotty.tools.dotc.util.SourceFile import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.util.Spans.Span +import dotty.tools.pc.printer.ShortenedTypePrinter +import dotty.tools.pc.utils.InteractiveEnrichments.* import org.eclipse.lsp4j.InlayHint import org.eclipse.lsp4j.InlayHintKind -import org.eclipse.{lsp4j as l} +import org.eclipse.lsp4j as l class PcInlayHintsProvider( driver: InteractiveDriver, params: InlayHintsParams, - symbolSearch: SymbolSearch, + symbolSearch: SymbolSearch )(using ReportContext): val uri = params.uri().nn @@ -51,13 +53,13 @@ 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 .getOrElse(unit.tpdTree) .enclosedChildren(pos.span) - .flatMap(tpdTree => deepFolder(InlayHints.empty, tpdTree).result()) + .flatMap(tpdTree => deepFolder(InlayHints.empty(params.uri().nn), tpdTree).result()) private def adjustPos(pos: SourcePosition): SourcePosition = pos.adjust(text)._1 @@ -65,67 +67,133 @@ class PcInlayHintsProvider( def collectDecorations( inlayHints: InlayHints, tree: Tree, + parent: Option[Tree] ): InlayHints = + /* + XRay hints and closing labels 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 + + val withClosingLabels = tree match + case ClosingLabel(name, pos) => + firstPassHints.add( + adjustPos(pos).endPos.toLsp, + List(LabelPart(name)), + InlayHintKind.Parameter, + InlayHintOrigin.ClosingLabel + ) + case _ => firstPassHints + tree match case ImplicitConversion(symbol, range) => val adjusted = adjustPos(range) - inlayHints + withClosingLabels .add( adjusted.startPos.toLsp, labelPart(symbol, symbol.decodedName) :: LabelPart("(") :: Nil, InlayHintKind.Parameter, + InlayHintOrigin.ImplicitConversion ) .add( adjusted.endPos.toLsp, LabelPart(")") :: Nil, InlayHintKind.Parameter, + InlayHintOrigin.ImplicitConversion ) - case ImplicitParameters(symbols, pos, allImplicit) => - val labelParts = symbols.map(s => List(labelPart(s, s.decodedName))) - val label = - if allImplicit then labelParts.separated("(using ", ", ", ")") - else labelParts.separated(", ") - inlayHints.add( + case ImplicitParameters(trees, pos) => + withClosingLabels.add( adjustPos(pos).toLsp, - label, + ImplicitParameters.partsFromImplicitArgs(trees).map((label, maybeSymbol) => + maybeSymbol match + case Some(symbol) => labelPart(symbol, label) + case None => LabelPart(label) + ), InlayHintKind.Parameter, + InlayHintOrigin.ImplicitParameters ) case ValueOf(label, pos) => - inlayHints.add( + withClosingLabels.add( adjustPos(pos).toLsp, LabelPart("(") :: LabelPart(label) :: List(LabelPart(")")), InlayHintKind.Parameter, + InlayHintOrigin.ImplicitParameters ) case TypeParameters(tpes, pos, sel) if !syntheticTupleApply(sel) => val label = tpes.map(toLabelParts(_, pos)).separated("[", ", ", "]") - inlayHints.add( + withClosingLabels.add( adjustPos(pos).endPos.toLsp, label, InlayHintKind.Type, + InlayHintOrigin.TypeParameters ) case InferredType(tpe, pos, defTree) if !isErrorTpe(tpe) => val adjustedPos = adjustPos(pos).endPos - if inlayHints.containsDef(adjustedPos.start) then inlayHints - else - inlayHints - .add( - adjustedPos.toLsp, - LabelPart(": ") :: toLabelParts(tpe, pos), - InlayHintKind.Type, - ) - .addDefinition(adjustedPos.start) - case _ => inlayHints + withClosingLabels + .add( + adjustedPos.toLsp, + LabelPart(": ") :: toLabelParts(tpe, pos), + InlayHintKind.Type, + InlayHintOrigin.InferredType + ) + case Parameters(isInfixFun, args) => + def isNamedParam(pos: SourcePosition): Boolean = + val start = text.indexWhere(!_.isWhitespace, pos.start) + val end = text.lastIndexWhere(!_.isWhitespace, pos.end - 1) + + text.slice(start, end).contains('=') + + def isBlockParam(pos: SourcePosition): Boolean = + val start = text.indexWhere(!_.isWhitespace, pos.start) + val end = text.lastIndexWhere(!_.isWhitespace, pos.end - 1) + val startsWithBrace = text.lift(start).contains('{') + val endsWithBrace = text.lift(end).contains('}') + + startsWithBrace && endsWithBrace + + def adjustBlockParamPos(pos: SourcePosition): SourcePosition = + pos.withStart(pos.start + 1) + + args.foldLeft(withClosingLabels) { + case (ih, (name, pos0, isByName)) => + val pos = adjustPos(pos0) + val isBlock = isBlockParam(pos) + val namedLabel = + if params.namedParameters() && !isInfixFun && !isBlock && !isNamedParam(pos) then s"${name} = " else "" + val byNameLabel = + if params.byNameParameters() && isByName && (!isInfixFun || isBlock) then "=> " else "" + + val labelStr = s"${namedLabel}${byNameLabel}" + val hintPos = if isBlock then adjustBlockParamPos(pos) else pos + + if labelStr.nonEmpty then + ih.add( + hintPos.startPos.toLsp, + List(LabelPart(labelStr)), + InlayHintKind.Parameter, + if params.byNameParameters then InlayHintOrigin.ByNameParameters else InlayHintOrigin.NamedParameters + ) + else ih + } + case _ => withClosingLabels private def toLabelParts( tpe: Type, - pos: SourcePosition, + pos: SourcePosition ): List[LabelPart] = - val tpdPath = - Interactive.pathTo(unit.tpdTree, pos.span) - - val indexedCtx = IndexedContext(Interactive.contextOfPath(tpdPath)) + val tpdPath = Interactive.pathTo(unit.tpdTree, pos.span) + val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) + val indexedCtx = IndexedContext(pos, tpdPath, newctx) + import indexedCtx.ctx val printer = ShortenedTypePrinter( symbolSearch )(using indexedCtx) @@ -140,20 +208,18 @@ class PcInlayHintsProvider( isInScope(tycon) && args.forall(isInScope) case _ => true if isInScope(tpe) then tpe - else tpe.deepDealias(using indexedCtx.ctx) + else tpe.deepDealiasAndSimplify(using indexedCtx.ctx) val dealiased = optDealias(tpe) val tpeStr = printer.tpe(dealiased) val usedRenames = printer.getUsedRenames val parts = partsFromType(dealiased, usedRenames) InlayHints.makeLabelParts(parts, tpeStr) - end toLabelParts - private val definitions = IndexedContext(ctx).ctx.definitions private def syntheticTupleApply(tree: Tree): Boolean = tree match case sel: Select => - if definitions.isTupleNType(sel.symbol.info.finalResultType) then + if ctx.definitions.isTupleNType(sel.symbol.info.finalResultType) then sel match case Select(tupleClass: Ident, _) if !tupleClass.span.isZeroExtent && @@ -169,17 +235,17 @@ class PcInlayHintsProvider( if symbol.source == pos.source then LabelPart( label, - pos = Some(symbol.sourcePos.toLsp.getStart().nn), + pos = Some(symbol.sourcePos.toLsp.getStart().nn) ) else LabelPart( label, - symbol = SemanticdbSymbols.symbolName(symbol), + symbol = SemanticdbSymbols.symbolName(symbol) ) private def partsFromType( tpe: Type, - usedRenames: Map[Symbol, String], + usedRenames: Map[Symbol, String] ): List[LabelPart] = NamedPartsAccumulator(_ => true)(Nil, tpe) .filter(_.symbol != NoSymbol) @@ -193,7 +259,7 @@ end PcInlayHintsProvider object ImplicitConversion: def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context) = - if (params.implicitConversions()) { + if params.implicitConversions() then tree match case Apply(fun: Ident, args) if isSynthetic(fun) && args.exists(!_.span.isZeroExtent) => implicitConversion(fun, args) @@ -201,7 +267,7 @@ object ImplicitConversion: if name == nme.apply && isSynthetic(fun) && args.exists(!_.span.isZeroExtent) => implicitConversion(fun, args) case _ => None - } else None + else None private def isSynthetic(tree: Tree)(using Context) = tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit) @@ -210,77 +276,131 @@ object ImplicitConversion: args.lastOption.map(_.sourcePos).getOrElse(fun.sourcePos) Some( fun.symbol, - lastArgPos.withStart(fun.sourcePos.start), + lastArgPos.withStart(fun.sourcePos.start) ) -end ImplicitConversion object ImplicitParameters: def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context) = - if (params.implicitParameters()) { + if params.implicitParameters() then tree match - case Apply(fun, args) - if args.exists(isSyntheticArg) && !tree.sourcePos.span.isZeroExtent && !args.exists(isQuotes(_)) => - val (implicitArgs, providedArgs) = args.partition(isSyntheticArg) - val allImplicit = providedArgs.isEmpty || providedArgs.forall { - case Ident(name) => name == nme.MISSING - case _ => false - } - val pos = implicitArgs.head.sourcePos - Some(implicitArgs.map(_.symbol), pos, allImplicit) + case Apply(_, args) if hasImplicitArgs(tree, args) => implicitArgs(args) + case Inlined(Apply(_, args), _, _) if hasImplicitArgs(tree, args) => implicitArgs(args) case _ => None - } else None + else None + + private def hasImplicitArgs(tree: Tree, args: List[Tree])(using Context): Boolean = + args.exists(isSyntheticArg) && !tree.sourcePos.span.isZeroExtent && !args.exists(isQuotes) + + private def implicitArgs(args: List[Tree])(using Context): Option[(List[Tree], SourcePosition)] = + val found = args.filter(isSyntheticArg) + Option.when(found.nonEmpty)(found, found.head.sourcePos) @tailrec def isSyntheticArg(tree: Tree)(using Context): Boolean = tree match case tree: Ident => tree.span.isSynthetic && tree.symbol.isOneOf(Flags.GivenOrImplicit) - case Apply(fun, _ ) if tree.span.isZeroExtent => isSyntheticArg(fun) - case TypeApply(fun, _ ) if tree.span.isZeroExtent => isSyntheticArg(fun) + case Apply(fun, _) if tree.span.isZeroExtent => isSyntheticArg(fun) + case TypeApply(fun, _) if tree.span.isZeroExtent => isSyntheticArg(fun) case _ => false // Decorations for Quotes are rarely useful private def isQuotes(tree: Tree)(using Context) = tree.tpe.typeSymbol == defn.QuotesClass + def partsFromImplicitArgs(trees: List[Tree])(using Context): List[(String, Option[Symbol])] = + @tailrec + def recurseImplicitArgs( + currentArgs: List[Tree], + remainingArgsLists: List[List[Tree]], + parts: List[(String, Option[Symbol])] + ): List[(String, Option[Symbol])] = + (currentArgs, remainingArgsLists) match + case (Nil, Nil) => parts + case (Nil, headArgsList :: tailArgsList) => + if headArgsList.isEmpty then + recurseImplicitArgs( + headArgsList, + tailArgsList, + (")", None) :: parts + ) + else + recurseImplicitArgs( + headArgsList, + tailArgsList, + (", ", None) :: (")", None) :: parts + ) + case (arg :: remainingArgs, remainingArgsLists) => + arg match + case Apply(fun, args) => + val applyLabel = (fun.symbol.decodedName, Some(fun.symbol)) + recurseImplicitArgs( + args, + remainingArgs :: remainingArgsLists, + ("(", None) :: applyLabel :: parts + ) + case t if t.isTerm => + val termLabel = (t.symbol.decodedName, Some(t.symbol)) + if remainingArgs.isEmpty then + recurseImplicitArgs( + remainingArgs, + remainingArgsLists, + termLabel :: parts + ) + else + recurseImplicitArgs( + remainingArgs, + remainingArgsLists, + (", ", None) :: termLabel :: parts + ) + case _ => + recurseImplicitArgs( + remainingArgs, + remainingArgsLists, + parts + ) + ((")", None) :: recurseImplicitArgs( + trees, + Nil, + List(("(using ", None)) + )).reverse + end ImplicitParameters object ValueOf: def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context) = - if (params.implicitParameters()) { + if params.implicitParameters() then tree match case Apply(ta @ TypeApply(fun, _), _) if fun.span.isSynthetic && isValueOf(fun) => Some( "new " + tpnme.valueOf.decoded.capitalize + "(...)", - fun.sourcePos, + fun.sourcePos ) case _ => None - } else None + else None private def isValueOf(tree: Tree)(using Context) = val symbol = tree.symbol.maybeOwner symbol.name.decoded == tpnme.valueOf.decoded.capitalize -end ValueOf object TypeParameters: def unapply(tree: Tree)(using params: InlayHintsParams, ctx: Context) = - if (params.typeParameters()) { + if params.typeParameters() then tree match case TypeApply(sel: Select, _) if sel.isForComprehensionMethod || sel.isInfix || - sel.symbol.name == nme.unapply => + sel.symbol.name == nme.unapply => None case TypeApply(fun, args) if inferredTypeArgs(args) => val tpes = args.map(_.tpe.stripTypeVar.widen.finalResultType) Some((tpes, fun.sourcePos.endPos, fun)) case _ => None - } else None + else None private def inferredTypeArgs(args: List[Tree]): Boolean = args.forall { case tt: TypeTree if tt.span.exists && !tt.span.isZeroExtent => true case _ => false } -end TypeParameters object InferredType: opaque type Text = Array[Char] @@ -288,7 +408,7 @@ object InferredType: def apply(text: Array[Char]): Text = text def unapply(tree: Tree)(using params: InlayHintsParams, text: Text, ctx: Context) = - if (params.inferredTypes()) { + if params.inferredTypes() then tree match case vd @ ValDef(_, tpe, _) if isValidSpan(tpe.span, vd.nameSpan) && @@ -308,11 +428,11 @@ object InferredType: else None case bd @ Bind( name, - Ident(nme.WILDCARD), + Ident(nme.WILDCARD) ) if !bd.span.isZeroExtent && bd.symbol.isTerm && params.hintsInPatternMatch() => Some(bd.symbol.info, bd.namePos, bd) case _ => None - } else None + else None private def isNotInUnapply(vd: ValDef)(using Context) = vd.rhs.span.exists && vd.rhs.span.start > vd.nameSpan.end @@ -331,3 +451,150 @@ object InferredType: index >= 0 && index < afterDef.size && afterDef(index) == '@' 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 = + 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 = + val isInfixSelect = fun match + case Select(sel, _) => sel.isInfix + case _ => false + val source = fun.source + if args.isEmpty then isInfixSelect + else + (!(fun.span.end until args.head.span.start) + .map(source.apply) + .contains('.') && fun.symbol.is(Flags.ExtensionMethod)) || isInfixSelect + + def isRealApply(tree: Tree) = + !tree.symbol.isOneOf(Flags.GivenOrImplicit) && !tree.span.isZeroExtent + + def getUnderlyingFun(tree: Tree): Tree = + tree match + case Apply(fun, _) => getUnderlyingFun(fun) + case TypeApply(fun, _) => getUnderlyingFun(fun) + case t => t + + @tailrec + def isDefaultArg(arg: Tree): Boolean = arg match + case Ident(name) => name.is(DefaultGetterName) + case Select(_, name) => name.is(DefaultGetterName) + case Apply(fun, _) => isDefaultArg(fun) + case _ => false + + if params.namedParameters() || params.byNameParameters() then + tree match + case Apply(fun, args) if isRealApply(fun) => + val underlyingFun = getUnderlyingFun(fun) + if shouldSkipFun(underlyingFun) then + None + else + val funTp = fun.typeOpt.widenTermRefExpr + val paramNames = funTp.paramNamess.flatten + val paramInfos = funTp.paramInfoss.flatten + + Some( + isInfixFun(fun, args) || underlyingFun.isInfix, + args + .zip(paramNames) + .zip(paramInfos) + .collect { + case ((arg, paramName), paramInfo) if !arg.span.isZeroExtent && !isDefaultArg(arg) => + (paramName.fieldName, arg.sourcePos, paramInfo.isByName) + } + ) + 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 apply @ Apply(inner, _) + if !apply.span.isZeroExtent && inner.sourcePos.exists && !isParentOnSameLine && + !isParentApply && endsInSimpleSelect(apply) && isEndOfLine(tree.sourcePos) => + Some((apply.tpe.widen.finalResultType.deepDealiasAndSimplify, tree.sourcePos)) + /* + innerTree + .select + */ + case select @ Select(innerTree, _) + if !select.span.isZeroExtent && innerTree.sourcePos.exists && + !isParentOnSameLine && !isParentApply && isEndOfLine(tree.sourcePos) => + val tpe = parent match + case Some(ta: TypeApply) => ta.tpe + case _ => select.tpe + Some((tpe.widen.finalResultType.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 + +object ClosingLabel: + def unapply(tree: Tree)( + using params: InlayHintsParams, + ctx: Context + ): Option[(String, SourcePosition)] = + if params.closingLabels() then + tree match + case tree: DefDef if !isIgnored(tree.symbol) && endsWithBrace(tree) => + Some((tree.symbol.decodedName, tree.sourcePos)) + case tree: TypeDef if tree.isClassDef && endsWithBrace(tree) => + Some((tree.symbol.decodedName, tree.sourcePos)) + case _ => None + else None + + private def isIgnored(sym: Symbol)(using Context): Boolean = + sym.is(Flags.Synthetic) || sym.isConstructor || samePosAsOwner(sym) + + private def samePosAsOwner(sym: Symbol)(using Context): Boolean = + val owner = sym.owner + sym.span == owner.span + + private def endsWithBrace(tree: Tree)(using Context): Boolean = + val pos = tree.sourcePos + pos.exists && !pos.span.isZeroExtent && pos.source(pos.end - 1) == '}' +end ClosingLabel diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcInlineValueProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlineValueProvider.scala new file mode 100644 index 000000000000..76c4293cf942 --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/pc/PcInlineValueProvider.scala @@ -0,0 +1,335 @@ +package dotty.tools.pc + +import scala.meta.internal.pc.Definition +import scala.meta.internal.pc.InlineValueProvider +import scala.meta.internal.pc.InlineValueProvider.Errors +import scala.meta.internal.pc.RangeOffset +import scala.meta.internal.pc.Reference +import scala.meta.pc.OffsetParams + +import dotty.tools.dotc.ast.NavigateAST +import dotty.tools.dotc.ast.tpd.* +import dotty.tools.dotc.ast.untpd +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Flags.* +import dotty.tools.dotc.core.StdNames +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.interactive.Interactive +import dotty.tools.dotc.interactive.InteractiveDriver +import dotty.tools.dotc.util.SourceFile +import dotty.tools.dotc.util.SourcePosition +import dotty.tools.pc.IndexedContext.Result +import dotty.tools.pc.utils.InteractiveEnrichments.* + +import org.eclipse.lsp4j as l + +final class PcInlineValueProvider( + driver: InteractiveDriver, + val params: OffsetParams +) extends WithSymbolSearchCollector[Option[Occurence]](driver, params): + + // We return a result or an error + def getInlineTextEdits(): Either[String, List[l.TextEdit]] = + defAndRefs() match + case Right((defn, refs)) => + val edits = + if defn.shouldBeRemoved then + val defEdit = definitionTextEdit(defn) + val refsEdits = refs.map(referenceTextEdit(defn)) + defEdit :: refsEdits + else refs.map(referenceTextEdit(defn)) + Right(edits) + case Left(error) => Left(error) + + private def referenceTextEdit( + definition: Definition + )(ref: Reference): l.TextEdit = + if definition.requiresCurlyBraces && ref.requiresCurlyBraces then + new l.TextEdit( + ref.range, + s"""{${ref.rhs}}""" + ) + else if definition.requiresBrackets && ref.requiresBrackets then + new l.TextEdit( + ref.range, + s"""(${ref.rhs})""" + ) + else new l.TextEdit(ref.range, ref.rhs) + + private def definitionTextEdit(definition: Definition): l.TextEdit = + new l.TextEdit( + extend( + definition.rangeOffsets.start, + definition.rangeOffsets.end, + definition.range + ), + "" + ) + + private def extend( + startOffset: Int, + endOffset: Int, + range: l.Range + ): l.Range = + val (startWithSpace, endWithSpace): (Int, Int) = + extendRangeToIncludeWhiteCharsAndTheFollowingNewLine( + text + )(startOffset, endOffset) + val startPos = new l.Position( + range.getStart.nn.getLine, + range.getStart.nn.getCharacter - (startOffset - startWithSpace) + ) + val endPos = + if endWithSpace - 1 >= 0 && text(endWithSpace - 1) == '\n' then + new l.Position(range.getEnd.nn.getLine + 1, 0) + else + new l.Position( + range.getEnd.nn.getLine, + range.getEnd.nn.getCharacter + endWithSpace - endOffset + ) + + new l.Range(startPos, endPos) + + val position: l.Position = pos.toLsp.getStart().nn + + override def collect(parent: Option[Tree])( + tree: Tree | EndMarker, + pos: SourcePosition, + sym: Option[Symbol] + ): Option[Occurence] = + tree match + case tree: Tree => + val (adjustedPos, _) = pos.adjust(text) + Some(Occurence(tree, parent, adjustedPos)) + case _ => None + + def defAndRefs(): Either[String, (Definition, List[Reference])] = + val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) + val allOccurences = result().flatten + for + definition <- allOccurences + .collectFirst { case Occurence(defn: ValDef, _, pos) => + DefinitionTree(defn, pos) + } + .toRight(Errors.didNotFindDefinition) + path = Interactive.pathTo(unit.tpdTree, definition.tree.rhs.span)(using newctx) + indexedContext = IndexedContext(definition.tree.namePos, path, newctx) + symbols = symbolsUsedInDefn(definition.tree.rhs, indexedContext) + references <- getReferencesToInline(definition, allOccurences, symbols) + yield + val (deleteDefinition, refsEdits) = references + + val defPos = definition.tree.sourcePos + val defEdit = Definition( + defPos.toLsp, + RangeOffset(defPos.start, defPos.end), + definitionRequiresBrackets(definition.tree.rhs)(using newctx), + definitionNeedsCurlyBraces(definition.tree.rhs), + deleteDefinition + ) + + (defEdit, refsEdits) + end for + end defAndRefs + + private def stripIndentPrefix( + rhs: String, + refIndent: String, + defIndent: String, + hasNextLineAfterEqualsSign: Boolean + ): String = + val rhsLines = rhs.split("\n").nn.toList + rhsLines match + case h :: Nil => rhs + case h :: t => + val header = if !hasNextLineAfterEqualsSign then h else s"\n$refIndent $h" + header.nn ++ t.map(refIndent ++ _.nn.stripPrefix(defIndent)).mkString("\n", "\n", "") + case Nil => rhs + + private def definitionRequiresBrackets(tree: Tree)(using Context): Boolean = + NavigateAST + .untypedPath(tree.span) + .headOption + .map { + case _: untpd.If => true + case _: untpd.Function => true + case _: untpd.Match => true + case _: untpd.ForYield => true + case _: untpd.InfixOp => true + case _: untpd.ParsedTry => true + case _: untpd.Try => true + case _: untpd.Block => true + case _: untpd.Typed => true + case _ => false + } + .getOrElse(false) + + end definitionRequiresBrackets + + private def definitionNeedsCurlyBraces(tree: Tree): Boolean = + tree match + case _: Ident => false + case _ => true + + private def referenceRequiresBrackets(tree: Tree)(using Context): Boolean = + NavigateAST.untypedPath(tree.span) match + case (_: untpd.InfixOp) :: _ => true + case _ => + tree match + case _: Apply => StdNames.nme.raw.isUnary(tree.symbol.name) + case _: Select => true + case _: Ident => true + case _ => false + + end referenceRequiresBrackets + + private def extendWithSurroundingParens(pos: SourcePosition) = + /** Move `point` by `step` as long as the character at `point` is + * `acceptedChar` + */ + def extend(point: Int, acceptedChar: Char, step: Int): Int = + val newPoint = point + step + if newPoint > 0 && newPoint < text.length && + text(newPoint) == acceptedChar + then extend(newPoint, acceptedChar, step) + else point + val adjustedStart = extend(pos.start, '(', -1) + val adjustedEnd = extend(pos.end - 1, ')', 1) + 1 + text.slice(adjustedStart, adjustedEnd).mkString + + private def symbolsUsedInDefn(rhs: Tree, indexedContext: IndexedContext): Set[Symbol] = + // Check if the symbol is defined within the RHS expression itself + // (e.g., lambda parameters, local vals in blocks) + // Such symbols should not be checked for shadowing since they are + // locally bound and won't be affected by external shadowing + def isLocalToRhs(symbol: Symbol): Boolean = + symbol.sourcePos.exists && rhs.sourcePos.exists && + symbol.sourcePos.start >= rhs.sourcePos.start && + symbol.sourcePos.end <= rhs.sourcePos.end + + def collectNames( + symbols: Set[Symbol], + tree: Tree + ): Set[Symbol] = + tree match + case id: Ident + if !id.symbol.is(Synthetic) && !id.symbol.is(Implicit) && !isLocalToRhs(id.symbol) => + symbols + tree.symbol + case sel: Select if !isLocalToRhs(sel.symbol) => + indexedContext.lookupSym(sel.symbol) match + case IndexedContext.Result.InScope => symbols + sel.symbol + case _ => symbols + case _ => symbols + + val traverser = new DeepFolder[Set[Symbol]](collectNames) + traverser(Set(), rhs) + + private def getReferencesToInline( + definition: DefinitionTree, + allOccurences: List[Occurence], + symbols: Set[Symbol] + ): Either[String, (Boolean, List[Reference])] = + val defIsLocal = definition.tree.symbol.ownersIterator + .drop(1) + .exists(e => e.isTerm) + def allreferences = allOccurences.filterNot(_.isDefn) + def inlineAll() = + makeRefsEdits(allreferences, symbols, definition).map((true, _)) + if definition.tree.sourcePos.toLsp.encloses(position) + then if defIsLocal then inlineAll() else Left(Errors.notLocal) + else + allreferences match + case ref :: Nil if defIsLocal => inlineAll() + case list => + for + ref <- list + .find(_.pos.toLsp.encloses(position)) + .toRight(Errors.didNotFindReference) + refEdits <- makeRefsEdits(List(ref), symbols, definition) + yield (false, refEdits) + + extension (pos: SourcePosition) + def startColumnIndentPadding: String = + val source = pos.source + val offset = pos.start + var idx = source.startOfLine(offset) + val pad = new StringBuilder + while idx != offset && idx < source.content().length && source.content()(idx).isWhitespace do + pad.append(source.content()(idx)) + idx += 1 + pad.result() + + private def makeRefsEdits( + refs: List[Occurence], + symbols: Set[Symbol], + definition: DefinitionTree + ): Either[String, List[Reference]] = + val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) + def buildRef(occurrence: Occurence): Either[String, Reference] = + val path = + Interactive.pathTo(unit.tpdTree, occurrence.pos.span)(using newctx) + val indexedContext = IndexedContext(pos, path, newctx) + import indexedContext.ctx + val conflictingSymbols = symbols + .withFilter { + indexedContext.lookupSym(_) match + case IndexedContext.Result.Conflict => true + case _ => false + } + .map(_.fullNameBackticked) + val hasNextLineAfterEqualsSign = + definition.tree.sourcePos.startLine != definition.tree.rhs.sourcePos.startLine + if conflictingSymbols.isEmpty then + Right( + Reference( + occurrence.pos.toLsp, + stripIndentPrefix( + extendWithSurroundingParens(definition.tree.rhs.sourcePos), + occurrence.tree.startPos.startColumnIndentPadding, + definition.tree.startPos.startColumnIndentPadding, + hasNextLineAfterEqualsSign + ), + occurrence.parent.map(p => + RangeOffset(p.sourcePos.start, p.sourcePos.end) + ), + occurrence.parent + .map(p => referenceRequiresBrackets(p)(using newctx)) + .getOrElse(false), + occurrence.pos.start > 0 && text(occurrence.pos.start - 1) == '$' + ) + ) + else Left(Errors.variablesAreShadowed(conflictingSymbols.mkString(", "))) + refs.foldLeft((Right(List())): Either[String, List[Reference]])((acc, r) => + for + collectedEdits <- acc + currentEdit <- buildRef(r) + yield currentEdit :: collectedEdits + ) + +end PcInlineValueProvider + +case class Occurence(tree: Tree, parent: Option[Tree], pos: SourcePosition): + def isDefn = + tree match + case _: ValDef => true + case _ => false + +case class DefinitionTree(tree: ValDef, pos: SourcePosition) + +case class RangeOffset(start: Int, end: Int) + +case class Definition( + range: l.Range, + rangeOffsets: RangeOffset, + requiresBrackets: Boolean, + requiresCurlyBraces: Boolean, + shouldBeRemoved: Boolean +) + +case class Reference( + range: l.Range, + rhs: String, + parentOffsets: Option[RangeOffset], + requiresBrackets: Boolean, + requiresCurlyBraces: Boolean +) diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcInlineValueProviderImpl.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlineValueProviderImpl.scala deleted file mode 100644 index bbba44d0d84f..000000000000 --- a/presentation-compiler/src/main/dotty/tools/pc/PcInlineValueProviderImpl.scala +++ /dev/null @@ -1,206 +0,0 @@ -package dotty.tools.pc - -import scala.meta.internal.pc.Definition -import scala.meta.internal.pc.InlineValueProvider -import scala.meta.internal.pc.InlineValueProvider.Errors -import scala.meta.internal.pc.RangeOffset -import scala.meta.internal.pc.Reference -import scala.meta.pc.OffsetParams - -import dotty.tools.dotc.ast.NavigateAST -import dotty.tools.dotc.ast.tpd.* -import dotty.tools.dotc.ast.untpd -import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Flags.* -import dotty.tools.dotc.core.StdNames -import dotty.tools.dotc.core.Symbols.Symbol -import dotty.tools.dotc.interactive.Interactive -import dotty.tools.dotc.interactive.InteractiveDriver -import dotty.tools.dotc.util.SourcePosition -import dotty.tools.pc.utils.InteractiveEnrichments.* - -import org.eclipse.lsp4j as l - -final class PcInlineValueProviderImpl( - driver: InteractiveDriver, - val params: OffsetParams -) extends WithSymbolSearchCollector[Option[Occurence]](driver, params) - with InlineValueProvider: - - val position: l.Position = pos.toLsp.getStart().nn - - override def collect(parent: Option[Tree])( - tree: Tree | EndMarker, - pos: SourcePosition, - sym: Option[Symbol] - ): Option[Occurence] = - tree match - case tree: Tree => - val (adjustedPos, _) = pos.adjust(text) - Some(Occurence(tree, parent, adjustedPos)) - case _ => None - - override def defAndRefs(): Either[String, (Definition, List[Reference])] = - val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) - val allOccurences = result().flatten - for - definition <- allOccurences - .collectFirst { case Occurence(defn: ValDef, _, pos) => - DefinitionTree(defn, pos) - } - .toRight(Errors.didNotFindDefinition) - symbols = symbolsUsedInDefn(definition.tree.rhs) - references <- getReferencesToInline(definition, allOccurences, symbols) - yield - val (deleteDefinition, refsEdits) = references - - val defPos = definition.tree.sourcePos - val defEdit = Definition( - defPos.toLsp, - adjustRhs(definition.tree.rhs.sourcePos), - RangeOffset(defPos.start, defPos.end), - definitionRequiresBrackets(definition.tree.rhs)(using newctx), - deleteDefinition - ) - - (defEdit, refsEdits) - end for - end defAndRefs - - private def definitionRequiresBrackets(tree: Tree)(using Context): Boolean = - NavigateAST - .untypedPath(tree.span) - .headOption - .map { - case _: untpd.If => true - case _: untpd.Function => true - case _: untpd.Match => true - case _: untpd.ForYield => true - case _: untpd.InfixOp => true - case _: untpd.ParsedTry => true - case _: untpd.Try => true - case _: untpd.Block => true - case _: untpd.Typed => true - case _ => false - } - .getOrElse(false) - - end definitionRequiresBrackets - - private def referenceRequiresBrackets(tree: Tree)(using Context): Boolean = - NavigateAST.untypedPath(tree.span) match - case (_: untpd.InfixOp) :: _ => true - case _ => - tree match - case _: Apply => StdNames.nme.raw.isUnary(tree.symbol.name) - case _: Select => true - case _: Ident => true - case _ => false - - end referenceRequiresBrackets - - private def adjustRhs(pos: SourcePosition) = - def extend(point: Int, acceptedChar: Char, step: Int): Int = - val newPoint = point + step - if newPoint > 0 && newPoint < text.length && text( - newPoint - ) == acceptedChar - then extend(newPoint, acceptedChar, step) - else point - val adjustedStart = extend(pos.start, '(', -1) - val adjustedEnd = extend(pos.end - 1, ')', 1) + 1 - text.slice(adjustedStart, adjustedEnd).mkString - - private def symbolsUsedInDefn( - rhs: Tree - ): List[Symbol] = - def collectNames( - symbols: List[Symbol], - tree: Tree - ): List[Symbol] = - tree match - case id: (Ident | Select) - if !id.symbol.is(Synthetic) && !id.symbol.is(Implicit) => - tree.symbol :: symbols - case _ => symbols - - val traverser = new DeepFolder[List[Symbol]](collectNames) - traverser(List(), rhs) - end symbolsUsedInDefn - - private def getReferencesToInline( - definition: DefinitionTree, - allOccurences: List[Occurence], - symbols: List[Symbol] - ): Either[String, (Boolean, List[Reference])] = - val defIsLocal = definition.tree.symbol.ownersIterator - .drop(1) - .exists(e => e.isTerm) - def allreferences = allOccurences.filterNot(_.isDefn) - def inlineAll() = - makeRefsEdits(allreferences, symbols).map((true, _)) - if definition.tree.sourcePos.toLsp.encloses(position) - then if defIsLocal then inlineAll() else Left(Errors.notLocal) - else - allreferences match - case ref :: Nil if defIsLocal => inlineAll() - case list => - for - ref <- list - .find(_.pos.toLsp.encloses(position)) - .toRight(Errors.didNotFindReference) - refEdits <- makeRefsEdits(List(ref), symbols) - yield (false, refEdits) - end if - end getReferencesToInline - - private def makeRefsEdits( - refs: List[Occurence], - symbols: List[Symbol] - ): Either[String, List[Reference]] = - val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) - def buildRef(occurrence: Occurence): Either[String, Reference] = - val path = - Interactive.pathTo(unit.tpdTree, occurrence.pos.span)(using newctx) - val indexedContext = IndexedContext( - Interactive.contextOfPath(path)(using newctx) - ) - import indexedContext.ctx - val conflictingSymbols = symbols - .withFilter { - indexedContext.lookupSym(_) match - case IndexedContext.Result.Conflict => true - case _ => false - } - .map(_.fullNameBackticked) - if conflictingSymbols.isEmpty then - Right( - Reference( - occurrence.pos.toLsp, - occurrence.parent.map(p => - RangeOffset(p.sourcePos.start, p.sourcePos.end) - ), - occurrence.parent - .map(p => referenceRequiresBrackets(p)(using newctx)) - .getOrElse(false) - ) - ) - else Left(Errors.variablesAreShadowed(conflictingSymbols.mkString(", "))) - end buildRef - refs.foldLeft((Right(List())): Either[String, List[Reference]])((acc, r) => - for - collectedEdits <- acc - currentEdit <- buildRef(r) - yield currentEdit :: collectedEdits - ) - end makeRefsEdits - -end PcInlineValueProviderImpl - -case class Occurence(tree: Tree, parent: Option[Tree], pos: SourcePosition): - def isDefn = - tree match - case _: ValDef => true - case _ => false - -case class DefinitionTree(tree: ValDef, pos: SourcePosition) diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcReferencesProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcReferencesProvider.scala index 49ed313faec4..ce892afc237a 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcReferencesProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcReferencesProvider.scala @@ -1,10 +1,9 @@ package dotty.tools.pc -import scala.language.unsafeNulls - import scala.jdk.CollectionConverters.* - +import scala.language.unsafeNulls import scala.meta.internal.metals.CompilerOffsetParams +import scala.meta.internal.pc.PcReferencesResult import scala.meta.pc.ReferencesRequest import scala.meta.pc.ReferencesResult @@ -13,20 +12,20 @@ import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.interactive.InteractiveDriver import dotty.tools.dotc.util.SourcePosition +import dotty.tools.pc.utils.InteractiveEnrichments.* + import org.eclipse.lsp4j import org.eclipse.lsp4j.Location -import dotty.tools.pc.utils.InteractiveEnrichments.* -import scala.meta.internal.pc.PcReferencesResult class PcReferencesProvider( driver: InteractiveDriver, - request: ReferencesRequest, + request: ReferencesRequest ) extends WithCompilationUnit(driver, request.file()) with PcCollector[Option[(String, Option[lsp4j.Range])]]: override def allowZeroExtentImplicits: Boolean = true private def soughtSymbols = - if(request.offsetOrSymbol().isLeft()) { + if request.offsetOrSymbol().isLeft() then val offsetParams = CompilerOffsetParams( request.file().uri(), request.file().text(), @@ -34,14 +33,13 @@ class PcReferencesProvider( ) val symbolSearch = new WithCompilationUnit(driver, offsetParams) with PcSymbolSearch symbolSearch.soughtSymbols.map(_._1) - } else { + else SymbolProvider.compilerSymbol(request.offsetOrSymbol().getRight()).map(symbolAlternatives(_)) - } def collect(parent: Option[Tree])( tree: Tree | EndMarker, toAdjust: SourcePosition, - symbol: Option[Symbol], + symbol: Option[Symbol] ): Option[(String, Option[lsp4j.Range])] = val (pos, _) = toAdjust.adjust(text) tree match @@ -57,13 +55,13 @@ class PcReferencesProvider( soughtSymbols match case Some(sought) if sought.nonEmpty => resultWithSought(sought) - .flatten - .groupMap(_._1) { case (_, optRange) => - optRange.map(new Location(request.file().uri().toString(), _)) - } - .map { case (symbol, locs) => - PcReferencesResult(symbol, locs.flatten.asJava) - } - .toList + .flatten + .groupMap(_._1) { case (_, optRange) => + optRange.map(new Location(request.file().uri().toString(), _)) + } + .map { case (symbol, locs) => + PcReferencesResult(symbol, locs.flatten.asJava) + } + .toList case _ => Nil end PcReferencesProvider diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcRenameProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcRenameProvider.scala index 666ccf9c614f..c6a816848492 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcRenameProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcRenameProvider.scala @@ -18,11 +18,19 @@ final class PcRenameProvider( name: Option[String] ) extends WithSymbolSearchCollector[l.TextEdit](driver, params): private val forbiddenMethods = - Set("equals", "hashCode", "unapply", "unary_!", "!") + Set("equals", "hashCode", "unapply", "apply", "", "unary_!", "!") + + private val soughtSymbolNames = soughtSymbols match + case Some((symbols, _)) => + symbols.filterNot(_.isError).map(symbol => symbol.decodedName.toString) + case None => Set.empty[String] + def canRenameSymbol(sym: Symbol)(using Context): Boolean = - (!sym.is(Method) || !forbiddenMethods(sym.decodedName)) - && (sym.ownersIterator.drop(1).exists(ow => ow.is(Method)) - || sym.source.path.isWorksheet) + val decodedName = sym.decodedName + def isForbiddenMethod = sym.is(Method) && forbiddenMethods(decodedName) + def local = sym.ownersIterator.drop(1).exists(ow => ow.is(Method)) + def isInWorksheet = sym.source.path.isWorksheet + !isForbiddenMethod && (local || isInWorksheet) && soughtSymbolNames(decodedName) def prepareRename(): Option[l.Range] = soughtSymbols.flatMap((symbols, pos) => @@ -40,12 +48,10 @@ final class PcRenameProvider( pos.toLsp, if stripBackticks then newName.stripBackticks else newName ) - end collect def rename(): List[l.TextEdit] = val (symbols, _) = soughtSymbols.getOrElse(Set.empty, pos) if symbols.nonEmpty && symbols.forall(canRenameSymbol(_)) then result() else Nil - end rename end PcRenameProvider diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcSemanticTokensProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcSemanticTokensProvider.scala index 216d9318197b..39d5d62ab7a2 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcSemanticTokensProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcSemanticTokensProvider.scala @@ -17,20 +17,18 @@ import dotty.tools.pc.utils.InteractiveEnrichments.* import org.eclipse.lsp4j.SemanticTokenModifiers import org.eclipse.lsp4j.SemanticTokenTypes -/** - * Provides semantic tokens of file(@param params) - * according to the LSP specification. +/** Provides semantic tokens of file(@param params) according to the LSP + * specification. */ final class PcSemanticTokensProvider( driver: InteractiveDriver, params: VirtualFileParams ): - /** - * Declaration is set for: - * 1. parameters, - * 2. defs/vals/vars without rhs, - * 3. type parameters, - * In all those cases we don't have a specific value for sure. + /** Declaration is set for: + * 1. parameters, + * 2. defs/vals/vars without rhs, + * 3. type parameters, In all those cases we don't have a specific value + * for sure. */ private def isDeclaration(tree: Tree | EndMarker) = tree match case df: ValOrDefDef => df.rhs.isEmpty @@ -40,14 +38,13 @@ final class PcSemanticTokensProvider( case _ => df.rhs.isEmpty case _ => false - /** - * Definition is set for: - * 1. defs/vals/vars/type with rhs. - * 2. pattern matches + /** Definition is set for: + * 1. defs/vals/vars/type with rhs. + * 2. pattern matches * - * We don't want to set it for enum cases despite the fact - * that the compiler sees them as vals, as it's not clear - * if they should be declaration/definition at all. + * We don't want to set it for enum cases despite the fact that the compiler + * sees them as vals, as it's not clear if they should be + * declaration/definition at all. */ private def isDefinition(tree: Tree | EndMarker) = tree match case _: EndMarker => true @@ -65,10 +62,10 @@ final class PcSemanticTokensProvider( parent: Option[Tree] )(tree: Tree | EndMarker, pos: SourcePosition, symbol: Option[Symbol]): Option[Node] = val sym = - tree match - case tree: Tree => - symbol.fold(tree.symbol)(identity) - case EndMarker(sym) => sym + tree match + case tree: Tree => + symbol.fold(tree.symbol)(identity) + case EndMarker(sym) => sym if !pos.exists || sym == NoSymbol then None else Some( @@ -79,8 +76,6 @@ final class PcSemanticTokensProvider( isDeclaration = isDeclaration(tree) ) ) - end collect - end Collector given Context = Collector.ctx @@ -129,7 +124,7 @@ final class PcSemanticTokensProvider( if sym.isGetter | sym.isSetter then getTypeId(SemanticTokenTypes.Variable) else getTypeId(SemanticTokenTypes.Method) // "def" - else if sym.isTerm && sym.info.typeSymbol.is(Flags.Module) then + else if sym.isTerm && sym.info.typeSymbol.is(Flags.Module) then getTypeId(SemanticTokenTypes.Class) // "class" else if sym.isTerm && (!sym.is(Flags.Param) || sym.is(Flags.ParamAccessor)) diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcSymbolSearch.scala b/presentation-compiler/src/main/dotty/tools/pc/PcSymbolSearch.scala index fd3d74f16c16..15a2cdd5cfe0 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcSymbolSearch.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcSymbolSearch.scala @@ -1,7 +1,5 @@ package dotty.tools.pc -import dotty.tools.pc.PcSymbolSearch.* - import dotty.tools.dotc.ast.NavigateAST import dotty.tools.dotc.ast.Positioned import dotty.tools.dotc.ast.tpd @@ -18,6 +16,7 @@ import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.interactive.Interactive import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.util.Spans.Span +import dotty.tools.pc.PcSymbolSearch.* import dotty.tools.pc.utils.InteractiveEnrichments.* trait PcSymbolSearch: @@ -49,7 +48,7 @@ trait PcSymbolSearch: lazy val soughtSymbols: Option[(Set[Symbol], SourcePosition)] = soughtSymbols(path) - def soughtSymbols(path: List[Tree]): Option[(Set[Symbol], SourcePosition)] = + private def soughtSymbols(path: List[Tree]): Option[(Set[Symbol], SourcePosition)] = val sought = path match /* reference of an extension paramter * extension [EF](<>: List[EF]) @@ -59,34 +58,44 @@ trait PcSymbolSearch: if id.symbol .is(Flags.Param) && id.symbol.owner.is(Flags.ExtensionMethod) => Some(findAllExtensionParamSymbols(id.sourcePos, id.name, id.symbol)) - /** - * Workaround for missing symbol in: - * class A[T](a: T) - * val x = new <>(1) + + /** Workaround for missing symbol in: + * ``` + * class A[T](a: T) + * val x = new <>(1) + * ``` */ case t :: (n: New) :: (sel: Select) :: _ if t.symbol == NoSymbol && sel.symbol.isConstructor => Some(symbolAlternatives(sel.symbol.owner), namePos(t)) - /** - * Workaround for missing symbol in: - * class A[T](a: T) - * val x = <>[Int](1) + + /** Workaround for missing symbol in: + * ``` + * class A[T](a: T) + * val x = <>[Int](1) + * ``` */ case (sel @ Select(New(t), _)) :: (_: TypeApply) :: _ if sel.symbol.isConstructor => Some(symbolAlternatives(sel.symbol.owner), namePos(t)) /* simple identifier: - * val a = val@@ue + value + * ``` + * val a = val@@ue + value + * ``` */ case (id: Ident) :: _ => Some(symbolAlternatives(id.symbol), id.sourcePos) /* simple selector: - * object.val@@ue + * ``` + * object.val@@ue + * ``` */ case (sel: Select) :: _ if selectNameSpan(sel).contains(pos.span) => Some(symbolAlternatives(sel.symbol), pos.withSpan(sel.nameSpan)) /* named argument: - * foo(nam@@e = "123") + * ``` + * foo(nam@@e = "123") + * ``` */ case (arg: NamedArg) :: (appl: Apply) :: _ => val realName = arg.name.stripModuleClassSuffix.lastPart @@ -109,31 +118,37 @@ trait PcSymbolSearch: s.owner.owner.info.member(s.name).symbol ) .filter(_ != NoSymbol), - pos, + pos ) else (Set(s), pos) } else None - end if /* all definitions: - * def fo@@o = ??? - * class Fo@@o = ??? + * ``` + * def fo@@o = ??? + * class Fo@@o = ??? + * ``` * etc. */ case (df: NamedDefTree) :: _ if df.nameSpan.contains(pos.span) && !isGeneratedGiven(df, sourceText) => Some(symbolAlternatives(df.symbol), pos.withSpan(df.nameSpan)) /* enum cases with params - * enum Foo: - * case B@@ar[A](i: A) + * ``` + * enum Foo: + * case B@@ar[A](i: A) + * ``` */ case (df: NamedDefTree) :: Template(_, _, self, _) :: _ if (df.name == nme.apply || df.name == nme.unapply) && df.nameSpan.isZeroExtent => Some(symbolAlternatives(self.tpt.symbol), self.sourcePos) - /** - * For traversing annotations: - * @JsonNo@@tification("") - * def params() = ??? + + /** For traversing annotations: + * + * ``` + * @JsonNo@@tification("") + * def params() = ??? + * ``` */ case (df: MemberDef) :: _ if df.span.contains(pos.span) => val annotTree = df.mods.annotations.find { t => @@ -146,9 +161,9 @@ trait PcSymbolSearch: }.headOption /* Import selectors: - * import scala.util.Tr@@y + * `import scala.util.Tr@@y` */ - case (imp: Import) :: _ if imp.span.contains(pos.span) => + case (imp: ImportOrExport) :: _ if imp.span.contains(pos.span) => imp .selector(pos.span) .map(sym => (symbolAlternatives(sym), sym.sourcePos)) @@ -166,9 +181,7 @@ trait PcSymbolSearch: extMethods: ExtMethods ): Option[ExtensionParamOccurence] = NavigateAST - .pathTo(pos.span, extMethods.paramss.flatten)(using - compilatonUnitContext - ) + .pathTo(pos.span, extMethods.paramss.flatten)(using compilatonUnitContext) .collectFirst { case v: untpd.ValOrTypeDef => ExtensionParamOccurence( @@ -188,17 +201,16 @@ trait PcSymbolSearch: for extensionMethodScope <- extensionMethods - occurrence <- collectParams(extensionMethodScope) - symbols <- collectAllExtensionParamSymbols( + occurrence <- collectParams(extensionMethodScope) + symbols <- collectAllExtensionParamSymbols( path.headOption.getOrElse(unit.tpdTree), occurrence ) yield symbols - end seekInExtensionParameters private def collectAllExtensionParamSymbols( tree: tpd.Tree, - occurrence: ExtensionParamOccurence, + occurrence: ExtensionParamOccurence ): Option[(Set[Symbol], SourcePosition)] = occurrence match case ExtensionParamOccurence(_, namePos, symbol, _) @@ -227,18 +239,17 @@ trait PcSymbolSearch: private def findAllExtensionParamSymbols( pos: SourcePosition, name: Name, - sym: Symbol, + sym: Symbol ) = val symbols = for methods <- extensionMethods.map(_.methods) symbols <- collectAllExtensionParamSymbols( unit.tpdTree, - ExtensionParamOccurence(name, pos, sym, methods), + ExtensionParamOccurence(name, pos, sym, methods) ) yield symbols symbols.getOrElse((symbolAlternatives(sym), pos)) - end findAllExtensionParamSymbols end PcSymbolSearch object PcSymbolSearch: @@ -268,8 +279,7 @@ object PcSymbolSearch: val nameSpan = df.nameSpan df.symbol.is(Flags.Given) && sourceText.substring( nameSpan.start, - nameSpan.end, + nameSpan.end ) != df.name.toString() end PcSymbolSearch - diff --git a/presentation-compiler/src/main/dotty/tools/pc/RawScalaPresentationCompiler.scala b/presentation-compiler/src/main/dotty/tools/pc/RawScalaPresentationCompiler.scala new file mode 100644 index 000000000000..09e647106b98 --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/pc/RawScalaPresentationCompiler.scala @@ -0,0 +1,373 @@ +package dotty.tools.pc + +import java.io.File +import java.net.URI +import java.nio.file.Path +import java.util.Optional +import java.util as ju + +import scala.jdk.CollectionConverters.* +import scala.language.unsafeNulls +import scala.meta.internal.metals.CompilerVirtualFileParams +import scala.meta.internal.metals.PcQueryContext +import scala.meta.internal.metals.ReportLevel +import scala.meta.internal.mtags.CommonMtagsEnrichments.* +import scala.meta.internal.pc.EmptySymbolSearch +import scala.meta.internal.pc.PresentationCompilerConfigImpl +import scala.meta.pc.* +import scala.meta.pc.PcSymbolInformation as IPcSymbolInformation +import scala.meta.pc.reports.EmptyReportContext +import scala.meta.pc.reports.ReportContext + +import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.interactive.InteractiveDriver +import dotty.tools.pc.InferExpectedType +import dotty.tools.pc.SymbolInformationProvider +import dotty.tools.pc.buildinfo.BuildInfo +import dotty.tools.pc.completions.CompletionProvider +import dotty.tools.pc.completions.OverrideCompletions + +import org.eclipse.lsp4j.DocumentHighlight +import org.eclipse.lsp4j.TextEdit +import org.eclipse.lsp4j as l + +/** The raw public API of the presentation compiler that does not handle + * synchronisation. Scala compiler can't run concurrent code at that point, so + * we need to enforce sequential, single threaded execution. + * + * It has to be implemented by the consumer of this API. + */ +case class RawScalaPresentationCompiler( + buildTargetIdentifier: String = "", + buildTargetName: Option[String] = None, + classpath: Seq[Path] = Nil, + options: List[String] = Nil, + search: SymbolSearch = EmptySymbolSearch, + config: PresentationCompilerConfig = PresentationCompilerConfigImpl(), + folderPath: Option[Path] = None, + reportsLevel: ReportLevel = ReportLevel.Info, + completionItemPriority: CompletionItemPriority = (_: String) => 0, + reportContext: ReportContext = EmptyReportContext(), + sourcePath: ju.function.Supplier[ju.List[Path]] = () => Nil.asJava, + semanticdbFileManager: SemanticdbFileManager = SemanticdbFileManager.EMPTY +) extends RawPresentationCompiler: + + def this() = this("uninitialized-presentation-compiler") + + given ReportContext = reportContext + + override def supportedCodeActions(): ju.List[String] = List( + CodeActionId.ConvertToNamedArguments, + CodeActionId.ImplementAbstractMembers, + CodeActionId.ExtractMethod, + CodeActionId.InlineValue, + CodeActionId.InsertInferredType, + CodeActionId.InsertInferredMethod, + PcConvertToNamedLambdaParameters.codeActionId, + ).asJava + + override val scalaVersion = BuildInfo.scalaVersion + + private val forbiddenOptions = Set("-print-lines", "-print-tasty") + private val forbiddenDoubleOptions = Set.empty[String] + + val driverSettings: List[String] = + val implicitSuggestionTimeout = List("-Ximport-suggestion-timeout", "0") + val defaultFlags = List("-color:never") + val filteredOptions = removeDoubleOptions(options.filterNot(forbiddenOptions)) + val classpathFlags = List("-classpath", classpath.mkString(File.pathSeparator)) + val sourcePathFlags = if config.sourcePathMode() != SourcePathMode.DISABLED then + List("-Ylogical-package-loading") + else Nil + filteredOptions ++ + defaultFlags ++ + implicitSuggestionTimeout ++ + classpathFlags ++ + sourcePathFlags + + lazy val driver: InteractiveDriver = + CachingDriver(driverSettings, sourcePath, semanticdbFileManager, config.sourcePathMode()) + + override def codeAction[T]( + params: OffsetParams, + codeActionId: String, + codeActionPayload: Optional[T] + ): ju.List[TextEdit] = + (codeActionId, codeActionPayload.asScala) match + case ( + CodeActionId.ConvertToNamedArguments, + Some(argIndices: ju.List[?]) + ) => + val payload = argIndices.asScala.collect { case i: Integer => i.toInt }.toSet + convertToNamedArguments(params, payload) + case (CodeActionId.ImplementAbstractMembers, _) => + implementAbstractMembers(params) + case (CodeActionId.InsertInferredType, _) => + insertInferredType(params) + case (CodeActionId.InsertInferredMethod, _) => + insertInferredMethod(params) + case (CodeActionId.InlineValue, _) => + inlineValue(params) + case (CodeActionId.ExtractMethod, Some(extractionPos: OffsetParams)) => + params match + case range: RangeParams => + extractMethod(range, extractionPos) + case _ => throw new IllegalArgumentException(s"Expected range parameters") + case (PcConvertToNamedLambdaParameters.codeActionId, _) => + PcConvertToNamedLambdaParameters(driver, params).convertToNamedLambdaParameters + case (id, _) => throw new IllegalArgumentException(s"Unsupported action id $id") + + override def withCompletionItemPriority( + priority: CompletionItemPriority + ): RawPresentationCompiler = + copy(completionItemPriority = priority) + + override def withBuildTargetName(buildTargetName: String): RawPresentationCompiler = + copy(buildTargetName = Some(buildTargetName)) + + override def withReportsLoggerLevel(level: String): RawPresentationCompiler = + copy(reportsLevel = ReportLevel.fromString(level)) + + private def removeDoubleOptions(options: List[String]): List[String] = + options match + case head :: _ :: tail if forbiddenDoubleOptions(head) => + removeDoubleOptions(tail) + case head :: tail => head :: removeDoubleOptions(tail) + case Nil => options + + override def semanticTokens( + params: VirtualFileParams + ): ju.List[Node] = + PcSemanticTokensProvider(driver, params).provide().asJava + + override def inlayHints( + params: InlayHintsParams + ): ju.List[l.InlayHint] = + PcInlayHintsProvider(driver, params, search) + .provide() + .asJava + + override def getTasty( + targetUri: URI, + isHttpEnabled: Boolean + ): String = + TastyUtils.getTasty(targetUri, isHttpEnabled) + + override def complete(params: OffsetParams, completionTriggerKind: l.CompletionTriggerKind): l.CompletionList = + CompletionProvider( + search, + driver, + () => InteractiveDriver(driverSettings, driver.logicalRootPackage), + params, + config, + buildTargetIdentifier, + folderPath, + completionItemPriority + ).completions() + + def definition(params: OffsetParams): DefinitionResult = + PcDefinitionProvider(driver, params, search).definitions() + + override def typeDefinition( + params: OffsetParams + ): DefinitionResult = + PcDefinitionProvider(driver, params, search).typeDefinitions() + + override def documentHighlight( + params: OffsetParams + ): ju.List[DocumentHighlight] = + PcDocumentHighlightProvider(driver, params).highlights.asJava + + override def references( + params: ReferencesRequest + ): ju.List[ReferencesResult] = + PcReferencesProvider(driver, params) + .references() + .asJava + + def inferExpectedType(params: OffsetParams): ju.Optional[String] = + InferExpectedType(search, driver, params).infer().asJava + + override def info( + symbol: String + ): Optional[IPcSymbolInformation] = + SymbolInformationProvider(using driver.currentCtx) + .info(symbol) + .map(_.asJava) + .asJava + + override def semanticdbTextDocument( + filename: URI, + code: String + ): Array[Byte] = + val provider = SemanticdbTextDocumentProvider(driver, folderPath) + provider.textDocument(filename, code) + + override def completionItemResolve( + item: l.CompletionItem, + symbol: String + ): l.CompletionItem = + CompletionItemResolver.resolve(item, symbol, search, config)(using driver.currentCtx) // FIX ME + + override def autoImports( + name: String, + params: scala.meta.pc.OffsetParams, + isExtension: java.lang.Boolean + ): ju.List[scala.meta.pc.AutoImportsResult] = + AutoImportsProvider( + search, + driver, + name, + params, + config, + buildTargetIdentifier + ) + .autoImports(isExtension) + .asJava + + override def implementAbstractMembers( + params: OffsetParams + ): ju.List[l.TextEdit] = + OverrideCompletions.implementAllAt( + params, + driver, + search, + config + ) + + override def insertInferredType( + params: OffsetParams + ): ju.List[l.TextEdit] = + InferredTypeProvider(params, driver, config, search) + .inferredTypeEdits() + .asJava + + private def insertInferredMethod( + params: OffsetParams + ): ju.List[l.TextEdit] = + InferredMethodProvider(params, driver, config, search) + .inferredMethodEdits() + .asJava + + override def inlineValue( + params: OffsetParams + ): ju.List[l.TextEdit] = + PcInlineValueProvider(driver, params).getInlineTextEdits() match + case Right(edits: List[TextEdit]) => edits.asJava + case Left(error: String) => throw new DisplayableException(error) + + override def extractMethod( + range: RangeParams, + extractionPos: OffsetParams + ): ju.List[l.TextEdit] = + ExtractMethodProvider( + range, + extractionPos, + driver, + search, + options.contains("-no-indent") + ) + .extractMethod() + .asJava + + override def convertToNamedArguments( + params: OffsetParams, + argIndices: ju.List[Integer] + ): ju.List[l.TextEdit] = + convertToNamedArguments(params, argIndices.asScala.toSet.map(_.toInt)) + + def convertToNamedArguments( + params: OffsetParams, + argIndices: Set[Int] + ): ju.List[l.TextEdit] = + ConvertToNamedArgumentsProvider( + driver, + params, + argIndices + ).convertToNamedArguments match + case Left(error: String) => throw new DisplayableException(error) + case Right(edits: List[l.TextEdit]) => edits.asJava + + override def selectionRange( + params: ju.List[OffsetParams] + ): ju.List[l.SelectionRange] = + SelectionRangeProvider( + driver, + params + ).selectionRange().asJava + + override def hover( + params: OffsetParams + ): ju.Optional[HoverSignature] = + HoverProvider.hover(params, driver, search, config.hoverContentType()) + + override def prepareRename( + params: OffsetParams + ): ju.Optional[l.Range] = + PcRenameProvider(driver, params, None).prepareRename().asJava + + override def rename( + params: OffsetParams, + name: String + ): ju.List[l.TextEdit] = + PcRenameProvider(driver, params, Some(name)).rename().asJava + + override def newInstance( + buildTargetIdentifier: String, + classpath: ju.List[Path], + options: ju.List[String], + sourcePath: ju.function.Supplier[ju.List[Path]] + ): RawPresentationCompiler = { + copy( + buildTargetIdentifier = buildTargetIdentifier, + classpath = classpath.asScala.toSeq, + options = options.asScala.toList, + sourcePath = sourcePath + ) + } + + override def newInstance( + buildTargetIdentifier: String, + classpath: ju.List[Path], + options: ju.List[String] + ): RawPresentationCompiler = + copy( + buildTargetIdentifier = buildTargetIdentifier, + classpath = classpath.asScala.toSeq, + options = options.asScala.toList + ) + + override def signatureHelp(params: OffsetParams): l.SignatureHelp = + SignatureHelpProvider.signatureHelp(driver, params, search) + + override def didChange(params: VirtualFileParams): ju.List[l.Diagnostic] = + DiagnosticProvider(driver, params).diagnostics().asJava + + override def didClose(uri: URI): Unit = + driver.close(uri) + + override def semanticdbTextDocument(params: VirtualFileParams): Array[Byte] = + semanticdbTextDocument(params.uri, params.text) + + override def buildTargetId(): String = buildTargetIdentifier + + override def withConfiguration( + config: PresentationCompilerConfig + ): RawPresentationCompiler = + copy(config = config) + + override def withSearch(search: SymbolSearch): RawPresentationCompiler = + copy(search = search) + + override def withReportContext(reportContext: ReportContext): RawPresentationCompiler = + copy(reportContext = reportContext) + + override def withWorkspace(workspace: Path): RawPresentationCompiler = + copy(folderPath = Some(workspace)) + + override def withSemanticdbFileManager( + semanticdbFileManager: SemanticdbFileManager + ): RawPresentationCompiler = + copy(semanticdbFileManager = semanticdbFileManager) + +end RawScalaPresentationCompiler diff --git a/presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerAccess.scala b/presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerAccess.scala index 1443fbcf37cc..d3fedca5b81d 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerAccess.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerAccess.scala @@ -3,12 +3,12 @@ package dotty.tools.pc import java.util.concurrent.ScheduledExecutorService import scala.concurrent.ExecutionContextExecutor -import scala.meta.internal.metals.ReportContext import scala.meta.internal.pc.CompilerAccess import scala.meta.pc.PresentationCompilerConfig +import scala.meta.pc.reports.ReportContext -import dotty.tools.dotc.reporting.StoreReporter import dotty.tools.dotc.interactive.InteractiveDriver +import dotty.tools.dotc.reporting.StoreReporter class Scala3CompilerAccess( config: PresentationCompilerConfig, @@ -27,10 +27,9 @@ class Scala3CompilerAccess( def newReporter = new StoreReporter(null) - /** - * Handle the exception in order to make sure that - * we retry immediately. Otherwise, we will wait until - * the end of the timeout, which is 20s by default. + /** Handle the exception in order to make sure that we retry immediately. + * Otherwise, we will wait until the end of the timeout, which is 20s by + * default. */ protected def handleSharedCompilerException( t: Throwable diff --git a/presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerWrapper.scala b/presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerWrapper.scala index 968c144625a3..c773e28d814d 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerWrapper.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerWrapper.scala @@ -3,8 +3,8 @@ package dotty.tools.pc import scala.meta.internal.pc.CompilerWrapper import scala.meta.internal.pc.ReporterAccess -import dotty.tools.dotc.reporting.StoreReporter import dotty.tools.dotc.interactive.InteractiveDriver +import dotty.tools.dotc.reporting.StoreReporter class Scala3CompilerWrapper(driver: InteractiveDriver) extends CompilerWrapper[StoreReporter, InteractiveDriver]: diff --git a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala index 218d92c38ffa..a41eb618dbf9 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala @@ -11,14 +11,12 @@ import java.util as ju import scala.concurrent.ExecutionContext import scala.concurrent.ExecutionContextExecutor -import scala.jdk.CollectionConverters._ +import scala.jdk.CollectionConverters.* import scala.language.unsafeNulls import scala.meta.internal.metals.CompilerVirtualFileParams import scala.meta.internal.metals.EmptyCancelToken -import scala.meta.internal.metals.EmptyReportContext -import scala.meta.internal.metals.ReportContext +import scala.meta.internal.metals.PcQueryContext import scala.meta.internal.metals.ReportLevel -import scala.meta.internal.metals.StdReportContext import scala.meta.internal.mtags.CommonMtagsEnrichments.* import scala.meta.internal.pc.CompilerAccess import scala.meta.internal.pc.DefinitionResultImpl @@ -26,21 +24,23 @@ import scala.meta.internal.pc.EmptyCompletionList import scala.meta.internal.pc.EmptySymbolSearch import scala.meta.internal.pc.PresentationCompilerConfigImpl import scala.meta.pc.* -import scala.meta.pc.{PcSymbolInformation as IPcSymbolInformation} +import scala.meta.pc.PcSymbolInformation as IPcSymbolInformation +import scala.meta.pc.reports.EmptyReportContext +import scala.meta.pc.reports.ReportContext +import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.interactive.InteractiveDriver import dotty.tools.dotc.reporting.StoreReporter -import dotty.tools.pc.completions.CompletionProvider import dotty.tools.pc.InferExpectedType -import dotty.tools.pc.completions.OverrideCompletions -import dotty.tools.pc.buildinfo.BuildInfo import dotty.tools.pc.SymbolInformationProvider -import dotty.tools.dotc.interactive.InteractiveDriver +import dotty.tools.pc.buildinfo.BuildInfo +import dotty.tools.pc.completions.CompletionProvider +import dotty.tools.pc.completions.OverrideCompletions import org.eclipse.lsp4j.DocumentHighlight import org.eclipse.lsp4j.TextEdit import org.eclipse.lsp4j as l - case class ScalaPresentationCompiler( buildTargetIdentifier: String = "", buildTargetName: Option[String] = None, @@ -53,8 +53,23 @@ case class ScalaPresentationCompiler( folderPath: Option[Path] = None, reportsLevel: ReportLevel = ReportLevel.Info, completionItemPriority: CompletionItemPriority = (_: String) => 0, + reportContext: ReportContext = EmptyReportContext(), + sourcePath: ju.function.Supplier[ju.List[Path]] = () => Nil.asJava, + semanticdbFileManager: SemanticdbFileManager = SemanticdbFileManager.EMPTY ) extends PresentationCompiler: + given ReportContext = reportContext + + override def supportedCodeActions(): ju.List[String] = List( + CodeActionId.ConvertToNamedArguments, + CodeActionId.ImplementAbstractMembers, + CodeActionId.ExtractMethod, + CodeActionId.InlineValue, + CodeActionId.InsertInferredType, + CodeActionId.InsertInferredMethod, + PcConvertToNamedLambdaParameters.codeActionId, + ).asJava + def this() = this("", None, Nil, Nil) val scalaVersion = BuildInfo.scalaVersion @@ -62,13 +77,45 @@ case class ScalaPresentationCompiler( private val forbiddenOptions = Set("-print-lines", "-print-tasty") private val forbiddenDoubleOptions = Set.empty[String] - given ReportContext = - folderPath - .map(StdReportContext(_, _ => buildTargetName, reportsLevel)) - .getOrElse(EmptyReportContext) + override def codeAction[T]( + params: OffsetParams, + codeActionId: String, + codeActionPayload: Optional[T] + ): CompletableFuture[ju.List[TextEdit]] = + (codeActionId, codeActionPayload.asScala) match + case ( + CodeActionId.ConvertToNamedArguments, + Some(argIndices: ju.List[?]) + ) => + val payload = + argIndices.asScala.collect { case i: Integer => i.toInt }.toSet + convertToNamedArguments(params, payload) + case (CodeActionId.ImplementAbstractMembers, _) => + implementAbstractMembers(params) + case (CodeActionId.InsertInferredType, _) => + insertInferredType(params) + case (CodeActionId.InsertInferredMethod, _) => + insertInferredMethod(params) + case (CodeActionId.InlineValue, _) => + inlineValue(params) + case (CodeActionId.ExtractMethod, Some(extractionPos: OffsetParams)) => + params match + case range: RangeParams => + extractMethod(range, extractionPos) + case _ => failedFuture(new IllegalArgumentException(s"Expected range parameters")) + case (PcConvertToNamedLambdaParameters.codeActionId, _) => + compilerAccess.withNonInterruptableCompiler(List.empty[l.TextEdit].asJava, params.token) { + access => PcConvertToNamedLambdaParameters(access.compiler(), params).convertToNamedLambdaParameters + }(params.toQueryContext) + case (id, _) => failedFuture(new IllegalArgumentException(s"Unsupported action id $id")) + + private def failedFuture[T](e: Throwable): CompletableFuture[T] = + val f = new CompletableFuture[T]() + f.completeExceptionally(e) + f override def withCompletionItemPriority( - priority: CompletionItemPriority + priority: CompletionItemPriority ): PresentationCompiler = copy(completionItemPriority = priority) @@ -78,20 +125,37 @@ case class ScalaPresentationCompiler( override def withReportsLoggerLevel(level: String): PresentationCompiler = copy(reportsLevel = ReportLevel.fromString(level)) + override def withSemanticdbFileManager( + semanticdbFileManager: SemanticdbFileManager + ): PresentationCompiler = + copy(semanticdbFileManager = semanticdbFileManager) + val compilerAccess: CompilerAccess[StoreReporter, InteractiveDriver] = Scala3CompilerAccess( config, sh, - () => new Scala3CompilerWrapper(CachingDriver(driverSettings)) + () => + new Scala3CompilerWrapper(CachingDriver( + driverSettings, + sourcePath, + semanticdbFileManager, + config.sourcePathMode() + )) )(using ec) - val driverSettings = + val driverSettings: List[String] = val implicitSuggestionTimeout = List("-Ximport-suggestion-timeout", "0") val defaultFlags = List("-color:never") val filteredOptions = removeDoubleOptions(options.filterNot(forbiddenOptions)) - - filteredOptions ::: defaultFlags ::: implicitSuggestionTimeout ::: "-classpath" :: classpath - .mkString(File.pathSeparator) :: Nil + val classpathFlags = List("-classpath", classpath.mkString(File.pathSeparator)) + val sourcePathFlags = if config.sourcePathMode() != SourcePathMode.DISABLED then + List("-Ylogical-package-loading") + else Nil + filteredOptions ++ + defaultFlags ++ + implicitSuggestionTimeout ++ + classpathFlags ++ + sourcePathFlags private def removeDoubleOptions(options: List[String]): List[String] = options match @@ -103,26 +167,26 @@ case class ScalaPresentationCompiler( override def semanticTokens( params: VirtualFileParams ): CompletableFuture[ju.List[Node]] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( new ju.ArrayList[Node](), params.token() ) { access => val driver = access.compiler() new PcSemanticTokensProvider(driver, params).provide().asJava - } + }(params.toQueryContext) override def inlayHints( params: InlayHintsParams ): ju.concurrent.CompletableFuture[ju.List[l.InlayHint]] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( new ju.ArrayList[l.InlayHint](), - params.token(), + params.token() ) { access => val driver = access.compiler() new PcInlayHintsProvider(driver, params, search) .provide() .asJava - } + }(params.toQueryContext) override def getTasty( targetUri: URI, @@ -133,7 +197,7 @@ case class ScalaPresentationCompiler( } def complete(params: OffsetParams): CompletableFuture[l.CompletionList] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( EmptyCompletionList(), params.token() ) { access => @@ -141,68 +205,67 @@ case class ScalaPresentationCompiler( new CompletionProvider( search, driver, - () => InteractiveDriver(driverSettings), + () => InteractiveDriver(driverSettings, driver.logicalRootPackage), params, config, buildTargetIdentifier, folderPath, completionItemPriority ).completions() - - } + }(params.toQueryContext) def definition(params: OffsetParams): CompletableFuture[DefinitionResult] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( DefinitionResultImpl.empty, params.token() ) { access => val driver = access.compiler() PcDefinitionProvider(driver, params, search).definitions() - } + }(params.toQueryContext) override def typeDefinition( params: OffsetParams ): CompletableFuture[DefinitionResult] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( DefinitionResultImpl.empty, params.token() ) { access => val driver = access.compiler() PcDefinitionProvider(driver, params, search).typeDefinitions() - } + }(params.toQueryContext) def documentHighlight( params: OffsetParams ): CompletableFuture[ju.List[DocumentHighlight]] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( List.empty[DocumentHighlight].asJava, params.token() ) { access => val driver = access.compiler() PcDocumentHighlightProvider(driver, params).highlights.asJava - } + }(params.toQueryContext) override def references( params: ReferencesRequest ): CompletableFuture[ju.List[ReferencesResult]] = - compilerAccess.withNonInterruptableCompiler(Some(params.file()))( + compilerAccess.withNonInterruptableCompiler( List.empty[ReferencesResult].asJava, - params.file().token, + params.file().token ) { access => val driver = access.compiler() PcReferencesProvider(driver, params) .references() .asJava - } + }(params.file().toQueryContext) def inferExpectedType(params: OffsetParams): CompletableFuture[ju.Optional[String]] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( Optional.empty(), - params.token, + params.token ) { access => val driver = access.compiler() new InferExpectedType(search, driver, params).infer().asJava - } + }(params.toQueryContext) def shutdown(): Unit = compilerAccess.shutdown() @@ -217,44 +280,40 @@ case class ScalaPresentationCompiler( symbol: String ): CompletableFuture[Optional[IPcSymbolInformation]] = compilerAccess.withNonInterruptableCompiler[Optional[IPcSymbolInformation]]( - None - )( Optional.empty(), - EmptyCancelToken, + EmptyCancelToken ) { access => SymbolInformationProvider(using access.compiler().currentCtx) .info(symbol) .map(_.asJava) .asJava - } + }(emptyQueryContext) def semanticdbTextDocument( filename: URI, code: String ): CompletableFuture[Array[Byte]] = val virtualFile = CompilerVirtualFileParams(filename, code) - compilerAccess.withNonInterruptableCompiler(Some(virtualFile))( + compilerAccess.withNonInterruptableCompiler( Array.empty[Byte], EmptyCancelToken ) { access => val driver = access.compiler() val provider = SemanticdbTextDocumentProvider(driver, folderPath) provider.textDocument(filename, code) - } + }(virtualFile.toQueryContext) def completionItemResolve( item: l.CompletionItem, symbol: String ): CompletableFuture[l.CompletionItem] = - compilerAccess.withNonInterruptableCompiler(None)( + compilerAccess.withNonInterruptableCompiler( item, EmptyCancelToken ) { access => val driver = access.compiler() - CompletionItemResolver.resolve(item, symbol, search, config)(using - driver.currentCtx - ) - } + CompletionItemResolver.resolve(item, symbol, search, config)(using driver.currentCtx) + }(emptyQueryContext) def autoImports( name: String, @@ -263,7 +322,7 @@ case class ScalaPresentationCompiler( ): CompletableFuture[ ju.List[scala.meta.pc.AutoImportsResult] ] = - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( List.empty[scala.meta.pc.AutoImportsResult].asJava, params.token() ) { access => @@ -278,13 +337,13 @@ case class ScalaPresentationCompiler( ) .autoImports(isExtension) .asJava - } + }(params.toQueryContext) def implementAbstractMembers( params: OffsetParams ): CompletableFuture[ju.List[l.TextEdit]] = val empty: ju.List[l.TextEdit] = new ju.ArrayList[l.TextEdit]() - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( empty, params.token() ) { pc => @@ -295,105 +354,120 @@ case class ScalaPresentationCompiler( search, config ) - } - end implementAbstractMembers + }(params.toQueryContext) override def insertInferredType( params: OffsetParams ): CompletableFuture[ju.List[l.TextEdit]] = val empty: ju.List[l.TextEdit] = new ju.ArrayList[l.TextEdit]() - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( empty, params.token() ) { pc => new InferredTypeProvider(params, pc.compiler(), config, search) .inferredTypeEdits() .asJava - } + }(params.toQueryContext) + + def insertInferredMethod( + params: OffsetParams + ): CompletableFuture[ju.List[l.TextEdit]] = + val empty: ju.List[l.TextEdit] = new ju.ArrayList[l.TextEdit]() + compilerAccess.withNonInterruptableCompiler( + empty, + params.token() + ) { pc => + new InferredMethodProvider(params, pc.compiler(), config, search) + .inferredMethodEdits() + .asJava + }(params.toQueryContext) override def inlineValue( params: OffsetParams ): CompletableFuture[ju.List[l.TextEdit]] = val empty: Either[String, List[l.TextEdit]] = Right(List()) (compilerAccess - .withInterruptableCompiler(Some(params))(empty, params.token()) { pc => - new PcInlineValueProviderImpl(pc.compiler(), params) + .withInterruptableCompiler(empty, params.token()) { pc => + new PcInlineValueProvider(pc.compiler(), params) .getInlineTextEdits() - }) + }(params.toQueryContext)) .thenApply { case Right(edits: List[TextEdit]) => edits.asJava case Left(error: String) => throw new DisplayableException(error) } - end inlineValue override def extractMethod( range: RangeParams, extractionPos: OffsetParams ): CompletableFuture[ju.List[l.TextEdit]] = val empty: ju.List[l.TextEdit] = new ju.ArrayList[l.TextEdit]() - compilerAccess.withInterruptableCompiler(Some(range))(empty, range.token()) { + compilerAccess.withInterruptableCompiler(empty, range.token()) { pc => new ExtractMethodProvider( range, extractionPos, pc.compiler(), search, - options.contains("-no-indent"), + options.contains("-no-indent") ) .extractMethod() .asJava - } - end extractMethod + }(range.toQueryContext) override def convertToNamedArguments( params: OffsetParams, argIndices: ju.List[Integer] + ): CompletableFuture[ju.List[l.TextEdit]] = + convertToNamedArguments(params, argIndices.asScala.toSet.map(_.toInt)) + + def convertToNamedArguments( + params: OffsetParams, + argIndices: Set[Int] ): CompletableFuture[ju.List[l.TextEdit]] = val empty: Either[String, List[l.TextEdit]] = Right(List()) (compilerAccess - .withNonInterruptableCompiler(Some(params))(empty, params.token()) { pc => + .withNonInterruptableCompiler(empty, params.token()) { pc => new ConvertToNamedArgumentsProvider( pc.compiler(), params, - argIndices.asScala.map(_.toInt).toSet + argIndices ).convertToNamedArguments - }) + }(params.toQueryContext)) .thenApplyAsync { case Left(error: String) => throw new DisplayableException(error) case Right(edits: List[l.TextEdit]) => edits.asJava } - end convertToNamedArguments override def selectionRange( params: ju.List[OffsetParams] ): CompletableFuture[ju.List[l.SelectionRange]] = CompletableFuture.completedFuture { - compilerAccess.withSharedCompiler(params.asScala.headOption)( + compilerAccess.withSharedCompiler( List.empty[l.SelectionRange].asJava ) { pc => new SelectionRangeProvider( pc.compiler(), - params, + params ).selectionRange().asJava - } + }(params.asScala.headOption.map(_.toQueryContext).getOrElse(emptyQueryContext)) } end selectionRange def hover( params: OffsetParams ): CompletableFuture[ju.Optional[HoverSignature]] = - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( ju.Optional.empty[HoverSignature](), params.token() ) { access => val driver = access.compiler() HoverProvider.hover(params, driver, search, config.hoverContentType()) - } + }(params.toQueryContext) end hover def prepareRename( params: OffsetParams ): CompletableFuture[ju.Optional[l.Range]] = - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( Optional.empty[l.Range](), params.token() ) { access => @@ -401,21 +475,35 @@ case class ScalaPresentationCompiler( Optional.ofNullable( PcRenameProvider(driver, params, None).prepareRename().orNull ) - } + }(params.toQueryContext) def rename( params: OffsetParams, name: String ): CompletableFuture[ju.List[l.TextEdit]] = - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( List[l.TextEdit]().asJava, params.token() ) { access => val driver = access.compiler() PcRenameProvider(driver, params, Some(name)).rename().asJava - } + }(params.toQueryContext) + + override def newInstance( + buildTargetIdentifier: String, + classpath: ju.List[Path], + options: ju.List[String], + sourcePath: ju.function.Supplier[ju.List[Path]] + ): PresentationCompiler = { + copy( + buildTargetIdentifier = buildTargetIdentifier, + classpath = classpath.asScala.toSeq, + options = options.asScala.toList, + sourcePath = sourcePath + ) + } - def newInstance( + override def newInstance( buildTargetIdentifier: String, classpath: ju.List[Path], options: ju.List[String] @@ -427,24 +515,30 @@ case class ScalaPresentationCompiler( ) def signatureHelp(params: OffsetParams): CompletableFuture[l.SignatureHelp] = - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( new l.SignatureHelp(), params.token() ) { access => val driver = access.compiler() SignatureHelpProvider.signatureHelp(driver, params, search) - } + }(params.toQueryContext) override def didChange( params: VirtualFileParams ): CompletableFuture[ju.List[l.Diagnostic]] = - CompletableFuture.completedFuture(Nil.asJava) + compilerAccess.withNonInterruptableCompiler( + ju.Collections.emptyList(), + EmptyCancelToken + ) { access => + val driver = access.compiler() + DiagnosticProvider(driver, params).diagnostics().asJava + }(params.toQueryContext) override def didClose(uri: URI): Unit = - compilerAccess.withNonInterruptableCompiler(None)( + compilerAccess.withNonInterruptableCompiler( (), EmptyCancelToken - ) { access => access.compiler().close(uri) } + ) { access => access.compiler().close(uri) }(emptyQueryContext) override def withExecutorService( executorService: ExecutorService @@ -464,9 +558,27 @@ case class ScalaPresentationCompiler( def withSearch(search: SymbolSearch): PresentationCompiler = copy(search = search) + override def withReportContext(reportContext: ReportContext): PresentationCompiler = + copy(reportContext = reportContext) + def withWorkspace(workspace: Path): PresentationCompiler = copy(folderPath = Some(workspace)) override def isLoaded() = compilerAccess.isLoaded() + def additionalReportData() = + s"""|Scala version: $scalaVersion + |Classpath: + |${classpath + .map(path => s"$path [${if path.exists then "exists" else "missing"} ]") + .mkString(", ")} + |Options: + |${options.mkString(" ")} + |""".stripMargin + + extension (params: VirtualFileParams) + def toQueryContext = PcQueryContext(Some(params), additionalReportData) + + def emptyQueryContext = PcQueryContext(None, additionalReportData) + end ScalaPresentationCompiler diff --git a/presentation-compiler/src/main/dotty/tools/pc/SelectionRangeProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/SelectionRangeProvider.scala index 7973f4103ff6..6012139989b5 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/SelectionRangeProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/SelectionRangeProvider.scala @@ -3,10 +3,11 @@ package dotty.tools.pc import java.nio.file.Paths import java.util as ju -import scala.jdk.CollectionConverters._ +import scala.jdk.CollectionConverters.* import scala.meta.pc.OffsetParams -import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.ast.NavigateAST +import dotty.tools.dotc.ast.untpd.* import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.interactive.Interactive import dotty.tools.dotc.interactive.InteractiveDriver @@ -17,37 +18,35 @@ import dotty.tools.pc.utils.InteractiveEnrichments.* import org.eclipse.lsp4j import org.eclipse.lsp4j.SelectionRange -/** - * Provides the functionality necessary for the `textDocument/selectionRange` request. +/** Provides the functionality necessary for the `textDocument/selectionRange` + * request. * - * @param compiler Metals Global presentation compiler wrapper. - * @param params offset params converted from the selectionRange params. + * @param compiler Metals Global presentation compiler wrapper. + * @param params offset params converted from the selectionRange params. */ -class SelectionRangeProvider( - driver: InteractiveDriver, - params: ju.List[OffsetParams] -): +class SelectionRangeProvider(driver: InteractiveDriver, params: ju.List[OffsetParams]): - /** - * Get the seletion ranges for the provider params + /** Get the seletion ranges for the provider params * - * @return selection ranges + * @return selection ranges */ def selectionRange(): List[SelectionRange] = given ctx: Context = driver.currentCtx params.asScala.toList.map { param => - val uri = param.uri().nn val text = param.text().nn val filePath = Paths.get(uri) val source = SourceFile.virtual(filePath.toString, text) driver.run(uri, source) val pos = driver.sourcePosition(param) - val path = - Interactive.pathTo(driver.openedTrees(uri), pos)(using ctx) + val unit = driver.compilationUnits(uri) + + val untpdPath: List[Tree] = NavigateAST + .pathTo(pos.span, List(unit.untpdTree), true).collect: + case untpdTree: Tree => untpdTree - val bareRanges = path + val bareRanges = untpdPath .flatMap(selectionRangesFromTree(pos)) val comments = @@ -77,32 +76,48 @@ class SelectionRangeProvider( } end selectionRange - /** Given a tree, create a seq of [[SelectionRange]]s corresponding to that tree. */ - private def selectionRangesFromTree(pos: SourcePosition)(tree: tpd.Tree)(using Context) = + /** Given a tree, create a seq of [[SelectionRange]]s corresponding to that + * tree. + */ + private def selectionRangesFromTree(pos: SourcePosition)(tree: Tree)(using Context) = def toSelectionRange(srcPos: SourcePosition) = val selectionRange = new SelectionRange() selectionRange.setRange(srcPos.toLsp) selectionRange - val treeSelectionRange = toSelectionRange(tree.sourcePos) - - tree match - case tpd.DefDef(name, paramss, tpt, rhs) => - // If source position is within a parameter list, add a selection range covering that whole list. - val selectedParams = - paramss - .iterator - .flatMap: // parameter list to a sourcePosition covering the whole list - case Seq(param) => Some(param.sourcePos) - case params @ Seq(head, tail*) => - val srcPos = head.sourcePos - val lastSpan = tail.last.span - Some(SourcePosition(srcPos.source, srcPos.span union lastSpan, srcPos.outer)) - case Seq() => None - .find(_.contains(pos)) - .map(toSelectionRange) - selectedParams ++ Seq(treeSelectionRange) - case _ => Seq(treeSelectionRange) + def maybeToSelectionRange(srcPos: SourcePosition): Option[SelectionRange] = + if srcPos.contains(pos) then Some(toSelectionRange(srcPos)) else None + + val treeSelectionRange = Seq(toSelectionRange(tree.sourcePos)) + + def allArgsSelectionRange(args: List[Tree]): Option[SelectionRange] = + args match + case Nil => None + case list => + val srcPos = list.head.sourcePos + val lastSpan = list.last.span + val allArgsSrcPos = SourcePosition(srcPos.source, srcPos.span `union` lastSpan, srcPos.outer) + maybeToSelectionRange(allArgsSrcPos) + + val allSelectionRanges: Iterable[SelectionRange] = tree match + case vdef @ ValDef(_, _, _) => + maybeToSelectionRange(vdef.namePos) + case tdef @ TypeDef(_, _) => + maybeToSelectionRange(tdef.namePos) + case mdef @ ModuleDef(_, _) => + maybeToSelectionRange(mdef.namePos) + case ddef @ DefDef(_, paramss, _, _) => + maybeToSelectionRange(ddef.namePos) ++: paramss.flatMap(allArgsSelectionRange) + case Apply(_, args) => + allArgsSelectionRange(args) + case TypeApply(_, args) => + allArgsSelectionRange(args) + case AppliedTypeTree(_, args) => + allArgsSelectionRange(args) + case Function(args, _) => + allArgsSelectionRange(args) + case _ => Seq.empty + allSelectionRanges ++ treeSelectionRange private def setParent( child: SelectionRange, diff --git a/presentation-compiler/src/main/dotty/tools/pc/SemanticdbSymbols.scala b/presentation-compiler/src/main/dotty/tools/pc/SemanticdbSymbols.scala index dd107329a7b1..b485f044361e 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/SemanticdbSymbols.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/SemanticdbSymbols.scala @@ -1,182 +1,14 @@ package dotty.tools.pc -import scala.util.control.NonFatal - import dotty.tools.dotc.core.Contexts.* -import dotty.tools.dotc.core.Flags.* -import dotty.tools.dotc.core.Names.* import dotty.tools.dotc.core.Symbols.* -import dotty.tools.dotc.semanticdb.* -import dotty.tools.pc.utils.InteractiveEnrichments.* +import dotty.tools.dotc.semanticdb object SemanticdbSymbols: def inverseSemanticdbSymbol(sym: String)(using ctx: Context): List[Symbol] = - import Scala3.StringOps.* - - val defns = ctx.definitions - import defns.* - - def loop(s: String): List[Symbol] = - if !s.isSymbol || s.isRootPackage then RootPackage :: Nil - else if s.isEmptyPackage then EmptyPackageVal :: Nil - else if s.isPackage then - try - val pkg = s.split('/').map(_.stripBackticks).mkString(".") - requiredPackage(pkg) :: Nil - catch - case NonFatal(_) => - Nil - else - val (desc, parent) = DescriptorParser(s) - val parentSymbol = loop(parent) - - def tryMember(sym: Symbol): List[Symbol] = - sym match - case NoSymbol => - Nil - case owner => - desc match - case Descriptor.None => - Nil - case Descriptor.Type(value) => - val typeSym = owner.info.decl(typeName(value)).symbol - // Semanticdb describes java static members as a reference from type - // while scalac puts static members into synthetic companion class - term - // To avoid issues with resolving static members return type and term in case of Java type - // Example: - // `java/nio/file/Files#exists()` - `exists` is a member of type `Files#` - // however in scalac this method is defined only in `module Files` - if typeSym.is(JavaDefined) then - typeSym :: owner.info.decl(termName(value)).symbol :: Nil - /** - * Looks like decl doesn't work for: - * package a: - * implicit class <> (i: Int): - * def inc = i + 1 - */ - else if typeSym == NoSymbol then - owner.info.member(typeName(value)).symbol :: Nil - else typeSym :: Nil - end if - case Descriptor.Term(value) => - val outSymbol = owner.info.decl(termName(value)).symbol - if outSymbol.exists - then outSymbol :: Nil - else if owner.is(Package) - then - // Fallback for type companion objects, - // e.g. - // ```File.scala - // package a - // type Cow = Int - // object Cow - // ``` - // `ScalaTopLevelMtags` emits `a/Cow.` - // but the symbol we look for is `a/File$package/Cow.` - // (look: tests.pc.CompletionWorkspaceSuite.type-apply) - owner.info.decls - .filter { s => - s.isPackageObject && s.name.decoded.endsWith("$package") - } - .flatMap(tryMember) - .toList - else Nil - end if - case Descriptor.Package(value) => - owner.info.decl(termName(value)).symbol :: Nil - case Descriptor.Parameter(value) => - // TODO - need to check how to implement this properly - // owner.paramSymss.flatten.filter(_.name.containsName(value)) - Nil - case Descriptor.TypeParameter(value) => - // TODO - need to check how to implement this properly - // owner.typeParams.filter(_.name.containsName(value)) - Nil - case Descriptor.Method(value, _) => - owner.info - .decl(termName(value)) - .alternatives - .iterator - .map(_.symbol) - .filter(sym => symbolName(sym) == s) - .toList - end match - end tryMember - - parentSymbol.flatMap(tryMember) - try - val res = loop(sym) - res.filterNot(_ == NoSymbol) - catch case NonFatal(e) => Nil - end inverseSemanticdbSymbol + semanticdb.symbolsFromName(sym) /** The semanticdb name of the given symbol */ def symbolName(sym: Symbol)(using Context): String = - val b = StringBuilder(20) - addSymName(b, sym) - b.toString - - /** - * Taken from https://github.com/lampepfl/dotty/blob/2db43dae1480825227eb30d291b0dd0f0494e0f6/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala#L293 - * In future might be replaced by usage of compiler implementation after merging https://github.com/lampepfl/dotty/pull/12885 - */ - private def addSymName(b: StringBuilder, sym: Symbol)(using Context): Unit = - - import dotty.tools.dotc.semanticdb.Scala3.{*, given} - - def addName(name: Name) = - val str = name.toString.unescapeUnicode - if str.nonEmpty && str.isJavaIdent then b append str - else b append '`' append str append '`' - - def addOwner(owner: Symbol): Unit = - if !owner.isRoot then addSymName(b, owner) - - def addOverloadIdx(sym: Symbol): Unit = - val decls = - val decls0 = sym.owner.info.decls.lookupAll(sym.name) - if sym.owner.isAllOf(JavaModule) then - decls0 ++ sym.owner.companionClass.info.decls.lookupAll(sym.name) - else decls0 - end decls - // private java constructors do not have a symbol created - val alts = decls - .filter(d => d.isOneOf(Method | Mutable) && !d.is(Private)) - .toList - .reverse - def find(filter: Symbol => Boolean) = alts match - case notSym :: rest if !filter(notSym) => - val idx = rest.indexWhere(filter) - if idx >= 0 then b.append('+').append(idx + 1) - case _ => - end find - val sig = sym.signature - find(_.signature == sig) - end addOverloadIdx - - def addDescriptor(sym: Symbol): Unit = - if sym.is(ModuleClass) then addDescriptor(sym.sourceModule) - else if sym.is(TypeParam) then - b.append('['); addName(sym.name); b.append(']') - else if sym.is(Param) then - b.append('('); addName(sym.name); b.append(')') - else if sym.isRoot then b.append(Symbols.RootPackage) - else if sym.isEmptyPackage then b.append(Symbols.EmptyPackage) - else if sym.isScala2PackageObject then - b.append(Symbols.PackageObjectDescriptor) - else - addName(sym.name) - if sym.is(Package) then b.append('/') - else if sym.isType || sym.isAllOf(JavaModule) then b.append('#') - else if sym.isOneOf(Method | Mutable) - && (!sym.is(StableRealizable) || sym.isConstructor) - then - b.append('('); addOverloadIdx(sym); b.append(").") - else b.append('.') - - if !sym.isRoot && sym != NoSymbol then addOwner(sym.owner) - addDescriptor(sym) - end addSymName - -end SemanticdbSymbols + semanticdb.symbolToName(sym) diff --git a/presentation-compiler/src/main/dotty/tools/pc/SemanticdbTextDocumentProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/SemanticdbTextDocumentProvider.scala index 43e75a4d6130..5837d0c23013 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/SemanticdbTextDocumentProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/SemanticdbTextDocumentProvider.scala @@ -9,11 +9,7 @@ import scala.meta.internal.mtags.MD5 import scala.util.Properties import dotty.tools.dotc.interactive.InteractiveDriver -import dotty.tools.dotc.semanticdb.ExtractSemanticDB -import dotty.tools.dotc.semanticdb.Language -import dotty.tools.dotc.semanticdb.Schema -import dotty.tools.dotc.semanticdb.TextDocument -import dotty.tools.dotc.semanticdb.internal.SemanticdbOutputStream +import dotty.tools.dotc.semanticdb import dotty.tools.dotc.util.SourceFile class SemanticdbTextDocumentProvider( @@ -32,8 +28,6 @@ class SemanticdbTextDocumentProvider( SourceFile.virtual(filePath.toString(), validCode) ) val tree = driver.currentCtx.run.nn.units.head.tpdTree - val extractor = ExtractSemanticDB.Extractor() - extractor.traverse(tree)(using driver.currentCtx) val path = workspace .flatMap { workspacePath => scala.util.Try(workspacePath.relativize(filePath)).toOption @@ -44,20 +38,6 @@ class SemanticdbTextDocumentProvider( } .getOrElse(filePath.toString()) - val document = TextDocument( - schema = Schema.SEMANTICDB4, - language = Language.SCALA, - uri = path.nn, - text = sourceCode, - md5 = MD5.compute(sourceCode), - symbols = extractor.symbolInfos.toList, - occurrences = extractor.occurrences.toList - ) - val byteStream = new ByteArrayOutputStream() - val out = SemanticdbOutputStream.newInstance(byteStream) - document.writeTo(out) - out.flush() - byteStream.toByteArray().nn - end textDocument + semanticdb.textDocumentBytes(tree, path.nn, sourceCode)(using driver.currentCtx) end SemanticdbTextDocumentProvider diff --git a/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala index bd16d2ce2aa9..d67ed55d6334 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala @@ -1,5 +1,11 @@ package dotty.tools.pc +import scala.jdk.CollectionConverters.* +import scala.meta.pc.OffsetParams +import scala.meta.pc.SymbolDocumentation +import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext + import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Symbols.* @@ -10,13 +16,8 @@ import dotty.tools.dotc.util.SourceFile import dotty.tools.pc.printer.ShortenedTypePrinter import dotty.tools.pc.printer.ShortenedTypePrinter.IncludeDefaultParam import dotty.tools.pc.utils.InteractiveEnrichments.* -import org.eclipse.lsp4j as l -import scala.jdk.CollectionConverters.* -import scala.meta.internal.metals.ReportContext -import scala.meta.pc.OffsetParams -import scala.meta.pc.SymbolDocumentation -import scala.meta.pc.SymbolSearch +import org.eclipse.lsp4j as l object SignatureHelpProvider: @@ -36,12 +37,11 @@ object SignatureHelpProvider: val pos = driver.sourcePosition(params, isZeroExtent = false) val path = Interactive.pathTo(unit.tpdTree, pos.span)(using driver.currentCtx) - val localizedContext = Interactive.contextOfPath(path)(using driver.currentCtx) - val indexedContext = IndexedContext(driver.currentCtx) + val newctx = driver.currentCtx.fresh.setCompilationUnit(unit) + val indexedContext = IndexedContext(pos, path, newctx) - given Context = localizedContext.fresh - .setCompilationUnit(unit) - .setPrinterFn(_ => ShortenedTypePrinter(search, IncludeDefaultParam.Never)(using indexedContext)) + given Context = + newctx.setPrinterFn(_ => ShortenedTypePrinter(search, IncludeDefaultParam.Never)(using indexedContext)) val (paramN, callableN, alternatives) = Signatures.signatureHelp(path, pos.span) @@ -76,7 +76,11 @@ object SignatureHelpProvider: val methodParams = info.parameters().nn.asScala val typeParams = info.typeParameters().nn.asScala - def updateParams(params: List[Signatures.Param], typeParamIndex: Int, methodParamIndex: Int): List[Signatures.Param] = + def updateParams( + params: List[Signatures.Param], + typeParamIndex: Int, + methodParamIndex: Int + ): List[Signatures.Param] = params match case (head: Signatures.MethodParam) :: tail => val rest = updateParams(tail, typeParamIndex, methodParamIndex + 1) @@ -127,14 +131,14 @@ object SignatureHelpProvider: case _ => false val prefix = if isImplicit then "using " else "" val isTypeParams = paramList.forall(_.isInstanceOf[Signatures.TypeParam]) && paramList.nonEmpty - val wrap: String => String = label => if isTypeParams then - s"[$label]" - else - s"($label)" + val wrap: String => String = label => + if isTypeParams then + s"[$label]" + else + s"($label)" wrap(labels.mkString(prefix, ", ", "")) }.mkString - val returnTypeLabel = signature.returnType.map(t => s": $t").getOrElse("") val label = s"${signature.name}$paramLists$returnTypeLabel" val documentation = signature.doc.map(markupContent) @@ -142,10 +146,8 @@ object SignatureHelpProvider: sig.setParameters(paramInfoss.asJava) documentation.foreach(sig.setDocumentation(_)) sig - end signatureToSignatureInformation - /** - * Convert `param` to `ParameterInformation` + /** Convert `param` to `ParameterInformation` */ private def paramToParameterInformation( param: Signatures.Param diff --git a/presentation-compiler/src/main/dotty/tools/pc/SymbolInformationProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/SymbolInformationProvider.scala index ccda618078b8..2dfb146a925c 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/SymbolInformationProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/SymbolInformationProvider.scala @@ -1,23 +1,22 @@ package dotty.tools.pc import scala.collection.mutable -import scala.util.control.NonFatal - +import scala.meta.internal.pc.PcSymbolInformation +import scala.meta.internal.pc.SymbolInfo import scala.meta.pc.PcSymbolKind import scala.meta.pc.PcSymbolProperty +import scala.util.control.NonFatal import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Denotations.{Denotation, MultiDenotation} import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Names.* import dotty.tools.dotc.core.StdNames.nme import dotty.tools.dotc.core.Symbols.* -import dotty.tools.pc.utils.InteractiveEnrichments.deepDealias import dotty.tools.pc.SemanticdbSymbols import dotty.tools.pc.utils.InteractiveEnrichments.allSymbols +import dotty.tools.pc.utils.InteractiveEnrichments.deepDealiasAndSimplify import dotty.tools.pc.utils.InteractiveEnrichments.stripBackticks -import scala.meta.internal.pc.PcSymbolInformation -import scala.meta.internal.pc.SymbolInfo -import dotty.tools.dotc.core.Denotations.{Denotation, MultiDenotation} class SymbolInformationProvider(using Context): @@ -39,23 +38,29 @@ class SymbolInformationProvider(using Context): else Nil val allParents = val visited = mutable.Set[Symbol]() - def collect(sym: Symbol): Unit = { + def collect(sym: Symbol): Unit = visited += sym if sym.isClass - then sym.asClass.parentSyms.foreach { - case parent if !visited(parent) => + then + sym.asClass.parentSyms.foreach { + case parent if !visited(parent) => collect(parent) - case _ => - } - } + case _ => + } collect(classSym) visited.toList.map(SemanticdbSymbols.symbolName) val dealisedSymbol = - if sym.isAliasType then sym.info.deepDealias.typeSymbol else sym + if sym.isAliasType then sym.info.deepDealiasAndSimplify.typeSymbol else sym val classOwner = sym.ownersIterator.drop(1).find(s => s.isClass || s.is(Flags.Module)) val overridden = sym.denot.allOverriddenSymbols.toList - val memberDefAnnots = sym.info.membersBasedOnFlags(Flags.Method, Flags.EmptyFlags).flatMap(_.allSymbols).flatMap(_.denot.annotations) + val memberDefAnnots = + if classSym.exists then + classSym.info + .membersBasedOnFlags(Flags.Method, Flags.EmptyFlags) + .flatMap(_.allSymbols) + .flatMap(_.denot.annotations) + else Nil val pcSymbolInformation = PcSymbolInformation( @@ -116,7 +121,7 @@ object SymbolProvider: def loop( owners: List[Symbol], - parts: List[(String, Boolean)], + parts: List[(String, Boolean)] ): List[Symbol] = parts match case (head, isClass) :: tl => diff --git a/presentation-compiler/src/main/dotty/tools/pc/TastyUtils.scala b/presentation-compiler/src/main/dotty/tools/pc/TastyUtils.scala index 747f104cfede..063e9b94554b 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/TastyUtils.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/TastyUtils.scala @@ -37,7 +37,6 @@ object TastyUtils: .element("pre", "class='container is-dark'")(_.raw(tastyHtml)) } .render - end htmlTasty private def tastyHtmlPageTitle(file: URI) = val filename = Paths.get(file).nn.getFileName().toString diff --git a/presentation-compiler/src/main/dotty/tools/pc/WithCompilationUnit.scala b/presentation-compiler/src/main/dotty/tools/pc/WithCompilationUnit.scala index 8110db269b3b..0071984c5e54 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/WithCompilationUnit.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/WithCompilationUnit.scala @@ -1,14 +1,12 @@ package dotty.tools.pc -import scala.language.unsafeNulls - import java.nio.file.Paths -import scala.meta as m - +import scala.language.unsafeNulls import scala.meta.internal.metals.CompilerOffsetParams import scala.meta.pc.OffsetParams import scala.meta.pc.VirtualFileParams +import scala.meta as m import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Flags @@ -20,7 +18,7 @@ import dotty.tools.pc.utils.InteractiveEnrichments.* class WithCompilationUnit( val driver: InteractiveDriver, - params: VirtualFileParams, + params: VirtualFileParams ): val uri = params.uri() val filePath = Paths.get(uri) @@ -50,7 +48,7 @@ class WithCompilationUnit( def primaryConstructorTypeParam(owner: Symbol) = for typeParams <- owner.primaryConstructor.paramSymss.headOption - param <- typeParams.find(_.name == sym.name) + param <- typeParams.find(_.name == sym.name) if (param.isType) yield param def additionalForEnumTypeParam(enumClass: Symbol) = @@ -76,7 +74,9 @@ class WithCompilationUnit( } else Set.empty val all = - if sym.is(Flags.ModuleClass) then + if sym.is(Flags.Exported) then + Set(sym, sym.sourceSymbol) + else if sym.is(Flags.ModuleClass) then Set(sym, sym.companionModule, sym.companionModule.companion) else if sym.isClass then Set(sym, sym.companionModule, sym.companion.moduleClass) @@ -89,7 +89,7 @@ class WithCompilationUnit( Set( sym, info.member(sym.asTerm.name.setterName).symbol, - info.member(sym.asTerm.name.getterName).symbol, + info.member(sym.asTerm.name.getterName).symbol ) ++ sym.allOverriddenSymbols.toSet // type used in primary constructor will not match the one used in the class else if sym.isTypeParam && sym.owner.isPrimaryConstructor then @@ -100,6 +100,5 @@ class WithCompilationUnit( ++ additionalForEnumTypeParam(sym.maybeOwner) + sym else Set(sym) all.filter(s => s != NoSymbol && !s.isError) - end symbolAlternatives end WithCompilationUnit diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/AmmoniteFileCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/AmmoniteFileCompletions.scala index 81337c7d8dcb..ce05bc09ba8a 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/AmmoniteFileCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/AmmoniteFileCompletions.scala @@ -4,7 +4,7 @@ package completions import java.nio.file.Files import java.nio.file.Path -import scala.jdk.CollectionConverters._ +import scala.jdk.CollectionConverters.* import scala.meta.internal.pc.CompletionFuzzy import dotty.tools.dotc.ast.tpd.* @@ -63,7 +63,7 @@ object AmmoniteFileCompletions: ) def matches(file: Path): Boolean = - (Files.isDirectory(file) || file.toAbsolutePath().toString.isAmmoniteScript) && + (Files.isDirectory(file) || file.toAbsolutePath().toString.isScalaScript) && query.exists(q => CompletionFuzzy.matches(q.nn, file.getFileName().toString)) (split, workspace) match @@ -95,6 +95,5 @@ object AmmoniteFileCompletions: } ++ parentTextEdit case _ => Nil - end match end contribute end AmmoniteFileCompletions diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/AmmoniteIvyCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/AmmoniteIvyCompletions.scala index 718b57cd4828..dde2f79d4014 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/AmmoniteIvyCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/AmmoniteIvyCompletions.scala @@ -32,14 +32,14 @@ object AmmoniteIvyCompletions: CoursierComplete.inferEditRange(pos.point, text) pos.withStart(rangeStart).withEnd(rangeEnd).toLsp val completions = coursierComplete.complete(dependency.nn) + val normalized = dependency.nn.replace(":::", ":").nn.replace("::", ":").nn + val isVersionCompletion = normalized.split(":").nn.length >= 3 completions .map(insertText => - CompletionValue.IvyImport( + CompletionValue.Coursier( insertText.stripPrefix(":"), Some(insertText), - Some(ivyEditRange) + Some(ivyEditRange), + isVersionCompletion ) ) - end match - end contribute -end AmmoniteIvyCompletions 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..756da2b44609 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala @@ -3,16 +3,15 @@ package dotty.tools.pc.completions import org.eclipse.lsp4j.Position import org.eclipse.lsp4j.Range -/** - * @param suffixes which we should insert - * @param prefixes which we should insert - * @param snippet which suffix should we insert the snippet $0 +/** @param suffixes which we should insert + * @param prefixes which we should insert + * @param snippet which suffix should we insert the snippet $0 */ case class CompletionAffix( suffixes: Set[Suffix], prefixes: Set[Prefix], snippet: Suffix, - currentPrefix: Option[String], + currentPrefix: Option[String] ): def addLabelSnippet = suffixes.exists(_.kind == SuffixKind.Bracket) def hasSnippet = snippet.kind != SuffixKind.NoSuffix @@ -39,7 +38,6 @@ case class CompletionAffix( val edit = toSuffix if edit.nonEmpty then Some(edit) else None - given Ordering[Position] = Ordering.by(elem => (elem.getLine, elem.getCharacter)) def toInsertRange: Option[Range] = @@ -56,16 +54,15 @@ 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 _ => "" - /** - * We need to insert previous prefix, but we don't want to display it in the label i.e. - * ```scala - * scala.util.Tr@@ - * ```` - * should return `new Try[T]: Try[T]` - * but insert `new scala.util.Try` - * + /** We need to insert previous prefix, but we don't want to display it in the + * label i.e. + * ```scala + * scala.util.Tr @@ + * ``` + * should return `new Try[T]: Try[T]` but insert `new scala.util.Try` */ def toInsertPrefix: String = loopPrefix(prefixes.toList.map(_.kind)) + currentPrefix.getOrElse("") @@ -80,14 +77,14 @@ object CompletionAffix: suffixes = Set.empty, prefixes = Set.empty, snippet = Affix(SuffixKind.NoSuffix), - currentPrefix = None, + currentPrefix = None ) 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/CompletionPos.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionPos.scala index 6d89cb663b9c..0b97701f744a 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionPos.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionPos.scala @@ -7,9 +7,9 @@ import scala.meta.pc.OffsetParams import dotty.tools.dotc.ast.untpd.* import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.interactive.Completion import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.util.Spans.* -import dotty.tools.dotc.interactive.Completion import dotty.tools.pc.utils.InteractiveEnrichments.* import org.eclipse.lsp4j as l @@ -18,15 +18,18 @@ case object Cursor: val value = "CURSOR" case class CompletionPos( - queryStart: Int, - identEnd: Int, - query: String, - originalCursorPosition: SourcePosition, - sourceUri: URI, - withCURSOR: Boolean + queryStart: Int, + identEnd: Int, + query: String, + originalCursorPosition: SourcePosition, + sourceUri: URI, + withCURSOR: Boolean, + hasLeadingBacktick: Boolean, + hasTrailingBacktick: Boolean ): def queryEnd: Int = originalCursorPosition.point - def stripSuffixEditRange: l.Range = new l.Range(originalCursorPosition.offsetToPos(queryStart), originalCursorPosition.offsetToPos(identEnd)) + def stripSuffixEditRange: l.Range = + new l.Range(originalCursorPosition.offsetToPos(queryStart), originalCursorPosition.offsetToPos(identEnd)) def toEditRange: l.Range = originalCursorPosition.withStart(queryStart).withEnd(originalCursorPosition.point).toLsp def toSourcePosition: SourcePosition = originalCursorPosition.withSpan(Span(queryStart, queryEnd, queryEnd)) @@ -38,21 +41,39 @@ object CompletionPos: adjustedPath: List[Tree], wasCursorApplied: Boolean )(using Context): CompletionPos = - val identEnd = adjustedPath match + def hasBacktickAt(offset: Int): Boolean = + sourcePos.source.content().lift(offset).contains('`') + + val (identEnd, hasTrailingBacktick) = adjustedPath match case (refTree: RefTree) :: _ if refTree.name.toString.contains(Cursor.value) => - refTree.span.end - Cursor.value.length - case (refTree: RefTree) :: _ => refTree.span.end - case _ => sourcePos.end + val refTreeEnd = refTree.span.end + val hasTrailingBacktick = hasBacktickAt(refTreeEnd - 1) + val identEnd = refTreeEnd - Cursor.value.length + (if hasTrailingBacktick then identEnd - 1 else identEnd, hasTrailingBacktick) + case (refTree: RefTree) :: _ => + val refTreeEnd = refTree.span.end + val hasTrailingBacktick = hasBacktickAt(refTreeEnd - 1) + (if hasTrailingBacktick then refTreeEnd - 1 else refTreeEnd, hasTrailingBacktick) + case _ => (sourcePos.end, false) val query = Completion.completionPrefix(adjustedPath, sourcePos) val start = sourcePos.end - query.length() + val hasLeadingBacktick = hasBacktickAt(start - 1) - CompletionPos(start, identEnd, query.nn, sourcePos, offsetParams.uri.nn, wasCursorApplied) + CompletionPos( + start, + identEnd, + query.nn, + sourcePos, + offsetParams.uri.nn, + wasCursorApplied, + hasLeadingBacktick, + hasTrailingBacktick + ) - /** - * Infer the indentation by counting the number of spaces in the given line. + /** Infer the indentation by counting the number of spaces in the given line. * - * @param lineOffset the offset position of the beginning of the line + * @param lineOffset the offset position of the beginning of the line */ private[completions] def inferIndent( lineOffset: Int, @@ -69,6 +90,5 @@ object CompletionPos: } do i += 1 (i, tabIndented) - end inferIndent end CompletionPos diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala index adaeadb12978..1aea7e5307db 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala @@ -3,22 +3,24 @@ package completions import java.nio.file.Path -import scala.jdk.CollectionConverters._ -import scala.meta.internal.metals.ReportContext +import scala.jdk.CollectionConverters.* +import scala.meta.pc.CompletionItemPriority import scala.meta.pc.OffsetParams import scala.meta.pc.PresentationCompilerConfig import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext +import scala.util.control.NonFatal import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Phases -import dotty.tools.dotc.core.StdNames.nme import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Names.DerivedName -import dotty.tools.dotc.interactive.Interactive +import dotty.tools.dotc.core.Phases +import dotty.tools.dotc.core.StdNames.nme import dotty.tools.dotc.interactive.Completion +import dotty.tools.dotc.interactive.Interactive import dotty.tools.dotc.interactive.InteractiveDriver import dotty.tools.dotc.parsing.Tokens import dotty.tools.dotc.profile.Profiler @@ -37,7 +39,6 @@ import org.eclipse.lsp4j.InsertTextFormat import org.eclipse.lsp4j.InsertTextMode import org.eclipse.lsp4j.Range as LspRange import org.eclipse.lsp4j.TextEdit -import scala.meta.pc.CompletionItemPriority object CompletionProvider: val allKeywords = @@ -61,12 +62,15 @@ class CompletionProvider( val (wasCursorApplied, code) = applyCompletionCursor(params) val sourceFile = SourceFile.virtual(uri, code) - /** Creating a new fresh driver is way slower than reusing existing one, - * but runnig a compilation has side effects that modifies the state of the driver. - * We don't want to affect cachingDriver state with compilation including "CURSOR" suffix. + /** Creating a new fresh driver is way slower than reusing existing one, but + * runnig a compilation has side effects that modifies the state of the + * driver. We don't want to affect cachingDriver state with compilation + * including "CURSOR" suffix. * - * We could in theory save this fresh driver for reuse, but it is a choice between extra memory usage and speed. - * The scenario in which "CURSOR" is applied (empty query or query equal to any keyword) has a slim chance of happening. + * We could in theory save this fresh driver for reuse, but it is a choice + * between extra memory usage and speed. The scenario in which "CURSOR" is + * applied (empty query or query equal to any keyword) has a slim chance of + * happening. */ val driver = if wasCursorApplied then freshDriver() else cachingDriver @@ -85,31 +89,36 @@ class CompletionProvider( val tpdPath = tpdPath0 match case Select(qual, name) :: tail - /** If for any reason we end up in param after lifting, we want to inline the synthetic val: - * List(1).iterator.sliding@@ will be transformed into: - * - * 1| val $1$: Iterator[Int] = List.apply[Int]([1 : Int]*).iterator - * 2| { - * 3| def $anonfun(size: Int, step: Int): $1$.GroupedIterator[Int] = - * 4| $1$.sliding[Int](size, step) - * 5| closure($anonfun) - * 6| }:((Int, Int) => Iterator[Int]#GroupedIterator[Int]) - * - * With completion being run at line 4 at @@: - * 4| $1$.sliding@@[Int](size, step) - * - */ - if qual.symbol.is(Flags.Synthetic) && qual.symbol.name.isInstanceOf[DerivedName] => - qual.symbol.defTree match - case valdef: ValDef => Select(valdef.rhs, name) :: tail - case _ => tpdPath0 + /** If for any reason we end up in param after lifting, we want to + * inline the synthetic val: + * ``` + * List(1).iterator.sliding@@ + * ``` + * will be transformed into: + * ``` + * 1| val $1$: Iterator[Int] = List.apply[Int]([1 : Int]*).iterator + * 2| { + * 3| def $anonfun(size: Int, step: Int): $1$.GroupedIterator[Int] = + * 4| $1$.sliding[Int](size, step) + * 5| closure($anonfun) + * 6| }:((Int, Int) => Iterator[Int]#GroupedIterator[Int]) + * ``` + * With completion being run at line 4 at @@: + * ``` + * 4| $1$.sliding@@[Int](size, step) + * ``` + */ + if qual.symbol.is( + Flags.Synthetic + ) && qual.span.isZeroExtent && qual.symbol.name.isInstanceOf[DerivedName] => + qual.symbol.defTree match + case valdef: ValDef if !valdef.rhs.isEmpty => Select(valdef.rhs, name) :: tail + case _ => tpdPath0 case _ => tpdPath0 + val indexedCtx = IndexedContext(pos, tpdPath, newctx) - val locatedCtx = Interactive.contextOfPath(tpdPath)(using newctx) - val indexedCtx = IndexedContext(locatedCtx) - - val completionPos = CompletionPos.infer(pos, params, adjustedPath, wasCursorApplied)(using locatedCtx) + val completionPos = CompletionPos.infer(pos, params, adjustedPath, wasCursorApplied)(using indexedCtx.ctx) val autoImportsGen = AutoImports.generator( completionPos.toSourcePosition, @@ -123,7 +132,7 @@ class CompletionProvider( val (completions, searchResult) = new Completions( text, - locatedCtx, + indexedCtx.ctx, search, buildTargetIdentifier, completionPos, @@ -146,7 +155,7 @@ class CompletionProvider( completionPos, tpdPath, indexedCtx - )(using locatedCtx) + )(using indexedCtx.ctx) } val isIncomplete = searchResult match case SymbolSearch.Result.COMPLETE => false @@ -160,25 +169,25 @@ class CompletionProvider( ) end completions - /** - * In case if completion comes from empty line like: - * {{{ - * class Foo: - * val a = 1 - * @@ - * }}} - * it's required to modify actual code by additional Ident. + /** In case if completion comes from empty line like: + * {{{ + * class Foo: + * val a = 1 + * @@ + * }}} + * it's required to modify actual code by additional Ident. * - * Otherwise, completion poisition doesn't point at any tree - * because scala parser trim end position to the last statement pos. + * Otherwise, completion poisition doesn't point at any tree because scala + * parser trim end position to the last statement pos. */ private def applyCompletionCursor(params: OffsetParams): (Boolean, String) = val text = params.text().nn val offset = params.offset().nn val query = Completion.naiveCompletionPrefix(text, offset) - - if offset > 0 && text.charAt(offset - 1).isUnicodeIdentifierPart - && !CompletionProvider.allKeywords.contains(query) then false -> text + def isValidLastChar = + val lastChar = text.charAt(offset - 1) + lastChar.isUnicodeIdentifierPart || lastChar == '.' + if offset > 0 && isValidLastChar && !CompletionProvider.allKeywords.contains(query) then false -> text else val isStartMultilineComment = @@ -187,8 +196,7 @@ class CompletionProvider( case '*' => text.charAt(i - 2) == '*' && text.charAt(i - 3) == '/' - case _ => false - ) + case _ => false) true -> ( if isStartMultilineComment then // Insert potentially missing `*/` to avoid comment out all codes after the "/**". @@ -196,7 +204,6 @@ class CompletionProvider( else text.substring(0, offset).nn + Cursor.value + text.substring(offset) ) - end applyCompletionCursor private def completionItems( completion: CompletionValue, @@ -217,10 +224,15 @@ class CompletionProvider( // to recalculate the description // related issue https://github.com/lampepfl/scala3/issues/11941 lazy val kind: CompletionItemKind = underlyingCompletion.completionItemKind - val description = underlyingCompletion.description(printer) - val label = underlyingCompletion.labelWithDescription(printer) + val description = + try underlyingCompletion.description(printer) + catch case NonFatal(_) => underlyingCompletion.label + val label = + if config.isDetailIncludedInLabel then + try completion.labelWithDescription(printer) + catch case NonFatal(_) => completion.label + else completion.label val ident = underlyingCompletion.insertText.getOrElse(underlyingCompletion.label) - lazy val isInStringInterpolation = path match // s"My name is $name" @@ -246,10 +258,16 @@ class CompletionProvider( range: Option[LspRange] = None ): CompletionItem = val oldText = params.text().nn.substring(completionPos.queryStart, completionPos.identEnd) - val editRange = if newText.startsWith(oldText) then completionPos.stripSuffixEditRange - else completionPos.toEditRange + val trimmedNewText = + var nt = newText + if completionPos.hasLeadingBacktick then nt = nt.stripPrefix("`") + if completionPos.hasTrailingBacktick then nt = nt.stripSuffix("`") + nt - val textEdit = new TextEdit(range.getOrElse(editRange), wrapInBracketsIfRequired(newText)) + val editRange = if trimmedNewText.startsWith(oldText) then completionPos.stripSuffixEditRange + else completionPos.toEditRange + + val textEdit = new TextEdit(range.getOrElse(editRange), wrapInBracketsIfRequired(trimmedNewText)) val item = new CompletionItem(label) item.setSortText(f"${idx}%05d") @@ -298,7 +316,9 @@ class CompletionProvider( mkItem(nameEdit.getNewText().nn, other.toList, range = Some(nameEdit.getRange().nn)) case _ => mkItem( - v.insertText.getOrElse(completionTextPrefix + ident.backticked(backtickSoftKeyword) + completionTextSuffix), + v.insertText.getOrElse( + completionTextPrefix + ident.backticked(backtickSoftKeyword) + completionTextSuffix + ), edits.edits, range = v.range ) @@ -310,7 +330,7 @@ class CompletionProvider( v.insertText.getOrElse( completionTextPrefix + ident.backticked(backtickSoftKeyword) + completionTextSuffix ), - range = v.range, + range = v.range ) // Special case when symbol is out of scope, and there is no auto import. // It means that it will use fully qualified path @@ -331,10 +351,7 @@ class CompletionProvider( ) + completionTextSuffix, range = v.range ) - end match end match - end match - end mkItemWithImports underlyingCompletion match case v: (CompletionValue.Workspace | CompletionValue.Extension | CompletionValue.ImplicitClass) => @@ -345,7 +362,5 @@ class CompletionProvider( val nameText = underlyingCompletion.insertText.getOrElse(ident.backticked(backtickSoftKeyword)) val nameWithAffixes = completionTextPrefix + nameText + completionTextSuffix mkItem(nameWithAffixes, range = underlyingCompletion.range) - - end match end completionItems end CompletionProvider diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala index 90b285bffb3a..bfe1399ae9da 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala @@ -30,7 +30,7 @@ enum CompletionSource: case NamedArgKind case AutoFillKind case FileSystemMemberKind - case IvyImportKind + case CoursierKind case InterpolatorKind case MatchCompletionKind case CaseKeywordKind @@ -49,17 +49,15 @@ sealed trait CompletionValue: def description(printer: ShortenedTypePrinter)(using Context): String = "" def insertMode: Option[InsertTextMode] = None def completionData(buildTargetIdentifier: String)( - using Context + using Context ): CompletionItemData = CompletionItemData("", buildTargetIdentifier, kind = completionItemDataKind) def command: Option[String] = None - /** - * Label with potentially attached description. + /** Label with potentially attached description. */ def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = label def lspTags(using Context): List[CompletionItemTag] = Nil -end CompletionValue object CompletionValue: @@ -133,25 +131,27 @@ object CompletionValue: ) extends Symbolic: override def completionItemDataKind: Integer = CompletionSource.CompilerKind.ordinal - /** - * We need to access original completion in sorting phase. - * This class is only a wrapper to hold both new completion and original completion. - * - * All methods are proxied to @param extraMethod - * - * FIXME Refactor this file to different architercture. At least to somethhing that is easier to modifiy and scale. - * One solution may be a migration to flag based solution. - */ + /** We need to access original completion in sorting phase. This class is only + * a wrapper to hold both new completion and original completion. + * + * All methods are proxied to @param extraMethod + * + * FIXME Refactor this file to different architercture. At least to + * somethhing that is easier to modifiy and scale. One solution may be a + * migration to flag based solution. + */ case class ExtraMethod( owner: Denotation, - extraMethod: Symbolic, + extraMethod: Symbolic ) extends Symbolic: override def additionalEdits: List[TextEdit] = extraMethod.additionalEdits override def command: Option[String] = extraMethod.command - override def completionData(buildTargetIdentifier: String)(using Context): CompletionItemData = extraMethod.completionData((buildTargetIdentifier)) + override def completionData(buildTargetIdentifier: String)(using Context): CompletionItemData = + extraMethod.completionData((buildTargetIdentifier)) override def completionItemKind(using Context): CompletionItemKind = extraMethod.completionItemKind override def description(printer: ShortenedTypePrinter)(using Context): String = extraMethod.description(printer) - override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = extraMethod.labelWithDescription(printer) + override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = + extraMethod.labelWithDescription(printer) override def range: Option[Range] = extraMethod.range override def denotation: Denotation = extraMethod.denotation override def label: String = extraMethod.label @@ -168,7 +168,7 @@ object CompletionValue: case class Scope( label: String, denotation: Denotation, - override val snippetAffix: CompletionAffix, + override val snippetAffix: CompletionAffix ) extends Symbolic: override def completionItemDataKind: Integer = CompletionSource.ScopeKind.ordinal @@ -190,14 +190,13 @@ object CompletionValue: s"${labelWithSuffix(printer)} -${description(printer)}" else super.labelWithDescription(printer) - /** - * CompletionValue for old implicit classes methods via SymbolSearch + /** CompletionValue for old implicit classes methods via SymbolSearch */ case class ImplicitClass( label: String, denotation: Denotation, override val snippetAffix: CompletionAffix, - override val importSymbol: Symbol, + override val importSymbol: Symbol ) extends Symbolic: override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Method @@ -205,8 +204,7 @@ object CompletionValue: override def description(printer: ShortenedTypePrinter)(using Context): String = s"${super.description(printer)} (implicit)" - /** - * CompletionValue for extension methods via SymbolSearch + /** CompletionValue for extension methods via SymbolSearch */ case class Extension( label: String, @@ -220,13 +218,13 @@ object CompletionValue: override def description(printer: ShortenedTypePrinter)(using Context): String = s"${printer.completionSymbol(denotation)} (extension)" - /** - * @param shortenedNames shortened type names by `Printer`. This field should be used for autoImports - * @param start Starting position of the completion - * this is needed, because for OverrideCompletion, completionPos - * doesn't capture the "correct" starting position. For example, - * when we type `override def fo@@` (where `@@` we invoke completion) - * `completionPos` is `fo`, instead of `override def fo`. + /** @param shortenedNames shortened type names by `Printer`. This field should + * be used for autoImports + * @param start Starting position of the completion this is needed, because + * for OverrideCompletion, completionPos doesn't capture the "correct" + * starting position. For example, when we type `override def fo@@` (where + * `@@` we invoke completion) `completionPos` is `fo`, instead of + * `override def fo`. */ case class Override( label: String, @@ -242,7 +240,6 @@ object CompletionValue: CompletionItemKind.Method override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = label - end Override case class NamedArg( label: String, @@ -258,16 +255,15 @@ object CompletionValue: override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = label - end NamedArg case class Autofill( - value: String + value: String, + override val label: String ) extends CompletionValue: override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Enum override def completionItemDataKind: Integer = CompletionSource.OverrideKind.ordinal override def insertText: Option[String] = Some(value) - override def label: String = "Autofill with default values" case class Keyword(label: String, override val insertText: Option[String]) extends CompletionValue: @@ -286,13 +282,14 @@ object CompletionValue: override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.File - case class IvyImport( + case class Coursier( label: String, override val insertText: Option[String], - override val range: Option[Range] + override val range: Option[Range], + isVersionCompletion: Boolean = false ) extends CompletionValue: override val filterText: Option[String] = insertText - override def completionItemDataKind: Integer = CompletionSource.IvyImportKind.ordinal + override def completionItemDataKind: Integer = CompletionSource.CoursierKind.ordinal override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Folder @@ -315,7 +312,6 @@ object CompletionValue: if isExtension then s"${printer.completionSymbol(symbol)} (extension)" else super.description(printer) override def isExtensionMethod: Boolean = isExtension - end Interpolator case class MatchCompletion( label: String, @@ -345,7 +341,7 @@ object CompletionValue: label override def description(printer: ShortenedTypePrinter)(using Context): String = - printer.completionSymbol(denotation) + printer.completionSymbol(denotation) end CaseKeyword case class Document(label: String, doc: String, description: String) @@ -362,17 +358,15 @@ object CompletionValue: override def insertMode: Option[InsertTextMode] = Some(InsertTextMode.AsIs) case class SingletonValue(label: String, info: Type, override val range: Option[Range]) - extends CompletionValue: - override def insertText: Option[String] = Some(label) - override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = - s"$label: ${printer.tpe(info)}" + extends CompletionValue: + override def insertText: Option[String] = Some(label) + override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = + s"$label: ${printer.tpe(info)}" - override def completionItemKind(using Context): CompletionItemKind = - CompletionItemKind.Constant + override def completionItemKind(using Context): CompletionItemKind = + CompletionItemKind.Constant - def namedArg(label: String, sym: ParamSymbol)(using - Context - ): CompletionValue = + def namedArg(label: String, sym: ParamSymbol)(using Context): CompletionValue = NamedArg(label, sym.info.widenTermRefExpr, sym.symbol) def keyword(label: String, insertText: String): CompletionValue = 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 1114a07f91b5..cecd401d81cc 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -5,16 +5,20 @@ import java.nio.file.Path import java.nio.file.Paths import scala.collection.mutable -import scala.meta.internal.metals.ReportContext import scala.meta.internal.mtags.CoursierComplete -import scala.meta.internal.pc.{IdentifierComparator, MemberOrdering, CompletionFuzzy} +import scala.meta.internal.pc.CompletionFuzzy +import scala.meta.internal.pc.IdentifierComparator +import scala.meta.internal.pc.MemberOrdering import scala.meta.pc.* +import scala.meta.pc.reports.ReportContext +import scala.util.control.NonFatal import dotty.tools.dotc.ast.tpd.* 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.* @@ -29,6 +33,7 @@ import dotty.tools.dotc.interactive.Completion.Mode import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.util.SrcPos import dotty.tools.pc.AutoImports.AutoImportsGenerator +import dotty.tools.pc.IndexedContext.Result import dotty.tools.pc.buildinfo.BuildInfo import dotty.tools.pc.completions.OverrideCompletions.OverrideExtractor import dotty.tools.pc.utils.InteractiveEnrichments.* @@ -72,8 +77,20 @@ class Completions( /* In case of `T@@[]` we should not add snippets. */ 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 sel :: (funSel @ Select(fun, name)) :: (appl: GenericApply) :: _ + if appl.fun == funSel && sel == fun => false + 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)) + /* In case of `def demo[F[_]: TC@@]` or `def demo[F[_]: {TC1, TC@@}]` + * we shouldn't add `[]` as we are in a context bound position. + */ + case Ident(_) :: (_: untpd.ContextBounds) :: _ => false + case Ident(_) :: (_: untpd.AppliedTypeTree) :: (_: untpd.ContextBounds) :: _ => false case _ => true) private lazy val isNew: Boolean = Completion.isInNewContext(adjustedPath) @@ -102,22 +119,51 @@ class Completions( if generalExclude then false else if completionMode.is(Mode.Type) then true else !isWildcardParam(sym) && (sym.isTerm || sym.is(Package)) - end if end includeSymbol lazy val fuzzyMatcher: Name => Boolean = name => - if completionMode.is(Mode.Member) then CompletionFuzzy.matchesSubCharacters(completionPos.query, name.toString) - else CompletionFuzzy.matches(completionPos.query, name.toString) - - def enrichedCompilerCompletions(qualType: Type): (List[CompletionValue], SymbolSearch.Result) = + val nameString = name.toString + if nameString.isEmpty then false + else if completionMode.is(Mode.Member) then CompletionFuzzy.matchesSubCharacters(completionPos.query, nameString) + else CompletionFuzzy.matches(completionPos.query, nameString) + + def enrichedCompilerCompletions( + qualType: Type, + fromPath: List[Tree] = path + ): (List[CompletionValue], SymbolSearch.Result) = val compilerCompletions = Completion - .rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath, Some(fuzzyMatcher)) + .rawCompletions( + completionPos.originalCursorPosition, + completionMode, + completionPos.query, + fromPath, + adjustedPath, + Some(fuzzyMatcher), + calculatedScopeContext = Some(indexedContext.scopeContext) + ) compilerCompletions .toList .flatMap(toCompletionValues) .filterInteresting(qualType) + /** When we have an erroneous apply, but we know the symbol + * of the qualifier symbol, we can still provide sensible completions + * for the apply. + */ + def withGuessApplyType(sel: Select)(using Context): List[CompletionValue] = + val qual = sel.qualifier + val name = sel.name + indexedContext.lookupSym(qual.symbol) match + case Result.InScope => + val typedSel = Select(qual.withType(qual.symbol.info.resultType), name) + val newPath = typedSel +: path.drop(1) + val (compilerCompletions, _) = + enrichedCompilerCompletions(qual.symbol.info.resultType, newPath) + compilerCompletions + case _ => + Nil + def completions(): (List[CompletionValue], SymbolSearch.Result) = val (advanced, exclusive) = advancedCompletions(path, completionPos) val (all, result) = @@ -125,27 +171,25 @@ class Completions( else val keywords = KeywordsCompletions.contribute(path, completionPos, comments) val allAdvanced = advanced ++ keywords - path match // should not show completions for toplevel case Nil | (_: PackageDef) :: _ if completionPos.originalCursorPosition.source.file.extension != "sc" => (allAdvanced, SymbolSearch.Result.COMPLETE) - case Select(qual, _) :: _ if qual.typeOpt.isErroneous => - (allAdvanced, SymbolSearch.Result.COMPLETE) + case (sel @ Select(qual, name)) :: _ if qual.typeOpt.isErroneous => + val fromCompiler = withGuessApplyType(sel) + (allAdvanced ++ fromCompiler, SymbolSearch.Result.COMPLETE) case Select(qual, _) :: _ => val (compiler, result) = enrichedCompilerCompletions(qual.typeOpt.widenDealias) (allAdvanced ++ compiler, result) case _ => val (compiler, result) = enrichedCompilerCompletions(defn.AnyType) (allAdvanced ++ compiler, result) - end match val application = CompletionApplication.fromPath(path) val ordering = completionOrdering(application) val sorted = all.sorted(ordering) val values = application.postProcess(sorted) (values, result) - end completions private def toCompletionValues( completion: Name, @@ -175,58 +219,93 @@ class Completions( else symbol.paramSymss.filter(!_.exists(_.isTypeParam)) private def isAbstractType(symbol: Symbol) = - (symbol.info.typeSymbol.is(Trait) // trait A{ def doSomething: Int} - // object B{ new A@@} - // Note: I realised that the value of Flag.Trait is flaky and - // leads to the failure of one of the DocSuite tests - || symbol.info.typeSymbol.isAllOf( - Flags.JavaInterface // in Java: interface A {} - // in Scala 3: object B { new A@@} - ) || symbol.info.typeSymbol.isAllOf( - Flags.PureInterface // in Java: abstract class Shape { abstract void draw();} - // Shape has only abstract members, so can be represented by a Java interface - // in Scala 3: object B{ new Shap@@ } - ) || (symbol.info.typeSymbol.is(Flags.Abstract) && - symbol.isClass) // so as to exclude abstract methods - // abstract class A(i: Int){ def doSomething: Int} - // object B{ new A@@} + try + (symbol.info.typeSymbol.is(Trait) // trait A{ def doSomething: Int} + // object B{ new A@@} + // Note: I realised that the value of Flag.Trait is flaky and + // leads to the failure of one of the DocSuite tests + || symbol.info.typeSymbol.isAllOf( + Flags.JavaInterface // in Java: interface A {} + // in Scala 3: object B { new A@@} + ) || symbol.info.typeSymbol.isAllOf( + Flags.PureInterface // in Java: abstract class Shape { abstract void draw();} + // Shape has only abstract members, so can be represented by a Java interface + // in Scala 3: object B{ new Shap@@ } + ) || (symbol.info.typeSymbol.is(Flags.Abstract) && + symbol.isClass) // so as to exclude abstract methods + // abstract class A(i: Int){ def doSomething: Int} + // object B{ new A@@} ) + catch case NonFatal(_) => false end isAbstractType - private def findSuffix(symbol: Symbol): CompletionAffix = - CompletionAffix.empty - .chain { suffix => // for [] suffix - if shouldAddSuffix && symbol.info.typeParams.nonEmpty then - suffix.withNewSuffixSnippet(Affix(SuffixKind.Bracket)) - else suffix - } - .chain { suffix => // for () suffix - if shouldAddSuffix && symbol.is(Flags.Method) then - val paramss = getParams(symbol) - paramss match - case Nil => suffix - case List(Nil) => suffix.withNewSuffix(Affix(SuffixKind.Brace)) - case _ if config.isCompletionSnippetsEnabled() => - val onlyParameterless = paramss.forall(_.isEmpty) - lazy val onlyImplicitOrTypeParams = paramss.forall( - _.exists { sym => - sym.isType || sym.is(Implicit) || sym.is(Given) - } - ) - if onlyParameterless then suffix.withNewSuffix(Affix(SuffixKind.Brace)) - else if onlyImplicitOrTypeParams then suffix - else if suffix.hasSnippet then suffix.withNewSuffix(Affix(SuffixKind.Brace)) - else suffix.withNewSuffixSnippet(Affix(SuffixKind.Brace)) + private def findSuffix(symbol: Symbol, adjustedPath: List[untpd.Tree]): CompletionAffix = + try + 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).nn.contains("using") => + suffix.withNewPrefix(Affix(PrefixKind.Using)) + case _ => suffix case _ => suffix - end match - else suffix - } - .chain { suffix => // for {} suffix - if shouldAddSuffix && isNew && isAbstractType(symbol) then - if suffix.hasSnippet then suffix.withNewSuffix(Affix(SuffixKind.Template)) - else suffix.withNewSuffixSnippet(Affix(SuffixKind.Template)) - else suffix - } + + } + .chain { suffix => // for () suffix + if shouldAddSuffix && symbol.is(Flags.Method) then + val paramss = getParams(symbol) + paramss match + case Nil => suffix + case List(Nil) => suffix.withNewSuffix(Affix(SuffixKind.Brace)) + case _ if config.isCompletionSnippetsEnabled() => + val onlyParameterless = paramss.forall(_.isEmpty) + lazy val onlyImplicitOrTypeParams = paramss.forall( + _.exists { sym => + sym.isType || sym.is(Implicit) || sym.is(Given) + } + ) + if onlyParameterless then suffix.withNewSuffix(Affix(SuffixKind.Brace)) + else if onlyImplicitOrTypeParams then suffix + else if suffix.hasSnippet then suffix.withNewSuffix(Affix(SuffixKind.Brace)) + else suffix.withNewSuffixSnippet(Affix(SuffixKind.Brace)) + case _ => suffix + else suffix + } + .chain { suffix => // for () suffix + if shouldAddSuffix && symbol.is(Flags.Method) then + val paramss = getParams(symbol) + paramss match + case Nil => suffix + case List(Nil) => suffix.withNewSuffix(Affix(SuffixKind.Brace)) + case _ if config.isCompletionSnippetsEnabled() => + val onlyParameterless = paramss.forall(_.isEmpty) + lazy val onlyImplicitOrTypeParams = paramss.forall( + _.exists { sym => + sym.isType || sym.is(Implicit) || sym.is(Given) + } + ) + if onlyParameterless then suffix.withNewSuffix(Affix(SuffixKind.Brace)) + else if onlyImplicitOrTypeParams then suffix + else if suffix.hasSnippet then suffix.withNewSuffix(Affix(SuffixKind.Brace)) + else suffix.withNewSuffixSnippet(Affix(SuffixKind.Brace)) + case _ => suffix + else suffix + } + .chain { suffix => // for {} suffix + if shouldAddSuffix && isNew && isAbstractType(symbol) then + if suffix.hasSnippet then suffix.withNewSuffix(Affix(SuffixKind.Template)) + else suffix.withNewSuffixSnippet(Affix(SuffixKind.Template)) + else suffix + } + catch case NonFatal(_) => CompletionAffix.empty end findSuffix @@ -235,27 +314,33 @@ class Completions( label: String, toCompletionValue: (String, SingleDenotation, CompletionAffix) => CompletionValue.Symbolic ): List[CompletionValue] = + def safeConstructorMembers(symbol: Symbol): List[Symbol] = + try + symbol.info.member(nme.CONSTRUCTOR).allSymbols + catch case NonFatal(_) => Nil val sym = denot.symbol - val hasNonSyntheticConstructor = sym.name.isTypeName && sym.isClass + def hasNonSyntheticConstructor = sym.name.isTypeName && sym.isClass && !sym.is(ModuleClass) && !sym.is(Trait) && !sym.is(Abstract) && !sym.is(Flags.JavaDefined) val (extraMethodDenots, skipOriginalDenot): (List[SingleDenotation], Boolean) = if shouldAddSnippet && isNew && hasNonSyntheticConstructor then - val constructors = sym.info.member(nme.CONSTRUCTOR).allSymbols.map(_.asSingleDenotation) + val constructors = safeConstructorMembers(sym).map(_.asSingleDenotation) .filter(_.symbol.isAccessibleFrom(denot.info)) constructors -> true // handling is LTS specific: It's a workaround to mittigate lack of https://github.com/scala/scala3/pull/18874 backport // which was required in backport of https://github.com/scala/scala3/pull/18914 to achive the improved completions semantics. else if shouldAddSnippet && completionMode.is(Mode.Term) && sym.name.isTermName && - !sym.is(Flags.JavaDefined) && (sym.isClass || sym.isOneOf(Flags.Module | Flags.Accessor) || (sym.isField && denot.info.isInstanceOf[TermRef])) then - + !sym.is(Flags.JavaDefined) && (sym.isClass || sym.isOneOf( + Flags.Module | Flags.Accessor + ) || (sym.isField && denot.info.isInstanceOf[TermRef])) + then val constructors = if sym.isAllOf(ConstructorProxyModule) then sym.companionClass.info.member(nme.CONSTRUCTOR).allSymbols else - val companionApplies = denot.info.member(nme.apply).allSymbols + val companionApplies = denot.info.member(nme.apply).allSymbols val classConstructors = if sym.companionClass.exists && !sym.companionClass.isOneOf(AbstractOrTrait) then - sym.companionClass.info.member(nme.CONSTRUCTOR).allSymbols + safeConstructorMembers(sym.companionClass) else Nil if companionApplies.exists(_.is(Synthetic)) then @@ -273,7 +358,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, _)) :: _ => @@ -295,15 +380,14 @@ 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 end completionsWithAffix - /** - * @return Tuple of completionValues and flag. If the latter boolean value is true + /** @return Tuple of completionValues and flag. If the latter boolean value is true * Metals should provide advanced completions only. */ private def advancedCompletions( @@ -411,7 +495,7 @@ class Completions( ) // unapply pattern - case Ident(name) :: (unapp : UnApply) :: _ => + case Ident(name) :: (unapp: UnApply) :: _ => ( CaseKeywordCompletion.contribute( EmptyTree, // no selector @@ -425,7 +509,7 @@ class Completions( ), false, ) - case Select(_, name) :: (unapp : UnApply) :: _ => + case Select(_, name) :: (unapp: UnApply) :: _ => ( CaseKeywordCompletion.contribute( EmptyTree, // no selector @@ -522,7 +606,7 @@ class Completions( config.isCompletionSnippetsEnabled() ) (args, false) - val singletonCompletions = InterCompletionType.inferType(path).map( + val singletonCompletions = InferCompletionType.inferType(path).map( SingletonCompletions.contribute(path, _, completionPos) ).getOrElse(Nil) (singletonCompletions ++ advanced, exclusive) @@ -571,6 +655,7 @@ class Completions( then indexedContext.lookupSym(sym) match case IndexedContext.Result.InScope => false + case IndexedContext.Result.Missing if indexedContext.rename(sym).isDefined => false case _ if completionMode.is(Mode.ImportOrExport) => visit( CompletionValue.Workspace( @@ -589,7 +674,7 @@ class Completions( else false, ) Some(search.search(query, buildTargetIdentifier, visitor).nn) - else if completionMode.is(Mode.Member) then + else if completionMode.is(Mode.Member) && query.nonEmpty then val visitor = new CompilerSearchVisitor(sym => def isExtensionMethod = sym.is(ExtensionMethod) && qualType.widenDealias <:< sym.extensionParam.info.widenDealias @@ -606,12 +691,11 @@ class Completions( constructorParam.exists(p => qualType.widenDealias <:< p.widenDealias ) - end isImplicitClass def isDefaultVariableSetter = sym.is(Flags.Accessor) && sym.is(Flags.Method) def isImplicitClassMember = isImplicitClass(sym.maybeOwner) && !sym.is(Flags.Synthetic) && sym.isPublic - && !sym.isConstructor && !isDefaultVariableSetter + && !sym.isConstructor && !isDefaultVariableSetter if isExtensionMethod then completionsWithAffix( @@ -682,44 +766,44 @@ class Completions( val buf = List.newBuilder[CompletionValue] def visit(head: CompletionValue): Boolean = val (id, include) = - head match - case doc: CompletionValue.Document => (doc.label, true) - case over: CompletionValue.Override => (over.label, true) - case ck: CompletionValue.CaseKeyword => (ck.label, true) - case symOnly: CompletionValue.Symbolic => - val sym = symOnly.symbol - val name = symOnly match - case CompletionValue.ExtraMethod(owner, extraMethod) => - SemanticdbSymbols.symbolName(owner.symbol) + SemanticdbSymbols.symbolName(extraMethod.symbol) - case _ => SemanticdbSymbols.symbolName(sym) - val suffix = - if symOnly.snippetAffix.addLabelSnippet then "[]" else "" - val id = name + suffix - val include = includeSymbol(sym) - (id, include) - case kw: CompletionValue.Keyword => (kw.label, true) - case mc: CompletionValue.MatchCompletion => (mc.label, true) - case autofill: CompletionValue.Autofill => - (autofill.label, true) - case fileSysMember: CompletionValue.FileSystemMember => - (fileSysMember.label, true) - case ii: CompletionValue.IvyImport => (ii.label, true) - case sv: CompletionValue.SingletonValue => (sv.label, true) - + try + head match + case doc: CompletionValue.Document => (doc.label, true) + case over: CompletionValue.Override => (over.label, true) + case ck: CompletionValue.CaseKeyword => (ck.label, true) + case symOnly: CompletionValue.Symbolic => + val sym = symOnly.symbol + val name = symOnly match + case CompletionValue.ExtraMethod(owner, extraMethod) => + SemanticdbSymbols.symbolName(owner.symbol) + SemanticdbSymbols.symbolName(extraMethod.symbol) + case _ => SemanticdbSymbols.symbolName(sym) + val suffix = + if symOnly.snippetAffix.addLabelSnippet then "[]" else "" + val id = name + suffix + val include = includeSymbol(sym) + (id, include) + case kw: CompletionValue.Keyword => (kw.label, true) + case mc: CompletionValue.MatchCompletion => (mc.label, true) + case autofill: CompletionValue.Autofill => + (autofill.label, true) + case fileSysMember: CompletionValue.FileSystemMember => + (fileSysMember.label, true) + case ii: CompletionValue.Coursier => (ii.label, true) + case sv: CompletionValue.SingletonValue => (sv.label, true) + catch case NonFatal(_) => ("", false) if !alreadySeen(id) && include then alreadySeen += id buf += head true else false - end visit l.foreach(visit) if enrich then val searchResult = enrichWithSymbolSearch(visit, qualType).getOrElse(SymbolSearch.Result.COMPLETE) - (deduplicateCompletions(buf.result), searchResult) - else (deduplicateCompletions(buf.result), SymbolSearch.Result.COMPLETE) + (deduplicateCompletions(buf.result()), searchResult) + else (deduplicateCompletions(buf.result()), SymbolSearch.Result.COMPLETE) end filterInteresting end extension @@ -735,15 +819,25 @@ class Completions( defn.Object_notifyAll, defn.Object_notify, defn.Predef_undefined, - defn.ObjectClass.info.member(nme.wait_).symbol, // NOTE(olafur) IntelliJ does not complete the root package and without this filter // then `_root_` would appear as a completion result in the code `foobar(_)` defn.RootPackage, // NOTE(gabro) valueOf was added as a Predef member in 2.13. We filter it out since is a niche // use case and it would appear upon typing 'val' - defn.ValueOfClass.info.member(nme.valueOf).symbol, - defn.ScalaPredefModule.requiredMethod(nme.valueOf) - ).flatMap(_.alternatives.map(_.symbol)).toSet + defn.ValueOfClass + ) ++ ( + Set( + defn.ObjectClass.info.member(nme.wait_), + defn.ScalaPredefModule.info.member(nme.valueOf) + ).flatMap(_.alternatives.map(_.symbol)).toSet + ) + + private 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 || @@ -756,12 +850,24 @@ class Completions( ): Int = import scala.meta.internal.pc.MemberOrdering.* - def hasGetter(sym: Symbol) = try - def isModuleOrClass = sym.is(Module) || sym.isClass - // isField returns true for some classes - def isJavaClass = sym.is(JavaDefined) && isModuleOrClass - (sym.isField && !isJavaClass && !isModuleOrClass) || sym.getter != NoSymbol - catch case _ => false + def hasGetter(sym: Symbol) = + try + def isModuleOrClass = sym.is(Module) || sym.isClass + // isField returns true for some classes + def isJavaClass = sym.is(JavaDefined) && isModuleOrClass + (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 @@ -780,7 +886,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) || @@ -792,14 +898,15 @@ 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 if sym.isDeprecated then relevance |= IsDeprecated if isEvilMethod(sym.name) then relevance |= IsEvilMethod if !completionMode.is(Mode.ImportOrExport) && - completionMode.is(Mode.Type) && !sym.isType then relevance |= IsNotTypeInTypePos + completionMode.is(Mode.Type) && !sym.isType + then relevance |= IsNotTypeInTypePos relevance end symbolRelevance @@ -870,8 +977,6 @@ class Completions( private def substituteTypeVars(symbol: Symbol): Symbol = val denot = symbol.asSeenFrom(tpe) symbol.withUpdatedTpe(denot.info) - - end new end forSelect def fromPath(path: List[Tree]): CompletionApplication = @@ -938,10 +1043,9 @@ class Completions( case _ => 5 priority(o1) - priority(o2) - end prioritizeByClass - /** - * Some completion values should be shown first such as CaseKeyword and - * NamedArg + + /** Some completion values should be shown first such as CaseKeyword and + * NamedArg */ def compareCompletionValue( sym1: CompletionValue.Symbolic, @@ -987,10 +1091,7 @@ class Completions( o1.label, o2.label ) - case ( - sym1: CompletionValue.Symbolic, - sym2: CompletionValue.Symbolic, - ) => + case (sym1: CompletionValue.Symbolic, sym2: CompletionValue.Symbolic) => if compareCompletionValue(sym1, sym2) then 0 else if compareCompletionValue(sym2, sym1) then 1 else @@ -1034,11 +1135,11 @@ class Completions( ) if byParamCount != 0 then byParamCount else s1.detailString.compareTo(s2.detailString) - end if - end if - end if - end if end if + case (sym1: CompletionValue.Coursier, sym2: CompletionValue.Coursier) => + val comparison = IdentifierComparator.compare(sym1.label, sym2.label) + if sym1.isVersionCompletion && sym2.isVersionCompletion then -comparison + else comparison case _ => val byClass = prioritizeByClass(o1, o2) if byClass != 0 then byClass diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/FilenameCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/FilenameCompletions.scala index 8d2e97856e82..8217f9673e0c 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/FilenameCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/FilenameCompletions.scala @@ -27,6 +27,3 @@ object FilenameCompletions: ) ) else Nil - - end contribute -end FilenameCompletions diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala index da46e5167834..7b361a091d4f 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala @@ -1,11 +1,11 @@ package dotty.tools.pc.completions import scala.collection.mutable.ListBuffer -import scala.meta.internal.metals.ReportContext import scala.meta.internal.pc.CompletionFuzzy import scala.meta.internal.pc.InterpolationSplice import scala.meta.pc.PresentationCompilerConfig import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Contexts.Context @@ -60,13 +60,11 @@ object InterpolatorCompletions: buildTargetIdentifier ) end match - end contribute - /** - * Find the identifier that corresponds to the previous interpolation splice. - * For string `s" $Main.metho@@ "` we want to get `Main` identifier. - * The difference with Scala 2 is that we search for it through the path using - * the created partial function. + /** Find the identifier that corresponds to the previous interpolation splice. + * For string `s" $Main.metho@@ "` we want to get `Main` identifier. The + * difference with Scala 2 is that we search for it through the path using + * the created partial function. */ private def interpolatorMemberArg( lit: Literal, @@ -87,17 +85,16 @@ object InterpolatorCompletions: i } case _ => None - end interpolatorMemberArg - /** - * A completion to select type members inside string interpolators. + /** A completion to select type members inside string interpolators. * - * Example: {{{ - * // before - * s"Hello $name.len@@!" - * // after - * s"Hello ${name.length()$0}" - * }}} + * Example: + * ``` + * // before + * s"Hello $name.len@@!" + * // after + * s"Hello ${name.length()$0}" + * ``` */ private def contributeMember( lit: Literal, @@ -111,7 +108,7 @@ object InterpolatorCompletions: )(using Context, ReportContext): List[CompletionValue] = def newText( label: String, - affix: CompletionAffix , + affix: CompletionAffix, identOrSelect: Ident | Select ): String = val snippetCursor = suffixEnding(affix.toSuffixOpt, areSnippetsSupported) @@ -124,7 +121,6 @@ object InterpolatorCompletions: .append(snippetCursor) .append('}') .toString - end newText def extensionMethods(qualType: Type) = val buffer = ListBuffer.empty[Symbol] @@ -134,11 +130,10 @@ object InterpolatorCompletions: then buffer.append(sym) true - else false, + else false ) search.searchMethods(completionPos.query, buildTargetIdentifier, visitor) buffer.toList - end extensionMethods def completionValues( syms: Seq[Symbol], @@ -166,17 +161,18 @@ object InterpolatorCompletions: Some(identOrSelect.name.toString() + "." + label), denot.symbol, isExtension = isExtension - ), + ) ) }.flatten - val qualType = for - parent <- path.tail.headOption.toList - if lit.span.exists && text.charAt(lit.span.point - 1) != '}' - identOrSelect <- path - .collectFirst(interpolatorMemberArg(lit, parent)) - .flatten - yield identOrSelect + val qualType = + for + parent <- path.tail.headOption.toList + if lit.span.exists && text.charAt(lit.span.point - 1) != '}' + identOrSelect <- path + .collectFirst(interpolatorMemberArg(lit, parent)) + .flatten + yield identOrSelect qualType.flatMap(identOrSelect => val tp = identOrSelect.symbol.info @@ -198,18 +194,19 @@ object InterpolatorCompletions: case None if areSnippetsSupported => "$0" case _ => "" - /** - * contributeScope provides completions to convert a string literal into splice, - * example `"Hello $na@@"`. + /** contributeScope provides completions to convert a string literal into + * splice, example `"Hello $na@@"`. * - * When converting a string literal into an interpolator we need to ensure a few cases: + * When converting a string literal into an interpolator we need to ensure a + * few cases: * - * - escape existing `$` characters into `$$`, which are printed as `\$\$` in order to - * escape the TextMate snippet syntax. - * - wrap completed name in curly braces `s"Hello ${name}_` when the trailing character - * can be treated as an identifier part. - * - insert the leading `s` interpolator. - * - place the cursor at the end of the completed name using TextMate `$0` snippet syntax. + * - escape existing `$` characters into `$$`, which are printed as `\$\$` + * in order to escape the TextMate snippet syntax. + * - wrap completed name in curly braces `s"Hello ${name}_` when the + * trailing character can be treated as an identifier part. + * - insert the leading `s` interpolator. + * - place the cursor at the end of the completed name using TextMate `$0` + * snippet syntax. */ private def contributeScope( text: String, @@ -241,13 +238,13 @@ object InterpolatorCompletions: val range = lit.sourcePos.withEnd(litStartPos).toLsp List(new l.TextEdit(range, "s")) else Nil - val dollarEdits = for - i <- litStartPos to litEndPos - if !hasStringInterpolator && - text.charAt(i) == '$' && i != interpolator.dollar - yield new l.TextEdit(lit.sourcePos.focusAt(i).toLsp, "$") + val dollarEdits = + for + i <- litStartPos to litEndPos + if !hasStringInterpolator && + text.charAt(i) == '$' && i != interpolator.dollar + yield new l.TextEdit(lit.sourcePos.focusAt(i).toLsp, "$") interpolatorEdit ++ dollarEdits - end additionalEdits def newText(symbolName: String, affix: CompletionAffix): String = val out = new StringBuilder() @@ -263,7 +260,6 @@ object InterpolatorCompletions: out.append(suffixEnding(affix.toSuffixOpt, areSnippetsSupported)) if symbolNeedsBraces && !hasClosingBrace then out.append('}') out.toString - end newText val workspaceSymbols = ListBuffer.empty[Symbol] val visitor = new CompilerSearchVisitor(sym => @@ -271,7 +267,7 @@ object InterpolatorCompletions: case IndexedContext.Result.InScope => false case _ => if sym.is(Flags.Module) then workspaceSymbols += sym - true, + true ) if interpolator.name.nonEmpty then search.search(interpolator.name, buildTargetIdentifier, visitor) @@ -298,9 +294,8 @@ object InterpolatorCompletions: None, sym, isWorkspace - ), + ) ) - end collectCompletions val fromWorkspace = workspaceSymbols.toList.collect(collectCompletions(isWorkspace = true)) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala index 5dd3c8dead4d..fb3d43d3bc8b 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala @@ -64,7 +64,6 @@ object KeywordsCompletions: ) && notInComment => CompletionValue.keyword(kw.name, kw.insertText) } - end match end contribute private def checkIfNotInComment( @@ -165,16 +164,15 @@ object KeywordsCompletions: ): TemplateKeywordAvailability = def collectTypeAndModuleDefs( - tree: untpd.Tree, - f: PartialFunction[untpd.Tree, Boolean] - )(using Context): List[untpd.Tree] = { + tree: untpd.Tree, + f: PartialFunction[untpd.Tree, Boolean] + )(using Context): List[untpd.Tree] = val buf = ListBuffer.empty[untpd.Tree] val traverser = new UntypedTreeTraverser: def traverse(tree: untpd.Tree)(using Context) = foldOver(if f(tree) then buf += tree, tree) traverser.traverse(tree) buf.toList - } /* * Finds tree which ends just before cursor positions, that may be extended or derive. @@ -188,13 +186,16 @@ object KeywordsCompletions: def findLastSatisfyingTree(untpdPath: List[Positioned]): Option[untpd.Tree] = untpdPath.headOption.flatMap { case untpdTree: untpd.Tree => - collectTypeAndModuleDefs(untpdTree, { - case typeDef: (untpd.TypeDef | untpd.ModuleDef) => - typeDef.span.exists && typeDef.span.end < pos.queryStart - case _ => false - }) - .filter(tree => tree.span.exists && tree.span.end < pos.queryStart) - .maxByOption(_.span.end) + collectTypeAndModuleDefs( + untpdTree, + { + case typeDef: (untpd.TypeDef | untpd.ModuleDef) => + typeDef.span.exists && typeDef.span.end < pos.queryStart + case _ => false + } + ) + .filter(tree => tree.span.exists && tree.span.end < pos.queryStart) + .maxByOption(_.span.end) case _ => None } @@ -209,9 +210,10 @@ object KeywordsCompletions: val untpdPath = NavigateAST.untypedPath(pos.originalCursorPosition.span) - findLastSatisfyingTree(untpdPath).orElse { enclosing match - case (typeDef: TypeDef) :: _ if typeDef.symbol.isEnumClass => untpdPath.headOption - case _ => None + findLastSatisfyingTree(untpdPath).orElse { + enclosing match + case (typeDef: TypeDef) :: _ if typeDef.symbol.isEnumClass => untpdPath.headOption + case _ => None }.map { case untpd.TypeDef(_, template: Template) => checkForPossibleKeywords(template) case untpd.ModuleDef(_, template: Template) => checkForPossibleKeywords(template) @@ -219,7 +221,6 @@ object KeywordsCompletions: case _ => TemplateKeywordAvailability.default }.getOrElse(TemplateKeywordAvailability.default) - end checkTemplateForNewParents end KeywordsCompletions diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala index 5e809f5b0110..df2bfd88dd4e 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala @@ -5,13 +5,12 @@ import java.net.URI import scala.collection.mutable import scala.collection.mutable.ListBuffer -import scala.jdk.CollectionConverters._ -import scala.meta.internal.metals.ReportContext +import scala.jdk.CollectionConverters.* import scala.meta.internal.pc.CompletionFuzzy import scala.meta.pc.PresentationCompilerConfig import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext -import dotty.tools.toOption import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Contexts.Context @@ -22,12 +21,12 @@ import dotty.tools.dotc.core.StdNames import dotty.tools.dotc.core.Symbols.NoSymbol import dotty.tools.dotc.core.Symbols.Symbol import dotty.tools.dotc.core.Types.AndType +import dotty.tools.dotc.core.Types.AppliedType import dotty.tools.dotc.core.Types.ClassInfo import dotty.tools.dotc.core.Types.NoType import dotty.tools.dotc.core.Types.OrType import dotty.tools.dotc.core.Types.Type import dotty.tools.dotc.core.Types.TypeRef -import dotty.tools.dotc.core.Types.AppliedType import dotty.tools.dotc.typer.Applications.unapplyArgs import dotty.tools.dotc.util.SourcePosition import dotty.tools.pc.AutoImports.AutoImportsGenerator @@ -41,20 +40,19 @@ import org.eclipse.lsp4j as l object CaseKeywordCompletion: - /** - * A `case` completion showing the valid subtypes of the type being deconstructed. + /** A `case` completion showing the valid subtypes of the type being deconstructed. * - * @param selector `selector` of `selector match { cases }` or `EmptyTree` when + * @param selector `selector` of `selector match { cases }` or `EmptyTree` when * not in a match expression (for example `List(1).foreach { case@@ }`. - * @param completionPos the position of the completion - * @param typedtree typed tree of the file, used for generating auto imports - * @param indexedContext - * @param config - * @param parent the parent tree node of the pattern match, for example `Apply(_, _)` when in + * @param completionPos the position of the completion + * @param typedtree typed tree of the file, used for generating auto imports + * @param indexedContext + * @param config + * @param parent the parent tree node of the pattern match, for example `Apply(_, _)` when in * `List(1).foreach { cas@@ }`, used as fallback to compute the type of the selector when * it's `EmptyTree`. - * @param patternOnly `None` for `case@@`, `Some(query)` for `case query@@ =>` or `case ab: query@@ =>` - * @param hasBind `true` when `case _: @@ =>`, if hasBind we don't need unapply completions + * @param patternOnly `None` for `case@@`, `Some(query)` for `case query@@ =>` or `case ab: query@@ =>` + * @param hasBind `true` when `case _: @@ =>`, if hasBind we don't need unapply completions */ def contribute( selector: Tree, @@ -93,7 +91,7 @@ object CaseKeywordCompletion: case (Ident(v), tpe) => v.decoded == value case (Select(_, v), tpe) => v.decoded == value case t => false - .map((_, id) => argPts(id).widen.deepDealias) + .map((_, id) => argPts(id).widen.deepDealiasAndSimplify) /* Parent is a function expecting a case match expression */ case TreeApply(fun, _) if !fun.tpe.isErroneous => fun.tpe.paramInfoss match @@ -103,160 +101,160 @@ object CaseKeywordCompletion: ) => val args = head.argTypes.init if args.length > 1 then - Some(definitions.tupleType(args).widen.deepDealias) - else args.headOption.map(_.widen.deepDealias) + Some(definitions.tupleType(args).widen.deepDealiasAndSimplify) + else args.headOption.map(_.widen.deepDealiasAndSimplify) case _ => None case _ => None case sel => - Some(sel.tpe.widen.deepDealias) + Some(sel.tpe.widen.deepDealiasAndSimplify) selTpe - .collect { case selTpe if selTpe != NoType => - val selectorSym = selTpe.typeSymbol - // Special handle case when selector is a tuple or `FunctionN`. - if definitions.isTupleClass(selectorSym) || definitions.isFunctionClass( - selectorSym - ) - then - if patternOnly.isEmpty then - val selectorTpe = selTpe.show - val tpeLabel = - if !selectorTpe.contains("x$1") /* selector of a function type? */ then - selectorTpe - else selector.symbol.info.show - val label = s"case ${tpeLabel} =>" - List( - CompletionValue.CaseKeyword( - selectorSym, - label, - Some( - if config.isCompletionSnippetsEnabled() then "case ($0) =>" - else "case () =>" - ), - Nil, - range = Some(completionPos.toEditRange), - command = config.parameterHintsCommand().toOption.flatMap(_.asScala), - ) + .collect { + case selTpe if selTpe != NoType => + val selectorSym = selTpe.typeSymbol + // Special handle case when selector is a tuple or `FunctionN`. + if definitions.isTupleClass(selectorSym) || definitions.isFunctionClass( + selectorSym ) - else Nil - else - val result = ListBuffer.empty[SymbolImport] - val isVisited = mutable.Set.empty[Symbol] - - val isBottom = Set[Symbol]( - definitions.NullClass, - definitions.NothingClass, - ) - val tpes = Set(selectorSym, selectorSym.companion) - def isSubclass(sym: Symbol) = tpes.exists(par => sym.isSubClass(par)) - - def visit(symImport: SymbolImport): Unit = - - def recordVisit(s: Symbol): Unit = - if s != NoSymbol && !isVisited(s) then - isVisited += s - recordVisit(s.moduleClass) - recordVisit(s.sourceModule) - - val sym = symImport.sym - if !isVisited(sym) then - recordVisit(sym) - if completionGenerator.fuzzyMatches(symImport.name) then - result += symImport - end visit - - // Step 1: walk through scope members. - def isValid(sym: Symbol) = !tpes(sym) && - !isBottom(sym) && - isSubclass(sym) && - (sym.is(Case) || sym.is(Flags.Module) || sym.isClass) && - (sym.isPublic || sym.isAccessibleFrom(selectorSym.info)) - - indexedContext.scopeSymbols - .foreach(s => - val ts = s.info.deepDealias.typeSymbol - if isValid(ts) then visit(autoImportsGen.inferSymbolImport(ts)) - ) - // Step 2: walk through known subclasses of sealed types. - val sealedDescs = subclassesForType( - selTpe.bounds.hi - ) - sealedDescs.foreach { sym => - val symbolImport = autoImportsGen.inferSymbolImport(sym) - visit(symbolImport) - } - val res = result.result().flatMap { - case si @ SymbolImport(sym, name, importSel) => - completionGenerator.labelForCaseMember(sym, name.value).map { - label => - (si, label) - } - } - val caseItems = - if res.isEmpty then completionGenerator.caseKeywordOnly - else - res.map((si, label) => - completionGenerator.toCompletionValue( - si.sym, + then + if patternOnly.isEmpty then + val selectorTpe = selTpe.show + val tpeLabel = + if !selectorTpe.contains("x$1") /* selector of a function type? */ then + selectorTpe + else selector.symbol.info.show + val label = s"case ${tpeLabel} =>" + List( + CompletionValue.CaseKeyword( + selectorSym, label, - autoImportsGen.renderImports(si.importSel.toList), + Some( + if config.isCompletionSnippetsEnabled() then "case ($0) =>" + else "case () =>" + ), + Nil, + range = Some(completionPos.toEditRange), + command = Option(config.parameterHintsCommand()).flatMap(_.nn.asScala), ) ) + else Nil + else + val result = ListBuffer.empty[SymbolImport] + val isVisited = mutable.Set.empty[Symbol] - includeExhaustive match - // In `List(foo).map { cas@@} we want to provide also `case (exhaustive)` completion - // which works like exhaustive match. - case Some(NewLineOptions(moveToNewLine, addNewLineAfter)) => - val sealedMembers = - val sealedMembers0 = - res.filter((si, _) => sealedDescs.contains(si.sym)) - sortSubclasses( - selectorSym.info, - sealedMembers0, - completionPos.sourceUri, - search, - ) - sealedMembers match - case (_, label) :: tail if tail.length > 0 => - val (newLine, addIndent) = - if moveToNewLine then ("\n\t", "\t") else ("", "") - val insertText = Some( - tail - .map(_._2) - .mkString( - if clientSupportsSnippets then - s"$newLine${label} $$0\n$addIndent" - else s"$newLine${label}\n$addIndent", - s"\n$addIndent", - if addNewLineAfter then "\n" else "", - ) + val isBottom = Set[Symbol]( + definitions.NullClass, + definitions.NothingClass, + ) + val tpes = Set(selectorSym, selectorSym.companion).filter(_ != NoSymbol) + def isSubclass(sym: Symbol) = tpes.exists(par => sym.isSubClass(par)) + + def visit(symImport: SymbolImport): Unit = + + def recordVisit(s: Symbol): Unit = + if s != NoSymbol && !isVisited(s) then + isVisited += s + recordVisit(s.moduleClass) + recordVisit(s.sourceModule) + + val sym = symImport.sym + if !isVisited(sym) then + recordVisit(sym) + if completionGenerator.fuzzyMatches(symImport.name) then + result += symImport + end visit + + // Step 1: walk through scope members. + def isValid(sym: Symbol) = !tpes(sym) && + !isBottom(sym) && + isSubclass(sym) && + (sym.is(Case) || sym.is(Flags.Module) || sym.isClass) && + (sym.isPublic || sym.isAccessibleFrom(selectorSym.info)) + + indexedContext.scopeSymbols + .foreach(s => + val ts = if s.is(Flags.Module) then s.info.typeSymbol else s.dealiasType + if isValid(ts) then + visit(autoImportsGen.inferSymbolImport(ts)) + ) + // Step 2: walk through known subclasses of sealed types. + val sealedDescs = subclassesForType( + selTpe.bounds.hi + ) + sealedDescs.foreach { sym => + val symbolImport = autoImportsGen.inferSymbolImport(sym) + visit(symbolImport) + } + + val res = result.result().flatMap { + case si @ SymbolImport(sym, name, importSel) => + completionGenerator.labelForCaseMember(sym, name.value).map { + label => + (si, label) + } + } + val caseItems = + if res.isEmpty then completionGenerator.caseKeywordOnly + else + res.map((si, label) => + completionGenerator.toCompletionValue( + si.sym, + label, + autoImportsGen.renderImports(si.importSel.toList), ) - val allImports = - sealedMembers.flatMap(_._1.importSel).distinct - val importEdit = autoImportsGen.renderImports(allImports) - val exhaustive = CompletionValue.MatchCompletion( - s"case (exhaustive)", - insertText, - importEdit.toList, - s" ${printer.tpe(selTpe)} (${res.length} cases)", + ) + + includeExhaustive match + // In `List(foo).map { cas@@} we want to provide also `case (exhaustive)` completion + // which works like exhaustive match. + case Some(NewLineOptions(moveToNewLine, addNewLineAfter)) => + val sealedMembers = + val sealedMembers0 = + res.filter((si, _) => sealedDescs.contains(si.sym)) + sortSubclasses( + selectorSym.info, + sealedMembers0, + completionPos.sourceUri, + search, ) - exhaustive :: caseItems - case _ => caseItems - end match - case None => caseItems - end match - end if + sealedMembers match + case (_, label) :: tail if tail.length > 0 => + val (newLine, addIndent) = + if moveToNewLine then ("\n\t", "\t") else ("", "") + val insertText = Some( + tail + .map(_._2) + .mkString( + if clientSupportsSnippets then + s"$newLine${label} $$0\n$addIndent" + else s"$newLine${label}\n$addIndent", + s"\n$addIndent", + if addNewLineAfter then "\n" else "", + ) + ) + val allImports = + sealedMembers.flatMap(_._1.importSel).distinct + val importEdit = autoImportsGen.renderImports(allImports) + val exhaustive = CompletionValue.MatchCompletion( + s"case (exhaustive)", + insertText, + importEdit.toList, + s" ${printer.tpe(selTpe)} (${res.length} cases)", + ) + exhaustive :: caseItems + case _ => caseItems + case None => caseItems + end if } .getOrElse(Nil) end contribute - /** - * A `match` keyword completion to generate an exhaustive pattern match for sealed types. + /** A `match` keyword completion to generate an exhaustive pattern match for sealed types. * - * @param selector the match expression being deconstructed or `EmptyTree` when + * @param selector the match expression being deconstructed or `EmptyTree` when * not in a match expression (for example `List(1).foreach { case@@ }`. - * @param completionPos the position of the completion - * @param typedtree typed tree of the file, used for generating auto imports + * @param completionPos the position of the completion + * @param typedtree typed tree of the file, used for generating auto imports */ def matchContribute( selector: Tree, @@ -277,8 +275,8 @@ object CaseKeywordCompletion: clientSupportsSnippets ) - val tpeStr = printer.tpe(selector.tpe.widen.deepDealias.bounds.hi) - val tpe = selector.typeOpt.widen.deepDealias.bounds.hi match + val tpeStr = printer.tpe(selector.tpe.widen.deepDealiasAndSimplify.bounds.hi) + val tpe = selector.typeOpt.widen.deepDealiasAndSimplify.bounds.hi match case tr @ TypeRef(_, _) => tr.underlying case t => t @@ -293,7 +291,6 @@ object CaseKeywordCompletion: val (labels, imports) = sortedSubclasses.map((si, label) => (label, si.importSel)).unzip - val (obracket, cbracket) = if noIndent then (" {", "}") else ("", "") val basicMatch = CompletionValue.MatchCompletion( "match", @@ -357,10 +354,9 @@ object CaseKeywordCompletion: ) def subclassesForType(tpe: Type)(using Context): List[Symbol] = - /** - * Split type made of & and | types to a list of simple types. - * For example, `(A | D) & (B & C)` returns `List(A, D, B, C). - * Later we use them to generate subclasses of each of these types. + /** Split type made of & and | types to a list of simple types. + * For example, `(A | D) & (B & C)` returns `List(A, D, B, C). + * Later we use them to generate subclasses of each of these types. */ def getParentTypes(tpe: Type, acc: List[Symbol]): List[Symbol] = tpe match @@ -371,10 +367,9 @@ object CaseKeywordCompletion: case t => tpe.typeSymbol :: acc - /** - * Check if `sym` is a subclass of type `tpe`. - * For `class A extends B with C with D` we have to construct B & C & D type, - * because `A <:< (B & C) == false`. + /** Check if `sym` is a subclass of type `tpe`. + * For `class A extends B with C with D` we have to construct B & C & D type, + * because `A <:< (B & C) == false`. */ def isExhaustiveMember(sym: Symbol): Boolean = sym.info match @@ -415,9 +410,7 @@ class CompletionValueGenerator( case Some(Cursor.value) => true case Some(query) => CompletionFuzzy.matches(query.replace(Cursor.value, "").nn, name) - def labelForCaseMember(sym: Symbol, name: String)(using - Context - ): Option[String] = + def labelForCaseMember(sym: Symbol, name: String)(using Context): Option[String] = val isModuleLike = sym.is(Flags.Module) || sym.isOneOf(JavaEnumTrait) || sym.isOneOf( JavaEnumValue @@ -447,14 +440,11 @@ class CompletionValueGenerator( name ) // Symbol is not a case class with unapply deconstructor so we use typed pattern, example `_: User` end if - end pattern val out = if patternOnly.isEmpty then s"case $pattern =>" else pattern Some(out) - end if - end labelForCaseMember def caseKeywordOnly: List[CompletionValue.Keyword] = if patternOnly.isEmpty then @@ -485,11 +475,8 @@ class CompletionValueGenerator( autoImport.toList, range = Some(completionPos.toEditRange) ) - end toCompletionValue - private def tryInfixPattern(sym: Symbol, name: String)(using - Context - ): Option[String] = + private def tryInfixPattern(sym: Symbol, name: String)(using Context): Option[String] = sym.primaryConstructor.paramSymss match case (a :: b :: Nil) :: Nil => Some( @@ -520,7 +507,6 @@ class CompletionValueGenerator( .map(param => param.showName) .mkString("(", ", ", ")") name + suffix - end unapplyPattern private def typePattern( sym: Symbol, @@ -552,11 +538,9 @@ class MatchCaseExtractor( ) == ' ' || text.charAt(completionPos.queryStart - 1) == '.') => Some(qualifier) case _ => None - end MatchExtractor + object CaseExtractor: - def unapply(path: List[Tree])(using - Context - ): Option[(Tree, Tree, Option[NewLineOptions])] = + def unapply(path: List[Tree])(using Context): Option[(Tree, Tree, Option[NewLineOptions])] = path match // foo match // case None => () @@ -605,7 +589,6 @@ class MatchCaseExtractor( ) case _ => None - end CaseExtractor object CasePatternExtractor: def unapply(path: List[Tree])(using Context) = @@ -632,8 +615,6 @@ class MatchCaseExtractor( Some((selector, parent, "")) case _ => None - end CasePatternExtractor - object TypedCasePatternExtractor: def unapply(path: List[Tree])(using Context) = path match @@ -654,7 +635,6 @@ class MatchCaseExtractor( ) => Some((selector, parent, name.decoded)) case _ => None - end TypedCasePatternExtractor end MatchCaseExtractor diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala index dd3a910beb4f..c8fecd2fde1a 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala @@ -1,35 +1,24 @@ package dotty.tools.pc.completions +import scala.annotation.tailrec import scala.util.Try -import dotty.tools.dotc.ast.Trees.ValDef import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.core.Constants.Constant -import dotty.tools.dotc.core.ContextOps.localContext import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Flags -import dotty.tools.dotc.core.Flags.Method import dotty.tools.dotc.core.NameKinds.DefaultGetterName import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.StdNames.* -import dotty.tools.dotc.core.SymDenotations.NoDenotation -import dotty.tools.dotc.core.Symbols -import dotty.tools.dotc.core.Symbols.defn -import dotty.tools.dotc.core.Symbols.NoSymbol import dotty.tools.dotc.core.Symbols.Symbol -import dotty.tools.dotc.core.Types.AndType -import dotty.tools.dotc.core.Types.AppliedType -import dotty.tools.dotc.core.Types.MethodType -import dotty.tools.dotc.core.Types.OrType -import dotty.tools.dotc.core.Types.RefinedType -import dotty.tools.dotc.core.Types.TermRef -import dotty.tools.dotc.core.Types.Type -import dotty.tools.dotc.core.Types.TypeBounds -import dotty.tools.dotc.core.Types.WildcardType +import dotty.tools.dotc.core.Symbols.defn +import dotty.tools.dotc.core.Types.* +import dotty.tools.pc.ApplyArgsExtractor +import dotty.tools.pc.ApplyExtractor import dotty.tools.pc.IndexedContext +import dotty.tools.pc.ParamSymbol import dotty.tools.pc.utils.InteractiveEnrichments.* -import scala.annotation.tailrec object NamedArgCompletions: @@ -37,69 +26,41 @@ object NamedArgCompletions: path: List[Tree], untypedPath: List[untpd.Tree], indexedContext: IndexedContext, - clientSupportsSnippets: Boolean, + clientSupportsSnippets: Boolean )(using ctx: Context): List[CompletionValue] = path match - case (ident: Ident) :: ValDef(_, _, _) :: Block(_, app: Apply) :: _ - if !app.fun.isInfix => + case (ident: Ident) :: ApplyExtractor(app) => contribute( - Some(ident), + ident, app, indexedContext, - clientSupportsSnippets, + clientSupportsSnippets ) - case (ident: Ident) :: rest => - def getApplyForContextFunctionParam(path: List[Tree]): Option[Apply] = - path match - // fun(arg@@) - case (app: Apply) :: _ => Some(app) - // fun(arg@@), where fun(argn: Context ?=> SomeType) - // recursively matched for multiple context arguments, e.g. Context1 ?=> Context2 ?=> SomeType - case (_: DefDef) :: Block(List(_), _: Closure) :: rest => - getApplyForContextFunctionParam(rest) - case _ => None - val contribution = - for - app <- getApplyForContextFunctionParam(rest) - if !app.fun.isInfix - yield - contribute( - Some(ident), - app, - indexedContext, - clientSupportsSnippets, - ) - contribution.getOrElse(Nil) case (app: Apply) :: _ => - /** - * def foo(aaa: Int, bbb: Int, ccc: Int) = ??? - * val x = foo( - * bbb = 123, + /** def foo(aaa: Int, bbb: Int, ccc: Int) = ??? val x = foo( bbb = 123, * ccc = 123, - * @@ - * ) - * In this case, typed path doesn't contain already provided arguments + * @@ ) In this case, typed path doesn't contain already provided + * arguments */ untypedPath match case (ident: Ident) :: (app: Apply) :: _ => contribute( - Some(ident), + ident, app, indexedContext, - clientSupportsSnippets, + clientSupportsSnippets ) case _ => Nil case _ => Nil end match - end contribute private def contribute( - ident: Option[Ident], + ident: Ident, apply: Apply, indexedContext: IndexedContext, - clientSupportsSnippets: Boolean, + clientSupportsSnippets: Boolean )(using context: Context): List[CompletionValue] = def isUselessLiteral(arg: Tree): Boolean = arg match @@ -107,159 +68,14 @@ object NamedArgCompletions: case Literal(Constant(null)) => true // nullLiteral case _ => false - def collectArgss(a: Apply): List[List[Tree]] = - def stripContextFuntionArgument(argument: Tree): List[Tree] = - argument match - case Block(List(d: DefDef), _: Closure) => - d.rhs match - case app: Apply => - app.args - case b @ Block(List(_: DefDef), _: Closure) => - stripContextFuntionArgument(b) - case _ => Nil - case v => List(v) - - val args = a.args.flatMap(stripContextFuntionArgument) - a.fun match - case app: Apply => collectArgss(app) :+ args - case _ => List(args) - end collectArgss - - val method = apply.fun - - val argss = collectArgss(apply) - - def fallbackFindApply(sym: Symbol) = - sym.info.member(nme.apply) match - case NoDenotation => Nil - case den => List(den.symbol) - - // fallback for when multiple overloaded methods match the supplied args - def fallbackFindMatchingMethods() = - def maybeNameAndIndexedContext( - method: Tree - ): Option[(Name, IndexedContext)] = - method match - case Ident(name) => Some((name, indexedContext)) - case Select(This(_), name) => Some((name, indexedContext)) - case Select(from, name) => - val symbol = from.symbol - val ownerSymbol = - if symbol.is(Method) && symbol.owner.isClass then - Some(symbol.owner) - else Try(symbol.info.classSymbol).toOption - ownerSymbol.map(sym => - (name, IndexedContext(context.localContext(from, sym))) - ) - case Apply(fun, _) => maybeNameAndIndexedContext(fun) - case _ => None - val matchingMethods = - for - (name, indexedContext) <- maybeNameAndIndexedContext(method) - potentialMatches <- indexedContext.findSymbol(name) - yield - potentialMatches.collect { - case m - if m.is(Flags.Method) && - m.vparamss.length >= argss.length && - Try(m.isAccessibleFrom(apply.symbol.info)).toOption - .getOrElse(false) && - m.vparamss - .zip(argss) - .reverse - .zipWithIndex - .forall { case (pair, index) => - FuzzyArgMatcher(m.tparams) - .doMatch(allArgsProvided = index != 0, ident) - .tupled(pair) - } => - m - } - matchingMethods.getOrElse(Nil) - end fallbackFindMatchingMethods - - val matchingMethods: List[Symbols.Symbol] = - if method.symbol.paramSymss.nonEmpty then - val allArgsAreSupplied = - val vparamss = method.symbol.vparamss - vparamss.length == argss.length && vparamss - .zip(argss) - .lastOption - .exists { case (baseParams, baseArgs) => - baseArgs.length == baseParams.length - } - // ``` - // m(arg : Int) - // m(arg : Int, anotherArg : Int) - // m(a@@) - // ``` - // complier will choose the first `m`, so we need to manually look for the other one - if allArgsAreSupplied then - val foundPotential = fallbackFindMatchingMethods() - if foundPotential.contains(method.symbol) then foundPotential - else method.symbol :: foundPotential - else List(method.symbol) - else if method.symbol.is(Method) || method.symbol == NoSymbol then - fallbackFindMatchingMethods() - else fallbackFindApply(method.symbol) - end if - end matchingMethods + val argsAndParams = ApplyArgsExtractor.getArgsAndParams( + Some(indexedContext), + apply, + ident.span + ) - val allParams = matchingMethods.flatMap { methodSym => - val vparamss = methodSym.vparamss - - // get params and args we are interested in - // e.g. - // in the following case, the interesting args and params are - // - params: [apple, banana] - // - args: [apple, b] - // ``` - // def curry(x: Int)(apple: String, banana: String) = ??? - // curry(1)(apple = "test", b@@) - // ``` - val (baseParams0, baseArgs) = - vparamss.zip(argss).lastOption.getOrElse((Nil, Nil)) - - val baseParams: List[ParamSymbol] = - def defaultBaseParams = baseParams0.map(JustSymbol(_)) - @tailrec - def getRefinedParams(refinedType: Type, level: Int): List[ParamSymbol] = - if level > 0 then - val resultTypeOpt = - refinedType match - case RefinedType(AppliedType(_, args), _, _) => args.lastOption - case AppliedType(_, args) => args.lastOption - case _ => None - resultTypeOpt match - case Some(resultType) => getRefinedParams(resultType, level - 1) - case _ => defaultBaseParams - else - refinedType match - case RefinedType(AppliedType(_, args), _, MethodType(ri)) => - baseParams0.zip(ri).zip(args).map { case ((sym, name), arg) => - RefinedSymbol(sym, name, arg) - } - case _ => defaultBaseParams - // finds param refinements for lambda expressions - // val hello: (x: Int, y: Int) => Unit = (x, _) => println(x) - @tailrec - def refineParams(method: Tree, level: Int): List[ParamSymbol] = - method match - case Select(Apply(f, _), _) => refineParams(f, level + 1) - case Select(h, name) => - // for Select(foo, name = apply) we want `foo.symbol` - if name == nme.apply then getRefinedParams(h.symbol.info, level) - else getRefinedParams(method.symbol.info, level) - case Apply(f, _) => - refineParams(f, level + 1) - case _ => getRefinedParams(method.symbol.info, level) - refineParams(method, 0) - end baseParams - - val args = ident - .map(i => baseArgs.filterNot(_ == i)) - .getOrElse(baseArgs) - .filterNot(isUselessLiteral) + val allParams = argsAndParams.flatMap { case (baseArgs, baseParams) => + val args = baseArgs.filterNot(a => a == ident || isUselessLiteral(a)) @tailrec def isDefaultArg(t: Tree): Boolean = t match @@ -294,11 +110,10 @@ object NamedArgCompletions: ) } - val prefix = ident - .map(_.name.toString) - .getOrElse("") - .replace(Cursor.value, "") - .nn + val prefix = + ident.name.toString + .replace(Cursor.value, "") + .nn val params: List[ParamSymbol] = allParams @@ -331,7 +146,7 @@ object NamedArgCompletions: allParams.exists(param => param.name.startsWith(prefix)) def isExplicitlyCalled = suffix.startsWith(prefix) def hasParamsToFill = allParams.count(!_.symbol.is(Flags.HasDefault)) > 1 - if clientSupportsSnippets && matchingMethods.length == 1 && (shouldShow || isExplicitlyCalled) && hasParamsToFill + if clientSupportsSnippets && argsAndParams.length == 1 && (shouldShow || isExplicitlyCalled) && hasParamsToFill then val editText = allParams.zipWithIndex .collect { @@ -339,14 +154,19 @@ object NamedArgCompletions: s"${param.nameBackticked.replace("$", "$$")} = $${${index + 1}${findDefaultValue(param)}}" } .mkString(", ") + val labelText = allParams + .collect { + case param if !param.symbol.is(Flags.HasDefault) => + s"${param.nameBackticked.replace("$", "$$")} = ???" + } + .mkString(", ") List( CompletionValue.Autofill( - editText + editText, + labelText ) ) else List.empty - end if - end fillAllFields def findPossibleDefaults(): List[CompletionValue] = params.flatMap { param => @@ -356,7 +176,7 @@ object NamedArgCompletions: param.nameBackticked + " = " + memberName + " " CompletionValue.namedArg( label = editText, - param, + param ) } } @@ -364,78 +184,9 @@ object NamedArgCompletions: params.map(p => CompletionValue.namedArg( s"${p.nameBackticked} = ", - p, + p ) ) ::: findPossibleDefaults() ::: fillAllFields() end contribute - extension (method: Symbols.Symbol) - def vparamss(using Context) = method.filteredParamss(_.isTerm) - def tparams(using Context) = method.filteredParamss(_.isType).flatten - def filteredParamss(f: Symbols.Symbol => Boolean)(using Context) = - method.paramSymss.filter(params => params.forall(f)) end NamedArgCompletions - -class FuzzyArgMatcher(tparams: List[Symbols.Symbol])(using Context): - - /** - * A heuristic for checking if the passed arguments match the method's arguments' types. - * For non-polymorphic methods we use the subtype relation (`<:<`) - * and for polymorphic methods we use a heuristic. - * We check the args types not the result type. - */ - def doMatch( - allArgsProvided: Boolean, - ident: Option[Ident] - )(expectedArgs: List[Symbols.Symbol], actualArgs: List[Tree]) = - (expectedArgs.length == actualArgs.length || - (!allArgsProvided && expectedArgs.length >= actualArgs.length)) && - actualArgs.zipWithIndex.forall { - case (arg: Ident, _) if ident.contains(arg) => true - case (NamedArg(name, arg), _) => - expectedArgs.exists { expected => - expected.name == name && (!arg.hasType || arg.typeOpt.unfold - .fuzzyArg_<:<(expected.info)) - } - case (arg, i) => - !arg.hasType || arg.typeOpt.unfold.fuzzyArg_<:<(expectedArgs(i).info) - } - - extension (arg: Type) - def fuzzyArg_<:<(expected: Type) = - if tparams.isEmpty then arg <:< expected - else arg <:< substituteTypeParams(expected) - def unfold = - arg match - case arg: TermRef => arg.underlying - case e => e - - private def substituteTypeParams(t: Type): Type = - t match - case e if tparams.exists(_ == e.typeSymbol) => - val matchingParam = tparams.find(_ == e.typeSymbol).get - matchingParam.info match - case b @ TypeBounds(_, _) => WildcardType(b) - case _ => WildcardType - case o @ OrType(e1, e2) => - OrType(substituteTypeParams(e1), substituteTypeParams(e2), o.isSoft) - case AndType(e1, e2) => - AndType(substituteTypeParams(e1), substituteTypeParams(e2)) - case AppliedType(et, eparams) => - AppliedType(et, eparams.map(substituteTypeParams)) - case _ => t - -end FuzzyArgMatcher - -sealed trait ParamSymbol: - def name: Name - def info: Type - def symbol: Symbol - def nameBackticked(using Context) = name.decoded.backticked - -case class JustSymbol(symbol: Symbol)(using Context) extends ParamSymbol: - def name: Name = symbol.name - def info: Type = symbol.info - -case class RefinedSymbol(symbol: Symbol, name: Name, info: Type) - extends ParamSymbol diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala index f5c15ca6df0e..100d9ddeecc9 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/OverrideCompletions.scala @@ -3,12 +3,12 @@ package completions import java.util as ju -import scala.jdk.CollectionConverters._ -import scala.meta.internal.metals.ReportContext +import scala.jdk.CollectionConverters.* import scala.meta.pc.OffsetParams import scala.meta.pc.PresentationCompilerConfig import scala.meta.pc.PresentationCompilerConfig.OverrideDefFormat import scala.meta.pc.SymbolSearch +import scala.meta.pc.reports.ReportContext import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.ast.tpd.Tree @@ -18,8 +18,8 @@ import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.NameKinds.DefaultGetterName import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.core.StdNames -import dotty.tools.dotc.core.SymDenotations.SymDenotation import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.SymDenotations.SymDenotation import dotty.tools.dotc.core.Types.Type import dotty.tools.dotc.interactive.Interactive import dotty.tools.dotc.interactive.InteractiveDriver @@ -39,11 +39,11 @@ object OverrideCompletions: private def defaultIndent(tabIndent: Boolean) = if tabIndent then 1 else 2 - /** - * @param td A surrounded type definition being complete - * @param filterName A prefix string for filtering, if None no filter - * @param start The starting point of the completion. For example, starting point is `*` - * `*override def f|` (where `|` represents the cursor position). + /** @param td A surrounded type definition being complete + * @param filterName A prefix string for filtering, if None no filter + * @param start The starting point of the completion. For example, starting + * point is `*` `*override def f|` (where `|` represents the cursor + * position). */ def contribute( td: TypeDef, @@ -79,7 +79,6 @@ object OverrideCompletions: !sym.isSetter && // exclude symbols desugared by default args !sym.name.is(DefaultGetterName) - end isOverrideable // Given the base class `trait Foo { def foo: Int; val bar: Int; var baz: Int }` // and typing `def @@` in the subclass of `Foo`, // suggest `def foo` and exclude `val bar`, and `var baz` from suggestion @@ -171,7 +170,6 @@ object OverrideCompletions: dd.symbol.owner.decodedName ) case _ => None - end FindTypeDef val uri = params.uri().nn val text = params.text().nn @@ -191,9 +189,7 @@ object OverrideCompletions: template :: path case path => path - val indexedContext = IndexedContext( - Interactive.contextOfPath(path)(using newctx) - ) + val indexedContext = IndexedContext(pos, path, newctx) import indexedContext.ctx lazy val autoImportsGen = AutoImports.generator( @@ -278,14 +274,16 @@ object OverrideCompletions: then "\n" + indentChar * necessaryIndent else "" (indent, indent, lastIndent) - end calcIndent val abstractMembers = defn.tpe.abstractTermMembers.map(_.symbol).groupBy(_.owner).map { - case (owner, members) => (owner, members.sortWith{ (sym1, sym2) => - if(sym1.sourcePos.exists && sym2.sourcePos.exists) - sym1.sourcePos.start <= sym2.sourcePos.start - else !sym2.sourcePos.exists - }) + case (owner, members) => ( + owner, + members.sortWith { (sym1, sym2) => + if sym1.sourcePos.exists && sym2.sourcePos.exists then + sym1.sourcePos.start <= sym2.sourcePos.start + else !sym2.sourcePos.exists + } + ) }.toSeq.sortBy(_._1.name.decoded).flatMap(_._2) val caseClassOwners = Set("Product", "Equals") @@ -435,17 +433,15 @@ object OverrideCompletions: sym.symbol, seenFrom, additionalMods = - if overrideKeyword.nonEmpty then List(overrideKeyword) else Nil, + if overrideKeyword.nonEmpty then List(overrideKeyword) else Nil ) else printer.defaultValueSignature( sym.symbol, seenFrom, additionalMods = - if overrideKeyword.nonEmpty then List(overrideKeyword) else Nil, + if overrideKeyword.nonEmpty then List(overrideKeyword) else Nil ) - end if - end signature val label = s"$overrideDefLabel$signature" val stub = @@ -465,20 +461,17 @@ object OverrideCompletions: private val interestingFlags = Flags.Method | Flags.Mutable - /** - * Infer the editPosition for "implement all" code action for the given TypeDef. + /** Infer the editPosition for "implement all" code action for the given + * TypeDef. * - * If there're braces like `class FooImpl extends Foo {}`, - * editPosition will be inside the braces. - * Otherwise, e.g. `class FooImpl extends Foo`, editPosition will be - * after the `extends Foo`. + * If there're braces like `class FooImpl extends Foo {}`, editPosition will + * be inside the braces. Otherwise, e.g. `class FooImpl extends Foo`, + * editPosition will be after the `extends Foo`. * - * @param text the whole text of the source file - * @param td the class/object to impement all + * @param text the whole text of the source file + * @param td the class/object to impement all */ - private def inferEditPosition(text: String, defn: TargetDef)(using - Context - ): SourcePosition = + private def inferEditPosition(text: String, defn: TargetDef)(using Context): SourcePosition = val span = hasBracesOrColon(text, defn) .map { offset => defn.sourcePos.span.withStart(offset + 1).withEnd(offset + 1) @@ -487,11 +480,8 @@ object OverrideCompletions: defn.sourcePos.span.withStart(defn.span.end) }) defn.sourcePos.withSpan(span) - end inferEditPosition - private def hasBracesOrColon(text: String, defn: TargetDef)(using - Context - ): Option[Int] = + private def hasBracesOrColon(text: String, defn: TargetDef)(using Context): Option[Int] = def hasSelfTypeAnnot = defn match case td: TypeDef => td.rhs match @@ -505,16 +495,13 @@ object OverrideCompletions: else text.indexOf("{", start) if braceOffset > 0 && braceOffset < defn.span.end then Some(braceOffset) else hasColon(text, defn) - end hasBracesOrColon - private def hasColon(text: String, defn: TargetDef)(using - Context - ): Option[Int] = + private def hasColon(text: String, defn: TargetDef)(using Context): Option[Int] = defn match - case td: TypeDef if text.charAt(td.rhs.span.end) == ':' => + case td: TypeDef if (td.rhs.span.end < text.length) && text.charAt(td.rhs.span.end) == ':' => Some(td.rhs.span.end) - case TypeDef(_, temp : Template) => - temp.parentsOrDerived.lastOption.map(_.span.end).filter(text.charAt(_) == ':') + case TypeDef(_, temp: Template) => + temp.parentsOrDerived.lastOption.map(_.span.end).filter(idx => text.length > idx && text.charAt(idx) == ':') case _ => None private def fallbackFromParent(parent: Tree, name: String)(using Context) = @@ -546,7 +533,7 @@ object OverrideCompletions: completing, dd.sourcePos.start, true, - None, + None ) ) @@ -563,7 +550,7 @@ object OverrideCompletions: None, ident.sourcePos.start, false, - None, + None ) ) @@ -580,7 +567,7 @@ object OverrideCompletions: None, id.sourcePos.start, true, - None, + None ) ) @@ -597,7 +584,7 @@ object OverrideCompletions: None, id.sourcePos.start, false, - Some(id.name.show), + Some(id.name.show) ) ) @@ -614,7 +601,7 @@ object OverrideCompletions: None, sel.sourcePos.start, false, - Some(name.show), + Some(name.show) ) ) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/ScalaCliCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/ScalaCliCompletions.scala index e2a0a033ee6b..e076375849a8 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/ScalaCliCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/ScalaCliCompletions.scala @@ -13,14 +13,20 @@ class ScalaCliCompletions( ): def unapply(path: List[Tree]) = def scalaCliDep = CoursierComplete.isScalaCliDep( - pos.lineContent.take(pos.column).stripPrefix("/* */ +class Script + +/** < script >alert('hello') */ +class ScriptWithSpaces + +/**  */ +class FakeSafeScript + +/** Example < Second <: Third <= Fourth */ +class NotATag + +/** Example < Second >: Third */ +class NotATagButHasGreaterThan + +/** aalert('hello') + */ +class TagOutsideCode + +/** + * see [[<:<]], or [[>:>]] + */ +class LinkToTagLike \ No newline at end of file diff --git a/scaladoc-testcases/src/tests/snippetTestcaseHidden.scala b/scaladoc-testcases/src/tests/snippetTestcaseHidden.scala new file mode 100644 index 000000000000..99d4fdbe53c9 --- /dev/null +++ b/scaladoc-testcases/src/tests/snippetTestcaseHidden.scala @@ -0,0 +1,14 @@ +package tests.snippetTestcaseHidden + +class SnippetTestcaseHidden: + /** + * ```scala sc-hidden sc-name:preamble + * val xs: List[Int] = List(1, 2, 3) + * ``` + * + * SNIPPET(OUTERLINEOFFSET:11,OUTERCOLUMNOFFSET:6,INNERLINEOFFSET:4,INNERCOLUMNOFFSET:2) + * ```scala sc:compile sc-compile-with:preamble + * val ys = xs.map(x => x * 2) + * ``` + */ + def a = 3 diff --git a/scaladoc-testcases/src/tests/snippetTestcaseMacro.scala b/scaladoc-testcases/src/tests/snippetTestcaseMacro.scala new file mode 100644 index 000000000000..e29c37eb91d1 --- /dev/null +++ b/scaladoc-testcases/src/tests/snippetTestcaseMacro.scala @@ -0,0 +1,21 @@ +package tests.snippetTestcaseMacro + +import scala.quoted.* + +/** Regression test for https://github.com/scala/scala3/issues/24946 + * Scaladoc snippet compilation fails when the snippet calls a macro + * defined in the same compilation unit. + */ +object MacroLib: + inline def hello: String = ${ helloImpl } + + def helloImpl(using Quotes): Expr[String] = Expr("Hello") + +class SnippetTestcaseMacro: + /** + * SNIPPET(OUTERLINEOFFSET:17,OUTERCOLUMNOFFSET:6,INNERLINEOFFSET:4,INNERCOLUMNOFFSET:2) + * ```scala sc:compile + * MacroLib.hello + * ``` + */ + def a = 3 diff --git a/scaladoc-testcases/src/tests/traitSignatures.scala b/scaladoc-testcases/src/tests/traitSignatures.scala index 8979c308a61c..98e5fe8ab980 100644 --- a/scaladoc-testcases/src/tests/traitSignatures.scala +++ b/scaladoc-testcases/src/tests/traitSignatures.scala @@ -7,4 +7,6 @@ trait B extends A trait C(a: Int) -trait D(b: Double) extends C, A \ No newline at end of file +trait D(b: Double) extends C, A + +trait E extends Any diff --git a/scaladoc/src/dotty/tools/scaladoc/api.scala b/scaladoc/src/dotty/tools/scaladoc/api.scala index 8ff40644fac2..878de14b3393 100644 --- a/scaladoc/src/dotty/tools/scaladoc/api.scala +++ b/scaladoc/src/dotty/tools/scaladoc/api.scala @@ -43,6 +43,7 @@ enum Modifier(val name: String, val prefix: Boolean): case Open extends Modifier("open", true) case Transparent extends Modifier("transparent", true) case Infix extends Modifier("infix", true) + case AbsOverride extends Modifier("abstract override", 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) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/DocRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/DocRenderer.scala index 1d634ab75dba..12de62a3a733 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/DocRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/DocRenderer.scala @@ -83,8 +83,8 @@ class DocRender(signatureRenderer: SignatureRenderer)(using DocContext): case Underline(text) => span(cls:="underline")(renderElement(text)) case Bold(text) => strong(renderElement(text)) case Monospace(text) => code(renderElement(text)) - case Superscript(text) => span(cls:="superscript")(renderElement(text)) // TODO implement style - case Subscript(text) => span(cls:="subscript")(renderElement(text)) // TODO implement style + case Superscript(text) => sup(renderElement(text)) + case Subscript(text) => sub(renderElement(text)) case Link(target, body) => renderLink(target, default => body.fold[TagArg](default)(renderElement)) case Text(text) => raw(text.escapeReservedTokens) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala index 7e5394b598fd..98479b8b95ea 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala @@ -80,9 +80,12 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext signatureRenderer.renderLink(stripQuotes(text), dri) case Annotation.UnresolvedParameter(_, value) => stripQuotes(value) + // named arguments might be used, so we can't always rely on the order of the parameters val (named, unnamed) = a.params.partition(_.name.nonEmpty) - val message = named.find(_.name.get == "message") - val since = named.find(_.name.get == "since") + val message: Option[Annotation.AnnotationParameter] = + named.find(_.name.get == "message").fold(unnamed.lift(0))(Some(_)) + val since: Option[Annotation.AnnotationParameter] = + named.find(_.name.get == "since").fold(unnamed.lift(1))(Some(_)) val content = ( Seq( diff --git a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala index 489720cc5936..ddfb2f93354b 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala @@ -55,11 +55,8 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite .map(relativizeIfNeeded) .map(_.toFile) .filter(_.exists) - .map(loadTemplateFile(_)) - val title = ( - optionTitle.map(TemplateName.SidebarDefined(_)) ++ - indexPageOpt.map(_.title) - ).headOption.getOrElse { + .map(loadTemplateFile(_, optionTitle.map(TemplateName.SidebarDefined(_)))) + val title = indexPageOpt.map(_.title).getOrElse { report.error(s"Title for subsection needs to be set in YAML config or in index file") TemplateName.FilenameDefined("unnamed_section") } @@ -88,7 +85,7 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite val file = path.toFile val title = optionTitle.map(TemplateName.SidebarDefined(_)) val templateFile = loadTemplateFile(file, title) - LoadedTemplate(templateFile, List.empty, pathFromRoot.resolve(file.getName).toFile, hidden) + LoadedTemplate(templateFile, List.empty, pathFromRoot.resolve(file.getName).toHtml.toFile, hidden) } val rootTemplate = LoadedTemplate(rootIndex, yamlRoot.nested.map(c => loadChild(ctx.docsPath)(c)) ++ loadBlog(), rootDest) val mappings = createMapping(rootTemplate) @@ -156,7 +153,7 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite val ((year, month, day), name) = splitDateName(templateFile) val destPath = ctx.docsPath.resolve(defaultDirectory).resolve(year).resolve(month).resolve(day).resolve(name) val date = dateFrom(templateFile, s"$year-$month-$day") - date -> LoadedTemplate(templateFile, List.empty, destPath.toFile) + date -> LoadedTemplate(templateFile, List.empty, destPath.toHtml.toFile) }.sortBy(_._1).reverse.map(_._2) Some(LoadedTemplate(indexPage, posts, indexDest.toFile)) @@ -189,7 +186,7 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite } else if (currRoot.exists && ctx.siteExtensions.exists(ext => currRoot.getName.endsWith(ext))) { val templateFile = loadTemplateFile(currRoot) - Some(LoadedTemplate(templateFile, List.empty, destMappingFunc(templateFile.file))) + Some(LoadedTemplate(templateFile, List.empty, destMappingFunc(templateFile.file).toPath.toHtml.toFile)) } else None } @@ -208,4 +205,4 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite private def toHtml: Path = p.getParent.resolve(p.getFileName.toString match case f if f.endsWith(".md") => f.stripSuffix(".md") + ".html" case f => f - ) \ No newline at end of file + ) diff --git a/scaladoc/src/dotty/tools/scaladoc/site/templates.scala b/scaladoc/src/dotty/tools/scaladoc/site/templates.scala index 92e0096e5af1..4a62b10ba6f0 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/templates.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/templates.scala @@ -81,8 +81,8 @@ case class TemplateFile( val path = Some(Paths.get(file.getAbsolutePath)) val pathBasedArg = ssctx.snippetCompilerArgs.get(path) val sourceFile = dotty.tools.dotc.util.SourceFile(dotty.tools.io.AbstractFile.getFile(path.get), scala.io.Codec.UTF8) - (str: String, lineOffset: SnippetChecker.LineOffset, argOverride: Option[SCFlags]) => { - val arg = argOverride.fold(pathBasedArg)(pathBasedArg.overrideFlag(_)) + (str: String, lineOffset: SnippetChecker.LineOffset, argOverride: Option[SnippetCompilerArg]) => { + val arg = argOverride.fold(pathBasedArg)(pathBasedArg.merge(_)) val compilerData = SnippetCompilerData( "staticsitesnippet", SnippetCompilerData.Position(configOffset - 1, 0) diff --git a/scaladoc/src/dotty/tools/scaladoc/snippets/FlexmarkSnippetProcessor.scala b/scaladoc/src/dotty/tools/scaladoc/snippets/FlexmarkSnippetProcessor.scala index c92853816d16..dd9cfbe6cc58 100644 --- a/scaladoc/src/dotty/tools/scaladoc/snippets/FlexmarkSnippetProcessor.scala +++ b/scaladoc/src/dotty/tools/scaladoc/snippets/FlexmarkSnippetProcessor.scala @@ -21,7 +21,7 @@ object FlexmarkSnippetProcessor: val lineOffset = node.getStartLineNumber + preparsed.fold(0)(_.strippedLinesBeforeNo) val info = node.getInfo.toString.split(" ") if info.contains("scala") then { - val argOverride = info + val flagOverride = info .find(_.startsWith("sc:")) .map(_.stripPrefix("sc:")) .map(SCFlagsParser.parse) @@ -34,6 +34,21 @@ object FlexmarkSnippetProcessor: ) None }) + + val scalacOptions: Seq[String] = info + .toIndexedSeq + .filter(_.startsWith("sc-opts:")) + .flatMap(_.stripPrefix("sc-opts:").split(",").map(_.trim).toSeq) + + val isHidden = info.contains("sc-hidden") + + val argOverride: Option[SnippetCompilerArg] = + (flagOverride, scalacOptions) match { + case (None, Seq()) => None + case (Some(flag), opts) => Some(SnippetCompilerArg(flag, opts)) + case (None, opts) => Some(SnippetCompilerArg(SCFlags.Compile, opts)) + } + val id = info .find(_.startsWith("sc-name:")) .map(_.stripPrefix("sc-name:")) @@ -75,8 +90,13 @@ object FlexmarkSnippetProcessor: result } - node.insertBefore(ExtendedFencedCodeBlock(id, node, snippetCompilationResult)) + if isHidden && id.isEmpty then + report.warning("Snippet with sc-hidden should also have sc-name: to be useful as a preamble for other snippets") + + val extendedNode = ExtendedFencedCodeBlock(id, node, snippetCompilationResult) + if !isHidden then node.insertBefore(extendedNode) node.unlink() + id.fold(snippetMap)(id => val snippetAsImport = s"""|//{i:$id |$snippet diff --git a/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetChecker.scala b/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetChecker.scala index 8f8b73a576d1..4f90fbcee984 100644 --- a/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetChecker.scala +++ b/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetChecker.scala @@ -59,4 +59,4 @@ class SnippetChecker(val args: Scaladoc.Args)(using cctx: CompilerContext): object SnippetChecker: type LineOffset = Int - type SnippetCheckingFunc = (String, LineOffset, Option[SCFlags]) => Option[SnippetCompilationResult] + type SnippetCheckingFunc = (String, LineOffset, Option[SnippetCompilerArg]) => Option[SnippetCompilationResult] diff --git a/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompiler.scala b/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompiler.scala index 03fdd4e849ff..ee3d71e43259 100644 --- a/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompiler.scala +++ b/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompiler.scala @@ -5,6 +5,7 @@ import dotty.tools.io.{AbstractFile, VirtualDirectory} import dotty.tools.dotc.Driver import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Mode +import dotty.tools.dotc.core.MacroClassLoader import dotty.tools.dotc.config.Settings.Setting._ import dotty.tools.dotc.interfaces.{ SourcePosition => ISourcePosition } import dotty.tools.dotc.ast.Trees.Tree @@ -41,7 +42,7 @@ class SnippetCompiler( ctx.setSetting(setting.setting, setting.value) } res.initialize()(using res) - res + MacroClassLoader.init(res) private val scala3Compiler = new Compiler @@ -85,7 +86,9 @@ class SnippetCompiler( private def additionalMessages(wrappedSnippet: WrappedSnippet, arg: SnippetCompilerArg, sourceFile: SourceFile, context: Context): Seq[SnippetCompilerMessage] = { Option.when(arg.flag == SCFlags.Fail && !context.reporter.hasErrors)( - SnippetCompilerMessage(None, "Snippet should not compile but compiled successfully", MessageLevel.Error) + SnippetCompilerMessage( + Some(Position(SourcePosition(sourceFile, NoSpan), wrappedSnippet.outerLineOffset)), + "Snippet should not compile but compiled successfully", MessageLevel.Error) ).toList } @@ -99,12 +102,19 @@ class SnippetCompiler( arg: SnippetCompilerArg, sourceFile: SourceFile ): SnippetCompilationResult = { - val context = SnippetDriver.currentCtx.fresh + val baseContext = SnippetDriver.currentCtx.fresh .setSetting( SnippetDriver.currentCtx.settings.outputDir, target ) .setReporter(new StoreReporter) + val context = + if arg.scalacOptions.isEmpty then baseContext + else + val args = arg.scalacOptions.toArray + SnippetDriver.setup(args, baseContext) match + case Some((_, ctx)) => ctx + case None => baseContext val run = newRun(using context) run.compileFromStrings(List(wrappedSnippet.snippet)) diff --git a/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompilerArgs.scala b/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompilerArgs.scala index 35a89d985d8e..b5a3f7dfe123 100644 --- a/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompilerArgs.scala +++ b/scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompilerArgs.scala @@ -3,8 +3,11 @@ package snippets import java.nio.file.Path -case class SnippetCompilerArg(flag: SCFlags): +case class SnippetCompilerArg(flag: SCFlags, scalacOptions: Seq[String] = Seq.empty): def overrideFlag(f: SCFlags): SnippetCompilerArg = copy(flag = f) + def withScalacOptions(opts: Seq[String]): SnippetCompilerArg = copy(scalacOptions = scalacOptions ++ opts) + def merge(other: SnippetCompilerArg): SnippetCompilerArg = + SnippetCompilerArg(other.flag, scalacOptions ++ other.scalacOptions) enum SCFlags(val flagName: String): case Compile extends SCFlags("compile") diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala index a5e32c7332bd..81415377beeb 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/BasicSupport.scala @@ -42,7 +42,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 fqNameWhitelist = Set( + val fqNameAllowlist = Set( "scala.specialized", "scala.throws", "scala.transient", @@ -56,7 +56,7 @@ trait BasicSupport: ) val documentedSymbol = summon[Quotes].reflect.Symbol.requiredClass("java.lang.annotation.Documented") val annotations = sym.annotations.filter { a => - a.tpe.typeSymbol.hasAnnotation(documentedSymbol) || fqNameWhitelist.contains(a.symbol.fullName) + a.tpe.typeSymbol.hasAnnotation(documentedSymbol) || fqNameAllowlist.contains(a.symbol.fullName) } annotations.map(parseAnnotation).reverse diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 4514cb42c9c3..d6e426f9c7fc 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -115,7 +115,7 @@ trait ClassLikeSupport: getSupertypesGraph(LinkToType(selfSignature, classDef.symbol.dri, bareClasslikeKind(classDef.symbol)), unpackTreeToClassDef(classDef).parents) ) - val kind = if intrinsicClassDefs.contains(classDef.symbol) then Kind.Class(Nil, Nil) else kindForClasslike(classDef) + val kind = if intrinsicClassDefs.contains(classDef.symbol) then bareClasslikeKind(classDef.symbol) else kindForClasslike(classDef) val baseMember = mkMember(classDef.symbol, kind, selfSignature)( modifiers = modifiers, @@ -284,7 +284,7 @@ trait ClassLikeSupport: case t: TypeTree => t.tpe.typeSymbol case tree if tree.symbol.isClassConstructor => tree.symbol.owner case tree => tree.symbol - if parentSymbol != defn.ObjectClass && parentSymbol != defn.AnyClass && !parentSymbol.isHiddenByVisibility + if parentSymbol != defn.ObjectClass && !parentSymbol.isHiddenByVisibility yield (parentTree, parentSymbol) def getConstructors: List[Symbol] = c.membersToDocument.collect { @@ -443,12 +443,13 @@ trait ClassLikeSupport: val inlinePrefix = if argument.symbol.flags.is(Flags.Inline) then "inline " else "" val nameIfNotSynthetic = Option.when(!argument.symbol.flags.is(Flags.Synthetic))(argument.symbol.normalizedName) val name = argument.symbol.normalizedName + val defaultValue = Option.when(argument.symbol.flags.is(Flags.HasDefault))(Plain(" = ...")) api.TermParameter( argument.symbol.getAnnotations(), inlinePrefix + prefix(argument.symbol), nameIfNotSynthetic, argument.symbol.dri, - memberInfo.get(name).fold(argument.tpt.asSignature(classDef))(_.asSignature(classDef)), + memberInfo.get(name).fold(argument.tpt.asSignature(classDef))(_.asSignature(classDef)) :++ defaultValue, isExtendedSymbol, isGrouped ) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala index 5bc1b98a7fff..0b8c4a2b5370 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala @@ -98,7 +98,8 @@ object SymOps: Flags.Open -> Modifier.Open, Flags.Override -> Modifier.Override, Flags.Case -> Modifier.Case, - Flags.Opaque -> Modifier.Opaque + Flags.Opaque -> Modifier.Opaque, + Flags.AbsOverride -> Modifier.AbsOverride, ).collect { case (flag, mod) if sym.flags.is(flag) => mod } @@ -222,12 +223,15 @@ class SymOpsWithLinkCache: val extension = ".html" val docURL = link.documentationUrl.toString def constructPathForJavadoc: String = - val l = "\\$+".r.replaceAllIn(location.mkString("/"), _ => ".") + val l = "\\$+".r.replaceAllIn(location.mkString("/").stripSuffix("$"), ".") val javadocAnchor = if anchor.isDefined then { - val paramSigs = sym.paramSymss.flatten.map(_.tree).collect { - case v: ValDef => v.tpt.tpe - }.map(getJavadocType) - "#" + sym.name + paramSigs.mkString("-","-","-") + if sym.isDefDef then + val paramSigs = sym.paramSymss.flatten.map(_.tree).collect { + case v: ValDef => v.tpt.tpe + }.map(getJavadocType) + "#" + sym.name + paramSigs.mkString("-","-","-") + else + "#" + sym.name } else "" docURL + l + extension + javadocAnchor diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala index f55451fdc636..3748f74dffb9 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TastyParser.scala @@ -134,6 +134,7 @@ case class ScaladocTastyInspector()(using ctx: DocContext) extends Inspector: def mergeAnyRefAliasAndObject(parser: TastyParser) = import parser.qctx.reflect._ + given parser.qctx.type = parser.qctx val javaLangObjectDef = defn.ObjectClass.tree.asInstanceOf[ClassDef] val objectMembers = parser.extractPatchedMembers(javaLangObjectDef) val aM = parser.parseTypeDef( @@ -142,7 +143,8 @@ case class ScaladocTastyInspector()(using ctx: DocContext) extends Inspector: ) "scala" -> aM.copy( kind = Kind.Class(Nil, Nil), - members = objectMembers + members = objectMembers, + modifiers = defn.ObjectClass.getExtraModifiers() ) object ScaladocTastyInspector: @@ -169,7 +171,12 @@ object ScaladocTastyInspector: val withNewMembers = p1.withNewMembers(p2.members) if withNewMembers.docs.isEmpty then withNewMembers.withDocs(p2.docs) else withNewMembers ) - basePck.withMembers((basePck.members ++ rest).sortBy(_.name)) + // Deduplicate members coming from different parser instances by a stable key: + // (location, member fullName, kind name). + val combined = basePck.members ++ rest + val keyed = combined.groupBy(m => (m.dri.location, m.fullName, m.kind.name)) + val uniqueMembers = keyed.values.map(g => g.find(_.docs.nonEmpty).getOrElse(g.head)).toList.sortBy(_.name) + basePck.withMembers(uniqueMembers) }.toList -> inspector.rootDoc end ScaladocTastyInspector diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala index 35cf1cb6eec3..1d5aa4e3a43a 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala @@ -4,9 +4,10 @@ package tasty import scala.jdk.CollectionConverters._ import scala.quoted._ +import scala.annotation.* -import NameNormalizer._ -import SyntheticsSupport._ +import NameNormalizer.* +import SyntheticsSupport.* trait TypesSupport: self: TastyParser => @@ -75,7 +76,7 @@ trait TypesSupport: else inner(tpe) ++ plain(".").l ++ suffix case tpe => inner(tpe) - // TODO #23 add support for all types signatures that makes sense + // TODO #23 add support for all types signatures that make sense private def inner( using Quotes, )( @@ -87,7 +88,7 @@ trait TypesSupport: ): SSignature = import reflect._ def noSupported(name: String): SSignature = - println(s"WARN: Unsupported type: $name: ${tp.show}") + report.warning(s"Unsupported type: $name: ${tp.show}") plain(s"Unsupported[$name]").l tp match case OrType(left, right) => @@ -141,24 +142,24 @@ trait TypesSupport: .reduceLeftOption((acc: SSignature, elem: SSignature) => acc ++ plain(", ").l ++ elem).getOrElse(List()) ++ plain(")").l - def parseRefinedElem(name: String, info: TypeRepr, polyTyped: SSignature = Nil): SSignature = ( info match { + def parseRefinedElem(name: String, info: TypeRepr, polyTyped: SSignature = Nil): SSignature = + val ssig = info match case m: MethodType => { val paramList = getParamList(m) keyword("def ").l ++ plain(name).l ++ polyTyped ++ paramList ++ plain(": ").l ++ inner(m.resType) } - case t: PolyType => { + case t: PolyType => val paramBounds = getParamBounds(t) - val parsedMethod = parseRefinedElem(name, t.resType) - if (!paramBounds.isEmpty){ + if !paramBounds.isEmpty then parseRefinedElem(name, t.resType, plain("[").l ++ paramBounds ++ plain("]").l) - } else parseRefinedElem(name, t.resType) - } + else parseRefinedElem(name, t.resType, polyTyped = Nil) case ByNameType(tp) => keyword("def ").l ++ plain(s"$name: ").l ++ inner(tp) case t: TypeBounds => keyword("type ").l ++ plain(name).l ++ inner(t) case t: TypeRef => keyword("val ").l ++ plain(s"$name: ").l ++ inner(t) case t: TermRef => keyword("val ").l ++ plain(s"$name: ").l ++ inner(t) case other => noSupported(s"Not supported type in refinement $info") - } ) ++ plain("; ").l + + ssig ++ plain("; ").l def parsePolyFunction(info: TypeRepr): SSignature = info match { case t: PolyType => @@ -225,13 +226,14 @@ trait TypesSupport: }) ++ plain("]").l case tp @ TypeRef(qual, typeName) => + inline def wrapping = shouldWrapInParens(inner = qual, outer = tp, isLeft = true) qual match { case r: RecursiveThis => tpe(s"this.$typeName").l case t if skipPrefix(t, elideThis) => tpe(tp.typeSymbol) case _: TermRef | _: ParamRef => val suffix = if tp.typeSymbol == Symbol.noSymbol then tpe(typeName).l else tpe(tp.typeSymbol) - inner(qual)(using skipTypeSuffix = true) ++ plain(".").l ++ suffix + inner(qual)(using indent = indent, skipTypeSuffix = true) ++ plain(".").l ++ suffix case ThisType(tr) => findSupertype(elideThis, tr.typeSymbol) match case Some((sym, AppliedType(tr2, args))) => @@ -245,17 +247,17 @@ trait TypesSupport: case _ => tpe(tp.typeSymbol) case Some(_) => tpe(tp.typeSymbol) case None => - val sig = inParens(inner(qual)(using skipTypeSuffix = true), shouldWrapInParens(qual, tp, true)) + val sig = inParens(inner(qual)(using indent = indent, skipTypeSuffix = true), wrapping) sig ++ plain(".").l ++ tpe(tp.typeSymbol) case _ => - val sig = inParens(inner(qual), shouldWrapInParens(qual, tp, true)) + val sig = inParens(inner(qual), wrapping) sig ++ keyword("#").l ++ tpe(tp.typeSymbol) } case tr @ TermRef(qual, typeName) => val prefix = qual match case t if skipPrefix(t, elideThis) => Nil - case tp => inner(tp)(using skipTypeSuffix = true) ++ plain(".").l + case tp => inner(tp)(using indent = indent, skipTypeSuffix = true) ++ plain(".").l val suffix = if skipTypeSuffix then Nil else List(plain("."), keyword("type")) val typeSig = tr.termSymbol.tree match case vd: ValDef if tr.termSymbol.flags.is(Flags.Module) => @@ -274,9 +276,9 @@ trait TypesSupport: val spaces = " " * (indent) val casesTexts = cases.flatMap { case MatchCase(from, to) => - keyword(caseSpaces + "case ").l ++ inner(from) ++ keyword(" => ").l ++ inner(to)(using indent = indent + 2) ++ plain("\n").l + keyword(caseSpaces + "case ").l ++ inner(from) ++ keyword(" => ").l ++ inner(to)(using indent = indent + 2, skipTypeSuffix = skipTypeSuffix) ++ plain("\n").l case TypeLambda(_, _, MatchCase(from, to)) => - keyword(caseSpaces + "case ").l ++ inner(from) ++ keyword(" => ").l ++ inner(to)(using indent = indent + 2) ++ plain("\n").l + keyword(caseSpaces + "case ").l ++ inner(from) ++ keyword(" => ").l ++ inner(to)(using indent = indent + 2, skipTypeSuffix = skipTypeSuffix) ++ plain("\n").l } inner(sc) ++ keyword(" match ").l ++ plain("{\n").l ++ casesTexts ++ plain(spaces + "}").l diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Cleaner.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Cleaner.scala index e495e90018cd..0997e2d6842f 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Cleaner.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Cleaner.scala @@ -1,10 +1,91 @@ package dotty.tools.scaladoc package tasty.comments +/** + * Removes HTML tags except simple ones that can be translated or that are definitely harmless, + * translates Scala/Javadoc tags, and generally cleans the input. + * Not the fastest code in the world, and should eventually be replaced by a real parser, + * but works fine for now. */ object Cleaner { import Regexes._ import java.util.regex.Matcher + // Tags that are considered safe enough and do not need escaping + private val SafeTags = Set( + "a", "abbr", "address", "area", "blockquote", "br", "b", "caption", "cite", "code", "col", "colgroup", + "dd", "del", "dfn", "em", "hr", "img", "ins", "i", "kbd", "label", "legend", "pre", "q", "samp", + "small", "span", "strong", "sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "var" + ) + + private def cleanHtml(text: String): String = { + val result = StringBuilder() + var index = 0 + var insideCode = false + var insideLink = false + while (index < text.length) { + if (insideCode) { + result.append(text(index)) + if (index >= 2 && text(index) == '`' && text(index - 1) == '`' && text(index - 2) == '`') { + insideCode = false + } + index += 1 + } else if (insideLink) { + result.append(text(index)) + if (index >= 1 && text(index) == ']' && text(index - 1) == ']') { + insideLink = false + } + index += 1 + } else if (index <= text.length - 3 && text(index) == '`' && text(index + 1) == '`' && text(index + 2) == '`') { + result.append("```") + insideCode = true + index += 3 + } else if (index <= text.length - 2 && text(index) == '[' && text(index + 1) == '[') { + result.append("[[") + insideLink = true + index += 2 + } else if (text(index) == safeTagMarker) { + // ignore it, it's a character that should never appear in everyday text anyway + index += 1 + } else if (text(index) == '<') { + val endOfNameIndex = text.indexOf(' ', index) + val endOfTagIndex = text.indexOf('>', index) + if (endOfNameIndex == -1 || endOfNameIndex == index + 1 || endOfTagIndex == -1) { + // not actually a tag, e.g., "< hello >", "a "<" // not actually a tag + case "p" | "div" => "\n\n" + case "h1" => "\n= " + case "/h1" => " =\n" + case "h2" => "\n== " + case "/h2" => " ==\n" + case "h3" => "\n=== " + case "/h3" => " ===\n" + case "h4" | "h5" | "h6" => "\n==== " + case "/h4" | "/h5" | "/h6" => " ====\n" + case "li" => "\n * - " + case "/li" => "" + case other => + val simple = if (other(0) == '/') other.substring(1) else other + if (SafeTags(simple)) { + s"$safeTagMarker${text.substring(index, endOfTagIndex + 1)}$safeTagMarker" + } else { + "" + } + }) + index = endOfTagIndex + 1 + } + } else { + result.append(text(index)) + index += 1 + } + } + result.toString + } + /** Prepares the comment for pre-parsing: removes documentation markers and * extra whitespace, removes dangerous HTML and Javadoc tags, and splits it * into lines. @@ -18,12 +99,8 @@ object Cleaner { } } val strippedComment = comment.trim.stripPrefix("/*").stripSuffix("*/") - val safeComment = DangerousTags.replaceAllIn(strippedComment, { htmlReplacement(_) }) - val javadoclessComment = JavadocTags.replaceAllIn(safeComment, { javadocReplacement(_) }) - val markedTagComment = - SafeTags.replaceAllIn(javadoclessComment, { mtch => - Matcher.quoteReplacement(s"$safeTagMarker${mtch.matched}$safeTagMarker") - }) - markedTagComment.linesIterator.toList map (cleanLine) + val safeComment = cleanHtml(strippedComment) + val javadoclessComment = JavadocTags.replaceAllIn(safeComment, javadocReplacement) + javadoclessComment.linesIterator.toList.map(cleanLine) } } diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/CommentRegex.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/CommentRegex.scala index 3ff022198446..4de66d04ec2f 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/CommentRegex.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/CommentRegex.scala @@ -12,12 +12,6 @@ object Regexes { val CleanCommentLine = new Regex("""(?:\s*\*\s?\s?)?(.*)""") - /** Dangerous HTML tags that should be replaced by something safer, - * such as wiki syntax, or that should be dropped - */ - val DangerousTags = - new Regex("""<(/?(div|ol|ul|li|h[1-6]|p))( [^>]*)?/?>|""") - /** Javadoc tags that should be replaced by something useful, such as wiki * syntax, or that should be dropped. */ val JavadocTags = @@ -36,27 +30,7 @@ object Regexes { } } - /** Maps a dangerous HTML tag to a safe wiki replacement, or an empty string - * if it cannot be salvaged. */ - def htmlReplacement(mtch: Regex.Match): String = mtch.group(1) match { - case "p" | "div" => "\n\n" - case "h1" => "\n= " - case "/h1" => " =\n" - case "h2" => "\n== " - case "/h2" => " ==\n" - case "h3" => "\n=== " - case "/h3" => " ===\n" - case "h4" | "h5" | "h6" => "\n==== " - case "/h4" | "/h5" | "/h6" => " ====\n" - case "li" => "\n * - " - case _ => "" - } - - /** Safe HTML tags that can be kept. */ - val SafeTags = - new Regex("""((&\w+;)|(&#\d+;)|(]*)?/?>))""") - - val safeTagMarker = '\u000E' + val safeTagMarker = '\u000E' // IMPORTANT: Only change if you've updated the sanitization tests to match val endOfLine = '\u000A' val endOfText = '\u0003' diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala index ff4405d3ec71..2b07318475fc 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala @@ -112,7 +112,7 @@ abstract class MarkupConversion[T](val repr: Repr)(using dctx: DocContext) { case None => sym.dri DocLink.ToDRI(dri, targetText) case None => - val txt = s"No DRI found for query" + val txt = s"Couldn't resolve a member for the given link query" val msg = s"$txt: $queryStr" if (!summon[DocContext].args.noLinkWarnings) then @@ -137,8 +137,8 @@ abstract class MarkupConversion[T](val repr: Repr)(using dctx: DocContext) { val scDataCollector = SnippetCompilerDataCollector[qctx.type](qctx) val data = scDataCollector.getSnippetCompilerData(s, s) val sourceFile = scDataCollector.getSourceFile(s) - (str: String, lineOffset: SnippetChecker.LineOffset, argOverride: Option[SCFlags]) => { - val arg = argOverride.fold(pathBasedArg)(pathBasedArg.overrideFlag(_)) + (str: String, lineOffset: SnippetChecker.LineOffset, argOverride: Option[SnippetCompilerArg]) => { + val arg = argOverride.fold(pathBasedArg)(pathBasedArg.merge(_)) val res = snippetChecker.checkSnippet(str, Some(data), arg, lineOffset, sourceFile) res.filter(r => !r.isSuccessful).foreach(_.reportMessages()(using compilerContext)) res diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/MemberLookup.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/MemberLookup.scala index b2c4e1bdcac4..2eda2de6f0dd 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/MemberLookup.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/MemberLookup.scala @@ -31,8 +31,8 @@ trait MemberLookup { def nearestPackage(sym: Symbol): Symbol = if sym.flags.is(Flags.Package) then sym else nearestPackage(sym.owner) - def nearestMembered(sym: Symbol): Symbol = - if sym.isClassDef || sym.flags.is(Flags.Package) then sym else nearestMembered(sym.owner) + def nearestMember(sym: Symbol): Symbol = + if sym.isClassDef || sym.flags.is(Flags.Package) then sym else nearestMember(sym.owner) val res: Option[(Symbol, String, Option[Symbol])] = { def toplevelLookup(querystrings: List[String]) = @@ -43,7 +43,7 @@ trait MemberLookup { ownerOpt match { case Some(owner) => - val nearest = nearestMembered(owner) + val nearest = nearestMember(owner) val nearestCls = nearestClass(owner) val nearestPkg = nearestPackage(owner) def relativeLookup(querystrings: List[String], owner: Symbol): Option[(Symbol, Option[Symbol])] = { @@ -180,13 +180,33 @@ trait MemberLookup { val sel = MemberLookup.Selector.fromString(q) val res = sel.kind match { case MemberLookup.SelectorKind.NoForce => - val lookedUp = localLookup(sel, owner).toSeq + // Extract just the method name from the signature (removing type params and param list) + val methodName = extractMethodName(sel.ident) + val nameSel = MemberLookup.Selector(methodName, sel.kind) + val lookedUp = localLookup(nameSel, owner).toSeq // note: those flag lookups are necessary b/c for objects we return their classes - lookedUp.find(s => s.isType && !s.flags.is(Flags.Module)).orElse( - lookedUp.find(s => s.isTerm || s.flags.is(Flags.Module)) - ) + val typeMatch = lookedUp.find(s => s.isType && !s.flags.is(Flags.Module)) + if typeMatch.isDefined then typeMatch + else { + val termMatches = + lookedUp.filter(s => s.isTerm || s.flags.is(Flags.Module)) + if termMatches.size <= 1 then termMatches.headOption + else { + // Multiple overloads found, try to match based on signature in query + parseSignatureParams(sel.ident) match { + case Some(queryParamTypes) => + // Signature was provided, try to find matching overload + termMatches.find { sym => + matchesParameterTypes(sym, queryParamTypes) + }.orElse(termMatches.headOption) + case None => + // No signature provided, fall back to first match + termMatches.headOption + } + } + } case _ => - localLookup(sel, owner).nextOption + localLookup(sel, owner).nextOption() } res match { case None => None @@ -221,6 +241,110 @@ trait MemberLookup { } } } + + /** Parse parameter types from a method signature. + * Input: "[A](b:scala.collection.mutable.Buffer[A],c:Int)" + * Output: Some(List("scala.collection.mutable.Buffer", "Int")) + * + * Returns: + * - None if no parameter list is present (e.g., "method" or "method[A]") + * - Some(Nil) if parameter list is empty (e.g., "method()") + * - Some(List(...)) if parameters are present + */ + private def parseSignatureParams(signature: String): Option[List[String]] = { + // Normalize the signature by removing backslashes (they're used in scaladoc to escape dots) + val normalizedSignature = signature.replace("\\.", ".") + + // Find the parameter list (after any type params) + val parenStart = normalizedSignature.indexOf('(') + if parenStart == -1 then return None + + val parenEnd = normalizedSignature.lastIndexOf(')') + if parenEnd == -1 || parenEnd <= parenStart then return None + + val paramList = normalizedSignature.substring(parenStart + 1, parenEnd) + if paramList.isEmpty then return Some(Nil) + + // Split by comma, respecting nested brackets + val params = scala.collection.mutable.ListBuffer[String]() + var depth = 0 + var start = 0 + for i <- 0 until paramList.length do + val c = paramList.charAt(i) + if c == '[' then depth += 1 + else if c == ']' then depth -= 1 + else if c == ',' && depth == 0 then + params += paramList.substring(start, i).trim + start = i + 1 + params += paramList.substring(start).trim + + // Extract type from each "name:Type" pair, stripping type arguments + Some(params.toList.flatMap { param => + val colonIdx = param.indexOf(':') + if colonIdx == -1 then None + else + val typePart = param.substring(colonIdx + 1).trim + // Remove type arguments to get the base type + val bracketIdx = typePart.indexOf('[') + val baseType = if bracketIdx == -1 then typePart else typePart.substring(0, bracketIdx) + Some(baseType) + }) + } + + /** Extract simple type name from a possibly qualified type. + * "scala.collection.mutable.Buffer[A]" -> "Buffer" + * "Int" -> "Int" + */ + private def extractSimpleTypeName(qualifiedType: String): String = { + // Remove type arguments first + val withoutTypeArgs = qualifiedType.indexOf('[') match { + case -1 => qualifiedType + case i => qualifiedType.substring(0, i) + } + // Get last segment after '.' + withoutTypeArgs.split('.').last + } + + /** Extract just the method name from a signature, removing type parameters and parameter lists. + * For example, from "asJava[A](b:scala.collection.mutable.Buffer[A])*" extracts "asJava". + */ + private def extractMethodName(signature: String): String = { + // Find the first occurrence of [ or (, and return everything before it + val bracketIndex = signature.indexOf('[') + val parenIndex = signature.indexOf('(') + if bracketIndex == -1 && parenIndex == -1 then signature + else { + val firstSpecial = if bracketIndex == -1 then parenIndex else if parenIndex == -1 then bracketIndex else math.min(bracketIndex, parenIndex) + signature.substring(0, firstSpecial) + } + } + + /** Check if a method's parameter types match the expected parameter types from the query. + * This compares the simple type names of all parameters in order, which could, theoretically, + * have edge cases with same-named types in different packages + */ + private def matchesParameterTypes(using Quotes)(sym: reflect.Symbol, queryParamTypes: List[String]): Boolean = { + import reflect._ + + def getMethodParamTypes(tpe: TypeRepr): Option[List[String]] = tpe match { + case MethodType(_, paramTypes, _) => + Some(paramTypes.map(pt => extractSimpleTypeName(pt.show))) + case PolyType(_, _, resType) => + getMethodParamTypes(resType) + case _ => None + } + + try { + getMethodParamTypes(sym.info) match { + case Some(actualTypes) => + val querySimpleTypes = queryParamTypes.map(extractSimpleTypeName) + actualTypes == querySimpleTypes + case None => false + } + } catch { + case _: Exception => false + } + } } object MemberLookup extends MemberLookup { diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Queries.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Queries.scala index a271f8c8e907..7d4efb39c63b 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Queries.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Queries.scala @@ -63,8 +63,12 @@ class QueryParser(val query: CharSequence) { def readSegmentedQuery(): QuerySegment = { val id = readIdentifier() - if atEnd() || lookingAt('(') || lookingAt('[') then { + if atEnd() then { Query.Id(id.asString) + } else if lookingAt('(') || lookingAt('[') then { + // Include the entire signature in the identifier for overload resolution + val signature = readSignature() + Query.Id(id.asString + signature) } else { val ch = popCh() if ch == '.' || ch == '#' @@ -73,6 +77,21 @@ class QueryParser(val query: CharSequence) { } } + /** Read type parameters and parameter lists for overload resolution. + * This reads everything up to the next '.' or '#' or end of string. + */ + private def readSignature(): String = { + var depth = 0 + var result = new StringBuilder() + while !atEnd() && (depth > 0 || !lookingAt('.') && !lookingAt('#')) do { + val ch = popCh() + result.append(ch) + if ch == '[' || ch == '(' then depth += 1 + else if ch == ']' || ch == ')' then depth -= 1 + } + result.toString + } + def readIdentifier(): Query.Qual = { assertBounds("expected start of identifier") if lookingAt('`') then { diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SectionRenderingExtension.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SectionRenderingExtension.scala index f2b2546c11ad..072591ea57bc 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SectionRenderingExtension.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SectionRenderingExtension.scala @@ -26,21 +26,17 @@ object SectionRenderingExtension extends HtmlRenderer.HtmlRendererExtension: def rendererOptions(opt: MutableDataHolder): Unit = () case class AnchorLink(link: String) extends BlankLine(BasedSequence.EmptyBasedSequence()) - object SectionHandler extends CustomNodeRenderer[Section]: - val repeatedIds: mutable.Map[(NodeRendererContext, String), Int] = mutable.Map() + class SectionHandler extends CustomNodeRenderer[Section]: val idGenerator = new HeaderIdGenerator.Factory().create() + idGenerator.setResolveDupes(true) override def render(node: Section, c: NodeRendererContext, html: HtmlWriter): Unit = val Section(header, body) = node - val headerText = header.getText.toString - val idSuffix = repeatedIds.getOrElseUpdate((c, headerText), 0) - val ifSuffixStr = if(idSuffix == 0) then "" else idSuffix.toString - repeatedIds.update((c, headerText), idSuffix + 1) /* #19524 flexmark's `HeaderIdGenerator` does not appear to be thread-safe, * so we protect its usage with a full `synchronize`. */ val id = idGenerator.synchronized { - idGenerator.getId(headerText + ifSuffixStr) + idGenerator.getId(header.getText) } val anchor = AnchorLink(s"#$id") @@ -68,13 +64,12 @@ object SectionRenderingExtension extends HtmlRenderer.HtmlRendererExtension: object Render extends NodeRenderer: override def getNodeRenderingHandlers: JSet[NodeRenderingHandler[_]] = JSet( - new NodeRenderingHandler(classOf[Section], SectionHandler), + new NodeRenderingHandler(classOf[Section], new SectionHandler), new NodeRenderingHandler(classOf[AnchorLink], AnchorLinkHandler) ) object Factory extends NodeRendererFactory: override def apply(options: DataHolder): NodeRenderer = Render - def extend(htmlRendererBuilder: HtmlRenderer.Builder, tpe: String): Unit = htmlRendererBuilder.nodeRendererFactory(Factory) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/wiki/Entities.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/wiki/Entities.scala index f11e8095afe7..86e7298226ea 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/wiki/Entities.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/wiki/Entities.scala @@ -5,8 +5,7 @@ import scala.collection.{Seq => _, _} // import representations._ /** A body of text. A comment has a single body, which is composed of - * at least one block. Inside every body is exactly one summary (see - * [[scala.tools.nsc.doc.model.comment.Summary]]). */ + * at least one block. Inside every body is exactly one summary. */ final case class Body(blocks: Seq[Block]) { /** The summary text of the comment body. */ diff --git a/scaladoc/src/dotty/tools/scaladoc/util/html.scala b/scaladoc/src/dotty/tools/scaladoc/util/html.scala index 9d832d28ee0b..5f4f898ccd67 100644 --- a/scaladoc/src/dotty/tools/scaladoc/util/html.scala +++ b/scaladoc/src/dotty/tools/scaladoc/util/html.scala @@ -91,6 +91,8 @@ object HTML: val tr = Tag("tr") val td = Tag("td") val section = Tag("section") + val sup = Tag("sup") + val sub = Tag("sub") val cls = Attr("class") val href = Attr("href") diff --git a/scaladoc/src/scala/tasty/inspector/TastyInspector.scala b/scaladoc/src/scala/tasty/inspector/TastyInspector.scala index 14e5f019b433..47501521d612 100644 --- a/scaladoc/src/scala/tasty/inspector/TastyInspector.scala +++ b/scaladoc/src/scala/tasty/inspector/TastyInspector.scala @@ -9,7 +9,7 @@ import scala.quoted.runtime.impl.QuotesImpl import dotty.tools.dotc.Compiler import dotty.tools.dotc.Driver import dotty.tools.dotc.Run -import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Contexts.{Context, ctx} import dotty.tools.dotc.core.Mode import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.fromtasty._ @@ -69,7 +69,7 @@ object TastyInspector: override def phaseName: String = "tastyInspector" override def runOn(units: List[CompilationUnit])(using ctx0: Context): List[CompilationUnit] = - // NOTE: although this is a phase, do not expect this to be ran with an xsbti.CompileProgress + // NOTE: although this is a phase, do not expect this to be run with an xsbti.CompileProgress val ctx = QuotesCache.init(ctx0.fresh) runOnImpl(units)(using ctx) diff --git a/scaladoc/test-documentations/sidebar/_docs/directory/index.md b/scaladoc/test-documentations/sidebar/_docs/directory/index.md new file mode 100644 index 000000000000..12c9cfda21a8 --- /dev/null +++ b/scaladoc/test-documentations/sidebar/_docs/directory/index.md @@ -0,0 +1,3 @@ +# Directory index + +This is the index file of the directory. \ No newline at end of file diff --git a/scaladoc/test-documentations/sidebar/_docs/index.md b/scaladoc/test-documentations/sidebar/_docs/index.md new file mode 100644 index 000000000000..c8a9c6a53ce7 --- /dev/null +++ b/scaladoc/test-documentations/sidebar/_docs/index.md @@ -0,0 +1,3 @@ +# Docs Home + +This is the index page. \ No newline at end of file diff --git a/scaladoc/test-documentations/sidebar/sidebar.yml b/scaladoc/test-documentations/sidebar/sidebar.yml new file mode 100644 index 000000000000..17f84b590bae --- /dev/null +++ b/scaladoc/test-documentations/sidebar/sidebar.yml @@ -0,0 +1,5 @@ +index: index.md +subsection: + - title: A Directory Subsection + index: directory/index.md + directory: directory diff --git a/scaladoc/test-source-links/dotty/tools/scaladoc/source-links/RemoteLinksTest.scala b/scaladoc/test-source-links/dotty/tools/scaladoc/source-links/RemoteLinksTest.scala index 1e8a1227235d..027ec9ee923a 100644 --- a/scaladoc/test-source-links/dotty/tools/scaladoc/source-links/RemoteLinksTest.scala +++ b/scaladoc/test-source-links/dotty/tools/scaladoc/source-links/RemoteLinksTest.scala @@ -94,4 +94,4 @@ class RemoteLinksTest: } } IO.foreachFileIn(output, processFile) - mtsl.result + mtsl.result() diff --git a/scaladoc/test/dotty/tools/scaladoc/BaseHtmlTest.scala b/scaladoc/test/dotty/tools/scaladoc/BaseHtmlTest.scala index 1c21ccaa70ab..79dbf904db93 100644 --- a/scaladoc/test/dotty/tools/scaladoc/BaseHtmlTest.scala +++ b/scaladoc/test/dotty/tools/scaladoc/BaseHtmlTest.scala @@ -70,3 +70,19 @@ class BaseHtmlTest: assertTrue(s"File at $path does not exisits!", Files.exists(path)) val document = Jsoup.parse(IO.read(path)) op(DocumentContext(document, path)) + + def docHtml(dir: String, cls: String, syntax: String): String = + val dest = Files.createTempDirectory("test-doc").toFile + try + val args = Scaladoc.Args( + name = projectName, + tastyFiles = tastyFiles(dir), + output = dest, + projectVersion = Some(projectVersion), + defaultSyntax = List(syntax), + ) + Scaladoc.run(args)(using testContext) + val path = dest.toPath.resolve(s"tests/$dir/$cls.html") + val doc = org.jsoup.Jsoup.parse(dotty.tools.scaladoc.util.IO.read(path)) + doc.select(".doc").html() + finally dotty.tools.scaladoc.util.IO.delete(dest) \ No newline at end of file diff --git a/scaladoc/test/dotty/tools/scaladoc/ExternalLocationProviderIntegrationTest.scala b/scaladoc/test/dotty/tools/scaladoc/ExternalLocationProviderIntegrationTest.scala index a63f699c4c2f..d5a952931a10 100644 --- a/scaladoc/test/dotty/tools/scaladoc/ExternalLocationProviderIntegrationTest.scala +++ b/scaladoc/test/dotty/tools/scaladoc/ExternalLocationProviderIntegrationTest.scala @@ -7,6 +7,7 @@ import dotty.tools.scaladoc.test.BuildInfo import java.nio.file.Path; import org.jsoup.Jsoup import util.IO +import scala.util.Properties class JavadocExternalLocationProviderIntegrationTest extends ExternalLocationProviderIntegrationTest( "externalJavadoc", @@ -23,6 +24,14 @@ class JavadocExternalLocationProviderIntegrationTest extends ExternalLocationPro ) ) +class JavadocStaticFieldExternalLocationProviderReproTest extends ExternalLocationProviderIntegrationTest( + "externalJavadoc", + List(".*java.*::javadoc::https://docs.oracle.com/en/java/javase/17/docs/api/java.base/"), + List( + "https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Double.html#MIN_VALUE" + ) +) + class Scaladoc2ExternalLocationProviderIntegrationTest extends ExternalLocationProviderIntegrationTest( "externalScaladoc2", List( @@ -73,7 +82,6 @@ class Scaladoc2LegacyExternalLocationProviderIntegrationTest extends LegacyExter ) ) - abstract class ExternalLocationProviderIntegrationTest( name: String, mappings: Seq[String], @@ -87,31 +95,33 @@ abstract class ExternalLocationProviderIntegrationTest( ).toList ) - override def runTest = afterRendering { - val output = summon[DocContext].args.output.toPath - val linksBuilder = List.newBuilder[String] + override def runTest = if(!Properties.isWin) { + afterRendering { + val output = summon[DocContext].args.output.toPath + val linksBuilder = List.newBuilder[String] - def processFile(path: Path): Unit = - val document = Jsoup.parse(IO.read(path)) - val content = document.select(".documentableElement").forEach { elem => - val hrefValues = elem.select("a").asScala.map { a => - a.attr("href") + def processFile(path: Path): Unit = + val document = Jsoup.parse(IO.read(path)) + val content = document.select(".documentableElement").forEach { elem => + val hrefValues = elem.select("a").asScala.map { a => + a.attr("href") + } + linksBuilder ++= hrefValues } - linksBuilder ++= hrefValues - } - IO.foreachFileIn(output, processFile) - val links = linksBuilder.result - val errors = expectedLinks.flatMap(expect => Option.when(!links.contains(expect))(expect)) - if !errors.isEmpty then { - val reportMessage = - "External location provider integration test failed.\n" + - "Missing links:\n" - + errors.mkString("\n","\n","\n") - + "Found links:" + links.mkString("\n","\n","\n") - reportError(reportMessage) - } - } :: Nil + IO.foreachFileIn(output, processFile) + val links = linksBuilder.result() + val errors = expectedLinks.flatMap(expect => Option.when(!links.contains(expect))(expect)) + if !errors.isEmpty then { + val reportMessage = + "External location provider integration test failed.\n" + + "Missing links:\n" + + errors.mkString("\n","\n","\n") + + "Found links:" + links.mkString("\n","\n","\n") + reportError(reportMessage) + } + } :: Nil + } abstract class LegacyExternalLocationProviderIntegrationTest( name: String, diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/AbstractMemberSignaturesTest.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/AbstractMemberSignaturesTest.scala index 72124ab2fbcc..09b1ea3fbaf9 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/AbstractMemberSignaturesTest.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/AbstractMemberSignaturesTest.scala @@ -42,4 +42,4 @@ class AbstractMembers extends ScaladocTest("abstractmembersignatures"): } } IO.foreachFileIn(output, processFile) - signatures.result.groupMap(_._1)(_._2) + signatures.result().groupMap(_._1)(_._2) diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala index bdedc3f14ce0..e0d9b8df00ec 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala @@ -92,7 +92,7 @@ abstract class SignatureTest( yield name private def signaturesFromSources(source: Source, kinds: Seq[String]): Seq[SignatureRes] = - source.getLines.map(_.trim) + source.getLines().map(_.trim) .filterNot(_.isEmpty) .filterNot(l => l.startWithAnyOfThese("=",":","{","}", "//") && !l.startsWith("//expected:")) .toSeq @@ -129,7 +129,7 @@ abstract class SignatureTest( } IO.foreachFileIn(output, processFile) - signatures.result + signatures.result() object SignatureTest { val classlikeKinds = Seq("class", "object", "trait", "enum") // TODO add docs for packages diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala index b658bfcdedc0..5469c06c8eb1 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala @@ -55,6 +55,8 @@ class GivenSignatures extends SignatureTest("givenSignatures", SignatureTest.all class Annotations extends SignatureTest("annotations", SignatureTest.all) +class Deprecated extends SignatureTest("deprecated", SignatureTest.all) + class InheritanceLoop extends SignatureTest("inheritanceLoop", SignatureTest.all) class InheritedMembers extends SignatureTest("inheritedMembers2", SignatureTest.all.filter(_ != "class"), @@ -122,3 +124,5 @@ class InfixTypes extends SignatureTest("infixTypes", SignatureTest.all) class ExtendsCall extends SignatureTest("extendsCall", SignatureTest.all) class RightAssocExtension extends SignatureTest("rightAssocExtension", SignatureTest.all) + +class OptionalParams extends SignatureTest("optionalParams", SignatureTest.all) diff --git a/scaladoc/test/dotty/tools/scaladoc/site/SiteGeneratationTest.scala b/scaladoc/test/dotty/tools/scaladoc/site/SiteGeneratationTest.scala index e012044156cc..89650b6e463a 100644 --- a/scaladoc/test/dotty/tools/scaladoc/site/SiteGeneratationTest.scala +++ b/scaladoc/test/dotty/tools/scaladoc/site/SiteGeneratationTest.scala @@ -124,3 +124,10 @@ class SiteGeneratationTest extends BaseHtmlTest: ) } } + + @Test + def sidebarFile() = withGeneratedSite(testDocPath.resolve("sidebar")) { + withHtmlFile("docs/index.html")( + _.assertTextsIn("#leftColumn #docs-nav > div:nth-child(1) > span > a > span", "A Directory Subsection")) + withHtmlFile("docs/directory/index.html")(_.assertTextsIn("title", "A Directory Subsection")) + } diff --git a/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTest.scala b/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTest.scala index 616f7ae7f35e..4dc7dd939cac 100644 --- a/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTest.scala +++ b/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTest.scala @@ -23,7 +23,7 @@ abstract class SnippetsE2eTest(testName: String, flag: SCFlags) extends Scaladoc val source = Source.fromFile(s"${BuildInfo.test_testcasesSourceRoot}/tests/$testName.scala") - val snippetsCount = source.getLines.filter(_.indexOf("```scala") != -1).size + val snippetsCount = source.getLines().filter(l => l.indexOf("```scala") != -1 && !l.contains("sc-hidden")).size def report(str: String) = s"""|In test $testName: |$str""".stripMargin diff --git a/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTestcases.scala b/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTestcases.scala index 7ec71600c68a..8dc72f476345 100644 --- a/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTestcases.scala +++ b/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTestcases.scala @@ -8,3 +8,7 @@ class SnippetE2eTestcase2 extends SnippetsE2eTest("snippetTestcase2", SCFlags.Co class SnippetE2eTestcase3 extends SnippetsE2eTest("snippetTestcase3", SCFlags.Compile) + +class SnippetE2eTestcaseMacro extends SnippetsE2eTest("snippetTestcaseMacro", SCFlags.Compile) + +class SnippetE2eTestcaseHidden extends SnippetsE2eTest("snippetTestcaseHidden", SCFlags.Compile) diff --git a/scaladoc/test/dotty/tools/scaladoc/tasty/comments/CaretTest.scala b/scaladoc/test/dotty/tools/scaladoc/tasty/comments/CaretTest.scala new file mode 100644 index 000000000000..6ec8f1b5ba3a --- /dev/null +++ b/scaladoc/test/dotty/tools/scaladoc/tasty/comments/CaretTest.scala @@ -0,0 +1,35 @@ +package dotty.tools.scaladoc +package tasty +package comments + +import org.junit.Test +import org.junit.Assert._ +import java.nio.file.Files + +/** Test that `` tags render correctly in both wiki and markdown modes, + * and that `^...^` in wiki mode creates a `...`. + * + * See https://github.com/scala/scala3/issues/25517 + */ +class CaretTest extends BaseHtmlTest: + + private def docHtml(cls: String, syntax: String): String = + super.docHtml("i25517", cls, syntax) + + @Test def supTagsInMarkdown(): Unit = + val html = docHtml("SupDefault", "markdown") + assertEquals("

229 230

", html) + + @Test def supTagsInWiki(): Unit = + val html = docHtml("SupWiki", "wiki") + assertTrue(html.contains("229")) + assertTrue(html.contains("230")) + + @Test def bareCaretBrokenInWiki(): Unit = + val html = docHtml("CaretWiki", "wiki") + assertTrue(html.contains("")) + + @Test def pairedCaretInWiki(): Unit = + val html = docHtml("PairedCaretWiki", "wiki") + assertTrue(html.contains("229")) + assertTrue(html.contains("230")) diff --git a/scaladoc/test/dotty/tools/scaladoc/tasty/comments/MemberLookupTests.scala b/scaladoc/test/dotty/tools/scaladoc/tasty/comments/MemberLookupTests.scala index 5f4b85251407..80ed7c99e065 100644 --- a/scaladoc/test/dotty/tools/scaladoc/tasty/comments/MemberLookupTests.scala +++ b/scaladoc/test/dotty/tools/scaladoc/tasty/comments/MemberLookupTests.scala @@ -17,6 +17,7 @@ class LookupTestCases[Q <: Quotes](val q: Quotes) { testOwnerlessLookupOfClassWithinPackageWithPackageObject() testOwnedLookup() testStrictMemberLookup() + testOverloadedMethodLookup() } def testOwnerlessLookup(): Unit = { @@ -173,6 +174,149 @@ class LookupTestCases[Q <: Quotes](val q: Quotes) { assertTrue("strict member lookup should not look outside", MemberLookup.lookup(parseQuery(query), owner).isEmpty) } + def testOverloadedMethodLookup(): Unit = { + // Test basic overload resolution with collection types + val collectionOverloadCases = List[(String, Sym)]( + // Buffer overload + "tests.overloadedMethods.OverloadedMethods.processBuffer[A](b:scala.collection.mutable.Buffer[A])*" -> + cls("tests.overloadedMethods.OverloadedMethods").funOverload("processBuffer", "Buffer"), + // Map overload + "tests.overloadedMethods.OverloadedMethods.processMap[K,V](m:scala.collection.mutable.Map[K,V])*" -> + cls("tests.overloadedMethods.OverloadedMethods").funOverload("processMap", "Map"), + // Set overload + "tests.overloadedMethods.OverloadedMethods.processSet[A](s:scala.collection.mutable.Set[A])*" -> + cls("tests.overloadedMethods.OverloadedMethods").funOverload("processSet", "Set"), + // Seq overload + "tests.overloadedMethods.OverloadedMethods.processSeq[A](s:scala.collection.mutable.Seq[A])*" -> + cls("tests.overloadedMethods.OverloadedMethods").funOverload("processSeq", "Seq"), + // Iterator overload + "tests.overloadedMethods.OverloadedMethods.processIterator[A](it:scala.collection.Iterator[A])*" -> + cls("tests.overloadedMethods.OverloadedMethods").funOverload("processIterator", "Iterator"), + // Iterable overload + "tests.overloadedMethods.OverloadedMethods.processIterable[A](it:scala.collection.mutable.Iterable[A])*" -> + cls("tests.overloadedMethods.OverloadedMethods").funOverload("processIterable", "Iterable"), + // List overload + "tests.overloadedMethods.OverloadedMethods.processList[A](l:scala.collection.immutable.List[A])*" -> + cls("tests.overloadedMethods.OverloadedMethods").funOverload("processList", "List"), + // Properties overload + "tests.overloadedMethods.OverloadedMethods.processProperties(p:java.util.Properties)*" -> + cls("tests.overloadedMethods.OverloadedMethods").funOverload("processProperties", "Properties"), + ) + + collectionOverloadCases.foreach { case (query, sym) => + testOwnerlessLookup(query, sym) + } + + // Test transform method with multiple overloads including primitive Int + val transformOverloadCases = List[(String, Sym)]( + // Int overload (primitive type) + "tests.overloadedMethods.OverloadedMethods.transform(x:Int)*" -> + cls("tests.overloadedMethods.OverloadedMethods").funOverload("transform", "Int"), + // Buffer overload + "tests.overloadedMethods.OverloadedMethods.transform[A](b:scala.collection.mutable.Buffer[A])*" -> + cls("tests.overloadedMethods.OverloadedMethods").funOverload("transform", "Buffer"), + // Map overload + "tests.overloadedMethods.OverloadedMethods.transform[K,V](m:scala.collection.mutable.Map[K,V])*" -> + cls("tests.overloadedMethods.OverloadedMethods").funOverload("transform", "Map"), + // Set overload + "tests.overloadedMethods.OverloadedMethods.transform[A](s:scala.collection.mutable.Set[A])*" -> + cls("tests.overloadedMethods.OverloadedMethods").funOverload("transform", "Set"), + // Properties overload + "tests.overloadedMethods.OverloadedMethods.transform(p:java.util.Properties)*" -> + cls("tests.overloadedMethods.OverloadedMethods").funOverload("transform", "Properties"), + ) + + transformOverloadCases.foreach { case (query, sym) => + testOwnerlessLookup(query, sym) + } + + // Test static asJava methods in companion object + val staticOverloadCases = List[(String, Sym)]( + // Buffer overload + "tests.overloadedMethods.OverloadedMethods.asJava[A](b:scala.collection.mutable.Buffer[A])*" -> + cls("tests.overloadedMethods.OverloadedMethods$").funOverload("asJava", "Buffer"), + // Map overload + "tests.overloadedMethods.OverloadedMethods.asJava[K,V](m:scala.collection.mutable.Map[K,V])*" -> + cls("tests.overloadedMethods.OverloadedMethods$").funOverload("asJava", "Map"), + // Set overload + "tests.overloadedMethods.OverloadedMethods.asJava[A](s:scala.collection.mutable.Set[A])*" -> + cls("tests.overloadedMethods.OverloadedMethods$").funOverload("asJava", "Set"), + // Properties overload + "tests.overloadedMethods.OverloadedMethods.asJava(p:java.util.Properties)*" -> + cls("tests.overloadedMethods.OverloadedMethods$").funOverload("asJava", "Properties"), + ) + + staticOverloadCases.foreach { case (query, sym) => + testOwnerlessLookup(query, sym) + } + + // Test custom types (non-collection) + val customTypeCases = List[(String, Sym)]( + "tests.overloadedMethods.CustomTypeOverloads.process(x:tests.overloadedMethods.CustomTypeA)*" -> + cls("tests.overloadedMethods.CustomTypeOverloads").funOverload("process", "CustomTypeA"), + "tests.overloadedMethods.CustomTypeOverloads.process(x:tests.overloadedMethods.CustomTypeB)*" -> + cls("tests.overloadedMethods.CustomTypeOverloads").funOverload("process", "CustomTypeB"), + ) + + customTypeCases.foreach { case (query, sym) => + testOwnerlessLookup(query, sym) + } + + // Test multiple parameters + val multiParamCases = List[(String, Sym)]( + "tests.overloadedMethods.MultiParamOverloads.merge(a:scala.collection.mutable.Buffer[_],b:scala.collection.mutable.Set[_])*" -> + cls("tests.overloadedMethods.MultiParamOverloads").funOverload("merge", "Buffer", "Set"), + "tests.overloadedMethods.MultiParamOverloads.merge(a:scala.collection.mutable.Set[_],b:scala.collection.mutable.Buffer[_])*" -> + cls("tests.overloadedMethods.MultiParamOverloads").funOverload("merge", "Set", "Buffer"), + "tests.overloadedMethods.MultiParamOverloads.combine(a:Int,b:String)*" -> + cls("tests.overloadedMethods.MultiParamOverloads").funOverload("combine", "Int", "String"), + "tests.overloadedMethods.MultiParamOverloads.combine(a:String,b:Int)*" -> + cls("tests.overloadedMethods.MultiParamOverloads").funOverload("combine", "String", "Int"), + ) + + multiParamCases.foreach { case (query, sym) => + testOwnerlessLookup(query, sym) + } + + // Test no-arg vs arg overloads - this tests that empty parameter lists + // are correctly distinguished from missing signatures + val noArgOverloadCases = List[(String, Sym)]( + // No-arg version + "tests.overloadedMethods.NoArgOverloads.action()*" -> + cls("tests.overloadedMethods.NoArgOverloads").funOverload("action"), + // Int arg version + "tests.overloadedMethods.NoArgOverloads.action(x:Int)*" -> + cls("tests.overloadedMethods.NoArgOverloads").funOverload("action", "Int"), + // String arg version + "tests.overloadedMethods.NoArgOverloads.action(s:String)*" -> + cls("tests.overloadedMethods.NoArgOverloads").funOverload("action", "String"), + // Two arg version + "tests.overloadedMethods.NoArgOverloads.action(x:Int,y:String)*" -> + cls("tests.overloadedMethods.NoArgOverloads").funOverload("action", "Int", "String"), + // No-arg getValue + "tests.overloadedMethods.NoArgOverloads.getValue()*" -> + cls("tests.overloadedMethods.NoArgOverloads").funOverload("getValue"), + // Int arg getValue + "tests.overloadedMethods.NoArgOverloads.getValue(index:Int)*" -> + cls("tests.overloadedMethods.NoArgOverloads").funOverload("getValue", "Int"), + ) + + noArgOverloadCases.foreach { case (query, sym) => + testOwnerlessLookup(query, sym) + } + + // Test fallback behavior - queries without signature should fall back to first match + val fallbackCases = List[String]( + "tests.overloadedMethods.OverloadedMethods.transform", + "tests.overloadedMethods.OverloadedMethods.processBuffer", + ) + + fallbackCases.foreach { query => + val lookupRes = MemberLookup.lookupOpt(parseQuery(query), None) + assertTrue(s"Fallback lookup should succeed for: $query", lookupRes.nonEmpty) + } + } + given q.type = q def parseQuery(query: String): Query = { @@ -189,6 +333,46 @@ class LookupTestCases[Q <: Quotes](val q: Quotes) { val List(sym) = symbol.methodMember(name) Sym(sym) def tpe(name: String) = Sym(symbol.typeMember(name)) + /** Find a specific overloaded method by matching parameter types. + * @param name The method name + * @param paramTypes The simple names of parameter types to match (e.g., "Buffer", "Int") + */ + def funOverload(name: String, paramTypes: String*): Sym = { + import q.reflect._ + + def extractSimpleTypeName(qualifiedType: String): String = { + val withoutTypeArgs = qualifiedType.indexOf('[') match { + case -1 => qualifiedType + case i => qualifiedType.substring(0, i) + } + withoutTypeArgs.split('.').last + } + + def getMethodParamTypes(tpe: TypeRepr): Option[List[String]] = tpe match { + case MethodType(_, pts, _) => + Some(pts.map(pt => extractSimpleTypeName(pt.show))) + case PolyType(_, _, resType) => + getMethodParamTypes(resType) + case _ => None + } + + val methods = symbol.methodMember(name) + val targetTypes = paramTypes.toList + val matchingMethod = methods.find { sym => + getMethodParamTypes(sym.info) match { + case Some(actualTypes) => actualTypes == targetTypes + case None => false + } + } + matchingMethod match { + case Some(sym) => Sym(sym) + case None => + throw new AssertionError( + s"Could not find overload $name(${paramTypes.mkString(", ")}) in ${symbol.fullName}. " + + s"Available overloads: ${methods.map(m => m.name + ": " + m.info.show).mkString("; ")}" + ) + } + } } def cls(fqn: String) = Sym(q.reflect.Symbol.classSymbol(fqn)) diff --git a/scaladoc/test/dotty/tools/scaladoc/tasty/comments/QueryParserTests.scala b/scaladoc/test/dotty/tools/scaladoc/tasty/comments/QueryParserTests.scala index 0d33a9363bac..91f013414e0a 100644 --- a/scaladoc/test/dotty/tools/scaladoc/tasty/comments/QueryParserTests.scala +++ b/scaladoc/test/dotty/tools/scaladoc/tasty/comments/QueryParserTests.scala @@ -48,6 +48,61 @@ class QueryParserTests { testSuccess("#foo\\(ignoredOverloadDefinition*", StrictMemberId("foo(ignoredOverloadDefinition*")) testSuccess("#bar\\[ignoredOverloadDefinition*", StrictMemberId("bar[ignoredOverloadDefinition*")) + // Test overloaded method signature parsing + testSuccess("processBuffer[A](b:scala.collection.mutable.Buffer[A])*", Id("processBuffer[A](b:scala.collection.mutable.Buffer[A])*")) + testSuccess("processMap[K,V](m:scala.collection.mutable.Map[K,V])*", Id("processMap[K,V](m:scala.collection.mutable.Map[K,V])*")) + testSuccess("processSet[A](s:scala.collection.mutable.Set[A])*", Id("processSet[A](s:scala.collection.mutable.Set[A])*")) + testSuccess("processSeq[A](s:scala.collection.mutable.Seq[A])*", Id("processSeq[A](s:scala.collection.mutable.Seq[A])*")) + testSuccess("processIterator[A](it:scala.collection.Iterator[A])*", Id("processIterator[A](it:scala.collection.Iterator[A])*")) + testSuccess("processIterable[A](it:scala.collection.mutable.Iterable[A])*", Id("processIterable[A](it:scala.collection.mutable.Iterable[A])*")) + testSuccess("processList[A](l:scala.collection.immutable.List[A])*", Id("processList[A](l:scala.collection.immutable.List[A])*")) + testSuccess("processProperties(p:java.util.Properties)*", Id("processProperties(p:java.util.Properties)*")) + + // Test qualified identifiers with overloaded method signatures + testSuccess("tests.overloadedMethods.OverloadedMethods.processBuffer[A](b:scala.collection.mutable.Buffer[A])*", + l2q("tests".dot, "overloadedMethods".dot, "OverloadedMethods".dot)("processBuffer[A](b:scala.collection.mutable.Buffer[A])*")) + testSuccess("tests.overloadedMethods.OverloadedMethods.processMap[K,V](m:scala.collection.mutable.Map[K,V])*", + l2q("tests".dot, "overloadedMethods".dot, "OverloadedMethods".dot)("processMap[K,V](m:scala.collection.mutable.Map[K,V])*")) + testSuccess("tests.overloadedMethods.OverloadedMethods.processSet[A](s:scala.collection.mutable.Set[A])*", + l2q("tests".dot, "overloadedMethods".dot, "OverloadedMethods".dot)("processSet[A](s:scala.collection.mutable.Set[A])*")) + + // Test method with multiple parameter types in signature + testSuccess("transform[K,V](m:scala.collection.mutable.Map[K,V])*", Id("transform[K,V](m:scala.collection.mutable.Map[K,V])*")) + testSuccess("transform[A](b:scala.collection.mutable.Buffer[A])*", Id("transform[A](b:scala.collection.mutable.Buffer[A])*")) + testSuccess("transform(x:Int)*", Id("transform(x:Int)*")) + + // Test edge cases for overload signatures + // Nested generics + testSuccess("foo[A](x:Map[String,List[A]])*", Id("foo[A](x:Map[String,List[A]])*")) + testSuccess("f[A,B](x:Either[Option[A],List[B]])*", Id("f[A,B](x:Either[Option[A],List[B]])*")) + + // Multiple parameters + testSuccess("bar(a:Int,b:String)*", Id("bar(a:Int,b:String)*")) + testSuccess("baz(a:Int,b:String,c:Boolean)*", Id("baz(a:Int,b:String,c:Boolean)*")) + testSuccess("merge(a:Buffer[_],b:Set[_])*", Id("merge(a:Buffer[_],b:Set[_])*")) + + // Empty parameters (no-arg methods) + testSuccess("noArgs()*", Id("noArgs()*")) + testSuccess("action()*", Id("action()*")) + testSuccess("pkg.Class.action()*", l2q("pkg".dot, "Class".dot)("action()*")) + + // No type parameters + testSuccess("simpleMethod(x:Int)*", Id("simpleMethod(x:Int)*")) + testSuccess("twoParams(a:String,b:Boolean)*", Id("twoParams(a:String,b:Boolean)*")) + + // Deeply nested types + testSuccess("complex[A](x:Map[String,Map[Int,List[A]]])*", Id("complex[A](x:Map[String,Map[Int,List[A]]])*")) + + // Custom/user-defined types + testSuccess("process(x:tests.CustomType)*", Id("process(x:tests.CustomType)*")) + testSuccess("handle(x:com.example.MyClass)*", Id("handle(x:com.example.MyClass)*")) + + // Qualified identifiers with complex signatures + testSuccess("pkg.Class.method[A](x:Map[String,List[A]])*", + l2q("pkg".dot, "Class".dot)("method[A](x:Map[String,List[A]])*")) + testSuccess("a.b.c.method(a:Int,b:String)*", + l2q("a".dot, "b".dot, "c".dot)("method(a:Int,b:String)*")) + testFailAt("#", 1) testFailAt("#`", 2) testFailAt("``", 2) diff --git a/scaladoc/test/dotty/tools/scaladoc/tasty/comments/TagSanitizationTest.scala b/scaladoc/test/dotty/tools/scaladoc/tasty/comments/TagSanitizationTest.scala new file mode 100644 index 000000000000..d29b6fdba3ed --- /dev/null +++ b/scaladoc/test/dotty/tools/scaladoc/tasty/comments/TagSanitizationTest.scala @@ -0,0 +1,46 @@ +package dotty.tools.scaladoc +package tasty +package comments + +import org.junit.Test +import org.junit.Assert.* + +import java.nio.file.Files + +class TagSanitizationTest extends BaseHtmlTest: + private def docHtml(cls: String): String = + super.docHtml("sanitization", cls, "markdown") + + @Test def scriptTag(): Unit = + val html = docHtml("Script") + assertEquals("

alert('hello')

", html) + + @Test def scriptTagWithSpaces(): Unit = + val html = docHtml("ScriptWithSpaces") + assertEquals("

< script >alert('hello')

", html) + + @Test def scriptTagWithSafeChar(): Unit = + val html = docHtml("FakeSafeScript") + assertEquals("

alert('hello')

", html) + + @Test def notATag(): Unit = + val html = docHtml("NotATag") + assertEquals("

Example < Second <: Third <= Fourth

", html) + + @Test def notATagButHasGreaterThan(): Unit = + val html = docHtml("NotATagButHasGreaterThan") + assertEquals("

Example < Second >: Third

", html) + + @Test def notATagButNoSpaces(): Unit = + val html = docHtml("NotATagButNoSpaces") + assertEquals("

a<b

", html) + + @Test def tagOutsideCode(): Unit = + val html = docHtml("TagOutsideCode") + assertFalse(html, html.contains("")) + + @Test def linkToTagLike(): Unit = + val html = docHtml("LinkToTagLike") + // ensure we don't treat the text between <:< and >:> as a tag content + assertTrue(html, html.contains("or")) diff --git a/scaladoc/test/dotty/tools/scaladoc/tasty/util/TestUtils.scala b/scaladoc/test/dotty/tools/scaladoc/tasty/util/TestUtils.scala index 356268c44b3a..4c5e747fe9c1 100644 --- a/scaladoc/test/dotty/tools/scaladoc/tasty/util/TestUtils.scala +++ b/scaladoc/test/dotty/tools/scaladoc/tasty/util/TestUtils.scala @@ -20,7 +20,7 @@ object TestUtils { if classRoot.isDirectory then val bld = new ListBuffer[String] go(bld)(classRoot) - bld.result + bld.result() else sys.error(s"Class root could not be found: $classRoot") diff --git a/scaladoc/test/dotty/tools/scaladoc/testUtils.scala b/scaladoc/test/dotty/tools/scaladoc/testUtils.scala index 2ba78c321eab..9d1cc59dc3e3 100644 --- a/scaladoc/test/dotty/tools/scaladoc/testUtils.scala +++ b/scaladoc/test/dotty/tools/scaladoc/testUtils.scala @@ -18,7 +18,7 @@ case class ReportedDiagnostics(errors: List[Diagnostic], warnings: List[Diagnost extension (c: CompilerContext) def reportedDiagnostics: ReportedDiagnostics = val t = c.reporter.asInstanceOf[TestReporter] - ReportedDiagnostics(t.errors.result, t.warnings.result, t.infos.result) + ReportedDiagnostics(t.errors.result(), t.warnings.result(), t.infos.result()) def assertNoWarning(diag: ReportedDiagnostics) = assertEquals("Warnings should be empty", Nil, diag.warningMsgs) def assertNoErrors(diag: ReportedDiagnostics) = assertEquals("Erros should be empty", Nil, diag.errorMsgs) diff --git a/sjs-compiler-tests/test/scala/dotty/tools/dotc/ScalaJSCompilationTests.scala b/sjs-compiler-tests/test/scala/dotty/tools/dotc/ScalaJSCompilationTests.scala index 0f4eb633b770..285064cebca9 100644 --- a/sjs-compiler-tests/test/scala/dotty/tools/dotc/ScalaJSCompilationTests.scala +++ b/sjs-compiler-tests/test/scala/dotty/tools/dotc/ScalaJSCompilationTests.scala @@ -19,7 +19,7 @@ class ScalaJSCompilationTests extends ParallelTesting { // Test suite configuration -------------------------------------------------- def maxDuration = 60.seconds - def numberOfSlaves = 5 + def numberOfWorkers = 5 def safeMode = Properties.testsSafeMode def isInteractive = SummaryReport.isInteractive def testFilter = Properties.testsFilter @@ -39,6 +39,7 @@ class ScalaJSCompilationTests extends ParallelTesting { override protected def shouldSkipTestSource(testSource: TestSource): Boolean = testSource.allToolArgs.get(ToolName.ScalaJS).exists(_.contains("--skip")) + || super.shouldSkipTestSource(testSource) override def runMain(classPath: String, toolArgs: ToolArgs)(implicit summaryReport: SummaryReporting): Status = import scala.concurrent.ExecutionContext.Implicits.global diff --git a/staging/test-resources/repl-staging/i6007 b/staging/test-resources/repl-staging/i6007 index dcb99cc47c67..1a47c6fdc16a 100644 --- a/staging/test-resources/repl-staging/i6007 +++ b/staging/test-resources/repl-staging/i6007 @@ -1,9 +1,9 @@ scala> import scala.quoted._ scala> import quoted.staging.{Compiler => StagingCompiler, _} scala> implicit def compiler: StagingCompiler = StagingCompiler.make(getClass.getClassLoader) -def compiler: scala.quoted.staging.Compiler +def compiler: StagingCompiler scala> def v(using Quotes) = '{ (if true then Some(1) else None).map(v => v+1) } -def v(using x$1: scala.quoted.Quotes): scala.quoted.Expr[Option[Int]] +def v(using x$1: Quotes): Expr[Option[Int]] scala> scala.quoted.staging.withQuotes(v.show) val res0: String = (if (true) scala.Some.apply[scala.Int](1) else scala.None).map[scala.Int](((v: scala.Int) => v.+(1))) scala> scala.quoted.staging.run(v) diff --git a/staging/test-resources/repl-staging/i6263 b/staging/test-resources/repl-staging/i6263 index 8d967c1c58ac..986561872179 100644 --- a/staging/test-resources/repl-staging/i6263 +++ b/staging/test-resources/repl-staging/i6263 @@ -1,9 +1,9 @@ scala> import quoted._ scala> import quoted.staging.{Compiler => StagingCompiler, _} scala> implicit def compiler: StagingCompiler = StagingCompiler.make(getClass.getClassLoader) -def compiler: scala.quoted.staging.Compiler +def compiler: StagingCompiler scala> def fn[T : Type](v : T) = println("ok") -def fn[T](v: T)(implicit evidence$1: scala.quoted.Type[T]): Unit +def fn[T](v: T)(implicit evidence$1: Type[T]): Unit scala> withQuotes { fn("foo") } ok scala> withQuotes { fn((1,2)) } diff --git a/tasty/src/dotty/tools/tasty/TastyBuffer.scala b/tasty/src/dotty/tools/tasty/TastyBuffer.scala index f9266cf23617..2dec2bef8cbb 100644 --- a/tasty/src/dotty/tools/tasty/TastyBuffer.scala +++ b/tasty/src/dotty/tools/tasty/TastyBuffer.scala @@ -63,25 +63,32 @@ class TastyBuffer(initialSize: Int) { length += n } - /** Write a natural number in big endian format, base 128. - * All but the last digits have bit 0x80 set. + /** Write a 31-bit natural (nonnegative) integer number in big endian format, base 128, each digit being a byte. + * All bytes except the last one have bit 0x80 unset. */ - def writeNat(x: Int): Unit = + def writeNat(x: Int): Unit = { + if (x < 0) { + throw new IllegalArgumentException(s"Expected a natural (nonnegative) number to write, but got: $x") + } writeLongNat(x.toLong & 0x00000000FFFFFFFFL) + } - /** Write a natural number in 2's complement big endian format, base 128. - * All but the last digits have bit 0x80 set. + /** Write a 32-bit integer number in 2's complement big endian format, base 128, each digit being a byte. + * All bytes except the last one have bit 0x80 unset. */ def writeInt(x: Int): Unit = writeLongInt(x) /** - * Like writeNat, but for longs. Note that the - * binary representation of LongNat is identical to Nat - * if the long value is in the range Int.MIN_VALUE to - * Int.MAX_VALUE. + * Write a 63-bit natural (nonnegative) number in big endian format, base 128, each digit being a byte. + * All bytes except the last one have bit 0x80 unset. + * Note that the binary representation of LongNat is identical to Nat + * if the long value is in the range 0 to Int.MAX_VALUE. */ def writeLongNat(x: Long): Unit = { + if (x < 0) { + throw new IllegalArgumentException(s"Expected a natural (nonnegative) number to write, but got: $x") + } def writePrefix(x: Long): Unit = { val y = x >>> 7 if (y != 0L) writePrefix(y) @@ -92,7 +99,8 @@ class TastyBuffer(initialSize: Int) { writeByte(((x & 0x7f) | 0x80).toInt) } - /** Like writeInt, but for longs */ + /** Write a 64-bit long integer number in 2's complement big endian format, base 128, each digit being a byte. + * All bytes except the last one have bit 0x80 unset. */ def writeLongInt(x: Long): Unit = { def writePrefix(x: Long): Unit = { val y = x >> 7 diff --git a/tasty/src/dotty/tools/tasty/TastyReader.scala b/tasty/src/dotty/tools/tasty/TastyReader.scala index 31407f7a4ab8..ec1ae69551f9 100644 --- a/tasty/src/dotty/tools/tasty/TastyReader.scala +++ b/tasty/src/dotty/tools/tasty/TastyReader.scala @@ -27,7 +27,7 @@ class TastyReader(val bytes: Array[Byte], start: Int, end: Int, val base: Int = /** The address of the next byte to read */ def currentAddr: Addr = addr(bp) - /** the address one greater than the last brte to read */ + /** the address one greater than the last byte to read */ def endAddr: Addr = addr(end) /** Have all bytes been read? */ @@ -57,20 +57,34 @@ class TastyReader(val bytes: Array[Byte], start: Int, end: Int, val base: Int = result } - /** Read a natural number fitting in an Int in big endian format, base 128. - * All but the last digits have bit 0x80 set. + /** Read a 31-bit natural (nonnegative) integer number in big endian format, base 128, each digit being a byte. + * All bytes except the last one have bit 0x80 unset. */ - def readNat(): Int = readLongNat().toInt + def readNat(): Int = { + val l = readLongNat() + if (l > Int.MaxValue) { + throw new UnpickleException(s"Expected a 31-bit nat, got: $l") + } + l.toInt + } - /** Read an integer number in 2's complement big endian format, base 128. - * All but the last digits have bit 0x80 set. + /** Read a 32-bit integer number in 2's complement big endian format, base 128, each digit being a byte. + * All bytes except the last one have bit 0x80 unset. */ - def readInt(): Int = readLongInt().toInt + def readInt(): Int = { + val l = readLongInt() + val i = l.toInt + if (i.toLong != l) { + throw new UnpickleException(s"Expected a 32-bit int, got: $l") + } + i + } - /** Read a natural number fitting in a Long in big endian format, base 128. - * All but the last digits have bit 0x80 set. + /** Read a 63-bit natural (nonnegative) number in big endian format, base 128, each digit being a byte. + * All bytes except the last one have bit 0x80 unset. */ def readLongNat(): Long = { + val ogBp = bp var b = 0L var x = 0L while ({ @@ -78,13 +92,19 @@ class TastyReader(val bytes: Array[Byte], start: Int, end: Int, val base: Int = x = (x << 7) | (b & 0x7f) bp += 1 (b & 0x80) == 0 - }) - () + }) () + if (bp - ogBp > 9) { + throw new UnpickleException(s"Expected a long nat, but read too many bytes (${bp - ogBp})") + } + assert(x >= 0, "We read <= 9 groups of 7 bits so x must be nonnegative here") x } - /** Read a long integer number in 2's complement big endian format, base 128. */ + /** Read a 64-bit long integer number in 2's complement big endian format, base 128, each digit being a byte. + * All bytes except the last one have bit 0x80 unset. + */ def readLongInt(): Long = { + val ogBp = bp var b = bytes(bp) var x: Long = (b << 1).toByte >> 1 // sign extend with bit 6. bp += 1 @@ -93,13 +113,16 @@ class TastyReader(val bytes: Array[Byte], start: Int, end: Int, val base: Int = x = (x << 7) | (b & 0x7f) bp += 1 } + if (bp - ogBp > 10) { + throw new UnpickleException(s"Expected a long int, but read too many bytes (${bp - ogBp})") + } x } /** Read an uncompressed Long stored in 8 bytes in big endian format */ def readUncompressedLong(): Long = { var x: Long = 0 - for (i <- 0 to 7) + for (_ <- 0 to 7) x = (x << 8) | (readByte() & 0xff) x } diff --git a/tests/cmdTest-sbt-tests/sourcepath-with-inline-api-hash/project/build.properties b/tests/cmdTest-sbt-tests/sourcepath-with-inline-api-hash/project/build.properties index db1723b08622..73df629ac1a7 100644 --- a/tests/cmdTest-sbt-tests/sourcepath-with-inline-api-hash/project/build.properties +++ b/tests/cmdTest-sbt-tests/sourcepath-with-inline-api-hash/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.5 +sbt.version=1.10.7 diff --git a/tests/cmdTest-sbt-tests/sourcepath-with-inline/project/build.properties b/tests/cmdTest-sbt-tests/sourcepath-with-inline/project/build.properties index db1723b08622..73df629ac1a7 100644 --- a/tests/cmdTest-sbt-tests/sourcepath-with-inline/project/build.properties +++ b/tests/cmdTest-sbt-tests/sourcepath-with-inline/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.5 +sbt.version=1.10.7 diff --git a/tests/coverage/pos/BranchesIgnoredLocally.scala b/tests/coverage/pos/BranchesIgnoredLocally.scala new file mode 100644 index 000000000000..5608b3975fcf --- /dev/null +++ b/tests/coverage/pos/BranchesIgnoredLocally.scala @@ -0,0 +1,27 @@ +package covtest + +class BranchesIgnoredLocally: + def coveredBranch(x: Int): String = + if x > 0 then "positive" + else "non-positive" + + // $COVERAGE-OFF$ + def ignoredBranch(x: Int): String = + if x > 0 then "positive" + else "non-positive" + + def ignoredMatch(x: Int): String = x match + case 1 => "one" + case 2 => "two" + case _ => "other" + + def ignoredTryCatch: Int = + try 1 / 0 + catch + case _: ArithmeticException => -1 + // $COVERAGE-ON$ + + def coveredMatch(x: Any): String = x match + case _: Int => "int" + case _: String => "string" + case _ => "other" diff --git a/tests/coverage/pos/BranchesIgnoredLocally.scoverage.check b/tests/coverage/pos/BranchesIgnoredLocally.scoverage.check new file mode 100644 index 000000000000..109efe98546e --- /dev/null +++ b/tests/coverage/pos/BranchesIgnoredLocally.scoverage.check @@ -0,0 +1,564 @@ +# 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 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +coveredBranch +92 +97 +5 +> +Apply +false +0 +false +x > 0 + +1 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +coveredBranch +103 +113 +5 + +Literal +false +0 +false +"positive" + +2 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +coveredBranch +103 +113 +5 + +Literal +true +0 +false +"positive" + +3 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +coveredBranch +123 +137 +6 + +Literal +false +0 +false +"non-positive" + +4 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +coveredBranch +123 +137 +6 + +Literal +true +0 +false +"non-positive" + +5 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +coveredBranch +49 +66 +4 +coveredBranch +DefDef +false +0 +false +def coveredBranch + +6 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredBranch +204 +209 +10 +> +Apply +false +0 +true +x > 0 + +7 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredBranch +215 +225 +10 + +Literal +false +0 +true +"positive" + +8 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredBranch +215 +225 +10 + +Literal +true +0 +true +"positive" + +9 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredBranch +235 +249 +11 + +Literal +false +0 +true +"non-positive" + +10 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredBranch +235 +249 +11 + +Literal +true +0 +true +"non-positive" + +11 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredBranch +161 +178 +9 +ignoredBranch +DefDef +false +0 +true +def ignoredBranch + +12 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredMatch +310 +315 +14 + +Literal +false +0 +true +"one" + +13 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredMatch +307 +315 +14 + +Block +true +0 +true +=> "one" + +14 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredMatch +330 +335 +15 + +Literal +false +0 +true +"two" + +15 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredMatch +327 +335 +15 + +Block +true +0 +true +=> "two" + +16 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredMatch +350 +357 +16 + +Literal +false +0 +true +"other" + +17 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredMatch +347 +357 +16 + +Block +true +0 +true +=> "other" + +18 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredMatch +253 +269 +13 +ignoredMatch +DefDef +false +0 +true +def ignoredMatch + +19 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredTryCatch +396 +401 +19 +/ +Apply +false +0 +true +1 / 0 + +20 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredTryCatch +396 +397 +19 + +Literal +false +0 +true +1 + +21 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredTryCatch +396 +401 +19 +/ +Apply +true +0 +true +1 / 0 + +22 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredTryCatch +449 +451 +21 + +Literal +false +0 +true +-1 + +23 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredTryCatch +446 +451 +21 + +Block +true +0 +true +=> -1 + +24 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +ignoredTryCatch +361 +380 +18 +ignoredTryCatch +DefDef +false +0 +true +def ignoredTryCatch + +25 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +coveredMatch +536 +541 +25 + +Literal +false +0 +false +"int" + +26 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +coveredMatch +533 +541 +25 + +Block +true +0 +false +=> "int" + +27 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +coveredMatch +564 +572 +26 + +Literal +false +0 +false +"string" + +28 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +coveredMatch +561 +572 +26 + +Block +true +0 +false +=> "string" + +29 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +coveredMatch +587 +594 +27 + +Literal +false +0 +false +"other" + +30 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +coveredMatch +584 +594 +27 + +Block +true +0 +false +=> "other" + +31 +BranchesIgnoredLocally.scala +covtest +BranchesIgnoredLocally +Class +covtest.BranchesIgnoredLocally +coveredMatch +474 +490 +24 +coveredMatch +DefDef +false +0 +false +def coveredMatch + 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/DefaultArgs.scala b/tests/coverage/pos/DefaultArgs.scala new file mode 100644 index 000000000000..1e7136b44d15 --- /dev/null +++ b/tests/coverage/pos/DefaultArgs.scala @@ -0,0 +1,29 @@ +package covtest + +// Test case for default arguments with coverage. +// Default getter methods ($default$N) should not be instrumented/lifted +// as this can cause issues with ScalaJS IR (https://github.com/scala/scala3/issues/20255). + +class DefaultArgs: + def methodWithDefaults( + a: String, + b: Int = 0, + c: Boolean = true + ): String = + s"$a, $b, $c" + + def caller(): String = + // This call uses default arguments for b and c + methodWithDefaults("test") + + def callerPartial(): String = + // This call uses default argument only for c + methodWithDefaults("test", 42) + +object DefaultArgs: + def staticMethod(x: Int = 10, y: Int = 20): Int = + x + y + + def staticCaller(): Int = + staticMethod() + staticMethod(5) + staticMethod(5, 15) + diff --git a/tests/coverage/pos/DefaultArgs.scoverage.check b/tests/coverage/pos/DefaultArgs.scoverage.check new file mode 100644 index 000000000000..2b4da7ddafc7 --- /dev/null +++ b/tests/coverage/pos/DefaultArgs.scoverage.check @@ -0,0 +1,428 @@ +# 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 +DefaultArgs.scala +covtest +DefaultArgs +Class +covtest.DefaultArgs +methodWithDefaults +349 +362 +13 +s +Apply +false +0 +false +s"$a, $b, $c" + +1 +DefaultArgs.scala +covtest +DefaultArgs +Class +covtest.DefaultArgs +methodWithDefaults +254 +276 +8 +methodWithDefaults +DefDef +false +0 +false +def methodWithDefaults + +2 +DefaultArgs.scala +covtest +DefaultArgs +Class +covtest.DefaultArgs +methodWithDefaults$default$2 +306 +307 +10 + +Literal +false +0 +false +0 + +3 +DefaultArgs.scala +covtest +DefaultArgs +Class +covtest.DefaultArgs +methodWithDefaults$default$2 +306 +324 +10 +methodWithDefaults$default$2 +DefDef +false +0 +false +0,\n c: Boolean + +4 +DefaultArgs.scala +covtest +DefaultArgs +Class +covtest.DefaultArgs +methodWithDefaults$default$3 +326 +330 +11 + +Literal +false +0 +false +true + +5 +DefaultArgs.scala +covtest +DefaultArgs +Class +covtest.DefaultArgs +methodWithDefaults$default$3 +326 +344 +11 +methodWithDefaults$default$3 +DefDef +false +0 +false +true\n ): String = + +6 +DefaultArgs.scala +covtest +DefaultArgs +Class +covtest.DefaultArgs +caller +445 +471 +17 +methodWithDefaults +Apply +false +0 +false +methodWithDefaults("test") + +7 +DefaultArgs.scala +covtest +DefaultArgs +Class +covtest.DefaultArgs +caller +464 +470 +17 + +Literal +false +0 +false +"test" + +8 +DefaultArgs.scala +covtest +DefaultArgs +Class +covtest.DefaultArgs +caller +366 +376 +15 +caller +DefDef +false +0 +false +def caller + +9 +DefaultArgs.scala +covtest +DefaultArgs +Class +covtest.DefaultArgs +callerPartial +559 +589 +21 +methodWithDefaults +Apply +false +0 +false +methodWithDefaults("test", 42) + +10 +DefaultArgs.scala +covtest +DefaultArgs +Class +covtest.DefaultArgs +callerPartial +578 +584 +21 + +Literal +false +0 +false +"test" + +11 +DefaultArgs.scala +covtest +DefaultArgs +Class +covtest.DefaultArgs +callerPartial +586 +588 +21 + +Literal +false +0 +false +42 + +12 +DefaultArgs.scala +covtest +DefaultArgs +Class +covtest.DefaultArgs +callerPartial +475 +492 +19 +callerPartial +DefDef +false +0 +false +def callerPartial + +13 +DefaultArgs.scala +covtest +DefaultArgs +Object +covtest.DefaultArgs +staticMethod +667 +672 +25 ++ +Apply +false +0 +false +x + y + +14 +DefaultArgs.scala +covtest +DefaultArgs +Object +covtest.DefaultArgs +staticMethod +613 +629 +24 +staticMethod +DefDef +false +0 +false +def staticMethod + +15 +DefaultArgs.scala +covtest +DefaultArgs +Object +covtest.DefaultArgs +staticMethod$default$1 +639 +641 +24 + +Literal +false +0 +false +10 + +16 +DefaultArgs.scala +covtest +DefaultArgs +Object +covtest.DefaultArgs +staticMethod$default$1 +639 +651 +24 +staticMethod$default$1 +DefDef +false +0 +false +10, y: Int = + +17 +DefaultArgs.scala +covtest +DefaultArgs +Object +covtest.DefaultArgs +staticMethod$default$2 +652 +654 +24 + +Literal +false +0 +false +20 + +18 +DefaultArgs.scala +covtest +DefaultArgs +Object +covtest.DefaultArgs +staticMethod$default$2 +652 +664 +24 +staticMethod$default$2 +DefDef +false +0 +false +20): Int =\n + +19 +DefaultArgs.scala +covtest +DefaultArgs +Object +covtest.DefaultArgs +staticCaller +706 +760 +28 ++ +Apply +false +0 +false +staticMethod() + staticMethod(5) + staticMethod(5, 15) + +20 +DefaultArgs.scala +covtest +DefaultArgs +Object +covtest.DefaultArgs +staticCaller +723 +738 +28 +staticMethod +Apply +false +0 +false +staticMethod(5) + +21 +DefaultArgs.scala +covtest +DefaultArgs +Object +covtest.DefaultArgs +staticCaller +736 +737 +28 + +Literal +false +0 +false +5 + +22 +DefaultArgs.scala +covtest +DefaultArgs +Object +covtest.DefaultArgs +staticCaller +741 +760 +28 +staticMethod +Apply +false +0 +false +staticMethod(5, 15) + +23 +DefaultArgs.scala +covtest +DefaultArgs +Object +covtest.DefaultArgs +staticCaller +676 +692 +27 +staticCaller +DefDef +false +0 +false +def staticCaller + diff --git a/tests/coverage/pos/Enum.scoverage.check b/tests/coverage/pos/Enum.scoverage.check index 6806934e0dec..90c5a4aa8469 100644 --- a/tests/coverage/pos/Enum.scoverage.check +++ b/tests/coverage/pos/Enum.scoverage.check @@ -21,6 +21,74 @@ 0 Enum.scala covtest +EnumTypes +Object +covtest.EnumTypes + +239 +249 +11 + +Apply +false +0 +false +case Empty + +1 +Enum.scala +covtest +Planet +Class +covtest.Planet + +322 +333 +14 + +Literal +false +0 +false +6.67300E-11 + +2 +Enum.scala +covtest +Planet +Class +covtest.Planet +surfaceGravity +359 +386 +15 +/ +Apply +false +0 +false +G * mass / (radius * radius + +3 +Enum.scala +covtest +Planet +Class +covtest.Planet +surfaceGravity +371 +386 +15 +* +Apply +false +0 +false +radius * radius + +4 +Enum.scala +covtest Planet Class covtest.Planet @@ -35,7 +103,24 @@ false false def surfaceGravity -1 +5 +Enum.scala +covtest +Planet +Class +covtest.Planet +surfaceWeight +432 +458 +16 +* +Apply +false +0 +false +otherMass * surfaceGravity + +6 Enum.scala covtest Planet @@ -52,7 +137,7 @@ false false surfaceGravity -2 +7 Enum.scala covtest Planet @@ -69,7 +154,245 @@ false false def surfaceWeight +8 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes + +485 +512 +18 + +Apply +false +0 +false +Planet(3.303e+23, 2.4397e6) + +9 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes + +538 +565 +19 + +Apply +false +0 +false +Planet(4.869e+24, 6.0518e6) + +10 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes + +591 +619 +20 + +Apply +false +0 +false +Planet(5.976e+24, 6.37814e6) + +11 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes + +645 +672 +21 + +Apply +false +0 +false +Planet(6.421e+23, 3.3972e6) + +12 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes + +698 +725 +22 + +Apply +false +0 +false +Planet(1.9e+27, 7.1492e7) + +13 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes + +751 +778 +23 + +Apply +false +0 +false +Planet(5.688e+26, 6.0268e7) + +14 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes + +804 +831 +24 + +Apply +false +0 +false +Planet(8.686e+25, 2.5559e7) + +15 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes + +857 +884 +25 + +Apply +false +0 +false +Planet(1.024e+26, 2.4746e7) + +16 +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))) + +17 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +test +984 +985 +30 + +Literal +false +0 +false +1 + +18 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +test +987 +1037 +30 +apply +Apply +false +0 +false +ListEnum.Cons(2, ListEnum.Cons(3, ListEnum.Empty)) + +19 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +test +1001 +1002 +30 + +Literal +false +0 +false +2 + +20 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +test +1004 +1036 +30 +apply +Apply +false +0 +false +ListEnum.Cons(3, ListEnum.Empty) + +21 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +test +1018 +1019 +30 + +Literal +false +0 +false 3 + +22 Enum.scala covtest EnumTypes @@ -86,7 +409,7 @@ false false println("Example 1: \\n"+emptyList) -4 +23 Enum.scala covtest EnumTypes @@ -103,7 +426,24 @@ false false "Example 1: \\n"+emptyList -5 +24 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +test +1051 +1066 +31 + +Literal +false +0 +false +"Example 1: \\n" + +25 Enum.scala covtest EnumTypes @@ -120,7 +460,7 @@ false false println(s"${list}\\n") -6 +26 Enum.scala covtest EnumTypes @@ -137,7 +477,24 @@ false false s"${list}\\n" -7 +27 +Enum.scala +covtest +EnumTypes +Object +covtest.EnumTypes +calculateEarthWeightOnPlanets +1183 +1222 +35 +/ +Apply +false +0 +false +earthWeight/Planet.Earth.surfaceGravity + +28 Enum.scala covtest EnumTypes @@ -154,7 +511,7 @@ false false Planet.Earth.surfaceGravity -8 +29 Enum.scala covtest EnumTypes @@ -171,7 +528,7 @@ false false for p <- Planet.values do\n println(s"Your weight on $p is ${p.surfaceWeight(mass)}") -9 +30 Enum.scala covtest EnumTypes @@ -181,14 +538,14 @@ calculateEarthWeightOnPlanets 1238 1251 36 -refArrayOps -Apply +values +Select false 0 false Planet.values -10 +31 Enum.scala covtest EnumTypes @@ -205,7 +562,7 @@ false false println(s"Your weight on $p is ${p.surfaceWeight(mass)}") -11 +32 Enum.scala covtest EnumTypes @@ -222,7 +579,7 @@ false false s"Your weight on $p is ${p.surfaceWeight(mass)}" -12 +33 Enum.scala covtest EnumTypes @@ -239,7 +596,7 @@ false false p.surfaceWeight(mass) -13 +34 Enum.scala covtest EnumTypes @@ -256,7 +613,7 @@ false false def calculateEarthWeightOnPlanets -14 +35 Enum.scala covtest EnumTypes @@ -273,7 +630,7 @@ false false println("Example 2:") -15 +36 Enum.scala covtest EnumTypes @@ -290,7 +647,7 @@ false false calculateEarthWeightOnPlanets(80) -16 +37 Enum.scala covtest EnumTypes diff --git a/tests/coverage/pos/EnumJava.scoverage.check b/tests/coverage/pos/EnumJava.scoverage.check index 1bdba951d6ae..4b527360257f 100644 --- a/tests/coverage/pos/EnumJava.scoverage.check +++ b/tests/coverage/pos/EnumJava.scoverage.check @@ -18,3 +18,54 @@ # - description (can be multi-line) # ' ' sign # ------------------------------------------ +0 +EnumJava.scala +covtest +covtest +Object +covtest.covtest + +90 +100 +4 + +Apply +false +0 +false +MyLogLevel + +1 +EnumJava.scala +covtest +covtest +Object +covtest.covtest + +122 +132 +5 + +Apply +false +0 +false +MyLogLevel + +2 +EnumJava.scala +covtest +covtest +Object +covtest.covtest + +154 +164 +6 + +Apply +false +0 +false +MyLogLevel + 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 7370a3909c54..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,24 +188,7 @@ false false Nil.foreach(_ => println("user code here")) -14 -For.scala -covtest -For$package -Object -covtest.For$package -testForeach -301 -304 -14 -Nil -Ident -false -0 -false -Nil - -15 +10 For.scala covtest For$package @@ -290,7 +205,7 @@ false false println("user code here") -16 +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 53008bd727c5..cf8d1bcdb0f7 100644 --- a/tests/coverage/pos/Inlined.scoverage.check +++ b/tests/coverage/pos/Inlined.scoverage.check @@ -25,264 +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 -159 -7 -List -Ident -false -0 -false -List - -5 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -155 -169 -7 -length -Select -false -0 -false -List(l).length - +215 +216 6 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -288 -330 -11 -assertFailed -Apply -false -0 -false -scala.runtime.Scala3RunTime.assertFailed() - -7 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -288 -330 -11 -assertFailed -Apply -true -0 -false -scala.runtime.Scala3RunTime.assertFailed() - -8 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -330 -330 -11 Literal -true -0 -false - - -9 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -180 -187 -8 -apply -Apply false 0 false -List(l) - -10 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -180 -184 -8 -List -Ident -false -0 -false -List +1 -11 +1 Inlined.scala covtest Inlined$package Object covtest.Inlined$package testInlined -180 +179 194 -8 -length -Select -false -0 -false -List(l).length - -12 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -288 -330 -11 -assertFailed -Apply -false -0 -false -scala.runtime.Scala3RunTime.assertFailed() - -13 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -288 -330 -11 -assertFailed -Apply -true -0 -false -scala.runtime.Scala3RunTime.assertFailed() - -14 -Inlined.scala -covtest -Inlined$package -Object -covtest.Inlined$package -testInlined -330 -330 -11 - -Literal -true -0 -false - - -15 -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 005ce981aa1a..bd7bd1a65b6f 100644 --- a/tests/coverage/pos/InlinedFromLib.scoverage.check +++ b/tests/coverage/pos/InlinedFromLib.scoverage.check @@ -25,264 +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 -202 -8 -List -Ident -false -0 -false -List - -5 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -198 -212 -8 -length -Select -false -0 -false -List(l).length - -6 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -186 -213 -8 -assertFailed -Apply -false -0 -false -assert(l == List(l).length) - -7 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -186 -213 -8 -assertFailed -Apply -true -0 -false -assert(l == List(l).length) - -8 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -186 -213 -8 Literal -true -0 -false -assert(l == List(l).length) - -9 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -223 -230 -9 -apply -Apply -false -0 -false -List(l) - -10 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -223 -227 -9 -List -Ident false 0 false -List +1 -11 +1 InlinedFromLib.scala covtest InlinedFromLib$package Object covtest.InlinedFromLib$package testInlined -223 +222 237 -9 -length -Select -false -0 -false -List(l).length - -12 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -216 -243 -9 -assertFailed -Apply -false -0 -false -assert(List(l).length == 1) - -13 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -216 -243 -9 -assertFailed -Apply -true -0 -false -assert(List(l).length == 1) - -14 -InlinedFromLib.scala -covtest -InlinedFromLib$package -Object -covtest.InlinedFromLib$package -testInlined -216 -243 -9 - -Literal -true -0 -false -assert(List(l).length == 1) - -15 -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..efd410be5bca 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,24 @@ false false def partialCond -12 +23 +SimpleMethods.scala +covtest +C +Class +covtest.C +new1 +291 +297 +21 + +Apply +false +0 +false +new {} + +24 SimpleMethods.scala covtest C @@ -239,7 +443,24 @@ false false def new1 -13 +25 +SimpleMethods.scala +covtest +C +Class +covtest.C +tryCatch +330 +332 +24 + +Literal +false +0 +false +() + +26 SimpleMethods.scala covtest C @@ -256,7 +477,24 @@ true false () -14 +27 +SimpleMethods.scala +covtest +C +Class +covtest.C +tryCatch +370 +371 +26 + +Literal +false +0 +false +1 + +28 SimpleMethods.scala covtest C @@ -273,7 +511,7 @@ true false => 1 -15 +29 SimpleMethods.scala covtest C diff --git a/tests/coverage/pos/SimpleMethodsIgnoredLocally.scala b/tests/coverage/pos/SimpleMethodsIgnoredLocally.scala new file mode 100644 index 000000000000..d55cf8556b31 --- /dev/null +++ b/tests/coverage/pos/SimpleMethodsIgnoredLocally.scala @@ -0,0 +1,29 @@ +package covtest + +class C: + def a: C = this + def b: Unit = return + def c: Unit = () + def d: Int = 12 + def e: Null = null + + // $COVERAGE-OFF$ + def block: Int = + "literal" + 0 + + def cond: Boolean = + if false then true + else false + + def partialCond: Unit = + if false then () + + // $COVERAGE-ON$ + + def new1: C = new {} + + def tryCatch: Unit = + try () + catch + case e: Exception => 1 diff --git a/tests/coverage/pos/SimpleMethodsIgnoredLocally.scoverage.check b/tests/coverage/pos/SimpleMethodsIgnoredLocally.scoverage.check new file mode 100644 index 000000000000..71d21df39318 --- /dev/null +++ b/tests/coverage/pos/SimpleMethodsIgnoredLocally.scoverage.check @@ -0,0 +1,530 @@ +# 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 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +a +28 +33 +4 +a +DefDef +false +0 +false +def a + +1 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +b +60 +66 +5 + +Literal +false +0 +false +return + +2 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +b +46 +51 +5 +b +DefDef +false +0 +false +def b + +3 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +c +83 +85 +6 + +Literal +false +0 +false +() + +4 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +c +69 +74 +6 +c +DefDef +false +0 +false +def c + +5 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +d +101 +103 +7 + +Literal +false +0 +false +12 + +6 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +d +88 +93 +7 +d +DefDef +false +0 +false +def d + +7 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +e +120 +124 +8 + +Literal +false +0 +false +null + +8 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +e +106 +111 +8 +e +DefDef +false +0 +false +def e + +9 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +block +169 +178 +12 + +Literal +false +0 +true +"literal" + +10 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +block +183 +184 +13 + +Literal +false +0 +true +0 + +11 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +block +148 +157 +11 +block +DefDef +false +0 +true +def block + +12 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +cond +215 +220 +16 + +Literal +false +0 +true +false + +13 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +cond +226 +230 +16 + +Literal +false +0 +true +true + +14 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +cond +226 +230 +16 + +Literal +true +0 +true +true + +15 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +cond +240 +245 +17 + +Literal +false +0 +true +false + +16 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +cond +240 +245 +17 + +Literal +true +0 +true +false + +17 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +cond +188 +196 +15 +cond +DefDef +false +0 +true +def cond + +18 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +partialCond +280 +285 +20 + +Literal +false +0 +true +false + +19 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +partialCond +291 +293 +20 + +Literal +false +0 +true +() + +20 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +partialCond +291 +293 +20 + +Literal +true +0 +true +() + +21 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +partialCond +293 +293 +20 + +Literal +true +0 +true + + +22 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +partialCond +249 +264 +19 +partialCond +DefDef +false +0 +true +def partialCond + +23 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +new1 +331 +337 +24 + +Apply +false +0 +false +new {} + +24 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +new1 +317 +325 +24 +new1 +DefDef +false +0 +false +def new1 + +25 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +tryCatch +370 +372 +27 + +Literal +false +0 +false +() + +26 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +tryCatch +370 +372 +27 + +Literal +true +0 +false +() + +27 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +tryCatch +410 +411 +29 + +Literal +false +0 +false +1 + +28 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +tryCatch +407 +411 +29 + +Block +true +0 +false +=> 1 + +29 +SimpleMethodsIgnoredLocally.scala +covtest +C +Class +covtest.C +tryCatch +341 +353 +26 +tryCatch +DefDef +false +0 +false +def tryCatch + diff --git a/tests/coverage/pos/SingletonLiftCoverage.scala b/tests/coverage/pos/SingletonLiftCoverage.scala new file mode 100644 index 000000000000..c580b2bc77b8 --- /dev/null +++ b/tests/coverage/pos/SingletonLiftCoverage.scala @@ -0,0 +1,19 @@ +/** Coverage test: singleton precision preserved when lifting for coverage. + * + * Regression test for the GenBCodeOps crash (Found: Int, Required: (9 : Int)). + * Both examples trigger LiftCoverage to lift Apply args; the fix preserves + * singleton types so Ycheck passes. + */ +package covtest + +object Ops: + final val PUBLIC = 1 + final val STATIC = 8 + +object SingletonLiftMinimal: + // + 0 forces the arg to be an Apply (lifted by coverage), producing Ops.PUBLIC | x$1 + final val PublicStatic = Ops.PUBLIC | (Ops.STATIC + 0) + +object SingletonLiftJavaReflect: + final val PublicStatic = + java.lang.reflect.Modifier.PUBLIC | java.lang.reflect.Modifier.STATIC diff --git a/tests/coverage/pos/SingletonLiftCoverage.scoverage.check b/tests/coverage/pos/SingletonLiftCoverage.scoverage.check new file mode 100644 index 000000000000..ae83cef4e71e --- /dev/null +++ b/tests/coverage/pos/SingletonLiftCoverage.scoverage.check @@ -0,0 +1,105 @@ +# 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 +SingletonLiftCoverage.scala +covtest +Ops +Object +covtest.Ops + +331 +332 +10 + +Literal +false +0 +false +1 + +1 +SingletonLiftCoverage.scala +covtest +Ops +Object +covtest.Ops + +354 +355 +11 + +Literal +false +0 +false +8 + +2 +SingletonLiftCoverage.scala +covtest +SingletonLiftMinimal +Object +covtest.SingletonLiftMinimal + +501 +529 +15 +| +Apply +false +0 +false +Ops.PUBLIC | (Ops.STATIC + 0 + +3 +SingletonLiftCoverage.scala +covtest +SingletonLiftMinimal +Object +covtest.SingletonLiftMinimal + +515 +529 +15 ++ +Apply +false +0 +false +Ops.STATIC + 0 + +4 +SingletonLiftCoverage.scala +covtest +SingletonLiftJavaReflect +Object +covtest.SingletonLiftJavaReflect + +596 +665 +19 +| +Apply +false +0 +false +java.lang.reflect.Modifier.PUBLIC | java.lang.reflect.Modifier.STATIC + diff --git a/tests/coverage/pos/StructuralTypes.scoverage.check b/tests/coverage/pos/StructuralTypes.scoverage.check index 91bb60f9f293..b1f7109564c6 100644 --- a/tests/coverage/pos/StructuralTypes.scoverage.check +++ b/tests/coverage/pos/StructuralTypes.scoverage.check @@ -43,14 +43,14 @@ Class covtest.Record $anonfun 159 -163 +171 6 -_1 -Select +== +Apply false 0 false -_._1 +_._1 == name 2 StructuralTypes.scala @@ -110,6 +110,23 @@ StructuralTypes Object covtest.StructuralTypes test +270 +307 +13 +apply +Apply +false +0 +false +Record("name" -> "Emma", "age" -> 42) + +6 +StructuralTypes.scala +covtest +StructuralTypes +Object +covtest.StructuralTypes +test 277 293 13 @@ -120,7 +137,7 @@ false false "name" -> "Emma" -6 +7 StructuralTypes.scala covtest StructuralTypes @@ -137,7 +154,7 @@ false false "age" -> 42 -7 +8 StructuralTypes.scala covtest StructuralTypes @@ -154,7 +171,7 @@ false false person.name -8 +9 StructuralTypes.scala covtest StructuralTypes diff --git a/tests/coverage/pos/TryInstrumentCasesIgnoredLocally.scala b/tests/coverage/pos/TryInstrumentCasesIgnoredLocally.scala new file mode 100644 index 000000000000..899c165ada0d --- /dev/null +++ b/tests/coverage/pos/TryInstrumentCasesIgnoredLocally.scala @@ -0,0 +1,73 @@ +package covtest + +class AllTreesIgnoredLocally: + + // --- Apply --- + def coveredApply(x: Int): Int = x + 1 + // $COVERAGE-OFF$ + def ignoredApply(x: Int): Int = x + 1 + // $COVERAGE-ON$ + + // --- Literal --- + def coveredLiteral: Int = 42 + // $COVERAGE-OFF$ + def ignoredLiteral: Int = 42 + // $COVERAGE-ON$ + + // --- Select (parameterless method on object) --- + def coveredSelect(s: String): Int = s.length + // $COVERAGE-OFF$ + def ignoredSelect(s: String): Int = s.length + // $COVERAGE-ON$ + + // --- Ident (local parameterless def) --- + def coveredIdent: Int = + def local: Int = 1 + local + // $COVERAGE-OFF$ + def ignoredIdent: Int = + def local: Int = 1 + local + // $COVERAGE-ON$ + + // --- transformBranch (if/else) --- + def coveredBranch(x: Int): String = + if x > 0 then "pos" + else "neg" + // $COVERAGE-OFF$ + def ignoredBranch(x: Int): String = + if x > 0 then "pos" + else "neg" + // $COVERAGE-ON$ + + // --- instrumentBody --- + def coveredBody: Int = 99 + // $COVERAGE-OFF$ + def ignoredBody: Int = 99 + // $COVERAGE-ON$ + + def coveredClosureDef: Int ?=> Int = + 99 + // $COVERAGE-OFF$ + def ignoredClosureDef: Int ?=> Int = + 99 + // $COVERAGE-ON$ + + def coveredClosureBody: Int ?=> Int = + 99 + def ignoredClosureBody: Int ?=> Int = + // $COVERAGE-OFF$ + 99 + // $COVERAGE-ON$ + + +// --- instrumentSecondaryCtor --- +class CoveredCtor(val x: Int): + def this(s: String) = + this(s.length) + +// $COVERAGE-OFF$ +class IgnoredCtor(val x: Int): + def this(s: String) = + this(s.length) +// $COVERAGE-ON$ diff --git a/tests/coverage/pos/TryInstrumentCasesIgnoredLocally.scoverage.check b/tests/coverage/pos/TryInstrumentCasesIgnoredLocally.scoverage.check new file mode 100644 index 000000000000..2201c3a3e001 --- /dev/null +++ b/tests/coverage/pos/TryInstrumentCasesIgnoredLocally.scoverage.check @@ -0,0 +1,836 @@ +# 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 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredApply +101 +106 +6 ++ +Apply +false +0 +false +x + 1 + +1 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredApply +69 +85 +6 +coveredApply +DefDef +false +0 +false +def coveredApply + +2 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredApply +161 +166 +8 ++ +Apply +false +0 +true +x + 1 + +3 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredApply +129 +145 +8 +ignoredApply +DefDef +false +0 +true +def ignoredApply + +4 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredLiteral +236 +238 +12 + +Literal +false +0 +false +42 + +5 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredLiteral +210 +228 +12 +coveredLiteral +DefDef +false +0 +false +def coveredLiteral + +6 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredLiteral +287 +289 +14 + +Literal +false +0 +true +42 + +7 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredLiteral +261 +279 +14 +ignoredLiteral +DefDef +false +0 +true +def ignoredLiteral + +8 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredSelect +401 +409 +18 +length +Apply +false +0 +false +s.length + +9 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredSelect +365 +382 +18 +coveredSelect +DefDef +false +0 +false +def coveredSelect + +10 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredSelect +468 +476 +20 +length +Apply +false +0 +true +s.length + +11 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredSelect +432 +449 +20 +ignoredSelect +DefDef +false +0 +true +def ignoredSelect + +12 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +local +589 +590 +25 + +Literal +false +0 +false +1 + +13 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +local +572 +581 +25 +local +DefDef +false +0 +false +def local + +14 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredIdent +595 +600 +26 +local +Ident +false +0 +false +local + +15 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredIdent +544 +560 +24 +coveredIdent +DefDef +false +0 +false +def coveredIdent + +16 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +local +668 +669 +29 + +Literal +false +0 +true +1 + +17 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +local +651 +660 +29 +local +DefDef +false +0 +true +def local + +18 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredIdent +674 +679 +30 +local +Ident +false +0 +true +local + +19 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredIdent +623 +639 +28 +ignoredIdent +DefDef +false +0 +true +def ignoredIdent + +20 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredBranch +784 +789 +35 +> +Apply +false +0 +false +x > 0 + +21 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredBranch +795 +800 +35 + +Literal +false +0 +false +"pos" + +22 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredBranch +795 +800 +35 + +Literal +true +0 +false +"pos" + +23 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredBranch +810 +815 +36 + +Literal +false +0 +false +"neg" + +24 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredBranch +810 +815 +36 + +Literal +true +0 +false +"neg" + +25 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredBranch +741 +758 +34 +coveredBranch +DefDef +false +0 +false +def coveredBranch + +26 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredBranch +881 +886 +39 +> +Apply +false +0 +true +x > 0 + +27 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredBranch +892 +897 +39 + +Literal +false +0 +true +"pos" + +28 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredBranch +892 +897 +39 + +Literal +true +0 +true +"pos" + +29 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredBranch +907 +912 +40 + +Literal +false +0 +true +"neg" + +30 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredBranch +907 +912 +40 + +Literal +true +0 +true +"neg" + +31 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredBranch +838 +855 +38 +ignoredBranch +DefDef +false +0 +true +def ignoredBranch + +32 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredBody +986 +988 +44 + +Literal +false +0 +false +99 + +33 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredBody +963 +978 +44 +coveredBody +DefDef +false +0 +false +def coveredBody + +34 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredBody +1034 +1036 +46 + +Literal +false +0 +true +99 + +35 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredBody +1011 +1026 +46 +ignoredBody +DefDef +false +0 +true +def ignoredBody + +36 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +$anonfun +1100 +1102 +50 + +Literal +false +0 +false +99 + +37 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredClosureDef +1059 +1080 +49 +coveredClosureDef +DefDef +false +0 +false +def coveredClosureDef + +38 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +$anonfun +1166 +1168 +53 + +Literal +false +0 +true +99 + +39 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredClosureDef +1125 +1146 +52 +ignoredClosureDef +DefDef +false +0 +true +def ignoredClosureDef + +40 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +$anonfun +1233 +1235 +57 + +Literal +false +0 +false +99 + +41 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +coveredClosureBody +1191 +1213 +56 +coveredClosureBody +DefDef +false +0 +false +def coveredClosureBody + +42 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +$anonfun +1300 +1302 +60 + +Literal +false +0 +true +99 + +43 +TryInstrumentCasesIgnoredLocally.scala +covtest +AllTreesIgnoredLocally +Class +covtest.AllTreesIgnoredLocally +ignoredClosureBody +1238 +1260 +58 +ignoredClosureBody +DefDef +false +0 +false +def ignoredClosureBody + +44 +TryInstrumentCasesIgnoredLocally.scala +covtest +CoveredCtor +Class +covtest.CoveredCtor + +1392 +1400 +66 + +DefDef +false +0 +false +def this + +45 +TryInstrumentCasesIgnoredLocally.scala +covtest +CoveredCtor +Class +covtest.CoveredCtor + +1423 +1431 +67 +length +Apply +false +0 +false +s.length + +46 +TryInstrumentCasesIgnoredLocally.scala +covtest +IgnoredCtor +Class +covtest.IgnoredCtor + +1485 +1493 +71 + +DefDef +false +0 +true +def this + +47 +TryInstrumentCasesIgnoredLocally.scala +covtest +IgnoredCtor +Class +covtest.IgnoredCtor + +1516 +1524 +72 +length +Apply +false +0 +true +s.length + diff --git a/tests/coverage/pos/TypeLambdas.scoverage.check b/tests/coverage/pos/TypeLambdas.scoverage.check index 46c84036a2ff..f5779a45268b 100644 --- a/tests/coverage/pos/TypeLambdas.scoverage.check +++ b/tests/coverage/pos/TypeLambdas.scoverage.check @@ -42,15 +42,15 @@ TypeLambdas Object covtest.TypeLambdas test -306 -309 +310 +318 14 -Map -Ident +-> +Apply false 0 false -Map +1 -> "1" 2 TypeLambdas.scala @@ -59,15 +59,15 @@ TypeLambdas Object covtest.TypeLambdas test -310 -318 -14 --> +324 +334 +15 +println Apply false 0 false -1 -> "1" +println(m) 3 TypeLambdas.scala @@ -76,15 +76,15 @@ TypeLambdas Object covtest.TypeLambdas test -324 -334 -15 -println +367 +377 +17 +apply Apply false 0 false -println(m) +("a", "b") 4 TypeLambdas.scala diff --git a/tests/coverage/pos/WholeFileIgnoredLocally.scala b/tests/coverage/pos/WholeFileIgnoredLocally.scala new file mode 100644 index 000000000000..66203e8c7b67 --- /dev/null +++ b/tests/coverage/pos/WholeFileIgnoredLocally.scala @@ -0,0 +1,15 @@ +package covtest + +// $COVERAGE-OFF$ +class WholeFileIgnored: + def f(x: Int): Int = x + 1 + + def g: String = "hello" + + def h(x: Int): String = + if x > 0 then "positive" + else "negative" + +object WholeFileIgnoredObj: + val x = 42 + def compute(a: Int, b: Int): Int = a + b diff --git a/tests/coverage/pos/WholeFileIgnoredLocally.scoverage.check b/tests/coverage/pos/WholeFileIgnoredLocally.scoverage.check new file mode 100644 index 000000000000..1ea43c00e2f6 --- /dev/null +++ b/tests/coverage/pos/WholeFileIgnoredLocally.scoverage.check @@ -0,0 +1,241 @@ +# 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 +WholeFileIgnoredLocally.scala +covtest +WholeFileIgnored +Class +covtest.WholeFileIgnored +f +82 +87 +5 ++ +Apply +false +0 +true +x + 1 + +1 +WholeFileIgnoredLocally.scala +covtest +WholeFileIgnored +Class +covtest.WholeFileIgnored +f +61 +66 +5 +f +DefDef +false +0 +true +def f + +2 +WholeFileIgnoredLocally.scala +covtest +WholeFileIgnored +Class +covtest.WholeFileIgnored +g +107 +114 +7 + +Literal +false +0 +true +"hello" + +3 +WholeFileIgnoredLocally.scala +covtest +WholeFileIgnored +Class +covtest.WholeFileIgnored +g +91 +96 +7 +g +DefDef +false +0 +true +def g + +4 +WholeFileIgnoredLocally.scala +covtest +WholeFileIgnored +Class +covtest.WholeFileIgnored +h +149 +154 +10 +> +Apply +false +0 +true +x > 0 + +5 +WholeFileIgnoredLocally.scala +covtest +WholeFileIgnored +Class +covtest.WholeFileIgnored +h +160 +170 +10 + +Literal +false +0 +true +"positive" + +6 +WholeFileIgnoredLocally.scala +covtest +WholeFileIgnored +Class +covtest.WholeFileIgnored +h +160 +170 +10 + +Literal +true +0 +true +"positive" + +7 +WholeFileIgnoredLocally.scala +covtest +WholeFileIgnored +Class +covtest.WholeFileIgnored +h +180 +190 +11 + +Literal +false +0 +true +"negative" + +8 +WholeFileIgnoredLocally.scala +covtest +WholeFileIgnored +Class +covtest.WholeFileIgnored +h +180 +190 +11 + +Literal +true +0 +true +"negative" + +9 +WholeFileIgnoredLocally.scala +covtest +WholeFileIgnored +Class +covtest.WholeFileIgnored +h +118 +123 +9 +h +DefDef +false +0 +true +def h + +10 +WholeFileIgnoredLocally.scala +covtest +WholeFileIgnoredObj +Object +covtest.WholeFileIgnoredObj + +230 +232 +14 + +Literal +false +0 +true +42 + +11 +WholeFileIgnoredLocally.scala +covtest +WholeFileIgnoredObj +Object +covtest.WholeFileIgnoredObj +compute +270 +275 +15 ++ +Apply +false +0 +true +a + b + +12 +WholeFileIgnoredLocally.scala +covtest +WholeFileIgnoredObj +Object +covtest.WholeFileIgnoredObj +compute +235 +246 +15 +compute +DefDef +false +0 +true +def compute + 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/i25482.scala b/tests/coverage/pos/i25482.scala new file mode 100644 index 000000000000..b7a3e669ef78 --- /dev/null +++ b/tests/coverage/pos/i25482.scala @@ -0,0 +1,12 @@ +//> using options -Yexplicit-nulls + +import scala.language.unsafeNulls + +object Lookup: + + abstract class AbstractFile2: + def lookupName(name: String, directory: Boolean): AbstractFile2 | Null + + private final def lookupPath2(base: AbstractFile2, pathParts: Seq[String]): AbstractFile2 | Null = + var file: AbstractFile2 | Null = base + file.lookupName(pathParts.last, true) diff --git a/tests/coverage/pos/i25482.scoverage.check b/tests/coverage/pos/i25482.scoverage.check new file mode 100644 index 000000000000..135110cdeab1 --- /dev/null +++ b/tests/coverage/pos/i25482.scoverage.check @@ -0,0 +1,88 @@ +# 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 +i25482.scala + +Lookup +Object +.Lookup +lookupPath2 +342 +379 +12 +lookupName +Apply +false +0 +false +file.lookupName(pathParts.last, true) + +1 +i25482.scala + +Lookup +Object +.Lookup +lookupPath2 +358 +372 +12 +last +Select +false +0 +false +pathParts.last + +2 +i25482.scala + +Lookup +Object +.Lookup +lookupPath2 +374 +378 +12 + +Literal +false +0 +false +true + +3 +i25482.scala + +Lookup +Object +.Lookup +lookupPath2 +197 +226 +10 +lookupPath2 +DefDef +false +0 +false +private final def lookupPath2 + 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 9354a4e67b29..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,24 +69,7 @@ false false Seq(brokenSynchronizedBlock(false), brokenSynchronizedBlock(true)) -4 -i16940/i16940.scala - -Test -Object -.Test - -387 -390 -20 -Seq -Ident -false -0 -false -Seq - -5 +3 i16940/i16940.scala Test @@ -120,7 +86,7 @@ false false brokenSynchronizedBlock(false) -6 +4 i16940/i16940.scala Test @@ -137,7 +103,7 @@ false false brokenSynchronizedBlock(true) -7 +5 i16940/i16940.scala Test @@ -154,75 +120,58 @@ false false println(test) -8 +6 i16940/i16940.scala Test Object .Test -$anonfun -508 -525 -23 -assertFailed + +539 +540 +25 +DurationInt Apply false 0 false -assert(test == 2) +3 -9 +7 i16940/i16940.scala Test Object .Test -$anonfun -508 -525 -23 -assertFailed -Apply -true -0 + +539 +548 +25 +seconds +Select false -assert(test == 2) - -10 -i16940/i16940.scala - -Test -Object -.Test -$anonfun -508 -525 -23 - -Literal -true 0 false -assert(test == 2) +3.seconds -11 +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 -12 +9 i16940/i16940.scala i16940$package @@ -239,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} -13 +10 i16940/i16940.scala i16940$package @@ -256,7 +205,7 @@ false false Thread.sleep(500) -14 +11 i16940/i16940.scala i16940$package @@ -273,7 +222,7 @@ true false {\n Thread.sleep(500)\n } -15 +12 i16940/i16940.scala i16940$package @@ -290,7 +239,7 @@ true false -16 +13 i16940/i16940.scala i16940$package @@ -307,7 +256,7 @@ false false synchronized {\n val tmp = test\n Thread.sleep(1000)\n test = tmp + 1\n } -17 +14 i16940/i16940.scala i16940$package @@ -324,7 +273,24 @@ false false Thread.sleep(1000) -18 +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 108887701017..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,24 +69,7 @@ false false List(Array[String]()*) -5 -i18233-min/i18233-min.scala - -i18233-min$package -Object -.i18233-min$package -aList -14 -18 -2 -List -Ident -false -0 -false -List - -6 +3 i18233-min/i18233-min.scala i18233-min$package @@ -137,7 +86,7 @@ false false Array[String]() -7 +4 i18233-min/i18233-min.scala i18233-min$package @@ -154,7 +103,7 @@ false false def aList -8 +5 i18233-min/i18233-min.scala i18233-min$package @@ -171,41 +120,58 @@ false false Array("abc", "def") -9 +6 i18233-min/i18233-min.scala i18233-min$package Object .i18233-min$package arr -38 -45 -4 -arr -DefDef +56 +61 +5 + +Literal false 0 false -def arr +"abc" -10 +7 i18233-min/i18233-min.scala i18233-min$package Object .i18233-min$package -anotherList -91 -101 +arr +63 +68 +5 + +Literal +false +0 +false +"def" + 8 -apply -Apply +i18233-min/i18233-min.scala + +i18233-min$package +Object +.i18233-min$package +arr +38 +45 +4 +arr +DefDef false 0 false -List(arr*) +def arr -11 +9 i18233-min/i18233-min.scala i18233-min$package @@ -213,16 +179,16 @@ Object .i18233-min$package anotherList 91 -95 +101 8 -List -Ident +apply +Apply false 0 false -List +List(arr*) -12 +10 i18233-min/i18233-min.scala i18233-min$package @@ -239,7 +205,7 @@ false false arr -13 +11 i18233-min/i18233-min.scala i18233-min$package diff --git a/tests/coverage/run/i18233/test.scoverage.check b/tests/coverage/run/i18233/test.scoverage.check index 97e16be7839f..878ca8a14705 100644 --- a/tests/coverage/run/i18233/test.scoverage.check +++ b/tests/coverage/run/i18233/test.scoverage.check @@ -42,23 +42,6 @@ Foo Object .Foo render -54 -58 -5 -List -Ident -false -0 -false -List - -2 -i18233/i18233.scala - -Foo -Object -.Foo -render 59 65 5 @@ -69,7 +52,7 @@ false false values -3 +2 i18233/i18233.scala Foo @@ -86,7 +69,7 @@ false false values.tail -4 +3 i18233/i18233.scala Foo @@ -103,7 +86,7 @@ false false List(values.tail*).mkString -5 +4 i18233/i18233.scala Foo @@ -120,7 +103,7 @@ false false def render -6 +5 i18233/i18233.scala Test @@ -137,7 +120,7 @@ false false println(Foo.render) -7 +6 i18233/i18233.scala Test diff --git a/tests/coverage/run/inheritance/test.scoverage.check b/tests/coverage/run/inheritance/test.scoverage.check index 387a080463e2..877270a0bd2e 100644 --- a/tests/coverage/run/inheritance/test.scoverage.check +++ b/tests/coverage/run/inheritance/test.scoverage.check @@ -38,6 +38,23 @@ println("block") 1 inheritance/test.scala +C1 +Class +.C1 + +102 +103 +3 + +Literal +false +0 +false +1 + +2 +inheritance/test.scala + C2 Class .C2 @@ -52,7 +69,7 @@ false false A(2,2) -2 +3 inheritance/test.scala test$package @@ -69,7 +86,7 @@ false false println(C1().x) -3 +4 inheritance/test.scala test$package @@ -86,7 +103,7 @@ false false C1() -4 +5 inheritance/test.scala test$package @@ -103,7 +120,7 @@ false false println(C2().x) -5 +6 inheritance/test.scala test$package @@ -120,7 +137,7 @@ false false C2() -6 +7 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 92877b704324..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 -195 -199 +200 +203 10 -List -Ident + +Literal false 0 false -List +"d" 7 interpolation/test.scala @@ -144,15 +144,15 @@ Test Object .Test main -229 -278 -11 -map -Apply +205 +208 +10 + +Literal false 0 false -xs.zipWithIndex.map((s, i) => println(s"$i: $s")) +"o" 8 interpolation/test.scala @@ -161,17 +161,68 @@ 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")) -9 +12 interpolation/test.scala Test @@ -188,7 +239,7 @@ false false println(s"$i: $s") -10 +13 interpolation/test.scala Test @@ -205,7 +256,7 @@ false false s"$i: $s" -11 +14 interpolation/test.scala Test @@ -222,7 +273,7 @@ false false println(simple(1, "abc")) -12 +15 interpolation/test.scala Test @@ -239,7 +290,7 @@ false false simple(1, "abc") -13 +16 interpolation/test.scala Test @@ -256,7 +307,7 @@ false false println(hexa(127)) -14 +17 interpolation/test.scala Test @@ -273,7 +324,7 @@ false false hexa(127) -15 +18 interpolation/test.scala Test @@ -290,7 +341,7 @@ false false println(raw"a\\nb") -16 +19 interpolation/test.scala Test @@ -307,7 +358,7 @@ false false raw"a\\nb" -17 +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 206fc7dfadda..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 -22 -26 +27 +28 2 -List -Ident + +Literal false 0 false -List +1 2 lifting/test.scala @@ -76,15 +76,15 @@ Vals Class .Vals -46 -57 +51 +52 3 -apply -Apply + +Literal false 0 false -List(1,2,3) +1 4 lifting/test.scala @@ -93,32 +93,32 @@ Vals Class .Vals -46 -50 +53 +54 3 -List -Ident + +Literal false 0 false -List +2 5 lifting/test.scala -A +Vals Class -.A -msg -104 -136 -6 -+ -Apply +.Vals + +55 +56 +3 + +Literal false 0 false -"string" + a + "." + b + "." + c +3 6 lifting/test.scala @@ -128,14 +128,14 @@ Class .A msg 104 -132 +136 6 + Apply false 0 false -"string" + a + "." + b + "." +"string" + a + "." + b + "." + c 7 lifting/test.scala @@ -145,14 +145,14 @@ Class .A msg 104 -126 +112 6 -+ -Apply + +Literal false 0 false -"string" + a + "." + b +"string" 8 lifting/test.scala @@ -161,15 +161,15 @@ A Class .A msg -104 -122 +70 +77 6 -+ -Apply +msg +DefDef false 0 false -"string" + a + "." +def msg 9 lifting/test.scala @@ -177,35 +177,18 @@ lifting/test.scala A Class .A -msg -104 -116 -6 -+ -Apply +integer +158 +159 +7 + +Literal false 0 false -"string" + a - -10 -lifting/test.scala - -A -Class -.A -msg -70 -77 -6 -msg -DefDef -false 0 -false -def msg -11 +10 lifting/test.scala A @@ -222,7 +205,7 @@ false false def integer -12 +11 lifting/test.scala A @@ -239,7 +222,7 @@ false false def ex -13 +12 lifting/test.scala test$package @@ -256,7 +239,41 @@ false false A() +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 @@ -273,7 +290,7 @@ false false def f -15 +16 lifting/test.scala test$package @@ -290,7 +307,24 @@ false false a.msg(i, 0, a.integer) -16 +17 +lifting/test.scala + +test$package +Object +.test$package +Test +273 +274 +15 + +Literal +false +0 +false +0 + +18 lifting/test.scala test$package @@ -307,7 +341,7 @@ false false a.integer -17 +19 lifting/test.scala test$package @@ -324,7 +358,7 @@ false false println(x) -18 +20 lifting/test.scala test$package @@ -341,24 +375,24 @@ false false a.ex.msg(i, 0, a.ex.integer) -19 +21 lifting/test.scala test$package Object .test$package Test -306 -310 +318 +319 17 -ex -Select + +Literal false 0 false -a.ex +0 -20 +22 lifting/test.scala test$package @@ -375,7 +409,7 @@ false false a.ex -21 +23 lifting/test.scala test$package @@ -392,7 +426,7 @@ false false a.ex.integer -22 +24 lifting/test.scala test$package @@ -409,7 +443,7 @@ false false println(x) -23 +25 lifting/test.scala test$package @@ -426,7 +460,7 @@ false false a.msg(f(), 0, i) -24 +26 lifting/test.scala test$package @@ -443,7 +477,24 @@ false false f() -25 +27 +lifting/test.scala + +test$package +Object +.test$package +Test +365 +366 +19 + +Literal +false +0 +false +0 + +28 lifting/test.scala test$package @@ -460,7 +511,7 @@ false false println(x) -26 +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..704062b2dbc8 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,7 @@ false false def x -1 +2 trait/test.scala Impl3 @@ -52,7 +69,7 @@ false false Impl2() -2 +3 trait/test.scala test$package @@ -69,7 +86,7 @@ false false println(Impl1().x) -3 +4 trait/test.scala test$package @@ -86,7 +103,7 @@ false false Impl1() -4 +5 trait/test.scala test$package @@ -103,7 +120,7 @@ false false Impl1().x -5 +6 trait/test.scala test$package @@ -120,7 +137,7 @@ false false println(Impl2().p) -6 +7 trait/test.scala test$package @@ -137,7 +154,7 @@ false false Impl2() -7 +8 trait/test.scala test$package @@ -154,7 +171,7 @@ false false println(Impl3().p) -8 +9 trait/test.scala test$package @@ -171,7 +188,7 @@ false false Impl3() -9 +10 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 f0382d7405be..e199fee6b817 100644 --- a/tests/coverage/run/type-apply/test.measurement.check +++ b/tests/coverage/run/type-apply/test.measurement.check @@ -1,7 +1,7 @@ 6 1 -3 2 -5 +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 3ad1944563a3..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 @@ -76,15 +76,15 @@ test$package Object .test$package Test -171 -175 +178 +179 5 -List -Ident + +Literal false 0 false -List +2 4 type-apply/test.scala @@ -92,16 +92,16 @@ type-apply/test.scala test$package Object .test$package -$anonfun -192 -199 +Test +180 +181 5 -apply -Apply + +Literal false 0 false -List(a) +3 5 type-apply/test.scala @@ -111,14 +111,14 @@ Object .test$package $anonfun 192 -196 +199 5 -List -Ident +apply +Apply false 0 false -List +List(a) 6 type-apply/test.scala 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/disabled/partest/run/delambdafy_t6028.scala b/tests/disabled/partest/run/delambdafy_t6028.scala index 0b7ef48c3df8..981b6b759ebc 100644 --- a/tests/disabled/partest/run/delambdafy_t6028.scala +++ b/tests/disabled/partest/run/delambdafy_t6028.scala @@ -3,7 +3,7 @@ import java.io.{Console => _, _} object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Ydelambdafy:method -Xprint:lambdalift -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Ydelambdafy:method -Vprint:lambdalift -d " + testOutput.path override def code = """class T(classParam: Int) { | val field: Int = 0 diff --git a/tests/disabled/partest/run/delambdafy_t6555.scala b/tests/disabled/partest/run/delambdafy_t6555.scala index a1dcfe790c3b..b40e1c0e4faf 100644 --- a/tests/disabled/partest/run/delambdafy_t6555.scala +++ b/tests/disabled/partest/run/delambdafy_t6555.scala @@ -3,7 +3,7 @@ import java.io.{Console => _, _} object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Xprint:specialize -Ydelambdafy:method -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Vprint:specialize -Ydelambdafy:method -d " + testOutput.path override def code = "class Foo { val f = (param: Int) => param } " diff --git a/tests/disabled/partest/run/delambdafy_uncurry_byname_inline.scala b/tests/disabled/partest/run/delambdafy_uncurry_byname_inline.scala index 8f480fa80488..f8729e3c6710 100644 --- a/tests/disabled/partest/run/delambdafy_uncurry_byname_inline.scala +++ b/tests/disabled/partest/run/delambdafy_uncurry_byname_inline.scala @@ -3,7 +3,7 @@ import java.io.{Console => _, _} object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Xprint:uncurry -Ydelambdafy:inline -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Vprint:uncurry -Ydelambdafy:inline -d " + testOutput.path override def code = """class Foo { | def bar(x: => Int) = x diff --git a/tests/disabled/partest/run/delambdafy_uncurry_byname_method.scala b/tests/disabled/partest/run/delambdafy_uncurry_byname_method.scala index 1adeec843390..0c21c232ae2a 100644 --- a/tests/disabled/partest/run/delambdafy_uncurry_byname_method.scala +++ b/tests/disabled/partest/run/delambdafy_uncurry_byname_method.scala @@ -3,7 +3,7 @@ import java.io.{Console => _, _} object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Xprint:uncurry -Ydelambdafy:method -Ystop-after:uncurry -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Vprint:uncurry -Ydelambdafy:method -Ystop-after:uncurry -d " + testOutput.path override def code = """class Foo { | def bar(x: => Int) = x diff --git a/tests/disabled/partest/run/delambdafy_uncurry_inline.scala b/tests/disabled/partest/run/delambdafy_uncurry_inline.scala index b42b65f5bbda..b397189271d9 100644 --- a/tests/disabled/partest/run/delambdafy_uncurry_inline.scala +++ b/tests/disabled/partest/run/delambdafy_uncurry_inline.scala @@ -3,7 +3,7 @@ import java.io.{Console => _, _} object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Xprint:uncurry -Ydelambdafy:inline -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Vprint:uncurry -Ydelambdafy:inline -d " + testOutput.path override def code = """class Foo { | def bar = { diff --git a/tests/disabled/partest/run/delambdafy_uncurry_method.scala b/tests/disabled/partest/run/delambdafy_uncurry_method.scala index a988fb2ee7bf..db2ca7061985 100644 --- a/tests/disabled/partest/run/delambdafy_uncurry_method.scala +++ b/tests/disabled/partest/run/delambdafy_uncurry_method.scala @@ -3,7 +3,7 @@ import java.io.{Console => _, _} object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Xprint:uncurry -Ydelambdafy:method -Ystop-after:uncurry -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Vprint:uncurry -Ydelambdafy:method -Ystop-after:uncurry -d " + testOutput.path override def code = """class Foo { | def bar = { diff --git a/tests/disabled/partest/run/dynamic-applyDynamic.scala b/tests/disabled/partest/run/dynamic-applyDynamic.scala index b06041194c1e..65457a8b7b42 100644 --- a/tests/disabled/partest/run/dynamic-applyDynamic.scala +++ b/tests/disabled/partest/run/dynamic-applyDynamic.scala @@ -3,7 +3,7 @@ import scala.tools.partest.DirectTest object Test extends DirectTest { override def extraSettings: String = - s"-usejavacp -Xprint-pos -Xprint:typer -Yrangepos -Ystop-after:typer -d ${testOutput.path}" + s"-usejavacp -Xprint-pos -Vprint:typer -Yrangepos -Ystop-after:typer -d ${testOutput.path}" override def code = """ object X { @@ -23,4 +23,4 @@ object Test extends DirectTest { import language.dynamics class D extends Dynamic { def applyDynamic(name: String)(value: Any) = ??? -} \ No newline at end of file +} diff --git a/tests/disabled/partest/run/dynamic-applyDynamicNamed.scala b/tests/disabled/partest/run/dynamic-applyDynamicNamed.scala index cc59f9058be9..929485982d82 100644 --- a/tests/disabled/partest/run/dynamic-applyDynamicNamed.scala +++ b/tests/disabled/partest/run/dynamic-applyDynamicNamed.scala @@ -3,7 +3,7 @@ import scala.tools.partest.DirectTest object Test extends DirectTest { override def extraSettings: String = - s"-usejavacp -Xprint-pos -Xprint:typer -Yrangepos -Ystop-after:typer -d ${testOutput.path}" + s"-usejavacp -Xprint-pos -Vprint:typer -Yrangepos -Ystop-after:typer -d ${testOutput.path}" override def code = """ object X { diff --git a/tests/disabled/partest/run/dynamic-selectDynamic.scala b/tests/disabled/partest/run/dynamic-selectDynamic.scala index bd6c138c5002..2b92bcbb7cdd 100644 --- a/tests/disabled/partest/run/dynamic-selectDynamic.scala +++ b/tests/disabled/partest/run/dynamic-selectDynamic.scala @@ -3,7 +3,7 @@ import scala.tools.partest.DirectTest object Test extends DirectTest { override def extraSettings: String = - s"-usejavacp -Xprint-pos -Xprint:typer -Yrangepos -Ystop-after:typer -d ${testOutput.path}" + s"-usejavacp -Xprint-pos -Vprint:typer -Yrangepos -Ystop-after:typer -d ${testOutput.path}" override def code = """ object X { diff --git a/tests/disabled/partest/run/dynamic-updateDynamic.scala b/tests/disabled/partest/run/dynamic-updateDynamic.scala index 80fe0ea35f4c..5d1518550f39 100644 --- a/tests/disabled/partest/run/dynamic-updateDynamic.scala +++ b/tests/disabled/partest/run/dynamic-updateDynamic.scala @@ -3,7 +3,7 @@ import scala.tools.partest.DirectTest object Test extends DirectTest { override def extraSettings: String = - s"-usejavacp -Xprint-pos -Xprint:typer -Yrangepos -Ystop-after:typer -d ${testOutput.path}" + s"-usejavacp -Xprint-pos -Vprint:typer -Yrangepos -Ystop-after:typer -d ${testOutput.path}" override def code = """ object X { diff --git a/tests/disabled/partest/run/existential-rangepos.scala b/tests/disabled/partest/run/existential-rangepos.scala index 7d2b0810d342..50335ac62cea 100644 --- a/tests/disabled/partest/run/existential-rangepos.scala +++ b/tests/disabled/partest/run/existential-rangepos.scala @@ -1,7 +1,7 @@ import scala.tools.partest._ object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Yrangepos -Xprint:patmat -Xprint-pos -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Yrangepos -Vprint:patmat -Xprint-pos -d " + testOutput.path override def code = """ abstract class A[T] { diff --git a/tests/disabled/partest/run/t4287inferredMethodTypes.scala b/tests/disabled/partest/run/t4287inferredMethodTypes.scala index f14e672da88a..cc9c057899ad 100644 --- a/tests/disabled/partest/run/t4287inferredMethodTypes.scala +++ b/tests/disabled/partest/run/t4287inferredMethodTypes.scala @@ -3,7 +3,7 @@ import scala.tools.partest.DirectTest object Test extends DirectTest { override def extraSettings: String = - s"-usejavacp -Yinfer-argument-types -Xprint-pos -Xprint:typer -Yrangepos -Ystop-after:typer -d ${testOutput.path}" + s"-usejavacp -Yinfer-argument-types -Xprint-pos -Vprint:typer -Yrangepos -Ystop-after:typer -d ${testOutput.path}" override def code = """ class A(a: Int = A.a) @@ -22,4 +22,4 @@ class B extends A { compile() } } -} \ No newline at end of file +} diff --git a/tests/disabled/partest/run/t5603.scala b/tests/disabled/partest/run/t5603.scala index 77c2775cc353..c047fe7896b5 100644 --- a/tests/disabled/partest/run/t5603.scala +++ b/tests/disabled/partest/run/t5603.scala @@ -7,7 +7,7 @@ import scala.tools.nsc.reporters.ConsoleReporter object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Xprint:parser -Ystop-after:parser -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Vprint:parser -Ystop-after:parser -d " + testOutput.path override def code = """ trait Greeting { diff --git a/tests/disabled/partest/run/t5699.scala b/tests/disabled/partest/run/t5699.scala index ec3b1d26b490..bdcdfed93583 100755 --- a/tests/disabled/partest/run/t5699.scala +++ b/tests/disabled/partest/run/t5699.scala @@ -7,7 +7,7 @@ object Test extends DirectTest { |public @interface MyAnnotation { String value(); } """.stripMargin - override def extraSettings: String = "-usejavacp -Ystop-after:typer -Xprint:parser" + override def extraSettings: String = "-usejavacp -Ystop-after:typer -Vprint:parser" override def show(): Unit = { // redirect err to out, for logging diff --git a/tests/disabled/partest/run/t6028.scala b/tests/disabled/partest/run/t6028.scala index a6f920c5bb67..6e4e179f1dfd 100644 --- a/tests/disabled/partest/run/t6028.scala +++ b/tests/disabled/partest/run/t6028.scala @@ -3,7 +3,7 @@ import java.io.{Console => _, _} object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Ydelambdafy:inline -Xprint:lambdalift -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Ydelambdafy:inline -Vprint:lambdalift -d " + testOutput.path override def code = """class T(classParam: Int) { | val field: Int = 0 diff --git a/tests/disabled/partest/run/t6288.scala b/tests/disabled/partest/run/t6288.scala index cf5865e95a0e..242555ac75a5 100644 --- a/tests/disabled/partest/run/t6288.scala +++ b/tests/disabled/partest/run/t6288.scala @@ -3,7 +3,7 @@ import java.io.{Console => _, _} object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Xprint:patmat -Xprint-pos -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Vprint:patmat -Xprint-pos -d " + testOutput.path override def code = """ diff --git a/tests/disabled/partest/run/t6555.scala b/tests/disabled/partest/run/t6555.scala index cc0e4d1bfadb..e1db058da1d7 100644 --- a/tests/disabled/partest/run/t6555.scala +++ b/tests/disabled/partest/run/t6555.scala @@ -3,7 +3,7 @@ import java.io.{Console => _, _} object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Xprint:specialize -Ydelambdafy:inline -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Vprint:specialize -Ydelambdafy:inline -d " + testOutput.path override def code = "class Foo { val f = (param: Int) => param } " diff --git a/tests/disabled/partest/run/t7271.scala b/tests/disabled/partest/run/t7271.scala index 69d5ea377ea4..6eadb7816c0e 100644 --- a/tests/disabled/partest/run/t7271.scala +++ b/tests/disabled/partest/run/t7271.scala @@ -7,7 +7,7 @@ import scala.reflect.internal.Positions object Test extends DirectTest { - override def extraSettings: String = "-usejavacp -Xprint:parser -Ystop-after:parser -d " + testOutput.path + override def extraSettings: String = "-usejavacp -Vprint:parser -Ystop-after:parser -d " + testOutput.path override def code = """ class C { diff --git a/tests/explicit-nulls/run/i25208/Macro_1.scala b/tests/explicit-nulls/run/i25208/Macro_1.scala new file mode 100644 index 000000000000..2c155efe13bd --- /dev/null +++ b/tests/explicit-nulls/run/i25208/Macro_1.scala @@ -0,0 +1,20 @@ +import scala.quoted.* + +object NonNullFold: + extension [A](inline arg: A | Null) + inline def foldNN[B](inline default: => B)(inline fn: A => B): B = + ${ foldNNImpl('arg, 'default, 'fn) } + + private def foldNNImpl[A: Type, B: Type]( + arg: Expr[A | Null], + default: Expr[B], + fn: Expr[A => B] + )(using Quotes): Expr[B] = + '{ + val t = $arg + + given CanEqual[A | Null, Null] = CanEqual.derived + + if t == null then $default + else ${ Expr.betaReduce('{ $fn(t) }) } + } diff --git a/tests/explicit-nulls/run/i25208/Test_2.scala b/tests/explicit-nulls/run/i25208/Test_2.scala new file mode 100644 index 000000000000..b26c2ae0e115 --- /dev/null +++ b/tests/explicit-nulls/run/i25208/Test_2.scala @@ -0,0 +1,7 @@ +import NonNullFold.* + +object Test: + def main(args: Array[String]): Unit = + val x: String | Null = "not null" + val y = x.foldNN("it's null")(x => x) + println(y) diff --git a/tests/explicit-nulls/run/xml.scala b/tests/explicit-nulls/run/xml.scala new file mode 100644 index 000000000000..ffcb24bdb412 --- /dev/null +++ b/tests/explicit-nulls/run/xml.scala @@ -0,0 +1,59 @@ + +object Test { + import scala.xml._ + + def main(args: Array[String]): Unit = { + val xml = world + assert(xml.toString == "helloworld") + val sq: NodeBuffer = + assert(sq.mkString == "helloworld") + + val subSq: Node =
+ assert(subSq.child.toString == "Vector(b, c)") // implementation detail + + val attrSeq: Elem = + assert(attrSeq.attributes.asInstanceOf[UnprefixedAttribute].value.toString == "Vector(txt, &entityref;, txt)") + + val g: Group = + assert(g.nodes.toString == "Vector(a, b, c)") + } +} +package scala.xml { + type MetaData = AnyRef + + trait NamespaceBinding + object TopScope extends NamespaceBinding + object Null + abstract class Node { + def label: String + def child: Seq[Node] + override def toString = label + child.mkString + } + class Elem(prefix: String, val label: String, val attributes: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, val child: Node*) extends Node + class NodeBuffer extends Seq[Node] { + val nodes = scala.collection.mutable.ArrayBuffer.empty[Node] + def &+(o: Any): NodeBuffer = + o match { + case n: Node => nodes.addOne(n) ; this + case t: Text => nodes.addOne(Atom(t)) ; this + } + // Members declared in scala.collection.IterableOnce + def iterator: Iterator[scala.xml.Node] = nodes.iterator + // Members declared in scala.collection.SeqOps + def apply(i: Int): scala.xml.Node = nodes(i) + def length: Int = nodes.length + } + case class Text(text: String) + case class Atom(t: Text) extends Node { + def label = t.text + def child = Nil + } + + case class UnprefixedAttribute(key: String, value: Seq[Node], next: MetaData) + case class EntityRef(entityName: String) extends Node { + def label = s"&$entityName;" + def child = Nil + } + + case class Group(nodes: Seq[Node]) +} diff --git a/tests/init/tasty-error/typedef/C.scala b/tests/init/tasty-error/typedef/C.scala new file mode 100644 index 000000000000..9dccdd5e5954 --- /dev/null +++ b/tests/init/tasty-error/typedef/C.scala @@ -0,0 +1 @@ +class C \ No newline at end of file diff --git a/tests/init/tasty-error/typedef/Main.scala b/tests/init/tasty-error/typedef/Main.scala new file mode 100644 index 000000000000..232a7c34c4e1 --- /dev/null +++ b/tests/init/tasty-error/typedef/Main.scala @@ -0,0 +1,2 @@ +class Main extends B{} // anypos-error +class ExtendsB extends B{} \ No newline at end of file diff --git a/tests/init/tasty-error/typedef/v0/A.scala b/tests/init/tasty-error/typedef/v0/A.scala new file mode 100644 index 000000000000..9c86f47e2c77 --- /dev/null +++ b/tests/init/tasty-error/typedef/v0/A.scala @@ -0,0 +1,3 @@ +class A(c1: C) { + def fail(a: Int, b: Int): Int = a +} diff --git a/tests/init/tasty-error/typedef/v1/A.scala b/tests/init/tasty-error/typedef/v1/A.scala new file mode 100644 index 000000000000..a79930e5dd30 --- /dev/null +++ b/tests/init/tasty-error/typedef/v1/A.scala @@ -0,0 +1,3 @@ +class A(c1: C, c2: C) { + def fail(a: Int, b: Int): Int = a +} diff --git a/tests/init/tasty-error/typedef/v1/B.scala b/tests/init/tasty-error/typedef/v1/B.scala new file mode 100644 index 000000000000..801efcb5fdf0 --- /dev/null +++ b/tests/init/tasty-error/typedef/v1/B.scala @@ -0,0 +1,4 @@ +class B extends C{ + new A(this, this).fail(0,0) + val x = 5 +} \ No newline at end of file diff --git a/tests/init/tasty-error/val-or-defdef/Main.scala b/tests/init/tasty-error/val-or-defdef/Main.scala new file mode 100644 index 000000000000..2b27dd2b0d1f --- /dev/null +++ b/tests/init/tasty-error/val-or-defdef/Main.scala @@ -0,0 +1 @@ +class Main extends B{} // anypos-error \ No newline at end of file diff --git a/tests/init/tasty-error/val-or-defdef/v0/A.scala b/tests/init/tasty-error/val-or-defdef/v0/A.scala new file mode 100644 index 000000000000..be8c7d214378 --- /dev/null +++ b/tests/init/tasty-error/val-or-defdef/v0/A.scala @@ -0,0 +1,3 @@ +class A { + def fail(a: Int, b: Int): Int = a +} \ No newline at end of file diff --git a/tests/init/tasty-error/val-or-defdef/v1/A.scala b/tests/init/tasty-error/val-or-defdef/v1/A.scala new file mode 100644 index 000000000000..fa2956d1de7e --- /dev/null +++ b/tests/init/tasty-error/val-or-defdef/v1/A.scala @@ -0,0 +1,3 @@ +class A { + def fail(a: Int): Int = a +} \ No newline at end of file diff --git a/tests/init/tasty-error/val-or-defdef/v1/B.scala b/tests/init/tasty-error/val-or-defdef/v1/B.scala new file mode 100644 index 000000000000..3059f487db64 --- /dev/null +++ b/tests/init/tasty-error/val-or-defdef/v1/B.scala @@ -0,0 +1,4 @@ +class B { + val a: A = new A + val x = a.fail(0) +} \ No newline at end of file diff --git a/tests/neg-custom-args/captures/capt1.check b/tests/neg-custom-args/captures/capt1.check index 85d3b2a7ddcb..cc85fc67db76 100644 --- a/tests/neg-custom-args/captures/capt1.check +++ b/tests/neg-custom-args/captures/capt1.check @@ -17,6 +17,7 @@ | ^ | Found: Int ->{x} Int | Required: Matchable + | 15 | f | | longer explanation available when compiling with `-explain` @@ -25,6 +26,7 @@ | ^ | Found: A^{x} | Required: A + | 22 | def m() = if x == null then y else y 23 | F(22) | @@ -34,6 +36,7 @@ | ^ | Found: A^{x} | Required: A + | 27 | def m() = if x == null then y else y | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/cc-this2.check b/tests/neg-custom-args/captures/cc-this2.check index d2f87a131791..860b2e3aee8d 100644 --- a/tests/neg-custom-args/captures/cc-this2.check +++ b/tests/neg-custom-args/captures/cc-this2.check @@ -1,6 +1,6 @@ - -- Error: tests/neg-custom-args/captures/cc-this2/D_2.scala:2:6 -------------------------------------------------------- 2 |class D extends C: // error |^ |reference (caps.cap : Any) is not included in allowed capture set {} of pure base class class C + | 3 | this: D^ => diff --git a/tests/neg-custom-args/captures/exception-definitions.check b/tests/neg-custom-args/captures/exception-definitions.check index deec9343e58b..068d11fca3ea 100644 --- a/tests/neg-custom-args/captures/exception-definitions.check +++ b/tests/neg-custom-args/captures/exception-definitions.check @@ -2,6 +2,7 @@ 2 |class Err extends Exception: // error |^ |reference (caps.cap : Any) is not included in allowed capture set {} of pure base class class Throwable + | 3 | self: Err^ => -- Error: tests/neg-custom-args/captures/exception-definitions.scala:10:6 ---------------------------------------------- 10 |class Err4(c: Any^) extends AnyVal // error diff --git a/tests/neg-custom-args/captures/i23234.scala b/tests/neg-custom-args/captures/i23234.scala new file mode 100644 index 000000000000..61d7123c8b78 --- /dev/null +++ b/tests/neg-custom-args/captures/i23234.scala @@ -0,0 +1,5 @@ +abstract class MyFun: + def apply(x: Int): Int + +object Test: + val myFun: MyFun = (x: Int) ?=> x + 10 // error \ No newline at end of file diff --git a/tests/neg-custom-args/captures/lazylists-exceptions.check b/tests/neg-custom-args/captures/lazylists-exceptions.check index f58ed265d3be..f6acc2e3c43f 100644 --- a/tests/neg-custom-args/captures/lazylists-exceptions.check +++ b/tests/neg-custom-args/captures/lazylists-exceptions.check @@ -4,8 +4,7 @@ | Result of `try` cannot have type LazyList[Int]^ since | that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. + | 37 | tabulate(10) { i => -38 | if i > 9 then throw Ex1() -39 | i * i -40 | } +38 |... 41 | catch case ex: Ex1 => LazyNil diff --git a/tests/neg-custom-args/captures/lazylists2.check b/tests/neg-custom-args/captures/lazylists2.check index 72efbc08f8e2..03d029d3e090 100644 --- a/tests/neg-custom-args/captures/lazylists2.check +++ b/tests/neg-custom-args/captures/lazylists2.check @@ -3,10 +3,9 @@ | ^ | Found: LazyList[B^?]^{f, xs} | Required: LazyList[B]^{f} + | 19 | this: (Mapped^{xs, f}) => -20 | def isEmpty = false -21 | def head: B = f(xs.head) -22 | def tail: LazyList[B]^{this} = xs.tail.map(f) +20 |... 23 | new Mapped | | longer explanation available when compiling with `-explain` @@ -15,10 +14,9 @@ | ^ | Found: LazyList[B^?]^{f, xs} | Required: LazyList[B]^{xs} + | 28 | this: Mapped^{xs, f} => -29 | def isEmpty = false -30 | def head: B = f(xs.head) -31 | def tail: LazyList[B]^{this} = xs.tail.map(f) +29 |... 32 | new Mapped | | longer explanation available when compiling with `-explain` @@ -35,10 +33,9 @@ | ^ | Found: LazyList[B^?]^{f, xs} | Required: LazyList[B]^{xs} + | 46 | this: (Mapped^{xs, f}) => -47 | def isEmpty = false -48 | def head: B = f(xs.head) -49 | def tail: LazyList[B]^{xs, f} = xs.tail.map(f) +47 |... 50 | new Mapped | | longer explanation available when compiling with `-explain` @@ -46,4 +43,5 @@ 60 | class Mapped2 extends Mapped: // error | ^ | references {f, xs} are not all included in allowed capture set {} of the self type of class Mapped2 + | 61 | this: Mapped => diff --git a/tests/neg-custom-args/captures/nested-objects-printing.check b/tests/neg-custom-args/captures/nested-objects-printing.check new file mode 100644 index 000000000000..ccc051f35879 --- /dev/null +++ b/tests/neg-custom-args/captures/nested-objects-printing.check @@ -0,0 +1,7 @@ +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/nested-objects-printing.scala:4:19 ----------------------- +4 | val x: Int = B.this // error, was "found: (B: A.B.type)", should be "found: (B: B.type)" + | ^^^^^^ + | Found: (B : B.type) + | Required: Int + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/nested-objects-printing.scala b/tests/neg-custom-args/captures/nested-objects-printing.scala new file mode 100644 index 000000000000..68a670fe26b0 --- /dev/null +++ b/tests/neg-custom-args/captures/nested-objects-printing.scala @@ -0,0 +1,4 @@ +object A: + def f = + object B: + val x: Int = B.this // error, was "found: (B: A.B.type)", should be "found: (B: B.type)" diff --git a/tests/neg-custom-args/captures/real-try.check b/tests/neg-custom-args/captures/real-try.check index c7e87991fea1..b58e82117fbe 100644 --- a/tests/neg-custom-args/captures/real-try.check +++ b/tests/neg-custom-args/captures/real-try.check @@ -10,9 +10,9 @@ | Result of `try` cannot have type () => Unit since | that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. + | 13 | () => foo(1) -14 | catch -15 | case _: Ex1 => ??? +14 |... 16 | case _: Ex2 => ??? -- Error: tests/neg-custom-args/captures/real-try.scala:18:2 ----------------------------------------------------------- 18 | try // error @@ -20,9 +20,9 @@ | Result of `try` cannot have type () => Cell[Unit]^? since | that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. + | 19 | () => Cell(foo(1)) -20 | catch -21 | case _: Ex1 => ??? +20 |... 22 | case _: Ex2 => ??? -- Error: tests/neg-custom-args/captures/real-try.scala:24:10 ---------------------------------------------------------- 24 | val b = try // error @@ -30,7 +30,7 @@ | Result of `try` cannot have type Cell[box () => Unit]^? since | the part box () => Unit of that type captures the root capability `cap`. | This is often caused by a locally generated exception capability leaking as part of its result. + | 25 | Cell(() => foo(1))//: Cell[box {ev} () => Unit] <: Cell[box {cap} () => Unit] -26 | catch -27 | case _: Ex1 => ??? +26 |... 28 | case _: Ex2 => ??? diff --git a/tests/neg-custom-args/captures/try.check b/tests/neg-custom-args/captures/try.check index 4af370bfba1a..f331266dac00 100644 --- a/tests/neg-custom-args/captures/try.check +++ b/tests/neg-custom-args/captures/try.check @@ -10,6 +10,7 @@ | ^ | Found: (x: CT[Exception]^) ->? () ->{x} Nothing | Required: (x$0: CanThrow[Exception]) => () -> Nothing + | 30 | (x: CanThrow[Exception]) => () => raise(new Exception)(using x) 31 | } { | @@ -24,6 +25,7 @@ | ^ | Found: () ->{x$0} Int | Required: () -> Int + | 53 | (ex: Exception) => () => 22 54 |} | diff --git a/tests/neg-custom-args/i20491/README.md b/tests/neg-custom-args/i20491/README.md new file mode 100644 index 000000000000..f7a4b59112cc --- /dev/null +++ b/tests/neg-custom-args/i20491/README.md @@ -0,0 +1,30 @@ +# Test case for https://github.com/scala/scala3/issues/20491 + +## Test setup + +The test simulates a missing class dependency: + +- `cp/testpkg/A.class` + `cp/testpkg/A.tasty` - A Scala trait that references `otherpkg.MissingDep` +- `cp/otherpkg/` - An empty directory (`MissingDep.class` is missing) + +## How to regenerate the test files + +**otherpkg/MissingDep.java** +```java +package otherpkg; +public class MissingDep {} +``` + +**A.scala** +```scala +package testpkg +trait A: + def getDep: otherpkg.MissingDep +``` + +```bash +scala-cli compile A.scala otherpkg/MissingDep.java -d out +``` + +Copy `A.class` and `A.tasty` to `cp/testpkg/`, and ensure `cp/otherpkg/` directory +exists but is empty (do not include `MissingDep.class`). diff --git a/tests/neg-custom-args/i20491/Test.check b/tests/neg-custom-args/i20491/Test.check new file mode 100644 index 000000000000..0bbea27f4fd0 --- /dev/null +++ b/tests/neg-custom-args/i20491/Test.check @@ -0,0 +1,3 @@ +-- Error: A.scala:4:6 -------------------------------------------------------------------------------------------------- +Cannot resolve reference to type otherpkg.type.MissingDep. +The classfile defining the type might be missing from the classpath. diff --git a/tests/neg-custom-args/i20491/Test.scala b/tests/neg-custom-args/i20491/Test.scala new file mode 100644 index 000000000000..27bc5bd89e6a --- /dev/null +++ b/tests/neg-custom-args/i20491/Test.scala @@ -0,0 +1,2 @@ +// anypos-error +val a: testpkg.A = ??? diff --git a/tests/neg-custom-args/i20491/cp/testpkg/A.class b/tests/neg-custom-args/i20491/cp/testpkg/A.class new file mode 100644 index 000000000000..2f5ab170784e Binary files /dev/null and b/tests/neg-custom-args/i20491/cp/testpkg/A.class differ diff --git a/tests/neg-custom-args/i20491/cp/testpkg/A.tasty b/tests/neg-custom-args/i20491/cp/testpkg/A.tasty new file mode 100644 index 000000000000..e824c5c866c7 Binary files /dev/null and b/tests/neg-custom-args/i20491/cp/testpkg/A.tasty differ diff --git a/tests/neg-deep-subtype/i25216.check b/tests/neg-deep-subtype/i25216.check new file mode 100644 index 000000000000..fe985d02b842 --- /dev/null +++ b/tests/neg-deep-subtype/i25216.check @@ -0,0 +1,12 @@ +-- [E007] Type Mismatch Error: tests/neg-deep-subtype/i25216.scala:9:14 ------------------------------------------------ +9 |def f(z: Z) = z.v // error + | ^ + | Found: (z : Z) + | Required: Selectable | Dynamic + | + | The following import might fix the problem: + | + | import scala.reflect.Selectable.reflectiveSelectable + | + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-deep-subtype/i25216.scala b/tests/neg-deep-subtype/i25216.scala new file mode 100644 index 000000000000..8d93d2ff6092 --- /dev/null +++ b/tests/neg-deep-subtype/i25216.scala @@ -0,0 +1,9 @@ +trait X[A] + +given [A <: X[B], B <: A]: (A => B) = ??? + +type Z = { + val v: Int +} + +def f(z: Z) = z.v // error diff --git a/tests/neg-macros/annot-crash.check b/tests/neg-macros/annot-crash.check index 16eb0f68bc44..9de0a5db0adf 100644 --- a/tests/neg-macros/annot-crash.check +++ b/tests/neg-macros/annot-crash.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/annot-crash/Test_2.scala:1:0 ---------------------------------------------------------------- 1 |@crash // error |^^^^^^ diff --git a/tests/neg-macros/annot-empty-result.check b/tests/neg-macros/annot-empty-result.check index 6d43c19664cb..a6b7e15450c8 100644 --- a/tests/neg-macros/annot-empty-result.check +++ b/tests/neg-macros/annot-empty-result.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/annot-empty-result/Test_2.scala:5:2 --------------------------------------------------------- 5 | @nilAnnot // error | ^^^^^^^^^ diff --git a/tests/neg-macros/annot-error-annot.check b/tests/neg-macros/annot-error-annot.check index f150b4561e2c..2b953b938967 100644 --- a/tests/neg-macros/annot-error-annot.check +++ b/tests/neg-macros/annot-error-annot.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/annot-error-annot/Test_2.scala:17:6 --------------------------------------------------------- 16 |@error 17 |class cGlobal // error @@ -34,12 +33,14 @@ 32 | given gMember2: Num[Int] with // error | ^ | MACRO ERROR + | 33 | def zero = 0 -- Error: tests/neg-macros/annot-error-annot/Test_2.scala:35:8 --------------------------------------------------------- 34 | @error 35 | given gMember3(using DummyImplicit): Num[Int] with // error | ^ | MACRO ERROR + | 36 | def zero = 0 -- Error: tests/neg-macros/annot-error-annot/Test_2.scala:39:8 --------------------------------------------------------- 38 | @error @@ -76,12 +77,14 @@ 54 | given gLocal2: Num[Int] with // error | ^ | MACRO ERROR + | 55 | def zero = 0 -- Error: tests/neg-macros/annot-error-annot/Test_2.scala:57:10 -------------------------------------------------------- 56 | @error 57 | given gLocal3(using DummyImplicit): Num[Int] with // error | ^ | MACRO ERROR + | 58 | def zero = 0 -- Error: tests/neg-macros/annot-error-annot/Test_2.scala:61:10 -------------------------------------------------------- 60 | @error @@ -118,10 +121,12 @@ 10 |given gGlobal2: Num[Int] with // error |^ |MACRO ERROR + | 11 | def zero = 0 -- Error: tests/neg-macros/annot-error-annot/Test_2.scala:13:6 --------------------------------------------------------- 12 |@error 13 |given gGlobal3(using DummyImplicit): Num[Int] with // error |^ |MACRO ERROR + | 14 | def zero = 0 diff --git a/tests/neg-macros/annot-ill-abort.check b/tests/neg-macros/annot-ill-abort.check index b969b3ad4313..25c3f710f3ca 100644 --- a/tests/neg-macros/annot-ill-abort.check +++ b/tests/neg-macros/annot-ill-abort.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/annot-ill-abort/Test_2.scala:1:0 ------------------------------------------------------------ 1 |@crash // error |^^^^^^ diff --git a/tests/neg-macros/annot-mod-class-add-top-method.check b/tests/neg-macros/annot-mod-class-add-top-method.check index 28fb93bb29db..73bd71926b91 100644 --- a/tests/neg-macros/annot-mod-class-add-top-method.check +++ b/tests/neg-macros/annot-mod-class-add-top-method.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/annot-mod-class-add-top-method/Test_2.scala:1:0 --------------------------------------------- 1 |@addTopLevelMethod // error |^^^^^^^^^^^^^^^^^^ diff --git a/tests/neg-macros/annot-mod-class-add-top-val.check b/tests/neg-macros/annot-mod-class-add-top-val.check index bc21173923f1..14537b1d9ab9 100644 --- a/tests/neg-macros/annot-mod-class-add-top-val.check +++ b/tests/neg-macros/annot-mod-class-add-top-val.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/annot-mod-class-add-top-val/Test_2.scala:1:0 ------------------------------------------------ 1 |@addTopLevelVal // error |^^^^^^^^^^^^^^^ diff --git a/tests/neg-macros/annot-on-type.check b/tests/neg-macros/annot-on-type.check index 3844c3eeebe9..3841aca5a8ee 100644 --- a/tests/neg-macros/annot-on-type.check +++ b/tests/neg-macros/annot-on-type.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/annot-on-type/Test_2.scala:6:7 -------------------------------------------------------------- 5 | @voidAnnot 6 | type C // error diff --git a/tests/neg-macros/annot-result-owner.check b/tests/neg-macros/annot-result-owner.check index e2209998579c..7adeeefe1868 100644 --- a/tests/neg-macros/annot-result-owner.check +++ b/tests/neg-macros/annot-result-owner.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/annot-result-owner/Test_2.scala:1:0 --------------------------------------------------------- 1 |@insertVal // error |^^^^^^^^^^ diff --git a/tests/neg-macros/beta-reduce-inline-result.check b/tests/neg-macros/beta-reduce-inline-result.check index 7b615feef308..ee773bbec7c7 100644 --- a/tests/neg-macros/beta-reduce-inline-result.check +++ b/tests/neg-macros/beta-reduce-inline-result.check @@ -1,4 +1,3 @@ - -- [E007] Type Mismatch Error: tests/neg-macros/beta-reduce-inline-result/Test_2.scala:11:41 --------------------------- 11 | val x2: 4 = Macros.betaReduce(dummy1)(3) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/neg-macros/delegate-match-1.check b/tests/neg-macros/delegate-match-1.check index 93b20beba183..89869725709f 100644 --- a/tests/neg-macros/delegate-match-1.check +++ b/tests/neg-macros/delegate-match-1.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/delegate-match-1/Test_2.scala:6:2 ----------------------------------------------------------- 6 | f // error | ^ diff --git a/tests/neg-macros/delegate-match-2.check b/tests/neg-macros/delegate-match-2.check index e26742452b5b..d8481d5e7e3c 100644 --- a/tests/neg-macros/delegate-match-2.check +++ b/tests/neg-macros/delegate-match-2.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/delegate-match-2/Test_2.scala:5:2 ----------------------------------------------------------- 5 | f // error | ^ diff --git a/tests/neg-macros/delegate-match-3.check b/tests/neg-macros/delegate-match-3.check index 2b6fda278fda..0d1d5d6cf3c4 100644 --- a/tests/neg-macros/delegate-match-3.check +++ b/tests/neg-macros/delegate-match-3.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/delegate-match-3/Test_2.scala:3:2 ----------------------------------------------------------- 3 | f // error | ^ diff --git a/tests/neg-macros/i11386.check b/tests/neg-macros/i11386.check index f74ea5b7ff20..c017b275855d 100644 --- a/tests/neg-macros/i11386.check +++ b/tests/neg-macros/i11386.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/i11386/Test_2.scala:6:10 -------------------------------------------------------------------- 6 | dummy(0) // error | ^ diff --git a/tests/neg-macros/i13991.check b/tests/neg-macros/i13991.check index ca9f924a6693..763b1db753ff 100644 --- a/tests/neg-macros/i13991.check +++ b/tests/neg-macros/i13991.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/i13991/Test_2.scala:6:5 --------------------------------------------------------------------- 6 | v2 // error | ^^ diff --git a/tests/neg-macros/i15159.check b/tests/neg-macros/i15159.check new file mode 100644 index 000000000000..39bf8d4821ac --- /dev/null +++ b/tests/neg-macros/i15159.check @@ -0,0 +1,16 @@ +-- Error: tests/neg-macros/i15159/Test_2.scala:5:16 -------------------------------------------------------------------- +5 | TestMacro.test[A] // error + | ^^^^^^^^^^^^^^^^^ + | Exception occurred while executing macro expansion. + | java.lang.AssertionError: class X is not a member of A + | at TestMacro$.testImpl$$anonfun$1(Macro_1.scala:8) + | at scala.collection.immutable.List.map(List.scala:236) + | at TestMacro$.testImpl(Macro_1.scala:7) + | + |--------------------------------------------------------------------------------------------------------------------- + |Inline stack trace + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from Macro_1.scala:3 +3 | inline def test[T]: Unit = ${ testImpl[T] } + | ^^^^^^^^^^^^^^^^ + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg-macros/i15159/Macro_1.scala b/tests/neg-macros/i15159/Macro_1.scala new file mode 100644 index 000000000000..617478f51965 --- /dev/null +++ b/tests/neg-macros/i15159/Macro_1.scala @@ -0,0 +1,10 @@ +import scala.quoted.* +object TestMacro: + inline def test[T]: Unit = ${ testImpl[T] } + def testImpl[T: Type](using Quotes): Expr[Unit] = + import quotes.reflect.* + val tpe = TypeRepr.of[T] + tpe.typeSymbol.children.map { childSymbol => + tpe.memberType(childSymbol) // not a member of tpe + } + '{ () } diff --git a/tests/neg-macros/i15159/Test_2.scala b/tests/neg-macros/i15159/Test_2.scala new file mode 100644 index 000000000000..0c290822550e --- /dev/null +++ b/tests/neg-macros/i15159/Test_2.scala @@ -0,0 +1,6 @@ +sealed trait A +case class X(i: Int) extends A + +object Test extends App { + TestMacro.test[A] // error +} diff --git a/tests/neg-macros/i16582.check b/tests/neg-macros/i16582.check index 546d0b7efaf3..462811fb8103 100644 --- a/tests/neg-macros/i16582.check +++ b/tests/neg-macros/i16582.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/i16582/Test_2.scala:6:27 -------------------------------------------------------------------- 6 | val o2 = ownerDoesNotWork(2) // error | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/neg-macros/i16835.check b/tests/neg-macros/i16835.check index fb02f3c7f13f..d77d53677600 100644 --- a/tests/neg-macros/i16835.check +++ b/tests/neg-macros/i16835.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/i16835/Test_2.scala:1:17 -------------------------------------------------------------------- 1 |def test: Unit = foo // error | ^^^ diff --git a/tests/neg-macros/i18113.check b/tests/neg-macros/i18113.check index 7a7a7069bd92..89942493b6dc 100644 --- a/tests/neg-macros/i18113.check +++ b/tests/neg-macros/i18113.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/i18113/Test_2.scala:7:8 --------------------------------------------------------------------- 7 | X.test(ref) // error | ^^^^^^^^^^^ diff --git a/tests/neg-macros/i18113b.check b/tests/neg-macros/i18113b.check index dd2091eba09f..d256c4060f12 100644 --- a/tests/neg-macros/i18113b.check +++ b/tests/neg-macros/i18113b.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/i18113b/Test_2.scala:7:8 -------------------------------------------------------------------- 7 | X.test(ref) // error | ^^^^^^^^^^^ diff --git a/tests/neg-macros/i21916.check b/tests/neg-macros/i21916.check new file mode 100644 index 000000000000..3b9a3e229945 --- /dev/null +++ b/tests/neg-macros/i21916.check @@ -0,0 +1,14 @@ +-- Error: tests/neg-macros/i21916/Test_2.scala:3:27 -------------------------------------------------------------------- +3 |@main def Test = Macro.test() // error + | ^^^^^^^^^^^^ + | Exception occurred while executing macro expansion. + | java.lang.AssertionError: Illegal empty Array type constructor. Please supply a type parameter. + | at Macro$.testImpl(Macro_1.scala:8) + | + |--------------------------------------------------------------------------------------------------------------------- + |Inline stack trace + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from Macro_1.scala:3 +3 | inline def test() = ${testImpl} + | ^^^^^^^^^^^ + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg-macros/i21916/Macro_1.scala b/tests/neg-macros/i21916/Macro_1.scala new file mode 100644 index 000000000000..78af2cde2963 --- /dev/null +++ b/tests/neg-macros/i21916/Macro_1.scala @@ -0,0 +1,9 @@ +import scala.quoted._ +object Macro: + inline def test() = ${testImpl} + def testImpl(using Quotes): Expr[Any] = { + import quotes.reflect._ + val tpe = TypeRepr.of[Array[Byte]] match + case AppliedType(tycons, _) => tycons + Literal(ClassOfConstant(tpe)).asExpr + } diff --git a/tests/neg-macros/i21916/Test_2.scala b/tests/neg-macros/i21916/Test_2.scala new file mode 100644 index 000000000000..103bced0c04e --- /dev/null +++ b/tests/neg-macros/i21916/Test_2.scala @@ -0,0 +1,3 @@ +// lack of type ascription is on purpose, +// as that was part of what would cause the crash before. +@main def Test = Macro.test() // error diff --git a/tests/neg-macros/i23008.check b/tests/neg-macros/i23008.check new file mode 100644 index 000000000000..3d342ef797d7 --- /dev/null +++ b/tests/neg-macros/i23008.check @@ -0,0 +1,13 @@ +-- Error: tests/neg-macros/i23008/Test_2.scala:1:24 -------------------------------------------------------------------- +1 |@main def Test = Macros.buildString // error + | ^^^^^^^^^^^^^^^^^^ + | Exception occurred while executing macro expansion. + | java.lang.IllegalArgumentException: requirement failed: value of StringConstant cannot be `null` + | + |--------------------------------------------------------------------------------------------------------------------- + |Inline stack trace + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from Macro_1.scala:4 +4 | inline def buildString = ${buildStringCode} + | ^^^^^^^^^^^^^^^^^^ + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg-macros/i23008/Macro_1.scala b/tests/neg-macros/i23008/Macro_1.scala new file mode 100644 index 000000000000..56d4015ee179 --- /dev/null +++ b/tests/neg-macros/i23008/Macro_1.scala @@ -0,0 +1,16 @@ +import scala.quoted.* + +object Macros { + inline def buildString = ${buildStringCode} + + def buildStringCode(using Quotes): Expr[String] = { + import quotes.reflect.* + val str: String = null + try + Expr(str) + catch + case error: java.lang.IllegalArgumentException => + error.setStackTrace(Array[StackTraceElement]()) + throw error + } +} \ No newline at end of file diff --git a/tests/neg-macros/i23008/Test_2.scala b/tests/neg-macros/i23008/Test_2.scala new file mode 100644 index 000000000000..5ebf2715cf14 --- /dev/null +++ b/tests/neg-macros/i23008/Test_2.scala @@ -0,0 +1 @@ +@main def Test = Macros.buildString // error diff --git a/tests/neg-macros/i25743.check b/tests/neg-macros/i25743.check new file mode 100644 index 000000000000..717a95bbf885 --- /dev/null +++ b/tests/neg-macros/i25743.check @@ -0,0 +1,38 @@ +-- Error: tests/neg-macros/i25743/Test_2.scala:2:16 -------------------------------------------------------------------- + 2 | Macro.foo[Int]() // error // error + | ^^^^^^^^^^^^^^^^ + |Malformed tree was found while expanding macro with -Xcheck-macros. + |The tree does not conform to the compiler's tree invariants. + | + |Macro was: + |scala.quoted.runtime.Expr.splice[scala.Unit](((evidence$2: scala.quoted.Quotes) ?=> Macro.fooImpl[scala.Int](scala.quoted.Type.of[scala.Int](evidence$2), evidence$2))) + | + |The macro returned: + |Macro_1$package.useTag[scala.Int](Tag.apply[scala.Int]) + | + |Error: + |Cannot create Tag for scala.Int + | + |stacktrace available when compiling with `-Ydebug` + |-------------------------------------------------------------------------------------------------------------------- + |Inline stack trace + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from Macro_1.scala:12 +12 | inline def foo[A](): Unit = ${ fooImpl[A] } + | ^^^^^^^^^^^^^^^ + -------------------------------------------------------------------------------------------------------------------- +-- Error: tests/neg-macros/i25743/Test_2.scala:2:16 -------------------------------------------------------------------- + 2 | Macro.foo[Int]() // error // error + | ^^^^^^^^^^^^^^^^ + | Cannot create Tag for scala.Int + |-------------------------------------------------------------------------------------------------------------------- + |Inline stack trace + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from Test_2.scala:2 +14 | '{ useTag(Tag.apply[A]) } + | ^^^^^^^^^^^^ + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from Test_2.scala:2 +14 | '{ useTag(Tag.apply[A]) } + | ^^^^^^^^^^^^^^^^^^^^ + -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg-macros/i25743/Macro_1.scala b/tests/neg-macros/i25743/Macro_1.scala new file mode 100644 index 000000000000..2b786c5087e8 --- /dev/null +++ b/tests/neg-macros/i25743/Macro_1.scala @@ -0,0 +1,14 @@ +import scala.quoted.* + +trait Tag[A] +object Tag: + inline def apply[A]: Tag[A] = ${ applyImpl[A] } + def applyImpl[A: Type](using Quotes): Expr[Tag[A]] = + quotes.reflect.report.errorAndAbort(s"Cannot create Tag for ${Type.show[A]}") + +def useTag[A](tag: Tag[A]): Unit = () + +object Macro: + inline def foo[A](): Unit = ${ fooImpl[A] } + def fooImpl[A: Type](using Quotes): Expr[Unit] = + '{ useTag(Tag.apply[A]) } diff --git a/tests/neg-macros/i25743/Test_2.scala b/tests/neg-macros/i25743/Test_2.scala new file mode 100644 index 000000000000..c0abed97350b --- /dev/null +++ b/tests/neg-macros/i25743/Test_2.scala @@ -0,0 +1,2 @@ +def test = + Macro.foo[Int]() // error // error diff --git a/tests/neg-macros/i6432.check b/tests/neg-macros/i6432.check index 2ea6f3ba0300..d9206a1ffab7 100644 --- a/tests/neg-macros/i6432.check +++ b/tests/neg-macros/i6432.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/i6432/Test_2.scala:4:6 ---------------------------------------------------------------------- 4 | foo"abc${"123"}xyz${"456"}fgh" // error // error // error | ^^^ diff --git a/tests/neg-macros/i6432b.check b/tests/neg-macros/i6432b.check index 395d089c92e9..8cd2820f4fef 100644 --- a/tests/neg-macros/i6432b.check +++ b/tests/neg-macros/i6432b.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/i6432b/Test_2.scala:4:8 --------------------------------------------------------------------- 4 | foo"""abc${"123"}xyz${"456"}fgh""" // error // error // error | ^^^ diff --git a/tests/neg-macros/i6976.check b/tests/neg-macros/i6976.check index 0c30aa856ed1..380c71ede8cc 100644 --- a/tests/neg-macros/i6976.check +++ b/tests/neg-macros/i6976.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/i6976/Test_2.scala:5:44 --------------------------------------------------------------------- 5 | def main(args: Array[String]): Unit = mcr { 2 } // error | ^^^^^^^^^ diff --git a/tests/neg-macros/i9014.check b/tests/neg-macros/i9014.check index c0d08a42ef2a..27688c904c11 100644 --- a/tests/neg-macros/i9014.check +++ b/tests/neg-macros/i9014.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/i9014/Test_2.scala:1:23 --------------------------------------------------------------------- 1 |val tests = summon[Bar] // error | ^ diff --git a/tests/neg-macros/i9014b.check b/tests/neg-macros/i9014b.check index 4098bccf462b..aca1cd062b76 100644 --- a/tests/neg-macros/i9014b.check +++ b/tests/neg-macros/i9014b.check @@ -1,4 +1,3 @@ - -- [E172] Type Error: tests/neg-macros/i9014b/Test_2.scala:1:23 -------------------------------------------------------- 1 |val tests = summon[Bar] // error | ^ diff --git a/tests/neg-macros/i9685.check b/tests/neg-macros/i9685.check index bb25a0808aca..80cdf9d4bcbf 100644 --- a/tests/neg-macros/i9685.check +++ b/tests/neg-macros/i9685.check @@ -1,4 +1,3 @@ - -- [E008] Not Found Error: tests/neg-macros/i9685/Test_2.scala:3:4 ----------------------------------------------------- 3 | 1.asdf // error | ^^^^^^ diff --git a/tests/neg-macros/ill-abort.check b/tests/neg-macros/ill-abort.check index c267c2e79ecf..849d74c6b7e0 100644 --- a/tests/neg-macros/ill-abort.check +++ b/tests/neg-macros/ill-abort.check @@ -1,4 +1,3 @@ - -- Error: tests/neg-macros/ill-abort/quoted_2.scala:1:15 --------------------------------------------------------------- 1 |def test = fail() // error | ^^^^^^ diff --git a/tests/neg-scalajs/js-enums.check b/tests/neg-scalajs/js-enums.check index e54b2a5af2da..8b8a71b1fec3 100644 --- a/tests/neg-scalajs/js-enums.check +++ b/tests/neg-scalajs/js-enums.check @@ -2,6 +2,7 @@ 4 |enum MyEnum extends js.Object: // error |^ |MyEnum extends scala.reflect.Enum which does not extend js.Any. + | 5 | case Foo -- Error: tests/neg-scalajs/js-enums.scala:9:5 ------------------------------------------------------------------------- 7 |@js.native @@ -9,11 +10,13 @@ 9 |enum MyEnumNative extends js.Object: // error |^ |MyEnumNative extends scala.reflect.Enum which does not extend js.Any. + | 10 | case Bar -- Error: tests/neg-scalajs/js-enums.scala:12:5 ------------------------------------------------------------------------ 12 |enum MyEnumAny extends js.Any: // error |^ |Non-native JS classes and objects cannot directly extend AnyRef. They must extend a JS class (native or not). + | 13 | case Foo -- Error: tests/neg-scalajs/js-enums.scala:17:5 ------------------------------------------------------------------------ 15 |@js.native @@ -21,4 +24,5 @@ 17 |enum MyEnumNativeAny extends js.Any: // error |^ |MyEnumNativeAny extends scala.reflect.Enum which does not extend js.Any. + | 18 | case Bar diff --git a/tests/neg/19680.check b/tests/neg/19680.check index 5bdaaad99c2a..f7547d6a4eee 100644 --- a/tests/neg/19680.check +++ b/tests/neg/19680.check @@ -7,7 +7,10 @@ | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | - | Tree: new Config() + | Tree: + | + | new Config() + | | I tried to show that | Config | conforms to diff --git a/tests/neg/19680b.check b/tests/neg/19680b.check index 06ff26ee3289..6c5c60b29dee 100644 --- a/tests/neg/19680b.check +++ b/tests/neg/19680b.check @@ -7,7 +7,10 @@ | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | - | Tree: "hello" + | Tree: + | + | "hello" + | | I tried to show that | ("hello" : String) | conforms to diff --git a/tests/neg/22145.check b/tests/neg/22145.check new file mode 100644 index 000000000000..4592c42e9e7f --- /dev/null +++ b/tests/neg/22145.check @@ -0,0 +1,4 @@ +-- [E008] Not Found Error: tests/neg/22145.scala:5:7 ------------------------------------------------------------------- +5 | base.foo() // error + | ^^^^^^^^ + | value foo is not a member of foo.Collection diff --git a/tests/neg/22145.scala b/tests/neg/22145.scala new file mode 100644 index 000000000000..59d58b167ab4 --- /dev/null +++ b/tests/neg/22145.scala @@ -0,0 +1,8 @@ +package foo + +trait Collection: + val base: Collection = ??? + base.foo() // error + + object O extends Collection: + def foo(): Int = ??? diff --git a/tests/neg/22145c.check b/tests/neg/22145c.check new file mode 100644 index 000000000000..ddfb6b9daf1d --- /dev/null +++ b/tests/neg/22145c.check @@ -0,0 +1,4 @@ +-- [E008] Not Found Error: tests/neg/22145c.scala:4:35 ----------------------------------------------------------------- +4 | def bar(base: Collection) = base.foo // error + | ^^^^^^^^ + | value foo is not a member of foo.Collection diff --git a/tests/neg/22145c.scala b/tests/neg/22145c.scala new file mode 100644 index 000000000000..7776e57e7906 --- /dev/null +++ b/tests/neg/22145c.scala @@ -0,0 +1,8 @@ +package foo + +trait Collection: + def bar(base: Collection) = base.foo // error + object a extends Collection: + def foo: Int = 0 + object b extends Collection: + def foo: Int = 1 diff --git a/tests/neg/22145d.check b/tests/neg/22145d.check new file mode 100644 index 000000000000..ac6469c10b82 --- /dev/null +++ b/tests/neg/22145d.check @@ -0,0 +1,9 @@ +-- [E008] Not Found Error: tests/neg/22145d.scala:10:4 ----------------------------------------------------------------- +10 | 2.f() // error + | ^^^ + | value f is not a member of Int, but could be made available as an extension method. + | + | The following import might fix the problem: + | + | import foo.O2.f + | diff --git a/tests/neg/22145d.scala b/tests/neg/22145d.scala new file mode 100644 index 000000000000..bfb68e088322 --- /dev/null +++ b/tests/neg/22145d.scala @@ -0,0 +1,10 @@ +package foo + +class C[T]: + extension (x: T) def f(): Int = 1 + +object O1 extends C[String] +object O2 extends C[Int] + +def main = + 2.f() // error diff --git a/tests/neg/22145e.check b/tests/neg/22145e.check new file mode 100644 index 000000000000..e1c34e59f239 --- /dev/null +++ b/tests/neg/22145e.check @@ -0,0 +1,9 @@ +-- [E008] Not Found Error: tests/neg/22145e.scala:11:4 ----------------------------------------------------------------- +11 | 2.f() // error + | ^^^ + | value f is not a member of Int, but could be made available as an extension method. + | + | The following import might fix the problem: + | + | import foo.O2.Ext.f + | diff --git a/tests/neg/22145e.scala b/tests/neg/22145e.scala new file mode 100644 index 000000000000..579fd65c685e --- /dev/null +++ b/tests/neg/22145e.scala @@ -0,0 +1,11 @@ +package foo + +class C[T]: + object Ext: + extension (x: T) def f(): Int = 1 + +object O1 extends C[String] +object O2 extends C[Int] + +def main = + 2.f() // error diff --git a/tests/neg/22145f.check b/tests/neg/22145f.check new file mode 100644 index 000000000000..b870eb21057a --- /dev/null +++ b/tests/neg/22145f.check @@ -0,0 +1,10 @@ +-- [E008] Not Found Error: tests/neg/22145f.scala:11:6 ----------------------------------------------------------------- +11 | 2.f() // error + | ^^^ + | value f is not a member of Int, but could be made available as an extension method. + | + | One of the following imports might fix the problem: + | + | import C.this.O1.O2.Ext.f + | import C.this.O2.Ext.f + | diff --git a/tests/neg/22145f.scala b/tests/neg/22145f.scala new file mode 100644 index 000000000000..97f1336d1b1b --- /dev/null +++ b/tests/neg/22145f.scala @@ -0,0 +1,11 @@ +package foo + +class C[T]: + object Ext: + extension (x: T) def f(): Int = 1 + + object O1 extends C[String] + object O2 extends C[Int] + + def g = + 2.f() // error diff --git a/tests/neg/22145g.check b/tests/neg/22145g.check new file mode 100644 index 000000000000..175949ac113f --- /dev/null +++ b/tests/neg/22145g.check @@ -0,0 +1,4 @@ +-- [E008] Not Found Error: tests/neg/22145g.scala:10:4 ----------------------------------------------------------------- +10 | 2.f() // error + | ^^^ + | value f is not a member of Int diff --git a/tests/neg/22145g.scala b/tests/neg/22145g.scala new file mode 100644 index 000000000000..8b888516c044 --- /dev/null +++ b/tests/neg/22145g.scala @@ -0,0 +1,10 @@ +package foo + +class C[T]: + extension (x: T) def f(): Int = 1 + +object O: + val c0: C[String] = new C[String] + val c1: C[Int] = new C[Int] + // Currently no import suggestions here + 2.f() // error diff --git a/tests/neg/24782.check b/tests/neg/24782.check new file mode 100644 index 000000000000..5c6e55f5599d --- /dev/null +++ b/tests/neg/24782.check @@ -0,0 +1,6 @@ +-- [E050] Type Error: tests/neg/24782.scala:4:28 ----------------------------------------------------------------------- +4 | def f(using pure: Pure) = S(s => pure(s)) // error (but no compiler crash / stack overflow) + | ^ + | expression does not take more parameters + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/24782.scala b/tests/neg/24782.scala new file mode 100644 index 000000000000..28f2df112f7b --- /dev/null +++ b/tests/neg/24782.scala @@ -0,0 +1,5 @@ +object S { + trait F[A] + type Pure = [a] => a => F[a] + def f(using pure: Pure) = S(s => pure(s)) // error (but no compiler crash / stack overflow) +} diff --git a/tests/neg/6570-1.check b/tests/neg/6570-1.check index bdbadd0f752a..ca3976d9a9ef 100644 --- a/tests/neg/6570-1.check +++ b/tests/neg/6570-1.check @@ -1,14 +1,14 @@ --- [E007] Type Mismatch Error: tests/neg/6570-1.scala:23:27 ------------------------------------------------------------ +-- [E007] Type Mismatch Error: tests/neg/6570-1.scala:23:14 ------------------------------------------------------------ 23 | def thing = new Trait1 {} // error - | ^ - | Found: Object with Trait1 {...} - | Required: N[Box[Int & String]] + | ^^^^^^^^^^^^^ + | Found: Object with Trait1 {...} + | Required: N[Box[Int & String]] | - | Note: a match type could not be fully reduced: + | Note: a match type could not be fully reduced: | - | trying to reduce N[Box[Int & String]] - | failed since selector Box[Int & String] - | is uninhabited (there are no values of that type). + | trying to reduce N[Box[Int & String]] + | failed since selector Box[Int & String] + | is uninhabited (there are no values of that type). | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg/6570-1.scala:36:54 ------------------------------------------------------------ diff --git a/tests/neg/block-ending-in-def.check b/tests/neg/block-ending-in-def.check new file mode 100644 index 000000000000..29c03ad37f46 --- /dev/null +++ b/tests/neg/block-ending-in-def.check @@ -0,0 +1,9 @@ +-- [E007] Type Mismatch Error: tests/neg/block-ending-in-def.scala:3:47 ------------------------------------------------ +3 | if i == 0 then xs else loop(i - 1, 1 :: xs) // error + | ^ + | Found: Unit + | Required: List[Int] + | + | Maybe the enclosing block is missing a final expression after the definition of `loop`? + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/block-ending-in-def.scala b/tests/neg/block-ending-in-def.scala new file mode 100644 index 000000000000..b16fcb78cb29 --- /dev/null +++ b/tests/neg/block-ending-in-def.scala @@ -0,0 +1,4 @@ +def make(): List[Int] = + def loop(i: Int, xs: List[Int]): List[Int] = + if i == 0 then xs else loop(i - 1, 1 :: xs) // error + diff --git a/tests/neg/doubleDefinition.check b/tests/neg/doubleDefinition.check index 953a0ba8c128..3c164fcf878b 100644 --- a/tests/neg/doubleDefinition.check +++ b/tests/neg/doubleDefinition.check @@ -1,130 +1,166 @@ -- [E120] Naming Error: tests/neg/doubleDefinition.scala:14:5 ---------------------------------------------------------- 14 | def foo(x: List[B]): Function1[B, B] = ??? // error: same jvm signature | ^ - | Double definition: + | Conflicting definitions: | def foo(x: List[A]): A => A in class Test2 at line 13 and | def foo(x: List[B]): B => B in class Test2 at line 14 - | have the same type after erasure. + | have the same type (x: List): Function1 after erasure. | | Consider adding a @targetName annotation to one of the conflicting definitions | for disambiguation. + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:21:5 ---------------------------------------------------------- 21 | def foo(x: List[A]): Function2[B, B, B] = ??? // error | ^ - | Double definition: + | Conflicting definitions: | def foo(x: List[A]): A => A in class Test3 at line 20 and | def foo(x: List[A]): (B, B) => B in class Test3 at line 21 | have matching parameter types. -- [E120] Naming Error: tests/neg/doubleDefinition.scala:26:5 ---------------------------------------------------------- 26 | def foo = 2 // error | ^ - | Double definition: + | Conflicting definitions: | val foo: Int in class Test4 at line 25 and | def foo: Int in class Test4 at line 26 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:31:5 ---------------------------------------------------------- 31 | val foo = 1 // error | ^ - | Double definition: + | Conflicting definitions: | def foo: Int in class Test4b at line 30 and | val foo: Int in class Test4b at line 31 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:36:5 ---------------------------------------------------------- 36 | var foo = 1 // error | ^ - | Double definition: + | Conflicting definitions: | def foo: Int in class Test4c at line 35 and | var foo: Int in class Test4c at line 36 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:41:5 ---------------------------------------------------------- 41 | def foo = 2 // error | ^ - | Double definition: + | Conflicting definitions: | var foo: Int in class Test4d at line 40 and | def foo: Int in class Test4d at line 41 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:55:5 ---------------------------------------------------------- 55 | def foo(x: List[B]): Function1[B, B] = ??? // error: same jvm signature | ^ - | Double definition: + | Conflicting definitions: | def foo(x: List[A]): A => A in trait Test6 at line 54 and | def foo(x: List[B]): B => B in trait Test6 at line 55 - | have the same type after erasure. + | have the same type (x: List): Function1 after erasure. | | Consider adding a @targetName annotation to one of the conflicting definitions | for disambiguation. + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:62:5 ---------------------------------------------------------- 62 | def foo(x: List[A]): Function2[B, B, B] = ??? // error | ^ - | Double definition: + | Conflicting definitions: | def foo(x: List[A]): A => A in trait Test7 at line 61 and | def foo(x: List[A]): (B, B) => B in trait Test7 at line 62 | have matching parameter types. -- [E120] Naming Error: tests/neg/doubleDefinition.scala:67:5 ---------------------------------------------------------- 67 | def foo = 2 // error | ^ - | Double definition: + | Conflicting definitions: | val foo: Int in class Test8 at line 66 and | def foo: Int in class Test8 at line 67 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:72:5 ---------------------------------------------------------- 72 | val foo = 1 // error | ^ - | Double definition: + | Conflicting definitions: | def foo: Int in class Test8b at line 71 and | val foo: Int in class Test8b at line 72 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:77:5 ---------------------------------------------------------- 77 | var foo = 1 // error | ^ - | Double definition: + | Conflicting definitions: | def foo: Int in class Test8c at line 76 and | var foo: Int in class Test8c at line 77 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:82:5 ---------------------------------------------------------- 82 | def foo = 2 // error | ^ - | Double definition: + | Conflicting definitions: | var foo: Int in class Test8d at line 81 and | def foo: Int in class Test8d at line 82 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:88:5 ---------------------------------------------------------- 88 | def foo: String // error | ^ - | Double definition: + | Conflicting definitions: | val foo: Int in class Test9 at line 87 and | def foo: String in class Test9 at line 88 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:92:5 ---------------------------------------------------------- 92 | def foo: Int // error | ^ - | Double definition: + | Conflicting definitions: | val foo: Int in class Test10 at line 91 and | def foo: Int in class Test10 at line 92 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:96:5 ---------------------------------------------------------- 96 | def foo: String // error | ^ - | Double definition: + | Conflicting definitions: | val foo: Int in class Test11 at line 95 and | def foo: String in class Test11 at line 96 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:100:5 --------------------------------------------------------- 100 | def foo: Int // error | ^ - | Double definition: + | Conflicting definitions: | val foo: Int in class Test12 at line 99 and | def foo: Int in class Test12 at line 100 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:104:5 --------------------------------------------------------- 104 | def foo: String // error | ^ - | Double definition: + | Conflicting definitions: | var foo: Int in class Test13 at line 103 and | def foo: String in class Test13 at line 104 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:108:5 --------------------------------------------------------- 108 | def foo: Int // error | ^ - | Double definition: + | Conflicting definitions: | var foo: Int in class Test14 at line 107 and | def foo: Int in class Test14 at line 108 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:112:5 --------------------------------------------------------- 112 | def foo: String // error | ^ - | Double definition: + | Conflicting definitions: | var foo: Int in class Test15 at line 111 and | def foo: String in class Test15 at line 112 + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/doubleDefinition.scala:116:5 --------------------------------------------------------- 116 | def foo: Int // error | ^ - | Double definition: + | Conflicting definitions: | var foo: Int in class Test16 at line 115 and | def foo: Int in class Test16 at line 116 + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/exports.check b/tests/neg/exports.check index 79951cebfc39..bb4d7ab59421 100644 --- a/tests/neg/exports.check +++ b/tests/neg/exports.check @@ -15,33 +15,39 @@ -- [E120] Naming Error: tests/neg/exports.scala:23:33 ------------------------------------------------------------------ 23 | export printUnit.{stat => _, _} // error: double definition | ^ - | Double definition: + | Conflicting definitions: | def status: List[String] in class Copier at line 28 and | final def status: List[String] in class Copier at line 23 - | have the same type after erasure. + | have the same type (): List after erasure. | | Consider adding a @targetName annotation to one of the conflicting definitions | for disambiguation. + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/exports.scala:24:20 ------------------------------------------------------------------ 24 | export scanUnit._ // error: double definition | ^ - | Double definition: + | Conflicting definitions: | final def status: List[String] in class Copier at line 23 and | final def status: List[String] in class Copier at line 24 - | have the same type after erasure. + | have the same type (): List after erasure. | | Consider adding a @targetName annotation to one of the conflicting definitions | for disambiguation. + | + | longer explanation available when compiling with `-explain` -- [E120] Naming Error: tests/neg/exports.scala:26:21 ------------------------------------------------------------------ 26 | export printUnit.status // error: double definition | ^ - | Double definition: + | Conflicting definitions: | final def status: List[String] in class Copier at line 24 and | final def status: List[String] in class Copier at line 26 - | have the same type after erasure. + | have the same type (): List after erasure. | | Consider adding a @targetName annotation to one of the conflicting definitions | for disambiguation. + | + | longer explanation available when compiling with `-explain` -- Error: tests/neg/exports.scala:35:24 -------------------------------------------------------------------------------- 35 | export this.{concat => ++} // error: no eligible member | ^^^^^^^^^^^^ @@ -55,9 +61,11 @@ -- [E120] Naming Error: tests/neg/exports.scala:46:15 ------------------------------------------------------------------ 46 | export bar._ // error: double definition | ^ - | Double definition: + | Conflicting definitions: | val bar: Bar in class Baz at line 45 and | final def bar: (Baz.this.bar.bar : => (Baz.this.bar.baz.bar : Bar)) in class Baz at line 46 + | + | longer explanation available when compiling with `-explain` -- [E083] Type Error: tests/neg/exports.scala:57:11 -------------------------------------------------------------------- 57 | export printer.* // error: not stable | ^^^^^^^ diff --git a/tests/neg/exports3.scala b/tests/neg/exports3.scala new file mode 100644 index 000000000000..eaea93d9f7ce --- /dev/null +++ b/tests/neg/exports3.scala @@ -0,0 +1,41 @@ +trait P: + def foo: Int + +class A extends P: + export this.foo // error + +trait Q extends P: + def bar: Int + +trait R extends P: + def baz: Int + val a1: A + val a2: A + +abstract class B extends R: + self => + export this.baz // error + export self.bar // error + export this.a1.foo + export self.a2.foo // error + export a2.foo // error + +abstract class D extends P: + val p: P + export p.foo + +abstract class E: + self: P => + export self.foo // error + +abstract class F: + self: P => + export this.foo // error + +class G(p: P): + self: P => + export p.foo + +class H(p: P): + self: P => + export this.p.foo \ No newline at end of file diff --git a/tests/neg/extensions-can-only-have-using.scala b/tests/neg/extensions-can-only-have-using.scala new file mode 100644 index 000000000000..fcf451fc7258 --- /dev/null +++ b/tests/neg/extensions-can-only-have-using.scala @@ -0,0 +1,3 @@ +extension(x: Any)() // error + def f = 42 +val x = Nil.f diff --git a/tests/neg/f-interpolator-neg.check b/tests/neg/f-interpolator-neg.check index ea8df052589e..fe1f41ab3871 100644 --- a/tests/neg/f-interpolator-neg.check +++ b/tests/neg/f-interpolator-neg.check @@ -14,187 +14,187 @@ 7 | new StringContext("", "").f() // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | too few arguments for interpolated string --- Error: tests/neg/f-interpolator-neg.scala:11:7 ---------------------------------------------------------------------- -11 | f"$s%b" // error - | ^ - | Found: (s : String), Required: Boolean, Null --- Error: tests/neg/f-interpolator-neg.scala:12:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Warning: tests/neg/f-interpolator-neg.scala:11:9 ----------------------------------------------- +11 | f"$s%b" // warn only + | ^ + | non-Boolean value formats as "true" for non-null references and boxed primitives, otherwise "false" +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:12:7 ------------------------------------------------- 12 | f"$s%c" // error | ^ | Found: (s : String), Required: Char, Byte, Short, Int --- Error: tests/neg/f-interpolator-neg.scala:13:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:13:7 ------------------------------------------------- 13 | f"$f%c" // error | ^ | Found: (f : Double), Required: Char, Byte, Short, Int --- Error: tests/neg/f-interpolator-neg.scala:14:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:14:7 ------------------------------------------------- 14 | f"$s%x" // error | ^ | Found: (s : String), Required: Int, Long, Byte, Short, BigInt --- Error: tests/neg/f-interpolator-neg.scala:15:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:15:7 ------------------------------------------------- 15 | f"$b%d" // error | ^ | Found: (b : Boolean), Required: Int, Long, Byte, Short, BigInt --- Error: tests/neg/f-interpolator-neg.scala:16:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:16:7 ------------------------------------------------- 16 | f"$s%d" // error | ^ | Found: (s : String), Required: Int, Long, Byte, Short, BigInt --- Error: tests/neg/f-interpolator-neg.scala:17:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:17:7 ------------------------------------------------- 17 | f"$f%o" // error | ^ | Found: (f : Double), Required: Int, Long, Byte, Short, BigInt --- Error: tests/neg/f-interpolator-neg.scala:18:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:18:7 ------------------------------------------------- 18 | f"$s%e" // error | ^ | Found: (s : String), Required: Double, Float, BigDecimal --- Error: tests/neg/f-interpolator-neg.scala:19:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:19:7 ------------------------------------------------- 19 | f"$b%f" // error | ^ | Found: (b : Boolean), Required: Double, Float, BigDecimal --- Error: tests/neg/f-interpolator-neg.scala:20:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:20:9 ------------------------------------------------- 20 | f"$s%i" // error | ^ | illegal conversion character 'i' --- Error: tests/neg/f-interpolator-neg.scala:24:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:24:9 ------------------------------------------------- 24 | f"$s%+ 0,(s" // error | ^^^^^ | Illegal flag '+' --- Error: tests/neg/f-interpolator-neg.scala:25:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:25:9 ------------------------------------------------- 25 | f"$c%#+ 0,(c" // error | ^^^^^^ | Only '-' allowed for c conversion --- Error: tests/neg/f-interpolator-neg.scala:26:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:26:9 ------------------------------------------------- 26 | f"$d%#d" // error | ^ | # not allowed for d conversion --- Error: tests/neg/f-interpolator-neg.scala:27:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:27:9 ------------------------------------------------- 27 | f"$d%,x" // error | ^ | ',' only allowed for d conversion of integral types --- Error: tests/neg/f-interpolator-neg.scala:28:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:28:9 ------------------------------------------------- 28 | f"$d%+ (x" // error | ^^^ | only use '+' for BigInt conversions to o, x, X --- Error: tests/neg/f-interpolator-neg.scala:29:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:29:9 ------------------------------------------------- 29 | f"$f%,(a" // error | ^^ | ',' not allowed for a, A --- Error: tests/neg/f-interpolator-neg.scala:30:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:30:9 ------------------------------------------------- 30 | f"$t%#+ 0,(tT" // error | ^^^^^^ | Only '-' allowed for date/time conversions --- Error: tests/neg/f-interpolator-neg.scala:31:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:31:7 ------------------------------------------------- 31 | f"%-#+ 0,(n" // error | ^^^^^^^ | flags not allowed --- Error: tests/neg/f-interpolator-neg.scala:32:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:32:7 ------------------------------------------------- 32 | f"%#+ 0,(%" // error | ^^^^^^ | Illegal flag '#' --- Error: tests/neg/f-interpolator-neg.scala:36:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:36:9 ------------------------------------------------- 36 | f"$c%.2c" // error | ^^ | precision not allowed --- Error: tests/neg/f-interpolator-neg.scala:37:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:37:9 ------------------------------------------------- 37 | f"$d%.2d" // error | ^^ | precision not allowed --- Error: tests/neg/f-interpolator-neg.scala:38:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:38:7 ------------------------------------------------- 38 | f"%.2%" // error | ^^ | precision not allowed --- Error: tests/neg/f-interpolator-neg.scala:39:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:39:7 ------------------------------------------------- 39 | f"%.2n" // error | ^^ | precision not allowed --- Error: tests/neg/f-interpolator-neg.scala:40:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:40:9 ------------------------------------------------- 40 | f"$f%.2a" // error | ^^ | precision not allowed --- Error: tests/neg/f-interpolator-neg.scala:41:9 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:41:9 ------------------------------------------------- 41 | f"$t%.2tT" // error | ^^ | precision not allowed --- Error: tests/neg/f-interpolator-neg.scala:45:7 ---------------------------------------------------------------------- +-- [E209] Interpolation Error: tests/neg/f-interpolator-neg.scala:45:7 ------------------------------------------------- 45 | f"% ??? // 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/i12448.scala b/tests/neg/i12448.scala index e495cfd19f1d..df9ee94ecbfe 100644 --- a/tests/neg/i12448.scala +++ b/tests/neg/i12448.scala @@ -1,5 +1,5 @@ object Main { def mkArray[T <: A]: T#AType // error // error - mkArray[Array] // was: "assertion failed: invalid prefix HKTypeLambda..." + mkArray[Array] val x = mkArray[Array] } diff --git a/tests/neg/i12605.check b/tests/neg/i12605.check new file mode 100644 index 000000000000..57bb8718cb9a --- /dev/null +++ b/tests/neg/i12605.check @@ -0,0 +1,22 @@ +-- [E040] Syntax Error: tests/neg/i12605.scala:3:32 -------------------------------------------------------------------- +3 | List((2, 3), (3, 4)).filter case (a, b) => b > a // error // error + | ^^^^ + | unindent expected, but 'case' found +-- Error: tests/neg/i12605.scala:4:58 ---------------------------------------------------------------------------------- +4 | def m(): List[(Int, Int)] = List((2, 3), (3, 4)).filter case (a, b) => b > a // error // error + | ^^^^ + | end of statement expected but 'case' found +-- [E007] Type Mismatch Error: tests/neg/i12605.scala:3:4 -------------------------------------------------------------- +3 | List((2, 3), (3, 4)).filter case (a, b) => b > a // error // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Found: (((Int, Int)) => Boolean) => List[(Int, Int)] + | Required: List[(Int, Int)] + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/i12605.scala:4:30 ------------------------------------------------------------- +4 | def m(): List[(Int, Int)] = List((2, 3), (3, 4)).filter case (a, b) => b > a // error // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Found: (((Int, Int)) => Boolean) => List[(Int, Int)] + | Required: List[(Int, Int)] + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i12605.scala b/tests/neg/i12605.scala index 72d38fac2f5c..07290eedc5e2 100644 --- a/tests/neg/i12605.scala +++ b/tests/neg/i12605.scala @@ -1,3 +1,4 @@ object Foo: def joe(): List[(Int, Int)] = List((2, 3), (3, 4)).filter case (a, b) => b > a // error // error + def m(): List[(Int, Int)] = List((2, 3), (3, 4)).filter case (a, b) => b > a // error // 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/i13044.check b/tests/neg/i13044.check index 69c61b06c353..71fef532ec43 100644 --- a/tests/neg/i13044.check +++ b/tests/neg/i13044.check @@ -1,63 +1,18 @@ --- Error: tests/neg/i13044.scala:46:40 --------------------------------------------------------------------------------- -46 | implicit def typeSchema: Schema[A] = Schema.gen // error // error +-- [E172] Type Error: tests/neg/i13044.scala:61:40 --------------------------------------------------------------------- +61 | implicit def typeSchema: Schema[A] = Schema.gen // error | ^^^^^^^^^^ - | given instance gen is declared as `inline`, but was not inlined + | No given instance of type Schema[B] was found. + | I found: | - | Try increasing `-Xmax-inlines` above 32 + | Schema.gen[B] + | + | But given instance gen in trait SchemaDerivation does not match type Schema[B]. |-------------------------------------------------------------------------------------------------------------------- |Inline stack trace |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]] - | ^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 -29 | lazy val fields = recurse[m.MirroredElemTypes] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 -33 | inline given gen[A]: Schema[A] = derived - | ^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 -17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]] - | ^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 -29 | lazy val fields = recurse[m.MirroredElemTypes] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 -33 | inline given gen[A]: Schema[A] = derived - | ^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 -17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]] - | ^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 -29 | lazy val fields = recurse[m.MirroredElemTypes] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 -33 | inline given gen[A]: Schema[A] = derived - | ^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 -17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]] - | ^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 -29 | lazy val fields = recurse[m.MirroredElemTypes] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 -33 | inline given gen[A]: Schema[A] = derived - | ^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 -17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]] - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^ |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:17 18 | builder :: recurse[ts] @@ -71,76 +26,3 @@ 33 | inline given gen[A]: Schema[A] = derived | ^^^^^^^ -------------------------------------------------------------------------------------------------------------------- --- Error: tests/neg/i13044.scala:46:40 --------------------------------------------------------------------------------- -46 | implicit def typeSchema: Schema[A] = Schema.gen // error // error - | ^^^^^^^^^^ - | method recurse is declared as `inline`, but was not inlined - | - | Try increasing `-Xmax-inlines` above 32 - |-------------------------------------------------------------------------------------------------------------------- - |Inline stack trace - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -18 | builder :: recurse[ts] - | ^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -29 | lazy val fields = recurse[m.MirroredElemTypes] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -33 | inline given gen[A]: Schema[A] = derived - | ^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]] - | ^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -29 | lazy val fields = recurse[m.MirroredElemTypes] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -33 | inline given gen[A]: Schema[A] = derived - | ^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]] - | ^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -29 | lazy val fields = recurse[m.MirroredElemTypes] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -33 | inline given gen[A]: Schema[A] = derived - | ^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]] - | ^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -29 | lazy val fields = recurse[m.MirroredElemTypes] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -33 | inline given gen[A]: Schema[A] = derived - | ^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]] - | ^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -18 | builder :: recurse[ts] - | ^^^^^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -29 | lazy val fields = recurse[m.MirroredElemTypes] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |This location contains code that was inlined from i13044.scala:18 -33 | inline given gen[A]: Schema[A] = derived - | ^^^^^^^ - -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i13044.scala b/tests/neg/i13044.scala index ad7687bbf2b8..2f9cae2f9039 100644 --- a/tests/neg/i13044.scala +++ b/tests/neg/i13044.scala @@ -33,7 +33,22 @@ trait SchemaDerivation { inline given gen[A]: Schema[A] = derived } -case class H(i: Int) +case class X15(i: Int) +case class X14(i: X15) +case class X13(i: X14) +case class X12(i: X13) +case class X11(i: X12) +case class X10(i: X11) +case class X9(i: X10) +case class X8(i: X9) +case class X7(i: X8) +case class X6(i: X7) +case class X5(i: X6) +case class X4(i: X5) +case class X3(i: X4) +case class X2(i: X3) +case class X1(i: X2) +case class H(i: X1) case class G(h: H) case class F(g: G) case class E(f: Option[F]) @@ -43,5 +58,5 @@ case class B(c: C) case class A(a: A, b: B) object TestApp { - implicit def typeSchema: Schema[A] = Schema.gen // error // error + implicit def typeSchema: Schema[A] = Schema.gen // error } diff --git a/tests/neg/i13461-a.scala b/tests/neg/i13461-a.scala new file mode 100644 index 000000000000..70ec689601f1 --- /dev/null +++ b/tests/neg/i13461-a.scala @@ -0,0 +1,9 @@ +package i13461: + + opaque type Opaque = Int + transparent inline def op: Opaque = (123: Opaque) + + object Main: + def main(args: Array[String]): Unit = + val o22: 123 = op // error + 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/i14401.check b/tests/neg/i14401.check new file mode 100644 index 000000000000..9704759505b1 --- /dev/null +++ b/tests/neg/i14401.check @@ -0,0 +1,30 @@ +-- [E039] Reference Error: tests/neg/i14401.scala:6:17 ----------------------------------------------------------------- +6 | def f: Int = x // error + | ^ + | forward reference to x extends over the definition of x (on line 7) + | + | longer explanation available when compiling with `-explain` +-- [E039] Reference Error: tests/neg/i14401.scala:10:17 ---------------------------------------------------------------- +10 | def f: Int = g // error + | ^ + | forward reference to g (defined on line 12) extends over the definition of x (on line 11) + | + | longer explanation available when compiling with `-explain` +-- [E039] Reference Error: tests/neg/i14401.scala:15:17 ---------------------------------------------------------------- +15 | def f: Int = g // error + | ^ + | forward reference to g (defined on line 17) extends over the definition of x (on line 16) + | + | longer explanation available when compiling with `-explain` +-- [E039] Reference Error: tests/neg/i14401.scala:31:20 ---------------------------------------------------------------- +31 | yield a + 27 // error + | ^ + | forward reference to ec (defined on line 32) extends over the definition of z (on line 29) + | + | longer explanation available when compiling with `-explain` +-- [E039] Reference Error: tests/neg/i14401.scala:38:28 ---------------------------------------------------------------- +38 | class NotUsed {val xs = args} // error + | ^^^^ + | forward reference to args (defined on line 41) extends over the definition of dummy (on line 39) + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i14401.scala b/tests/neg/i14401.scala new file mode 100644 index 000000000000..b0297453c561 --- /dev/null +++ b/tests/neg/i14401.scala @@ -0,0 +1,41 @@ +object Test: + def f: Int = x + val x: Int = f + + locally: + def f: Int = x // error + val x: Int = f + + locally: + def f: Int = g // error + val x: Int = f + def g: Int = x + + locally: + def f: Int = g // error + var x: Int = f + def g: Int = x + + locally: + def f: Int = g + Console.println("foo") + def g: Int = f + + locally: + import scala.concurrent.{ExecutionContext, Future}, ExecutionContext.Implicits + + def foo: Future[Int] = + val fInt = Future.successful(42) + val z = + for a <- fInt + yield a + 27 // error + implicit val ec: ExecutionContext = Implicits.global + z + foo + +object MyApp: + def main(args: Array[String]) = + class NotUsed {val xs = args} // error + val dummy = false + // oops, shadows the parameter + def args = Seq("a","b","c") diff --git a/tests/neg/i14401b.check b/tests/neg/i14401b.check new file mode 100644 index 000000000000..73488486f18d --- /dev/null +++ b/tests/neg/i14401b.check @@ -0,0 +1,20 @@ +-- [E039] Reference Error: tests/neg/i14401b.scala:5:17 ---------------------------------------------------------------- +5 | def f: Int = x; // error + | ^ + | forward reference to x extends over the definition of x (on line 6) + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | x is used before you define it, and the definition of x + | appears between that use and the definition of x. + | + | Forward references are allowed only if there are no value definitions between + | the reference and the definition that is referred to. + | Specifically, any statement between the reference and the definition + | cannot be a variable definition, and if it's a value definition, it must be lazy. + | + | Define x before it is used, + | or move the definition of x so it does not appear between + | the declaration of x and its use, + | or define x as lazy. + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i14401b.scala b/tests/neg/i14401b.scala new file mode 100644 index 000000000000..37aa43debd26 --- /dev/null +++ b/tests/neg/i14401b.scala @@ -0,0 +1,6 @@ +//> using options --explain + +object Test: + locally: + def f: Int = x; // error + val x: Int = f; diff --git a/tests/neg/i14401c.check b/tests/neg/i14401c.check new file mode 100644 index 000000000000..920f564faf42 --- /dev/null +++ b/tests/neg/i14401c.check @@ -0,0 +1,20 @@ +-- [E039] Reference Error: tests/neg/i14401c.scala:5:17 ---------------------------------------------------------------- +5 | def f: Int = g // error + | ^ + | forward reference to g (defined on line 7) extends over the definition of x (on line 6) + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | g is used before you define it, and the definition of x + | appears between that use and the definition of g. + | + | Forward references are allowed only if there are no value definitions between + | the reference and the definition that is referred to. + | Specifically, any statement between the reference and the definition + | cannot be a variable definition, and if it's a value definition, it must be lazy. + | + | Define g before it is used, + | or move the definition of x so it does not appear between + | the declaration of g and its use, + | or define x as lazy. + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i14401c.scala b/tests/neg/i14401c.scala new file mode 100644 index 000000000000..4d2467858e02 --- /dev/null +++ b/tests/neg/i14401c.scala @@ -0,0 +1,7 @@ +//> using options --explain + +object Test: + locally: + def f: Int = g // error + var x: Int = f + def g: Int = x diff --git a/tests/neg/i14966a.check b/tests/neg/i14966a.check index 777d1ec74955..fd941509807a 100644 --- a/tests/neg/i14966a.check +++ b/tests/neg/i14966a.check @@ -1,10 +1,12 @@ -- [E120] Naming Error: tests/neg/i14966a.scala:3:6 -------------------------------------------------------------------- 3 | def f(x: List[Int]): String = ??? // error | ^ - | Double definition: + | Conflicting definitions: | def f[X <: String](x: List[X]): String in class Test at line 2 and | def f(x: List[Int]): String in class Test at line 3 | have the same type (x: scala.collection.immutable.List): String after erasure. | | Consider adding a @targetName annotation to one of the conflicting definitions | for disambiguation. + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i15144.scala b/tests/neg/i15144.scala new file mode 100644 index 000000000000..af73a9c08e03 --- /dev/null +++ b/tests/neg/i15144.scala @@ -0,0 +1,16 @@ +//> using options --release 11 +// test: -jvm 17+ +// Must be tested where release target < current JVM. +// Maybe a bug that ct.sym is not used if release == JVM version. + +import java.time.Instant + +class C: + def f: Instant = new Instant // error + def g: Instant = Instant() // error + def p: P = new P // error + +class P private () + +@main def test() = println: + C().f diff --git a/tests/neg/i15692.scala b/tests/neg/i15692.scala new file mode 100644 index 000000000000..0cb163426691 --- /dev/null +++ b/tests/neg/i15692.scala @@ -0,0 +1,5 @@ +trait TC[X] +object TC { + given [T, S <: TC[S]](using TC[S]): TC[T] = ??? + summon[TC[Int]] // error +} diff --git a/tests/neg/i15784.check b/tests/neg/i15784.check index c1540b33d956..d8c9c193c7dc 100644 --- a/tests/neg/i15784.check +++ b/tests/neg/i15784.check @@ -10,3 +10,17 @@ | Not found: A | | longer explanation available when compiling with `-explain` +-- Warning: tests/neg/i15784.scala:7:7 --------------------------------------------------------------------------------- +7 | case X: Int => X // warn + | ^ + | Type ascriptions after patterns other than: + | * variable pattern, e.g. `case x: String =>` + | * number literal pattern, e.g. `case 10.5: Double =>` + | are no longer supported. Remove the type ascription or move it to a separate variable pattern. +-- Warning: tests/neg/i15784.scala:10:7 -------------------------------------------------------------------------------- +10 | case `Int`: Int => `Int` // warn + | ^^^^^ + | Type ascriptions after patterns other than: + | * variable pattern, e.g. `case x: String =>` + | * number literal pattern, e.g. `case 10.5: Double =>` + | are no longer supported. Remove the type ascription or move it to a separate variable pattern. diff --git a/tests/neg/i15784.scala b/tests/neg/i15784.scala index a58a27fb3cfa..4be909198eaa 100644 --- a/tests/neg/i15784.scala +++ b/tests/neg/i15784.scala @@ -1,4 +1,10 @@ def i15784 = List(42) match case List(_, Rest @ `a`) => Rest // error case List(_, Rest @ A) => Rest // error - case _ => ??? \ No newline at end of file + case _ => ??? + +def case2 = 42 match + case X: Int => X // warn + +def case3 = 42 match + case `Int`: Int => `Int` // warn diff --git a/tests/neg/i15850-a.check b/tests/neg/i15850-a.check new file mode 100644 index 000000000000..7b3a98b2f9c4 --- /dev/null +++ b/tests/neg/i15850-a.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i15850-a.scala:1:8 --------------------------------------------------------------------------------- +1 |val x = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | String constant is too long for the JVM diff --git a/tests/neg/i15850-a.scala b/tests/neg/i15850-a.scala new file mode 100644 index 000000000000..e7903d14c9b6 --- /dev/null +++ b/tests/neg/i15850-a.scala @@ -0,0 +1 @@ +val x = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" // error \ No newline at end of file diff --git a/tests/neg/i15850-b.check b/tests/neg/i15850-b.check new file mode 100644 index 000000000000..a4bb0056a3fa --- /dev/null +++ b/tests/neg/i15850-b.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i15850-b.scala:9:4 --------------------------------------------------------------------------------- +9 |def crash(x: Level7): Unit = () // error + | ^ + | Method signature is too long for the JVM diff --git a/tests/neg/i15850-b.scala b/tests/neg/i15850-b.scala new file mode 100644 index 000000000000..749adf8c0a74 --- /dev/null +++ b/tests/neg/i15850-b.scala @@ -0,0 +1,9 @@ +type Level1 = Tuple4[Unit, Unit, Unit, Unit] +type Level2 = Tuple4[Level1, Level1, Level1, Level1] +type Level3 = Tuple4[Level2, Level2, Level2, Level2] +type Level4 = Tuple4[Level3, Level3, Level3, Level3] +type Level5 = Tuple4[Level4, Level4, Level4, Level4] +type Level6 = Tuple4[Level5, Level5, Level5, Level5] +type Level7 = Tuple4[Level6, Level6, Level6, Level6] + +def crash(x: Level7): Unit = () // error \ No newline at end of file diff --git a/tests/neg/i18545.check b/tests/neg/i18545.check new file mode 100644 index 000000000000..1b336b0cc5ab --- /dev/null +++ b/tests/neg/i18545.check @@ -0,0 +1,26 @@ +-- [E173] Reference Error: tests/neg/i18545.scala:16:20 ---------------------------------------------------------------- +16 | def test: IOLocal.IOLocalImpl[Int] = // error + | ^^^^^^^^^^^^^^^^^^^ + |class IOLocalImpl cannot be accessed as a member of iolib.IOLocal.type from the top-level definitions in package tests. + | private[IOLocal] class IOLocalImpl can only be accessed from object IOLocal in package iolib. +-- [E173] Reference Error: tests/neg/i18545.scala:17:24 ---------------------------------------------------------------- +17 | IOLocal.IOLocalImpl.apply(42) // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + |method apply cannot be accessed as a member of iolib.IOLocal.IOLocalImpl.type from the top-level definitions in package tests. + | private[IOLocal] method apply can only be accessed from object IOLocal in package iolib. +-- [E050] Type Error: tests/neg/i18545.scala:18:22 --------------------------------------------------------------------- +18 | def test2 = IOLocal.IOLocalImpl(42) // error + | ^^^^^^^^^^^^^^^^^^^ + | object IOLocalImpl in object IOLocal does not take parameters + | + | longer explanation available when compiling with `-explain` +-- [E173] Reference Error: tests/neg/i18545.scala:19:22 ---------------------------------------------------------------- +19 | def test3 = IOLocal.AltIOLocalImpl.apply(42) // error + | ^^^^^^^^^^^^^^^^^^^^^^ + |object AltIOLocalImpl cannot be accessed as a member of iolib.IOLocal.type from the top-level definitions in package tests. + | private[IOLocal] object AltIOLocalImpl can only be accessed from object IOLocal in package iolib. +-- [E173] Reference Error: tests/neg/i18545.scala:20:22 ---------------------------------------------------------------- +20 | def test4 = IOLocal.AltIOLocalImpl(42) // error + | ^^^^^^^^^^^^^^^^^^^^^^ + |object AltIOLocalImpl cannot be accessed as a member of iolib.IOLocal.type from the top-level definitions in package tests. + | private[IOLocal] object AltIOLocalImpl can only be accessed from object IOLocal in package iolib. diff --git a/tests/neg/i18545.scala b/tests/neg/i18545.scala new file mode 100644 index 000000000000..86f18cd60771 --- /dev/null +++ b/tests/neg/i18545.scala @@ -0,0 +1,20 @@ +package iolib: + case class IO[A](value: A) + + sealed trait IOLocal[A] + + object IOLocal: + def apply[A](default: A): IO[IOLocal[A]] = IO(new IOLocalImpl(default)) + + private[IOLocal] final class IOLocalImpl[A](default: A) extends IOLocal[A] + object IOLocalImpl + + private[IOLocal] final class AltIOLocalImpl[A](default: A) extends IOLocal[A] + +package tests: + import iolib.IOLocal + def test: IOLocal.IOLocalImpl[Int] = // error + IOLocal.IOLocalImpl.apply(42) // error + def test2 = IOLocal.IOLocalImpl(42) // error + def test3 = IOLocal.AltIOLocalImpl.apply(42) // error + def test4 = IOLocal.AltIOLocalImpl(42) // error diff --git a/tests/neg/i18584/T.java b/tests/neg/i18584/T.java new file mode 100644 index 000000000000..ad05ca47aefe --- /dev/null +++ b/tests/neg/i18584/T.java @@ -0,0 +1,9 @@ +// test: -jvm 25+ + +int k = 0; +File f = null; +java.io.File g = f; +@Deprecated void main() { return; } +public record Person(String name, int age) { } +public class H { } +public static int k() { return 1; } diff --git a/tests/neg/i18584/Test.scala b/tests/neg/i18584/Test.scala new file mode 100644 index 000000000000..46977fea6d20 --- /dev/null +++ b/tests/neg/i18584/Test.scala @@ -0,0 +1,5 @@ +class Test { + new U + new H() // error + new T.H() // error +} diff --git a/tests/neg/i18584/U.java b/tests/neg/i18584/U.java new file mode 100644 index 000000000000..f9c640868358 --- /dev/null +++ b/tests/neg/i18584/U.java @@ -0,0 +1 @@ +public class U { } diff --git a/tests/neg/i18737.check b/tests/neg/i18737.check index f2067ad017cd..4f9ac50a784b 100644 --- a/tests/neg/i18737.check +++ b/tests/neg/i18737.check @@ -7,7 +7,10 @@ | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | - | Tree: v + | Tree: + | + | v + | | I tried to show that | (v : String & Long) | conforms to @@ -32,7 +35,10 @@ | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | - | Tree: v + | Tree: + | + | v + | | I tried to show that | (v : String | Long) | conforms to diff --git a/tests/neg/i19077.check b/tests/neg/i19077.check new file mode 100644 index 000000000000..4478de217fd7 --- /dev/null +++ b/tests/neg/i19077.check @@ -0,0 +1,35 @@ +-- Error: tests/neg/i19077.scala:4:35 ---------------------------------------------------------------------------------- +4 |def g(@deprecatedName("a") x: Int, a: Int) = x+a // error + | ^^^^^^ + | parameter name must be distinct from deprecated name +-- Error: tests/neg/i19077.scala:6:36 ---------------------------------------------------------------------------------- +6 |def h(@deprecatedName("a") x: Int, @deprecatedName("a") y: Int) = x+y // error + | ^^^^^^^^^^^^^^^^^^^^ + | deprecated parameter name must be distinct from other names +-- Error: tests/neg/i19077.scala:8:15 ---------------------------------------------------------------------------------- +8 |def i(x: Int, @deprecatedName("x") y: Int) = x+y // error + | ^^^^^^^^^^^^^^^^^^^^ + | deprecated parameter name must be distinct from other names +-- [E161] Naming Error: tests/neg/i19077.scala:10:35 ------------------------------------------------------------------- +10 |def j(x: Int, @deprecatedName("x") x: Int) = x // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | x is already defined as parameter x + | + | Note that overloaded methods must all be defined in the same group of toplevel definitions +-- Error: tests/neg/i19077.scala:13:15 --------------------------------------------------------------------------------- +13 | f(1, 2, 3, x = 42) // error + | ^^^^^^ + | parameter x of method f: (x: Int, y: Int, z: Int): Int is already instantiated +-- Error: tests/neg/i19077.scala:14:15 --------------------------------------------------------------------------------- +14 | f(1, 2, 3, w = 42) // error + | ^^^^^^ + | parameter w of method f: (x: Int, y: Int, z: Int): Int is already instantiated +-- Error: tests/neg/i19077.scala:15:20 --------------------------------------------------------------------------------- +15 | f(1, 2, w = 42, z = 27) // error + | ^^^^^^ + | parameter z of method f: (x: Int, y: Int, z: Int): Int is already instantiated +-- Error: tests/neg/i19077.scala:16:20 --------------------------------------------------------------------------------- +16 | f(1, 2, z = 42, w = 27) // error + | ^^^^^^ + | parameter w of method f: (x: Int, y: Int, z: Int): Int is already instantiated +there was 1 deprecation warning; re-run with -deprecation for details diff --git a/tests/neg/i19077.scala b/tests/neg/i19077.scala new file mode 100644 index 000000000000..cfb94cf8d456 --- /dev/null +++ b/tests/neg/i19077.scala @@ -0,0 +1,21 @@ + +def f(@deprecatedName("x") x: Int, @deprecatedName y: Int, @deprecatedName("w") z: Int) = x+y+z + +def g(@deprecatedName("a") x: Int, a: Int) = x+a // error + +def h(@deprecatedName("a") x: Int, @deprecatedName("a") y: Int) = x+y // error + +def i(x: Int, @deprecatedName("x") y: Int) = x+y // error + +def j(x: Int, @deprecatedName("x") x: Int) = x // error + +@main def Test = + f(1, 2, 3, x = 42) // error + f(1, 2, 3, w = 42) // error + f(1, 2, w = 42, z = 27) // error + f(1, 2, z = 42, w = 27) // error + +object X { def m[T](i: Int)(s: String) = s*i; def m[T](i: Int)(d: Double) = d*i } + +object Y: + def f = X.m(42)(s = "") // overloading resolution objects to methRef.symbol.paramSymss diff --git a/tests/neg/i19320.scala b/tests/neg/i19320.scala index e802215a1f10..3597f02cf431 100644 --- a/tests/neg/i19320.scala +++ b/tests/neg/i19320.scala @@ -1,7 +1,7 @@ -//> using scala "3.3.1" -//> using dep org.http4s::http4s-ember-client:1.0.0-M40 -//> using dep org.http4s::http4s-ember-server:1.0.0-M40 -//> using dep org.http4s::http4s-dsl:1.0.0-M40 +//!> using scala "3.3.1" +//!> using dep org.http4s::http4s-ember-client:1.0.0-M40 +//!> using dep org.http4s::http4s-ember-server:1.0.0-M40 +//!> using dep org.http4s::http4s-dsl:1.0.0-M40 //import cats.effect.* //import cats.implicits.* diff --git a/tests/neg/i19351.check b/tests/neg/i19351.check deleted file mode 100644 index a9ee10510b32..000000000000 --- a/tests/neg/i19351.check +++ /dev/null @@ -1,4 +0,0 @@ --- Error: tests/neg/i19351/A.scala:3:35 -------------------------------------------------------------------------------- -3 | inline def myMacro(): x.type = ${myMacroExpr} // error - | ^ - |Cyclic macro dependency; macro refers to a toplevel symbol in tests/neg/i19351/A.scala from which the macro is called diff --git a/tests/neg/i19351/A.scala b/tests/neg/i19351/A.scala deleted file mode 100644 index f2258631a15a..000000000000 --- a/tests/neg/i19351/A.scala +++ /dev/null @@ -1,5 +0,0 @@ -//object A: - val x: Int = 1 - inline def myMacro(): x.type = ${myMacroExpr} // error - def test = myMacro() - diff --git a/tests/neg/i19351a.check b/tests/neg/i19351a.check deleted file mode 100644 index 10789c2db5aa..000000000000 --- a/tests/neg/i19351a.check +++ /dev/null @@ -1,4 +0,0 @@ --- Error: tests/neg/i19351a/Test.scala:8:34 ---------------------------------------------------------------------------- -8 |inline def not(b: Bool): Bool = ${notMacro('b)} // error - | ^ - |Cyclic macro dependency; macro refers to a toplevel symbol in tests/neg/i19351a/Test.scala from which the macro is called diff --git a/tests/neg/i19413.scala b/tests/neg/i19413.scala new file mode 100644 index 000000000000..92eb6ce611bd --- /dev/null +++ b/tests/neg/i19413.scala @@ -0,0 +1,14 @@ + +import hello.bla.* // error + +//transparent inline def hello: kla.type = kla // ok +def hello: kla.type = kla + +object kla: + def ra = ??? + object bla { val ra1 = 31 } + +//import kla.bla.ra1 + +@main def test = println: + ra1 diff --git a/tests/neg/19414-desugared.check b/tests/neg/i19414-desugared.check similarity index 91% rename from tests/neg/19414-desugared.check rename to tests/neg/i19414-desugared.check index cc51ee471553..72a3a5eabd37 100644 --- a/tests/neg/19414-desugared.check +++ b/tests/neg/i19414-desugared.check @@ -1,4 +1,4 @@ --- [E172] Type Error: tests/neg/19414-desugared.scala:22:34 ------------------------------------------------------------ +-- [E172] Type Error: tests/neg/i19414-desugared.scala:22:34 ----------------------------------------------------------- 22 | summon[BodySerializer[JsObject]] // error: Ambiguous given instances | ^ |No best given instance of type BodySerializer[JsObject] was found for parameter x of method summon in object Predef. diff --git a/tests/neg/19414-desugared.scala b/tests/neg/i19414-desugared.scala similarity index 100% rename from tests/neg/19414-desugared.scala rename to tests/neg/i19414-desugared.scala diff --git a/tests/neg/19414.check b/tests/neg/i19414.check similarity index 91% rename from tests/neg/19414.check rename to tests/neg/i19414.check index 016e3942c825..10bc939494c6 100644 --- a/tests/neg/19414.check +++ b/tests/neg/i19414.check @@ -1,4 +1,4 @@ --- [E172] Type Error: tests/neg/19414.scala:15:34 ---------------------------------------------------------------------- +-- [E172] Type Error: tests/neg/i19414.scala:15:34 --------------------------------------------------------------------- 15 | summon[BodySerializer[JsObject]] // error: Ambiguous given instances | ^ |No best given instance of type BodySerializer[JsObject] was found for parameter x of method summon in object Predef. diff --git a/tests/neg/19414.scala b/tests/neg/i19414.scala similarity index 100% rename from tests/neg/19414.scala rename to tests/neg/i19414.scala diff --git a/tests/neg/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/i20245.check b/tests/neg/i20245.check index 565bde7678b7..7541d1125572 100644 --- a/tests/neg/i20245.check +++ b/tests/neg/i20245.check @@ -1,4 +1,3 @@ - -- [E046] Cyclic Error: tests/neg/i20245/Typer_2.scala:16:57 ----------------------------------------------------------- 16 | private[typer] val unification = new Unification(using this) // error | ^ @@ -15,3 +14,17 @@ | Run with both -explain-cyclic and -Ydebug-cyclic to see full stack trace. | | longer explanation available when compiling with `-explain` +-- [E046] Cyclic Error: tests/neg/i20245/Typer_2.scala:10:7 ------------------------------------------------------------ +10 |import effekt.source.{ resolve } // error + | ^ + | Cyclic reference involving class Context + | + | The error occurred while trying to compute the base classes of class Context + | which required to compute the base classes of trait TyperOps + | which required to compute the signature of trait TyperOps + | which required to elaborate the export clause export unification.requireSubtype + | which required to compute the base classes of class Context + | + | Run with both -explain-cyclic and -Ydebug-cyclic to see full stack trace. + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i20245/Typer_2.scala b/tests/neg/i20245/Typer_2.scala index ed7f05de80d0..bf4718de759c 100644 --- a/tests/neg/i20245/Typer_2.scala +++ b/tests/neg/i20245/Typer_2.scala @@ -7,7 +7,7 @@ import effekt.util.messages.ErrorReporter import effekt.context.{ Context } // This import is also NECESSARY for the cyclic error -import effekt.source.{ resolve } +import effekt.source.{ resolve } // error trait TyperOps extends ErrorReporter { self: Context => diff --git a/tests/neg/i20265-1.check b/tests/neg/i20265-1.check new file mode 100644 index 000000000000..6eb9f4cfe5c6 --- /dev/null +++ b/tests/neg/i20265-1.check @@ -0,0 +1,5 @@ +-- Error: tests/neg/i20265-1.scala:4:6 --------------------------------------------------------------------------------- +4 | def apply(args: Tuple.Map[m.MirroredElemTypes, Expr]): Expr[T] = ??? // error + | ^ + | non-private method apply in trait Ops refers to private given instance m + | in its type signature (args: Tuple.Map[Ops.this.m.MirroredElemTypes, Expr]): Expr[T] diff --git a/tests/neg/i20265-1.scala b/tests/neg/i20265-1.scala new file mode 100644 index 000000000000..085d387ae901 --- /dev/null +++ b/tests/neg/i20265-1.scala @@ -0,0 +1,9 @@ +trait Expr[T] + +trait Ops[T](using m: scala.deriving.Mirror.ProductOf[T]) { + def apply(args: Tuple.Map[m.MirroredElemTypes, Expr]): Expr[T] = ??? // error +} + +case class P(a: Int) +object P extends Ops[P] + diff --git a/tests/neg/i20265.check b/tests/neg/i20265.check new file mode 100644 index 000000000000..a55e4eba7c45 --- /dev/null +++ b/tests/neg/i20265.check @@ -0,0 +1,28 @@ +-- [E172] Type Error: tests/neg/i20265.scala:22:95 --------------------------------------------------------------------- +22 | println(summon[((String --> Unit) * (String --> Unit)) =:= Hinze[(String + String) --> Unit]]) // error + | ^ + | Cannot prove that (String --> Unit) * (String --> Unit) =:= Hinze[String + String --> Unit]. + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Hinze[String + String --> Unit] + | failed since selector (String + String --> Unit)#unfix + | does not match case k1 + k2 --> v => Hinze[k1 --> v] * Hinze[k2 --> v] + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case k1 * k2 --> v => k1 --> Hinze[k2 --> v] +-- [E172] Type Error: tests/neg/i20265.scala:23:66 --------------------------------------------------------------------- +23 | println(summon[String =:= Hinze[Fix[Lambda[String]#L] --> Unit]]) // error + | ^ + | Cannot prove that String =:= Hinze[Fix[Lambda[String]#L] --> Unit]. + | + | Note: a match type could not be fully reduced: + | + | trying to reduce Hinze[Fix[Lambda[String]#L] --> Unit] + | failed since selector (Fix[Lambda[String]#L] --> Unit)#unfix + | does not match case k1 + k2 --> v => Hinze[k1 --> v] * Hinze[k2 --> v] + | and cannot be shown to be disjoint from it either. + | Therefore, reduction cannot advance to the remaining case + | + | case k1 * k2 --> v => k1 --> Hinze[k2 --> v] diff --git a/tests/neg/i20265.scala b/tests/neg/i20265.scala new file mode 100644 index 000000000000..1d312188ae99 --- /dev/null +++ b/tests/neg/i20265.scala @@ -0,0 +1,23 @@ +//> using options -source:3.3 + +trait Poly +trait -->[X, Y] extends Poly +trait +[X, Y] extends Poly +trait *[X, Y] extends Poly + +type Hinze[X <: Fix[?]] = X#unfix match + case (k1 + k2) --> v => Hinze[(k1 --> v)] * Hinze[(k2 --> v)] + case (k1 * k2) --> v => k1 --> Hinze[(k2 --> v)] + +trait Lambda[V]: + type Abs[X] = V * X + type App[X] = X * X + type L[X] = V + Abs[X] + App[X] + +trait Fix[F[X]]: + type unfix = F[Fix[F]] + +@main +def m = + println(summon[((String --> Unit) * (String --> Unit)) =:= Hinze[(String + String) --> Unit]]) // error + println(summon[String =:= Hinze[Fix[Lambda[String]#L] --> Unit]]) // error diff --git a/tests/neg/i20768.check b/tests/neg/i20768.check new file mode 100644 index 000000000000..5c77d000fa2a --- /dev/null +++ b/tests/neg/i20768.check @@ -0,0 +1,8 @@ +-- [E172] Type Error: tests/neg/i20768/Bystander.scala:15:42 ----------------------------------------------------------- +15 | >> StateT.pure(computedResult) // error + | ^ + |Ambiguous given instances: both method catsInstancesForList in object Monad and method catsBimonadForFunction0 in object Monad match type cats.Monad[[_] =>> Any] of parameter F of method pure in object StateT +-- [E172] Type Error: tests/neg/i20768/Bystander.scala:14:21 ----------------------------------------------------------- +14 | .set(???) // error + | ^ + |Ambiguous given instances: both method catsInstancesForList in object Monad and method catsBimonadForFunction0 in object Monad match type cats.Monad[F] of parameter F of method set in object StateT diff --git a/tests/neg/i20768/Bystander.scala b/tests/neg/i20768/Bystander.scala new file mode 100644 index 000000000000..275e450ba2d9 --- /dev/null +++ b/tests/neg/i20768/Bystander.scala @@ -0,0 +1,19 @@ +//> using options -source:future + +import cats.Eval +import cats.data.StateT + +case class Bystander() + +object Bystander: + def of(index: Int): StateT[Eval, Unit, Bystander] = + StateT.get.flatMap { partialResultsCache => + _of(index) + .flatMap(computedResult => + StateT + .set(???) // error + >> StateT.pure(computedResult) // error + ) + } + + def _of(index: Int): StateT[Eval, Unit, Bystander] = ??? diff --git a/tests/neg/i20768/cats.scala b/tests/neg/i20768/cats.scala new file mode 100644 index 000000000000..2318b7c188ba --- /dev/null +++ b/tests/neg/i20768/cats.scala @@ -0,0 +1,30 @@ +package cats + +trait Monad[F[_]] + +object Monad: + implicit def catsInstancesForList: Monad[List] = ??? + implicit def catsBimonadForFunction0: Bimonad[Function0] = ??? + +trait Bimonad[F[_]] extends Monad[F] + +class Eval[+A] +object Eval: + implicit val catsMonadForEval: Monad[Eval] = new Monad[Eval] {} + +package data: + class IndexedStateT[F[_], SA, SB, A]: + def flatMap[B, SC](fas: A => IndexedStateT[F, SB, SC, B])(implicit F: Monad[F]): IndexedStateT[F, SA, SC, B] = ??? + + object IndexedStateT: + implicit def catsDataMonadForIndexedStateT[F[_], S](implicit F0: Monad[F]): Monad[[A] =>> IndexedStateT[F, S, S, A]] = + new Monad[[A] =>> IndexedStateT[F, S, S, A]] { + def pure[A](a: A): IndexedStateT[F, S, S, A] = ??? + def flatMap[A, B](fa: IndexedStateT[F, S, S, A])(f: A => IndexedStateT[F, S, S, B]): IndexedStateT[F, S, S, B] = ??? + } + + type StateT[F[_], S, A] = IndexedStateT[F, S, S, A] + object StateT: + def set[F[_], S](s: S)(implicit F: Monad[F]): StateT[F, S, Unit] = ??? + def pure[F[_], S, A](a: A)(implicit F: Monad[F]): StateT[F, S, A] = ??? + def get[F[_], S](implicit F: Monad[F]): StateT[F, S, S] = ??? diff --git a/tests/neg/i21207.scala b/tests/neg/i21207.scala new file mode 100644 index 000000000000..3a257fe08139 --- /dev/null +++ b/tests/neg/i21207.scala @@ -0,0 +1,32 @@ + +abstract class P { + def foo1(): Int = 1 + def foo2(x: Int): Int = 22 + def g4(s: String): String = s * 4 +} + +class C extends P { + def foo1(x: Int): Int = 11 + def foo2(): Int = 2 + + def foo3(): Int = 3 + def foo3(x: Int): Int = 33 + + def g4(): Int = 4 +} + +object Test extends App { + val c = new C + println(c.foo1) // error was omitted because of nullary fallback during ambiguous overload resolution + println(c.foo2) // error, add parens + println(c.foo3) // error + println(c.g4) // error + val s: String = c.g4 // error missing arg, expected type picks method + println(s) +} + +/* Scala 2 warns for all three selections: +warning: Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method foo1, +or remove the empty argument list from its definition (Java-defined methods are exempt). +In Scala 3, an unapplied method like this will be eta-expanded into a function. [quickfixable] +*/ 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/i21351.scala b/tests/neg/i21351.scala new file mode 100644 index 000000000000..b22f3046d7d1 --- /dev/null +++ b/tests/neg/i21351.scala @@ -0,0 +1,10 @@ +//> using options -Wconf:name=MissingEmptyArgumentList:e + +def f = () => 42 + +object X: + def apply() = 42 + +@main def Test = + f.apply // error boosted warning + X.apply // error always diff --git a/tests/neg/i22193.scala b/tests/neg/i22193.scala new file mode 100644 index 000000000000..f7ee5b1cf5e1 --- /dev/null +++ b/tests/neg/i22193.scala @@ -0,0 +1,32 @@ + +def fn2(arg: String, arg2: String)(f: String => Unit): Unit = f(arg) + +def fn3(arg: String, arg2: String)(f: => Unit): Unit = f + +def test1() = + + // ok baseline + fn2(arg = "blue sleeps faster than tuesday", arg2 = "the quick brown fox jumped over the lazy dog"): env => + val x = env + println(x) + + fn2( // error not a legal formal parameter for a function literal + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog"): env => + val x = env // error + println(x) + + fn2( + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog"): + env => // error indented definitions expected, identifier env found + val x = env + println(x) + +def test2() = + + fn3( // error missing argument list for value of type (=> Unit) => Unit + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog"): + val x = "Hello" // error + println(x) // error diff --git a/tests/neg/i22357.check b/tests/neg/i22357.check new file mode 100644 index 000000000000..213782a7fc6c --- /dev/null +++ b/tests/neg/i22357.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i22357.scala:1:0 ----------------------------------------------------------------------------------- +1 |@([A] =>> Int) // error + |^^^^^^^^^^^^^^ + |[A] =>> Int does not have a constructor diff --git a/tests/neg/i22357.scala b/tests/neg/i22357.scala new file mode 100644 index 000000000000..d572c150fb81 --- /dev/null +++ b/tests/neg/i22357.scala @@ -0,0 +1,2 @@ +@([A] =>> Int) // error +def i = 1 diff --git a/tests/neg/i22357a.check b/tests/neg/i22357a.check new file mode 100644 index 000000000000..9b2bcd2510d4 --- /dev/null +++ b/tests/neg/i22357a.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i22357a.scala:2:6 ---------------------------------------------------------------------------------- +2 | new ([A] =>> Int)(2) // error + | ^^^^^^^^^^^^^ + | [A] =>> Int does not have a constructor diff --git a/tests/neg/i22357a.scala b/tests/neg/i22357a.scala new file mode 100644 index 000000000000..b6c9c04fb268 --- /dev/null +++ b/tests/neg/i22357a.scala @@ -0,0 +1,2 @@ +def main = + new ([A] =>> Int)(2) // error diff --git a/tests/neg/i22423.check b/tests/neg/i22423.check new file mode 100644 index 000000000000..ec189d99524a --- /dev/null +++ b/tests/neg/i22423.check @@ -0,0 +1,24 @@ +-- Error: tests/neg/i22423.scala:35:14 --------------------------------------------------------------------------------- +35 | exportReader[Settings] // error + | ^^^^^^^^^^^^^^^^^^^^^^ + | cannot reduce summonFrom with + | patterns : case given reader @ _:ConfigReader[List[String]] + |-------------------------------------------------------------------------------------------------------------------- + |Inline stack trace + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from i22423.scala:12 +12 | summonFrom { case reader: ConfigReader[A] => reader } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from i22423.scala:12 +15 | summonConfigReader[List[String]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from i22423.scala:12 + 8 | readCaseClass() + | ^^^^^^^^^^^^^^^ + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from i22423.scala:12 +30 |inline given exportReader[A]: Exported[ConfigReader[A]] = Exported(HintsAwareConfigReaderDerivation.deriveReader[A]) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i22423.scala b/tests/neg/i22423.scala new file mode 100644 index 000000000000..4204f8f93216 --- /dev/null +++ b/tests/neg/i22423.scala @@ -0,0 +1,35 @@ +//> using options -Xmax-inlines:7 +import scala.deriving.Mirror +import scala.compiletime._ +import scala.compiletime.ops.int._ + +object HintsAwareConfigReaderDerivation { + inline def deriveReader[A]: ConfigReader[A] = + readCaseClass() + ??? + + private inline def summonConfigReader[A]: ConfigReader[A] = + summonFrom { case reader: ConfigReader[A] => reader } + + private inline def readCaseClass(): Unit = + summonConfigReader[List[String]] + val a1: Int = ??? + val a2: EmptyTuple = ??? + a1 *: a2 + ??? +} + +trait ConfigReader[A] +object ConfigReader { + implicit def traversableReader[A, F[A] <: TraversableOnce[A]](implicit configConvert: ConfigReader[A]): ConfigReader[F[A]] = ??? + implicit def exportedReader[A](implicit exported: Exported[ConfigReader[A]]): ConfigReader[A] = exported.instance + case class Exported[A](instance: A) +} + +import ConfigReader._ +inline given exportReader[A]: Exported[ConfigReader[A]] = Exported(HintsAwareConfigReaderDerivation.deriveReader[A]) + +case class Settings(rules: List[String]) + +val settings = + exportReader[Settings] // error diff --git a/tests/neg/i22552.check b/tests/neg/i22552.check new file mode 100644 index 000000000000..f6713b480699 --- /dev/null +++ b/tests/neg/i22552.check @@ -0,0 +1,12 @@ +-- [E214] Syntax Error: tests/neg/i22552.scala:3:10 -------------------------------------------------------------------- +3 | type A[X: TC] // error + | ^^^^ + | Context bounds are not allowed in this position +-- [E214] Syntax Error: tests/neg/i22552.scala:4:13 -------------------------------------------------------------------- +4 | type C = [X: TC] =>> List[X] // error + | ^^^^ + | Context bounds are not allowed in this position +-- [E214] Syntax Error: tests/neg/i22552.scala:5:13 -------------------------------------------------------------------- +5 | type D = [X: TC] => () => List[X] // error + | ^^^^ + | Context bounds are not allowed in this position diff --git a/tests/neg/i22552.scala b/tests/neg/i22552.scala new file mode 100644 index 000000000000..d76c6ec2af8d --- /dev/null +++ b/tests/neg/i22552.scala @@ -0,0 +1,5 @@ +trait Foo: + type TC[T] + type A[X: TC] // error + type C = [X: TC] =>> List[X] // error + type D = [X: TC] => () => List[X] // error diff --git a/tests/neg/i22560.scala b/tests/neg/i22560.scala new file mode 100644 index 000000000000..2957ac4a1bf1 --- /dev/null +++ b/tests/neg/i22560.scala @@ -0,0 +1,22 @@ + +class A: + protected class B + +// This fails to compile, as expected +val x = A().B() // error + +object C: + protected val p = "protected" + protected def getString() = "Hello!" + protected class D: + def d = D() // ok + +// This fails to compile +// val y = C.p + +// And this also fails to compile +// val z = C.getString() + +// However, this compiles just fine. +val alpha = C.D() // error +val beta = new C.D() // error diff --git a/tests/neg/i22560b.scala b/tests/neg/i22560b.scala new file mode 100644 index 000000000000..58b41b6f8766 --- /dev/null +++ b/tests/neg/i22560b.scala @@ -0,0 +1,20 @@ + +class Enumeration: + protected class Val(i: Int): + def this() = this(42) + object Val + +class Test extends Enumeration: + val Hearts = Val(27) // error + val Diamonds = Val() // error + +package p: + private[p] class C(i: Int) // ctor proxy gets privateWithin of class + private[p] class D(i: Int) + object D + private class E(i: Int) + +package q: + def f() = p.C(42) // error + def g() = p.D(42) // error + def h() = p.E(42) // error diff --git a/tests/neg/i22560c/client_2.scala b/tests/neg/i22560c/client_2.scala new file mode 100644 index 000000000000..c04720cd207b --- /dev/null +++ b/tests/neg/i22560c/client_2.scala @@ -0,0 +1,11 @@ + +package i22560: + val alpha = C.D() // error + + class Test extends Enumeration: + val Hearts = Val(27) // error + val Diamonds = Val() // error + +package q: + def f() = p.C(42) // error + def g() = p.D(42) // error diff --git a/tests/neg/i22560c/lib_1.scala b/tests/neg/i22560c/lib_1.scala new file mode 100644 index 000000000000..2d9d24962b8d --- /dev/null +++ b/tests/neg/i22560c/lib_1.scala @@ -0,0 +1,16 @@ + +package i22560: + + object C: + protected class D + + class Enumeration: + protected class Val(i: Int): + def this() = this(42) + object Val + +package p: + private[p] class C(i: Int) // companion gets privateWithin of class + private[p] class D(i: Int) // ctor proxy gets privateWithin of class + object D + diff --git a/tests/neg/i22575.check b/tests/neg/i22575.check new file mode 100644 index 000000000000..2345f9a00821 --- /dev/null +++ b/tests/neg/i22575.check @@ -0,0 +1,9 @@ +-- Error: tests/neg/i22575.scala:11:21 --------------------------------------------------------------------------------- +11 | requireConst(transp(nonTranspDouble)) // error + | ^^^^^^^^^^^^^^^^^^^^^^^ + | expected a constant value but found: ( + | { + | def $anonfun(x: Int): Int = x.*(2) + | closure($anonfun) + | } + | :(Int => Int)).apply(2) diff --git a/tests/neg/i22575.scala b/tests/neg/i22575.scala new file mode 100644 index 000000000000..8a667ef0fafd --- /dev/null +++ b/tests/neg/i22575.scala @@ -0,0 +1,12 @@ +import scala.compiletime.requireConst + +inline def nonTransp(inline f: Int => Int): Int = f(1) +transparent inline def transp(inline f: Int => Int): Int = f(2) +inline def nonTranspDouble: Int => Int = x => x * 2 +transparent inline def transpDouble: Int => Int = x => x * 2 + +def Test = + requireConst(nonTransp(nonTranspDouble)) + requireConst(nonTransp(transpDouble)) + requireConst(transp(nonTranspDouble)) // error + requireConst(transp(transpDouble)) diff --git a/tests/neg/i22592.scala b/tests/neg/i22592.scala new file mode 100644 index 000000000000..6a9a89cacf2a --- /dev/null +++ b/tests/neg/i22592.scala @@ -0,0 +1,14 @@ +import scala.quoted.* + +trait Foo: + def inherited = () + +class Bar extends Foo: + def local = () + def localArg(arg: Any) = () + + def macro1(using Quotes): Expr[Unit] = '{ local } // error + def macro3(using Quotes): Expr[Unit] = '{ inherited } // error + def macro4(using Quotes): Expr[Unit] = '{ this.local } // error + def macro5(using Quotes): Expr[Unit] = '{ this.inherited } // error + def macro6(using Quotes): Expr[Unit] = '{ localArg(this) } // error // error 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 new file mode 100644 index 000000000000..0f06d97f73e0 --- /dev/null +++ b/tests/neg/i22620.scala @@ -0,0 +1,8 @@ + +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/i22660.check b/tests/neg/i22660.check new file mode 100644 index 000000000000..22c8b68654e3 --- /dev/null +++ b/tests/neg/i22660.check @@ -0,0 +1,4 @@ +-- [E214] Syntax Error: tests/neg/i22660.scala:2:10 -------------------------------------------------------------------- +2 |type Foo[T: Equatable] // error + | ^^^^^^^^^^^ + | Context bounds are not allowed in this position diff --git a/tests/neg/i22660.scala b/tests/neg/i22660.scala new file mode 100644 index 000000000000..650ceac4f48c --- /dev/null +++ b/tests/neg/i22660.scala @@ -0,0 +1,2 @@ +trait Equatable[T] +type Foo[T: Equatable] // error diff --git a/tests/neg/i22670.check b/tests/neg/i22670.check new file mode 100644 index 000000000000..3c62f7323104 --- /dev/null +++ b/tests/neg/i22670.check @@ -0,0 +1,19 @@ +-- [E222] Syntax Error: tests/neg/i22670/i22670-macro.scala:4:8 -------------------------------------------------------- + 4 |package xy // error named package required for warning + |^ + |The package name `i22670-macro$package` will be encoded on the classpath, and can lead to undefined behaviour. + | + 5 |import scala.quoted.* + 6 |... + 9 | Expr("hello") + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Tools may not handle directories whose names differ from their corresponding package names. + | For example, `p-q` is encoded as `p$minusq` when written to the file system. + | + | Package objects derive their names from the file names, so files such as `myfile.test.scala` + | or `myfile-test.scala` can produce encoded names for the generated package objects. + | + | In this case, the name `i22670-macro$package` is encoded as `i22670$minusmacro$package`. + -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i22670/i22670-macro.scala b/tests/neg/i22670/i22670-macro.scala new file mode 100644 index 000000000000..76fdcb1dc3ce --- /dev/null +++ b/tests/neg/i22670/i22670-macro.scala @@ -0,0 +1,12 @@ +//> using options -Werror -Wconf:id=E222:e -explain + +//package `X-Y` // explicit package name gets a diagnostic +package xy // error named package required for warning + +import scala.quoted.* + +transparent inline def foo = + ${ fooImpl } + +def fooImpl(using Quotes): Expr[Any] = + Expr("hello") diff --git a/tests/neg/i22670/i22670-usage.scala b/tests/neg/i22670/i22670-usage.scala new file mode 100644 index 000000000000..f2986b91c08d --- /dev/null +++ b/tests/neg/i22670/i22670-usage.scala @@ -0,0 +1,5 @@ + +//import `X-Y`.* +import xy.foo + +val x = foo // Cyclic macro dependencies (if compilation proceeds) diff --git a/tests/neg/i22671.check b/tests/neg/i22671.check new file mode 100644 index 000000000000..5de8d968a53b --- /dev/null +++ b/tests/neg/i22671.check @@ -0,0 +1,61 @@ +-- [E007] Type Mismatch Error: tests/neg/i22671.scala:41:22 ------------------------------------------------------------ +41 | names_times(fields(0)) += fields(1).toLong // error + | ^^^^^^^^^ + | Found: Char + | Required: String + | + | longer explanation available when compiling with `-explain` +-- [E008] Not Found Error: tests/neg/i22671.scala:45:6 ----------------------------------------------------------------- +45 | x() += "42" // error + | ^^^^^^ + | value += is not a member of Int - did you mean Int.!=? or perhaps Int.<=? +-- [E052] Type Error: tests/neg/i22671.scala:49:6 ---------------------------------------------------------------------- +49 | c = 42 // error + | ^^^^^^ + | Reassignment to val c + | + | longer explanation available when compiling with `-explain` +-- [E052] Type Error: tests/neg/i22671.scala:9:6 ----------------------------------------------------------------------- +9 | X.w = 27 // error + | ^^^^^^^^ + | Reassignment to val w + | + | longer explanation available when compiling with `-explain` +-- [E052] Type Error: tests/neg/i22671.scala:12:6 ---------------------------------------------------------------------- +12 | X.x = 27 // error + | ^^^^^^^^ + | Reassignment to val x + | + | longer explanation available when compiling with `-explain` +-- [E052] Type Error: tests/neg/i22671.scala:16:4 ---------------------------------------------------------------------- +16 | x = 27 // error + | ^^^^^^ + | Reassignment to val x + | + | longer explanation available when compiling with `-explain` +-- [E052] Type Error: tests/neg/i22671.scala:20:4 ---------------------------------------------------------------------- +20 | y = 27 // error + | ^^^^^^ + | Reassignment to val y + | + | longer explanation available when compiling with `-explain` +-- [E052] Type Error: tests/neg/i22671.scala:24:4 ---------------------------------------------------------------------- +24 | y = 27 // error + | ^^^^^^ + | Reassignment to val y + | + | longer explanation available when compiling with `-explain` +-- [E052] Type Error: tests/neg/i22671.scala:28:4 ---------------------------------------------------------------------- +28 | x = 27 // error + | ^^^^^^ + | Reassignment to val x + | + | longer explanation available when compiling with `-explain` +-- [E008] Not Found Error: tests/neg/i22671.scala:31:6 ----------------------------------------------------------------- +31 | X.x += 27 // error + | ^^^^^^ + | value += is not a member of Int - did you mean Int.!=? or perhaps Int.<=? +-- [E008] Not Found Error: tests/neg/i22671.scala:32:4 ----------------------------------------------------------------- +32 | 1 += 1 // error + | ^^^^ + | value += is not a member of Int - did you mean (1 : Int).!=? or perhaps (1 : Int).<=? diff --git a/tests/neg/i22671.scala b/tests/neg/i22671.scala new file mode 100644 index 000000000000..afb04f4cea73 --- /dev/null +++ b/tests/neg/i22671.scala @@ -0,0 +1,49 @@ +object X: + val w: Int = 42 + def w(y: Int): Int = x + y + def x: Int = 42 + def x(y: Int): Int = x + y + val z = 26 + +def w = + X.w = 27 // error + +def f = + X.x = 27 // error + +def g = + import X.x + x = 27 // error + +def h = + import X.x as y + y = 27 // error + +def i = + import X.z as y + y = 27 // error + +def j = + val x = 42 + x = 27 // error + +def k = + X.x += 27 // error + 1 += 1 // error + + +object t8763: + import collection.mutable + def bar(): Unit = + val names_times = mutable.Map.empty[String, mutable.Set[Long]] + val line = "" + val Array(fields) = line.split("\t") + names_times(fields(0)) += fields(1).toLong // error + +object t9834: + object x { def apply() = 42 ; def update(i: Int) = () } + x() += "42" // error + +class C(c: Int): + def test(): Unit = + c = 42 // error diff --git a/tests/neg/i22721.check b/tests/neg/i22721.check new file mode 100644 index 000000000000..44b0749f3262 --- /dev/null +++ b/tests/neg/i22721.check @@ -0,0 +1,6 @@ +-- [E161] Naming Error: tests/neg/i22721/test2.scala:3:12 -------------------------------------------------------------- +3 |private def foobar: Int = 0 // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |foobar is already defined as method foobar in tests/neg/i22721/test1.scala + | + |Note that overloaded methods must all be defined in the same group of toplevel definitions diff --git a/tests/neg/i22721/test1.scala b/tests/neg/i22721/test1.scala new file mode 100644 index 000000000000..b3f1b1fba690 --- /dev/null +++ b/tests/neg/i22721/test1.scala @@ -0,0 +1,4 @@ +package example + +private def foobar: String = "foo" +object test1 { def x = foobar } diff --git a/tests/neg/i22721/test2.scala b/tests/neg/i22721/test2.scala new file mode 100644 index 000000000000..c70a42894dcd --- /dev/null +++ b/tests/neg/i22721/test2.scala @@ -0,0 +1,4 @@ +package example + +private def foobar: Int = 0 // error +object test2 { def x = foobar } diff --git a/tests/neg/i22752.scala b/tests/neg/i22752.scala new file mode 100644 index 000000000000..5c93b3ccbd68 --- /dev/null +++ b/tests/neg/i22752.scala @@ -0,0 +1,4 @@ +class Inner(body: Int ?=> Int) extends AnyVal: // error + def rescue: Int ?=> Int = ??? + +class Inner2(body: Int => Int) extends AnyVal // ok diff --git a/tests/neg/i22851.scala b/tests/neg/i22851.scala new file mode 100644 index 000000000000..9b675d13b16b --- /dev/null +++ b/tests/neg/i22851.scala @@ -0,0 +1,10 @@ +object Test { + def first[T <: Product](t: T)(using + m: scala.deriving.Mirror.ProductOf[T] { + type MirroredElemTypes <: NonEmptyTuple + } + ): Tuple.Head[m.MirroredElemTypes] = ??? + + first(EmptyTuple) // error + // MirroredElemTypes missmatch, expected: EmptyString, found: <: NonEmptyTuple. +} diff --git a/tests/neg/i22906.check b/tests/neg/i22906.check new file mode 100644 index 000000000000..118f9f4fa069 --- /dev/null +++ b/tests/neg/i22906.check @@ -0,0 +1,6 @@ +Flag -indent set repeatedly +-- Error: tests/neg/i22906.scala:6:15 ---------------------------------------------------------------------------------- +6 | {`1`: Int => 5} // error + | ^ + | parentheses are required around the parameter of a lambda + | This construct can be rewritten automatically under -rewrite -source 3.0-migration. diff --git a/tests/neg/i22906.scala b/tests/neg/i22906.scala new file mode 100644 index 000000000000..ca464e99bd48 --- /dev/null +++ b/tests/neg/i22906.scala @@ -0,0 +1,6 @@ +//> using options -rewrite -indent +//> nominally using scala 3.7.0-RC1 +// does not reproduce under "vulpix" test rig, which enforces certain flag sets? + +def program: Int => Int = + {`1`: Int => 5} // 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/i23036.check b/tests/neg/i23036.check new file mode 100644 index 000000000000..a5116ca904a8 --- /dev/null +++ b/tests/neg/i23036.check @@ -0,0 +1,40 @@ +-- Error: tests/neg/i23036.scala:2:2 ----------------------------------------------------------------------------------- +2 | import self.* // error + | ^^^^^^ + | imports are not allowed inside extensions +-- Error: tests/neg/i23036.scala:3:14 ---------------------------------------------------------------------------------- +3 | opaque type Maybe[+A] = A // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | types are not allowed inside extensions +-- Error: tests/neg/i23036.scala:4:7 ----------------------------------------------------------------------------------- +4 | type B = self.type // error + | ^^^^^^^^^^^^^^^^^^ + | types are not allowed inside extensions +-- Error: tests/neg/i23036.scala:5:6 ----------------------------------------------------------------------------------- +5 | val x: Int = self // error + | ^^^^^^^^^^^^^^^^^ + | values are not allowed inside extensions +-- Error: tests/neg/i23036.scala:6:6 ----------------------------------------------------------------------------------- +6 | var z: Int = self // error + | ^^^^^^^^^^^^^^^^^ + | values are not allowed inside extensions +-- Error: tests/neg/i23036.scala:10:2 ---------------------------------------------------------------------------------- +10 | import self.* // error + | ^^^^^^ + | imports are not allowed inside extensions +-- Error: tests/neg/i23036.scala:11:14 --------------------------------------------------------------------------------- +11 | opaque type X[+A] = A // error + | ^^^^^^^^^^^^^^^^^^^^^ + | types are not allowed inside extensions +-- Error: tests/neg/i23036.scala:12:7 ---------------------------------------------------------------------------------- +12 | type Y = self.type // error + | ^^^^^^^^^^^^^^^^^^ + | types are not allowed inside extensions +-- Error: tests/neg/i23036.scala:13:6 ---------------------------------------------------------------------------------- +13 | val x: Int = self // error + | ^^^^^^^^^^^^^^^^^ + | values are not allowed inside extensions +-- Error: tests/neg/i23036.scala:14:6 ---------------------------------------------------------------------------------- +14 | var z: Int = self // error + | ^^^^^^^^^^^^^^^^^ + | values are not allowed inside extensions diff --git a/tests/neg/i23036.scala b/tests/neg/i23036.scala new file mode 100644 index 000000000000..77bc5c8ffc20 --- /dev/null +++ b/tests/neg/i23036.scala @@ -0,0 +1,14 @@ +extension (self: Int) { + import self.* // error + opaque type Maybe[+A] = A // error + type B = self.type // error + val x: Int = self // error + var z: Int = self // error +} + +extension (self: Any) + import self.* // error + opaque type X[+A] = A // error + type Y = self.type // error + val x: Int = self // error + var z: Int = self // error diff --git a/tests/neg/i23111.check b/tests/neg/i23111.check new file mode 100644 index 000000000000..017657eac15c --- /dev/null +++ b/tests/neg/i23111.check @@ -0,0 +1,12 @@ +-- [E086] Syntax Error: tests/neg/i23111.scala:2:47 -------------------------------------------------------------------- +2 | def bar: (a: Int, b: Int) => A.this.type = x => ??? // error + | ^^^^^^^^ + | Wrong number of parameters, expected: 2 + | + | longer explanation available when compiling with `-explain` +-- [E086] Syntax Error: tests/neg/i23111.scala:3:45 -------------------------------------------------------------------- +3 | def baz: (a: Int, b: Int) => this.type = x => ??? // error + | ^^^^^^^^ + | Wrong number of parameters, expected: 2 + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i23111.scala b/tests/neg/i23111.scala new file mode 100644 index 000000000000..99dd3927e0ac --- /dev/null +++ b/tests/neg/i23111.scala @@ -0,0 +1,3 @@ +trait A: + def bar: (a: Int, b: Int) => A.this.type = x => ??? // error + def baz: (a: Int, b: Int) => this.type = x => ??? // error diff --git a/tests/neg/i23119.check b/tests/neg/i23119.check new file mode 100644 index 000000000000..b852c0af720e --- /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..d1495fa56203 --- /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/i23156.scala b/tests/neg/i23156.scala new file mode 100644 index 000000000000..c8f01ab16857 --- /dev/null +++ b/tests/neg/i23156.scala @@ -0,0 +1,6 @@ +object Unpack { + (1, 2) match { + case Unpack(first, _) => first + } + def unapply(e: (Int, Int)): Option[T] = ??? // error +} \ No newline at end of file diff --git a/tests/neg/i23179-reverse.scala b/tests/neg/i23179-reverse.scala new file mode 100644 index 000000000000..f31a838fb114 --- /dev/null +++ b/tests/neg/i23179-reverse.scala @@ -0,0 +1,22 @@ +// Test that reverse cases of #23179 are properly rejected by the type checker. +// The original issue was about SAM expecting Array[T] but impl having Array[? >: T]. +// Due to array invariance, the reverse cases cannot occur. + +object TestReverse { + trait A { def f(a: Array[? <: AnyRef]): Any } + def g(a: A) = a.f(Array.empty[AnyRef]) + + def test(): Unit = { + g((x: Array[AnyRef]) => x.headOption) // error: Array[AnyRef] is not a subtype of Array[? <: AnyRef] + } +} + +object TestResult { + trait A { def f(a: Int): Array[AnyRef] } + def g(a: A) = a.f(1) + + def test(): Unit = { + val arr: Array[? >: AnyRef] = Array("hello") + g((x: Int) => arr) // error: Array[? >: AnyRef] is not a subtype of Array[AnyRef] + } +} diff --git a/tests/neg/i23261.scala b/tests/neg/i23261.scala new file mode 100644 index 000000000000..7186ebd9e615 --- /dev/null +++ b/tests/neg/i23261.scala @@ -0,0 +1,7 @@ +@main def main(): Unit = + summon[0.0 =:= -0.0] // error: Cannot prove that (0.0: Double) =:= (-0.0: Double). + val d: 0.0 = -0.0 // error: Cannot prove that (0.0: Double) =:= (-0.0: Double). + val d2: -0.0 = 0.0 // error: Cannot prove that (-0.0: Double) =:= (0.0: Double). + summon[0.0f =:= -0.0f] // error: Cannot prove that (0.0f: Float) =:= (-0.0f: Float). + val f: 0.0f = -0.0f // error: Cannot prove that (0.0f: Float) =:= (-0.0f: Float). + val f2: -0.0f = 0.0f // error: Cannot prove that (-0.0f: Float) =:= (0.0f: Float). diff --git a/tests/neg/i23350.check b/tests/neg/i23350.check new file mode 100644 index 000000000000..ac64b3d22c1e --- /dev/null +++ b/tests/neg/i23350.check @@ -0,0 +1,45 @@ +-- [E120] Naming Error: tests/neg/i23350.scala:8:7 --------------------------------------------------------------------- +8 |object D extends A: // error + | ^ + | Name clash between defined and inherited member: + | def apply(p: A.this.Props): Unit in class A at line 5 and + | def apply(a: UndefOr2[String]): Unit in object D at line 10 + | have the same type (a: Object): Unit after erasure. + | + | Consider adding a @targetName annotation to one of the conflicting definitions + | for disambiguation. + |--------------------------------------------------------------------------------------------------------------------- + | 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 + | + | def apply(p: A.this.Props): Unit + | def apply(a: UndefOr2[String]): Unit + | + | erase to the identical signature + | + | (a: Object): Unit + | + | 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. + | 2. Keep the same names in source but give one definition a distinct + | 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/i23350.scala b/tests/neg/i23350.scala new file mode 100644 index 000000000000..fa85805f8ae3 --- /dev/null +++ b/tests/neg/i23350.scala @@ -0,0 +1,10 @@ +//> using options -explain + +abstract class A: + type Props + def apply(p: Props) = () + +type UndefOr2[A] = A | Unit +object D extends A: // error + case class Props() + def apply(a: UndefOr2[String]) = () diff --git a/tests/neg/i23402.check b/tests/neg/i23402.check new file mode 100644 index 000000000000..b258ab79e75c --- /dev/null +++ b/tests/neg/i23402.check @@ -0,0 +1,45 @@ +-- [E120] Naming Error: tests/neg/i23402.scala:6:5 --------------------------------------------------------------------- +6 | def apply(p1: String)(p2: Int): A = A(p1, p2) // error + | ^ + | Conflicting definitions: + | def apply(p1: String, p2: Int): A in object A at line 5 and + | def apply(p1: String)(p2: Int): A in object A at line 6 + | have the same type (p1: String, p2: Int): A after erasure. + | + | Consider adding a @targetName annotation to one of the conflicting definitions + | for disambiguation. + |--------------------------------------------------------------------------------------------------------------------- + | 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 + | + | def apply(p1: String, p2: Int): A + | def apply(p1: String)(p2: Int): A + | + | erase to the identical signature + | + | (p1: String, p2: Int): A + | + | 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. + | 2. Keep the same names in source but give one definition a distinct + | 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/i23402.scala b/tests/neg/i23402.scala new file mode 100644 index 000000000000..9f6fcdf032aa --- /dev/null +++ b/tests/neg/i23402.scala @@ -0,0 +1,7 @@ +//> using options -explain + +class A(p1: String, p2: Int) +object A { + def apply(p1: String, p2: Int): A = A(p1, p2) + def apply(p1: String)(p2: Int): A = A(p1, p2) // error +} diff --git a/tests/neg/i23402b.check b/tests/neg/i23402b.check new file mode 100644 index 000000000000..5036cf20097f --- /dev/null +++ b/tests/neg/i23402b.check @@ -0,0 +1,12 @@ +-- [E120] Naming Error: tests/neg/i23402b.scala:4:5 -------------------------------------------------------------------- +4 | def apply[T](p1: String)(p2: Int): A = A(p1, p2) // error + | ^ + | Conflicting definitions: + | def apply[T](p1: String, p2: Int): A in object A at line 3 and + | def apply[T](p1: String)(p2: Int): A in object A at line 4 + | have the same type (p1: String, p2: Int): A after erasure. + | + | Consider adding a @targetName annotation to one of the conflicting definitions + | for disambiguation. + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i23402b.scala b/tests/neg/i23402b.scala new file mode 100644 index 000000000000..1b486f21b583 --- /dev/null +++ b/tests/neg/i23402b.scala @@ -0,0 +1,5 @@ +class A(p1: String, p2: Int) +object A { + def apply[T](p1: String, p2: Int): A = A(p1, p2) + def apply[T](p1: String)(p2: Int): A = A(p1, p2) // error +} diff --git a/tests/neg/i23402c.check b/tests/neg/i23402c.check new file mode 100644 index 000000000000..be73dd9c361d --- /dev/null +++ b/tests/neg/i23402c.check @@ -0,0 +1,12 @@ +-- [E120] Naming Error: tests/neg/i23402c.scala:4:5 -------------------------------------------------------------------- +4 | def apply[T](p1: String)(p2: Int): A = A(p1, p2) // error + | ^ + | Conflicting definitions: + | def apply(p1: String, p2: Int): A in object A at line 3 and + | def apply[T](p1: String)(p2: Int): A in object A at line 4 + | have the same type (p1: String, p2: Int): A after erasure. + | + | Consider adding a @targetName annotation to one of the conflicting definitions + | for disambiguation. + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i23402c.scala b/tests/neg/i23402c.scala new file mode 100644 index 000000000000..c28c0c512459 --- /dev/null +++ b/tests/neg/i23402c.scala @@ -0,0 +1,5 @@ +class A(p1: String, p2: Int) +object A { + def apply(p1: String, p2: Int): A = A(p1, p2) + def apply[T](p1: String)(p2: Int): A = A(p1, p2) // error +} diff --git a/tests/neg/i23402d.check b/tests/neg/i23402d.check new file mode 100644 index 000000000000..38806f9f122e --- /dev/null +++ b/tests/neg/i23402d.check @@ -0,0 +1,12 @@ +-- [E120] Naming Error: tests/neg/i23402d.scala:5:4 -------------------------------------------------------------------- +5 |def f(x: Any): Unit = ??? // error + | ^ + | Conflicting definitions: + | def f[T](x: T): Unit in the top-level definitions in package at line 4 and + | def f(x: Any): Unit in the top-level definitions in package at line 5 + | have the same type (x: Object): Unit after erasure. + | + | Consider adding a @targetName annotation to one of the conflicting definitions + | for disambiguation. + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i23402d.scala b/tests/neg/i23402d.scala new file mode 100644 index 000000000000..674515fefd14 --- /dev/null +++ b/tests/neg/i23402d.scala @@ -0,0 +1,5 @@ +// This test checks that the example given in the `-explain` of the +// `DoubleDefinition` message is correct. + +def f[T](x: T): Unit = ??? +def f(x: Any): Unit = ??? // error diff --git a/tests/neg/i23474.check b/tests/neg/i23474.check new file mode 100644 index 000000000000..441978be6809 --- /dev/null +++ b/tests/neg/i23474.check @@ -0,0 +1,25 @@ +-- Error: tests/neg/i23474.scala:5:11 ---------------------------------------------------------------------------------- +5 |case class Y(val comment: String) extends Comment // error + | ^ + | class Y needs to be abstract, since var comment_=(x$1: String): Unit in trait Comment is not defined + | (Note that an abstract var requires a setter in addition to the getter) +-- Error: tests/neg/i23474.scala:7:6 ----------------------------------------------------------------------------------- +7 |class Z extends Comment: // error + | ^ + | class Z needs to be abstract, since var comment_=(x$1: String): Unit in trait Comment is not defined + | (Note that an abstract var requires a setter in addition to the getter) +-- [E164] Declaration Error: tests/neg/i23474.scala:11:15 -------------------------------------------------------------- +11 | override def comment: String = "" // error + | ^ + | error overriding variable comment in trait Comment of type String; + | method comment of type => String cannot override a mutable variable +-- Error: tests/neg/i23474.scala:10:6 ---------------------------------------------------------------------------------- +10 |class X extends Comment: // error + | ^ + | class X needs to be abstract, since var comment_=(x$1: String): Unit in trait Comment is not defined + | (Note that an abstract var requires a setter in addition to the getter) +-- Error: tests/neg/i23474.scala:13:6 ---------------------------------------------------------------------------------- +13 |class W extends Comment // error + | ^ + | class W needs to be abstract, since var comment: String in trait Comment is not defined + | (Note that variables need to be initialized to be defined) diff --git a/tests/neg/i23474.scala b/tests/neg/i23474.scala new file mode 100644 index 000000000000..3e34c2f457c0 --- /dev/null +++ b/tests/neg/i23474.scala @@ -0,0 +1,19 @@ +trait Comment { + var comment: String +} + +case class Y(val comment: String) extends Comment // error + +class Z extends Comment: // error + val comment: String = "" + +class X extends Comment: // error + override def comment: String = "" // error + +class W extends Comment // error + + +class OK: + val comment: String = "" + def comment_=(x: String): Unit = () + diff --git a/tests/neg/i23504.scala b/tests/neg/i23504.scala new file mode 100644 index 000000000000..e53337eaa105 --- /dev/null +++ b/tests/neg/i23504.scala @@ -0,0 +1,3 @@ +def test = + Seq.empty[[T] =>> () => ?].head() // error + Seq.empty[[T] =>> Int => Int].head(1) // 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/i23729.check b/tests/neg/i23729.check new file mode 100644 index 000000000000..58b03baf8b34 --- /dev/null +++ b/tests/neg/i23729.check @@ -0,0 +1,14 @@ +-- [E018] Syntax Error: tests/neg/i23729.scala:17:33 ------------------------------------------------------------------- +17 | def start: List[Direction] = match self // error syntax + | ^^^^^ + | expression expected but match found + | + | longer explanation available when compiling with `-explain` +-- Error: tests/neg/i23729.scala:18:6 ---------------------------------------------------------------------------------- +18 | case Empty => Nil // error poor recovery + | ^^^^ + | end of extension method expected but 'case' found +-- Error: tests/neg/i23729.scala:19:6 ---------------------------------------------------------------------------------- +19 | case Node(_, l, _) => l.start :+ Left // error poor recovery + | ^^^^ + | end of extension method expected but 'case' found diff --git a/tests/neg/i23729.scala b/tests/neg/i23729.scala new file mode 100644 index 000000000000..b575cbc0c35f --- /dev/null +++ b/tests/neg/i23729.scala @@ -0,0 +1,19 @@ + +trait Collection[Self, Element]: + type Index + extension (self: Self) + def start: Index + +sealed trait Tree[+T] +object Tree: + case object Empty extends Tree[Nothing] + case class Node[+T](value: T, lhs: Tree[T], rhs: Tree[T]) extends Tree[T] + +enum Direction: + case Left, Right, Here +given [T]: Collection[Tree[T], T] with + type Index = List[Direction] + extension (self: Tree[T]) + def start: List[Direction] = match self // error syntax + case Empty => Nil // error poor recovery + case Node(_, l, _) => l.start :+ Left // error poor recovery 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..ee41e04c75c8 --- /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..13a07b98591e --- /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/i24067.scala b/tests/neg/i24067.scala new file mode 100644 index 000000000000..2589245cf430 --- /dev/null +++ b/tests/neg/i24067.scala @@ -0,0 +1,13 @@ +class Toto +class Tata extends Toto + +extension (x: Toto) + def add(y: Toto): Toto = Toto() +extension (x: Tata) + def add(y: Toto): Toto = { println("this is what we want called!") ; Toto() } + def add(y: Tata): Tata = Tata() + +@main def Test = println: + val a: Tata = Tata() + val b: Toto = Toto() + a.add(b) // error diff --git a/tests/neg/i24168.scala b/tests/neg/i24168.scala new file mode 100644 index 000000000000..b554e4d817d5 --- /dev/null +++ b/tests/neg/i24168.scala @@ -0,0 +1,12 @@ +trait Generic extends Selectable: + def applyDynamic(name: String)(args: Any*): Any = () + +val foo: Generic { + def unapply(x: Int): Option[Unit] +} = new Generic: + def unapply(x: Int): Option[Unit] = Some(()) + +def x = + 42 match + case foo(()) => println("lol") // error + diff --git a/tests/neg/i24249.check b/tests/neg/i24249.check new file mode 100644 index 000000000000..fab37ddbe676 --- /dev/null +++ b/tests/neg/i24249.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i24249.scala:4:24 ---------------------------------------------------------------------------------- +4 | def this(s: String) = this(j = s.toInt, i = 27) // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | secondary constructor must call a preceding constructor diff --git a/tests/neg/i24249.scala b/tests/neg/i24249.scala new file mode 100644 index 000000000000..2bac54550d2e --- /dev/null +++ b/tests/neg/i24249.scala @@ -0,0 +1,9 @@ + + +class C(i: Int) { + def this(s: String) = this(j = s.toInt, i = 27) // error + + def this(i: Int, j: Int) = this(i + j) + + def c = 42 * i +} diff --git a/tests/neg/i24250.scala b/tests/neg/i24250.scala new file mode 100644 index 000000000000..853a3aaea486 --- /dev/null +++ b/tests/neg/i24250.scala @@ -0,0 +1,7 @@ + +class C +val cc = + new C + end new // error +val single = + new C: end new // error // error diff --git a/tests/neg/i24355.scala b/tests/neg/i24355.scala new file mode 100644 index 000000000000..2d94a6cf68d6 --- /dev/null +++ b/tests/neg/i24355.scala @@ -0,0 +1,7 @@ +object test { + case class Foo(i: Int, s: String) + + def f[A <: Tuple, B](v: A)(using m: scala.deriving.Mirror.ProductOf[B] { type MirroredElemTypes >: A }): B = m.fromTuple(v) + + f[(Any, String), Foo](("a", "b")) // error +} diff --git a/tests/neg/i24681.check b/tests/neg/i24681.check new file mode 100644 index 000000000000..8eebab0c421c --- /dev/null +++ b/tests/neg/i24681.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i24681.scala:3:9 ----------------------------------------------------------------------------------- +3 |class Foo @targetName("bla") () // error + | ^^^^^^^^^^^^^^^^^^^^^^ + | @targetName annotation may not be used on a constructor diff --git a/tests/neg/i24681.scala b/tests/neg/i24681.scala new file mode 100644 index 000000000000..a5d6d05b4bb5 --- /dev/null +++ b/tests/neg/i24681.scala @@ -0,0 +1,3 @@ +import scala.annotation.targetName + +class Foo @targetName("bla") () // error diff --git a/tests/neg/i24711-java-nested-types.check b/tests/neg/i24711-java-nested-types.check new file mode 100644 index 000000000000..84de6b2f3eb0 --- /dev/null +++ b/tests/neg/i24711-java-nested-types.check @@ -0,0 +1,21 @@ +-- [E007] Type Mismatch Error: tests/neg/i24711-java-nested-types.scala:2:20 ------------------------------------------- +2 | val test1: Int = (??? : java.util.Map.Entry[String, Int]) // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Found: java.util.Map.Entry[String, Int] + | Required: Int + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/i24711-java-nested-types.scala:4:20 ------------------------------------------- +4 | val test2: Int = (??? : java.util.Map.Entry[String, Int]) // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Found: java.util.Map.Entry[String, Int] + | Required: Int + | + | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/i24711-java-nested-types.scala:7:20 ------------------------------------------- +7 | val test4: Int = (??? : Outer#Inner) // error + | ^^^^^^^^^^^^^^^^^ + | Found: Test.this.Outer#Inner + | Required: Int + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i24711-java-nested-types.scala b/tests/neg/i24711-java-nested-types.scala new file mode 100644 index 000000000000..9c1c7e310f3c --- /dev/null +++ b/tests/neg/i24711-java-nested-types.scala @@ -0,0 +1,8 @@ +class Test { + val test1: Int = (??? : java.util.Map.Entry[String, Int]) // error + + val test2: Int = (??? : java.util.Map.Entry[String, Int]) // error + + trait Outer { type Inner } + val test4: Int = (??? : Outer#Inner) // error +} diff --git a/tests/neg/i24745.scala b/tests/neg/i24745.scala new file mode 100644 index 000000000000..a98cd0ecc693 --- /dev/null +++ b/tests/neg/i24745.scala @@ -0,0 +1,34 @@ +extension (s: String) + def f_::(using DummyImplicit): String = s.reverse + def g_::(x: (String, Int)): String = s"$s${x._1 * x._2}" + def ok_::(using suffix: String, n: Int): String = s"$s${suffix * n}" + def no_::(suffix: String, n: Int): String = s"$s${suffix * n}" // error + def huh_::(using DummyImplicit)(suffix: String, n: Int): String = s"$s${suffix * n}" + +def local = + extension (s: String) def f_::(using DummyImplicit): String = s.reverse + "hello, world".f_:: + +@main def Test = + println( + "hello, world".f_:: + ) + println( + f_:: + ("hello, world") + (using DummyImplicit.dummyImplicit) + ) + println( + ("s", 3).g_::("hello, world") + ) + println( + "hello, world" g_:: ("s", 3) + ) + println{ + given String = "s" + given Int = 3 + "hello, world".ok_:: + } + println( + "hello, world".huh_::("s", 3) + ) \ No newline at end of file diff --git a/tests/neg/i24789.scala b/tests/neg/i24789.scala new file mode 100644 index 000000000000..aeb602b4ea49 --- /dev/null +++ b/tests/neg/i24789.scala @@ -0,0 +1,10 @@ +//> using options -Werror -explain + +case class A(a: Int) +case class B(b: Int) +case class C(c: Int) + +val a = (A(1): A | B) match + case A(_) => "OK" + case B(_) => "OK" + case C(_) => "Not OK" // error diff --git a/tests/neg/i24871.scala b/tests/neg/i24871.scala new file mode 100644 index 000000000000..3088dc376e23 --- /dev/null +++ b/tests/neg/i24871.scala @@ -0,0 +1,10 @@ +class C[T] + +object X: + def apply(xs: Int*)(d: Double) = Nil + def apply[T](xs: Int*)(t: C[T])(d: Double) = Nil + def apply()(d: Double) = List(List(d)) + def apply[T]()(t: C[T])(d: Double) = List(List(d)) + +@main def Test = println: + X()(3.14) // error diff --git a/tests/neg/i24879.scala b/tests/neg/i24879.scala new file mode 100644 index 000000000000..62a33512207f --- /dev/null +++ b/tests/neg/i24879.scala @@ -0,0 +1,8 @@ +class C: + class D + +class Baz: + val c = C() + export c.* + +@main def Test = Baz().D() // error (Baz#c : C) is not a valid class prefix, since it is not an immutable path diff --git a/tests/neg/i24891.scala b/tests/neg/i24891.scala new file mode 100644 index 000000000000..2a8d657f124a --- /dev/null +++ b/tests/neg/i24891.scala @@ -0,0 +1,5 @@ +def test1: Unit = { + val x1 = IArray("a") + val x2 = x1.apply + val x3: IArray[IArray[String]] = x2 // error +} diff --git a/tests/neg/i24995.check b/tests/neg/i24995.check new file mode 100644 index 000000000000..f1b7a2051378 --- /dev/null +++ b/tests/neg/i24995.check @@ -0,0 +1,24 @@ +-- [E006] Not Found Error: tests/neg/i24995.scala:2:7 ------------------------------------------------------------------ +2 |val x: Mop[Int, Int] = Mop() // error // error + | ^^^ + | Not found: type Mop - did you mean Map? + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/i24995.scala:2:23 ----------------------------------------------------------------- +2 |val x: Mop[Int, Int] = Mop() // error // error + | ^^^ + | Not found: Mop - did you mean Map? + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/i24995.scala:6:9 ------------------------------------------------------------------ +6 | val y: Mop[Int, Int] = Mop() // error // error + | ^^^ + | Not found: type Mop + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/i24995.scala:6:25 ----------------------------------------------------------------- +6 | val y: Mop[Int, Int] = Mop() // error // error + | ^^^ + | Not found: Mop + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i24995.scala b/tests/neg/i24995.scala new file mode 100644 index 000000000000..436b3470798b --- /dev/null +++ b/tests/neg/i24995.scala @@ -0,0 +1,6 @@ + +val x: Mop[Int, Int] = Mop() // error // error + +def f() = + import Predef.{Map as _, *} + val y: Mop[Int, Int] = Mop() // error // error \ No newline at end of file diff --git a/tests/neg/i25248.scala b/tests/neg/i25248.scala new file mode 100644 index 000000000000..d81e960cb347 --- /dev/null +++ b/tests/neg/i25248.scala @@ -0,0 +1,2 @@ +val y = new c(for baz <- if baz // x then y else new A { // error + val z = w } do 27) 42 // error diff --git a/tests/neg/i25260.check b/tests/neg/i25260.check new file mode 100644 index 000000000000..46be3f71694a --- /dev/null +++ b/tests/neg/i25260.check @@ -0,0 +1,6 @@ +-- [E095] Syntax Error: tests/neg/i25260.scala:3:30 -------------------------------------------------------------------- +3 | expr match { case '{ type t : List; $x: t } => ??? } // error + | ^ + | =, >:, or <: expected, but ':' found + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i25260.scala b/tests/neg/i25260.scala new file mode 100644 index 000000000000..784ed73e55d4 --- /dev/null +++ b/tests/neg/i25260.scala @@ -0,0 +1,3 @@ +import scala.quoted._ +def expr(expr: Expr[Any])(using Quotes) = + expr match { case '{ type t : List; $x: t } => ??? } // error diff --git a/tests/neg/i25442.check b/tests/neg/i25442.check new file mode 100644 index 000000000000..f028b1a16b73 --- /dev/null +++ b/tests/neg/i25442.check @@ -0,0 +1,34 @@ +-- [E173] Reference Error: tests/neg/i25442/protected-constructors.scala:7:22 ------------------------------------------ +7 |class C extends B(new A(1)) // error + | ^ + | constructor A cannot be accessed as a member of protectedCtors.A from class C. + | protected constructor A can only be accessed from class C in package protectedCtors or one of its subclasses. +-- [E173] Reference Error: tests/neg/i25442/protected-constructors.scala:10:22 ----------------------------------------- +10 |class E extends D(new D(1)) // error + | ^ + | constructor D cannot be accessed as a member of protectedCtors.D from class E. + | protected constructor D can only be accessed from class E in package protectedCtors or one of its subclasses. +-- Error: tests/neg/i25442/protected-constructors.scala:18:24 ---------------------------------------------------------- +18 |class J extends G(new F(1)) // error: protected primary in super args + | ^ + | too many arguments for constructor F in class F: (): protectedCtors.F +-- [E173] Reference Error: tests/neg/i25442/protected-constructors.scala:22:14 ----------------------------------------- +22 | val k = new A(1) // error + | ^ + | constructor A cannot be accessed as a member of protectedCtors.A from class K. + | protected constructor A can only be accessed from class K in package protectedCtors or one of its subclasses. +-- [E173] Reference Error: tests/neg/i25442/protected-constructors.scala:27:28 ----------------------------------------- +27 |class N extends M(() => new A(2)) // error + | ^ + | constructor A cannot be accessed as a member of protectedCtors.A from class N. + | protected constructor A can only be accessed from class N in package protectedCtors or one of its subclasses. +-- [E173] Reference Error: tests/neg/i25442/test.scala:7:15 ------------------------------------------------------------ +7 | def t2 = new I(42) // error + | ^ + | constructor I cannot be accessed as a member of I from class M. + | protected constructor I can only be accessed from class M or one of its subclasses. +-- [E173] Reference Error: tests/neg/i25442/test.scala:8:35 ------------------------------------------------------------ +8 | def this(x: Int) = { this(); new I(x) } // error + | ^ + | constructor I cannot be accessed as a member of I from class M. + | protected constructor I can only be accessed from class M or one of its subclasses. diff --git a/tests/neg/i25442/protected-constructors.scala b/tests/neg/i25442/protected-constructors.scala new file mode 100644 index 000000000000..915a861cab51 --- /dev/null +++ b/tests/neg/i25442/protected-constructors.scala @@ -0,0 +1,27 @@ +package protectedCtors + +class A protected (x: Int) + +// Protected constructor in super call arguments (transitive parent) +class B(a: A) extends A(a.hashCode) +class C extends B(new A(1)) // error + +class D protected (x: Any) +class E extends D(new D(1)) // error + +// Mixed visibility constructors +class F protected (x: Int) { + def this() = this(0) // public secondary +} +class G(f: F) extends F(f.hashCode) +class H extends G(new F()) // ok: public secondary in super args +class J extends G(new F(1)) // error: protected primary in super args + +// new in primary constructor body +class K extends A(42) { + val k = new A(1) // error +} + +// Lambda in super call arguments +class M(f: () => A) extends A(1) +class N extends M(() => new A(2)) // error diff --git a/tests/neg/i25442/test.scala b/tests/neg/i25442/test.scala new file mode 100644 index 000000000000..fd46f37a636d --- /dev/null +++ b/tests/neg/i25442/test.scala @@ -0,0 +1,9 @@ +class I protected (x: Int) { + protected def f = x +} + +class M protected () extends I(42) { + def t1 = new M() // ok + def t2 = new I(42) // error + def this(x: Int) = { this(); new I(x) } // error +} diff --git a/tests/neg/i25456.check b/tests/neg/i25456.check new file mode 100644 index 000000000000..4c06b4278527 --- /dev/null +++ b/tests/neg/i25456.check @@ -0,0 +1,22 @@ +-- [E040] Syntax Error: tests/neg/i25456.scala:6:41 -------------------------------------------------------------------- +6 | def generate[F[_]: { ApplicativeError[_[?], ?] }]: Int = 1 // error // error // error // nopos-error + | ^ + | ']' expected, but '[' found +-- [E035] Syntax Error: tests/neg/i25456.scala:6:46 -------------------------------------------------------------------- +6 | def generate[F[_]: { ApplicativeError[_[?], ?] }]: Int = 1 // error // error // error // nopos-error + | ^ + | Unbound wildcard type + | + | longer explanation available when compiling with `-explain` +-- [E019] Syntax Error: tests/neg/i25456.scala:6:48 -------------------------------------------------------------------- +6 | def generate[F[_]: { ApplicativeError[_[?], ?] }]: Int = 1 // error // error // error // nopos-error + | ^ + | Missing return type + | + | longer explanation available when compiling with `-explain` +-- Warning: tests/neg/i25456.scala:6:40 -------------------------------------------------------------------------------- +6 | def generate[F[_]: { ApplicativeError[_[?], ?] }]: Int = 1 // error // error // error // nopos-error + | ^ + | `_` is deprecated for wildcard arguments of types: use `?` instead + | This construct can be rewritten automatically under -rewrite -source 3.4-migration. +No warnings can be incurred under -Werror diff --git a/tests/neg/i25500.scala b/tests/neg/i25500.scala new file mode 100644 index 000000000000..08ffe95bd98f --- /dev/null +++ b/tests/neg/i25500.scala @@ -0,0 +1,7 @@ +trait A[B[_]]: + type C + +def apply[D[_]](other: (a: A[D]) ?=> a.C): Any = ??? + +def main = + apply(()) // error diff --git a/tests/neg/i25531.check b/tests/neg/i25531.check new file mode 100644 index 000000000000..e6fa22598a82 --- /dev/null +++ b/tests/neg/i25531.check @@ -0,0 +1,13 @@ +-- Error: tests/neg/i25531.scala:3:6 ----------------------------------------------------------------------------------- +3 |class Walker extends FileVisitor[Path] {} // error + | ^^^^^^ + |class Walker needs to be abstract, since: + |it has 4 unimplemented members. + |/** As seen from class Walker, the missing signatures are as follows. + | * For convenience, these are usable as stub implementations. + | */ + | def postVisitDirectory(x0: java.nio.file.Path, x1: java.io.IOException): java.nio.file.FileVisitResult = ??? + | def preVisitDirectory + | (x0: java.nio.file.Path, x1: java.nio.file.attribute.BasicFileAttributes): java.nio.file.FileVisitResult = ??? + | def visitFile(x0: java.nio.file.Path, x1: java.nio.file.attribute.BasicFileAttributes): java.nio.file.FileVisitResult = ??? + | def visitFileFailed(x0: java.nio.file.Path, x1: java.io.IOException): java.nio.file.FileVisitResult = ??? diff --git a/tests/neg/i25531.scala b/tests/neg/i25531.scala new file mode 100644 index 000000000000..5dd33c61f4f9 --- /dev/null +++ b/tests/neg/i25531.scala @@ -0,0 +1,3 @@ +import java.nio.file.* + +class Walker extends FileVisitor[Path] {} // error diff --git a/tests/neg/i25531b.check b/tests/neg/i25531b.check new file mode 100644 index 000000000000..0c00d53d1ae6 --- /dev/null +++ b/tests/neg/i25531b.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i25531b.scala:1:6 ---------------------------------------------------------------------------------- +1 |class Rble extends Readable // error + | ^^^^ + |class Rble needs to be abstract, since def read(x0: java.nio.CharBuffer): Int in trait Readable in package java.lang is not defined diff --git a/tests/neg/i25531b.scala b/tests/neg/i25531b.scala new file mode 100644 index 000000000000..583610809e79 --- /dev/null +++ b/tests/neg/i25531b.scala @@ -0,0 +1 @@ +class Rble extends Readable // error diff --git a/tests/neg/i25548.scala b/tests/neg/i25548.scala new file mode 100644 index 000000000000..5b71afb19dcc --- /dev/null +++ b/tests/neg/i25548.scala @@ -0,0 +1,6 @@ +//> using options -Wconf:any:error -Wconf:cat=deprecation:warning -Werror + +class Location @deprecated("", "") (value: String) +object Location { + def apply(value: String): Location = new Location(value) // error +} diff --git a/tests/neg/i25683.scala b/tests/neg/i25683.scala new file mode 100644 index 000000000000..071a792ed145 --- /dev/null +++ b/tests/neg/i25683.scala @@ -0,0 +1,9 @@ +object OptionExample { + def f(x: Any) = () + def example = { + f( + if ( + false + ) + ) // error + val someVal // error diff --git a/tests/neg/i25716.scala b/tests/neg/i25716.scala new file mode 100644 index 000000000000..5ded9241554d --- /dev/null +++ b/tests/neg/i25716.scala @@ -0,0 +1,8 @@ +def g[@inline x: _](y: x): Unit = () // error: Unbound wildcard type + +trait F[A] +trait G[A] +def ok[A: {F, G}](a: A): Unit = () // error +def f0[A: {F, _}](a: A): Unit = () // error +def f1[A: {_, G}](a: A): Unit = () // error +def f2[A: {_, _}](a: A): Unit = () // error diff --git a/tests/neg/i25717.scala b/tests/neg/i25717.scala new file mode 100644 index 000000000000..4755cea51e31 --- /dev/null +++ b/tests/neg/i25717.scala @@ -0,0 +1,9 @@ +import scala.quoted.* + +object Crash { + def macroImpl(using Quotes)(q"a: String"): Expr[Boolean] = ??? // error + + def f(s"a: String") = ??? // error + + def softie(into q"a: String") = ??? // error +} diff --git a/tests/neg/i4368.scala b/tests/neg/i4368.scala index 95c342d5fe2a..6718aece4eba 100644 --- a/tests/neg/i4368.scala +++ b/tests/neg/i4368.scala @@ -98,7 +98,7 @@ object Test6 { object Test7 { class Fix[F[_]] { - class Foo { type R >: F[T] <: F[T] } // error: cyclic + class Foo { type R >: F[T] <: F[T] } // error type T = F[Foo#R] } @@ -149,9 +149,9 @@ object Test9 { object i4369 { trait X { self => type R <: Z - type Z >: X { type R = self.R; type Z = self.R } // error: cyclic // error: cyclic // error: cyclic + type Z >: X { type R = self.R; type Z = self.R } // error: cyclic } - class Foo extends X { type R = Foo; type Z = Foo } + class Foo extends X { type R = Foo; type Z = Foo } // error } object i4370 { class Foo { type R = A } diff --git a/tests/neg/i4369c.scala b/tests/neg/i4369c.scala index a2bfbcb598d6..06a77d51cdbe 100644 --- a/tests/neg/i4369c.scala +++ b/tests/neg/i4369c.scala @@ -1,5 +1,18 @@ trait X { self => type R <: Z - type Z >: X { type R = self.R; type Z = self.R } // error // error // error + type Z >: + X { // error + type R = // was-error + self.R + type Z = // was-error + self.R + } +} + +class Foo // error + extends X { + type R = + Foo + type Z = + Foo } -class Foo extends X { type R = Foo; type Z = Foo } diff --git a/tests/neg/i4520.check b/tests/neg/i4520.check new file mode 100644 index 000000000000..ce3030b9021d --- /dev/null +++ b/tests/neg/i4520.check @@ -0,0 +1,9 @@ +-- [E120] Naming Error: tests/neg/i4520.scala:6:46 --------------------------------------------------------------------- +6 |case class LabeledEdge[T, U](_1: T, label: U, _2: T) extends EdgeLike[T] // error + | ^ + | Conflicting definitions: + | def _2: U in class LabeledEdge at line 6 and + | val _2: T in class LabeledEdge at line 6 + | _2 is a case element selector and must name the 2th element + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i4520.scala b/tests/neg/i4520.scala new file mode 100644 index 000000000000..5bb3c87d07fb --- /dev/null +++ b/tests/neg/i4520.scala @@ -0,0 +1,6 @@ +trait EdgeLike[T] { + def _1: T + def _2: T +} + +case class LabeledEdge[T, U](_1: T, label: U, _2: T) extends EdgeLike[T] // error diff --git a/tests/neg/i7359-f.check b/tests/neg/i7359-f.check index 5eabb4e1dd8c..9dca40aebb35 100644 --- a/tests/neg/i7359-f.check +++ b/tests/neg/i7359-f.check @@ -4,7 +4,7 @@ | Name clash between inherited members: | def equals[T >: Boolean <: Boolean](obj: Any): T in trait SAMTrait at line 3 and | def equals(x$0: Any): Boolean in class Any - | have the same type after erasure. + | have the same type (x$0: Object): Boolean after erasure. | | Consider adding a @targetName annotation to one of the conflicting definitions | for disambiguation. diff --git a/tests/neg/i8194.check b/tests/neg/i8194.check new file mode 100644 index 000000000000..58b163b142f5 --- /dev/null +++ b/tests/neg/i8194.check @@ -0,0 +1,12 @@ +-- [E173] Reference Error: tests/neg/i8194.scala:5:22 ------------------------------------------------------------------ +5 |@main def main = Test.foo // error + | ^^^^^^^^ + | class A cannot be accessed as a member of (Test : Test.type) from the top-level definitions in package . + | private class A can only be accessed from object Test. + |--------------------------------------------------------------------------------------------------------------------- + |Inline stack trace + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from i8194.scala:3 +3 | inline def foo: Int = A().test + | ^ + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i8194.scala b/tests/neg/i8194.scala new file mode 100644 index 000000000000..d24910c9504c --- /dev/null +++ b/tests/neg/i8194.scala @@ -0,0 +1,5 @@ +object Test { + private class A() { def test = 42 } + inline def foo: Int = A().test +} +@main def main = Test.foo // 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/i9408b.check b/tests/neg/i9408b.check index 5f8a854a9282..9adc5300de3c 100644 --- a/tests/neg/i9408b.check +++ b/tests/neg/i9408b.check @@ -1,4 +1,3 @@ - -- Error: tests/neg/i9408b/Test_2.scala:8:20 --------------------------------------------------------------------------- 8 | val length: Int = "abc" // error | ^^^^^ diff --git a/tests/neg/if-error.check b/tests/neg/if-error.check index d8350da1872b..edab056a9da8 100644 --- a/tests/neg/if-error.check +++ b/tests/neg/if-error.check @@ -3,6 +3,7 @@ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Found: Unit | Required: Int + | | Maybe you are missing an else part for the conditional? | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/indentRight.scala b/tests/neg/indentRight.scala index 8eb9deb23389..e76c83a02c23 100644 --- a/tests/neg/indentRight.scala +++ b/tests/neg/indentRight.scala @@ -24,8 +24,8 @@ object Test { } trait A - case class B() extends A // error: Line is indented too far to the right - case object C extends A // error: Line is indented too far to the right + case class B() extends A + case object C extends A if (true) // OK println("hi") diff --git a/tests/neg/infer-union-warning-id.scala b/tests/neg/infer-union-warning-id.scala new file mode 100644 index 000000000000..152423218d5c --- /dev/null +++ b/tests/neg/infer-union-warning-id.scala @@ -0,0 +1,5 @@ +//> using options -Winfer-union -Wconf:id=E225:error + +case class Pair[A](a: A, b: A) + +val _ = Pair(1, "") // error \ No newline at end of file diff --git a/tests/neg/infix-then-complex-expr.check b/tests/neg/infix-then-complex-expr.check new file mode 100644 index 000000000000..595b8c293ed7 --- /dev/null +++ b/tests/neg/infix-then-complex-expr.check @@ -0,0 +1,17 @@ +-- Error: tests/neg/infix-then-complex-expr.scala:3:2 ------------------------------------------------------------------ +3 | && // error + | ^^ + |end of statement expected but identifier found - did you intend to apply an infix operator? If so, wrap the right operand in parentheses or in a block. +-- [E002] Syntax Warning: tests/neg/infix-then-complex-expr.scala:4:2 -------------------------------------------------- +4 | try java.lang.Boolean.valueOf("true") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | A try without catch or finally is equivalent to putting + | its body in a block; no exceptions are handled. + | + | longer explanation available when compiling with `-explain` +-- [E129] Potential Issue Warning: tests/neg/infix-then-complex-expr.scala:2:2 ----------------------------------------- +2 | true + | ^^^^ + | A pure expression does nothing in statement position + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/infix-then-complex-expr.scala b/tests/neg/infix-then-complex-expr.scala new file mode 100644 index 000000000000..d1595b88f504 --- /dev/null +++ b/tests/neg/infix-then-complex-expr.scala @@ -0,0 +1,4 @@ +@main def test = println: + true + && // error + try java.lang.Boolean.valueOf("true") diff --git a/tests/neg/leading-infix-miss.check b/tests/neg/leading-infix-miss.check index c6bfa458d1ad..a7a5625464b5 100644 --- a/tests/neg/leading-infix-miss.check +++ b/tests/neg/leading-infix-miss.check @@ -12,4 +12,6 @@ | Found: Unit | Required: Boolean | + | Maybe the enclosing block is missing a final expression after the definition of `x`? + | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/mixin-forwarder-clash1.check b/tests/neg/mixin-forwarder-clash1.check index 8c1e2a7911ef..328786f9b551 100644 --- a/tests/neg/mixin-forwarder-clash1.check +++ b/tests/neg/mixin-forwarder-clash1.check @@ -4,7 +4,7 @@ | Name clash between inherited members: | def concat(suffix: Int): X in trait One at line 4 and | def concat[Dummy](suffix: Int): Y in trait Two at line 8 - | have the same type after erasure. + | have the same type (suffix: Int): Object after erasure. | | Consider adding a @targetName annotation to one of the conflicting definitions | for disambiguation. diff --git a/tests/neg/mixin-forwarder-clash2.check b/tests/neg/mixin-forwarder-clash2.check index 8956d96e2071..126382e563e5 100644 --- a/tests/neg/mixin-forwarder-clash2.check +++ b/tests/neg/mixin-forwarder-clash2.check @@ -1,11 +1,10 @@ - -- [E120] Naming Error: tests/neg/mixin-forwarder-clash2/B_2.scala:1:6 ------------------------------------------------- 1 |class Bar2 extends Bar1 with Two[Foo] // error | ^ | Name clash between inherited members: | def concat(suffix: Int): X in trait One at line 4 and | def concat[Dummy](suffix: Int): Y in trait Two at line 8 - | have the same type after erasure. + | have the same type (suffix: Int): Object after erasure. | | Consider adding a @targetName annotation to one of the conflicting definitions | for disambiguation. diff --git a/tests/neg/no-unit.check b/tests/neg/no-unit.check new file mode 100644 index 000000000000..50842ad2bd46 --- /dev/null +++ b/tests/neg/no-unit.check @@ -0,0 +1,8 @@ +-- Error: tests/neg/no-unit.scala:1:8 ---------------------------------------------------------------------------------- +1 |val u = Unit // error + | ^^^^ + | `Unit` companion object is not allowed in source; instead, use `()` for the unit value +-- Error: tests/neg/no-unit.scala:7:2 ---------------------------------------------------------------------------------- +7 | Unit // error + | ^^^^ + | `Unit` companion object is not allowed in source; instead, use `()` for the unit value diff --git a/tests/neg/no-unit.scala b/tests/neg/no-unit.scala new file mode 100644 index 000000000000..437620ed9074 --- /dev/null +++ b/tests/neg/no-unit.scala @@ -0,0 +1,7 @@ +val u = Unit // error + +inline def a(inline f: Unit ?=> Unit): Unit = f(using ()) + +def s5bug = a: + print(2) + Unit // error diff --git a/tests/neg/notfound-term-but-type.check b/tests/neg/notfound-term-but-type.check new file mode 100644 index 000000000000..0c083d713e6f --- /dev/null +++ b/tests/neg/notfound-term-but-type.check @@ -0,0 +1,6 @@ +-- [E006] Not Found Error: tests/neg/notfound-term-but-type.scala:1:22 ------------------------------------------------- +1 |def test[T] = println(T) // error + | ^ + | Expected a term, but found a type: T + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/notfound-term-but-type.scala b/tests/neg/notfound-term-but-type.scala new file mode 100644 index 000000000000..dfc3082e6cf5 --- /dev/null +++ b/tests/neg/notfound-term-but-type.scala @@ -0,0 +1 @@ +def test[T] = println(T) // error \ No newline at end of file diff --git a/tests/neg/notfound-type-but-term.check b/tests/neg/notfound-type-but-term.check new file mode 100644 index 000000000000..b73d9c01d306 --- /dev/null +++ b/tests/neg/notfound-type-but-term.check @@ -0,0 +1,6 @@ +-- [E006] Not Found Error: tests/neg/notfound-type-but-term.scala:3:12 ------------------------------------------------- +3 |def test(): O = ??? // error + | ^ + | Expected a type, but found a term: O - did you mean O.type? + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/notfound-type-but-term.scala b/tests/neg/notfound-type-but-term.scala new file mode 100644 index 000000000000..3ba70bbcfd0b --- /dev/null +++ b/tests/neg/notfound-type-but-term.scala @@ -0,0 +1,3 @@ +object O + +def test(): O = ??? // error diff --git a/tests/neg/nowarn.check b/tests/neg/nowarn.check deleted file mode 100644 index 636cabd44d07..000000000000 --- a/tests/neg/nowarn.check +++ /dev/null @@ -1,110 +0,0 @@ --- [E002] Syntax Warning: tests/neg/nowarn.scala:11:10 ----------------------------------------------------------------- -11 |def t1a = try 1 // warning (parser) - | ^^^^^ - | A try without catch or finally is equivalent to putting - | its body in a block; no exceptions are handled. - | - | longer explanation available when compiling with `-explain` --- [E002] Syntax Warning: tests/neg/nowarn.scala:25:25 ----------------------------------------------------------------- -25 |@nowarn(o.inl) def t2d = try 1 // two warnings (`inl` is not a compile-time constant) - | ^^^^^ - | A try without catch or finally is equivalent to putting - | its body in a block; no exceptions are handled. - | - | longer explanation available when compiling with `-explain` --- [E002] Syntax Warning: tests/neg/nowarn.scala:33:26 ----------------------------------------------------------------- -33 |@nowarn("id=1") def t4d = try 1 // error and warning (unused nowarn, wrong id) - | ^^^^^ - | A try without catch or finally is equivalent to putting - | its body in a block; no exceptions are handled. - | - | longer explanation available when compiling with `-explain` --- [E002] Syntax Warning: tests/neg/nowarn.scala:35:28 ----------------------------------------------------------------- -35 |@nowarn("verbose") def t5 = try 1 // warning with details - | ^^^^^ - | A try without catch or finally is equivalent to putting - | its body in a block; no exceptions are handled. -Matching filters for @nowarn or -Wconf: - - id=E2 - - name=EmptyCatchAndFinallyBlock - | - | longer explanation available when compiling with `-explain` --- [E129] Potential Issue Warning: tests/neg/nowarn.scala:15:11 -------------------------------------------------------- -15 |def t2 = { 1; 2 } // warning (the invalid nowarn doesn't silence anything) - | ^ - | A pure expression does nothing in statement position - | - | longer explanation available when compiling with `-explain` --- Warning: tests/neg/nowarn.scala:14:8 -------------------------------------------------------------------------------- -14 |@nowarn("wat?") // warning (typer, invalid filter) - | ^^^^^^ - | Invalid message filter - | unknown filter: wat? --- [E129] Potential Issue Warning: tests/neg/nowarn.scala:18:12 -------------------------------------------------------- -18 |def t2a = { 1; 2 } // warning (invalid nowarn doesn't silence) - | ^ - | A pure expression does nothing in statement position - | - | longer explanation available when compiling with `-explain` --- Warning: tests/neg/nowarn.scala:17:8 -------------------------------------------------------------------------------- -17 |@nowarn(t1a.toString) // warning (typer, argument not a compile-time constant) - | ^^^^^^^^^^^^ - | filter needs to be a compile-time constant string --- Warning: tests/neg/nowarn.scala:25:10 ------------------------------------------------------------------------------- -25 |@nowarn(o.inl) def t2d = try 1 // two warnings (`inl` is not a compile-time constant) - | ^^^^^ - | filter needs to be a compile-time constant string --- Deprecation Warning: tests/neg/nowarn.scala:39:10 ------------------------------------------------------------------- -39 |def t6a = f // warning (refchecks, deprecation) - | ^ - | method f is deprecated --- Deprecation Warning: tests/neg/nowarn.scala:42:30 ------------------------------------------------------------------- -42 |@nowarn("msg=fish") def t6d = f // error (unused nowarn), warning (deprecation) - | ^ - | method f is deprecated --- Deprecation Warning: tests/neg/nowarn.scala:49:10 ------------------------------------------------------------------- -49 |def t7c = f // warning (deprecation) - | ^ - | method f is deprecated --- [E092] Pattern Match Unchecked Warning: tests/neg/nowarn.scala:55:7 ------------------------------------------------- -55 | case _: List[Int] => 0 // warning (patmat, unchecked) - | ^ - |the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from Any - | - | longer explanation available when compiling with `-explain` --- Error: tests/neg/nowarn.scala:33:1 ---------------------------------------------------------------------------------- -33 |@nowarn("id=1") def t4d = try 1 // error and warning (unused nowarn, wrong id) - |^^^^^^^^^^^^^^^ - |@nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:42:1 ---------------------------------------------------------------------------------- -42 |@nowarn("msg=fish") def t6d = f // error (unused nowarn), warning (deprecation) - |^^^^^^^^^^^^^^^^^^^ - |@nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:50:5 ---------------------------------------------------------------------------------- -50 | : @nowarn("msg=fish") // error (unused nowarn) - | ^^^^^^^^^^^^^^^^^^^ - | @nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:62:0 ---------------------------------------------------------------------------------- -62 |@nowarn def t9a = { 1: @nowarn; 2 } // error (outer @nowarn is unused) - |^^^^^^^ - |@nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:63:27 --------------------------------------------------------------------------------- -63 |@nowarn def t9b = { 1: Int @nowarn; 2 } // error (inner @nowarn is unused, it covers the type, not the expression) - | ^^^^^^^ - | @nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:68:0 ---------------------------------------------------------------------------------- -68 |@nowarn @ann(f) def t10b = 0 // error (unused nowarn) - |^^^^^^^ - |@nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:69:8 ---------------------------------------------------------------------------------- -69 |@ann(f: @nowarn) def t10c = 0 // error (unused nowarn), should be silent - | ^^^^^^^ - | @nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:72:0 ---------------------------------------------------------------------------------- -72 |@nowarn class I1a { // error (unused nowarn) - |^^^^^^^ - |@nowarn annotation does not suppress any warnings --- Error: tests/neg/nowarn.scala:77:0 ---------------------------------------------------------------------------------- -77 |@nowarn class I1b { // error (unused nowarn) - |^^^^^^^ - |@nowarn annotation does not suppress any warnings diff --git a/tests/neg/nowarn.scala b/tests/neg/nowarn.scala deleted file mode 100644 index 5b18ab5ccc51..000000000000 --- a/tests/neg/nowarn.scala +++ /dev/null @@ -1,89 +0,0 @@ -//> using options -deprecation -Wunused:nowarn "-Wconf:msg=@nowarn annotation does not suppress any warnings:e" - -import scala.annotation.{ nowarn, Annotation } - -// This test doesn't run with `-Werror`, because once there's an error, later phases are skipped and we would not see -// their warnings. -// Instead, this test runs with `-Wunused:nowarn -Wconf:msg=@nowarn annotation does not suppress any warnings:e`. -// Only "unused nowarn" warnings are reported as errors. Since these warnings are reported at the very end, all other -// phases of the compiler run normally. - -def t1a = try 1 // warning (parser) -@nowarn("msg=try without catch") def t1b = try 1 - -@nowarn("wat?") // warning (typer, invalid filter) -def t2 = { 1; 2 } // warning (the invalid nowarn doesn't silence anything) - -@nowarn(t1a.toString) // warning (typer, argument not a compile-time constant) -def t2a = { 1; 2 } // warning (invalid nowarn doesn't silence) - -object o: - final val const = "msg=try" - inline def inl = "msg=try" - -@nowarn(o.const) def t2c = try 1 // no warning -@nowarn(o.inl) def t2d = try 1 // two warnings (`inl` is not a compile-time constant) - -@nowarn("id=E129") def t3a = { 1; 2 } -@nowarn("name=PureExpressionInStatementPosition") def t3b = { 1; 2 } - -@nowarn("id=E002") def t4a = try 1 -@nowarn("id=E2") def t4b = try 1 -@nowarn("id=2") def t4c = try 1 -@nowarn("id=1") def t4d = try 1 // error and warning (unused nowarn, wrong id) - -@nowarn("verbose") def t5 = try 1 // warning with details - -@deprecated def f = 0 - -def t6a = f // warning (refchecks, deprecation) -@nowarn("cat=deprecation") def t6b = f -@nowarn("msg=deprecated") def t6c = f -@nowarn("msg=fish") def t6d = f // error (unused nowarn), warning (deprecation) -@nowarn("") def t6e = f -@nowarn def t6f = f - -def t7a = f: @nowarn("cat=deprecation") -def t7b = f - : @nowarn("msg=deprecated") -def t7c = f // warning (deprecation) - : @nowarn("msg=fish") // error (unused nowarn) -def t7d = f: @nowarn("") -def t7e = f: @nowarn - -def t8a(x: Any) = x match - case _: List[Int] => 0 // warning (patmat, unchecked) - case _ => 1 - -@nowarn("cat=unchecked") def t8(x: Any) = x match - case _: List[Int] => 0 - case _ => 1 - -@nowarn def t9a = { 1: @nowarn; 2 } // error (outer @nowarn is unused) -@nowarn def t9b = { 1: Int @nowarn; 2 } // error (inner @nowarn is unused, it covers the type, not the expression) - -class ann(a: Any) extends Annotation - -@ann(f) def t10a = 0 // should be a deprecation warning, but currently isn't -@nowarn @ann(f) def t10b = 0 // error (unused nowarn) -@ann(f: @nowarn) def t10c = 0 // error (unused nowarn), should be silent - -def forceCompletionOfI1a = (new I1a).m -@nowarn class I1a { // error (unused nowarn) - @nowarn def m = { 1; 2 } -} - -// completion during type checking -@nowarn class I1b { // error (unused nowarn) - @nowarn def m = { 1; 2 } -} - -@nowarn class I1c { - def m = { 1; 2 } -} - -trait T { - @nowarn val t1 = { 0; 1 } -} - -class K extends T diff --git a/tests/neg/overload_repeated.scala b/tests/neg/overload_repeated.scala index 8e2b34bc411f..6d6fca2c84a1 100644 --- a/tests/neg/overload_repeated.scala +++ b/tests/neg/overload_repeated.scala @@ -2,5 +2,11 @@ object Test { def bar1(x: Any) = 1 def bar1(x: String*) = 2 + // (b) isn't as good as (a) because of ints + // (a) isn't as good as (b) because T in Class[? <: T] may not subtype of Number + def bar2[T](a: Class[? <: T]): Int = 1 // (a) + def bar2[T <: Number](a: Class[T], ints: Int*): Int = 2 // (b) + assert(bar1("") == 1) // error: ambiguous in Scala 2 and Scala 3 + assert(bar2(classOf[Integer]) == 1) // error: ambiguous in Scala 2 and Scala 3 } diff --git a/tests/neg/override-erasure-clash.check b/tests/neg/override-erasure-clash.check index e936a8de2397..136eb0b4885f 100644 --- a/tests/neg/override-erasure-clash.check +++ b/tests/neg/override-erasure-clash.check @@ -4,4 +4,6 @@ | Name clash between defined and inherited member: | def f(): Int in class A at line 3 and | def g(): Int in class B at line 5 - | have the same name and type after erasure. + | have the same name and type (): Int after erasure. + | + | longer explanation available when compiling with `-explain` 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/polymorphic-functions1.check b/tests/neg/polymorphic-functions1.check index 7374075de072..01b21fe75520 100644 --- a/tests/neg/polymorphic-functions1.check +++ b/tests/neg/polymorphic-functions1.check @@ -1,7 +1,7 @@ --- [E007] Type Mismatch Error: tests/neg/polymorphic-functions1.scala:1:53 --------------------------------------------- +-- [E007] Type Mismatch Error: tests/neg/polymorphic-functions1.scala:1:37 --------------------------------------------- 1 |val f: [T] => (x: T) => x.type = [T] => (x: Int) => x // error - | ^ - | Found: [T] => (x: Int) => Int - | Required: [T] => (x: T) => x.type + | ^^^^^^^^^^^^^^^^^^^^ + | Found: [T] => (x: Int) => Int + | Required: [T] => (x: T) => x.type | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/reassignment.check b/tests/neg/reassignment.check new file mode 100644 index 000000000000..6b171d326b68 --- /dev/null +++ b/tests/neg/reassignment.check @@ -0,0 +1,53 @@ +-- [E052] Type Error: tests/neg/reassignment.scala:4:10 ---------------------------------------------------------------- +4 |val y = x = 0 // error + | ^^^^^ + | Reassignment to val x + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | You can not assign a new value to x as values can't be changed. + | Reassigment is only permitted if the variable is declared with `var`. + --------------------------------------------------------------------------------------------------------------------- +-- [E052] Type Error: tests/neg/reassignment.scala:5:19 ---------------------------------------------------------------- +5 |val z: Boolean = x = 0 // error + | ^^^^^ + | Reassignment to val x. Maybe you meant to write an equality test using `==`? + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | An equality test is written "x == y" using `==`. A single `=` stands + | for assigment, where a variable on the left gets a new value on the right. + | This is only permitted if the variable is declared with `var`. + --------------------------------------------------------------------------------------------------------------------- +-- [E052] Type Error: tests/neg/reassignment.scala:6:13 ---------------------------------------------------------------- +6 |def f = if x = 0 then 1 else 2 // error + | ^^^^^ + | Reassignment to val x. Maybe you meant to write an equality test using `==`? + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | An equality test is written "x == y" using `==`. A single `=` stands + | for assigment, where a variable on the left gets a new value on the right. + | This is only permitted if the variable is declared with `var`. + --------------------------------------------------------------------------------------------------------------------- +-- [E052] Type Error: tests/neg/reassignment.scala:8:4 ----------------------------------------------------------------- +8 | x = 0 // error + | ^^^^^ + | Reassignment to val x + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | You can not assign a new value to x as values can't be changed. + | Reassigment is only permitted if the variable is declared with `var`. + --------------------------------------------------------------------------------------------------------------------- +-- [E052] Type Error: tests/neg/reassignment.scala:9:4 ----------------------------------------------------------------- +9 | x = 2 // error + | ^^^^^ + | Reassignment to val x. Maybe you meant to write an equality test using `==`? + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | An equality test is written "x == y" using `==`. A single `=` stands + | for assigment, where a variable on the left gets a new value on the right. + | This is only permitted if the variable is declared with `var`. + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/reassignment.scala b/tests/neg/reassignment.scala new file mode 100644 index 000000000000..c831c12dba5a --- /dev/null +++ b/tests/neg/reassignment.scala @@ -0,0 +1,10 @@ +//> using options -explain + +val x = 1 +val y = x = 0 // error +val z: Boolean = x = 0 // error +def f = if x = 0 then 1 else 2 // error +def g: Boolean = + x = 0 // error + x = 2 // error + diff --git a/tests/neg/rightassoc-extmethod.check b/tests/neg/rightassoc-extmethod.check index a1d2328ed2ff..80f02851474c 100644 --- a/tests/neg/rightassoc-extmethod.check +++ b/tests/neg/rightassoc-extmethod.check @@ -1,8 +1,4 @@ --- Error: tests/neg/rightassoc-extmethod.scala:1:23 -------------------------------------------------------------------- -1 |extension (x: Int) def +: (using String): Int = x // error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | right-associative extension method cannot start with using clause -- Error: tests/neg/rightassoc-extmethod.scala:2:23 -------------------------------------------------------------------- 2 |extension (x: Int) def *: (y: Int, z: Int) = x // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | right-associative extension method must start with a single parameter + | right-associative extension method must start with a single parameter, consider a tupled parameter instead diff --git a/tests/neg/rightassoc-extmethod.scala b/tests/neg/rightassoc-extmethod.scala index 4a136ca6eac3..9fbea9d4a04a 100644 --- a/tests/neg/rightassoc-extmethod.scala +++ b/tests/neg/rightassoc-extmethod.scala @@ -1,3 +1,2 @@ -extension (x: Int) def +: (using String): Int = x // error +extension (x: Int) def +: (using String): Int = x extension (x: Int) def *: (y: Int, z: Int) = x // error - diff --git a/tests/neg/serialversionuid-not-const.scala b/tests/neg/serialversionuid-not-const.scala index 1b24112dbf70..21b304f7c93c 100644 --- a/tests/neg/serialversionuid-not-const.scala +++ b/tests/neg/serialversionuid-not-const.scala @@ -1,6 +1,6 @@ @SerialVersionUID(13l.toLong) class C1 extends Serializable // error @SerialVersionUID(13l) class C2 extends Serializable // OK -@SerialVersionUID(13.asInstanceOf[Long]) class C3 extends Serializable // error +@SerialVersionUID(13.asInstanceOf[Long]) class C3 extends Serializable // OK was error @SerialVersionUID(Test.bippy) class C4 extends Serializable // error object Test { diff --git a/tests/neg/stackoverflowmacro.check b/tests/neg/stackoverflowmacro.check new file mode 100644 index 000000000000..8451298c5659 --- /dev/null +++ b/tests/neg/stackoverflowmacro.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/stackoverflowmacro/2_Use.scala:2:13 ---------------------------------------------------------------- +2 | def Test = scala.compiletime.testing.typeCheckErrors("stackOverflowMacro") // error + | ^ + | String constant is too long for the JVM diff --git a/tests/neg/stackoverflowmacro/1_Macro.scala b/tests/neg/stackoverflowmacro/1_Macro.scala new file mode 100644 index 000000000000..b6c0cca8e8ce --- /dev/null +++ b/tests/neg/stackoverflowmacro/1_Macro.scala @@ -0,0 +1,8 @@ +import scala.quoted.* + +transparent inline def stackOverflowMacro: Int = ${ StackOverflowMacro.impl } + +object StackOverflowMacro: + def impl(using Quotes): Expr[Int] = '{ ${impl} + ${impl} } + + diff --git a/tests/neg/stackoverflowmacro/2_Use.scala b/tests/neg/stackoverflowmacro/2_Use.scala new file mode 100644 index 000000000000..7e2039d1a040 --- /dev/null +++ b/tests/neg/stackoverflowmacro/2_Use.scala @@ -0,0 +1,3 @@ +object Use { + def Test = scala.compiletime.testing.typeCheckErrors("stackOverflowMacro") // error +} diff --git a/tests/neg/t4727.check b/tests/neg/t4727.check new file mode 100644 index 000000000000..a04d6d29b96a --- /dev/null +++ b/tests/neg/t4727.check @@ -0,0 +1,84 @@ +-- [E007] Type Mismatch Error: tests/neg/t4727.scala:10:8 -------------------------------------------------------------- +10 | new C[Int] // error + | ^^^^^^ + | Found: Null + | Required: Int + | Error occurred in an application involving default arguments. + | Note that implicit conversions were not tried because the result of an implicit conversion + | must be more specific than Int + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | + | Tree: + | + | C.$lessinit$greater$default$1[Int] + | + | I tried to show that + | Null + | conforms to + | Int + | but none of the attempts shown below succeeded: + | + | ==> Null <: Int = false + | + | The tests were made under the empty constraint + | + | Expanded application: new C[Int](C.$lessinit$greater$default$1[Int]) + -------------------------------------------------------------------------------------------------------------------- +-- [E007] Type Mismatch Error: tests/neg/t4727.scala:11:4 -------------------------------------------------------------- +11 | f[Int]() // error + | ^^^^^^ + | Found: Null + | Required: Int + | Error occurred in an application involving default arguments. + | Note that implicit conversions were not tried because the result of an implicit conversion + | must be more specific than Int + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | + | Tree: + | + | t4727$package.f$default$1[Int] + | + | I tried to show that + | Null + | conforms to + | Int + | but none of the attempts shown below succeeded: + | + | ==> Null <: Int = false + | + | The tests were made under the empty constraint + | + | Expanded application: f[Int](t4727$package.f$default$1[Int]) + -------------------------------------------------------------------------------------------------------------------- +-- [E007] Type Mismatch Error: tests/neg/t4727.scala:12:4 -------------------------------------------------------------- +12 | g[Int, Int](42)(y = 27) // error + | ^^^^^^^^^^^^^^^ + | Found: Null + | Required: Int + | Error occurred in an application involving default arguments. + | Note that implicit conversions were not tried because the result of an implicit conversion + | must be more specific than Int + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | + | Tree: + | + | t4727$package.g$default$2[Int, Int](42) + | + | I tried to show that + | Null + | conforms to + | Int + | but none of the attempts shown below succeeded: + | + | ==> Null <: Int = false + | + | The tests were made under the empty constraint + | + | Expanded application: g[Int, Int](42)(t4727$package.g$default$2[Int, Int](42), y = 27) + -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/t4727.scala b/tests/neg/t4727.scala new file mode 100644 index 000000000000..a9acf8f322b4 --- /dev/null +++ b/tests/neg/t4727.scala @@ -0,0 +1,14 @@ +//> using options -explain + +class C[T](x : T = null) + +def f[A](x: A = null) = x +def g[A <: Int, B](i: A)(x: B = null, y: Int) = x.toString * (i + y) + +object Test { + def main(args: Array[String]): Unit = { + new C[Int] // error + f[Int]() // error + g[Int, Int](42)(y = 27) // error + } +} diff --git a/tests/neg/t5702-neg-bad-and-wild.check b/tests/neg/t5702-neg-bad-and-wild.check index 261b2a9e44d1..da6d551bd3b3 100644 --- a/tests/neg/t5702-neg-bad-and-wild.check +++ b/tests/neg/t5702-neg-bad-and-wild.check @@ -46,13 +46,6 @@ | no pattern match extractor named * was found | | longer explanation available when compiling with `-explain` --- Warning: tests/neg/t5702-neg-bad-and-wild.scala:13:22 --------------------------------------------------------------- -13 | case List(1, _*3:) => // error // error - | ^ - | Type ascriptions after patterns other than: - | * variable pattern, e.g. `case x: String =>` - | * number literal pattern, e.g. `case 10.5: Double =>` - | are no longer supported. Remove the type ascription or move it to a separate variable pattern. -- Warning: tests/neg/t5702-neg-bad-and-wild.scala:22:20 --------------------------------------------------------------- 22 | val K(x @ _*) = k | ^ 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/neg/toplevel-cyclic/defs_1.scala b/tests/neg/toplevel-cyclic/defs_1.scala index 34b0475066b0..f40fa5bdebdd 100644 --- a/tests/neg/toplevel-cyclic/defs_1.scala +++ b/tests/neg/toplevel-cyclic/defs_1.scala @@ -1 +1 @@ -type A = B +type A = B // error: recursion limit exceeded diff --git a/tests/neg/toplevel-cyclic/moredefs_1.scala b/tests/neg/toplevel-cyclic/moredefs_1.scala index 3c8c3de93aa7..91360d0c0f4d 100644 --- a/tests/neg/toplevel-cyclic/moredefs_1.scala +++ b/tests/neg/toplevel-cyclic/moredefs_1.scala @@ -1 +1 @@ -type B = A // error: recursion limit exceeded +type B = A diff --git a/tests/neg/toplevel-overload/moredefs_1.scala b/tests/neg/toplevel-overload/moredefs_1.scala index 5ba8cfc52078..74cdd3ac1bb6 100644 --- a/tests/neg/toplevel-overload/moredefs_1.scala +++ b/tests/neg/toplevel-overload/moredefs_1.scala @@ -2,4 +2,4 @@ trait B def f(x: B) = s"B" // error: has already been compiled -private def g(): Unit = () // OK, since it is private \ No newline at end of file +private def g(): Unit = () // error: has already been compiled (def is visible in package) \ No newline at end of file diff --git a/tests/neg/type-params.check b/tests/neg/type-params.check new file mode 100644 index 000000000000..eb24e1c4bd73 --- /dev/null +++ b/tests/neg/type-params.check @@ -0,0 +1,109 @@ +-- [E053] Type Error: tests/neg/type-params.scala:14:13 ---------------------------------------------------------------- +14 | type t = x[x] // error + | ^^^^ + | x does not take type parameters + | + | longer explanation available when compiling with `-explain` +-- [E053] Type Error: tests/neg/type-params.scala:16:13 ---------------------------------------------------------------- +16 | val foo: s[Int] // error + | ^^^^^^ + | s does not take type parameters + | + | longer explanation available when compiling with `-explain` +-- Error: tests/neg/type-params.scala:17:35 ---------------------------------------------------------------------------- +17 | class WellKindedWrongSyntax[s <: List] // error + | ^^^^ + | type List takes type parameters +-- Error: tests/neg/type-params.scala:21:26 ---------------------------------------------------------------------------- +21 | object mp extends Monad[Tuple2] // error + | ^^^^^^ + | Type argument [T1, T2] =>> (T1, T2) does not have the same kind as its bound [x] +-- Error: tests/neg/type-params.scala:24:26 ---------------------------------------------------------------------------- +24 | trait ms1 extends Monad[String] // error + | ^^^^^^ + | Type argument String does not have the same kind as its bound [x] +-- Error: tests/neg/type-params.scala:25:29 ---------------------------------------------------------------------------- +25 | trait ms2[t] extends Monad[t] // error + | ^ + | Type argument t does not have the same kind as its bound [x] +-- Error: tests/neg/type-params.scala:26:35 ---------------------------------------------------------------------------- +26 | trait ms3[m[_], t] extends Monad[m[t]] // error -- added to check regression on bug + | ^^^^ + | Type argument m[t] does not have the same kind as its bound [x] +-- Error: tests/neg/type-params.scala:31:31 ---------------------------------------------------------------------------- +31 | trait Bar2[m[_]] extends Foo[m] // error check that m is properly recognized as kind *->*, while * is expected + | ^ + | Type argument m does not have the same kind as its bound +-- [E053] Type Error: tests/neg/type-params.scala:37:20 ---------------------------------------------------------------- +37 |class t1701 extends java.lang.Cloneable[String, Option, Int] // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Cloneable does not take type parameters + | + | longer explanation available when compiling with `-explain` +-- [E053] Type Error: tests/neg/type-params.scala:39:24 ---------------------------------------------------------------- +39 |trait t0842[T] { def m: this.type[T] = this } // error + | ^^^^^^^^^^^^ + | (t0842.this : t0842[T]) does not take type parameters + | + | longer explanation available when compiling with `-explain` +-- [E134] Type Error: tests/neg/type-params.scala:45:10 ---------------------------------------------------------------- +45 | println(a[A]) // error + | ^ + | None of the overloaded alternatives of method apply in trait Function1 with types + | (v1: t278.this.A): Unit + | (): Unit + | match type arguments [t278.this.A] and expected type Any +-- [E120] Naming Error: tests/neg/type-params.scala:44:6 --------------------------------------------------------------- +44 | def a = (p: A) => () // error + | ^ + | Conflicting definitions: + | def a: () => Unit in trait t278 at line 43 and + | def a: t278.this.A => Unit in trait t278 at line 44 + | have the same type (): Function1 after erasure. + | + | Consider adding a @targetName annotation to one of the conflicting definitions + | for disambiguation. + | + | longer explanation available when compiling with `-explain` +-- Error: tests/neg/type-params.scala:4:7 ------------------------------------------------------------------------------ +4 | "".==[Int] // error + | ^^^^^^^^^^ + | method == in class Any does not take type parameters +-- Error: tests/neg/type-params.scala:5:17 ----------------------------------------------------------------------------- +5 | ("": AnyRef).==[Int] // error + | ^^^^^^^^^^^^^^^^^^^^ + | method == in class Any does not take type parameters +-- Error: tests/neg/type-params.scala:6:17 ----------------------------------------------------------------------------- +6 | ("": Object).==[Int] // error + | ^^^^^^^^^^^^^^^^^^^^ + | method == in class Any does not take type parameters +-- Error: tests/neg/type-params.scala:9:14 ----------------------------------------------------------------------------- +9 | classOf[Int][Int] // error + | ^^^^^^^^^^^^^^^^^ + | illegal repeated type application + | You might have meant something like: + | classOf[Int, Int] +-- [E140] Cyclic Error: tests/neg/type-params.scala:34:11 -------------------------------------------------------------- +34 | def g[X, A[X] <: A[X]](x: A[X]) = x // error // error + | ^ + | illegal cyclic type reference: upper bound ... of type A refers back to the type itself + | + | Run with -explain-cyclic for more details. +-- [E053] Type Error: tests/neg/type-params.scala:34:28 ---------------------------------------------------------------- +34 | def g[X, A[X] <: A[X]](x: A[X]) = x // error // error + | ^^^^ + | A does not take type parameters + | + | longer explanation available when compiling with `-explain` +-- [E140] Cyclic Error: tests/neg/type-params.scala:35:8 --------------------------------------------------------------- +35 | def f[C[X] <: C[X]](l: C[_]) = l.x // error // error + | ^ + | illegal cyclic type reference: upper bound ... of type C refers back to the type itself + | + | Run with -explain-cyclic for more details. +-- [E053] Type Error: tests/neg/type-params.scala:35:25 ---------------------------------------------------------------- +35 | def f[C[X] <: C[X]](l: C[_]) = l.x // error // error + | ^^^^ + | C does not take type parameters + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/type-params.scala b/tests/neg/type-params.scala new file mode 100644 index 000000000000..3a5279868405 --- /dev/null +++ b/tests/neg/type-params.scala @@ -0,0 +1,45 @@ + +def `t8219 Any == is not overloaded in error message`: Unit = + // Scala 2 once defined AnyRef == and Any == as overloaded. Spec defines only Any ==. + "".==[Int] // error + ("": AnyRef).==[Int] // error + ("": Object).==[Int] // error + +def `extra type arg to class literal` = + classOf[Int][Int] // error + +class `various ancient kindedness`: + + class WellKinded[x]: + type t = x[x] // error + trait WellKindedBounded[s <: Throwable]: + val foo: s[Int] // error + class WellKindedWrongSyntax[s <: List] // error + + class Monad[m[x]] + + object mp extends Monad[Tuple2] // error + + // expecting types of kind *->* + trait ms1 extends Monad[String] // error + trait ms2[t] extends Monad[t] // error + trait ms3[m[_], t] extends Monad[m[t]] // error -- added to check regression on bug + + // expecting types of kind * + trait Foo[x] + trait Bar1[m[_]] extends Foo[m[Int]] // check that m[Int] is properly recognized as kind-* + trait Bar2[m[_]] extends Foo[m] // error check that m is properly recognized as kind *->*, while * is expected + +def `t2918 t5093 detect cyclic error` = + def g[X, A[X] <: A[X]](x: A[X]) = x // error // error + def f[C[X] <: C[X]](l: C[_]) = l.x // error // error + +class t1701 extends java.lang.Cloneable[String, Option, Int] // error + +trait t0842[T] { def m: this.type[T] = this } // error + +trait t278: + class A + def a = () => () + def a = (p: A) => () // error + println(a[A]) // error diff --git a/tests/neg/typetest.scala b/tests/neg/typetest.scala deleted file mode 100644 index a49e30054b9f..000000000000 --- a/tests/neg/typetest.scala +++ /dev/null @@ -1,7 +0,0 @@ -object Test { - - val i: Int = 1 - - println(i.isInstanceOf[Object]) // error -} - diff --git a/tests/neg/yimports-custom.check b/tests/neg/yimports-custom.check index 6ed2eb8b1df3..af6b8792f611 100644 --- a/tests/neg/yimports-custom.check +++ b/tests/neg/yimports-custom.check @@ -1,4 +1,3 @@ - -- [E006] Not Found Error: tests/neg/yimports-custom/C_2.scala:5:16 ---------------------------------------------------- 5 | def greet() = println("hello, world!") // error | ^^^^^^^ diff --git a/tests/neg/yimports-stable.check b/tests/neg/yimports-stable.check index 6a0b059de908..ba87116955c2 100644 --- a/tests/neg/yimports-stable.check +++ b/tests/neg/yimports-stable.check @@ -1,4 +1,3 @@ - error: bad preamble import hello.world.potions -- [E006] Not Found Error: tests/neg/yimports-stable/C_2.scala:4:9 ----------------------------------------------------- 4 | val v: Numb = magic // error // error diff --git a/tests/new/test.scala b/tests/new/test.scala new file mode 100644 index 000000000000..a55b76a6ed30 --- /dev/null +++ b/tests/new/test.scala @@ -0,0 +1,7 @@ +object e: + def foldRL(f: Int => Int)(g: String => Int) = ??? + + +val xs = e.foldRL(i => i + 1)(s => s.length) + + diff --git a/tests/patmat/patmat-lazy-nothing-not-exhaustive.check b/tests/patmat/patmat-lazy-nothing-not-exhaustive.check new file mode 100644 index 000000000000..aef9e1aed027 --- /dev/null +++ b/tests/patmat/patmat-lazy-nothing-not-exhaustive.check @@ -0,0 +1 @@ +8: Pattern Match Exhaustivity: Bar() \ No newline at end of file diff --git a/tests/patmat/patmat-lazy-nothing-not-exhaustive.scala b/tests/patmat/patmat-lazy-nothing-not-exhaustive.scala new file mode 100644 index 000000000000..b66eb63c110c --- /dev/null +++ b/tests/patmat/patmat-lazy-nothing-not-exhaustive.scala @@ -0,0 +1,10 @@ +sealed trait Adt +case class Foo() extends Adt +case class Bar() extends Adt { + lazy val x: Nothing = throw new Exception() +} + +def shouldThrowAWarning(x: Adt) = + x match { // warn + case Foo() => "Foo" + } diff --git a/tests/patmat/t4408.scala b/tests/patmat/t4408.scala index 419b6636971e..a50cb175f80a 100644 --- a/tests/patmat/t4408.scala +++ b/tests/patmat/t4408.scala @@ -10,7 +10,7 @@ object Test { for(i <- head){ print(i) } - println + println() println(s) } } diff --git a/tests/pending/pos/t5777.scala b/tests/pending/pos/t5777.scala index 24cea3616346..0d703b5fcce0 100644 --- a/tests/pending/pos/t5777.scala +++ b/tests/pending/pos/t5777.scala @@ -23,7 +23,7 @@ object MyApp { val r1 = new Poly[BigInt.type](BigInt) (null.asInstanceOf[BigInt.E] : r1.ring.E) - // Oddly, -Xprint:typer reports that r and r1 have the same inferred type. + // Oddly, -Vprint:typer reports that r and r1 have the same inferred type. // // private[this] val r: Poly[BigInt.type] = new Poly[BigInt.type](BigInt); // def r: Poly[BigInt.type] = MyApp.this.r; diff --git a/tests/pos-custom-args/captures/i24456.scala b/tests/pos-custom-args/captures/i24456.scala new file mode 100644 index 000000000000..e70145325113 --- /dev/null +++ b/tests/pos-custom-args/captures/i24456.scala @@ -0,0 +1,14 @@ +// Similar to i24207 + +class Generator: + private def generateTable(table: Table) = + val (ownRelations, unusedTerm) = calculateOwnRelations(table) + None + + private def calculateOwnRelations(table: Table) = + val ownRelations = table.relations.filter(_.association.isDefined) + (ownRelations, Nil) + +case class Table(relations: Seq[TableRelation]) +case class TableRelation(association: Option[Association]) +trait Association \ No newline at end of file diff --git a/tests/pos-custom-args/captures/i25497.scala b/tests/pos-custom-args/captures/i25497.scala new file mode 100644 index 000000000000..37383a14ec91 --- /dev/null +++ b/tests/pos-custom-args/captures/i25497.scala @@ -0,0 +1,7 @@ +enum Result[+T, +E]: + case Ok[+T](value: T) extends Result[T, Nothing] + case Err[+E](error: E) extends Result[Nothing, E] + +def catchException(body: => Int): Result[Int, Null] = + try Result.Ok(body) + catch case _: Throwable => Result.Err(null) \ 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-macros/i13319/Macro_1.scala b/tests/pos-macros/i13319/Macro_1.scala new file mode 100644 index 000000000000..8a666f5c4849 --- /dev/null +++ b/tests/pos-macros/i13319/Macro_1.scala @@ -0,0 +1,12 @@ +import scala.quoted._ +object Macro: + inline def apply[A]: Unit = ${impl[A]} + + private def impl[A: Type](using Quotes): Expr[String] = + import quotes.reflect._ + val t = TypeRepr.of[A] + Expr.ofList(t.baseClasses.drop(1).filter(_.flags.is(Flags.Trait)).map { baseSymbol => + t.memberType(baseSymbol).asType match { case '[t] => 42} + Expr("") + }) + Expr("") diff --git a/tests/pos-macros/i13319/Test_2.scala b/tests/pos-macros/i13319/Test_2.scala new file mode 100644 index 000000000000..e4ae7c834b71 --- /dev/null +++ b/tests/pos-macros/i13319/Test_2.scala @@ -0,0 +1 @@ +@main def Test = Macro[Option[String]] diff --git a/tests/pos-macros/i13461-d/Macro_1.scala b/tests/pos-macros/i13461-d/Macro_1.scala new file mode 100644 index 000000000000..52e83cee0d2a --- /dev/null +++ b/tests/pos-macros/i13461-d/Macro_1.scala @@ -0,0 +1,8 @@ +import scala.quoted.* +opaque type MyOpaque = Int +object MyOpaque: + val one: MyOpaque = 1 + transparent inline def apply(): MyOpaque = ${ applyMacro } + private def applyMacro(using Quotes): Expr[MyOpaque] = + import quotes.reflect.* + '{ one } diff --git a/tests/pos-macros/i13461-d/Test_2.scala b/tests/pos-macros/i13461-d/Test_2.scala new file mode 100644 index 000000000000..ac5a1745eeb9 --- /dev/null +++ b/tests/pos-macros/i13461-d/Test_2.scala @@ -0,0 +1,8 @@ +trait Leak[T]: + type Out +given [T]: Leak[T] with + type Out = T +extension [T](t: T)(using l: Leak[T]) def leak: l.Out = ??? + +val x = MyOpaque().leak +val shouldWork = summon[x.type <:< MyOpaque] diff --git a/tests/pos-macros/i13821-a/Macro.scala b/tests/pos-macros/i13821-a/Macro.scala new file mode 100644 index 000000000000..3583a44ffddc --- /dev/null +++ b/tests/pos-macros/i13821-a/Macro.scala @@ -0,0 +1,6 @@ +trait Conversions: + given conv(using DFBits.Candidate): Conversion[Int, DFVal] = ??? + +import scala.quoted.* +transparent inline def f: Short = ${ getWidthMacro } +private def getWidthMacro(using Quotes): Expr[Short] = '{ ??? } diff --git a/tests/pos-macros/i13821-a/Test.scala b/tests/pos-macros/i13821-a/Test.scala new file mode 100644 index 000000000000..86f5a6289a80 --- /dev/null +++ b/tests/pos-macros/i13821-a/Test.scala @@ -0,0 +1,12 @@ +type DFBits = Long +object DFBits: + def a: Unit = f // forces suspension of this compilation unit in typer + def b: DFVal = 2 // uses implicit conversion `DFVal.conv` + + trait Candidate + object Candidate: + given candidate: Candidate = ??? // completed in run 3 but created in run 2 +end DFBits + +trait DFVal +object DFVal extends Conversions diff --git a/tests/pos-macros/i13821-b/Macro.scala b/tests/pos-macros/i13821-b/Macro.scala new file mode 100644 index 000000000000..c1c9c5e17851 --- /dev/null +++ b/tests/pos-macros/i13821-b/Macro.scala @@ -0,0 +1,7 @@ +import outer._ +trait Conversions: + given conv(using DFBits.Candidate): Conversion[Int, DFVal] = ??? + +import scala.quoted.* +transparent inline def f: Short = ${ getWidthMacro } +private def getWidthMacro(using Quotes): Expr[Short] = '{ ??? } diff --git a/tests/pos-macros/i13821-b/Test.scala b/tests/pos-macros/i13821-b/Test.scala new file mode 100644 index 000000000000..5c9b8755a33c --- /dev/null +++ b/tests/pos-macros/i13821-b/Test.scala @@ -0,0 +1,14 @@ +type outer = Int +object outer: + type DFBits = Long + object DFBits: + def a: Unit = f // forces suspension of this compilation unit in typer + def b: DFVal = 2 // uses implicit conversion `DFVal.conv` + + trait Candidate + object Candidate: + given candidate: Candidate = ??? // completed in run 3 but created in run 2 + end DFBits + + trait DFVal + object DFVal extends Conversions diff --git a/tests/pos-macros/i19351/A.scala b/tests/pos-macros/i19351/A.scala new file mode 100644 index 000000000000..0b2c7acc3797 --- /dev/null +++ b/tests/pos-macros/i19351/A.scala @@ -0,0 +1,6 @@ +// object A: + val x: Int = 1 + inline def myMacro(): x.type = ${myMacroExpr} + def test = myMacro() + +@main def main() = () \ No newline at end of file diff --git a/tests/neg/i19351/B.scala b/tests/pos-macros/i19351/B.scala similarity index 84% rename from tests/neg/i19351/B.scala rename to tests/pos-macros/i19351/B.scala index 8c8c071f3607..a22b4d5c51b6 100644 --- a/tests/neg/i19351/B.scala +++ b/tests/pos-macros/i19351/B.scala @@ -1,3 +1,3 @@ import scala.quoted.* -//import A.* +// import A.* def myMacroExpr(using Quotes): Expr[x.type] = '{???} \ No newline at end of file diff --git a/tests/neg/i19351a/Macro.scala b/tests/pos-macros/i19351a/Macro.scala similarity index 100% rename from tests/neg/i19351a/Macro.scala rename to tests/pos-macros/i19351a/Macro.scala diff --git a/tests/neg/i19351a/Test.scala b/tests/pos-macros/i19351a/Test.scala similarity index 87% rename from tests/neg/i19351a/Test.scala rename to tests/pos-macros/i19351a/Test.scala index 84fb6ca4ae78..7f9e6dbc9786 100644 --- a/tests/neg/i19351a/Test.scala +++ b/tests/pos-macros/i19351a/Test.scala @@ -5,7 +5,7 @@ type Bool = [R] => (R, R) => R val True: Bool = [R] => (t: R, _: R) => t val False: Bool = [R] => (_: R, f: R) => f -inline def not(b: Bool): Bool = ${notMacro('b)} // error +inline def not(b: Bool): Bool = ${notMacro('b)} inline def show(b: Bool): String = ${showMacro('b)} //inline def not(b: Bool): Bool = ${foldMacro('b, 'False, 'True)} //inline def show(b: Bool): String = ${foldMacro('b, '{"TRUE"}, '{"FALSE"})} diff --git a/tests/pos-macros/i20349a/Macro_1.scala b/tests/pos-macros/i20349a/Macro_1.scala new file mode 100644 index 000000000000..41c5083b328c --- /dev/null +++ b/tests/pos-macros/i20349a/Macro_1.scala @@ -0,0 +1,11 @@ +import scala.quoted.* + +object Macros { + def valuesImpl[A: Type](using Quotes): Expr[Any] = { + import quotes.reflect.* + val symbol = TypeRepr.of[A].typeSymbol.fieldMember("value") + Ref(symbol).asExprOf[Any] + } + + transparent inline def values[A]: Any = ${ valuesImpl[A] } +} diff --git a/tests/pos-macros/i20349a/Test_2.scala b/tests/pos-macros/i20349a/Test_2.scala new file mode 100644 index 000000000000..4569c501e055 --- /dev/null +++ b/tests/pos-macros/i20349a/Test_2.scala @@ -0,0 +1,16 @@ + +class Cls{ + object a { + object domain { + val value = "" + } + } + Macros.values[a.domain.type] +} + +object Test { + lazy val script = new Cls() + def main(args: Array[String]): Unit = + val _ = script.hashCode() + ??? +} diff --git a/tests/pos-macros/i20349b/Macro_1.scala b/tests/pos-macros/i20349b/Macro_1.scala new file mode 100644 index 000000000000..413274b4b516 --- /dev/null +++ b/tests/pos-macros/i20349b/Macro_1.scala @@ -0,0 +1,43 @@ +import scala.quoted.* + +object Macros { + + + def valuesImpl[A: Type](using quotes: Quotes): Expr[List[A]] = { + import quotes.*, quotes.reflect.* + + extension (sym: Symbol) + def isPublic: Boolean = !sym.isNoSymbol && + !(sym.flags.is(Flags.Private) || sym.flags.is(Flags.PrivateLocal) || sym.flags.is(Flags.Protected) || + sym.privateWithin.isDefined || sym.protectedWithin.isDefined) + + def isSealed[A: Type]: Boolean = + TypeRepr.of[A].typeSymbol.flags.is(Flags.Sealed) + + def extractSealedSubtypes[A: Type]: List[Type[?]] = { + def extractRecursively(sym: Symbol): List[Symbol] = + if sym.flags.is(Flags.Sealed) then sym.children.flatMap(extractRecursively) + else if sym.flags.is(Flags.Enum) then List(sym.typeRef.typeSymbol) + else if sym.flags.is(Flags.Module) then List(sym.typeRef.typeSymbol.moduleClass) + else List(sym) + + extractRecursively(TypeRepr.of[A].typeSymbol).distinct.map(typeSymbol => + typeSymbol.typeRef.asType + ) + } + + if isSealed[A] then { + val refs = extractSealedSubtypes[A].flatMap { tpe => + val sym = TypeRepr.of(using tpe).typeSymbol + val isCaseVal = sym.isPublic && sym.flags + .is(Flags.Case | Flags.Enum) && (sym.flags.is(Flags.JavaStatic) || sym.flags.is(Flags.StableRealizable)) + + if (isCaseVal) then List(Ref(sym).asExprOf[A]) + else Nil + } + Expr.ofList(refs) + } else '{ Nil } + } + + inline def values[A]: List[A] = ${ valuesImpl[A] } +} diff --git a/tests/pos-macros/i20349b/Test_2.scala b/tests/pos-macros/i20349b/Test_2.scala new file mode 100644 index 000000000000..a392a636dc44 --- /dev/null +++ b/tests/pos-macros/i20349b/Test_2.scala @@ -0,0 +1,14 @@ +class Test { + object domain { + enum PaymentMethod: + case PayPal(email: String) + case Card(digits: Long, name: String) + case Cash + } + println(Macros.values[domain.PaymentMethod]) +} +object Test { + lazy val script = new Test() + def main(args: Array[String]): Unit = + val _ = script.hashCode() +} diff --git a/tests/pos-macros/i20449/Macro.scala b/tests/pos-macros/i20449/Macro.scala new file mode 100644 index 000000000000..56c9a625e894 --- /dev/null +++ b/tests/pos-macros/i20449/Macro.scala @@ -0,0 +1,3 @@ +import scala.quoted.* +transparent inline def getTypeInfo[T]() = ${ getTypeInfoImpl[T] } +def getTypeInfoImpl[T: Type](using ctx: Quotes): Expr[Unit] = '{ () } diff --git a/tests/pos-macros/i20449/Main.scala b/tests/pos-macros/i20449/Main.scala new file mode 100644 index 000000000000..c6f5c0b05b69 --- /dev/null +++ b/tests/pos-macros/i20449/Main.scala @@ -0,0 +1,6 @@ + +class Wrapper1[A] +val a = { + getTypeInfo[Any]() + val wrapper2 = Wrapper1[Any]() +} diff --git a/tests/pos-macros/i21721-a/Macro.scala b/tests/pos-macros/i21721-a/Macro.scala new file mode 100644 index 000000000000..dfdbff6a659b --- /dev/null +++ b/tests/pos-macros/i21721-a/Macro.scala @@ -0,0 +1,12 @@ +import quoted.* + +object Macro: + inline def impl(inline expr: Any): Any = + ${implImpl('expr)} + + def implImpl(expr: Expr[Any])(using q: Quotes): Expr[Any] = + import q.reflect.* + expr.asTerm.asInstanceOf[Inlined].body match + // this should not fail with a MatchError + case TypeBlock(_, _) => '{ "TypeBlock" } + case _ => '{ "Nothing" } diff --git a/tests/pos-macros/i21721-a/Test.scala b/tests/pos-macros/i21721-a/Test.scala new file mode 100644 index 000000000000..ebe91bae9ef4 --- /dev/null +++ b/tests/pos-macros/i21721-a/Test.scala @@ -0,0 +1,5 @@ +object Test: + // give a Block(...) to the macro + Macro.impl: + val a = 3 + a diff --git a/tests/pos-macros/i21721-b/Macro_1.scala b/tests/pos-macros/i21721-b/Macro_1.scala new file mode 100644 index 000000000000..aad7d6232c8f --- /dev/null +++ b/tests/pos-macros/i21721-b/Macro_1.scala @@ -0,0 +1,11 @@ +import scala.quoted._ + +inline def test(): Any = ${ testImpl } + +def testImpl(using Quotes): Expr[Any] = + import quotes.reflect._ + TypeBlock(Nil, TypeTree.of[Int]) match + case Block(_, res) => + res.asExpr // unexpected case - would crash here, as res of TypeBlock is not a term + case _ => + '{()} // expected case diff --git a/tests/pos-macros/i21721-b/Test_2.scala b/tests/pos-macros/i21721-b/Test_2.scala new file mode 100644 index 000000000000..9b75f0ca5777 --- /dev/null +++ b/tests/pos-macros/i21721-b/Test_2.scala @@ -0,0 +1 @@ +@main def Test() = test() diff --git a/tests/pos-macros/i22424/Macro_1.scala b/tests/pos-macros/i22424/Macro_1.scala new file mode 100644 index 000000000000..634cfb0055c7 --- /dev/null +++ b/tests/pos-macros/i22424/Macro_1.scala @@ -0,0 +1,25 @@ + +import scala.quoted.* + +object MockMaker: + inline def inlineMock[T]: Unit = ${instance[T]} + transparent inline def transparentInlineMock[T]: Unit = ${instance[T]} + + def instance[T: Type](using quotes: Quotes): Expr[Unit] = + import quotes.reflect._ + val tpe = TypeRepr.of[T] + val symbol = tpe.typeSymbol.methodMember("innerTraitInOptions").head + tpe.memberType(symbol) match + case mt @ MethodType(_, args, _) => + assert(args.head.typeSymbol != TypeRepr.of[Nothing].typeSymbol, "argument is incorrectly approximated") + val shownType = mt.show + val expectedType = "(x: m.Embedded#ATrait[scala.Predef.String, scala.Int])m.Embedded#ATrait[scala.Predef.String, scala.Int]" + assert(shownType == expectedType, s"Incorrect type shown. Obtained: $shownType, Expected: $expectedType") + '{()} + +trait PolymorphicTrait { + trait Embedded { + trait ATrait[A, B] + def innerTraitInOptions(x: ATrait[String, Int]): ATrait[String, Int] + } +} diff --git a/tests/pos-macros/i22424/Test_2.scala b/tests/pos-macros/i22424/Test_2.scala new file mode 100644 index 000000000000..0a231d820381 --- /dev/null +++ b/tests/pos-macros/i22424/Test_2.scala @@ -0,0 +1,4 @@ +@main def Test = + val m = new PolymorphicTrait {} + MockMaker.inlineMock[m.Embedded] + MockMaker.transparentInlineMock[m.Embedded] diff --git a/tests/pos-macros/i22584/Macro.scala b/tests/pos-macros/i22584/Macro.scala new file mode 100644 index 000000000000..46d4d39565de --- /dev/null +++ b/tests/pos-macros/i22584/Macro.scala @@ -0,0 +1,30 @@ +//> using options -Yretain-trees + +import scala.quoted.* + +object Macros { + + inline def myMacro[A]: Unit = ${ myMacroImpl[A] } + + private def myMacroImpl[A](using quotes: Quotes, tpe: Type[A]): Expr[Unit] = { + import quotes.reflect.* + + val typeRepr = TypeRepr.of[A] + val typeSymbol = typeRepr.typeSymbol + + val caseFieldSymbols: List[Symbol] = typeSymbol.fieldMembers + val caseFieldValOrDefDefs: List[ValDef | DefDef] = + caseFieldSymbols.sortBy(_.toString).map { + _.tree match { + case valDef: ValDef => valDef + case defDef: DefDef => defDef + case _ => report.errorAndAbort("???") + } + } + + val expected = "List(ValDef(boolean,TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),class Boolean)],EmptyTree), ValDef(finalVal,Ident(String),Literal(Constant(result))), ValDef(int,TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),class Int)],EmptyTree), ValDef(string,TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class scala)),object Predef),type String)],EmptyTree))" + assert(caseFieldValOrDefDefs.toString == expected) + + '{ () } + } +} diff --git a/tests/pos-macros/i22584/Main.scala b/tests/pos-macros/i22584/Main.scala new file mode 100644 index 000000000000..4b6442078f96 --- /dev/null +++ b/tests/pos-macros/i22584/Main.scala @@ -0,0 +1,4 @@ +import Types.* + +@main def main() = + Macros.myMacro[MyClass1] diff --git a/tests/pos-macros/i22584/Types.scala b/tests/pos-macros/i22584/Types.scala new file mode 100644 index 000000000000..01560035ca60 --- /dev/null +++ b/tests/pos-macros/i22584/Types.scala @@ -0,0 +1,9 @@ +object Types { + final case class MyClass1( + int: Int, + string: String, + boolean: Boolean, + ) { + final val finalVal: String = "result" + } +} diff --git a/tests/pos-macros/i22585/Macro.scala b/tests/pos-macros/i22585/Macro.scala new file mode 100644 index 000000000000..0daf304ab3a7 --- /dev/null +++ b/tests/pos-macros/i22585/Macro.scala @@ -0,0 +1,22 @@ +import scala.quoted.* + +trait Hammer[I, O] { + def hammer(input: I): O +} + +object Hammer { + inline def makeProductHammerMacro[I, O](): Hammer[I, O] = + ${ makeProductHammerMacroImpl[I, O] } + + def makeProductHammerMacroImpl[I: Type, O: Type](using Quotes): Expr[Hammer[I, O]] = + '{ makeHammer[I, O]() } + + inline def makeHammer[S, O](): Hammer[S, O] = + new Hammer[S, O] { + lazy val (hammer: Hammer[?, Int], idx: Int) = ??? + + override def hammer(input: S): O = { + hammer.hammer(???.asInstanceOf).asInstanceOf[O] + } + } +} \ No newline at end of file diff --git a/tests/pos-macros/i22585/Main.scala b/tests/pos-macros/i22585/Main.scala new file mode 100644 index 000000000000..6b09d1fb719c --- /dev/null +++ b/tests/pos-macros/i22585/Main.scala @@ -0,0 +1,5 @@ +object HammerSpec { + case class A(x: Int) + case class B(x: Int) + Hammer.makeProductHammerMacro[A, B]() +} \ No newline at end of file diff --git a/tests/pos-macros/i23423/A_1.scala b/tests/pos-macros/i23423/A_1.scala new file mode 100644 index 000000000000..62455c74eed0 --- /dev/null +++ b/tests/pos-macros/i23423/A_1.scala @@ -0,0 +1,16 @@ +package pkg + +import scala.quoted.* + +trait HasElem { + type Elem + type Alias = Elem +} + +object Macro: + inline def foo: Unit = ${fooImpl} + def fooImpl(using Quotes): Expr[Unit] = + '{ + val lll: (he: HasElem) => he.Alias = + (hx: HasElem) => ??? + } diff --git a/tests/pos-macros/i23423/B_2.scala b/tests/pos-macros/i23423/B_2.scala new file mode 100644 index 000000000000..144eaf4ce1da --- /dev/null +++ b/tests/pos-macros/i23423/B_2.scala @@ -0,0 +1,6 @@ +object Test: + def test: Unit = pkg.Macro.foo + // used to be error: + // Found: (hx: pkg.HasElem) => hx.Elem + // Required: (he: pkg.HasElem) => he.Elem + diff --git a/tests/pos-macros/i24821/Macro_1.scala b/tests/pos-macros/i24821/Macro_1.scala new file mode 100644 index 000000000000..ca5e9425ac54 --- /dev/null +++ b/tests/pos-macros/i24821/Macro_1.scala @@ -0,0 +1,57 @@ +import scala.quoted._ + +trait Foo { + protected def pSelect: Int = 4 + protected def pApply(i: Int): Int = 4 + i + protected def pTypeApply[T]: T = ??? + def foo: Int +} + +// base case +inline def baseCaseMacro: Foo = ${baseCaseMacroImpl()} +def baseCaseMacroImpl(using Quotes)(): Expr[Foo] = + '{ + new Foo { + def foo: Int = pSelect + } + } + +// base case - transparent +transparent inline def baseCaseTransparentMacro: Foo = ${baseCaseTransparentMacroImpl()} +def baseCaseTransparentMacroImpl(using Quotes)(): Expr[Foo] = + '{ + new Foo { + def foo: Int = pSelect + } + } + +// nested classes +inline def nestedClassesMacro: Foo = ${nestedClassesMacroImpl()} +def nestedClassesMacroImpl(using Quotes)(): Expr[Foo] = + '{ + new Foo { a => + def foo: Int = 0 + def bar = + new Foo { b => + def foo: Int = a.pSelect + pSelect + b.pSelect + } + } + } + +// apply +inline def applyMacro: Foo = ${applyMacroImpl()} +def applyMacroImpl(using Quotes)(): Expr[Foo] = + '{ + new Foo { + def foo: Int = pApply(1) + } + } + +// type apply +inline def typeApplyMacro: Foo = ${typeApplyMacroImpl()} +def typeApplyMacroImpl(using Quotes)(): Expr[Foo] = + '{ + new Foo { + def foo: Int = pTypeApply[Int] + } + } diff --git a/tests/pos-macros/i24821/Test_2.scala b/tests/pos-macros/i24821/Test_2.scala new file mode 100644 index 000000000000..331bcc4e5f56 --- /dev/null +++ b/tests/pos-macros/i24821/Test_2.scala @@ -0,0 +1,6 @@ +@main def main = + baseCaseMacro + baseCaseTransparentMacro + nestedClassesMacro + applyMacro + typeApplyMacro diff --git a/tests/pos-macros/i25541/Macro_1.scala b/tests/pos-macros/i25541/Macro_1.scala new file mode 100644 index 000000000000..8be7560df288 --- /dev/null +++ b/tests/pos-macros/i25541/Macro_1.scala @@ -0,0 +1,24 @@ +import scala.deriving.Mirror +import scala.quoted.* + +object LocalMacros: + def macroRWImpl[T: Type](using Quotes): Expr[Unit] = + import quotes.reflect.* + + def inspect(tpe: TypeRepr, typeArgs: List[TypeRepr]): Unit = + val constructorSym = tpe.typeSymbol.primaryConstructor + val constructorParamSymss = constructorSym.paramSymss + val (tparams0, params0) = constructorParamSymss.flatten.partition(_.isType) + val constructorTpe = tpe.memberType(constructorSym).widen + params0.foreach { param => + constructorTpe.memberType(param).substituteTypes(tparams0, typeArgs) + } + + TypeRepr.of[T] match + case applied: AppliedType => inspect(TypeRepr.of[T], applied.args) + case _ => inspect(TypeRepr.of[T], Nil) + + '{ () } + +trait Custom: + inline def macroRW[T](using Mirror.Of[T]): Unit = ${ LocalMacros.macroRWImpl[T] } diff --git a/tests/pos-macros/i25541/Test_2.scala b/tests/pos-macros/i25541/Test_2.scala new file mode 100644 index 000000000000..e5178ed304de --- /dev/null +++ b/tests/pos-macros/i25541/Test_2.scala @@ -0,0 +1,5 @@ +final case class MessageError[Node](message: Option[String], code: Option[Int], data: Option[Node]) + +object Test: + def readWriter(custom: Custom): Unit = + custom.macroRW[MessageError[Int]] diff --git a/tests/pos-with-compiler/Fileish.scala b/tests/pos-with-compiler/Fileish.scala index 8ba62efb3298..9fdced47608b 100644 --- a/tests/pos-with-compiler/Fileish.scala +++ b/tests/pos-with-compiler/Fileish.scala @@ -15,7 +15,6 @@ class Fileish(val path: Path, val input: () => InputStream) extends Streamable.C def parent = path.parent def name = path.name - def isSourceFile = path.hasExtension("java", "scala") private lazy val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim } lazy val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".") @@ -28,7 +27,6 @@ class Fileish2(val path: Path, val input: () => InputStream) extends Streamable. def parent = path.parent def name = path.name - def isSourceFile = path.hasExtension("java", "scala") private val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim } lazy val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".") @@ -42,7 +40,6 @@ class Fileish3(val path: Path, val input: () => InputStream) extends Streamable. def parent = path.parent def name = path.name - def isSourceFile = path.hasExtension("java", "scala") private val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim } private val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".") diff --git a/tests/pos/22219.scala b/tests/pos/22219.scala new file mode 100644 index 000000000000..fd455d5dc902 --- /dev/null +++ b/tests/pos/22219.scala @@ -0,0 +1,5 @@ +type MonthNumber = 1 | 2 + +def main = + val x = 1: MonthNumber + val y: MonthNumber = x diff --git a/tests/pos/22219b.scala b/tests/pos/22219b.scala new file mode 100644 index 000000000000..8046ef802845 --- /dev/null +++ b/tests/pos/22219b.scala @@ -0,0 +1,20 @@ +type MonthNumber = + 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 + +def main = + List[(String, MonthNumber)]( + "January" -> 1, + "February" -> 2, + "March" -> 3, + "April" -> 4, + "May" -> 5, + "June"-> 6, + "July" -> 7, + "August" -> 8, + "September" -> 9, + "October" -> 10, + "November" -> 11, + "December" -> 12 + ).foreach { (name, number) => + summon[number.type <:< MonthNumber] + } diff --git a/tests/pos/23056.scala b/tests/pos/23056.scala new file mode 100644 index 000000000000..e38909e16c61 --- /dev/null +++ b/tests/pos/23056.scala @@ -0,0 +1,18 @@ +def Test = { + val ops: CompileOps[Option, Option, Any] = ??? + ops.to(List).map(_.reverse) // error +} + +trait CompileOps[F[_], G[_], O]: + def to(collector: Collector[O]): G[collector.Out] +trait Collector[-A] { + type Out +} + +object Collector: + type Aux[A, X] = Collector[A] { type Out = X } + + given [A, C[_]]: Conversion[ + scala.collection.IterableFactory[C], + Collector.Aux[A, C[A]] + ] = ??? diff --git a/tests/pos/3403/NamedImpl_2.java b/tests/pos/3403/NamedImpl_2.java new file mode 100644 index 000000000000..fb890d38581c --- /dev/null +++ b/tests/pos/3403/NamedImpl_2.java @@ -0,0 +1,11 @@ +package bug; + +import java.io.Serializable; +import java.lang.annotation.Annotation; + +public class NamedImpl_2 implements Named_1 { + + public Class annotationType() { + return null; + } +} diff --git a/tests/pos/3403/Named_1.java b/tests/pos/3403/Named_1.java new file mode 100644 index 000000000000..30a6c9839a0d --- /dev/null +++ b/tests/pos/3403/Named_1.java @@ -0,0 +1,3 @@ +package bug; + +public @interface Named_1 {} diff --git a/tests/pos/3403/test_3.scala b/tests/pos/3403/test_3.scala new file mode 100644 index 000000000000..1d4446328e96 --- /dev/null +++ b/tests/pos/3403/test_3.scala @@ -0,0 +1,3 @@ +class C { + new bug.NamedImpl_2 +} diff --git a/tests/pos/annot-17242.scala b/tests/pos/annot-17242.scala index a8fcc9dbe15f..a1a62638ced3 100644 --- a/tests/pos/annot-17242.scala +++ b/tests/pos/annot-17242.scala @@ -1,5 +1,3 @@ -// See also tests/pos/annot-21595.scala - class local(predicate: Int) extends annotation.StaticAnnotation def failing1(x: Int, z: Int @local(x + x)) = () diff --git a/tests/pos/annot-18064.scala b/tests/pos/annot-18064.scala index b6a67ea9ebe7..465f627ff861 100644 --- a/tests/pos/annot-18064.scala +++ b/tests/pos/annot-18064.scala @@ -1,4 +1,4 @@ -//> using options "-Xprint:typer" +//> using options "-Vprint:typer" class myAnnot[T]() extends annotation.Annotation diff --git a/tests/pos/annot-default-arg-22874.scala b/tests/pos/annot-default-arg-22874.scala new file mode 100644 index 000000000000..7a983f4d3351 --- /dev/null +++ b/tests/pos/annot-default-arg-22874.scala @@ -0,0 +1,7 @@ +package defaultArgBug + +class staticAnnot(arg: Int) extends scala.annotation.StaticAnnotation +class refiningAnnot(arg: Int) extends scala.annotation.RefiningAnnotation + +def f1(a: Int, b: Int @staticAnnot(a + a) = 42): Int = b +def f2(a: Int, b: Int @refiningAnnot(a + a) = 42): Int = b diff --git a/tests/pos/annot-main-22364.scala b/tests/pos/annot-main-22364.scala new file mode 100644 index 000000000000..205589b525bc --- /dev/null +++ b/tests/pos/annot-main-22364.scala @@ -0,0 +1,5 @@ +def id[T](x: T): T = x + +class ann(x: Int) extends annotation.Annotation + +@ann(id(22)) @main def blop = () diff --git a/tests/pos/annot-main-22364b.scala b/tests/pos/annot-main-22364b.scala new file mode 100644 index 000000000000..c4e3067d7325 --- /dev/null +++ b/tests/pos/annot-main-22364b.scala @@ -0,0 +1,6 @@ +import util.chaining.* + +class ann(x: Int = 1, y: Int) extends annotation.Annotation + +@ann(y = 22.tap(println)) @main def blop = () + diff --git a/tests/pos/annot-main-22364c.scala b/tests/pos/annot-main-22364c.scala new file mode 100644 index 000000000000..53a6abe1a56b --- /dev/null +++ b/tests/pos/annot-main-22364c.scala @@ -0,0 +1,10 @@ +package p + +object P1: + class ann(x: Int) extends annotation.Annotation + +object P2: + def id[T](x: T): T = x + +object P3: + @P1.ann(P2.id(22)) @main def blop = () diff --git a/tests/pos/annot-refining-avoid.scala b/tests/pos/annot-refining-avoid.scala new file mode 100644 index 000000000000..89976cfa0431 --- /dev/null +++ b/tests/pos/annot-refining-avoid.scala @@ -0,0 +1,11 @@ +import scala.annotation.RefiningAnnotation + +class myAnnot extends RefiningAnnotation + +type Refined = Int @myAnnot + +def test = + val result = + val tmp: Refined = ??? + tmp + result: Refined diff --git a/tests/pos/i10343.scala b/tests/pos/i10343.scala new file mode 100644 index 000000000000..9a1ea873f671 --- /dev/null +++ b/tests/pos/i10343.scala @@ -0,0 +1,11 @@ +//> using options -Ycheck:all + +class C { + type A + inline def test: A = ??? +} + +@main def m = { + val c = C() + val x: c.A = c.test +} diff --git a/tests/pos/i11729.scala b/tests/pos/i11729.scala index e97b285ac6a2..79d3174dc2e9 100644 --- a/tests/pos/i11729.scala +++ b/tests/pos/i11729.scala @@ -6,7 +6,7 @@ type Return[X] = X match object Return: def apply[A](a:A):Return[A] = a match - case a: List[t] => a + case a: List[?] => a case a: Any => List(a) object Test1: @@ -18,7 +18,7 @@ type Boxed[X] = X match case Any => Box[X] def box[X](x: X): Boxed[X] = x match - case b: Box[t] => b + case b: Box[?] => b case x: Any => Box(x) case class Box[A](a:A): diff --git a/tests/pos/i13461-a.scala b/tests/pos/i13461-a.scala new file mode 100644 index 000000000000..6391681c07b1 --- /dev/null +++ b/tests/pos/i13461-a.scala @@ -0,0 +1,12 @@ +package i13461: + + opaque type Opaque = Int + transparent inline def op: Opaque = 123 + transparent inline def oop: i13461.Opaque = 123 + + object Main: + def main(args: Array[String]): Unit = + val o2: Opaque = op + val o3: Opaque = oop // needs to be unwrapped from Typed generated in adapt + val o22: 123 = op + val o23: 123 = oop diff --git a/tests/pos/i13461-c.scala b/tests/pos/i13461-c.scala new file mode 100644 index 000000000000..ee5eff463b5a --- /dev/null +++ b/tests/pos/i13461-c.scala @@ -0,0 +1,23 @@ +object Time: + opaque type Time = String + opaque type Seconds <: Time = String + + transparent inline def sec(n: Double): Seconds = + s"${n}s": Seconds // opaque type aliases have to be dealiased in nested calls, otherwise the resulting program might not be typed correctly + + transparent inline def testInference(): List[Time] = + List(sec(5)) // infers List[String] and returns List[Time] & List[String], not List[Seconds] + transparent inline def testGuarded(): List[Time] = + List(sec(5)): List[Seconds] // returns List[Seconds] + transparent inline def testExplicitTime(): List[Time] = + List[Seconds](sec(5)) // returns List[Seconds] + transparent inline def testExplicitString(): List[Time] = + List[String](sec(5)) // returns List[Time] & List[String] + +end Time + +@main def main() = + val t1: List[String] = Time.testInference() // returns List[Time.Time] & List[String] + val t2: List[Time.Seconds] = Time.testGuarded() // returns List[Time.Seconds] + val t3: List[Time.Seconds] = Time.testExplicitTime() // returns List[Time.Seconds] + val t4: List[String] = Time.testExplicitString() // returns List[Time.Time] & List[String] diff --git a/tests/pos/i13889.scala b/tests/pos/i13889.scala new file mode 100644 index 000000000000..68cb314a3104 --- /dev/null +++ b/tests/pos/i13889.scala @@ -0,0 +1,17 @@ +class DiscordRecord(fields: Map[String, Any]) extends Selectable { + def selectDynamic(name: String): Any = fields(name) +} + +type IdentifyScope = DiscordRecord { + val username: String +} + +type IncludeIf[Stated <: String, Requirement <: String, IfMatch] = Seq[Stated] match { + case Seq[Requirement] => IfMatch + case _ => Any +} + +type RawUser[Scopes <: String] = DiscordRecord & IncludeIf[Scopes, "identify", IdentifyScope] + +val obj: RawUser["identify"] = ??? +val _ = obj.username diff --git a/tests/pos/i14152.scala b/tests/pos/i14152.scala index 2377d5ffeae3..d72180a7b5d4 100644 --- a/tests/pos/i14152.scala +++ b/tests/pos/i14152.scala @@ -25,6 +25,6 @@ def foo[F[_]](fn: [A] => Inv[A] => F[A]) = object O1 extends AnyRef val res0 = fn(new Inv(fn(new Inv[O1.type](O1)))) val res1: F[F[O1.type]] = res0 - res1 // checked with -Xprint:typer that this widens to Any + res1 // checked with -Vprint:typer that this widens to Any // instead of the original F[F[O1.type]] // or the incorrectly avoided F[? <: F[? <: Object]] diff --git a/tests/pos/i14907.scala b/tests/pos/i14907.scala index 4a60362d0f73..ae19e5231a66 100644 --- a/tests/pos/i14907.scala +++ b/tests/pos/i14907.scala @@ -5,7 +5,7 @@ object Module { case 1 => Any *: Fill[0] } extension[N <: Int] (f: Fun[N]) - def apply: Fill[N] => Any = ??? + def apply(): Fill[N] => Any = ??? - Fun[1]()(???) + Fun[1]()()(???) } diff --git a/tests/pos/i15692.scala b/tests/pos/i15692.scala new file mode 100644 index 000000000000..99eddcd33d71 --- /dev/null +++ b/tests/pos/i15692.scala @@ -0,0 +1,24 @@ +sealed trait Nat +sealed trait Succ[Prev <: Nat] extends Nat +sealed trait Zero extends Nat + +class Sum[M <: Nat, N <: Nat] { + type Out <: Nat +} + +object Sum { + type Aux[M <: Nat, N <: Nat, R <: Nat] = Sum[M, N] { type Out = R } + + implicit def sum0[N <: Nat]: Sum.Aux[Zero, N, N] = new Sum[Zero, N] { type Out = N } + implicit def sum1[M <: Nat, N <: Nat, R <: Nat](implicit sum: Sum.Aux[M, Succ[N], R]): Sum.Aux[Succ[M], N, R] = + new Sum[Succ[M], N] { type Out = R } +} + +object Test { + def main(args: Array[String]): Unit = { + type _3 = Succ[Succ[Succ[Zero]]] + type _5 = Succ[Succ[_3]] + + implicitly[Sum[_3, _5]] + } +} diff --git a/tests/pos/i15784.scala b/tests/pos/i15784.scala index ab4b82638a42..dff40f4a4ddc 100644 --- a/tests/pos/i15784.scala +++ b/tests/pos/i15784.scala @@ -1,8 +1,19 @@ -def i15784 = List(42) match +//> using options -Werror + +def case1 = List(42) match case List(_, rest @ _*) => rest + case _ => ??? + +def case2 = List(42) match case List(_, Rest @ _*) => Rest + case _ => ??? + +def case3 = List(42) match case List(_, `Rest` @ _*) => Rest case _ => ??? -def i15784_auxiliary = 42 match +def case4 = 42 match case `type` : Int => `type` + +def case5 = 42 match + case X @ (_: Int) => 32 diff --git a/tests/pos/i15786.scala b/tests/pos/i15786.scala new file mode 100644 index 000000000000..8c9b605d9c3d --- /dev/null +++ b/tests/pos/i15786.scala @@ -0,0 +1,15 @@ +class A(val f: () => Int) { + def mA(p: Int = 0): Int = p +} + +trait B { + def mB(p1: Int): Unit +} + +class C[T](val f1: B, val f2: T) + +val f = new A(() => { + val x: B = null + C[Int](x, 0).f1.mB(1); + 1 +}).mA() diff --git a/tests/pos/i15850.scala b/tests/pos/i15850.scala new file mode 100644 index 000000000000..2ba59d9560c5 --- /dev/null +++ b/tests/pos/i15850.scala @@ -0,0 +1 @@ +val x = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" \ No newline at end of file diff --git a/tests/pos/i15944.scala b/tests/pos/i15944.scala new file mode 100644 index 000000000000..0b090bd41a54 --- /dev/null +++ b/tests/pos/i15944.scala @@ -0,0 +1,44 @@ +object Foo: + class Wrap(a: String): + def wow = s"$a - WOW!" + extension (s: String) + //private def wrap: Wrap = Wrap(s) // works + private def wrap = Wrap(s) // Not found: Wrap + export wrap.* // comment this line and it works + +class Bar: + class Wrap(a: String): + def wow = s"$a - WOW!" + val wrap = Wrap("string") // Not found: Wrap + export wrap.* + +object WorkingAlternative: + object Foo: + class Wrap(a: String): + def wow = s"$a - WOW!" + + object Bar: + import Foo.Wrap + extension (s: String) + private def wrap = Wrap(s) // works when not a member of enclosing element + export wrap.* + +class C: + class D + +class Baz: + val c = new C + export c.* + +class Bah: + val c = new C + type D = c.D + +@main def Test = + val bah = Bah() + println: + new bah.D + + val baz = Baz() + println: + baz.D() diff --git a/tests/pos/i16004.scala b/tests/pos/i16004.scala new file mode 100644 index 000000000000..9a8ae4a21abc --- /dev/null +++ b/tests/pos/i16004.scala @@ -0,0 +1,33 @@ + +import scala.concurrent.duration.FiniteDuration +import scala.concurrent.duration._ + +trait Resource[F[_], A] +trait IO[A] + +trait Cache[F[_], K, V] + +object Cache { + + final case class Config(expireAfterRead: FiniteDuration) + + def expiring[F[_], K, V]( + expireAfter: FiniteDuration + ): Resource[F, Cache[F, K, V]] = ??? + + def expiring[F[_], K, V]( + config: Config, + partitions: Option[Int] = None + ): Resource[F, Cache[F, K, V]] = ??? + + /* Without partitions being specified, error is yielded */ + + val notCompiling = expiring[IO, Int, Int]( + config = Config(expireAfterRead = 1.minute) + ) + + val compiling = expiring[IO, Int, Int]( + config = Config(expireAfterRead = 1.minute), + partitions = None + ) +} diff --git a/tests/pos/i17310.scala b/tests/pos/i17310.scala new file mode 100644 index 000000000000..793ad98c9784 --- /dev/null +++ b/tests/pos/i17310.scala @@ -0,0 +1,9 @@ +class Foo[T] { + def m(x: T): Boolean = true + def m(x: T, y: Int): Boolean = false +} + +def test(): Unit = { + val x: Foo[Float] = new Foo[Float]() + val y: Function1[? >: Float, Boolean] = x.m _ +} diff --git a/tests/pos/i17631.scala b/tests/pos/i17631.scala index 7b8a064493df..ddcb71354968 100644 --- a/tests/pos/i17631.scala +++ b/tests/pos/i17631.scala @@ -1,4 +1,4 @@ -//> using options -Xfatal-warnings -Wunused:all -deprecation -feature +//> using options -Werror -Wunused:all -deprecation -feature object foo { type Bar @@ -32,3 +32,23 @@ object Main { (bad1, bad2) } } + +def `i18388`: Unit = + def func(pred: [A] => A => Boolean): Unit = + val _ = pred + () + val _ = func + +trait L[T]: + type E + +def `i19748` = + type Warn1 = [T] => (l: L[T]) => T => l.E + type Warn2 = [T] => L[T] => T + type Warn3 = [T] => T => T + def use(x: (Warn1, Warn2, Warn3)) = x + use + +type NoWarning1 = [T] => (l: L[T]) => T => l.E +type NoWarning2 = [T] => L[T] => T +type NoWarning3 = [T] => T => T diff --git a/tests/pos/i17762.scala b/tests/pos/i17762.scala deleted file mode 100644 index 65275c4619db..000000000000 --- a/tests/pos/i17762.scala +++ /dev/null @@ -1,21 +0,0 @@ -//> using options -Xfatal-warnings -Wunused:all - -class SomeType - -def testIt(st1: SomeType, st2: SomeType): Boolean = - given CanEqual[SomeType, SomeType] = CanEqual.derived - st1 == st2 - -object HasCanEqual: - given f: CanEqual[SomeType, SomeType] = CanEqual.derived - -object UsesCanEqual: - import HasCanEqual.given - def testIt(st1: SomeType, st2: SomeType): Boolean = - st1 == st2 - -object UsesCanEqual2: - import HasCanEqual.f - - def testIt(st1: SomeType, st2: SomeType): Boolean = - st1 == st2 \ No newline at end of file diff --git a/tests/pos/i18097.1.scala b/tests/pos/i18097.1.scala new file mode 100644 index 000000000000..b7b57467e3b0 --- /dev/null +++ b/tests/pos/i18097.1.scala @@ -0,0 +1,22 @@ +opaque type Pos = Double + +object Pos: + extension (x: Pos) + def mult1(y: Pos): Pos = x * y + +extension (x: Pos) + def mult2(y: Pos): Pos = x * y + +class Test: + def test(key: String, a: Pos, b: Pos): Unit = + val tup1 = new Tuple1(Pos.mult1(a)(b)) + val res1: Pos = tup1._1 + + val tup2 = new Tuple1(a.mult1(b)) + val res2: Pos = tup2._1 + + val tup3 = new Tuple1(mult2(a)(b)) + val res3: Pos = tup3._1 + + val tup4 = new Tuple1(a.mult2(b)) + val res4: Pos = tup4._1 // was error: Found: (tup4._4 : Double) Required: Pos diff --git a/tests/pos/i18097.2.scala b/tests/pos/i18097.2.scala new file mode 100644 index 000000000000..c676479aab42 --- /dev/null +++ b/tests/pos/i18097.2.scala @@ -0,0 +1,13 @@ +opaque type Namespace = List[String] + +object Namespace: + def apply(head: String): Namespace = List(head) + +extension (ns: Namespace) + def appended(segment: String): Namespace = ns.appended(segment) + +object Main: + def main(args: Array[String]): Unit = + val a: Namespace = Namespace("a") + .appended("B") + .appended("c") // was error: Found: List[String] Required: Namespace diff --git a/tests/pos/i18097.2.works.scala b/tests/pos/i18097.2.works.scala new file mode 100644 index 000000000000..3ba8e056a4a5 --- /dev/null +++ b/tests/pos/i18097.2.works.scala @@ -0,0 +1,13 @@ +object Main: + opaque type Namespace = List[String] + + object Namespace: + def apply(head: String): Namespace = List(head) + + extension (ns: Namespace) + def appended(segment: String): Namespace = ns.appended(segment) + + def main(args: Array[String]): Unit = + val a: Namespace = Namespace("a") + .appended("B") + .appended("c") diff --git a/tests/pos/i18097.3/Opaque.scala b/tests/pos/i18097.3/Opaque.scala new file mode 100644 index 000000000000..cb9c9eaedfb3 --- /dev/null +++ b/tests/pos/i18097.3/Opaque.scala @@ -0,0 +1,9 @@ +package test + +type Foo = Unit +val bar: Foo = () + +opaque type Opaque = Unit + +extension (foo: Foo) + def go: Option[Opaque] = ??? diff --git a/tests/pos/i18097.3/Test.scala b/tests/pos/i18097.3/Test.scala new file mode 100644 index 000000000000..38f2199944c2 --- /dev/null +++ b/tests/pos/i18097.3/Test.scala @@ -0,0 +1,13 @@ +package test + +final case class Test(value: Opaque) + +def test: Test = + bar.go match + case Some(value) => Test(value) // was error: Found: (value : Unit) Required: test.Opaque + case _ => ??? + +def test2: Test = + go(bar) match + case Some(value) => Test(value) + case _ => ??? diff --git a/tests/pos/i18097.orig.scala b/tests/pos/i18097.orig.scala new file mode 100644 index 000000000000..092a904f6de4 --- /dev/null +++ b/tests/pos/i18097.orig.scala @@ -0,0 +1,20 @@ +opaque type PositiveNumber = Double + +object PositiveNumber: + extension (x: PositiveNumber) + def mult1(other: PositiveNumber): PositiveNumber = + x * other + +extension (x: PositiveNumber) + def mult2(other: PositiveNumber): PositiveNumber = + x * other + +object Test: + def multMap1[A](x: Map[A, PositiveNumber], num: PositiveNumber): Map[A, PositiveNumber] = x.map((key, value) => key -> value.mult1(num)).toMap + + def multMap2[A](x: Map[A, PositiveNumber], num: PositiveNumber): Map[A, PositiveNumber] = x.map((key, value) => key -> value.mult2(num)).toMap // was error +// ^ +// Cannot prove that (A, Double) <:< (A, V2). +// +// where: V2 is a type variable with constraint <: PositiveNumber + def multMap2_2[A](x: Map[A, PositiveNumber], num: PositiveNumber): Map[A, PositiveNumber] = x.map((key, value) => key -> mult2(value)(num)).toMap diff --git a/tests/pos/i18123b.scala b/tests/pos/i18123b.scala new file mode 100644 index 000000000000..714850004d2c --- /dev/null +++ b/tests/pos/i18123b.scala @@ -0,0 +1,25 @@ +// may not compile anymore in Scala 3.4+ +package pkg + +trait P[+T] + +extension [T](inline parse0: P[T]) + inline def | [V >: T](inline other: P[V]): P[V] = ??? + +extension [T](inline parse0: => P[T]) + inline def rep[V](inline min: Int = 0)(using repeater: Implicits.Repeater[T, V]): P[V] = ??? + +object Implicits: + trait Repeater[-T, R] + object Repeater: + implicit def GenericRepeaterImplicit[T]: Repeater[T, Seq[T]] = ??? + +sealed trait RegexTree +abstract class Node extends RegexTree +class CharClassIntersection() extends Node + +def classItem: P[RegexTree] = ??? +def charClassIntersection: P[CharClassIntersection] = ??? + +def x = + (charClassIntersection.rep() | classItem.rep()) diff --git a/tests/pos/i18151a.scala b/tests/pos/i18151a.scala new file mode 100644 index 000000000000..6be2c5c23a30 --- /dev/null +++ b/tests/pos/i18151a.scala @@ -0,0 +1,10 @@ +case class El[A](attr: String, child: String) + +transparent inline def inlineTest(): String = + inline { + val el: El[Any] = El("1", "2") + El[Any](el.attr, el.child) + } match + case El(attr, child) => attr + child + +def test: Unit = inlineTest() diff --git a/tests/pos/i18151b.scala b/tests/pos/i18151b.scala new file mode 100644 index 000000000000..01d2aaee972a --- /dev/null +++ b/tests/pos/i18151b.scala @@ -0,0 +1,10 @@ +case class El[A](val attr: String, val child: String) + +transparent inline def tmplStr(inline t: El[Any]): String = + inline t match + case El(attr, child) => attr + child + +def test: Unit = tmplStr { + val el = El("1", "2") + El[Any](el.attr, null) +} diff --git a/tests/pos/i18151c.scala b/tests/pos/i18151c.scala new file mode 100644 index 000000000000..a46ec9dd927c --- /dev/null +++ b/tests/pos/i18151c.scala @@ -0,0 +1,39 @@ +import scala.compiletime.* +import scala.compiletime.ops.any.ToString + +trait Attr +case object EmptyAttr extends Attr +transparent inline def attrStr(inline a: Attr): String = inline a match + case EmptyAttr => "" +transparent inline def attrStrHelper(inline a: Attr): String = inline a match + case EmptyAttr => "" +trait TmplNode +case class El[T <: String & Singleton, A <: Attr, C <: Tmpl](val tag: T, val attr: A, val child: C) + extends TmplNode +case class Sib[L <: Tmpl, R <: Tmpl](left: L, right: R) extends TmplNode +type TmplSingleton = String | Char | Int | Long | Float | Double | Boolean +type Tmpl = TmplNode | Unit | (TmplSingleton & Singleton) +transparent inline def tmplStr(inline t: Tmpl): String = inline t match + case El(tag, attr, child) => inline attrStr(attr) match + case "" => "<" + tag + ">" + tmplStr(child) + case x => "<" + tag + " " + x + ">" + tmplStr(child) + case Sib(left, right) => inline tmplStr(right) match + case "" => tmplStr(left) + case right => tmplStrHelper(left) + right + case () => "" + case s: (t & TmplSingleton) => constValue[ToString[t]] +transparent inline def tmplStrHelper(inline t: Tmpl): String = inline t match + case El(tag, attr, child) => inline (tmplStr(child), attrStr(attr)) match + case ("", "") => "<" + tag + "/>" + case (child, "") => "<" + tag + ">" + child + "" + case ("", attr) => "<" + tag + " " + attr + "/>" + case (child, attr) => "<" + tag + " " + attr + ">" + child + "" + case Sib(left, right) => tmplStrHelper(left) + tmplStrHelper(right) + case () => "" + case s: (t & TmplSingleton) => constValue[ToString[t]] +transparent inline def el(tag: String & Singleton): El[tag.type, EmptyAttr.type, Unit] = + El(tag, EmptyAttr, ()) +extension [T <: String & Singleton, A <: Attr, C <: Tmpl](el: El[T, A, C]) + transparent inline def >>[C2 <: Tmpl](child: C2) = El(el.tag, el.attr, el.child ++ child) + +extension [L <: Tmpl](left: L) transparent inline def ++[R <: Tmpl](right: R) = Sib(left, right) diff --git a/tests/pos/i18366.scala b/tests/pos/i18366.scala deleted file mode 100644 index 698510ad13a2..000000000000 --- a/tests/pos/i18366.scala +++ /dev/null @@ -1,10 +0,0 @@ -//> using options -Xfatal-warnings -Wunused:all - -trait Builder { - def foo(): Unit -} - -def repro = - val builder: Builder = ??? - import builder.{foo => bar} - bar() \ No newline at end of file diff --git a/tests/pos/i18450.scala b/tests/pos/i18450.scala new file mode 100644 index 000000000000..42c4830a85e7 --- /dev/null +++ b/tests/pos/i18450.scala @@ -0,0 +1,15 @@ +//> using options -explain-cyclic -Ydebug-cyclic + +class C: + extension (s: String) + def f = "hello, world" + def g = f + + //def k = k // Overloaded or recursive method k needs return type + // if k is not forced, it fails with: + // value k is not a member of String. + // Extension methods were tried, ... + + def e = + import this.{f as hw} + hw // this.f diff --git a/tests/pos/i18530/Code.java b/tests/pos/i18530/Code.java new file mode 100644 index 000000000000..fd40ae702639 --- /dev/null +++ b/tests/pos/i18530/Code.java @@ -0,0 +1,11 @@ +package bug.code; + +import bug.actions.Smart.Action; + +import java.util.ArrayDeque; + +public class Code { + public static class Variable { + public ArrayDeque actions = new ArrayDeque(); + } +} diff --git a/tests/pos/i18530/Smart.java b/tests/pos/i18530/Smart.java new file mode 100644 index 000000000000..b5b99c4daae4 --- /dev/null +++ b/tests/pos/i18530/Smart.java @@ -0,0 +1,7 @@ +package bug.actions; + +public class Smart { + public abstract class Action { + + } +} diff --git a/tests/pos/i18530/Test.scala b/tests/pos/i18530/Test.scala new file mode 100644 index 000000000000..fd5e3867ff4f --- /dev/null +++ b/tests/pos/i18530/Test.scala @@ -0,0 +1,3 @@ +object Test { + val v = new bug.code.Code.Variable() +} diff --git a/tests/pos/i18531/Code.java b/tests/pos/i18531/Code.java new file mode 100644 index 000000000000..96720438d07e --- /dev/null +++ b/tests/pos/i18531/Code.java @@ -0,0 +1,8 @@ +package bug.code; + +import static bug.code.Symbol.*; + +public class Code { + public void constant(LoadableConstant c) { + } +} diff --git a/tests/pos/i18531/Constant.java b/tests/pos/i18531/Constant.java new file mode 100644 index 000000000000..b70655f2c1bb --- /dev/null +++ b/tests/pos/i18531/Constant.java @@ -0,0 +1,6 @@ +package bug.code; + +public interface Constant { + interface LoadableConstant extends Constant { + } +} diff --git a/tests/pos/i18531/Symbol.java b/tests/pos/i18531/Symbol.java new file mode 100644 index 000000000000..8567be87ed70 --- /dev/null +++ b/tests/pos/i18531/Symbol.java @@ -0,0 +1,4 @@ +package bug.code; + +public abstract class Symbol implements Constant { +} diff --git a/tests/pos/i18531/Test.scala b/tests/pos/i18531/Test.scala new file mode 100644 index 000000000000..e7a7b05fd74f --- /dev/null +++ b/tests/pos/i18531/Test.scala @@ -0,0 +1,3 @@ +object Test { + val c = new bug.code.Code() +} diff --git a/tests/pos/i18533/Cat.java b/tests/pos/i18533/Cat.java index 394f8de37c13..56b5e0296043 100644 --- a/tests/pos/i18533/Cat.java +++ b/tests/pos/i18533/Cat.java @@ -1,8 +1,8 @@ //> using javacOpt --enable-preview --source 17 -// test: -jvm 17+ +// test: -jvm 17 package i18533; public final class Cat extends Pet { -} \ No newline at end of file +} diff --git a/tests/pos/i18533/Dog.java b/tests/pos/i18533/Dog.java index 73d137313dc7..34a21602db0d 100644 --- a/tests/pos/i18533/Dog.java +++ b/tests/pos/i18533/Dog.java @@ -1,7 +1,5 @@ -//> using javacOpt --enable-preview --source 17 - package i18533; public non-sealed class Dog extends Pet { -} \ No newline at end of file +} diff --git a/tests/pos/i18533/Pet.java b/tests/pos/i18533/Pet.java index 990c4fbabd70..c9618adc7254 100644 --- a/tests/pos/i18533/Pet.java +++ b/tests/pos/i18533/Pet.java @@ -1,7 +1,5 @@ -//> using javacOpt --enable-preview --source 17 - package i18533; public sealed class Pet permits Cat, Dog { -} \ No newline at end of file +} diff --git a/tests/pos/i18533b/Cat.java b/tests/pos/i18533b/Cat.java new file mode 100644 index 000000000000..52f8b5bf2d6a --- /dev/null +++ b/tests/pos/i18533b/Cat.java @@ -0,0 +1,7 @@ +// test: -jvm 21+ + +package i18533; + +public final class Cat extends Pet { + +} diff --git a/tests/pos/i18533b/Dog.java b/tests/pos/i18533b/Dog.java new file mode 100644 index 000000000000..34a21602db0d --- /dev/null +++ b/tests/pos/i18533b/Dog.java @@ -0,0 +1,5 @@ +package i18533; + +public non-sealed class Dog extends Pet { + +} diff --git a/tests/pos/i18533b/Pet.java b/tests/pos/i18533b/Pet.java new file mode 100644 index 000000000000..c9618adc7254 --- /dev/null +++ b/tests/pos/i18533b/Pet.java @@ -0,0 +1,5 @@ +package i18533; + +public sealed class Pet permits Cat, Dog { + +} diff --git a/tests/pos/i18584/T.java b/tests/pos/i18584/T.java new file mode 100644 index 000000000000..ad05ca47aefe --- /dev/null +++ b/tests/pos/i18584/T.java @@ -0,0 +1,9 @@ +// test: -jvm 25+ + +int k = 0; +File f = null; +java.io.File g = f; +@Deprecated void main() { return; } +public record Person(String name, int age) { } +public class H { } +public static int k() { return 1; } diff --git a/tests/pos/i18584/Test.scala b/tests/pos/i18584/Test.scala new file mode 100644 index 000000000000..c4f42cf01183 --- /dev/null +++ b/tests/pos/i18584/Test.scala @@ -0,0 +1,3 @@ +class Test { + new U +} diff --git a/tests/pos/i18584/U.java b/tests/pos/i18584/U.java new file mode 100644 index 000000000000..f9c640868358 --- /dev/null +++ b/tests/pos/i18584/U.java @@ -0,0 +1 @@ +public class U { } diff --git a/tests/pos/i19642/Valid.java b/tests/pos/i19642/Valid.java new file mode 100644 index 000000000000..17e0e1173726 --- /dev/null +++ b/tests/pos/i19642/Valid.java @@ -0,0 +1,17 @@ +package lib; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE }) +@Retention(RUNTIME) +@Documented +public @interface Valid {} diff --git a/tests/pos/i19642/i19642.java b/tests/pos/i19642/i19642.java new file mode 100644 index 000000000000..c7d8ba9f0e72 --- /dev/null +++ b/tests/pos/i19642/i19642.java @@ -0,0 +1,9 @@ +package app; + +import java.util.Optional; +import lib.*; + +public class i19642 { + private String @lib.Valid [] flatArray; + private String @lib.Valid [] @lib.Valid [] nestedArray; +} diff --git a/tests/pos/i19687.scala b/tests/pos/i19687.scala new file mode 100644 index 000000000000..610700d2a6a0 --- /dev/null +++ b/tests/pos/i19687.scala @@ -0,0 +1,11 @@ +// Original issue: enum with GADT witness, uninhabitable case eliminated +enum Eq[A, B]: + case Refl[A]() extends Eq[A, A] + +enum T[A]: + case S(witness: Eq[A, String]) + case I(witness: Eq[A, Int]) + +def test(tagged: T[Int]): Int = + tagged match + case T.I(Eq.Refl()) => 0 diff --git a/tests/pos/i19687a.scala b/tests/pos/i19687a.scala new file mode 100644 index 000000000000..b209df29c104 --- /dev/null +++ b/tests/pos/i19687a.scala @@ -0,0 +1,11 @@ +// Sealed trait version of i19687 +sealed trait Eq2[A, B] +case class Refl2[A]() extends Eq2[A, A] + +sealed trait T2[A] +case class S2[A](witness: Eq2[A, String]) extends T2[A] +case class I2[A](witness: Eq2[A, Int]) extends T2[A] + +def test(tagged: T2[Int]): Int = + tagged match + case I2(Refl2()) => 0 diff --git a/tests/pos/i19687b.scala b/tests/pos/i19687b.scala new file mode 100644 index 000000000000..11e454ce7f9c --- /dev/null +++ b/tests/pos/i19687b.scala @@ -0,0 +1,13 @@ +// Multiple fields where one is uninhabitable +enum Eq[A, B]: + case Refl[A]() extends Eq[A, A] + +case class Multi[A](x: Int, witness: Eq[A, String], y: String) + +sealed trait T[A] +case class P[A](m: Multi[A]) extends T[A] +case class Q[A](witness: Eq[A, Int]) extends T[A] + +def test(t: T[Int]): Int = + t match + case Q(Eq.Refl()) => 0 diff --git a/tests/pos/i19687c.scala b/tests/pos/i19687c.scala new file mode 100644 index 000000000000..beb136f6b325 --- /dev/null +++ b/tests/pos/i19687c.scala @@ -0,0 +1,12 @@ +// All children eliminated except the matched one +enum Eq[A, B]: + case Refl[A]() extends Eq[A, A] + +sealed trait T[A] +case class A1[A](w: Eq[A, String]) extends T[A] +case class B1[A](w: Eq[A, Boolean]) extends T[A] +case class C1[A](w: Eq[A, Int]) extends T[A] + +def test(t: T[Int]): Int = + t match + case C1(Eq.Refl()) => 0 diff --git a/tests/pos/i19687d.scala b/tests/pos/i19687d.scala new file mode 100644 index 000000000000..e80e5ac8d53c --- /dev/null +++ b/tests/pos/i19687d.scala @@ -0,0 +1,13 @@ +// Two type params with cross-constrained fields +enum Eq[A, B]: + case Refl[A]() extends Eq[A, A] + +case class Linked[A, B](fst: Eq[A, Int], snd: Eq[B, String]) + +sealed trait T[A, B] +case class W[A, B](link: Linked[A, B]) extends T[A, B] +case class V[A, B](wa: Eq[A, Int], wb: Eq[B, Int]) extends T[A, B] + +def test(t: T[Int, Int]): Int = + t match + case V(Eq.Refl(), Eq.Refl()) => 0 diff --git a/tests/pos/i19687e.scala b/tests/pos/i19687e.scala new file mode 100644 index 000000000000..76128ebe7c53 --- /dev/null +++ b/tests/pos/i19687e.scala @@ -0,0 +1,14 @@ +// Nested case class with uninhabitable field +enum Eq[A, B]: + case Refl[A]() extends Eq[A, A] + +case class Inner[A](w: Eq[A, String]) +case class Outer[A](inner: Inner[A]) + +sealed trait T[A] +case class X[A](o: Outer[A]) extends T[A] +case class Y[A](w: Eq[A, Int]) extends T[A] + +def test(t: T[Int]): Int = + t match + case Y(Eq.Refl()) => 0 diff --git a/tests/pos/i19907_slow_1000_3.scala b/tests/pos/i19907_slow_1000_3.scala new file mode 100644 index 000000000000..5ed7d2f7e1bf --- /dev/null +++ b/tests/pos/i19907_slow_1000_3.scala @@ -0,0 +1,1024 @@ + + +abstract class ServerEndpoint[-R, F[_]] { + type T0 + type T1 + type T2 +} +object ServerEndpoint { + type Full[_T0, _T1, _T2, -R, F[_]] = + ServerEndpoint[R, F] { + type T0 = _T0 + type T1 = _T1 + type T2 = _T2 + } + def apply[_T0, _T1, _T2, R, F[_]]: ServerEndpoint.Full[_T0, _T1, _T2, R, F] = ??? +} + +object Test { + type Route = ServerEndpoint[Any, Option] + def routes: List[ServerEndpoint[Any, Option]] = { + val instance0 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance1 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance2 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance3 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance4 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance5 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance6 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance7 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance8 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance9 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance10 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance11 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance12 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance13 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance14 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance15 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance16 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance17 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance18 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance19 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance20 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance21 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance22 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance23 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance24 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance25 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance26 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance27 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance28 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance29 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance30 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance31 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance32 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance33 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance34 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance35 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance36 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance37 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance38 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance39 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance40 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance41 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance42 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance43 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance44 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance45 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance46 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance47 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance48 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance49 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance50 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance51 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance52 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance53 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance54 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance55 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance56 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance57 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance58 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance59 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance60 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance61 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance62 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance63 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance64 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance65 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance66 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance67 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance68 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance69 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance70 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance71 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance72 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance73 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance74 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance75 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance76 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance77 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance78 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance79 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance80 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance81 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance82 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance83 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance84 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance85 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance86 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance87 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance88 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance89 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance90 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance91 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance92 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance93 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance94 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance95 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance96 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance97 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance98 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance99 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance100 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance101 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance102 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance103 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance104 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance105 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance106 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance107 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance108 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance109 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance110 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance111 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance112 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance113 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance114 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance115 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance116 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance117 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance118 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance119 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance120 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance121 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance122 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance123 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance124 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance125 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance126 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance127 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance128 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance129 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance130 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance131 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance132 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance133 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance134 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance135 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance136 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance137 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance138 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance139 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance140 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance141 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance142 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance143 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance144 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance145 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance146 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance147 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance148 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance149 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance150 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance151 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance152 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance153 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance154 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance155 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance156 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance157 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance158 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance159 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance160 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance161 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance162 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance163 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance164 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance165 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance166 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance167 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance168 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance169 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance170 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance171 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance172 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance173 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance174 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance175 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance176 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance177 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance178 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance179 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance180 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance181 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance182 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance183 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance184 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance185 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance186 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance187 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance188 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance189 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance190 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance191 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance192 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance193 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance194 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance195 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance196 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance197 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance198 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance199 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance200 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance201 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance202 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance203 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance204 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance205 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance206 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance207 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance208 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance209 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance210 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance211 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance212 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance213 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance214 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance215 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance216 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance217 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance218 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance219 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance220 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance221 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance222 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance223 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance224 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance225 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance226 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance227 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance228 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance229 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance230 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance231 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance232 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance233 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance234 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance235 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance236 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance237 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance238 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance239 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance240 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance241 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance242 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance243 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance244 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance245 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance246 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance247 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance248 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance249 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance250 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance251 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance252 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance253 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance254 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance255 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance256 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance257 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance258 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance259 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance260 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance261 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance262 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance263 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance264 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance265 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance266 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance267 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance268 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance269 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance270 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance271 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance272 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance273 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance274 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance275 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance276 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance277 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance278 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance279 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance280 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance281 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance282 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance283 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance284 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance285 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance286 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance287 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance288 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance289 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance290 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance291 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance292 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance293 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance294 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance295 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance296 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance297 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance298 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance299 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance300 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance301 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance302 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance303 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance304 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance305 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance306 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance307 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance308 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance309 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance310 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance311 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance312 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance313 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance314 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance315 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance316 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance317 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance318 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance319 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance320 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance321 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance322 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance323 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance324 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance325 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance326 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance327 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance328 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance329 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance330 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance331 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance332 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance333 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance334 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance335 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance336 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance337 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance338 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance339 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance340 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance341 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance342 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance343 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance344 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance345 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance346 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance347 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance348 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance349 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance350 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance351 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance352 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance353 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance354 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance355 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance356 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance357 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance358 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance359 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance360 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance361 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance362 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance363 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance364 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance365 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance366 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance367 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance368 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance369 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance370 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance371 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance372 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance373 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance374 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance375 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance376 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance377 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance378 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance379 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance380 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance381 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance382 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance383 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance384 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance385 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance386 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance387 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance388 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance389 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance390 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance391 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance392 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance393 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance394 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance395 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance396 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance397 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance398 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance399 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance400 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance401 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance402 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance403 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance404 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance405 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance406 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance407 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance408 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance409 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance410 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance411 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance412 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance413 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance414 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance415 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance416 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance417 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance418 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance419 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance420 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance421 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance422 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance423 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance424 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance425 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance426 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance427 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance428 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance429 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance430 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance431 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance432 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance433 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance434 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance435 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance436 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance437 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance438 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance439 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance440 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance441 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance442 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance443 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance444 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance445 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance446 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance447 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance448 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance449 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance450 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance451 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance452 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance453 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance454 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance455 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance456 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance457 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance458 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance459 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance460 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance461 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance462 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance463 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance464 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance465 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance466 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance467 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance468 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance469 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance470 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance471 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance472 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance473 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance474 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance475 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance476 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance477 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance478 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance479 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance480 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance481 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance482 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance483 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance484 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance485 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance486 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance487 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance488 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance489 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance490 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance491 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance492 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance493 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance494 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance495 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance496 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance497 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance498 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance499 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance500 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance501 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance502 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance503 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance504 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance505 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance506 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance507 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance508 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance509 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance510 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance511 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance512 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance513 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance514 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance515 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance516 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance517 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance518 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance519 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance520 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance521 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance522 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance523 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance524 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance525 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance526 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance527 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance528 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance529 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance530 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance531 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance532 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance533 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance534 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance535 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance536 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance537 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance538 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance539 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance540 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance541 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance542 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance543 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance544 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance545 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance546 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance547 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance548 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance549 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance550 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance551 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance552 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance553 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance554 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance555 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance556 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance557 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance558 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance559 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance560 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance561 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance562 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance563 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance564 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance565 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance566 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance567 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance568 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance569 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance570 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance571 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance572 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance573 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance574 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance575 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance576 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance577 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance578 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance579 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance580 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance581 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance582 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance583 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance584 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance585 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance586 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance587 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance588 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance589 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance590 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance591 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance592 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance593 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance594 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance595 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance596 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance597 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance598 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance599 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance600 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance601 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance602 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance603 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance604 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance605 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance606 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance607 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance608 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance609 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance610 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance611 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance612 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance613 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance614 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance615 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance616 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance617 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance618 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance619 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance620 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance621 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance622 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance623 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance624 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance625 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance626 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance627 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance628 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance629 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance630 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance631 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance632 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance633 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance634 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance635 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance636 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance637 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance638 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance639 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance640 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance641 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance642 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance643 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance644 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance645 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance646 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance647 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance648 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance649 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance650 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance651 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance652 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance653 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance654 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance655 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance656 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance657 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance658 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance659 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance660 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance661 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance662 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance663 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance664 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance665 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance666 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance667 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance668 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance669 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance670 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance671 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance672 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance673 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance674 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance675 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance676 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance677 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance678 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance679 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance680 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance681 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance682 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance683 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance684 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance685 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance686 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance687 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance688 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance689 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance690 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance691 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance692 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance693 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance694 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance695 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance696 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance697 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance698 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance699 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance700 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance701 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance702 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance703 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance704 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance705 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance706 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance707 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance708 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance709 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance710 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance711 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance712 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance713 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance714 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance715 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance716 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance717 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance718 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance719 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance720 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance721 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance722 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance723 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance724 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance725 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance726 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance727 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance728 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance729 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance730 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance731 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance732 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance733 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance734 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance735 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance736 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance737 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance738 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance739 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance740 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance741 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance742 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance743 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance744 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance745 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance746 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance747 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance748 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance749 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance750 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance751 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance752 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance753 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance754 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance755 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance756 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance757 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance758 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance759 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance760 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance761 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance762 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance763 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance764 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance765 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance766 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance767 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance768 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance769 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance770 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance771 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance772 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance773 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance774 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance775 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance776 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance777 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance778 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance779 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance780 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance781 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance782 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance783 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance784 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance785 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance786 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance787 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance788 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance789 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance790 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance791 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance792 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance793 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance794 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance795 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance796 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance797 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance798 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance799 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance800 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance801 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance802 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance803 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance804 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance805 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance806 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance807 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance808 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance809 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance810 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance811 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance812 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance813 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance814 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance815 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance816 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance817 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance818 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance819 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance820 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance821 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance822 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance823 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance824 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance825 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance826 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance827 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance828 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance829 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance830 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance831 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance832 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance833 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance834 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance835 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance836 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance837 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance838 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance839 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance840 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance841 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance842 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance843 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance844 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance845 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance846 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance847 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance848 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance849 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance850 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance851 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance852 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance853 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance854 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance855 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance856 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance857 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance858 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance859 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance860 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance861 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance862 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance863 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance864 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance865 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance866 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance867 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance868 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance869 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance870 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance871 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance872 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance873 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance874 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance875 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance876 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance877 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance878 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance879 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance880 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance881 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance882 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance883 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance884 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance885 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance886 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance887 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance888 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance889 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance890 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance891 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance892 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance893 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance894 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance895 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance896 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance897 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance898 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance899 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance900 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance901 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance902 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance903 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance904 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance905 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance906 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance907 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance908 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance909 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance910 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance911 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance912 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance913 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance914 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance915 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance916 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance917 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance918 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance919 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance920 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance921 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance922 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance923 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance924 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance925 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance926 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance927 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance928 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance929 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance930 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance931 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance932 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance933 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance934 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance935 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance936 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance937 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance938 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance939 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance940 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance941 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance942 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance943 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance944 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance945 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance946 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance947 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance948 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance949 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance950 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance951 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance952 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance953 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance954 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance955 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance956 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance957 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance958 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance959 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance960 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance961 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance962 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance963 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance964 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance965 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance966 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance967 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance968 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance969 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance970 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance971 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance972 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance973 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance974 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance975 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance976 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance977 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance978 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance979 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance980 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance981 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance982 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance983 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance984 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance985 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance986 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance987 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance988 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance989 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance990 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance991 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance992 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance993 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance994 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance995 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance996 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance997 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance998 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance999 = ServerEndpoint[Unit, Unit, Int, Any, Option] + List(instance0, instance1, instance2, instance3, instance4, instance5, instance6, instance7, instance8, instance9, instance10, instance11, instance12, instance13, instance14, instance15, instance16, instance17, instance18, instance19, instance20, instance21, instance22, instance23, instance24, instance25, instance26, instance27, instance28, instance29, instance30, instance31, instance32, instance33, instance34, instance35, instance36, instance37, instance38, instance39, instance40, instance41, instance42, instance43, instance44, instance45, instance46, instance47, instance48, instance49, instance50, instance51, instance52, instance53, instance54, instance55, instance56, instance57, instance58, instance59, instance60, instance61, instance62, instance63, instance64, instance65, instance66, instance67, instance68, instance69, instance70, instance71, instance72, instance73, instance74, instance75, instance76, instance77, instance78, instance79, instance80, instance81, instance82, instance83, instance84, instance85, instance86, instance87, instance88, instance89, instance90, instance91, instance92, instance93, instance94, instance95, instance96, instance97, instance98, instance99, instance100, instance101, instance102, instance103, instance104, instance105, instance106, instance107, instance108, instance109, instance110, instance111, instance112, instance113, instance114, instance115, instance116, instance117, instance118, instance119, instance120, instance121, instance122, instance123, instance124, instance125, instance126, instance127, instance128, instance129, instance130, instance131, instance132, instance133, instance134, instance135, instance136, instance137, instance138, instance139, instance140, instance141, instance142, instance143, instance144, instance145, instance146, instance147, instance148, instance149, instance150, instance151, instance152, instance153, instance154, instance155, instance156, instance157, instance158, instance159, instance160, instance161, instance162, instance163, instance164, instance165, instance166, instance167, instance168, instance169, instance170, instance171, instance172, instance173, instance174, instance175, instance176, instance177, instance178, instance179, instance180, instance181, instance182, instance183, instance184, instance185, instance186, instance187, instance188, instance189, instance190, instance191, instance192, instance193, instance194, instance195, instance196, instance197, instance198, instance199, instance200, instance201, instance202, instance203, instance204, instance205, instance206, instance207, instance208, instance209, instance210, instance211, instance212, instance213, instance214, instance215, instance216, instance217, instance218, instance219, instance220, instance221, instance222, instance223, instance224, instance225, instance226, instance227, instance228, instance229, instance230, instance231, instance232, instance233, instance234, instance235, instance236, instance237, instance238, instance239, instance240, instance241, instance242, instance243, instance244, instance245, instance246, instance247, instance248, instance249, instance250, instance251, instance252, instance253, instance254, instance255, instance256, instance257, instance258, instance259, instance260, instance261, instance262, instance263, instance264, instance265, instance266, instance267, instance268, instance269, instance270, instance271, instance272, instance273, instance274, instance275, instance276, instance277, instance278, instance279, instance280, instance281, instance282, instance283, instance284, instance285, instance286, instance287, instance288, instance289, instance290, instance291, instance292, instance293, instance294, instance295, instance296, instance297, instance298, instance299, instance300, instance301, instance302, instance303, instance304, instance305, instance306, instance307, instance308, instance309, instance310, instance311, instance312, instance313, instance314, instance315, instance316, instance317, instance318, instance319, instance320, instance321, instance322, instance323, instance324, instance325, instance326, instance327, instance328, instance329, instance330, instance331, instance332, instance333, instance334, instance335, instance336, instance337, instance338, instance339, instance340, instance341, instance342, instance343, instance344, instance345, instance346, instance347, instance348, instance349, instance350, instance351, instance352, instance353, instance354, instance355, instance356, instance357, instance358, instance359, instance360, instance361, instance362, instance363, instance364, instance365, instance366, instance367, instance368, instance369, instance370, instance371, instance372, instance373, instance374, instance375, instance376, instance377, instance378, instance379, instance380, instance381, instance382, instance383, instance384, instance385, instance386, instance387, instance388, instance389, instance390, instance391, instance392, instance393, instance394, instance395, instance396, instance397, instance398, instance399, instance400, instance401, instance402, instance403, instance404, instance405, instance406, instance407, instance408, instance409, instance410, instance411, instance412, instance413, instance414, instance415, instance416, instance417, instance418, instance419, instance420, instance421, instance422, instance423, instance424, instance425, instance426, instance427, instance428, instance429, instance430, instance431, instance432, instance433, instance434, instance435, instance436, instance437, instance438, instance439, instance440, instance441, instance442, instance443, instance444, instance445, instance446, instance447, instance448, instance449, instance450, instance451, instance452, instance453, instance454, instance455, instance456, instance457, instance458, instance459, instance460, instance461, instance462, instance463, instance464, instance465, instance466, instance467, instance468, instance469, instance470, instance471, instance472, instance473, instance474, instance475, instance476, instance477, instance478, instance479, instance480, instance481, instance482, instance483, instance484, instance485, instance486, instance487, instance488, instance489, instance490, instance491, instance492, instance493, instance494, instance495, instance496, instance497, instance498, instance499, instance500, instance501, instance502, instance503, instance504, instance505, instance506, instance507, instance508, instance509, instance510, instance511, instance512, instance513, instance514, instance515, instance516, instance517, instance518, instance519, instance520, instance521, instance522, instance523, instance524, instance525, instance526, instance527, instance528, instance529, instance530, instance531, instance532, instance533, instance534, instance535, instance536, instance537, instance538, instance539, instance540, instance541, instance542, instance543, instance544, instance545, instance546, instance547, instance548, instance549, instance550, instance551, instance552, instance553, instance554, instance555, instance556, instance557, instance558, instance559, instance560, instance561, instance562, instance563, instance564, instance565, instance566, instance567, instance568, instance569, instance570, instance571, instance572, instance573, instance574, instance575, instance576, instance577, instance578, instance579, instance580, instance581, instance582, instance583, instance584, instance585, instance586, instance587, instance588, instance589, instance590, instance591, instance592, instance593, instance594, instance595, instance596, instance597, instance598, instance599, instance600, instance601, instance602, instance603, instance604, instance605, instance606, instance607, instance608, instance609, instance610, instance611, instance612, instance613, instance614, instance615, instance616, instance617, instance618, instance619, instance620, instance621, instance622, instance623, instance624, instance625, instance626, instance627, instance628, instance629, instance630, instance631, instance632, instance633, instance634, instance635, instance636, instance637, instance638, instance639, instance640, instance641, instance642, instance643, instance644, instance645, instance646, instance647, instance648, instance649, instance650, instance651, instance652, instance653, instance654, instance655, instance656, instance657, instance658, instance659, instance660, instance661, instance662, instance663, instance664, instance665, instance666, instance667, instance668, instance669, instance670, instance671, instance672, instance673, instance674, instance675, instance676, instance677, instance678, instance679, instance680, instance681, instance682, instance683, instance684, instance685, instance686, instance687, instance688, instance689, instance690, instance691, instance692, instance693, instance694, instance695, instance696, instance697, instance698, instance699, instance700, instance701, instance702, instance703, instance704, instance705, instance706, instance707, instance708, instance709, instance710, instance711, instance712, instance713, instance714, instance715, instance716, instance717, instance718, instance719, instance720, instance721, instance722, instance723, instance724, instance725, instance726, instance727, instance728, instance729, instance730, instance731, instance732, instance733, instance734, instance735, instance736, instance737, instance738, instance739, instance740, instance741, instance742, instance743, instance744, instance745, instance746, instance747, instance748, instance749, instance750, instance751, instance752, instance753, instance754, instance755, instance756, instance757, instance758, instance759, instance760, instance761, instance762, instance763, instance764, instance765, instance766, instance767, instance768, instance769, instance770, instance771, instance772, instance773, instance774, instance775, instance776, instance777, instance778, instance779, instance780, instance781, instance782, instance783, instance784, instance785, instance786, instance787, instance788, instance789, instance790, instance791, instance792, instance793, instance794, instance795, instance796, instance797, instance798, instance799, instance800, instance801, instance802, instance803, instance804, instance805, instance806, instance807, instance808, instance809, instance810, instance811, instance812, instance813, instance814, instance815, instance816, instance817, instance818, instance819, instance820, instance821, instance822, instance823, instance824, instance825, instance826, instance827, instance828, instance829, instance830, instance831, instance832, instance833, instance834, instance835, instance836, instance837, instance838, instance839, instance840, instance841, instance842, instance843, instance844, instance845, instance846, instance847, instance848, instance849, instance850, instance851, instance852, instance853, instance854, instance855, instance856, instance857, instance858, instance859, instance860, instance861, instance862, instance863, instance864, instance865, instance866, instance867, instance868, instance869, instance870, instance871, instance872, instance873, instance874, instance875, instance876, instance877, instance878, instance879, instance880, instance881, instance882, instance883, instance884, instance885, instance886, instance887, instance888, instance889, instance890, instance891, instance892, instance893, instance894, instance895, instance896, instance897, instance898, instance899, instance900, instance901, instance902, instance903, instance904, instance905, instance906, instance907, instance908, instance909, instance910, instance911, instance912, instance913, instance914, instance915, instance916, instance917, instance918, instance919, instance920, instance921, instance922, instance923, instance924, instance925, instance926, instance927, instance928, instance929, instance930, instance931, instance932, instance933, instance934, instance935, instance936, instance937, instance938, instance939, instance940, instance941, instance942, instance943, instance944, instance945, instance946, instance947, instance948, instance949, instance950, instance951, instance952, instance953, instance954, instance955, instance956, instance957, instance958, instance959, instance960, instance961, instance962, instance963, instance964, instance965, instance966, instance967, instance968, instance969, instance970, instance971, instance972, instance973, instance974, instance975, instance976, instance977, instance978, instance979, instance980, instance981, instance982, instance983, instance984, instance985, instance986, instance987, instance988, instance989, instance990, instance991, instance992, instance993, instance994, instance995, instance996, instance997, instance998, instance999) + } +} + diff --git a/tests/pos/i19907_slow_1000_4.scala b/tests/pos/i19907_slow_1000_4.scala new file mode 100644 index 000000000000..231bfadc9354 --- /dev/null +++ b/tests/pos/i19907_slow_1000_4.scala @@ -0,0 +1,1026 @@ + + +abstract class ServerEndpoint[-R, F[_]] { + type T0 + type T1 + type T2 + type T3 +} +object ServerEndpoint { + type Full[_T0, _T1, _T2, _T3, -R, F[_]] = + ServerEndpoint[R, F] { + type T0 = _T0 + type T1 = _T1 + type T2 = _T2 + type T3 = _T3 + } + def apply[_T0, _T1, _T2, _T3, R, F[_]]: ServerEndpoint.Full[_T0, _T1, _T2, _T3, R, F] = ??? +} + +object Test { + type Route = ServerEndpoint[Any, Option] + def routes: List[ServerEndpoint[Any, Option]] = { + val instance0 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance1 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance2 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance3 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance4 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance5 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance6 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance7 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance8 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance9 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance10 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance11 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance12 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance13 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance14 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance15 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance16 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance17 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance18 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance19 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance20 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance21 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance22 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance23 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance24 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance25 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance26 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance27 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance28 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance29 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance30 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance31 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance32 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance33 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance34 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance35 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance36 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance37 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance38 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance39 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance40 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance41 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance42 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance43 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance44 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance45 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance46 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance47 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance48 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance49 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance50 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance51 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance52 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance53 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance54 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance55 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance56 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance57 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance58 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance59 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance60 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance61 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance62 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance63 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance64 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance65 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance66 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance67 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance68 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance69 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance70 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance71 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance72 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance73 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance74 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance75 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance76 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance77 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance78 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance79 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance80 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance81 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance82 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance83 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance84 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance85 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance86 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance87 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance88 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance89 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance90 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance91 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance92 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance93 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance94 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance95 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance96 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance97 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance98 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance99 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance100 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance101 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance102 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance103 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance104 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance105 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance106 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance107 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance108 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance109 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance110 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance111 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance112 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance113 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance114 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance115 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance116 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance117 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance118 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance119 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance120 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance121 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance122 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance123 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance124 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance125 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance126 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance127 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance128 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance129 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance130 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance131 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance132 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance133 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance134 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance135 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance136 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance137 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance138 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance139 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance140 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance141 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance142 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance143 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance144 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance145 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance146 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance147 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance148 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance149 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance150 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance151 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance152 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance153 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance154 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance155 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance156 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance157 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance158 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance159 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance160 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance161 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance162 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance163 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance164 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance165 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance166 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance167 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance168 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance169 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance170 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance171 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance172 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance173 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance174 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance175 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance176 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance177 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance178 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance179 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance180 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance181 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance182 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance183 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance184 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance185 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance186 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance187 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance188 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance189 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance190 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance191 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance192 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance193 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance194 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance195 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance196 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance197 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance198 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance199 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance200 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance201 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance202 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance203 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance204 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance205 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance206 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance207 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance208 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance209 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance210 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance211 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance212 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance213 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance214 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance215 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance216 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance217 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance218 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance219 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance220 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance221 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance222 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance223 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance224 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance225 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance226 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance227 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance228 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance229 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance230 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance231 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance232 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance233 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance234 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance235 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance236 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance237 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance238 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance239 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance240 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance241 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance242 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance243 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance244 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance245 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance246 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance247 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance248 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance249 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance250 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance251 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance252 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance253 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance254 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance255 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance256 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance257 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance258 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance259 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance260 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance261 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance262 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance263 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance264 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance265 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance266 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance267 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance268 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance269 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance270 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance271 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance272 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance273 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance274 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance275 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance276 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance277 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance278 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance279 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance280 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance281 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance282 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance283 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance284 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance285 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance286 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance287 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance288 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance289 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance290 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance291 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance292 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance293 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance294 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance295 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance296 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance297 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance298 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance299 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance300 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance301 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance302 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance303 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance304 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance305 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance306 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance307 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance308 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance309 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance310 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance311 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance312 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance313 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance314 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance315 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance316 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance317 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance318 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance319 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance320 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance321 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance322 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance323 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance324 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance325 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance326 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance327 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance328 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance329 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance330 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance331 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance332 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance333 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance334 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance335 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance336 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance337 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance338 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance339 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance340 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance341 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance342 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance343 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance344 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance345 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance346 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance347 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance348 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance349 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance350 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance351 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance352 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance353 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance354 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance355 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance356 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance357 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance358 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance359 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance360 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance361 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance362 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance363 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance364 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance365 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance366 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance367 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance368 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance369 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance370 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance371 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance372 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance373 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance374 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance375 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance376 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance377 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance378 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance379 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance380 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance381 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance382 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance383 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance384 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance385 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance386 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance387 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance388 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance389 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance390 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance391 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance392 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance393 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance394 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance395 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance396 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance397 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance398 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance399 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance400 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance401 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance402 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance403 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance404 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance405 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance406 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance407 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance408 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance409 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance410 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance411 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance412 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance413 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance414 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance415 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance416 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance417 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance418 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance419 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance420 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance421 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance422 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance423 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance424 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance425 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance426 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance427 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance428 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance429 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance430 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance431 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance432 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance433 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance434 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance435 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance436 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance437 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance438 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance439 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance440 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance441 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance442 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance443 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance444 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance445 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance446 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance447 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance448 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance449 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance450 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance451 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance452 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance453 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance454 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance455 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance456 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance457 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance458 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance459 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance460 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance461 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance462 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance463 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance464 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance465 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance466 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance467 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance468 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance469 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance470 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance471 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance472 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance473 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance474 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance475 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance476 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance477 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance478 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance479 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance480 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance481 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance482 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance483 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance484 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance485 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance486 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance487 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance488 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance489 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance490 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance491 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance492 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance493 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance494 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance495 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance496 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance497 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance498 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance499 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance500 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance501 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance502 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance503 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance504 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance505 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance506 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance507 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance508 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance509 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance510 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance511 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance512 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance513 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance514 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance515 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance516 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance517 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance518 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance519 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance520 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance521 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance522 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance523 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance524 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance525 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance526 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance527 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance528 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance529 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance530 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance531 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance532 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance533 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance534 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance535 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance536 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance537 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance538 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance539 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance540 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance541 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance542 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance543 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance544 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance545 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance546 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance547 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance548 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance549 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance550 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance551 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance552 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance553 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance554 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance555 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance556 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance557 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance558 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance559 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance560 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance561 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance562 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance563 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance564 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance565 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance566 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance567 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance568 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance569 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance570 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance571 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance572 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance573 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance574 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance575 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance576 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance577 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance578 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance579 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance580 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance581 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance582 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance583 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance584 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance585 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance586 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance587 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance588 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance589 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance590 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance591 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance592 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance593 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance594 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance595 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance596 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance597 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance598 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance599 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance600 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance601 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance602 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance603 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance604 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance605 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance606 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance607 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance608 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance609 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance610 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance611 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance612 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance613 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance614 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance615 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance616 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance617 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance618 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance619 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance620 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance621 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance622 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance623 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance624 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance625 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance626 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance627 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance628 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance629 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance630 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance631 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance632 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance633 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance634 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance635 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance636 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance637 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance638 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance639 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance640 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance641 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance642 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance643 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance644 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance645 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance646 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance647 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance648 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance649 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance650 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance651 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance652 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance653 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance654 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance655 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance656 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance657 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance658 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance659 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance660 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance661 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance662 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance663 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance664 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance665 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance666 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance667 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance668 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance669 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance670 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance671 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance672 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance673 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance674 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance675 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance676 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance677 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance678 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance679 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance680 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance681 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance682 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance683 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance684 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance685 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance686 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance687 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance688 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance689 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance690 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance691 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance692 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance693 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance694 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance695 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance696 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance697 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance698 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance699 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance700 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance701 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance702 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance703 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance704 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance705 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance706 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance707 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance708 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance709 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance710 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance711 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance712 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance713 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance714 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance715 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance716 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance717 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance718 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance719 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance720 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance721 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance722 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance723 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance724 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance725 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance726 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance727 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance728 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance729 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance730 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance731 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance732 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance733 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance734 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance735 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance736 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance737 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance738 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance739 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance740 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance741 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance742 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance743 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance744 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance745 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance746 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance747 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance748 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance749 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance750 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance751 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance752 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance753 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance754 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance755 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance756 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance757 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance758 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance759 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance760 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance761 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance762 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance763 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance764 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance765 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance766 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance767 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance768 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance769 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance770 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance771 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance772 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance773 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance774 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance775 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance776 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance777 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance778 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance779 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance780 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance781 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance782 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance783 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance784 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance785 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance786 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance787 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance788 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance789 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance790 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance791 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance792 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance793 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance794 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance795 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance796 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance797 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance798 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance799 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance800 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance801 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance802 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance803 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance804 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance805 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance806 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance807 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance808 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance809 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance810 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance811 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance812 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance813 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance814 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance815 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance816 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance817 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance818 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance819 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance820 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance821 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance822 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance823 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance824 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance825 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance826 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance827 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance828 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance829 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance830 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance831 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance832 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance833 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance834 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance835 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance836 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance837 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance838 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance839 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance840 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance841 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance842 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance843 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance844 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance845 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance846 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance847 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance848 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance849 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance850 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance851 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance852 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance853 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance854 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance855 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance856 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance857 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance858 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance859 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance860 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance861 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance862 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance863 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance864 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance865 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance866 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance867 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance868 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance869 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance870 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance871 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance872 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance873 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance874 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance875 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance876 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance877 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance878 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance879 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance880 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance881 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance882 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance883 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance884 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance885 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance886 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance887 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance888 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance889 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance890 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance891 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance892 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance893 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance894 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance895 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance896 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance897 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance898 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance899 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance900 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance901 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance902 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance903 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance904 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance905 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance906 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance907 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance908 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance909 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance910 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance911 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance912 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance913 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance914 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance915 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance916 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance917 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance918 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance919 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance920 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance921 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance922 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance923 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance924 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance925 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance926 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance927 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance928 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance929 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance930 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance931 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance932 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance933 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance934 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance935 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance936 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance937 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance938 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance939 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance940 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance941 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance942 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance943 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance944 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance945 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance946 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance947 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance948 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance949 = ServerEndpoint[Unit, Unit, Int, Unit, Any, Option] + val instance950 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance951 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance952 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance953 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance954 = ServerEndpoint[Int, Int, Unit, Int, Any, Option] + val instance955 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance956 = ServerEndpoint[Int, Unit, Int, Unit, Any, Option] + val instance957 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance958 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance959 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance960 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance961 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance962 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance963 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance964 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance965 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance966 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance967 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance968 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance969 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance970 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance971 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + val instance972 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance973 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance974 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance975 = ServerEndpoint[Int, Int, Int, Int, Any, Option] + val instance976 = ServerEndpoint[Unit, Int, Int, Int, Any, Option] + val instance977 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance978 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance979 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance980 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance981 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance982 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance983 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance984 = ServerEndpoint[Unit, Unit, Int, Int, Any, Option] + val instance985 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance986 = ServerEndpoint[Unit, Int, Unit, Unit, Any, Option] + val instance987 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance988 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance989 = ServerEndpoint[Int, Unit, Unit, Unit, Any, Option] + val instance990 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance991 = ServerEndpoint[Unit, Int, Int, Unit, Any, Option] + val instance992 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance993 = ServerEndpoint[Unit, Int, Unit, Int, Any, Option] + val instance994 = ServerEndpoint[Int, Int, Unit, Unit, Any, Option] + val instance995 = ServerEndpoint[Unit, Unit, Unit, Int, Any, Option] + val instance996 = ServerEndpoint[Int, Unit, Unit, Int, Any, Option] + val instance997 = ServerEndpoint[Unit, Unit, Unit, Unit, Any, Option] + val instance998 = ServerEndpoint[Int, Unit, Int, Int, Any, Option] + val instance999 = ServerEndpoint[Int, Int, Int, Unit, Any, Option] + List(instance0, instance1, instance2, instance3, instance4, instance5, instance6, instance7, instance8, instance9, instance10, instance11, instance12, instance13, instance14, instance15, instance16, instance17, instance18, instance19, instance20, instance21, instance22, instance23, instance24, instance25, instance26, instance27, instance28, instance29, instance30, instance31, instance32, instance33, instance34, instance35, instance36, instance37, instance38, instance39, instance40, instance41, instance42, instance43, instance44, instance45, instance46, instance47, instance48, instance49, instance50, instance51, instance52, instance53, instance54, instance55, instance56, instance57, instance58, instance59, instance60, instance61, instance62, instance63, instance64, instance65, instance66, instance67, instance68, instance69, instance70, instance71, instance72, instance73, instance74, instance75, instance76, instance77, instance78, instance79, instance80, instance81, instance82, instance83, instance84, instance85, instance86, instance87, instance88, instance89, instance90, instance91, instance92, instance93, instance94, instance95, instance96, instance97, instance98, instance99, instance100, instance101, instance102, instance103, instance104, instance105, instance106, instance107, instance108, instance109, instance110, instance111, instance112, instance113, instance114, instance115, instance116, instance117, instance118, instance119, instance120, instance121, instance122, instance123, instance124, instance125, instance126, instance127, instance128, instance129, instance130, instance131, instance132, instance133, instance134, instance135, instance136, instance137, instance138, instance139, instance140, instance141, instance142, instance143, instance144, instance145, instance146, instance147, instance148, instance149, instance150, instance151, instance152, instance153, instance154, instance155, instance156, instance157, instance158, instance159, instance160, instance161, instance162, instance163, instance164, instance165, instance166, instance167, instance168, instance169, instance170, instance171, instance172, instance173, instance174, instance175, instance176, instance177, instance178, instance179, instance180, instance181, instance182, instance183, instance184, instance185, instance186, instance187, instance188, instance189, instance190, instance191, instance192, instance193, instance194, instance195, instance196, instance197, instance198, instance199, instance200, instance201, instance202, instance203, instance204, instance205, instance206, instance207, instance208, instance209, instance210, instance211, instance212, instance213, instance214, instance215, instance216, instance217, instance218, instance219, instance220, instance221, instance222, instance223, instance224, instance225, instance226, instance227, instance228, instance229, instance230, instance231, instance232, instance233, instance234, instance235, instance236, instance237, instance238, instance239, instance240, instance241, instance242, instance243, instance244, instance245, instance246, instance247, instance248, instance249, instance250, instance251, instance252, instance253, instance254, instance255, instance256, instance257, instance258, instance259, instance260, instance261, instance262, instance263, instance264, instance265, instance266, instance267, instance268, instance269, instance270, instance271, instance272, instance273, instance274, instance275, instance276, instance277, instance278, instance279, instance280, instance281, instance282, instance283, instance284, instance285, instance286, instance287, instance288, instance289, instance290, instance291, instance292, instance293, instance294, instance295, instance296, instance297, instance298, instance299, instance300, instance301, instance302, instance303, instance304, instance305, instance306, instance307, instance308, instance309, instance310, instance311, instance312, instance313, instance314, instance315, instance316, instance317, instance318, instance319, instance320, instance321, instance322, instance323, instance324, instance325, instance326, instance327, instance328, instance329, instance330, instance331, instance332, instance333, instance334, instance335, instance336, instance337, instance338, instance339, instance340, instance341, instance342, instance343, instance344, instance345, instance346, instance347, instance348, instance349, instance350, instance351, instance352, instance353, instance354, instance355, instance356, instance357, instance358, instance359, instance360, instance361, instance362, instance363, instance364, instance365, instance366, instance367, instance368, instance369, instance370, instance371, instance372, instance373, instance374, instance375, instance376, instance377, instance378, instance379, instance380, instance381, instance382, instance383, instance384, instance385, instance386, instance387, instance388, instance389, instance390, instance391, instance392, instance393, instance394, instance395, instance396, instance397, instance398, instance399, instance400, instance401, instance402, instance403, instance404, instance405, instance406, instance407, instance408, instance409, instance410, instance411, instance412, instance413, instance414, instance415, instance416, instance417, instance418, instance419, instance420, instance421, instance422, instance423, instance424, instance425, instance426, instance427, instance428, instance429, instance430, instance431, instance432, instance433, instance434, instance435, instance436, instance437, instance438, instance439, instance440, instance441, instance442, instance443, instance444, instance445, instance446, instance447, instance448, instance449, instance450, instance451, instance452, instance453, instance454, instance455, instance456, instance457, instance458, instance459, instance460, instance461, instance462, instance463, instance464, instance465, instance466, instance467, instance468, instance469, instance470, instance471, instance472, instance473, instance474, instance475, instance476, instance477, instance478, instance479, instance480, instance481, instance482, instance483, instance484, instance485, instance486, instance487, instance488, instance489, instance490, instance491, instance492, instance493, instance494, instance495, instance496, instance497, instance498, instance499, instance500, instance501, instance502, instance503, instance504, instance505, instance506, instance507, instance508, instance509, instance510, instance511, instance512, instance513, instance514, instance515, instance516, instance517, instance518, instance519, instance520, instance521, instance522, instance523, instance524, instance525, instance526, instance527, instance528, instance529, instance530, instance531, instance532, instance533, instance534, instance535, instance536, instance537, instance538, instance539, instance540, instance541, instance542, instance543, instance544, instance545, instance546, instance547, instance548, instance549, instance550, instance551, instance552, instance553, instance554, instance555, instance556, instance557, instance558, instance559, instance560, instance561, instance562, instance563, instance564, instance565, instance566, instance567, instance568, instance569, instance570, instance571, instance572, instance573, instance574, instance575, instance576, instance577, instance578, instance579, instance580, instance581, instance582, instance583, instance584, instance585, instance586, instance587, instance588, instance589, instance590, instance591, instance592, instance593, instance594, instance595, instance596, instance597, instance598, instance599, instance600, instance601, instance602, instance603, instance604, instance605, instance606, instance607, instance608, instance609, instance610, instance611, instance612, instance613, instance614, instance615, instance616, instance617, instance618, instance619, instance620, instance621, instance622, instance623, instance624, instance625, instance626, instance627, instance628, instance629, instance630, instance631, instance632, instance633, instance634, instance635, instance636, instance637, instance638, instance639, instance640, instance641, instance642, instance643, instance644, instance645, instance646, instance647, instance648, instance649, instance650, instance651, instance652, instance653, instance654, instance655, instance656, instance657, instance658, instance659, instance660, instance661, instance662, instance663, instance664, instance665, instance666, instance667, instance668, instance669, instance670, instance671, instance672, instance673, instance674, instance675, instance676, instance677, instance678, instance679, instance680, instance681, instance682, instance683, instance684, instance685, instance686, instance687, instance688, instance689, instance690, instance691, instance692, instance693, instance694, instance695, instance696, instance697, instance698, instance699, instance700, instance701, instance702, instance703, instance704, instance705, instance706, instance707, instance708, instance709, instance710, instance711, instance712, instance713, instance714, instance715, instance716, instance717, instance718, instance719, instance720, instance721, instance722, instance723, instance724, instance725, instance726, instance727, instance728, instance729, instance730, instance731, instance732, instance733, instance734, instance735, instance736, instance737, instance738, instance739, instance740, instance741, instance742, instance743, instance744, instance745, instance746, instance747, instance748, instance749, instance750, instance751, instance752, instance753, instance754, instance755, instance756, instance757, instance758, instance759, instance760, instance761, instance762, instance763, instance764, instance765, instance766, instance767, instance768, instance769, instance770, instance771, instance772, instance773, instance774, instance775, instance776, instance777, instance778, instance779, instance780, instance781, instance782, instance783, instance784, instance785, instance786, instance787, instance788, instance789, instance790, instance791, instance792, instance793, instance794, instance795, instance796, instance797, instance798, instance799, instance800, instance801, instance802, instance803, instance804, instance805, instance806, instance807, instance808, instance809, instance810, instance811, instance812, instance813, instance814, instance815, instance816, instance817, instance818, instance819, instance820, instance821, instance822, instance823, instance824, instance825, instance826, instance827, instance828, instance829, instance830, instance831, instance832, instance833, instance834, instance835, instance836, instance837, instance838, instance839, instance840, instance841, instance842, instance843, instance844, instance845, instance846, instance847, instance848, instance849, instance850, instance851, instance852, instance853, instance854, instance855, instance856, instance857, instance858, instance859, instance860, instance861, instance862, instance863, instance864, instance865, instance866, instance867, instance868, instance869, instance870, instance871, instance872, instance873, instance874, instance875, instance876, instance877, instance878, instance879, instance880, instance881, instance882, instance883, instance884, instance885, instance886, instance887, instance888, instance889, instance890, instance891, instance892, instance893, instance894, instance895, instance896, instance897, instance898, instance899, instance900, instance901, instance902, instance903, instance904, instance905, instance906, instance907, instance908, instance909, instance910, instance911, instance912, instance913, instance914, instance915, instance916, instance917, instance918, instance919, instance920, instance921, instance922, instance923, instance924, instance925, instance926, instance927, instance928, instance929, instance930, instance931, instance932, instance933, instance934, instance935, instance936, instance937, instance938, instance939, instance940, instance941, instance942, instance943, instance944, instance945, instance946, instance947, instance948, instance949, instance950, instance951, instance952, instance953, instance954, instance955, instance956, instance957, instance958, instance959, instance960, instance961, instance962, instance963, instance964, instance965, instance966, instance967, instance968, instance969, instance970, instance971, instance972, instance973, instance974, instance975, instance976, instance977, instance978, instance979, instance980, instance981, instance982, instance983, instance984, instance985, instance986, instance987, instance988, instance989, instance990, instance991, instance992, instance993, instance994, instance995, instance996, instance997, instance998, instance999) + } +} + diff --git a/tests/pos/i19907_slow_100_3.scala b/tests/pos/i19907_slow_100_3.scala new file mode 100644 index 000000000000..a723ea8e4623 --- /dev/null +++ b/tests/pos/i19907_slow_100_3.scala @@ -0,0 +1,124 @@ + + +abstract class ServerEndpoint[-R, F[_]] { + type T0 + type T1 + type T2 +} +object ServerEndpoint { + type Full[_T0, _T1, _T2, -R, F[_]] = + ServerEndpoint[R, F] { + type T0 = _T0 + type T1 = _T1 + type T2 = _T2 + } + def apply[_T0, _T1, _T2, R, F[_]]: ServerEndpoint.Full[_T0, _T1, _T2, R, F] = ??? +} + +object Test { + type Route = ServerEndpoint[Any, Option] + def routes: List[ServerEndpoint[Any, Option]] = { + val instance0 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance1 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance2 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance3 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance4 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance5 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance6 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance7 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance8 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance9 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance10 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance11 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance12 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance13 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance14 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance15 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance16 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance17 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance18 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance19 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance20 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance21 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance22 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance23 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance24 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance25 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance26 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance27 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance28 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance29 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance30 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance31 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance32 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance33 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance34 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance35 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance36 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance37 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance38 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance39 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance40 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance41 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance42 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance43 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance44 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance45 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance46 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance47 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance48 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance49 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance50 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance51 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance52 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance53 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance54 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance55 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance56 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance57 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance58 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance59 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance60 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance61 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance62 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance63 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance64 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance65 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance66 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance67 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance68 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance69 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance70 = ServerEndpoint[Unit, Unit, Int, Any, Option] + val instance71 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance72 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance73 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance74 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance75 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance76 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance77 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance78 = ServerEndpoint[Unit, Unit, Unit, Any, Option] + val instance79 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance80 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance81 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance82 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance83 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance84 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance85 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance86 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance87 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance88 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance89 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance90 = ServerEndpoint[Int, Int, Unit, Any, Option] + val instance91 = ServerEndpoint[Unit, Int, Int, Any, Option] + val instance92 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance93 = ServerEndpoint[Unit, Int, Unit, Any, Option] + val instance94 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance95 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance96 = ServerEndpoint[Int, Unit, Int, Any, Option] + val instance97 = ServerEndpoint[Int, Int, Int, Any, Option] + val instance98 = ServerEndpoint[Int, Unit, Unit, Any, Option] + val instance99 = ServerEndpoint[Unit, Int, Unit, Any, Option] + List(instance0, instance1, instance2, instance3, instance4, instance5, instance6, instance7, instance8, instance9, instance10, instance11, instance12, instance13, instance14, instance15, instance16, instance17, instance18, instance19, instance20, instance21, instance22, instance23, instance24, instance25, instance26, instance27, instance28, instance29, instance30, instance31, instance32, instance33, instance34, instance35, instance36, instance37, instance38, instance39, instance40, instance41, instance42, instance43, instance44, instance45, instance46, instance47, instance48, instance49, instance50, instance51, instance52, instance53, instance54, instance55, instance56, instance57, instance58, instance59, instance60, instance61, instance62, instance63, instance64, instance65, instance66, instance67, instance68, instance69, instance70, instance71, instance72, instance73, instance74, instance75, instance76, instance77, instance78, instance79, instance80, instance81, instance82, instance83, instance84, instance85, instance86, instance87, instance88, instance89, instance90, instance91, instance92, instance93, instance94, instance95, instance96, instance97, instance98, instance99) + } +} + diff --git a/tests/pos/i20063/JFun.java b/tests/pos/i20063/JFun.java new file mode 100644 index 000000000000..5c22e4f48931 --- /dev/null +++ b/tests/pos/i20063/JFun.java @@ -0,0 +1,4 @@ +@FunctionalInterface +public interface JFun { + String f(String s); +} diff --git a/tests/pos/i20063/JTest.java b/tests/pos/i20063/JTest.java new file mode 100644 index 000000000000..13dd0fde55b1 --- /dev/null +++ b/tests/pos/i20063/JTest.java @@ -0,0 +1,3 @@ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class JTest { +} diff --git a/tests/pos/i20063/Stest.scala b/tests/pos/i20063/Stest.scala new file mode 100644 index 000000000000..75cbbc339f3d --- /dev/null +++ b/tests/pos/i20063/Stest.scala @@ -0,0 +1,5 @@ +object Stest { + println { + (new JTest, ((s: String) => s): JFun) + } +} diff --git a/tests/pos/i20063/TestInstance.java b/tests/pos/i20063/TestInstance.java new file mode 100644 index 000000000000..c6a6a0502b9a --- /dev/null +++ b/tests/pos/i20063/TestInstance.java @@ -0,0 +1,17 @@ +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface TestInstance { + enum Lifecycle { + PER_CLASS; + } + Lifecycle value(); +} 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/i20335.scala b/tests/pos/i20335.scala new file mode 100644 index 000000000000..81d1cbd9f760 --- /dev/null +++ b/tests/pos/i20335.scala @@ -0,0 +1,14 @@ +import java.time.OffsetDateTime +import scala.concurrent.duration.* + +val dateTime: OffsetDateTime = OffsetDateTime.now() + +implicit class DateTimeOps(val dateTime: OffsetDateTime) { + def plus(amount: FiniteDuration): OffsetDateTime = + dateTime +} + +def test = { + dateTime.plus(Duration.Zero) + dateTime.plus(if (true) Duration.Zero else Duration.Zero) +} \ No newline at end of file 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/i21176-a/Macro.scala b/tests/pos/i21176-a/Macro.scala new file mode 100644 index 000000000000..bd937f6292ac --- /dev/null +++ b/tests/pos/i21176-a/Macro.scala @@ -0,0 +1,12 @@ +//> using options -Wsafe-init +import scala.quoted.* + +class Macro: + def tuple[T](name: String): (String, List[T]) = (name, List[T]()) + inline def nameTuple[T]: (String, List[T]) = tuple(Macro.named) + +object Macro: + def namedMacro(using q: Quotes): Expr[String] = + Expr("test") + + inline def named: String = ${Macro.namedMacro} diff --git a/tests/pos/i21176-a/Main.scala b/tests/pos/i21176-a/Main.scala new file mode 100644 index 000000000000..3af179c4b698 --- /dev/null +++ b/tests/pos/i21176-a/Main.scala @@ -0,0 +1,7 @@ +//> using options -Wsafe-init +class Test extends Macro: + val abc = nameTuple[Int] + +@main +def run(): Unit = + println(new Test().abc) diff --git a/tests/pos/i21176-b/Macro.scala b/tests/pos/i21176-b/Macro.scala new file mode 100644 index 000000000000..34e63d610bd5 --- /dev/null +++ b/tests/pos/i21176-b/Macro.scala @@ -0,0 +1,9 @@ +import scala.quoted.* + +class Macro: + inline def nameTuple[NameTuple_T]: (String, List[NameTuple_T]) = Macro.tuple[NameTuple_T](Macro.named) + +object Macro: + def namedMacro(using q: Quotes): Expr[String] = Expr("test") + inline def named: String = ${Macro.namedMacro} + def tuple[Tuple_T](name: String): (String, List[Tuple_T]) = (name, List.empty[Tuple_T]) diff --git a/tests/pos/i21176-b/Main.scala b/tests/pos/i21176-b/Main.scala new file mode 100644 index 000000000000..561f43b32dda --- /dev/null +++ b/tests/pos/i21176-b/Main.scala @@ -0,0 +1,2 @@ +class Test extends Macro: + val abc = nameTuple[Int] diff --git a/tests/pos/i21433.scala b/tests/pos/i21433.scala new file mode 100644 index 000000000000..0efc4ac197ae --- /dev/null +++ b/tests/pos/i21433.scala @@ -0,0 +1,6 @@ +trait A[T]: + type R = T ?=> Unit + def f: R = () + +class B extends A[Int]: + override def f: R = () diff --git a/tests/pos/i21749.scala b/tests/pos/i21749.scala new file mode 100644 index 000000000000..6180b629f06b --- /dev/null +++ b/tests/pos/i21749.scala @@ -0,0 +1,9 @@ +//> using options -Xfatal-warnings -no-indent + +object Test { + 1 match { + case 1 => + case class Test(name: String) + Nil + } +} diff --git a/tests/pos/i21931.scala b/tests/pos/i21931.scala new file mode 100644 index 000000000000..d4deb93f4c2e --- /dev/null +++ b/tests/pos/i21931.scala @@ -0,0 +1,18 @@ +object Test { + def f() = { + val NotFound: Char = 'a' + class crashing() { + class issue() { + NotFound + } + class Module() { + val obligatory = { + def anonIssue = { + issue() + } + anonIssue + } + } + } + } +} 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/i21981.alt.scala b/tests/pos/i21981.alt.scala new file mode 100644 index 000000000000..0662575757a8 --- /dev/null +++ b/tests/pos/i21981.alt.scala @@ -0,0 +1,24 @@ +trait Ops[F[_], A]: + def map0[B](f0: A => B): F[B] = ??? + +trait Functor1[G[_]] + +trait Functor2[H[_]] + +trait Ref[I[_], +E] + +class Test: + given [J[_]](using J: Functor1[J]): Functor2[J] with + extension [K1, K2](jk: J[(K1, K2)]) + def map2[L](f2: (K1, K2) => L): J[L] = ??? + + def t1[ + M[_[t]], + N[_], + ](using N: Functor1[N]): Unit = + + val x3: Ops[N, M[[t] =>> Ref[N, t]]] = ??? + + val x2: N[(M[N], M[[t] =>> Ref[N, t]])] = x3 + .map0 { refs => (???, refs) } + .map2 { case (not, refs) => (???, refs) } diff --git a/tests/pos/i21981.contrak.scala b/tests/pos/i21981.contrak.scala new file mode 100644 index 000000000000..6ce64e6d3ddf --- /dev/null +++ b/tests/pos/i21981.contrak.scala @@ -0,0 +1,27 @@ +case class Inv[T](x: T) +class Contra[-ContraParam](x: ContraParam) + +trait Ops[F[_], A]: + def map0[B](f0: A => Contra[B]): F[B] = ??? + +trait Functor1[G[_]] + +trait Functor2[H[_]] + +trait Ref[I[_], +E] + +class Test: + given [J[_]](using J: Functor1[J]): Functor2[J] with + extension [K](jk: J[Contra[K]]) + def map2[L](f2: K => L): J[L] = ??? + + def t1[ + M[_[t]], + N[_], + ](using N: Functor1[N]): Unit = + + val x3: Ops[N, M[[t] =>> Ref[N, t]]] = ??? + + val x2: N[(M[N], M[[t] =>> Ref[N, t]])] = x3 + .map0 { refs => Contra[Contra[(Nothing, M[[t] =>> Ref[N, t]])]](???) } + .map2 { case (not, refs) => (???, refs) } diff --git a/tests/pos/i21981.orig.scala b/tests/pos/i21981.orig.scala new file mode 100644 index 000000000000..c02eb4848649 --- /dev/null +++ b/tests/pos/i21981.orig.scala @@ -0,0 +1,29 @@ +object internal: + trait Functor[F[_]] { + extension [T](ft: F[T]) def map[T1](f: T => T1): F[T1] + } + +object cats: + trait Functor[F[_]] + object Functor: + trait Ops[F[_], A]: + def map[B](f: A => B): F[B] = ??? + def toAllFunctorOps[F[_], A](target: F[A])(using Functor[F]): Ops[F, A] = ??? + +given [F[_]](using cf: cats.Functor[F]): internal.Functor[F] with { + extension [T](ft: F[T]) def map[T1](f: T => T1): F[T1] = ??? +} + +trait Ref[F[_], +T] +class MemoizingEvaluator[Input[_[_]], Output[_[_]], F[_]: cats.Functor] { + type OptionRef[T] = Ref[F, Option[T]] + + def sequence[CaseClass[_[_]], G[_], H[_]](instance: CaseClass[[t] =>> G[H[t]]]): G[CaseClass[H]] = ??? + def collectValues(input: Input[F]): F[(Input[F], Input[OptionRef])] = { + val refsF: Input[[t] =>> F[OptionRef[t]]] = ??? + for { + refs <- cats.Functor.toAllFunctorOps(sequence[Input, F, OptionRef](refsF)) + updating = ??? + } yield (updating, refs) + } +} diff --git a/tests/pos/i21981.scala b/tests/pos/i21981.scala new file mode 100644 index 000000000000..62962c7f1c56 --- /dev/null +++ b/tests/pos/i21981.scala @@ -0,0 +1,24 @@ +trait Ops[F[_], A]: + def map0[B](f0: A => B): F[B] = ??? + +trait Functor1[G[_]] + +trait Functor2[H[_]] + +trait Ref[I[_], +E] + +class Test: + given [J[_]](using J: Functor1[J]): Functor2[J] with + extension [K](jk: J[K]) + def map2[L](f2: K => L): J[L] = ??? + + def t1[ + M[_[t]], + N[_], + ](using N: Functor1[N]): Unit = + + val x3: Ops[N, M[[t] =>> Ref[N, t]]] = ??? + + val x2: N[(M[N], M[[t] =>> Ref[N, t]])] = x3 + .map0 { refs => (???, refs) } + .map2 { case (not, refs) => (???, refs) } diff --git a/tests/pos/i22061.scala b/tests/pos/i22061.scala new file mode 100644 index 000000000000..c42475ea5ea5 --- /dev/null +++ b/tests/pos/i22061.scala @@ -0,0 +1,55 @@ +object TraitExtendingClass { + + /*abstract*/ + class A()( + implicit + val x: Int = 3 + ) + + trait B extends A + + class C(implicit c: Int = 42) + + @main def main(): Unit = { + + val a = new A() + val b = new B {} // error happens here + val c = new C() + + println(b.x) + } +} +def f(i: Int)(j: Int)(implicit x: Int = 42) = x*(i+j) +def g = f(1)(1) + +abstract class A()(implicit val x: Int = 3) +class A1 extends A +class A2 extends A() + +abstract class B()(using y: Int = 9) +class B1 extends B +class B2 extends B() + +abstract class C(implicit val x: Int = 3) +class C1 extends C +class C2 extends C() + +abstract class D(using y: Int = 9) +class D1 extends D +class D2 extends D() + +abstract class E()(implicit val x: Int = 3, val y: Int = 4) +class E1 extends E +class E2 extends E() + +abstract class F()(using y: Int = 9, x: Int = 8) +class F1 extends F +class F2 extends F() + +abstract class G(implicit val x: Int = 3, val y: Int = 4) +class G1 extends G +class G2 extends G() + +abstract class H(using y: Int = 9, x: Int = 8) +class H1 extends H +class H2 extends H() diff --git a/tests/pos/i22193.scala b/tests/pos/i22193.scala new file mode 100644 index 000000000000..2ba7f920fbd7 --- /dev/null +++ b/tests/pos/i22193.scala @@ -0,0 +1,141 @@ + +def fn2(arg: String, arg2: String)(f: String => Unit): Unit = f(arg) + +def fn3(arg: String, arg2: String)(f: => Unit): Unit = f + +def test() = + + fn2(arg = "blue sleeps faster than tuesday", arg2 = "the quick brown fox jumped over the lazy dog"): env => + val x = env + println(x) + + // doesn't compile + fn2( + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog"): env => + val x = env + println(x) + + fn2( + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog"): env => + val x = env + println(x) + + fn2( + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog"): env => + val x = env + println(x) + + // does compile + fn2( + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog"): + env => + val x = env + println(x) + + // does compile + fn2( + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog" + ): env => + val x = env + println(x) + + fn2( + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog" + ): env => + val x = env + println(x) + + fn3( + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog"): + val x = "Hello" + println(x) + + fn3( + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog"): + val x = "Hello" + println(x) + + fn3( // arg at 3, body at 3 + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog"): + val x = "Hello" + println(x) + + fn3( // arg at 3, body at 1: not sure if sig indent of 1 is allowed, saw some comments from odersky + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog"): + val x = "Hello" + println(x) + + fn3( // arg at 3, body at 2: even if sig indent of 1 is not allowed, body is at fn3+2, not arg2-1 + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog"): + val x = "Hello" + println(x) + + fn3( // arg at 3, body at 4 + arg = "blue sleeps faster than tuesday", + arg2 = "the quick brown fox jumped over the lazy dog"): + val x = "Hello" + println(x) + +// don't turn innocent empty cases into functions +def regress(x: Int) = + x match + case 42 => + case _ => + +// previously lookahead calculated indent width at the colon +def k(xs: List[Int]) = + xs.foldLeft( + 0) + : (acc, x) => + acc + x + +def `test kit`(xs: List[Int]): Unit = + def addOne(i: Int): Int = i + 1 + def isPositive(i: Int): Boolean = i > 0 + // doesn't compile but would be nice + // first body is indented "twice", or, rather, first outdent establishes an intermediate indentation level + xs.map: x => + x + 1 + .filter: x => + x > 0 + xs.map: + addOne + .filter: + isPositive + + // does compile + xs + .map: x => + x + 1 + .filter: x => + x > 0 + + // does compile but doesn't look good, at least, to some people + xs.map: x => + x + 1 + .filter: x => + x > 0 + +def `tested kit`(xs: List[Int]): Unit = + { + def addOne(i: Int): Int = i.+(1) + def isPositive(i: Int): Boolean = i.>(0) + xs.map[Int]((x: Int) => x.+(1)).filter((x: Int) => x.>(0)) + xs.map[Int]((i: Int) => addOne(i)).filter((i: Int) => isPositive(i)) + xs.map[Int]((x: Int) => x.+(1)).filter((x: Int) => x.>(0)) + { + xs.map[Int]((x: Int) => x.+(1)).filter((x: Int) => x.>(0)) + () + } + } diff --git a/tests/pos/i22257.fixed.scala b/tests/pos/i22257.fixed.scala new file mode 100644 index 000000000000..d0fa1a0e81cb --- /dev/null +++ b/tests/pos/i22257.fixed.scala @@ -0,0 +1,52 @@ + +object Scaffold { + + trait Arrow + object Arrow { + trait Outbound extends Arrow + } + + trait NodeKOrGraphK {} + + trait NodeK extends NodeKOrGraphK { + + type FBound <: Induction + + protected def getInduction: Seq[FBound] + } + + trait Induction { + def arrow: Arrow + def node: NodeK + } + + object Induction { + + trait FP[+N <: NodeK] extends Induction { // short for "fixed point" + def node: N + } + } + + trait GraphK extends NodeKOrGraphK { + + type Batch[+T] <: Iterable[T] + + type _Node <: NodeK + + def entries: Batch[_Node] + } + + trait Topology { + + type Node = NodeK { type FBound <: Topology.this.FBound } + trait Node_ extends NodeK { + type FBound = Topology.this.FBound + } + + type FP = Induction.FP[Node] + type FBound <: FP + + type Graph = GraphK { type _Node <: Node } + } + +} diff --git a/tests/pos/i22257.orig.scala b/tests/pos/i22257.orig.scala new file mode 100644 index 000000000000..0be8d02d5a85 --- /dev/null +++ b/tests/pos/i22257.orig.scala @@ -0,0 +1,52 @@ + +object Scaffold { + + trait Arrow + object Arrow { + trait Outbound extends Arrow + } + + trait NodeKOrGraphK {} + + trait NodeK extends NodeKOrGraphK { + + type FBound <: Induction + + protected def getInduction: Seq[FBound] + } + + trait Induction { + def arrow: Arrow + def node: NodeK + } + + object Induction { + + trait FP[+N <: NodeK] extends Induction { // short for "fixed point" + def node: N + } + } + + trait GraphK extends NodeKOrGraphK { + + type Batch[+T] <: Iterable[T] + + type _Node <: NodeK + + def entries: Batch[_Node] + } + + trait Topology { + + type FP = Induction.FP[Node] + type FBound <: FP + + type Node = NodeK { type FBound <: Topology.this.FBound } + trait Node_ extends NodeK { + type FBound = Topology.this.FBound + } + + type Graph = GraphK { type _Node <: Node } + } + +} diff --git a/tests/pos/i22257.scala b/tests/pos/i22257.scala new file mode 100644 index 000000000000..8cd797529097 --- /dev/null +++ b/tests/pos/i22257.scala @@ -0,0 +1,26 @@ +trait NodeK { type FBound } +trait Fixed[+N <: NodeK] + +type Bound1 <: FP1 +type FP1 = Fixed[Node1] +type Node1 = NodeK { type FBound <: Bound1 } // was-error + +type FP2 = Fixed[Node2] // was-error +type Bound2 <: FP2 +type Node2 = NodeK { type FBound <: Bound2 } + +type Node3 = NodeK { type FBound <: Bound3 } +type FP3 = Fixed[Node3] +type Bound3 <: FP3 + +type Bound4 <: FP4 +type Node4 = NodeK { type FBound <: Bound4 } // was-error +type FP4 = Fixed[Node4] + +type FP5 = Fixed[Node5] // was-error +type Node5 = NodeK { type FBound <: Bound5 } +type Bound5 <: FP5 + +type Node6 = NodeK { type FBound <: Bound6 } +type Bound6 <: FP6 +type FP6 = Fixed[Node6] diff --git a/tests/pos/i22266.scala b/tests/pos/i22266.scala new file mode 100644 index 000000000000..2ecdf9492a93 --- /dev/null +++ b/tests/pos/i22266.scala @@ -0,0 +1,22 @@ +sealed trait NonPolygon +sealed trait Polygon + +sealed trait SymmetryAspect +sealed trait RotationalSymmetry extends SymmetryAspect +sealed trait MaybeRotationalSymmetry extends SymmetryAspect + +enum Shape: + case Circle extends Shape with NonPolygon with RotationalSymmetry + case Triangle extends Shape with Polygon with MaybeRotationalSymmetry + case Square extends Shape with Polygon with RotationalSymmetry + +object Shape: + + def hasPolygon( + rotationalSyms: Vector[Shape & RotationalSymmetry], + maybeSyms: Vector[Shape & MaybeRotationalSymmetry] + ): Boolean = + val all = rotationalSyms.concat(maybeSyms) + all.exists: + case _: Polygon => true + case _ => false diff --git a/tests/pos/i22266.unenum.scala b/tests/pos/i22266.unenum.scala new file mode 100644 index 000000000000..e7529b7edabe --- /dev/null +++ b/tests/pos/i22266.unenum.scala @@ -0,0 +1,22 @@ +sealed trait NonPolygon +sealed trait Polygon + +sealed trait SymmetryAspect +sealed trait RotationalSymmetry extends SymmetryAspect +sealed trait MaybeRotationalSymmetry extends SymmetryAspect + +sealed abstract class Shape + +object Shape: + case object Circle extends Shape with NonPolygon with RotationalSymmetry + case object Triangle extends Shape with Polygon with MaybeRotationalSymmetry + case object Square extends Shape with Polygon with RotationalSymmetry + + def hasPolygon( + rotationalSyms: Vector[Shape & RotationalSymmetry], + maybeSyms: Vector[Shape & MaybeRotationalSymmetry] + ): Boolean = + val all = rotationalSyms.concat(maybeSyms) + all.exists: + case _: Polygon => true + case _ => false diff --git a/tests/pos/i22332.scala b/tests/pos/i22332.scala new file mode 100644 index 000000000000..1b0b6a370329 --- /dev/null +++ b/tests/pos/i22332.scala @@ -0,0 +1,5 @@ + +object Foo: + val foo = 42 + // one space + \ No newline at end of file diff --git a/tests/pos/i22408.scala b/tests/pos/i22408.scala new file mode 100644 index 000000000000..17fd0fbb474d --- /dev/null +++ b/tests/pos/i22408.scala @@ -0,0 +1,11 @@ +object Obj: + @scala.annotation.static + val some_static_value: Int = { + val some_local_value: Int = { + val some_local_value_1 = ??? + some_local_value_1 + } + some_local_value + } + +class Obj diff --git a/tests/pos/i22436/atest.scala b/tests/pos/i22436/atest.scala new file mode 100644 index 000000000000..7f77a5562385 --- /dev/null +++ b/tests/pos/i22436/atest.scala @@ -0,0 +1,3 @@ +object Case1 { + def myProps(transport: ProtocolTransport): Unit = ??? +} diff --git a/tests/pos/i22436/defs.scala b/tests/pos/i22436/defs.scala new file mode 100644 index 000000000000..c4bc3d74df25 --- /dev/null +++ b/tests/pos/i22436/defs.scala @@ -0,0 +1,7 @@ +object ProtocolTransport + +import ProtocolTransport.* + +@annotation.nowarn() +class ProtocolTransport() + diff --git a/tests/pos/i22468.scala b/tests/pos/i22468.scala new file mode 100644 index 000000000000..265d6d3a89b7 --- /dev/null +++ b/tests/pos/i22468.scala @@ -0,0 +1,10 @@ +import Result.* +opaque type Result[+E, +A] = Success[A] | Error[E] + +object Result: + opaque type Success[+A] = A + sealed abstract class Error[+E] + + extension [E, A](self: Result[E, A]) + inline def transform[B]: B = ??? + def problem: Boolean = transform[Boolean] diff --git a/tests/pos/i22470.scala b/tests/pos/i22470.scala new file mode 100644 index 000000000000..83599f2564fc --- /dev/null +++ b/tests/pos/i22470.scala @@ -0,0 +1,17 @@ +trait A +trait OuterClass +trait MidClass +trait InnerClass + +object Obj: + def outerDef(a: A) = + new OuterClass { + def midDef(): Unit = { + new MidClass { + val valdef = new InnerClass { + def innerDef() = + println(a) + } + } + } + } diff --git a/tests/pos/i22513.scala b/tests/pos/i22513.scala new file mode 100644 index 000000000000..2cf2bc58227a --- /dev/null +++ b/tests/pos/i22513.scala @@ -0,0 +1,27 @@ +opaque type R[T] <: T = T + +object Test { + enum E: + case A(a: Int) + + val v: R[E] = ??? + v match + case E.A(_) => +} + +sealed trait Foo + +case class FooA() extends Foo +case class FooB() extends Foo + +object O { + opaque type OpaqueFoo <: Foo = Foo + def fooB(): OpaqueFoo = FooB() +} + +@main def main = + val p: O.OpaqueFoo = O.fooB() + + p match + case _: FooA => println("fooA") + case _: FooB => println("fooB") diff --git a/tests/pos/i22518.scala b/tests/pos/i22518.scala new file mode 100644 index 000000000000..d530159701c4 --- /dev/null +++ b/tests/pos/i22518.scala @@ -0,0 +1,9 @@ +sealed trait Foo[T] +class Bar extends Foo[?] + +def mkFoo[T]: Foo[T] = + ??? + +def test: Unit = + mkFoo match + case _ => () diff --git a/tests/pos/i22527.scala b/tests/pos/i22527.scala new file mode 100644 index 000000000000..1ed3f29d83c5 --- /dev/null +++ b/tests/pos/i22527.scala @@ -0,0 +1,104 @@ +//> using options -Werror + +import annotation.* + +def f: Unit = + identity( + identity{ + class X extends AnyRef, Serializable + 42 + } + ) + +def test: Unit = + assert( + identity{ + true}, + "ok" + ) + +def toss: Unit = + assert( + throw + null, + "ok" + ) +def raise: Unit = + assert( + throw + null, "ok" // ok now + ) + +def callme[A](x: => A, msg: String) = try x.toString catch case t: RuntimeException => msg + +def orElse(x: Int): Unit = + callme( + if x > 0 then + true + else + false, "fail") + +@nowarn("id=E190&msg=Discarded non-Unit value") +def onlyIf(x: Int): Unit = + callme( + if (x > 0) then // then syntax + true, "fail") // warn value discard + +def h(xs: List[Int]) = + xs.foldLeft(0) + ( + (acc, x) => + acc + + x, + ) + +def sum(x: Int, y: Int, z: Int) = x+y+z + +def k(xs: List[Int], y: Int, z: Int) = + xs.foldLeft(0) + ( + (acc, x) => + sum( + x + + y + + z, + y, + z, + ) + ) + +object `arrow eol`: + def f(g: Int => Int, i: Int): Int = g(i) + def g(map: Int => Int): Int => Int = map + def test = + f( + g: x => + x + 1, 42 + ) + +def test2: Unit = + assert( + identity: + true, "ok" // NO error end of statement expected but ',' found + ) + +def orElse2(x: Int): Unit = + callme( + if x > 0 then + class X extends AnyRef, Serializable // NO error Not found: Serializable - did you mean Specializable? + true // NO error ',' or ')' expected, but 'true' found + else + false, "fail") + +def g: Unit = + identity( + x = + class X extends AnyRef, Serializable // NO error + 27 // NO error + ) + +@nowarn("id=E190&msg=Discarded non-Unit value") +def onlyIf2(x: Int): Unit = + callme( + if (x > 0) + true, "fail") // NO error syntax is OK after old-style conditional diff --git a/tests/pos/i22528.scala b/tests/pos/i22528.scala new file mode 100644 index 000000000000..00b2ef5a5edf --- /dev/null +++ b/tests/pos/i22528.scala @@ -0,0 +1,8 @@ +trait Bar +trait Baz + +def f = + identity( + locally: + trait Foo extends Bar, Baz + ) \ No newline at end of file diff --git a/tests/pos/i22548.scala b/tests/pos/i22548.scala new file mode 100644 index 000000000000..beb878d92670 --- /dev/null +++ b/tests/pos/i22548.scala @@ -0,0 +1,2 @@ +trait Bar[T] +class Foo[T <: Bar[T]] (private val buffer: Any) extends AnyVal diff --git a/tests/pos/i22560.scala b/tests/pos/i22560.scala new file mode 100644 index 000000000000..af4382ba5a15 --- /dev/null +++ b/tests/pos/i22560.scala @@ -0,0 +1,32 @@ + +package companionless: + + class Enumeration: + protected class Val(i: Int): + def this() = this(42) + + class Test extends Enumeration: + val Hearts = Val(27) + val Diamonds = Val() + + +package companioned: + + class Enumeration: + protected class Val(i: Int): + def this() = this(42) + protected object Val + + class Test extends Enumeration: + val Hearts = Val(27) + val Diamonds = Val() + +package p: + + package internal: + + protected[p] class P(i : Int) + private[p] class Q(i : Int) + + def f = internal.P(42) + def g = internal.Q(42) diff --git a/tests/pos/i22560b/client_2.scala b/tests/pos/i22560b/client_2.scala new file mode 100644 index 000000000000..5376bcfa03cd --- /dev/null +++ b/tests/pos/i22560b/client_2.scala @@ -0,0 +1,17 @@ + +package companionless: + + class Test extends Enumeration: + val Hearts = Val(27) + val Diamonds = Val() + + +package companioned: + + class Test extends Enumeration: + val Hearts = Val(27) + val Diamonds = Val() + +package p: + + def f = new internal.P(42) diff --git a/tests/pos/i22560b/lib_1.scala b/tests/pos/i22560b/lib_1.scala new file mode 100644 index 000000000000..d247d63ec9cf --- /dev/null +++ b/tests/pos/i22560b/lib_1.scala @@ -0,0 +1,19 @@ + +package companionless: + + class Enumeration: + protected class Val(i: Int): + def this() = this(42) + +package companioned: + + class Enumeration: + protected class Val(i: Int): + def this() = this(42) + protected object Val + +package p: + + package internal: + + protected[p] class P(i : Int) diff --git a/tests/pos/i22592.scala b/tests/pos/i22592.scala new file mode 100644 index 000000000000..f6a1f2eff696 --- /dev/null +++ b/tests/pos/i22592.scala @@ -0,0 +1,15 @@ +import scala.quoted.* + +trait Foo: + def inherited = () + +object Bar extends Foo: + def local = () + def localArg(arg: Any) = () + + def macro1(using Quotes): Expr[Unit] = '{ local } + def macro2(using Quotes): Expr[Unit] = '{ Bar.inherited } + def macro3(using Quotes): Expr[Unit] = '{ inherited } + def macro4(using Quotes): Expr[Unit] = '{ this.local } + def macro5(using Quotes): Expr[Unit] = '{ this.inherited } + def macro6(using Quotes): Expr[Unit] = '{ localArg(this) } diff --git a/tests/pos/i22593a.scala b/tests/pos/i22593a.scala new file mode 100644 index 000000000000..a1cf650193c0 --- /dev/null +++ b/tests/pos/i22593a.scala @@ -0,0 +1,16 @@ +import scala.quoted.* + +package jam { + trait JamCoreDsl { + implicit inline def defaultJamConfig: this.JamConfig = + new JamConfig(brewRecRegex = ".*") + class JamConfig(val brewRecRegex: String) + inline def brew(implicit inline config: JamConfig): Unit = ??? + } + private object internal extends JamCoreDsl + export internal._ +} + +object test { + jam.brew +} diff --git a/tests/pos/i22593b/Main.scala b/tests/pos/i22593b/Main.scala new file mode 100644 index 000000000000..dcc28e6a767c --- /dev/null +++ b/tests/pos/i22593b/Main.scala @@ -0,0 +1,27 @@ +import scala.quoted.* + +package jam { + trait JamCoreDsl { + implicit inline def defaultJamConfig: this.JamConfig = + new JamConfig(brewRecRegex = ".*") + class JamConfig(val brewRecRegex: String) + inline def brew(implicit inline config: JamConfig): Unit = ${ brewImpl() } + } + private object internal extends JamCoreDsl + export internal._ + + def brewImpl(using q: Quotes)(): Expr[Unit] = { + findSelf + '{()} + } + + private def findSelf(using q: Quotes): Unit = { + import q.reflect.* + def rec(s: Symbol): Option[Symbol] = s.maybeOwner match { + case o if o.isNoSymbol => None + case o if o.isClassDef => Option(o) + case o => rec(o) + } + rec(Symbol.spliceOwner) + } +} \ No newline at end of file diff --git a/tests/pos/i22593b/Test.scala b/tests/pos/i22593b/Test.scala new file mode 100644 index 000000000000..73c2f9ad7cb3 --- /dev/null +++ b/tests/pos/i22593b/Test.scala @@ -0,0 +1,3 @@ +object CoreSpec { + jam.brew +} \ No newline at end of file diff --git a/tests/pos/i22608.scala b/tests/pos/i22608.scala new file mode 100644 index 000000000000..e4b49e87769f --- /dev/null +++ b/tests/pos/i22608.scala @@ -0,0 +1,48 @@ + +def f(i: Int) = i +def g(i: Int, j: Int) = i+j + +def t = + val y = f( + if (true)// then + val x = 1 + 5 + else 7 + ) + y + +def u(j: Int) = + val y = g( + if (true)// then + val x = 1 + 5 + else 7, + j + ) + y + +def b(k: Boolean): Int = + f( + if ( + k + && b(!k) > 0 + ) then 27 + else 42 + ) + +def p(b: Boolean) = + import collection.mutable.ListBuffer + val xs, ys = ListBuffer.empty[String] + (if (b) + xs + else + ys) += "hello, world" + (xs.toString, ys.toString) + +def q(b: Boolean) = + import collection.mutable.ListBuffer + val xs, ys = ListBuffer.empty[String] + (if (b) + then xs + else ys) += "hello, world" + (xs.toString, ys.toString) diff --git a/tests/pos/i22648.scala b/tests/pos/i22648.scala new file mode 100644 index 000000000000..e1c7f73bb15e --- /dev/null +++ b/tests/pos/i22648.scala @@ -0,0 +1,14 @@ +import scala.quoted.* + +def fooImpl(using Quotes): Expr[Any] = + '{ + new AnyRef { + type T = Unit + def make: T = () + def take(t: T): Unit = () + }: { + type T + def make: T + def take(t: T): Unit + } + } diff --git a/tests/pos/i22676.scala b/tests/pos/i22676.scala new file mode 100644 index 000000000000..5654559b133f --- /dev/null +++ b/tests/pos/i22676.scala @@ -0,0 +1,11 @@ +package example + +trait Example { + class Input + + type Output[A] = A match { + case Input => Int + } +} + +class Ref(ref: Example#Input) diff --git a/tests/pos/i22713.scala b/tests/pos/i22713.scala new file mode 100644 index 000000000000..ec3e12cebaec --- /dev/null +++ b/tests/pos/i22713.scala @@ -0,0 +1,13 @@ +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.TimeUnit + +def Test = { + val worker: ScheduledExecutorService = ??? + object Worker extends Runnable { + def fails(): Either[Exception, Unit] = Right(worker.schedule(this, 5, TimeUnit.NANOSECONDS)) + def works(): Either[Exception, Unit] = Right { + worker.schedule(this, 5, TimeUnit.NANOSECONDS); () + } + def run(): Unit = ??? + } +} diff --git a/tests/pos/i22724.scala b/tests/pos/i22724.scala new file mode 100644 index 000000000000..6146e91b88ca --- /dev/null +++ b/tests/pos/i22724.scala @@ -0,0 +1,17 @@ +import java.util.concurrent.atomic.AtomicReference + +object UnboundedHub: + final class Node[A](var value: A, val pointer: AtomicReference[Pointer[A]]) + final case class Pointer[A](node: Node[A], subscribers: Int) + +private final class UnboundedHub[A]: + import UnboundedHub.* + + val publisherHead: AtomicReference[Node[A]] = new AtomicReference( + new Node[A]( + null.asInstanceOf[A], + new AtomicReference( + Pointer(null, 0) // error: too many arguments for constructor AtomicReference + ) + ) + ) \ No newline at end of file diff --git a/tests/pos/i22879.scala b/tests/pos/i22879.scala new file mode 100644 index 000000000000..121ff603c398 --- /dev/null +++ b/tests/pos/i22879.scala @@ -0,0 +1,9 @@ +sealed trait E[S] +final case class I[T, U <: Iterable[T]]() extends E[U] +class Test { + def test[X](a: E[X]): Unit = { + a match { + case I() => ??? + } + } +} diff --git a/tests/pos/i22922.scala b/tests/pos/i22922.scala new file mode 100644 index 000000000000..a65c99d6bd46 --- /dev/null +++ b/tests/pos/i22922.scala @@ -0,0 +1,57 @@ +abstract class Computation[+A, -U] +type !![+A, -U] = Computation[A, U] +type Const[C] = [_] =>> C + +final class EffectImpl[Fx]: + sealed trait ThisInterpreter extends Interpreter.Unsealed: + final override type Elim = Fx + abstract class Stateless[F[+_], G[+_], Fx] + extends Interpreter.Stateless[F, G, Fx] + with ThisInterpreter + +trait Effect: + val impl: EffectImpl[this.type] = ??? + +trait SourceEffect[O] extends Effect: + abstract class Stateless[U] extends StatelessReturn[Unit, U] + abstract class StatelessReturn[R, U] extends impl.Stateless[Const[Unit], Const[R], U] + +sealed trait Handler[From[+_], To[+_], Elim, Intro]: + final def handle[V] = new HandleSyntax[V] + final class HandleSyntax[V]: + def apply[A, W](comp: From[A] !! W)(using CanPartiallyHandle[V, W, Elim]): To[A] !! (V & Intro) = ??? + +sealed trait CanPartiallyHandle[U, V, W] // missing in StreamImpl.map +object CanPartiallyHandle: + given [U, V, W](using (W & U) <:< V): CanPartiallyHandle[U, V, W] = ??? + +sealed trait Interpreter: + type From[+A] + type To[+A] + type Elim + type Intro + + final def toHandler: Handler[From, To, Elim, Intro] = ??? +object Interpreter: + trait Unsealed extends Interpreter + abstract class Stateless[F[+_], G[+_], Fx] extends Interpreter: + final override type From[+A] = F[A] + final override type To[+A] = G[A] + final override type Intro = Fx + +object Syntax: + extension [U](comp: Unit !! U) + def asStream[A, V](fx: SourceEffect[A])(using (fx.type & V) =:= U): Stream[A, V] = ??? + +sealed abstract class Stream[+A, -U]: + def map[B](f: A => B): Stream[B, U] + +import Syntax.* +final case class StreamImpl[A, U](Fx: SourceEffect[A])(val compute: Unit !! (U & Fx.type)) + extends Stream[A, U]: + type Fx = Fx.type + override def map[B](f: A => B): Stream[B, U] = + case object Fx2 extends SourceEffect[B] + new Fx.Stateless[Fx2.type] {}.toHandler + .handle(compute) + .asStream(Fx2) // error diff --git a/tests/pos/i22923.scala b/tests/pos/i22923.scala new file mode 100644 index 000000000000..85d6f9281687 --- /dev/null +++ b/tests/pos/i22923.scala @@ -0,0 +1,10 @@ +object Test { + object E1 { + def unapply(x: Int): Some[Tuple.Map[(Int, Int), Option]] = ??? + } + + val s: Int = ??? + s match { + case E1(x, y) => x + } +} diff --git a/tests/pos/i22974a/Maybe_1.scala b/tests/pos/i22974a/Maybe_1.scala new file mode 100644 index 000000000000..bd4964b29582 --- /dev/null +++ b/tests/pos/i22974a/Maybe_1.scala @@ -0,0 +1,14 @@ +package pack +import Maybe._ +opaque type Maybe[+A] >: (Absent | Present[A]) = Absent | Present[A] +object Maybe: + sealed abstract class Absent + case object Absent extends Absent + object internal: + case class PresentAbsent(val depth: Int) + opaque type Present[+A] = A | internal.PresentAbsent + + extension [A](self: Maybe[A]) { + inline def flatten[B]: Maybe[B] = ??? + inline def isDefined: Boolean = ??? + } diff --git a/tests/pos/i22974a/macro_1.scala b/tests/pos/i22974a/macro_1.scala new file mode 100644 index 000000000000..61d7783bd38a --- /dev/null +++ b/tests/pos/i22974a/macro_1.scala @@ -0,0 +1,6 @@ +import scala.quoted._ + +inline def passThorugh(inline condition: Boolean): Any = + ${ passThorughImpl('{condition}) } + +def passThorughImpl(condition: Expr[Boolean])(using Quotes): Expr[Any] = condition diff --git a/tests/pos/i22974a/main_2.scala b/tests/pos/i22974a/main_2.scala new file mode 100644 index 000000000000..a0b55aff7f52 --- /dev/null +++ b/tests/pos/i22974a/main_2.scala @@ -0,0 +1,6 @@ +object Test { + def main(): Unit = + import pack.Maybe + val res: Maybe[Maybe[Int]] = ??? + passThorugh(res.flatten.isDefined) +} diff --git a/tests/pos/i22974b.scala b/tests/pos/i22974b.scala new file mode 100644 index 000000000000..4f6e23c0f97d --- /dev/null +++ b/tests/pos/i22974b.scala @@ -0,0 +1,18 @@ +object outer: + opaque type Queue = Queue.Unsafe + object Queue: + abstract class Unsafe + opaque type Unbounded = Queue + object Unbounded: + inline def initWith()(f: Unbounded => Unit): Unit = + f(Unsafe.init()) + + opaque type Unsafe <: Queue.Unsafe = Queue + object Unsafe: + def init[A](): Unsafe = ??? + +object Resource: + def run: Unit = + outer.Queue.Unbounded.initWith() { q => + ??? + } diff --git a/tests/pos/i22974c.scala b/tests/pos/i22974c.scala new file mode 100644 index 000000000000..77833340de19 --- /dev/null +++ b/tests/pos/i22974c.scala @@ -0,0 +1,18 @@ +object other: + sealed abstract class Absent + case object Absent extends Absent + case class PresentAbsent(val depth: Int) + opaque type Present[+A] = A | PresentAbsent + opaque type Maybe[+A] >: (Absent | Present[A]) = Absent | Present[A] + + extension [A](self: Maybe[A]) { + inline def flatten[B]: Maybe[B] = if self.isEmpty then Absent else ??? + def isEmpty: Boolean = self.isInstanceOf[Absent] + } + +class Test { + def main(): Unit = + import other.Maybe + val res: Maybe[Maybe[Int]] = ??? + res.flatten +} diff --git a/tests/pos/i23011.scala b/tests/pos/i23011.scala new file mode 100644 index 000000000000..86c49c086b42 --- /dev/null +++ b/tests/pos/i23011.scala @@ -0,0 +1,16 @@ + +type X = Double => Unit +def g(x: Double): Unit = ??? +def g(x: String): Unit = ??? + +def f1(x: X) = ??? + +def f2(x: X, y: Int) = ??? +def f2(x: X, y: Boolean) = ??? + +def f3(x: X)(y: Int) = ??? +def f3(x: X)(y: Boolean) = ??? + +val r1 = f1(g) // ok +val r2 = f2(g, 1) // ok +val r3 = f3(g)(1) // was error: ambiguous overload for g diff --git a/tests/pos/i23025.scala b/tests/pos/i23025.scala new file mode 100644 index 000000000000..23a2eb78613d --- /dev/null +++ b/tests/pos/i23025.scala @@ -0,0 +1,5 @@ + +class A { + def f: PartialFunction[Int, Int] = + a => { (try a catch { case e : Throwable => throw e}) match { case n => n } } +} diff --git a/tests/pos/i23032.scala b/tests/pos/i23032.scala new file mode 100644 index 000000000000..7e56d0f8b153 --- /dev/null +++ b/tests/pos/i23032.scala @@ -0,0 +1,2 @@ +def f[F[_], T, U]: F[T] | F[U] = ??? +def x = f.toString diff --git a/tests/pos/i23054.scala b/tests/pos/i23054.scala new file mode 100644 index 000000000000..8d5bf9de12fb --- /dev/null +++ b/tests/pos/i23054.scala @@ -0,0 +1,15 @@ + +object Bug: + + def m0(f: PartialFunction[Char, Unit]): Unit = () + + def m1(): Unit = + m0: x => + "abc".filter(_ == x) match + case _ => () + + def m2(): Unit = + m0: x => + x match + case _ => () + diff --git a/tests/pos/i23086.scala b/tests/pos/i23086.scala new file mode 100644 index 000000000000..2f8ee8689ad4 --- /dev/null +++ b/tests/pos/i23086.scala @@ -0,0 +1,2 @@ +class Test: + val _: this.type = summon[ValueOf[this.type]].value diff --git a/tests/pos/i23095/defs.scala b/tests/pos/i23095/defs.scala new file mode 100644 index 000000000000..085c63019ef6 --- /dev/null +++ b/tests/pos/i23095/defs.scala @@ -0,0 +1,18 @@ +import scala.compiletime.* +import scala.deriving.* + +trait SeqStringCodec[A]: + def decode(value: Seq[String]): Either[Any, A] + +trait UrlForm +trait FormCodec[A] + +object FormCodec { + inline given derived[A](using p: Mirror.ProductOf[A]): FormCodec[A] = + val codecs = summonAll[Tuple.Map[p.MirroredElemTypes, SeqStringCodec]].toArray + val values = codecs.map { + _.asInstanceOf[SeqStringCodec[?]] + .decode(List.empty[String]) + } + ??? +} diff --git a/tests/pos/i23095/test.scala b/tests/pos/i23095/test.scala new file mode 100644 index 000000000000..cf7a321cbcdf --- /dev/null +++ b/tests/pos/i23095/test.scala @@ -0,0 +1,3 @@ +given SeqStringCodec[Int] = ??? +given SeqStringCodec[String] = ??? +final case class Form(int: Int, string: String) derives FormCodec diff --git a/tests/pos/i23113.scala b/tests/pos/i23113.scala new file mode 100644 index 000000000000..20a9aaed7bbb --- /dev/null +++ b/tests/pos/i23113.scala @@ -0,0 +1,10 @@ +//> using options -Werror +sealed trait P +object P { + private abstract class B extends P +} + +def foo(opt: Option[P]): Unit = opt match { + case Some(_) => ??? + case None => ??? +} 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/i23155b.scala b/tests/pos/i23155b.scala new file mode 100644 index 000000000000..bf1e4542681c --- /dev/null +++ b/tests/pos/i23155b.scala @@ -0,0 +1,6 @@ +object Unpack_T { + (1, 2) match { + case Unpack_T(first, _) => first + } + def unapply(e: (Int, Int)): Some[Int *: Int *: EmptyTuple] = ??? +} diff --git a/tests/pos/i23194.scala b/tests/pos/i23194.scala new file mode 100644 index 000000000000..347b98efd877 --- /dev/null +++ b/tests/pos/i23194.scala @@ -0,0 +1,14 @@ +class R[T] extends annotation.StaticAnnotation + +class A[T]: + val next: A[T] = null + val self: this.type = this + val selfnext: this.next.type = this.next + def f: (A[T] @R[this.type], A[T] @R[this.next.type]) = ??? + def g: (A[T] @R[self.type], A[T] @R[selfnext.type]) = ??? + +class Test: + def test = + val (a, b) = A[String]().f + val (a2, b2) = A[String]().g + \ No newline at end of file diff --git a/tests/pos/i23217.scala b/tests/pos/i23217.scala new file mode 100644 index 000000000000..cf3427a2649e --- /dev/null +++ b/tests/pos/i23217.scala @@ -0,0 +1,20 @@ +trait HasA[T]: + type A = T + +object Test1: + def foo1(h: HasA[?])(a: h.A): Unit = {} + + def foo2(h1: HasA[?])(a1: h1.A): Unit = + foo1(h1)(a1) + + def foo3(h1: HasA[?], a1: h1.A): Unit = + foo1(h1)(a1) + +object Test2: + def bar1(h: HasA[?], a: h.A): Unit = {} + + def bar2(h1: HasA[?], a1: h1.A): Unit = + bar1(h1, a1) + + def foo3(h1: HasA[?])(a1: h1.A): Unit = + bar2(h1, a1) diff --git a/tests/pos/i23237a.scala b/tests/pos/i23237a.scala new file mode 100644 index 000000000000..bcac4c6f7050 --- /dev/null +++ b/tests/pos/i23237a.scala @@ -0,0 +1,29 @@ +sealed trait Dim +trait _2D extends Dim +trait _3D extends Dim + +sealed abstract class IntVector[D] +object IntVector{ + def apply[D](d: Array[Int]): IntVector[D] = ??? + def apply(x: Int, y: Int): IntVector2D = IntVector2D(x, y) + def apply(x: Int, y: Int, z: Int): IntVector3D = IntVector3D(x, y, z) +} +case class IntVector2D(i: Int, j: Int) extends IntVector[_2D] +case class IntVector3D(i: Int, j: Int, k: Int) extends IntVector[_3D] + +type DiscreteImage[D, A] = DiscreteField[D, DiscreteImageDomain, A] +class DiscreteField[D, DDomain[D] <: DiscreteDomain[D], A](val domain: DDomain[D], val data: IndexedSeq[A]) extends PartialFunction[PointId, A] { + override def apply(v1: PointId) = ??? + override def isDefinedAt(ptId: PointId) = ??? +} +object DiscreteField{ + implicit class DiscreteImageOps[D, A](discreteField: DiscreteField[D, DiscreteImageDomain, A]) { + def apply(idx: IntVector[D]): A = ??? + def isDefinedAt(idx: IntVector[D]): Boolean = ??? + } +} +trait DiscreteDomain[D] +trait DiscreteImageDomain[D] extends DiscreteDomain[D] +final case class PointId(id: Int) extends AnyVal + +def test[S](img: DiscreteImage[_3D, S]) = img(IntVector(1, 2, 3)) diff --git a/tests/pos/i23237b.scala b/tests/pos/i23237b.scala new file mode 100644 index 000000000000..b49df924e691 --- /dev/null +++ b/tests/pos/i23237b.scala @@ -0,0 +1,23 @@ +object types{ + final case class vec2(x: Float, y: Float) + final case class vec4(x: Float, y: Float, z: Float, w: Float) + object vec4: + def apply(xy: vec2, z: Float, w: Float): vec4 = vec4(xy.x, xy.y, z, w) + + opaque type Shader[In, Out] = In => Out + object Shader: + inline def apply[In, Out](f: In => Out): Shader[In, Out] = f + inline def apply[In](f: In => Unit): Shader[In, Unit] = f + inline def apply(body: => Any): Shader[Unit, Unit] = (_: Unit) => body +} +import types.* + +class GLSLEnvTests { + case class FragEnv(UV: vec2) + + inline def fragment: Shader[FragEnv, vec4] = + Shader { env => + val x = env.UV + vec4(env.UV, 0.0f, 1.0f) + } +} diff --git a/tests/pos/i23261.scala b/tests/pos/i23261.scala new file mode 100644 index 000000000000..04b0e3a2f93c --- /dev/null +++ b/tests/pos/i23261.scala @@ -0,0 +1,46 @@ +type DoubleToString[D <: Double] <: String = D match + case 0.0 => "0.0" + case -0.0 => "-0.0" + case _ => "_" + +type DoubleToString2[D <: Double] <: String = D match + case 0.0 => "0.0" + case _ => "_" + +type DoubleToString3[D <: Double] <: String = D match + case -0.0 => "-0.0" + case _ => "_" + +type FloatToString[F <: Float] <: String = F match + case 0.0f => "0.0f" + case -0.0f => "-0.0f" + case _ => "_" + +type FloatToString2[F <: Float] <: String = F match + case 0.0f => "0.0f" + case _ => "_" + +type FloatToString3[F <: Float] <: String = F match + case -0.0f => "-0.0f" + case _ => "_" + +@main def main(): Unit = { + summon[0.0 =:= 0.0] + summon[-0.0 =:= -0.0] + summon[DoubleToString[0.0] =:= "0.0"] + summon[DoubleToString[-0.0] =:= "-0.0"] + summon[DoubleToString[3.14] =:= "_"] + summon[DoubleToString2[0.0] =:= "0.0"] + summon[DoubleToString2[-0.0] =:= "_"] + summon[DoubleToString3[-0.0] =:= "-0.0"] + summon[DoubleToString3[0.0] =:= "_"] + summon[0.0f =:= 0.0f] + summon[-0.0f =:= -0.0f] + summon[FloatToString[0.0f] =:= "0.0f"] + summon[FloatToString[-0.0f] =:= "-0.0f"] + summon[FloatToString[3.14f] =:= "_"] + summon[FloatToString2[0.0f] =:= "0.0f"] + summon[FloatToString2[-0.0f] =:= "_"] + summon[FloatToString3[-0.0f] =:= "-0.0f"] + summon[FloatToString3[0.0f] =:= "_"] +} diff --git a/tests/pos/i23310.scala b/tests/pos/i23310.scala new file mode 100644 index 000000000000..3b2c7eda9141 --- /dev/null +++ b/tests/pos/i23310.scala @@ -0,0 +1,16 @@ + +object Example { + val pf: PartialFunction[Unit, Unit] = s => (s match { + case a => a + }) match { + case a => () + } +} + +object ExampleB: + def test = + List(42).collect: + _.match + case x => x + .match + case y => y + 27 diff --git a/tests/pos/i23317.scala b/tests/pos/i23317.scala new file mode 100644 index 000000000000..1381aad452d2 --- /dev/null +++ b/tests/pos/i23317.scala @@ -0,0 +1,26 @@ +import scala.quoted.* + +def goImpl(using Quotes): Expr[Int] = + List.empty[Type[?]] match + case Nil => + Expr(0) + case '[t1] :: Nil => + Expr(1) + case '[t1] :: '[t2] :: Nil => + Expr(2) + case '[t1] :: '[t2] :: '[t3] :: Nil => + Expr(3) + case '[t1] :: '[t2] :: '[t3] :: '[t4] :: Nil => + Expr(4) + case '[t1] :: '[t2] :: '[t3] :: '[t4] :: '[t5] :: Nil => + Expr(5) + case '[t1] :: '[t2] :: '[t3] :: '[t4] :: '[t5] :: '[t6] :: Nil => + Expr(6) + case '[t1] :: '[t2] :: '[t3] :: '[t4] :: '[t5] :: '[t6] :: '[t7] :: Nil => + Expr(7) + case '[t1] :: '[t2] :: '[t3] :: '[t4] :: '[t5] :: '[t6] :: '[t7] :: '[t8] :: Nil => + Expr(8) + case '[t1] :: '[t2] :: '[t3] :: '[t4] :: '[t5] :: '[t6] :: '[t7] :: '[t8] :: '[t9] :: Nil => + Expr(9) + case _ => + Expr(999) diff --git a/tests/pos/i23530.scala b/tests/pos/i23530.scala new file mode 100644 index 000000000000..822526f7562d --- /dev/null +++ b/tests/pos/i23530.scala @@ -0,0 +1,18 @@ +trait TestContainer: + trait TestPath: + type AbsMember + + extension (path: TestPath) + infix def ext(color: path.AbsMember): Unit = ??? + infix def ext(other: Int): Unit = ??? + +object Repro: + val dc2: TestContainer = ??? + import dc2.TestPath + + def transition(path: TestPath)(using DummyImplicit): TestPath = ??? + + def test: Unit = + val di: TestPath = ??? + // error + val z1 = transition(di).ext(1) diff --git a/tests/pos/i23611.scala b/tests/pos/i23611.scala new file mode 100644 index 000000000000..0fef178b9c32 --- /dev/null +++ b/tests/pos/i23611.scala @@ -0,0 +1,26 @@ +import java.io.{File, IOException} +import java.net.URI +import java.nio.file.{Path, Paths} +import scala.reflect.ClassTag + +trait FileConnectors { + def listPath(path: => Path): ZStream[Any, IOException, Path] + + final def listFile(file: => File): ZStream[Any, IOException, File] = + for { + path <- null.asInstanceOf[ZStream[Any, IOException, Path]] + r <- listPath(path).mapZIO(a => ZIO.attempt(a.toFile).refineToOrDie) + } yield r +} + +sealed trait ZIO[-R, +E, +A] +extension [R, E <: Throwable, A](self: ZIO[R, E, A]) + def refineToOrDie[E1 <: E: ClassTag]: ZIO[R, E1, A] = ??? + +object ZIO: + def attempt[A](code: => A): ZIO[Any, Throwable, A] = ??? + +sealed trait ZStream[-R, +E, +A]: + def map[B](f: A => B): ZStream[R, E, B] = ??? + def flatMap[R1 <: R, E1 >: E, B](f: A => ZStream[R1, E1, B]): ZStream[R1, E1, B] + def mapZIO[R1 <: R, E1 >: E, A1](f: A => ZIO[R1, E1, A1]): ZStream[R1, E1, A1] \ No newline at end of file diff --git a/tests/pos/i23611a.scala b/tests/pos/i23611a.scala new file mode 100644 index 000000000000..fbaf709e2f0e --- /dev/null +++ b/tests/pos/i23611a.scala @@ -0,0 +1,30 @@ +import java.io.{File, IOException} +import java.net.URI +import java.nio.file.{Path, Paths} +import scala.reflect.ClassTag + +trait FileConnectors { + def listPath(path: => Path): ZStream[Any, IOException, Path] + + final def listFile(file: => File): ZStream[Any, IOException, File] = + for { + path <- null.asInstanceOf[ZStream[Any, IOException, Path]] + r <- listPath(path).mapZIO(a => ZIO.attempt(a.toFile).refineToOrDie) + } yield r +} + +sealed abstract class CanFail[-E] +object CanFail: + given [E]: CanFail[E] = ??? + +sealed trait ZIO[-R, +E, +A] +extension [R, E <: Throwable, A](self: ZIO[R, E, A]) + def refineToOrDie[E1 <: E: ClassTag](using CanFail[E]): ZIO[R, E1, A] = ??? + +object ZIO: + def attempt[A](code: => A): ZIO[Any, Throwable, A] = ??? + +sealed trait ZStream[-R, +E, +A]: + def map[B](f: A => B): ZStream[R, E, B] = ??? + def flatMap[R1 <: R, E1 >: E, B](f: A => ZStream[R1, E1, B]): ZStream[R1, E1, B] + def mapZIO[R1 <: R, E1 >: E, A1](f: A => ZIO[R1, E1, A1]): ZStream[R1, E1, A1] \ No newline at end of file diff --git a/tests/pos/i23616.scala b/tests/pos/i23616.scala new file mode 100644 index 000000000000..48f91d8cd77f --- /dev/null +++ b/tests/pos/i23616.scala @@ -0,0 +1,22 @@ +trait Apply[F[_]]: + extension [A](fa: F[A]) + def map[B](f: A => B): F[B] + def map2[B, Z](fb: F[B])(f: (A, B) => Z): F[Z] = ??? + + private case class IsMap[T <: Tuple](value: Tuple.Map[T, F]) + private inline def tupledGeneric[T <: Tuple](tuple: Tuple.Map[T, F]): F[T] = + inline IsMap(tuple) match + case t: IsMap[h *: EmptyTuple] => t.value.head.map(_ *: EmptyTuple) + case t: IsMap[h *: t] => + val head = t.value.head + val tail = tupledGeneric(t.value.tail) + head.map2(tail)(_ *: _) + +trait Monad[F[_]] extends Apply[F]: + extension [A](fa: F[A]) def flatMap[B](f: A => F[B]): F[B] + extension [A](fa: F[A]) override def map[B](f: A => B): F[B] = ??? + +opaque type Kleisli[F[_], A, B] = A => F[B] +given [F[_], A](using F: Monad[F]): Monad[[B] =>> Kleisli[F, A, B]] with + extension [B](k: Kleisli[F, A, B]) + def flatMap[C](f: B => Kleisli[F, A, C]) = ??? \ No newline at end of file diff --git a/tests/pos/i23620.scala b/tests/pos/i23620.scala new file mode 100644 index 000000000000..aa81f09ee182 --- /dev/null +++ b/tests/pos/i23620.scala @@ -0,0 +1,18 @@ +trait Foo +trait Bar + +type FooOrBar = FooOrBar.Type +object FooOrBar: + opaque type Type <: (Foo | Bar) = Foo | Bar + + def bar: FooOrBar = new Bar {} + +trait Buz + +@main def main = + val p: FooOrBar | Buz = FooOrBar.bar + + p match + case _: Foo => println("foo") + case _: Buz => println("buz") + case _: Bar => println("bar") diff --git a/tests/pos/i23627.scala b/tests/pos/i23627.scala new file mode 100644 index 000000000000..1480a80c9c00 --- /dev/null +++ b/tests/pos/i23627.scala @@ -0,0 +1,18 @@ +trait TestContainer: + trait TestPath[T]: + type AbsMember + + extension (path: TestPath[?]) + infix def ext(color: path.AbsMember): Unit = ??? + infix def ext(other: Int): Unit = ??? + +object Repro: + val dc2: TestContainer = ??? + import dc2.TestPath + + def transition(path: TestPath[?])(using DummyImplicit): TestPath[?] = ??? + + def test: Unit = + val di: TestPath[?] = ??? + // error + val z1 = transition(di).ext(1) diff --git a/tests/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/i23928.scala b/tests/pos/i23928.scala new file mode 100644 index 000000000000..68ac019792a1 --- /dev/null +++ b/tests/pos/i23928.scala @@ -0,0 +1,5 @@ +object Test { + Nil match { + case Seq(xs*) => 42 + } +} diff --git a/tests/pos/i24007.scala b/tests/pos/i24007.scala new file mode 100644 index 000000000000..86d94211d273 --- /dev/null +++ b/tests/pos/i24007.scala @@ -0,0 +1,33 @@ +sealed trait CaseSet: + type EnumType + +object CaseSet { + def caseOf[A, Z >: A]: Cons[A, Empty[Z], Z] = ??? + type Aux[EnumType0] = CaseSet { type EnumType = EnumType0 } + + final class Empty[Z] extends CaseSet: + type EnumType = Z + + final class Cons[A, +T <: CaseSet.Aux[Z], Z](head: A, tail: T) extends CaseSet: + type EnumType = Z + def ++[That](that: That)(implicit append: Append[Z, Cons[A, T, Z], That]): append.Out = ??? +} + +sealed trait Append[EnumType, -Left, -Right]: + type Out <: CaseSet.Aux[EnumType] + +object Append: + type WithOut[EnumType, Left, Right, Out0] = Append[EnumType, Left, Right] { type Out = Out0 } + + implicit def AppendCons[A, Z, T <: CaseSet.Aux[Z], That <: CaseSet.Aux[Z]](implicit + append: Append[Z, T, That] + ): Append.WithOut[Z, CaseSet.Cons[A, T, Z], That, CaseSet.Cons[A, append.Out, Z]] = ??? + + implicit def AppendEmptyLeft[T <: CaseSet.Aux[Z], Z]: Append.WithOut[Z, CaseSet.Empty[Z], T, T] = + ??? + +object Test: + type UnionValue = Int | Boolean | String + val _ = CaseSet.caseOf[Int, UnionValue] ++ + CaseSet.caseOf[Boolean, UnionValue] ++ + CaseSet.caseOf[String, UnionValue] \ No newline at end of file diff --git a/tests/pos/i24038a.scala b/tests/pos/i24038a.scala new file mode 100644 index 000000000000..9386e7d98751 --- /dev/null +++ b/tests/pos/i24038a.scala @@ -0,0 +1,31 @@ +final class MBufferLong: + final def +=(elem: Long): this.type = ??? + +type M[Tup <: Tuple] <: Tuple = Tup match + case EmptyTuple => EmptyTuple + case h *: t => BufferOf[h] *: M[t] + +type M2[T <: Tuple] <: Tuple = (T, M[T]) match + case (h *: t, a *: b) => BufferOf[h] *: M2[t] + case (EmptyTuple, EmptyTuple) => EmptyTuple + case (_, EmptyTuple) => EmptyTuple + case (EmptyTuple, _) => EmptyTuple + +type BufferOf[T] = T match + case Long => MBufferLong + +inline def append[T](t: T, buffer: BufferOf[T]): BufferOf[T] = + inline (t, buffer) match + case (x: Long, y: BufferOf[Long]) => y.+=(x) + buffer + +transparent inline def appendBuffers[T <: Tuple](t: T, buffers: M[T]): M2[T] = { + inline (t, buffers) match + case abcd: ((h *: t), bh *: bt) => + val (hh *: tt, bh *: bt) = abcd + val x: BufferOf[h] = append[h](hh, bh.asInstanceOf[BufferOf[h]]) + x *: appendBuffers[t](tt, bt.asInstanceOf[M[t]]) + case _: (EmptyTuple, EmptyTuple) => EmptyTuple + case _: (_, EmptyTuple) => EmptyTuple + case _: (EmptyTuple, _) => EmptyTuple +} diff --git a/tests/pos/i24038b.scala b/tests/pos/i24038b.scala new file mode 100644 index 000000000000..ec0d12a4689b --- /dev/null +++ b/tests/pos/i24038b.scala @@ -0,0 +1,12 @@ +final class MBufferLong + +type M[Tup <: Tuple] <: Tuple = Tup match + case EmptyTuple => EmptyTuple + case h *: t => MBufferLong *: M[t] + +def appendBuffers[T <: Tuple](t: T, buffers: M[T]): Unit = { + (t, buffers) match + case abcd: (h *: t, bh *: bt) => + val (hh *: tt, bh *: bt) = abcd + summon[hh.type <:< h] +} diff --git a/tests/pos/i24074/JavaPart.java b/tests/pos/i24074/JavaPart.java new file mode 100644 index 000000000000..1a28b7e411c2 --- /dev/null +++ b/tests/pos/i24074/JavaPart.java @@ -0,0 +1,17 @@ +public class JavaPart { + public interface A { } + public interface B extends A { + int onlyInB(); + } + + public interface Lvl1 { + A[] getData(); + } + + public interface Lvl2 extends Lvl1 { + @Override + B[] getData(); + } + + public interface Lvl3 extends Lvl2, Lvl1 { } +} diff --git a/tests/pos/i24074/Test.scala b/tests/pos/i24074/Test.scala new file mode 100644 index 000000000000..7fd5a10330e6 --- /dev/null +++ b/tests/pos/i24074/Test.scala @@ -0,0 +1,3 @@ +object Test: + def test(lvl3: JavaPart.Lvl3): Unit = + lvl3.getData.head.onlyInB() diff --git a/tests/pos/i24134/JavaPartialFunction.scala b/tests/pos/i24134/JavaPartialFunction.scala new file mode 100644 index 000000000000..4fdda74e853c --- /dev/null +++ b/tests/pos/i24134/JavaPartialFunction.scala @@ -0,0 +1,20 @@ +final class Flow[In, Out]: + def collect[T](pf: PartialFunction[Out, T]): Flow[In, T] = ??? + +object Flow: + def create[T](): Flow[T, T] = ??? + + +abstract class Message: + def asTextMessage: TextMessage + +abstract class TextMessage extends Message + +abstract class JavaPartialFunction[A, B] extends PartialFunction[A, B]: + @throws(classOf[Exception]) // required, compiles without annotation + def apply(x: A, isCheck: Boolean): B + + final def isDefinedAt(x: A): Boolean = ??? + final override def apply(x: A): B = ??? + final override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = ??? + diff --git a/tests/pos/i24134/JavaTestServer.java b/tests/pos/i24134/JavaTestServer.java new file mode 100644 index 000000000000..05f5b61ef4b0 --- /dev/null +++ b/tests/pos/i24134/JavaTestServer.java @@ -0,0 +1,18 @@ +public class JavaTestServer { + public static Flow greeter() { + return Flow.create() + .collect( + new JavaPartialFunction() { + @Override + public Message apply(Message msg, boolean isCheck) throws Exception { + if (isCheck) throw new RuntimeException(); + else return handleTextMessage(msg.asTextMessage()); + } + }); + } + + public static TextMessage handleTextMessage(TextMessage msg) { + return null; + } +} + diff --git a/tests/pos/i24192.scala b/tests/pos/i24192.scala new file mode 100644 index 000000000000..ff95c3af55b5 --- /dev/null +++ b/tests/pos/i24192.scala @@ -0,0 +1,25 @@ +// https://github.com/scala/scala-collection-contrib/blob/main/src/main/scala/scala/collection/decorators/package.scala +object decorators { + trait IsMap[A] + implicit def mapDecorator[C](coll: C)(implicit map: IsMap[C]): Map[C, map.type] = ??? +} +import decorators.mapDecorator // unused, required to reprouce + +trait Eq[T] +trait Applicative[F[_]] +given Applicative[Option] = ??? + +trait Traverse[F[_]]: + // context bound required to reproduce + def sequence[G[_]: Applicative, A](fga: F[G[A]]): G[F[A]] = ??? + +object Traverse: + def apply[F[_]]: Traverse[F] = ??? + +trait Segment[Element: Eq] + +case class MergeResult[Element: Eq] private (segments: Seq[Segment[Element]]): + def thisFailsToCompile(): Option[MergeResult[Element]] = + Traverse[Seq] + .sequence(Seq.empty[Option[Segment[Element]]]) + .map(MergeResult.apply) // error \ No newline at end of file diff --git a/tests/pos/i24207.scala b/tests/pos/i24207.scala new file mode 100644 index 000000000000..557b6f982f2b --- /dev/null +++ b/tests/pos/i24207.scala @@ -0,0 +1,12 @@ +class Generator: + private def generateTable(table: Table) = + val (ownRelations, _) = calculateOwnRelations(table) + ownRelations + + private def calculateOwnRelations(table: Table) = + val ownRelations = table.relations.filter(_.association.isDefined) + (ownRelations, Nil) + +case class Table(relations: Seq[TableRelation]) +case class TableRelation(association: Option[Association]) +trait Association diff --git a/tests/pos/i24249.scala b/tests/pos/i24249.scala new file mode 100644 index 000000000000..1918136889ea --- /dev/null +++ b/tests/pos/i24249.scala @@ -0,0 +1,5 @@ + +import annotation.* + +class Foo: + @varargs def this(x: Int*) = this() diff --git a/tests/pos/i24250.scala b/tests/pos/i24250.scala new file mode 100644 index 000000000000..ac03476e1cf2 --- /dev/null +++ b/tests/pos/i24250.scala @@ -0,0 +1,28 @@ + +trait Foo +val foo = + new Foo: + // comment + end new +val foo2 = + new Foo: + end new +val foo3 = + new Foo { + } + end new + +class C +val c = + new C: + end new +val c2 = + new C { + } + +class D: +end D +val d = + new D: + def more = ??? + end new diff --git a/tests/pos/i24456.scala b/tests/pos/i24456.scala new file mode 100644 index 000000000000..e70145325113 --- /dev/null +++ b/tests/pos/i24456.scala @@ -0,0 +1,14 @@ +// Similar to i24207 + +class Generator: + private def generateTable(table: Table) = + val (ownRelations, unusedTerm) = calculateOwnRelations(table) + None + + private def calculateOwnRelations(table: Table) = + val ownRelations = table.relations.filter(_.association.isDefined) + (ownRelations, Nil) + +case class Table(relations: Seq[TableRelation]) +case class TableRelation(association: Option[Association]) +trait Association \ No newline at end of file diff --git a/tests/pos/i24479.scala b/tests/pos/i24479.scala new file mode 100644 index 000000000000..4ebe5c1c3213 --- /dev/null +++ b/tests/pos/i24479.scala @@ -0,0 +1,11 @@ +object test { + class A + class B extends A + + inline def f[T](inline v: Any): T = inline v match { + case x: T => x + } + + inline def b: B = new B() + f[A](b: A) +} diff --git a/tests/pos/i24507-jframe.scala b/tests/pos/i24507-jframe.scala new file mode 100644 index 000000000000..ec67ff9e9b49 --- /dev/null +++ b/tests/pos/i24507-jframe.scala @@ -0,0 +1,2 @@ +class Test extends javax.swing.JFrame { class Acc extends AccessibleJFrame } + diff --git a/tests/pos/i24557.scala b/tests/pos/i24557.scala new file mode 100644 index 000000000000..9ed743dee806 --- /dev/null +++ b/tests/pos/i24557.scala @@ -0,0 +1,9 @@ +object test { + sealed trait X[T] + trait Iterable[T] + case class I[T, C <: Iterable[T]]() extends X[C] + + def t[T](i: X[T]): Unit = + i match + case i @ I() => () +} diff --git a/tests/pos/i24631/TestJava.java b/tests/pos/i24631/TestJava.java new file mode 100644 index 000000000000..1a869cbdd6db --- /dev/null +++ b/tests/pos/i24631/TestJava.java @@ -0,0 +1,12 @@ +package example; + +public abstract class TestJava { + public abstract T create(String foo); + + // Note that this is the method that's called from Scala code + public T create() { return create(""); } + + public static class Concrete extends TestJava { + @Override public String create(String foo) { return foo; } + } +} diff --git a/tests/pos/i24631/test.scala b/tests/pos/i24631/test.scala new file mode 100644 index 000000000000..c54eb7f98c69 --- /dev/null +++ b/tests/pos/i24631/test.scala @@ -0,0 +1,2 @@ +val s = new example.TestJava.Concrete().create +val s2: String = s diff --git a/tests/pos/i24631b.scala b/tests/pos/i24631b.scala new file mode 100644 index 000000000000..9e408729c6c3 --- /dev/null +++ b/tests/pos/i24631b.scala @@ -0,0 +1,11 @@ +//> using options -source:3.0-migration + +abstract class C[A]: + def create(s: String): A + def create(): A = create("") + +class D extends C[String]: + def create(s: String): String = s + +val s = D().create +val s2: String = s diff --git a/tests/pos/i24717.scala b/tests/pos/i24717.scala new file mode 100644 index 000000000000..56309f9de547 --- /dev/null +++ b/tests/pos/i24717.scala @@ -0,0 +1,13 @@ +import scala.compiletime.ops.int.+ +import scala.compiletime.ops.int.S + +object test { + object O { + opaque type O = Int + transparent inline def v: O = 123 + } + + val a: 123 & O.O = O.v + val b: S[a.type] = 124 + val c: a.type + 1 = 124 +} diff --git a/tests/pos/i24750.scala b/tests/pos/i24750.scala new file mode 100644 index 000000000000..591e197b0f50 --- /dev/null +++ b/tests/pos/i24750.scala @@ -0,0 +1,14 @@ +//> using options -Werror +import scala.concurrent.duration.* + +object i24750: + + def f(duration: FiniteDuration): Unit = + duration.unit match + case NANOSECONDS => + case MICROSECONDS => + case MILLISECONDS => + case SECONDS => + case MINUTES => + case HOURS => + case DAYS => diff --git a/tests/pos/i24813.scala b/tests/pos/i24813.scala new file mode 100644 index 000000000000..3724d9d2b991 --- /dev/null +++ b/tests/pos/i24813.scala @@ -0,0 +1,8 @@ +object test { + class Seq[T] + inline def f[T]: T = scala.compiletime.summonFrom { + case given (Seq[t] =:= T) => new Seq[t] + } + + f[Seq[Int]] +} diff --git a/tests/pos/i24891.scala b/tests/pos/i24891.scala new file mode 100644 index 000000000000..e193ac0cd08d --- /dev/null +++ b/tests/pos/i24891.scala @@ -0,0 +1,68 @@ +object Main { + def test1: Unit = { + val x1 = IArray("a") + val x2 = IArray(x1) + val x3: IArray[IArray[String]] = x2 + } + def test2a: Unit = { + import lib.* + val x1 = MyIArray("a") + val x2 = MyIArray(x1) + val x3: MyIArray[MyIArray[String]] = x2 + } + def test2b: Unit = { + import lib2.* + val x1 = MyIArray("a") + val x2 = MyIArray(x1) + val x3: MyIArray[MyIArray[String]] = x2 + } + def test2c: Unit = { + import lib3.* + val x1 = MyIArray("a") + val x2 = x1.apply + val x3: MyIArray[MyIArray[String]] = x2 + } + def test3: Unit = { + val x1 = Array("a") + val x2 = Array(x1) + val x3: Array[Array[String]] = x2 + } +} + +object lib { + import reflect.* + + opaque type MyIArray[+T] = Array[? <: T] + + object MyIArray { + extension [T](arr: MyIArray[T]) { + def apply(n: Int): T = arr.asInstanceOf[Array[T]].apply(n) + } + def apply[T](xs: T*)(using ClassTag[T]): MyIArray[T] = Array(xs*) + } +} +object lib2 { + import reflect.* + + opaque type MyIArray[+T] = Array[? <: T] + + object MyIArray { + extension [T](arr: MyIArray[T]) { + def apply(n: Int): T = arr.asInstanceOf[Array[T]].apply(n) + } + def apply[T](x: T)(using ClassTag[T]): MyIArray[T] = Array(x) + } +} +object lib3 { + import reflect.* + + opaque type MyIArray[+T] = Array[? <: T] + + object MyIArray { + extension [T](arr: MyIArray[T]) { + def apply(n: Int): T = arr.asInstanceOf[Array[T]].apply(n) + def apply(using ClassTag[MyIArray[T]]): MyIArray[MyIArray[T]] = Array(arr) + } + def apply[T](xs: T*)(using ClassTag[T]): MyIArray[T] = Array(xs*) + } +} diff --git a/tests/pos/i24894a/Bar_1.scala b/tests/pos/i24894a/Bar_1.scala new file mode 100644 index 000000000000..0cf5b556dc79 --- /dev/null +++ b/tests/pos/i24894a/Bar_1.scala @@ -0,0 +1,10 @@ +object Bar { + import quoted.* + + inline transparent def bar(): Array[String] = ${ macroBar() } + def macroBar()(using Quotes): Expr[Array[String]] = { + val l = Range(0, 200, 10).map(_.toString).map(Expr(_)).toList + '{ Array[String](${ Varargs(l) }*) } + //'{ Array[String]("hello", "world") } + } +} diff --git a/tests/pos/i24894a/Foo_2.scala b/tests/pos/i24894a/Foo_2.scala new file mode 100644 index 000000000000..b11df905a911 --- /dev/null +++ b/tests/pos/i24894a/Foo_2.scala @@ -0,0 +1,2 @@ +@Param(Bar.bar()) class Foo +@Param(Array("hi", "bi")) class Baz diff --git a/tests/pos/i24894a/Param.java b/tests/pos/i24894a/Param.java new file mode 100644 index 000000000000..a3ba5cda00ec --- /dev/null +++ b/tests/pos/i24894a/Param.java @@ -0,0 +1,15 @@ + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Inherited +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Param { + + String[] value(); + +} diff --git a/tests/pos/i24894b/Bar_1.scala b/tests/pos/i24894b/Bar_1.scala new file mode 100644 index 000000000000..b95ea490e5af --- /dev/null +++ b/tests/pos/i24894b/Bar_1.scala @@ -0,0 +1,9 @@ +object Bar { + import quoted.* + + transparent inline def bar(): String = ${ macroBar() } + def macroBar()(using Quotes): Expr[String] = { + val s = "hello".reverse + Expr(s) + } +} diff --git a/tests/pos/i24894b/Foo_2.scala b/tests/pos/i24894b/Foo_2.scala new file mode 100644 index 000000000000..3b92d2473f3f --- /dev/null +++ b/tests/pos/i24894b/Foo_2.scala @@ -0,0 +1 @@ +@SuppressWarnings(Array(Bar.bar())) class Foo diff --git a/tests/pos/i24894c/OutputTimeUnit.java b/tests/pos/i24894c/OutputTimeUnit.java new file mode 100644 index 000000000000..687be57ca4ad --- /dev/null +++ b/tests/pos/i24894c/OutputTimeUnit.java @@ -0,0 +1,18 @@ +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + +@Inherited +@Target({ElementType.METHOD,ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface OutputTimeUnit { + + /** + * @return Time unit to use. + */ + TimeUnit value(); + +} diff --git a/tests/pos/i24894c/i21881.scala b/tests/pos/i24894c/i21881.scala new file mode 100644 index 000000000000..bd2995cb1ac7 --- /dev/null +++ b/tests/pos/i24894c/i21881.scala @@ -0,0 +1,15 @@ +class C: + + //inline val Seconds = java.util.concurrent.TimeUnit.SECONDS // not a literal + inline transparent def Seconds = java.util.concurrent.TimeUnit.SECONDS + + @OutputTimeUnit(Seconds) + def f(): Unit = () + + final val SecondsFinalVal = java.util.concurrent.TimeUnit.SECONDS + + @OutputTimeUnit(SecondsFinalVal) + def f2(): Unit = () + + @OutputTimeUnit(java.util.concurrent.TimeUnit.SECONDS) + def ok(): Unit = () diff --git a/tests/pos/i24990.scala b/tests/pos/i24990.scala new file mode 100644 index 000000000000..954f308c176e --- /dev/null +++ b/tests/pos/i24990.scala @@ -0,0 +1,12 @@ +//> using options -language:experimental.erasedDefinitions + +object test { + opaque type Foo = Int + + erased class Bar[A] + object Bar { inline given inst[A]: Bar[A] = new Bar[A] } +} + +def takesBar[A](a: A)(using b: test.Bar[A]): A = a + +val _ = takesBar(1) diff --git a/tests/pos/i25091.scala b/tests/pos/i25091.scala new file mode 100644 index 000000000000..63ca40d5add0 --- /dev/null +++ b/tests/pos/i25091.scala @@ -0,0 +1,22 @@ +transparent trait VersionComponent[T]: + protected def minimumValue: Int + inline def minimum: T = wrap(minimumValue) + inline def reset: T = minimum + inline def wrap(value: Int): T + inline def unwrap(value: T): Int + +opaque type MajorVersion = Int +object MajorVersion extends VersionComponent[MajorVersion]: + protected inline def minimumValue: Int = 0 + inline def wrap(value: Int): MajorVersion = value + inline def unwrap(mv: MajorVersion): Int = mv + +case class Version(major: MajorVersion) +object Version: + trait Increment[F]: + extension (v: Version) def increment: Version + + object Increment: + given Increment[MajorVersion] with + extension (v: Version) + inline def increment: Version = Version(MajorVersion.reset) diff --git a/tests/pos/i25091b.scala b/tests/pos/i25091b.scala new file mode 100644 index 000000000000..776b2017ee04 --- /dev/null +++ b/tests/pos/i25091b.scala @@ -0,0 +1,15 @@ +object Test: + trait VersionComponent[T]: + inline def minimum: T = wrap(42) + inline def wrap(value: Int): T + + opaque type MajorVersionType = Int + object MajorVersion extends VersionComponent[MajorVersionType]: + inline def wrap(value: Int): MajorVersionType = value + inline def reset: MajorVersionType = minimum + + trait Increment[F]: + def increment: MajorVersionType + + class MyIncrement extends Increment[MajorVersionType]: + inline def increment: MajorVersionType = MajorVersion.reset diff --git a/tests/pos/i25185.scala b/tests/pos/i25185.scala new file mode 100644 index 000000000000..ac18a7d7f5f3 --- /dev/null +++ b/tests/pos/i25185.scala @@ -0,0 +1,11 @@ +package example + +class A { + type B = Int + + object B { + inline def f1(x: Int): B = x + } + + def x1 = B.f1(2) +} \ No newline at end of file diff --git a/tests/pos/i25250.scala b/tests/pos/i25250.scala new file mode 100644 index 000000000000..b7397d78d0b2 --- /dev/null +++ b/tests/pos/i25250.scala @@ -0,0 +1,36 @@ +//> using options -Wall -Werror + +@main def test = { + println(Seq(res1, res2, res3, res4, res5).mkString("\n")) +} + +def fooB(b: Int) = b * b +def fooC(c: Int) = c * c * c + +// compiles +val res1 = fooB( + b = + fooC: + 2 + + 1 +) +val res2 = fooB: + fooC: + 2 + + 1 +val res3 = fooB( + fooC(2) + + 1 +) + +// doesn't compile +val res4 = fooB( + fooC: + 2 + + 1 +) +val res5 = fooB( + b = fooC: + 2 + + 1 +) diff --git a/tests/pos/i25292.scala b/tests/pos/i25292.scala new file mode 100644 index 000000000000..ed4b4e1eedd2 --- /dev/null +++ b/tests/pos/i25292.scala @@ -0,0 +1,6 @@ +//> using options -Ycheck:all + +val tuple: (42, true) = (42, true) +val b = tuple match + case (1, y) => () + case (x, y) => () diff --git a/tests/pos/i25362/Macro_1.scala b/tests/pos/i25362/Macro_1.scala new file mode 100644 index 000000000000..99cbadcb4cc1 --- /dev/null +++ b/tests/pos/i25362/Macro_1.scala @@ -0,0 +1,15 @@ +package example +import scala.deriving.Mirror +import scala.quoted.* + +class TC[A] { + type Out +} + +object TC { + transparent inline def derived[Left](using m: Mirror.ProductOf[Left]): TC[Left] = + ${ derivedImpl[Left, m.MirroredElemTypes] } + + private def derivedImpl[Left: Type, LeftTypes: Type](using Quotes): Expr[TC[Left]] = + '{ new TC[Left] { type Out = LeftTypes } } +} diff --git a/tests/pos/i25362/Test_2.scala b/tests/pos/i25362/Test_2.scala new file mode 100644 index 000000000000..c63c5f436068 --- /dev/null +++ b/tests/pos/i25362/Test_2.scala @@ -0,0 +1,14 @@ +package example + +opaque type BigTuple <: Repr = Repr +final case class Repr( + f1: Int, f2: Int, f3: Int, f4: Int, f5: Int, + f6: Int, f7: Int, f8: Int, f9: Int, f10: Int, + f11: Int, f12: Int, f13: Int, f14: Int, f15: Int, + f16: Int, f17: Int, f18: Int, f19: Int, f20: Int, + f21: Int, f22: Int, f23: Int, f24: Int, f25: Int, +) + +class test { + val merger = TC.derived[BigTuple] +} diff --git a/tests/pos/i25417.scala b/tests/pos/i25417.scala new file mode 100644 index 000000000000..1b1dacf3737e --- /dev/null +++ b/tests/pos/i25417.scala @@ -0,0 +1,24 @@ +import scala.compiletime.summonFrom + +trait Decoder[A] + +given Decoder[String] with {} + +trait Mirror[T]: + type IronType + +given [T](using mirror: Mirror[T], ev: Decoder[mirror.IronType]): Decoder[T] = + ev.asInstanceOf[Decoder[T]] + +object Foo: + opaque type T = String + + inline given Mirror[T] with + type IronType = String + +inline def summonFooDecoder: Decoder[Foo.T] = + summonFrom { + case decodeA: Decoder[Foo.T] => decodeA + } + +val fooDecoder: Decoder[Foo.T] = summonFooDecoder diff --git a/tests/pos/i25427.scala b/tests/pos/i25427.scala new file mode 100644 index 000000000000..c4449bd20b10 --- /dev/null +++ b/tests/pos/i25427.scala @@ -0,0 +1,15 @@ +import scala.compiletime.summonInline + +trait MyEvidence[T] + +object Scope: + opaque type T = String + + inline given MyEvidence[T] with {} + +inline def summonInlineNoOpProxy: MyEvidence[Scope.T] = summonInline[MyEvidence[Scope.T]] + +val smoke = ( + summonInline[MyEvidence[Scope.T]], + summonInlineNoOpProxy, +) diff --git a/tests/pos/i25442.scala b/tests/pos/i25442.scala new file mode 100644 index 000000000000..d9769facc05c --- /dev/null +++ b/tests/pos/i25442.scala @@ -0,0 +1,18 @@ +package protectedCtorsPos + +class A protected (x: Int) + +// Super calls are allowed +class B extends A(42) + +// Inner class extending protected parent +class C extends A(42) { + class Inner extends A(1) +} + +// Mixed visibility: public secondary constructor is accessible +class D protected (x: Int) { + def this() = this(0) +} +class E(d: D) extends D(d.hashCode) +class F extends E(new D()) diff --git a/tests/pos/i25500.scala b/tests/pos/i25500.scala new file mode 100644 index 000000000000..f9238fcf2e9a --- /dev/null +++ b/tests/pos/i25500.scala @@ -0,0 +1,13 @@ +trait A[B[_]]: + type C + +object A: + given A[Option] with + type C = Option[Int] + + def apply[E[_], F](e: E[F])(using a: A[E]): a.C = ??? + +def apply[D[_]](other: (a: A[D]) ?=> a.C): Any = ??? + +def main = + apply(A(Option(5))) \ No newline at end of file diff --git a/tests/pos/i25513.scala b/tests/pos/i25513.scala new file mode 100644 index 000000000000..73147dcb876b --- /dev/null +++ b/tests/pos/i25513.scala @@ -0,0 +1,5 @@ +type Foo[A, B] = ( + (A, B) match + case (Int, Int) => (A, B), + A +) diff --git a/tests/pos/i25542.scala b/tests/pos/i25542.scala new file mode 100644 index 000000000000..590d7ba5d62e --- /dev/null +++ b/tests/pos/i25542.scala @@ -0,0 +1,16 @@ +class PartialRequest: + protected def handle(i: Int): Int = i + +object PartialRequest: + trait Bundler: + def bundle(req: PartialRequest): Int => Int + + object Bundler: + inline given Bundler = new Bundler: + def bundle(req: PartialRequest): Int => Int = + (i: Int) => req.handle(i) + + def apply()(using bundler: Bundler): Int => Int = + bundler.bundle(new PartialRequest) + +val request = PartialRequest() diff --git a/tests/pos/i3323.scala b/tests/pos/i3323.scala deleted file mode 100644 index 94d072d4a2fc..000000000000 --- a/tests/pos/i3323.scala +++ /dev/null @@ -1,9 +0,0 @@ -//> using options -Xfatal-warnings -deprecation -feature - -class Foo { - def foo[A](lss: List[List[A]]): Unit = { - lss match { - case xss: List[List[A]] => - } - } -} diff --git a/tests/pos/i8581/Macro_1.scala b/tests/pos/i8581/Macro_1.scala new file mode 100644 index 000000000000..62bb18d97f7d --- /dev/null +++ b/tests/pos/i8581/Macro_1.scala @@ -0,0 +1,11 @@ +import scala.quoted.* + +case class Position() +object Position { + implicit inline def here: Position = ${ genPosition } + + private def genPosition(using Quotes): Expr[Position] = { + val lineNo: Int = quotes.reflect.Position.ofMacroExpansion.startLine + '{ Position() } + } +} diff --git a/tests/pos/i8581/Test_2.scala b/tests/pos/i8581/Test_2.scala new file mode 100644 index 000000000000..1d699b7db1dd --- /dev/null +++ b/tests/pos/i8581/Test_2.scala @@ -0,0 +1,16 @@ +import scala.concurrent.duration.FiniteDuration +import scala.language.implicitConversions +import scala.reflect.ClassTag + +class AnyShouldWrapper[T] + +class Test { + implicit def convertToAnyShouldWrapper[T](o: T)(implicit pos: Position): AnyShouldWrapper[T] = ??? + + def expectMsgType[T](implicit t: ClassTag[T]): T = ??? + def expectMsgType[T](max: FiniteDuration)(implicit t: ClassTag[T]): T = ??? + + def test(): Unit = { + expectMsgType[Int]: AnyShouldWrapper[Int] + } +} diff --git a/tests/pos/i8875.scala b/tests/pos/i8875.scala index c0de263417e0..e4ac9e057f64 100644 --- a/tests/pos/i8875.scala +++ b/tests/pos/i8875.scala @@ -1,7 +1,7 @@ -//> using options -Xprint:getters +//> using options -Vprint:getters class A { extension (a: Int) { def foo: Int = 1 } -} \ No newline at end of file +} diff --git a/tests/pos/match-constant-type.scala b/tests/pos/match-constant-type.scala new file mode 100644 index 000000000000..91c12566c394 --- /dev/null +++ b/tests/pos/match-constant-type.scala @@ -0,0 +1,3 @@ +object Test { + ("a": "a") match { case a => Set.empty["a"] + a } +} diff --git a/tests/pos/match-type-disjoint-22076.scala b/tests/pos/match-type-disjoint-22076.scala new file mode 100644 index 000000000000..c10555501b97 --- /dev/null +++ b/tests/pos/match-type-disjoint-22076.scala @@ -0,0 +1,8 @@ +trait Foo[CP <: NonEmptyTuple]: + type EndNode = Tuple.Last[CP] + +def f(end: Foo[?]): end.EndNode = + ??? + +trait Bar[CP <: NonEmptyTuple] extends Foo[CP]: + val v: EndNode = f(this) diff --git a/tests/pos/opaque-extension.scala b/tests/pos/opaque-extension.scala new file mode 100644 index 000000000000..b2cdd7b90659 --- /dev/null +++ b/tests/pos/opaque-extension.scala @@ -0,0 +1,20 @@ +//> using options -Yno-deep-subtypes + +object Record: + opaque type Record[F] = Unit + + extension [F](record: Record[F]) + def &[A](other: Record[A]): Record[F & A] = () + + class has[Name, Value]() + + extension (self: String) + def ~[Value](value: Value): Record[has[self.type, Value]] = () + +import Record.~ + +val r = "f1" ~ 1 & "f2" ~ "hello" & "f3" ~ true & "f4" ~ 4 & "f5" ~ 5 & + "f6" ~ 6 & "f7" ~ 7 & "f8" ~ 8 & "f9" ~ 9 & "f10" ~ 10 & + "f11" ~ 11 & "f12" ~ 12 & "f13" ~ 13 & "f14" ~ 14 & "f15" ~ 15 & + "f16" ~ 16 & "f17" ~ 17 & "f18" ~ 18 & "f19" ~ 19 & "f20" ~ 20 & + "f21" ~ 21 & "f22" ~ 22 & "f23" ~ 23 & "f24" ~ 24 & "f25" ~ 25 diff --git a/tests/pos/printbounds.scala b/tests/pos/printbounds.scala index a3db104f4a33..87e74da90dc9 100644 --- a/tests/pos/printbounds.scala +++ b/tests/pos/printbounds.scala @@ -5,7 +5,7 @@ class Test { val x: Tree[_] = ??? - val y = x // With -Xprint:typer this should print val x: Tree[_] = x + val y = x // With -Vprint:typer this should print val x: Tree[_] = x // used to print Tree[Nothing], which is confusing. } diff --git a/tests/pos/t12396/A_1.java b/tests/pos/t12396/A_1.java new file mode 100644 index 000000000000..a394d3a0a59d --- /dev/null +++ b/tests/pos/t12396/A_1.java @@ -0,0 +1,17 @@ +// test: -jvm 21+ + +public class A_1 { + public int f(Object s) { + switch(s) { + case Res.R -> { + return 1; + } + default -> { + return 3; + } + } + } + static enum Res { + R + } +} diff --git a/tests/pos/t12396/B_2.scala b/tests/pos/t12396/B_2.scala new file mode 100644 index 000000000000..662da7b3a575 --- /dev/null +++ b/tests/pos/t12396/B_2.scala @@ -0,0 +1,5 @@ +// test: -jvm 21+ + +class B { + def bar = (new A_1).f(null) +} diff --git a/tests/pos/t13127.scala b/tests/pos/t13127.scala new file mode 100644 index 000000000000..3c35437359f3 --- /dev/null +++ b/tests/pos/t13127.scala @@ -0,0 +1,27 @@ +import scala.annotation.unchecked.uncheckedOverride + +trait Hordering[T] extends java.util.Comparator[T] { + // overriding on java 26 and later, not overriding before + @uncheckedOverride def max[U <: T](x: U, y: U): U = x + @uncheckedOverride def min[U <: T](x: U, y: U): U = x +} + +trait Base[T] { + def max[U <: T](x: U, y: U): U = x + + def mux(x: String = "ex") = x +} + +trait Sub[T] extends Base[T] { + @uncheckedOverride def max[U <: T](x: U, y: U): U = x // overriding ok + @uncheckedOverride def min[U <: T](x: U, y: U): U = x // not overriding ok + + // overriding default + @uncheckedOverride def mux(x: String = "nox") = x + + def test = mux() +} + +trait OverrideOrdering[T] extends scala.math.Ordering[T] { + override def max[U <: T](x: U, y: U): U = x +} diff --git a/tests/pos/test-implicits2.scala b/tests/pos/test-implicits2.scala index 6d744395902e..7849c06e6dd1 100644 --- a/tests/pos/test-implicits2.scala +++ b/tests/pos/test-implicits2.scala @@ -1,6 +1,6 @@ /* Compile with - dotc implicits2.scala -Xprint:typer -Xprint-types -verbose + dotc implicits2.scala -Vprint:typer -Xprint-types -verbose and verify that the inserted wrapString comes from Predef. You should see diff --git a/tests/pos/tuple-exaustivity.scala b/tests/pos/tuple-exaustivity.scala deleted file mode 100644 index a27267fc89e5..000000000000 --- a/tests/pos/tuple-exaustivity.scala +++ /dev/null @@ -1,6 +0,0 @@ -//> using options -Xfatal-warnings -deprecation -feature - -def test(t: Tuple) = - t match - case Tuple() => - case head *: tail => diff --git a/tests/pos/typeclass-scaling.scala b/tests/pos/typeclass-scaling.scala index 0db663de4989..8f2e5381c6f6 100644 --- a/tests/pos/typeclass-scaling.scala +++ b/tests/pos/typeclass-scaling.scala @@ -5,7 +5,7 @@ import scala.annotation.tailrec // The following command: // -// sc typeclass-scaling.scala -Xmax-inlines 100 -Xprint:typer -color:never -pagewidth 1000 >& x +// sc typeclass-scaling.scala -Xmax-inlines 100 -Vprint:typer -color:never -pagewidth 1000 >& x // // produces an output file with `wc` measures (lines/words/chars): // 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/printing/annot-18064.scala b/tests/printing/annot-18064.scala index b6a67ea9ebe7..465f627ff861 100644 --- a/tests/printing/annot-18064.scala +++ b/tests/printing/annot-18064.scala @@ -1,4 +1,4 @@ -//> using options "-Xprint:typer" +//> using options "-Vprint:typer" class myAnnot[T]() extends annotation.Annotation diff --git a/tests/printing/dependent-annot-default-args.check b/tests/printing/dependent-annot-default-args.check deleted file mode 100644 index 44c1fe31e2d1..000000000000 --- a/tests/printing/dependent-annot-default-args.check +++ /dev/null @@ -1,25 +0,0 @@ -[[syntax trees at end of typer]] // tests/printing/dependent-annot-default-args.scala -package { - class annot(x: Any, y: Any) extends annotation.Annotation() { - private[this] val x: Any - private[this] val y: Any - } - final lazy module val annot: annot = new annot() - final module class annot() extends AnyRef() { this: annot.type => - def $lessinit$greater$default$2: Any @uncheckedVariance = 42 - } - final lazy module val dependent-annot-default-args$package: - dependent-annot-default-args$package = - new dependent-annot-default-args$package() - final module class dependent-annot-default-args$package() extends Object() { - this: dependent-annot-default-args$package.type => - def f(x: Int): Int @annot(x) = x - def test: Unit = - { - val y: Int = ??? - val z: Int @annot(y) = f(y) - () - } - } -} - diff --git a/tests/printing/dependent-annot-default-args.scala b/tests/printing/dependent-annot-default-args.scala deleted file mode 100644 index 075913a63689..000000000000 --- a/tests/printing/dependent-annot-default-args.scala +++ /dev/null @@ -1,6 +0,0 @@ -class annot(x: Any, y: Any = 42) extends annotation.Annotation -def f(x: Int): Int @annot(x) = x - -def test = - val y: Int = ??? - val z = f(y) diff --git a/tests/printing/transformed/lazy-vals-legacy.check b/tests/printing/transformed/lazy-vals-legacy.check index 2768a89b9c9e..e50586ecf5e2 100644 --- a/tests/printing/transformed/lazy-vals-legacy.check +++ b/tests/printing/transformed/lazy-vals-legacy.check @@ -1,4 +1,4 @@ -[[syntax trees at end of MegaPhase{dropOuterAccessors, checkNoSuperThis, flatten, transformWildcards, moveStatic, expandPrivate, restoreScopes, selectStatic, Collect entry points, collectSuperCalls, repeatableAnnotations}]] // tests/printing/transformed/lazy-vals-legacy.scala +[[syntax trees at end of MegaPhase{dropOuterAccessors, checkNoSuperThis, flatten, transformWildcards, moveStatic, expandPrivate, restoreScopes, selectStatic, Collect entry points, repeatableAnnotations}]] // tests/printing/transformed/lazy-vals-legacy.scala package { @SourceFile("tests/printing/transformed/lazy-vals-legacy.scala") final module class A extends Object { diff --git a/tests/printing/transformed/lazy-vals-new.check b/tests/printing/transformed/lazy-vals-new-offset.check similarity index 92% rename from tests/printing/transformed/lazy-vals-new.check rename to tests/printing/transformed/lazy-vals-new-offset.check index 05471e5677dc..71ce7375410c 100644 --- a/tests/printing/transformed/lazy-vals-new.check +++ b/tests/printing/transformed/lazy-vals-new-offset.check @@ -1,7 +1,7 @@ -[[syntax trees at end of MegaPhase{dropOuterAccessors, checkNoSuperThis, flatten, transformWildcards, moveStatic, expandPrivate, restoreScopes, selectStatic, Collect entry points, collectSuperCalls, repeatableAnnotations}]] // tests/printing/transformed/lazy-vals-new.scala +[[syntax trees at end of MegaPhase{dropOuterAccessors, checkNoSuperThis, flatten, transformWildcards, moveStatic, expandPrivate, restoreScopes, selectStatic, Collect entry points, repeatableAnnotations}]] // tests/printing/transformed/lazy-vals-new-offset.scala package { - @SourceFile("tests/printing/transformed/lazy-vals-new.scala") final module - class A extends Object { + @SourceFile("tests/printing/transformed/lazy-vals-new-offset.scala") final + module class A extends Object { def (): Unit = { super() diff --git a/tests/printing/transformed/lazy-vals-new-offset.flags b/tests/printing/transformed/lazy-vals-new-offset.flags new file mode 100644 index 000000000000..ebe10e985590 --- /dev/null +++ b/tests/printing/transformed/lazy-vals-new-offset.flags @@ -0,0 +1,3 @@ +-java-output-version:8 +-Ylegacy-lazy-vals:false +-Yfuture-lazy-vals:false \ No newline at end of file diff --git a/tests/printing/transformed/lazy-vals-new.scala b/tests/printing/transformed/lazy-vals-new-offset.scala similarity index 100% rename from tests/printing/transformed/lazy-vals-new.scala rename to tests/printing/transformed/lazy-vals-new-offset.scala diff --git a/tests/printing/transformed/lazy-vals-new-varhandle.check b/tests/printing/transformed/lazy-vals-new-varhandle.check new file mode 100644 index 000000000000..7f8ed1691c06 --- /dev/null +++ b/tests/printing/transformed/lazy-vals-new-varhandle.check @@ -0,0 +1,88 @@ +[[syntax trees at end of MegaPhase{dropOuterAccessors, checkNoSuperThis, flatten, transformWildcards, moveStatic, expandPrivate, restoreScopes, selectStatic, Collect entry points, repeatableAnnotations}]] // tests/printing/transformed/lazy-vals-new-varhandle.scala +package { + @SourceFile("tests/printing/transformed/lazy-vals-new-varhandle.scala") final + module class A extends Object { + def (): Unit = + { + super() + () + } + @static private def (): Unit = + { + A.x$lzy1$lzyHandle = + java.lang.invoke.MethodHandles.lookup().findVarHandle( + classOf[Object {...}], "x$lzy1", classOf[Object]) + () + } + @static private val x$lzy1$lzyHandle: java.lang.invoke.VarHandle = + java.lang.invoke.MethodHandles.lookup().findVarHandle( + classOf[Object {...}], "x$lzy1", classOf[Object]) + private def writeReplace(): Object = + new scala.runtime.ModuleSerializationProxy(classOf[A]) + @volatile private lazy var x$lzy1: Object = null + lazy def x(): Int = + { + val result: Object = A.x$lzy1 + if result.isInstanceOf[Int] then Int.unbox(result) else + if result.eq(scala.runtime.LazyVals.NullValue) then Int.unbox(null) + else Int.unbox(A.x$lzyINIT1()) + } + private def x$lzyINIT1(): Object = + while do + { + val current: Object = A.x$lzy1 + if current.eq(null) then + if + A.x$lzy1$lzyHandle.compareAndSet(this, null, + scala.runtime.LazyVals.Evaluating) + then + { + var result: Object = null + var resultNullable: Object = null + try + { + resultNullable = Int.box(2) + if resultNullable.eq(null) then + result = scala.runtime.LazyVals.NullValue else + result = resultNullable + () + } + finally + if + A.x$lzy1$lzyHandle.compareAndSet(this, + scala.runtime.LazyVals.Evaluating, result).unary_!() + then + { + val lock: scala.runtime.LazyVals.LazyVals$Waiting = + A.x$lzy1.asInstanceOf[ + scala.runtime.LazyVals.LazyVals$Waiting] + A.x$lzy1$lzyHandle.compareAndSet(this, lock, result) + lock.countDown() + } + else () + return resultNullable + } + else () + else + if + current.isInstanceOf[ + scala.runtime.LazyVals.LazyVals$LazyValControlState] + then + if current.eq(scala.runtime.LazyVals.Evaluating) then + { + A.x$lzy1$lzyHandle.compareAndSet(this, current, + new scala.runtime.LazyVals.LazyVals$Waiting()) + () + } + else + if current.isInstanceOf[scala.runtime.LazyVals.LazyVals$Waiting] + then + current.asInstanceOf[scala.runtime.LazyVals.LazyVals$Waiting]. + await() + else return null + else return current + } + } + final lazy module val A: A = new A() +} + diff --git a/tests/printing/transformed/lazy-vals-new-varhandle.flags b/tests/printing/transformed/lazy-vals-new-varhandle.flags new file mode 100644 index 000000000000..f8aba436ee81 --- /dev/null +++ b/tests/printing/transformed/lazy-vals-new-varhandle.flags @@ -0,0 +1,3 @@ +-java-output-version:9 +-Ylegacy-lazy-vals:false +-Yfuture-lazy-vals:true \ No newline at end of file diff --git a/tests/printing/transformed/lazy-vals-new-varhandle.scala b/tests/printing/transformed/lazy-vals-new-varhandle.scala new file mode 100644 index 000000000000..7240286af56a --- /dev/null +++ b/tests/printing/transformed/lazy-vals-new-varhandle.scala @@ -0,0 +1,3 @@ +object A { + lazy val x: Int = 2 +} diff --git a/tests/printing/untyped/i19643.check b/tests/printing/untyped/i19643.check new file mode 100644 index 000000000000..9a0405a6679d --- /dev/null +++ b/tests/printing/untyped/i19643.check @@ -0,0 +1,31 @@ +[[syntax trees at end of parser]] // tests/printing/untyped/i19643.java +package i19643 { + object MyAnnot() {} + trait MyAnnot private[this](x$1: scala.Unit) extends Object, _root_.java.lang. + annotation.Annotation { + def (reify: Class[? <: Object], + args: _root_.scala.Array[Class[? <: Object]]) + @_root_.scala.annotation.internal.AnnotationDefault def reify(): + Class[? <: Object] = _root_.scala.Predef.??? + @_root_.scala.annotation.internal.AnnotationDefault def args(): + _root_.scala.Array[Class[? <: Object]] = _root_.scala.Predef.??? + } + object MyAnnotated() { + @MyAnnot(reify = _root_.scala.Predef.classOf[String]) def method1() + : scala.Int = _root_.scala.Predef.??? + @MyAnnot(reify = _root_.scala.Predef.classOf[scala.Int]) def + method2(): scala.Int = _root_.scala.Predef.??? + @MyAnnot(reify = _root_.scala.Predef.classOf[scala.Long]) def + method3(): scala.Int = _root_.scala.Predef.??? + @MyAnnot(args = _root_.scala.Array(_root_.scala.Predef.classOf[String])) + def method4(): scala.Int = _root_.scala.Predef.??? + @MyAnnot(args = _root_.scala.Array(_root_.scala.Predef.classOf[scala.Int])) + def method5(): scala.Int = _root_.scala.Predef.??? + @MyAnnot(args = _root_.scala.Array(_root_.scala.Predef.classOf[scala.Long])) + def method6(): scala.Int = _root_.scala.Predef.??? + } + class MyAnnotated private[this](x$1: scala.Unit) extends Object { + def () = _root_.scala.Predef.??? + } +} + diff --git a/tests/printing/untyped/i19643.java b/tests/printing/untyped/i19643.java new file mode 100644 index 000000000000..afc598db2291 --- /dev/null +++ b/tests/printing/untyped/i19643.java @@ -0,0 +1,28 @@ +package i19643; + +@interface MyAnnot { + Class reify() default Object.class; + Class[] args() default {}; +} + +class MyAnnotated { + + @MyAnnot(reify = String.class) + public static int method1() { return 23; } + + @MyAnnot(reify = int.class) + public static int method2() { return 23; } + + @MyAnnot(reify = long.class) + public static int method3() { return 23; } + + @MyAnnot(args = {String.class}) + public static int method4() { return 23; } + + @MyAnnot(args = {int.class}) + public static int method5() { return 23; } + + @MyAnnot(args = {long.class}) + public static int method6() { return 23; } + +} diff --git a/tests/printing/untyped/strings.check b/tests/printing/untyped/strings.check new file mode 100644 index 000000000000..1d797c4654bd --- /dev/null +++ b/tests/printing/untyped/strings.check @@ -0,0 +1,8 @@ +[[syntax trees at end of parser]] // tests/printing/untyped/strings.scala +package { + class C { + def chars = "\b\t\n\f\r\"\'\\a\u0003" + def greeting = "hello, world" + } +} + diff --git a/tests/printing/untyped/strings.scala b/tests/printing/untyped/strings.scala new file mode 100644 index 000000000000..8f7183333df6 --- /dev/null +++ b/tests/printing/untyped/strings.scala @@ -0,0 +1,4 @@ + +class C: + def chars = "\b\t\n\f\r\"\'\\\u0061\u0003" + def greeting = "hello, world" diff --git a/tests/rewrites/ambiguous-named-tuple-assignment.check b/tests/rewrites/ambiguous-named-tuple-assignment.check new file mode 100644 index 000000000000..00e6cc4112f1 --- /dev/null +++ b/tests/rewrites/ambiguous-named-tuple-assignment.check @@ -0,0 +1,19 @@ +import scala.language.experimental.namedTuples + +object i21770: + def f(g: Int => Unit) = g(0) + var cache: Option[Int] = None + f(i => {cache = Some(i)}) + +object i21861: + var age: Int = 28 + { + age = 29 + } + + +object i21861c: + def age: Int = ??? + def age_=(x: Int): Unit = () + age = 29 + { age = 29 } diff --git a/tests/rewrites/ambiguous-named-tuple-assignment.scala b/tests/rewrites/ambiguous-named-tuple-assignment.scala new file mode 100644 index 000000000000..e9685b7b58cf --- /dev/null +++ b/tests/rewrites/ambiguous-named-tuple-assignment.scala @@ -0,0 +1,19 @@ +import scala.language.experimental.namedTuples + +object i21770: + def f(g: Int => Unit) = g(0) + var cache: Option[Int] = None + f(i => (cache = Some(i))) + +object i21861: + var age: Int = 28 + ( + age = 29 + ) + + +object i21861c: + def age: Int = ??? + def age_=(x: Int): Unit = () + age = 29 + ( age = 29 ) diff --git a/tests/rewrites/i24009.scala b/tests/rewrites/i24009.scala new file mode 100644 index 000000000000..73620d558e51 --- /dev/null +++ b/tests/rewrites/i24009.scala @@ -0,0 +1,9 @@ +//> using options -Wunused:imports -rewrite -Wconf:name=UnusedSymbol&origin=p.C:s + +package p: + class C + +package q: + import p.C // nowarn and no rewrite + + class D 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/rewrites/unused.check b/tests/rewrites/unused.check new file mode 100644 index 000000000000..585dc46ec39e --- /dev/null +++ b/tests/rewrites/unused.check @@ -0,0 +1,68 @@ + +//> using options -Wunused:all + +package p1: + import java.lang.Runnable + class C extends Runnable { def run() = () } + +package p2: + import java.lang.Runnable + class C extends Runnable { def run() = () } + +package p3: + import java.lang.Runnable + class C extends Runnable { def run() = () } + +package p4: + import java.lang.{Runnable, System}, System.out + class C extends Runnable { def run() = out.println() } + +package p5: + import java.lang.{Runnable, System}, System.out + class C extends Runnable { def run() = out.println() } + +package p6: + import java.lang.{Runnable, System}, System.out + class C extends Runnable { def run() = out.println() } + +package p7: + import java.lang.{Runnable, + System}, System.out + class C extends Runnable { def run() = out.println() } + +package p8: + import java.lang.{Runnable as R, System}, System.out + class C extends R { def run() = out.println() } + +package p9: + import java.lang.{Runnable as R, System, + Thread} + class C extends R { def run() = Thread(() => System.out.println()).start() } + +package p10: + object X: + def x = 42 + class C: + import X.* // preserve text, don't rewrite to p10.X.* + def c = x + +package p11: + import collection.mutable, mutable.ListBuffer // warn // warn // not checked :( + def buf = ListBuffer.empty[String] + +package p12: + import java.lang.System, java.lang.Runnable + class C extends Runnable { def run() = System.out.println() } + +package p13: + import java.lang.{Runnable, + + System}, System.out + class C extends Runnable { def run() = out.println() } + +package p14: + import collection.mutable + + import mutable.ListBuffer + + def buf = ListBuffer.empty[String] diff --git a/tests/rewrites/unused.scala b/tests/rewrites/unused.scala new file mode 100644 index 000000000000..866a36a7ca4b --- /dev/null +++ b/tests/rewrites/unused.scala @@ -0,0 +1,78 @@ + +//> using options -Wunused:all + +package p1: + import java.lang.Runnable + import java.lang.{Thread, String}, java.lang.Integer + class C extends Runnable { def run() = () } + +package p2: + import java.lang.Thread + import java.lang.String + import java.lang.Runnable + import java.lang.Integer + class C extends Runnable { def run() = () } + +package p3: + import java.lang.{Runnable, Thread, String} + class C extends Runnable { def run() = () } + +package p4: + import java.lang.{Runnable, System, Thread}, System.out + class C extends Runnable { def run() = out.println() } + +package p5: + import java.lang.{Thread, Runnable, System}, System.out + class C extends Runnable { def run() = out.println() } + +package p6: + import java.lang.{Runnable, System}, java.lang.Thread, System.out + class C extends Runnable { def run() = out.println() } + +package p7: + import java.lang.{Runnable, + Thread, + System}, System.out + class C extends Runnable { def run() = out.println() } + +package p8: + import java.lang.{Runnable as R, System, + Thread}, System.out + class C extends R { def run() = out.println() } + +package p9: + import java.lang.{Runnable as R, System, + Thread}, System.out + class C extends R { def run() = Thread(() => System.out.println()).start() } + +package p10: + object X: + def x = 42 + class C: + import X.*, java.util.HashMap // preserve text, don't rewrite to p10.X.* + def c = x + +package p11: + import collection.mutable, mutable.ListBuffer, java.lang.{Runnable, Thread} // warn // warn // not checked :( + def buf = ListBuffer.empty[String] + +package p12: + import collection.mutable, java.lang.System, java.lang.Runnable + class C extends Runnable { def run() = System.out.println() } + +package p13: + import java.lang.{Runnable, + + Thread, // leave one blank line instead of two + + System}, System.out + class C extends Runnable { def run() = out.println() } + +package p14: + import collection.mutable + + import java.lang.{Runnable, Thread} + + import mutable.ListBuffer + + def buf = ListBuffer.empty[String] diff --git a/tests/run-macros/i14902.check b/tests/run-macros/i14902.check index 9b27fcb7e5dc..5e6373a9516c 100644 --- a/tests/run-macros/i14902.check +++ b/tests/run-macros/i14902.check @@ -1,4 +1,4 @@ List(X) -List(X, Y, Z) +List(Y, Z, X) List(X) List(Y, Z) diff --git a/tests/run-macros/i17445-copy.check b/tests/run-macros/i17445-copy.check new file mode 100644 index 000000000000..be0acedaaf75 --- /dev/null +++ b/tests/run-macros/i17445-copy.check @@ -0,0 +1 @@ +Test passed! diff --git a/tests/run-macros/i17445-copy/Macro_1.scala b/tests/run-macros/i17445-copy/Macro_1.scala new file mode 100644 index 000000000000..238748a523ed --- /dev/null +++ b/tests/run-macros/i17445-copy/Macro_1.scala @@ -0,0 +1,11 @@ +import scala.quoted.* + +object TestMacro: + + inline def identityTreeMap[T](inline body: T): T = + ${ identityTreeMapImpl[T]('body) } + + def identityTreeMapImpl[T: Type](body: Expr[T])(using Quotes): Expr[T] = + import quotes.reflect.* + val mapped = new TreeMap {}.transformTerm(body.asTerm)(Symbol.spliceOwner) + mapped.asExprOf[T] diff --git a/tests/run-macros/i17445-copy/Test_2.scala b/tests/run-macros/i17445-copy/Test_2.scala new file mode 100644 index 000000000000..efbf0dffa737 --- /dev/null +++ b/tests/run-macros/i17445-copy/Test_2.scala @@ -0,0 +1,13 @@ +//> using options -Xcheck-macros + +case class P(x: Int, y: Int) + +object Test: + + def main(args: Array[String]): Unit = + val result = TestMacro.identityTreeMap { + val p = P(3, 4) + p.copy(y = 7) + } + assert(result == P(3, 7)) + println("Test passed!") diff --git a/tests/run-macros/i17445-new-default.check b/tests/run-macros/i17445-new-default.check new file mode 100644 index 000000000000..be0acedaaf75 --- /dev/null +++ b/tests/run-macros/i17445-new-default.check @@ -0,0 +1 @@ +Test passed! diff --git a/tests/run-macros/i17445-new-default/Macro_1.scala b/tests/run-macros/i17445-new-default/Macro_1.scala new file mode 100644 index 000000000000..e1a42ddc1076 --- /dev/null +++ b/tests/run-macros/i17445-new-default/Macro_1.scala @@ -0,0 +1,22 @@ +import scala.quoted.* + +object TestMacro: + + /** Macro that rewrites copy$default$1 (x's default) to copy$default$2 (y's default) + * in `p.copy(y = 7)`. This exercises Select.copy with a *new* DerivedName string + * that refers to a different but valid member. + */ + inline def swapDefault[T](inline body: T): T = + ${ swapDefaultImpl[T]('body) } + + def swapDefaultImpl[T: Type](body: Expr[T])(using Quotes): Expr[T] = + import quotes.reflect.* + val mapped = new TreeMap { + override def transformTerm(tree: Term)(owner: Symbol): Term = + tree match + case Select(qual, name) if name == "copy$default$1" => + Select.copy(tree)(transformTerm(qual)(owner), "copy$default$2") + case _ => + super.transformTerm(tree)(owner) + }.transformTerm(body.asTerm)(Symbol.spliceOwner) + mapped.asExprOf[T] diff --git a/tests/run-macros/i17445-new-default/Test_2.scala b/tests/run-macros/i17445-new-default/Test_2.scala new file mode 100644 index 000000000000..74d1b2347b51 --- /dev/null +++ b/tests/run-macros/i17445-new-default/Test_2.scala @@ -0,0 +1,15 @@ +case class P(x: Int, y: Int) + +object Test: + + def main(args: Array[String]): Unit = + // p.copy(y = 7) generates copy$default$1 for x (keeps p.x = 3). + // The macro rewrites copy$default$1 -> copy$default$2, + // so x's value becomes p.y (= 4) instead of p.x (= 3). + // Result: p.copy(4, 7) = P(4, 7) + val result = TestMacro.swapDefault { + val p = P(3, 4) + p.copy(y = 7) + } + assert(result == P(4, 7), s"Expected P(4, 7) but got $result") + println("Test passed!") diff --git a/tests/run-macros/i17445.check b/tests/run-macros/i17445.check new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/tests/run-macros/i17445.check @@ -0,0 +1 @@ +1 diff --git a/tests/run-macros/i17445/Macro_1.scala b/tests/run-macros/i17445/Macro_1.scala new file mode 100644 index 000000000000..80b950725320 --- /dev/null +++ b/tests/run-macros/i17445/Macro_1.scala @@ -0,0 +1,17 @@ +import scala.quoted.* + +object Macro { + + inline def changeIndexWhere[A](inline expr:A):A = ${ + changeIndexWhereImpl('expr) + } + + def changeIndexWhereImpl[A:Type](expr:Expr[A])(using Quotes):Expr[A] = { + import quotes.reflect.* + val r0 = expr.asTerm + val checker = new TreeMap() {} + val r = checker.transformTerm(r0)(Symbol.spliceOwner) + r.asExprOf[A] + } + +} diff --git a/tests/run-macros/i17445/Test_2.scala b/tests/run-macros/i17445/Test_2.scala new file mode 100644 index 000000000000..f97d8a94bb4a --- /dev/null +++ b/tests/run-macros/i17445/Test_2.scala @@ -0,0 +1,16 @@ +object Test { + + def main(args: Array[String]): Unit = { + val r = complileMe + println(r) + } + + def complileMe: Int = { + Macro.changeIndexWhere{ + val arr = Array(1,2,3) + val result = arr.indexWhere(_ == 2) + result + } + } + +} diff --git a/tests/run-macros/i20449.check b/tests/run-macros/i20449.check new file mode 100644 index 000000000000..289b3f95c488 --- /dev/null +++ b/tests/run-macros/i20449.check @@ -0,0 +1,50 @@ +------ UserName.T - Directly ------- +Original: Main_2$package.UserName.T +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ UserName.T - Directly ------- +Original: Main_2$package.UserName +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ ForeignWrapper1[UserName.T] ------- +Original: Main_2$package.UserName.T +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ ForeignWrapper2[UserName.T] ------- +Original: Main_2$package.UserName.T +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ ForeignWrapper1[UserName] ------- +Original: Main_2$package.UserName +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ ForeignWrapper2[UserName] ------- +Original: Main_2$package.UserName +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ Wrapper1[UserName.T] ------- +Original: Main_2$package.UserName.T +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ Wrapper2[UserName.T] ------- +Original: Main_2$package.UserName.T +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ Wrapper1[UserName] ------- +Original: Main_2$package.UserName +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + +------ Wrapper2[UserName] ------- +Original: Main_2$package.UserName +Dealias: Main_2$package.UserName.T +Dealias dealias: Main_2$package.UserName.T + diff --git a/tests/run-macros/i20449/Macro_1.scala b/tests/run-macros/i20449/Macro_1.scala new file mode 100644 index 000000000000..74b1dce3c4d6 --- /dev/null +++ b/tests/run-macros/i20449/Macro_1.scala @@ -0,0 +1,29 @@ +import scala.quoted.* + +class ForeignWrapper1[-A] { + inline def getTypeInfo(inline source: String): String = + ${ getTypeInfoImpl[A]('source) } + def createWrapper2 = ForeignWrapper2(this) +} + +class ForeignWrapper2[-A](val self: ForeignWrapper1[A]) { + inline def getTypeInfo(inline source: String): String = + ${getTypeInfoImpl[A]('source)} +} + +transparent inline def getTypeInfo[T](inline source: String) = + ${ getTypeInfoImpl[T]('source) } + +def getTypeInfoImpl[T: Type](source: Expr[String])(using ctx: Quotes) : Expr[String] = { + import ctx.reflect.* + + val tpe = TypeRepr.of[T] + val str = + s"""|------ ${source.valueOrAbort} ------- + |Original: ${tpe.show} + |Dealias: ${tpe.dealias.show} + |Dealias dealias: ${tpe.dealias.dealias.show} + """.stripMargin + + Expr(str) +} diff --git a/tests/run-macros/i20449/Main_2.scala b/tests/run-macros/i20449/Main_2.scala new file mode 100644 index 000000000000..3d2991489c1b --- /dev/null +++ b/tests/run-macros/i20449/Main_2.scala @@ -0,0 +1,41 @@ +object UserName { + opaque type T = String + + def apply(s: String): T = s +} + +type UserName = UserName.T + +class Wrapper1[-A] { + inline def getTypeInfo(inline source: String): String = + ${ getTypeInfoImpl[A]('source) } + def createWrapper2 = Wrapper2(this) +} + +class Wrapper2[-A](val self: Wrapper1[A]) { + inline def getTypeInfo(inline source: String): String = + ${getTypeInfoImpl[A]('source)} +} + + +@main def Test() = { + println(getTypeInfo[UserName.T]("UserName.T - Directly")) + println(getTypeInfo[UserName]("UserName.T - Directly")) + + val foreignWrapper = ForeignWrapper1[UserName.T]() + println(foreignWrapper.getTypeInfo("ForeignWrapper1[UserName.T]")) + println(foreignWrapper.createWrapper2.getTypeInfo("ForeignWrapper2[UserName.T]")) + + val foreignWrapper2 = ForeignWrapper1[UserName]() + println(foreignWrapper2.getTypeInfo("ForeignWrapper1[UserName]")) + println(foreignWrapper2.createWrapper2.getTypeInfo("ForeignWrapper2[UserName]")) + + val wrapper = Wrapper1[UserName.T]() + println(wrapper.getTypeInfo("Wrapper1[UserName.T]")) + println(wrapper.createWrapper2.getTypeInfo("Wrapper2[UserName.T]")) + + val wrapper2 = Wrapper1[UserName]() + println(wrapper2.getTypeInfo("Wrapper1[UserName]")) + println(wrapper2.createWrapper2.getTypeInfo("Wrapper2[UserName]")) + +} diff --git a/tests/run-macros/i22395.check b/tests/run-macros/i22395.check new file mode 100644 index 000000000000..5be77e32fac9 --- /dev/null +++ b/tests/run-macros/i22395.check @@ -0,0 +1 @@ +TypeRef(AppliedType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,module class )),module class )),Foo),List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),Int))),class Nested) diff --git a/tests/run-macros/i22395/Macro_1.scala b/tests/run-macros/i22395/Macro_1.scala new file mode 100644 index 000000000000..6f5cf55b9dd3 --- /dev/null +++ b/tests/run-macros/i22395/Macro_1.scala @@ -0,0 +1,14 @@ +import scala.quoted._ + +inline def test(): String = ${testImpl} +def testImpl(using Quotes) = { + import quotes.reflect._ + val fooSymbol = TypeRepr.of[Foo[Int]].typeSymbol + val nestedSymbol = fooSymbol.typeMember("Nested") + + Expr(TypeRepr.of[Foo[Int]].memberType(nestedSymbol).toString) +} + + +trait Foo[X]: + sealed abstract class Nested extends Foo[Int] diff --git a/tests/run-macros/i22395/Test_2.scala b/tests/run-macros/i22395/Test_2.scala new file mode 100644 index 000000000000..824e6ec869c9 --- /dev/null +++ b/tests/run-macros/i22395/Test_2.scala @@ -0,0 +1,2 @@ +@main def Test = + println(test()) diff --git a/tests/run-macros/i24006/Eval.scala b/tests/run-macros/i24006/Eval.scala new file mode 100644 index 000000000000..f32a066e4c40 --- /dev/null +++ b/tests/run-macros/i24006/Eval.scala @@ -0,0 +1,11 @@ + +import scala.quoted._ + +object Eval: + + inline def eval[T](x: List[T]) = ${ evalImpl[T]('x) } + def evalImpl[A](x: Expr[List[A]])(using Quotes): Expr[Unit] = + import quotes.reflect.* + println(x.asTerm.tpe.widen) + x.asTerm.tpe.widen.typeArgs.head + '{()} diff --git a/tests/run-macros/i24006/Test.scala b/tests/run-macros/i24006/Test.scala new file mode 100644 index 000000000000..f9d3223b9811 --- /dev/null +++ b/tests/run-macros/i24006/Test.scala @@ -0,0 +1,3 @@ +import Eval.eval + +@main def Test() = eval(List(1) :+ 4) diff --git a/tests/run-macros/tasty-extractors-2.check b/tests/run-macros/tasty-extractors-2.check index 5dd6af8d8b04..c9c3aec869d4 100644 --- a/tests/run-macros/tasty-extractors-2.check +++ b/tests/run-macros/tasty-extractors-2.check @@ -49,7 +49,7 @@ TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), None, List(DefDef("a", Nil, Inferred(), Some(Literal(IntConstant(0))))))), Literal(UnitConstant()))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") -Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil), TypeSelect(Select(Ident("_root_"), "scala"), "Product"), TypeSelect(Select(Ident("_root_"), "scala"), "Serializable")), None, List(DefDef("hashCode", List(TermParamClause(Nil)), Inferred(), Some(Apply(Ident("_hashCode"), List(This(Some("Foo")))))), DefDef("equals", List(TermParamClause(List(ValDef("x$0", Inferred(), None)))), Inferred(), Some(Apply(Select(Apply(Select(This(Some("Foo")), "eq"), List(TypeApply(Select(Ident("x$0"), "$asInstanceOf$"), List(Inferred())))), "||"), List(Match(Ident("x$0"), List(CaseDef(Bind("x$0", Typed(Wildcard(), Inferred())), None, Apply(Select(Literal(BooleanConstant(true)), "&&"), List(Apply(Select(Ident("x$0"), "canEqual"), List(This(Some("Foo"))))))), CaseDef(Wildcard(), None, Literal(BooleanConstant(false))))))))), DefDef("toString", List(TermParamClause(Nil)), Inferred(), Some(Apply(Ident("_toString"), List(This(Some("Foo")))))), DefDef("canEqual", List(TermParamClause(List(ValDef("that", Inferred(), None)))), Inferred(), Some(TypeApply(Select(Ident("that"), "isInstanceOf"), List(Inferred())))), DefDef("productArity", Nil, Inferred(), Some(Literal(IntConstant(0)))), DefDef("productPrefix", Nil, Inferred(), Some(Literal(StringConstant("Foo")))), DefDef("productElement", List(TermParamClause(List(ValDef("n", Inferred(), None)))), Inferred(), Some(Match(Ident("n"), List(CaseDef(Wildcard(), None, Apply(Ident("throw"), List(Apply(Select(New(Inferred()), ""), List(Apply(Select(Ident("n"), "toString"), Nil)))))))))), DefDef("productElementName", List(TermParamClause(List(ValDef("n", Inferred(), None)))), Inferred(), Some(Match(Ident("n"), List(CaseDef(Wildcard(), None, Apply(Ident("throw"), List(Apply(Select(New(Inferred()), ""), List(Apply(Select(Ident("n"), "toString"), Nil)))))))))), DefDef("copy", List(TermParamClause(Nil)), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil))))), ValDef("Foo", TypeIdent("Foo$"), Some(Apply(Select(New(TypeIdent("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil), Inferred()), Some(ValDef("_", Singleton(Ident("Foo")), None)), List(DefDef("apply", List(TermParamClause(Nil)), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil))), DefDef("unapply", List(TermParamClause(List(ValDef("x$1", Inferred(), None)))), Singleton(Literal(BooleanConstant(true))), Some(Literal(BooleanConstant(true)))), DefDef("toString", Nil, Inferred(), Some(Literal(StringConstant("Foo")))), TypeDef("MirroredMonoType", TypeBoundsTree(Inferred(), Inferred())), DefDef("fromProduct", List(TermParamClause(List(ValDef("x$0", Inferred(), None)))), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil)))))), Literal(UnitConstant()))) +Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil), TypeSelect(Select(Ident("_root_"), "scala"), "Product"), TypeSelect(Select(Ident("_root_"), "scala"), "Serializable")), None, List(DefDef("hashCode", List(TermParamClause(Nil)), Inferred(), Some(Literal(IntConstant(70822)))), DefDef("equals", List(TermParamClause(List(ValDef("x$0", Inferred(), None)))), Inferred(), Some(Apply(Select(Apply(Select(This(Some("Foo")), "eq"), List(TypeApply(Select(Ident("x$0"), "$asInstanceOf$"), List(Inferred())))), "||"), List(Match(Ident("x$0"), List(CaseDef(Bind("x$0", Typed(Wildcard(), Inferred())), None, Apply(Select(Literal(BooleanConstant(true)), "&&"), List(Apply(Select(Ident("x$0"), "canEqual"), List(This(Some("Foo"))))))), CaseDef(Wildcard(), None, Literal(BooleanConstant(false))))))))), DefDef("toString", List(TermParamClause(Nil)), Inferred(), Some(Apply(Ident("_toString"), List(This(Some("Foo")))))), DefDef("canEqual", List(TermParamClause(List(ValDef("that", Inferred(), None)))), Inferred(), Some(TypeApply(Select(Ident("that"), "isInstanceOf"), List(Inferred())))), DefDef("productArity", Nil, Inferred(), Some(Literal(IntConstant(0)))), DefDef("productPrefix", Nil, Inferred(), Some(Literal(StringConstant("Foo")))), DefDef("productElement", List(TermParamClause(List(ValDef("n", Inferred(), None)))), Inferred(), Some(Match(Ident("n"), List(CaseDef(Wildcard(), None, Apply(Ident("throw"), List(Apply(Select(New(Inferred()), ""), List(Apply(Select(Ident("n"), "toString"), Nil)))))))))), DefDef("productElementName", List(TermParamClause(List(ValDef("n", Inferred(), None)))), Inferred(), Some(Match(Ident("n"), List(CaseDef(Wildcard(), None, Apply(Ident("throw"), List(Apply(Select(New(Inferred()), ""), List(Apply(Select(Ident("n"), "toString"), Nil)))))))))), DefDef("copy", List(TermParamClause(Nil)), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil))))), ValDef("Foo", TypeIdent("Foo$"), Some(Apply(Select(New(TypeIdent("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", List(TermParamClause(Nil)), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil), Inferred()), Some(ValDef("_", Singleton(Ident("Foo")), None)), List(DefDef("apply", List(TermParamClause(Nil)), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil))), DefDef("unapply", List(TermParamClause(List(ValDef("x$1", Inferred(), None)))), Singleton(Literal(BooleanConstant(true))), Some(Literal(BooleanConstant(true)))), DefDef("toString", Nil, Inferred(), Some(Literal(StringConstant("Foo")))), TypeDef("MirroredMonoType", TypeBoundsTree(Inferred(), Inferred())), DefDef("fromProduct", List(TermParamClause(List(ValDef("x$0", Inferred(), None)))), Inferred(), Some(Apply(Select(New(Inferred()), ""), Nil)))))), Literal(UnitConstant()))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") Inlined(None, Nil, Block(List(ClassDef("Foo1", DefDef("", List(TermParamClause(List(ValDef("a", TypeIdent("Int"), None)))), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), None, List(ValDef("a", Inferred(), None)))), Literal(UnitConstant()))) diff --git a/tests/run-macros/type-members.check b/tests/run-macros/type-members.check new file mode 100644 index 000000000000..6805e6158e8e --- /dev/null +++ b/tests/run-macros/type-members.check @@ -0,0 +1,8 @@ +class FooSmall: List(type A, type B, type C, type D) +class FooLarge: List(type A, type B, type C, type D, type E) +type FooUnion: List() +type FooAnd: List(type A, type B, type C, type D, type E) +trait CLS1: List(type A, type B, type C, type B1, type B2, type A3, type B3, type B4) +type SharedAnd1: List(type B, type Shared, type A, type C) +type SharedAnd2: List(type C, type Shared, type A, type B) +type SharedUnion: List(type A, type Shared) diff --git a/tests/run-macros/type-members/Macro_1.scala b/tests/run-macros/type-members/Macro_1.scala new file mode 100644 index 000000000000..1aff04803798 --- /dev/null +++ b/tests/run-macros/type-members/Macro_1.scala @@ -0,0 +1,12 @@ +package example + +import scala.quoted.* + +object Macro { + inline def typeMembers[T <: AnyKind]: String = ${ typeMembersImpl[T] } + + def typeMembersImpl[T <: AnyKind: Type](using quotes: Quotes): Expr[String] = { + import quotes.reflect.* + Expr(s"${TypeRepr.of[T].typeSymbol}: ${TypeRepr.of[T].typeSymbol.typeMembers.toString}") + } +} diff --git a/tests/run-macros/type-members/Test_2.scala b/tests/run-macros/type-members/Test_2.scala new file mode 100644 index 000000000000..506e67068492 --- /dev/null +++ b/tests/run-macros/type-members/Test_2.scala @@ -0,0 +1,33 @@ +import example.Macro + +class FooSmall[A, B] { type D; type C } +class FooLarge[A, B, C] { type E; type D } + +type FooUnion[A, B] = FooSmall[A, B] | FooLarge[A, B, Int] +type FooAnd[A, B] = FooSmall[A, B] & FooLarge[A, B, Int] + +trait CLS4[A] { type B4 } +trait CLS3[A] extends CLS4[A] { type B3; type A3 } +trait CLS2[A] { type B2 } +trait CLS1[A, B, C] extends CLS2[A] with CLS3[B] { type B1 } + +trait SharedParent[A] { type Shared } +trait SharedA[A] extends SharedParent[A] { type B } +trait SharedB[A] extends SharedParent[A] { type C } +type SharedAnd1[A] = SharedA[A] & SharedB[A] +type SharedAnd2[A] = SharedB[A] & SharedA[A] +type SharedUnion[A] = SharedA[A] | SharedB[A] + +@main def Test(): Unit = { + println(Macro.typeMembers[FooSmall]) + println(Macro.typeMembers[FooLarge]) + + println(Macro.typeMembers[FooUnion]) + println(Macro.typeMembers[FooAnd]) + + println(Macro.typeMembers[CLS1]) + + println(Macro.typeMembers[SharedAnd1]) + println(Macro.typeMembers[SharedAnd2]) + println(Macro.typeMembers[SharedUnion]) +} diff --git a/tests/run/25-1/StringSearch.scala b/tests/run/25-1/StringSearch.scala new file mode 100644 index 000000000000..b98ea262d3b3 --- /dev/null +++ b/tests/run/25-1/StringSearch.scala @@ -0,0 +1,5 @@ +package p + +trait C[T] +trait Search[M] { def search(input: M): C[Int] = null } +object StringSearch extends Search[String] { } diff --git a/tests/run/25-1/Test.java b/tests/run/25-1/Test.java new file mode 100644 index 000000000000..df8319fd97d5 --- /dev/null +++ b/tests/run/25-1/Test.java @@ -0,0 +1,7 @@ +// scalajs: --skip + +public class Test { + public static void main(String[] args) { + p.StringSearch.search("test"); + } +} diff --git a/tests/run/25-2/Bar.scala b/tests/run/25-2/Bar.scala new file mode 100644 index 000000000000..f04c553422a7 --- /dev/null +++ b/tests/run/25-2/Bar.scala @@ -0,0 +1,7 @@ +package p + +abstract class Foo { + type T + def f(x: T): List[T] = List() +} +object Bar extends Foo { type T = String } diff --git a/tests/run/25-2/Test.java b/tests/run/25-2/Test.java new file mode 100644 index 000000000000..d4f260b74c0b --- /dev/null +++ b/tests/run/25-2/Test.java @@ -0,0 +1,7 @@ +// scalajs: --skip + +public class Test { + public static void main(String[] args) { + p.Bar.f(""); + } +} diff --git a/tests/run/26071.scala b/tests/run/26071.scala new file mode 100644 index 000000000000..f56e75f7f54f --- /dev/null +++ b/tests/run/26071.scala @@ -0,0 +1,10 @@ +abstract class OutPort { self: Outlet => + override def hashCode: Int = super.hashCode +} + +class Outlet extends OutPort + +object Test: + def main(args: Array[String]): Unit = + val myPort = Outlet() + println(myPort.hashCode) diff --git a/tests/run/2772.scala b/tests/run/2772.scala index fcde4c363b35..b2b3fa3578af 100644 --- a/tests/run/2772.scala +++ b/tests/run/2772.scala @@ -6,6 +6,7 @@ object Test { System.setErr(null) // setOut(null) confuses the testing framework... val a = () => foo(oldErr) a() + System.setErr(oldErr) } def foo(err: OutputStream): Unit = { diff --git a/tests/run/enum-java-scala.check b/tests/run/enum-java-scala.check new file mode 100644 index 000000000000..daa498773f34 --- /dev/null +++ b/tests/run/enum-java-scala.check @@ -0,0 +1,2 @@ +Scala: Testme Hello= Hello +Java: Testme Hello= Hello \ No newline at end of file diff --git a/tests/run/enum-java-scala/Test.java b/tests/run/enum-java-scala/Test.java new file mode 100644 index 000000000000..7d059e592098 --- /dev/null +++ b/tests/run/enum-java-scala/Test.java @@ -0,0 +1,9 @@ +// scalajs: --skip + +// see: https://github.com/scala/scala3/issues/12637 +public class Test { + public static void main(String[] args) { + TestenumS.go(); + System.out.println("Java: Testme Hello= " + Testme.Hello); + } +} diff --git a/tests/run/enum-java-scala/Testme.scala b/tests/run/enum-java-scala/Testme.scala new file mode 100644 index 000000000000..cf8b1e0abe36 --- /dev/null +++ b/tests/run/enum-java-scala/Testme.scala @@ -0,0 +1,5 @@ +object TestenumS: + def go() = println("Scala: Testme Hello= " + Testme.Hello) + +enum Testme extends java.lang.Enum[Testme]: + case Hello diff --git a/tests/run/evaluation-order.check b/tests/run/evaluation-order.check new file mode 100644 index 000000000000..3d6ae808c547 --- /dev/null +++ b/tests/run/evaluation-order.check @@ -0,0 +1,2 @@ +b evaluated +c evaluated diff --git a/tests/run/evaluation-order.scala b/tests/run/evaluation-order.scala new file mode 100644 index 000000000000..3cd5dab0f51b --- /dev/null +++ b/tests/run/evaluation-order.scala @@ -0,0 +1,11 @@ +abstract class Foo(a: => Int, b: Int, c: Int) + +object Bar extends Foo( + b = { println("b evaluated"); 2 }, + c = { println("c evaluated"); 3 }, + a = { println("a evaluated"); 1 } +) + +object Test: + def main(args: Array[String]): Unit = + Bar // trigger initialization diff --git a/tests/run/getclass.scala b/tests/run/getclass.scala index d8a339f8ea5a..dbf311980edf 100644 --- a/tests/run/getclass.scala +++ b/tests/run/getclass.scala @@ -1,5 +1,7 @@ // scalajs: --skip +import scala.util.Properties.isJavaAtLeast + class ValueClass(val i: Integer) extends AnyVal class SomeClass @@ -38,7 +40,10 @@ object Test { println("\nFunctions:") // FunctionN.getClass.toString has form of "class Test$$$Lambda$N/1349414238", // but "N/1349414238" depends on environment - println(f1.getClass.toString.take("class Test$$$Lambda$".length)) - println(f2.getClass.toString.take("class Test$$$Lambda$".length)) + val lambda = if isJavaAtLeast(21) then "class Test$$$Lambda/" else "class Test$$$Lambda$" + assert(f1.getClass.toString.take("class Test$$$Lambda$".length) == lambda) + assert(f2.getClass.toString.take("class Test$$$Lambda$".length) == lambda) + println("class Test$$$Lambda$") + println("class Test$$$Lambda$") } } diff --git a/tests/run/i13461-b.check b/tests/run/i13461-b.check new file mode 100644 index 000000000000..62c33240b000 --- /dev/null +++ b/tests/run/i13461-b.check @@ -0,0 +1,4 @@ +35s +35m +15s, 20m, 15m, 20s +15s, 15m, 15s, 20m diff --git a/tests/run/i13461-b.scala b/tests/run/i13461-b.scala new file mode 100644 index 000000000000..639e3e33b825 --- /dev/null +++ b/tests/run/i13461-b.scala @@ -0,0 +1,59 @@ +object Time: + opaque type Time = String + opaque type Seconds <: Time = String + opaque type Minutes <: Time = String + opaque type Mixed <: Time = String + + type Units = Seconds | Minutes + + def sec(n: Int): Seconds = + s"${n}s" + + def min(n: Int): Minutes = + s"${n}m" + + def mixed(t1: Time, t2: Time): Mixed = + s"${t1}, ${t2}" + + extension (t: Units) + def number: Int = + (t : String).init.toInt + + extension [T1 <: Time](inline a: T1) + transparent inline def +[T2 <: Time](inline b: T2): Time = + inline (a, b) match + case x: (Seconds, Seconds) => + (sec(x._1.number + x._2.number)) + + case x: (Minutes, Minutes) => + (min(x._1.number + x._2.number)) + + case x: (Time, Time) => + (mixed(x._1, x._2)) + end + +end Time + +import Time.* + +// Test seconds +val a = sec(15) +val b = sec(20) + +// Test minutes +val x = min(15) +val y = min(20) + +// Test mixes +val m1 = a + y +val m2 = x + b + +// Test upper type +val t1: Time = a +val t2: Time = x +val t3: Time = m1 + +@main def Test() = + println(a + b) + println(x + y) + println(m1 + m2) + println(t1 + t2 + t3) diff --git a/tests/run/i1354.scala b/tests/run/i1354.scala index 08b129e4ef0b..3305a560df0c 100644 --- a/tests/run/i1354.scala +++ b/tests/run/i1354.scala @@ -1,3 +1,6 @@ +// scalajs: --skip +// (because scala.js has special semantics for numbers) + object Test { def foo(a: Int | Double) = a match { case a: (Float | Boolean) => 1 diff --git a/tests/run/i1354b.check b/tests/run/i1354b.check new file mode 100644 index 000000000000..ce686866d0b6 --- /dev/null +++ b/tests/run/i1354b.check @@ -0,0 +1,6 @@ +0 +false +5 +1 +true +true diff --git a/tests/run/i1354b.scala b/tests/run/i1354b.scala new file mode 100644 index 000000000000..28ce2eed96e5 --- /dev/null +++ b/tests/run/i1354b.scala @@ -0,0 +1,29 @@ +// special version if i1354 that applies to Scala.js as well + +object Test { + def foo(a: Int | Double) = a match { + case a: (Char | Boolean) => 1 + case _ => 0 + } + + def typeTest(a: Int | Double) = a.isInstanceOf[Char | Boolean] // false + + def typeCast(a: Int | Double) = a.asInstanceOf[Char | Boolean] // no error + + def main(args: Array[String]): Unit = { + println(foo(4)) + + println(typeTest(4)) + + println(typeCast(5)) + + Boolean.box(true) match { + case a: (Float | Boolean) => println(1) + case _ => println(0) + } + + println(Boolean.box(true).isInstanceOf[Float | Boolean]) + + println(Boolean.box(true).asInstanceOf[Float | Boolean]) + } +} diff --git a/tests/run/i19587.scala b/tests/run/i19587.scala new file mode 100644 index 000000000000..25d7220fd99b --- /dev/null +++ b/tests/run/i19587.scala @@ -0,0 +1,34 @@ +case class Foo(bar: String, baz: Int) +object Foo: + def withDefaults(bar: String = "", baz: Int = 42) = Foo(bar, baz) + +object Test1: + export Foo.withDefaults + +object Test2: + export Foo.withDefaults as fooWithDefaults + +class Bar: + infix def bar(other: Bar) = 42 + +object Baz: + val b = Bar() + export b.bar + export b.bar as baz + +@main def Test = + // this works + assert: + Test1.withDefaults("test1") == Foo("test1", 42) + + // this doesn't work + assert: + Test2.fooWithDefaults("test2") == Foo("test2", 42) + + val b = Bar() + println: + b bar Bar() + println: + Baz bar Bar() + println: + Baz baz Bar() 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/i22345.scala b/tests/run/i22345.scala new file mode 100644 index 000000000000..56d44ff21507 --- /dev/null +++ b/tests/run/i22345.scala @@ -0,0 +1,9 @@ +import scala.reflect.ClassTag + +def makeSeq[T](args: T*): Seq[T] = args + +@main def Test: Unit = + val a: Array[(Int, String)] = Array[Int *: String *: EmptyTuple]() + val b: Array[(Int, String)] = Array[Int *: String *: EmptyTuple]((1, "hello")) + val c: Array[(Int, String)] = makeSeq[Int *: String *: EmptyTuple]().toArray + assert(summon[ClassTag[(Int, String)]] == summon[ClassTag[Int *: String *: EmptyTuple]]) diff --git a/tests/run/i22628.check b/tests/run/i22628.check new file mode 100644 index 000000000000..94ebd0cfec65 --- /dev/null +++ b/tests/run/i22628.check @@ -0,0 +1,2 @@ +MyServerCall.close +InnerServerCallBase.close diff --git a/tests/run/i22628/InnerServerCallBase.java b/tests/run/i22628/InnerServerCallBase.java new file mode 100644 index 000000000000..1f43653e394d --- /dev/null +++ b/tests/run/i22628/InnerServerCallBase.java @@ -0,0 +1,8 @@ +package mylib; + +abstract class InnerServerCallBase implements ServerCall { + @Override + public void close() { + System.out.println("InnerServerCallBase.close"); + } +} diff --git a/tests/run/i22628/MyServerCall.scala b/tests/run/i22628/MyServerCall.scala new file mode 100644 index 000000000000..b2a3c2fc6def --- /dev/null +++ b/tests/run/i22628/MyServerCall.scala @@ -0,0 +1,8 @@ +package mycode + +class MyServerCall extends mylib.PublicServerCallBase { + override def close(): Unit = { + println("MyServerCall.close") + super.close() + } +} diff --git a/tests/run/i22628/PublicServerCallBase.java b/tests/run/i22628/PublicServerCallBase.java new file mode 100644 index 000000000000..e324ba4c084e --- /dev/null +++ b/tests/run/i22628/PublicServerCallBase.java @@ -0,0 +1,4 @@ +package mylib; + +public abstract class PublicServerCallBase extends InnerServerCallBase { +} diff --git a/tests/run/i22628/ServerCall.java b/tests/run/i22628/ServerCall.java new file mode 100644 index 000000000000..dce1c8382e32 --- /dev/null +++ b/tests/run/i22628/ServerCall.java @@ -0,0 +1,5 @@ +package mylib; + +public interface ServerCall { + void close(); +} diff --git a/tests/run/i22628/Test.scala b/tests/run/i22628/Test.scala new file mode 100644 index 000000000000..4ff3d4e0a2ee --- /dev/null +++ b/tests/run/i22628/Test.scala @@ -0,0 +1,5 @@ +// scalajs: --skip + +object Test extends App { + new mycode.MyServerCall().close() +} diff --git a/tests/run/i22649/Macro_1.scala b/tests/run/i22649/Macro_1.scala new file mode 100644 index 000000000000..14b94f1e7f5f --- /dev/null +++ b/tests/run/i22649/Macro_1.scala @@ -0,0 +1,9 @@ +import scala.quoted.* + +inline def structureOf[T]: String = + ${ structureOfImpl[T] } + +private def structureOfImpl[T](using Quotes, Type[T]): Expr[String] = + import quotes.reflect.* + val str = Printer.TypeReprStructure.show(TypeRepr.of[T]) + Expr(str) diff --git a/tests/run/i22649/Test_2.scala b/tests/run/i22649/Test_2.scala new file mode 100644 index 000000000000..16347ae66140 --- /dev/null +++ b/tests/run/i22649/Test_2.scala @@ -0,0 +1,11 @@ +@main def Test = + val str = + structureOf[{ + type T + def make: T + }] + + assert( + str == """RecursiveType(rec1 => Refinement(Refinement(TypeRef(ThisType(TypeRef(NoPrefix(), "lang")), "Object"), "T", TypeBounds(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Nothing"), TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Any"))), "make", ByNameType(TypeRef(RecursiveThis(), "T"))))""", + s"Was $str" + ) diff --git a/tests/run/i22888.scala b/tests/run/i22888.scala new file mode 100644 index 000000000000..abc71376cc4d --- /dev/null +++ b/tests/run/i22888.scala @@ -0,0 +1,37 @@ +trait Foo: + type A[T] + var arr: Array[A[Int]] = null + +class Bar() extends Foo: + type A[T] = Int + +trait Foo2: + type Dummy + type A[T] <: Dummy + var arr: Array[A[Int]] = null + +class Bar2() extends Foo2: + type Dummy = Any + type A[T] = Int + +trait Foo3: + type A[T] <: Object + var arr: Array[A[String]] = null + +class Bar3() extends Foo3: + type A[T] = String + +object Test: + def main(args: Array[String]) = + val bar = new Bar() + bar.arr = Array.ofDim[Int](1) + bar.arr(0) = 123 + + val bar2 = new Bar2() + bar2.arr = Array.ofDim[Int](1) + bar2.arr(0) = 123 + + val bar3 = new Bar3() + bar3.arr = Array.ofDim[String](1) + bar3.arr(0) = "123" + diff --git a/tests/run/i22968.check b/tests/run/i22968.check new file mode 100644 index 000000000000..375518e921cd --- /dev/null +++ b/tests/run/i22968.check @@ -0,0 +1 @@ +List() diff --git a/tests/run/i22968.scala b/tests/run/i22968.scala new file mode 100644 index 000000000000..28ed954d1d86 --- /dev/null +++ b/tests/run/i22968.scala @@ -0,0 +1,7 @@ +import scala.compiletime.testing.typeCheckErrors + +object Test { + def main(args: Array[String]): Unit = { + println(typeCheckErrors("enum Foo { case A }")) + } +} diff --git a/tests/run/i22991/Bar.scala b/tests/run/i22991/Bar.scala new file mode 100644 index 000000000000..1611544fd706 --- /dev/null +++ b/tests/run/i22991/Bar.scala @@ -0,0 +1,10 @@ +class Blah extends scala.annotation.StaticAnnotation + +trait Barly { + def bar[T](a: String, @Foo v: Int)(@Foo b: T, @Blah w: Int) = () + extension [T](s: String) def f[U](@Foo t: T, @Foo u: U) = () +} + +class Bar extends Barly { + def bar2(@Foo v: Int) = () +} diff --git a/tests/run/i22991/Foo.java b/tests/run/i22991/Foo.java new file mode 100644 index 000000000000..ac4e8460ac3a --- /dev/null +++ b/tests/run/i22991/Foo.java @@ -0,0 +1,5 @@ +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Foo { +} diff --git a/tests/run/i22991/Test.scala b/tests/run/i22991/Test.scala new file mode 100644 index 000000000000..e1de929db7b3 --- /dev/null +++ b/tests/run/i22991/Test.scala @@ -0,0 +1,34 @@ +// scalajs: --skip +//Test java runtime reflection access to @Runtime annotations on method parameters. +@main def Test = + def check(actual: Int)(expect: Int)(msg: => String): Unit = + assert(actual == expect, s"$msg expected $expect but actually $actual") + locally: + // def bar[T](a: String, @Foo v: Int)(@Foo b: T, @Blah w: Int) + val method = classOf[Bar].getMethod("bar", classOf[String], classOf[Int], classOf[Object], classOf[Int]) + val annots = method.getParameterAnnotations() + check(annots.length)(4)("param count") + check(annots(0).length)(0)("count at 0") + check(annots(1).length)(1)("count at 1") + assert(annots(1)(0).isInstanceOf[Foo], "expect Foo at 1") + check(annots(2).length)(1)("count at 2") + assert(annots(2)(0).isInstanceOf[Foo], "expect Foo at 2") + check(annots(3).length)(0)("count at 3") + + locally: + // def bar2(@Foo v: Int) + val method = classOf[Bar].getMethod("bar2", classOf[Int]) + val annots = method.getParameterAnnotations() + check(annots.length)(1)("param count") + assert(annots(0)(0).isInstanceOf[Foo], "expect Foo at 0") + + locally: + // extension [T](s: String) def f[U](@Foo t: T, @Foo u: U) = () + val method = classOf[Bar].getMethod("f", classOf[String], classOf[Object], classOf[Object]) + val annots = method.getParameterAnnotations() + check(annots.length)(3)("param count") + check(annots(0).length)(0)("count at 0") + check(annots(1).length)(1)("count at 1") + check(annots(2).length)(1)("count at 2") + assert(annots(1)(0).isInstanceOf[Foo], "expect Foo at 1") + assert(annots(2)(0).isInstanceOf[Foo], "expect Foo at 2") diff --git a/tests/run/i23179.scala b/tests/run/i23179.scala new file mode 100644 index 000000000000..43e223b667f3 --- /dev/null +++ b/tests/run/i23179.scala @@ -0,0 +1,31 @@ +object Test { + // Basic case: SAM expects Array[AnyRef], impl has Array[? >: AnyRef] + trait A { def f(a: Array[AnyRef]): Any } + def g(a: A) = a.f(Array.empty[AnyRef]) + + // Multiple array parameters + trait MultiParam { def f(a: Array[AnyRef], b: Array[AnyRef]): Any } + def gMulti(a: MultiParam) = a.f(Array.empty[AnyRef], Array.empty[AnyRef]) + + // Mixed parameters: only some need adaptation + trait MixedParam { def f(a: Array[AnyRef], b: Int): Any } + def gMixed(a: MixedParam) = a.f(Array.empty[AnyRef], 42) + + // Generic return type + trait GenericReturn[T] { def f(a: Array[AnyRef]): T } + def gGeneric[T](a: GenericReturn[T]): T = a.f(Array.empty[AnyRef]) + + def main(args: Array[String]): Unit = { + // Basic case + g((x: Array[? >: AnyRef]) => x.headOption) + + // Multiple array parameters - both need adaptation + gMulti((x: Array[? >: AnyRef], y: Array[? >: AnyRef]) => (x.headOption, y.headOption)) + + // Mixed - only first param needs adaptation + gMixed((x: Array[? >: AnyRef], n: Int) => (x.headOption, n)) + + // Generic return type + val result: Option[?] = gGeneric((x: Array[? >: AnyRef]) => x.headOption) + } +} diff --git a/tests/run/i23245a/api.scala b/tests/run/i23245a/api.scala new file mode 100644 index 000000000000..c2b96527299d --- /dev/null +++ b/tests/run/i23245a/api.scala @@ -0,0 +1,23 @@ + +package logadapter: + + trait AbstractLogAdapter: + def info(message: String): Unit + + trait AbstractApi[T <: AbstractLogAdapter]: + def logAdapterFor(loggerName: String): T + trait SelfLogging: + given adapter: T = logAdapterFor("TestLogger") + // workaround: + //given () => T = logAdapterFor("TestLogger") + // or + //private val adapter = logAdapterFor("TestLogger") + //given T = adapter + // or just pollute the interface so it's never taken as pure + //private val z = 42 + + object Api extends AbstractApi[LogAdapter]: + def logAdapterFor(loggerName: String): LogAdapter = new LogAdapter(loggerName) + + class LogAdapter(loggerName: String) extends AbstractLogAdapter: + def info(message: String): Unit = System.err.println(s"INFO [${loggerName}] ${message}") diff --git a/tests/run/i23245a/test_2.scala b/tests/run/i23245a/test_2.scala new file mode 100644 index 000000000000..faf0960c29cb --- /dev/null +++ b/tests/run/i23245a/test_2.scala @@ -0,0 +1,6 @@ + + +object Test extends logadapter.Api.SelfLogging: + def main(args: Array[String]): Unit = + summon[logadapter.LogAdapter].info("Hello") + diff --git a/tests/run/i23245b/outer.scala b/tests/run/i23245b/outer.scala new file mode 100644 index 000000000000..d0c6c8458d36 --- /dev/null +++ b/tests/run/i23245b/outer.scala @@ -0,0 +1,10 @@ + +trait T: + def f = 42 + trait D: + lazy val g = f + +object C extends T + +// D parent of Z is taken as PureInterface under separate compilation +// and thus doesn't get an outer. diff --git a/tests/run/i23245b/test_2.scala b/tests/run/i23245b/test_2.scala new file mode 100644 index 000000000000..071e2a6136c2 --- /dev/null +++ b/tests/run/i23245b/test_2.scala @@ -0,0 +1,5 @@ + +object Z extends C.D + +@main def Test = println: + Z.g diff --git a/tests/run/i23245c/outer.scala b/tests/run/i23245c/outer.scala new file mode 100644 index 000000000000..5e0a2112a542 --- /dev/null +++ b/tests/run/i23245c/outer.scala @@ -0,0 +1,7 @@ + +trait T: + def f = 42 + trait D: + lazy val g = f + +object C extends T diff --git a/tests/run/i23245c/test.scala b/tests/run/i23245c/test.scala new file mode 100644 index 000000000000..071e2a6136c2 --- /dev/null +++ b/tests/run/i23245c/test.scala @@ -0,0 +1,5 @@ + +object Z extends C.D + +@main def Test = println: + Z.g diff --git a/tests/run/i23279.scala b/tests/run/i23279.scala new file mode 100644 index 000000000000..8774e5afcd79 --- /dev/null +++ b/tests/run/i23279.scala @@ -0,0 +1,28 @@ +inline def simpleInlineWrap(f: => Any): Unit = f + +@main def Test(): Unit = { + simpleInlineWrap { + object lifecycle { + object Lifecycle { + trait FromZIO + } + } + object defn { + val Lifecycle: lifecycle.Lifecycle.type = lifecycle.Lifecycle + } + val xa: defn.Lifecycle.type = defn.Lifecycle + } + + // more nested case + simpleInlineWrap { + object lifecycle { + object Lifecycle { + object FromZIO + } + } + object defn { + val Lifecycle: lifecycle.Lifecycle.type = lifecycle.Lifecycle + } + val xa: defn.Lifecycle.FromZIO.type = defn.Lifecycle.FromZIO + } +} diff --git a/tests/run/i23305.scala b/tests/run/i23305.scala new file mode 100644 index 000000000000..22cfe04339fc --- /dev/null +++ b/tests/run/i23305.scala @@ -0,0 +1,24 @@ +//> using options -language:experimental.erasedDefinitions + +erased trait DBMeta[A] + +trait Table[A] + +case class Saved[A]() +object Saved { + def table[A](using dbm: DBMeta[A]): Table[Saved[A]] = new Table[Saved[A]] {} +} + +case class deleteBulk[A](as: List[Saved[A]])(using dbm: DBMeta[A], tbl: Table[A]) { + final given savedTable: Table[Saved[A]] = Saved.table[A] +} + +case class Foo() +object Foo { + given dbMeta: DBMeta[Foo] = new DBMeta[Foo] {} + given table: Table[Foo] = new Table[Foo] {} +} + +@main def Test = + val del = deleteBulk(List(Saved[Foo]())) + del.savedTable diff --git a/tests/run/i23444.scala b/tests/run/i23444.scala new file mode 100644 index 000000000000..ada7545e077f --- /dev/null +++ b/tests/run/i23444.scala @@ -0,0 +1,22 @@ + +import annotation.* + +class Path(action: () => Unit, parent: Option[Path]): + object O: + @tailrec + def apply(): Unit = + action() + + parent match + case Some(p) => + p.O.apply() + case None => + +@main def Test: Unit = + var counter = 0 + val fun = () => { + counter += 1 + if counter > 2 then throw AssertionError("bad loop") + } + val path = Path(fun, Some(Path(fun, None))) + path.O() diff --git a/tests/run/i23477.scala b/tests/run/i23477.scala new file mode 100644 index 000000000000..2e705655943c --- /dev/null +++ b/tests/run/i23477.scala @@ -0,0 +1,8 @@ +//> using options -Ycheck:expandPrivate + +class C private (x: Int, y: Int = 1): + def this() = this(1) + +@main def Test = + println: + C() diff --git a/tests/run/i23479/NonSeal.java b/tests/run/i23479/NonSeal.java new file mode 100644 index 000000000000..214ff5e58fc6 --- /dev/null +++ b/tests/run/i23479/NonSeal.java @@ -0,0 +1,3 @@ +// test: -jvm 17+ +public non-sealed interface NonSeal extends Seal { +} diff --git a/tests/run/i23479/Seal.java b/tests/run/i23479/Seal.java new file mode 100644 index 000000000000..53a20ddd7d25 --- /dev/null +++ b/tests/run/i23479/Seal.java @@ -0,0 +1,6 @@ + +public sealed interface Seal permits NonSeal { + default int g() { + return 42; + } +} diff --git a/tests/run/i23479/test.scala b/tests/run/i23479/test.scala new file mode 100644 index 000000000000..7380afe564d7 --- /dev/null +++ b/tests/run/i23479/test.scala @@ -0,0 +1,4 @@ +// scalajs: --skip +class C() extends NonSeal + +@main def Test = C() diff --git a/tests/run/i23479b.scala b/tests/run/i23479b.scala new file mode 100644 index 000000000000..e4f1ba3e16eb --- /dev/null +++ b/tests/run/i23479b.scala @@ -0,0 +1,13 @@ +// scalajs: --skip +trait Ord[A] + +object Ord: + sealed trait Rev[A] extends Ord[A]: + def reverse: Rev[A] = this + + trait IntOrd extends Ord[Int] + + object Int extends IntOrd with Rev[Int] + +@main def Test = + assert(Ord.Int.getClass.getDeclaredMethods.map(_.getName).toList.contains("reverse")) 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/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/run/i24201a.check b/tests/run/i24201a.check new file mode 100644 index 000000000000..0894319466f1 --- /dev/null +++ b/tests/run/i24201a.check @@ -0,0 +1,3 @@ +E1 +1 +3 diff --git a/tests/run/i24201a.scala b/tests/run/i24201a.scala new file mode 100644 index 000000000000..2bf8ea18b1a8 --- /dev/null +++ b/tests/run/i24201a.scala @@ -0,0 +1,14 @@ +abstract class Foo[T](defaultValue: => T, arg1: Int = 1, arg2: Int = 2): + def getDefault: T = defaultValue + def getArg1: Int = arg1 + def getArg2: Int = arg2 + +enum Baz: + case E1, E2 + +object Baz extends Foo[Baz](Baz.E1, arg2 = 3) + +@main def Test = + println(Baz.getDefault) + println(Baz.getArg1) + println(Baz.getArg2) diff --git a/tests/run/i24201b.check b/tests/run/i24201b.check new file mode 100644 index 000000000000..dda62fb7e67a --- /dev/null +++ b/tests/run/i24201b.check @@ -0,0 +1,7 @@ +c evaluated +b evaluated +Bar body +b = 1 +c = 2 +a evaluated +a evaluated diff --git a/tests/run/i24201b.scala b/tests/run/i24201b.scala new file mode 100644 index 000000000000..b71ce12caef4 --- /dev/null +++ b/tests/run/i24201b.scala @@ -0,0 +1,19 @@ +// Test evaluation order with by-name parameters and named arguments in constructor +abstract class Foo(a: => Unit, b: Int, c: Int): + def triggerA(): Unit = a + def getB: Int = b + def getC: Int = c + +object Bar extends Foo( + c = { println("c evaluated"); 2 }, + a = { println("a evaluated") }, + b = { println("b evaluated"); 1 } +): + println("Bar body") + println(s"b = $getB") + println(s"c = $getC") + triggerA() + triggerA() // should evaluated again + +@main def Test = + Bar diff --git a/tests/run/i24357.scala b/tests/run/i24357.scala new file mode 100644 index 000000000000..6b6d46b85963 --- /dev/null +++ b/tests/run/i24357.scala @@ -0,0 +1,15 @@ +class E1 extends Exception +class E2 extends Exception + +type E1or2 = E1 | E2 + +def caughtE1orE2(body: => Nothing): Boolean = + try body + catch + case ex: E1or2 => true + case _ => false + +@main def Test = + assert(caughtE1orE2(throw new E1 {})) + assert(caughtE1orE2(throw new E2 {})) + assert(!caughtE1orE2(throw new Exception {})) diff --git a/tests/run/i24507.check b/tests/run/i24507.check new file mode 100644 index 000000000000..257cc5642cb1 --- /dev/null +++ b/tests/run/i24507.check @@ -0,0 +1 @@ +foo diff --git a/tests/run/i24507/A.java b/tests/run/i24507/A.java new file mode 100644 index 000000000000..489beb4dd6cb --- /dev/null +++ b/tests/run/i24507/A.java @@ -0,0 +1,9 @@ +package a; + +public class A { + protected class B { + public String foo() { + return "foo"; + } + } +} diff --git a/tests/run/i24507/test.scala b/tests/run/i24507/test.scala new file mode 100644 index 000000000000..38f1e6465f98 --- /dev/null +++ b/tests/run/i24507/test.scala @@ -0,0 +1,12 @@ +// scalajs: --skip +import a._ + +class T extends A { + class Acc extends B + val acc = new Acc + def foo(): String = acc.foo() +} + +@main def Test = + val t = new T + println(t.foo()) diff --git a/tests/run/i24553.check b/tests/run/i24553.check new file mode 100644 index 000000000000..33d01ab2ec88 --- /dev/null +++ b/tests/run/i24553.check @@ -0,0 +1,11 @@ +public boolean java.lang.Object.equals(java.lang.Object) +public final native java.lang.Class java.lang.Object.getClass() +public final native void java.lang.Object.notify() +public final native void java.lang.Object.notifyAll() +public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException +public final void java.lang.Object.wait() throws java.lang.InterruptedException +public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException +public int Foo.hello() +public int Foo.x() +public java.lang.String java.lang.Object.toString() +public native int java.lang.Object.hashCode() diff --git a/tests/run/i24553.scala b/tests/run/i24553.scala new file mode 100644 index 000000000000..80338a596cf4 --- /dev/null +++ b/tests/run/i24553.scala @@ -0,0 +1,8 @@ +// scalajs: --skip +class Foo: + val hello = 1337 + val x: hello.type = ??? + +@main def Test = + val mtds = classOf[Foo].getMethods().sortBy(_.toGenericString()) + for mtd <- mtds do println(mtd.toGenericString()) diff --git a/tests/run/i24573.check b/tests/run/i24573.check new file mode 100644 index 000000000000..f3c54b25aa25 --- /dev/null +++ b/tests/run/i24573.check @@ -0,0 +1,45 @@ +1 +2 +3 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +31 +32 +33 +34 +41 +42 +43 +44 +45 +46 +51 +52 +53 +55 +56 +57 +61 +62 +63 +64 +65 +71 +72 +75 +76 +81 +82 diff --git a/tests/run/i24573.scala b/tests/run/i24573.scala new file mode 100644 index 000000000000..f665c53e28f5 --- /dev/null +++ b/tests/run/i24573.scala @@ -0,0 +1,190 @@ +trait ConTU[-T] extends (T => Unit): + def apply(t: T): Unit + +trait ConTI[-T] extends (T => Int): + def apply(t: T): Int + +trait ConTS[-T] extends (T => String): + def apply(t: T): String + +trait ConIR[+R] extends (Int => R): + def apply(t: Int): R + +trait ConSR[+R] extends (String => R): + def apply(t: String): R + +trait ConUR[+R] extends (() => R): + def apply(): R + +trait ConII extends (Int => Int): + def apply(t: Int): Int + +trait ConSI extends (String => Int): + def apply(t: String): Int + +trait ConIS extends (Int => String): + def apply(t: Int): String + +trait ConUU extends (() => Unit): + def apply(): Unit + +trait ConVCVC extends (IntVal => IntVal): + def apply(t: IntVal): IntVal + +trait F1[-T, +R]: + def apply(t: T): R + +trait SFTU[-T] extends F1[T, Unit]: + def apply(t: T): Unit + +trait SFTI[-T] extends F1[T, Int]: + def apply(t: T): Int + +trait SFTS[-T] extends F1[T, String]: + def apply(t: T): String + +trait SFIR [+R] extends F1[Int, R]: + def apply(t: Int): R + +trait SFSR [+R] extends F1[String, R]: + def apply(t: String): R + +trait SFII extends F1[Int, Int]: + def apply(t: Int): Int + +trait SFSI extends F1[String, Int]: + def apply(t: String): Int + +trait SFIS extends F1[Int, String]: + def apply(t: Int): String + +trait SFIU extends F1[Int, Unit]: + def apply(t: Int): Unit + +trait SFVCVC extends F1[IntVal, IntVal]: + def apply(t: IntVal): IntVal + +trait F1U[-T]: + def apply(t: T): Unit + +trait SF2T[-T] extends F1U[T]: + def apply(t: T): Unit + +trait SF2I extends F1U[Int]: + def apply(t: Int): Unit + +trait SF2S extends F1U[String]: + def apply(t: String): Unit + +case class IntVal(value: Int) extends AnyVal + +object Test: + def main(args: Array[String]): Unit = + val fIU: (Int => Unit) = (x: Int) => println(x) // closure by JFunction1 + fIU(1) + + val fIS: (Int => String) = (x: Int) => x.toString // closure + println(fIS(2)) + + val fUI: (() => Int) = () => 3 // closure + println(fUI()) + + val conITU: ConTU[Int] = (x: Int) => println(x) // expanded + conITU(11) + val conITI: ConTI[Int] = (x: Int) => x // closure + println(conITI(12)) + val conITS: ConTS[Int] = (x: Int) => x.toString // closure + println(conITS(13)) + val conSTS: ConTS[String] = (x: String) => x // closure + println(conSTS("14")) + + val conIRS: ConIR[String] = (x: Int) => x.toString // expanded + println(conIRS(15)) + val conIRI: ConIR[Int] = (x: Int) => x // expanded + println(conIRI(16)) + val conIRU: ConIR[Unit] = (x: Int) => println(x) // expanded + conIRU(17) + + val conSRI: ConSR[Int] = (x: String) => x.toInt // closure + println(conSRI("18")) + val conURI: ConUR[Int] = () => 19 // closure + println(conURI()) + val conURU: ConUR[Unit] = () => println("20") // closure + conURU() + + val conII: ConII = (x: Int) => x // expanded + println(conII(21)) + val conSI: ConSI = (x: String) => x.toInt // closure + println(conSI("22")) + val conIS: ConIS = (x: Int) => x.toString // expanded + println(conIS(23)) + val conUU: ConUU = () => println("24") // expanded + conUU() + val conVCVC: ConVCVC = (x: IntVal) => IntVal(x.value + 1) // expanded + println(conVCVC(IntVal(24)).value) + + val ffIU: F1[Int, Unit] = (x: Int) => println(x) // closure + ffIU(31) + val ffIS: F1[Int, String] = (x: Int) => x.toString // closure + println(ffIS(32)) + val ffSU: F1[String, Unit] = (x: String) => println(x) // closure + ffSU("33") + val ffSI: F1[String, Int] = (x: String) => x.toInt // closure + println(ffSI("34")) + + val sfITU: SFTU[Int] = (x: Int) => println(x) // expanded + sfITU(41) + val sfSTU: SFTU[String] = (x: String) => println(x) // expanded + sfSTU("42") + + val sfITI: SFTI[Int] = (x: Int) => x // closure + println(sfITI(43)) + val sfSTI: SFTI[String] = (x: String) => x.toInt // closure + println(sfSTI("44")) + + val sfITS: SFTS[Int] = (x: Int) => x.toString // closure + println(sfITS(45)) + val sfSTS: SFTS[String] = (x: String) => x // closure + println(sfSTS("46")) + + val sfIRI: SFIR[Int] = (x: Int) => x // expanded + println(sfIRI(51)) + val sfIRS: SFIR[String] = (x: Int) => x.toString // expanded + println(sfIRS(52)) + val sfIRU: SFIR[Unit] = (x: Int) => println(x) // expanded + sfIRU(53) + + val sfSRI: SFSR[Int] = (x: String) => x.toInt // closure + println(sfSRI("55")) + val sfSRS: SFSR[String] = (x: String) => x // closure + println(sfSRS("56")) + val sfSRU: SFSR[Unit] = (x: String) => println(x) // closure + sfSRU("57") + + val sfII: SFII = (x: Int) => x // expanded + println(sfII(61)) + val sfSI: SFSI = (x: String) => x.toInt // closure + println(sfSI("62")) + val sfIS: SFIS = (x: Int) => x.toString // expanded + println(sfIS(63)) + val sfIU: SFIU = (x: Int) => println(x) // expanded + sfIU(64) + val sfVCVC: SFVCVC = (x: IntVal) => IntVal(x.value + 1) // expanded + println(sfVCVC(IntVal(64)).value) + + val f2ITU: F1U[Int] = (x: Int) => println(x) // closure + f2ITU(71) + val f2STU: F1U[String] = (x: String) => println(x) // closure + f2STU("72") + + val sf2IT: SF2T[Int] = (x: Int) => println(x) // closure + sf2IT(75) + val sf2ST: SF2T[String] = (x: String) => println(x) // closure + sf2ST("76") + + val sf2I: SF2I = (x: Int) => println(x) // expanded + sf2I(81) + val sf2S: SF2S = (x: String) => println(x) // closure + sf2S("82") + +end Test diff --git a/tests/run/i25000.scala b/tests/run/i25000.scala new file mode 100644 index 000000000000..36ee4572281e --- /dev/null +++ b/tests/run/i25000.scala @@ -0,0 +1,23 @@ +// Regression test: wildcardArgOK must handle mixed wildcard/concrete type args +// where both arg and formal have TypeBounds in the same position + +trait Format +trait Codec[L, H, F <: Format] // invariant + +trait Variant[O] { + def codec: Codec[?, O, ? <: Format] +} + +object Test { + def decode[L, H](codec: Codec[L, H, ? <: Format]): Int = 1 + def decode[L, H](other: String): Int = 2 + + def test[O](v: Variant[O]): Int = decode(v.codec) + + def main(args: Array[String]): Unit = { + val variant = new Variant[String] { + def codec: Codec[?, String, ? <: Format] = null + } + assert(test(variant) == 1) + } +} diff --git a/tests/run/i25000b.scala b/tests/run/i25000b.scala new file mode 100644 index 000000000000..db84076ae12f --- /dev/null +++ b/tests/run/i25000b.scala @@ -0,0 +1,19 @@ +trait Codec[L, H, F] // invariant + +trait Variant { + def codec: Codec[?, String, ? <: AnyRef] +} + +object Test { + def decode[L](codec: Codec[L, ? >: String, ? <: AnyRef]): Int = 1 + def decode(other: Int): Int = 2 + + def test(v: Variant): Int = decode(v.codec) + + def main(args: Array[String]): Unit = { + val variant = new Variant { + def codec: Codec[?, String, ? <: AnyRef] = null + } + assert(test(variant) == 1) + } +} diff --git a/tests/run/i25291/App_2.scala b/tests/run/i25291/App_2.scala new file mode 100644 index 000000000000..11f8fdb9137b --- /dev/null +++ b/tests/run/i25291/App_2.scala @@ -0,0 +1,3 @@ +package other + +class App extends pkg.Middle diff --git a/tests/run/i25291/Helper_2.scala b/tests/run/i25291/Helper_2.scala new file mode 100644 index 000000000000..f53f541941ee --- /dev/null +++ b/tests/run/i25291/Helper_2.scala @@ -0,0 +1,4 @@ +package pkg + +object Helper: + def callFoo(b: Base): Base = b.foo diff --git a/tests/run/i25291/Lib_1.scala b/tests/run/i25291/Lib_1.scala new file mode 100644 index 000000000000..5ddc2a4b8fb8 --- /dev/null +++ b/tests/run/i25291/Lib_1.scala @@ -0,0 +1,7 @@ +package pkg + +trait Base: + private[pkg] def foo: Base + +trait Middle extends Base: + override private[pkg] def foo: Middle = this diff --git a/tests/run/i25291/Test_2.scala b/tests/run/i25291/Test_2.scala new file mode 100644 index 000000000000..f616e0275027 --- /dev/null +++ b/tests/run/i25291/Test_2.scala @@ -0,0 +1,8 @@ +object Test: + def main(args: Array[String]): Unit = + val app = new other.App + // This would throw AbstractMethodError before the fix + // because the bridge from Base.foo():Base to Middle.foo():Middle was missing + val result = pkg.Helper.callFoo(app) + println(s"result eq app: ${result eq app}") + assert(result eq app, s"expected app instance but got $result") diff --git a/tests/run/i4528.scala b/tests/run/i4528.scala new file mode 100644 index 000000000000..8efcf121b085 --- /dev/null +++ b/tests/run/i4528.scala @@ -0,0 +1,9 @@ +import scala.reflect.Selectable.reflectiveSelectable + +type T = {val a: Int; def a_=(x: Int): Unit} + +@main def Test() = + val x: T = (new { var a = 10 }).asInstanceOf[T] + assert(x.a == 10) + x.a = 11 + assert(x.a == 11) diff --git a/tests/run/inline-numeric/test.scala b/tests/run/inline-numeric/test.scala index 9ca88aee0374..b77e8f3a4266 100644 --- a/tests/run/inline-numeric/test.scala +++ b/tests/run/inline-numeric/test.scala @@ -29,7 +29,7 @@ object tests: def a: Int = 0 def b: Int = 1 - val v1 = foo(a, b) // should be a + b * b // can check with -Xprint:inlining + val v1 = foo(a, b) // should be a + b * b // can check with -Vprint:inlining val v2 = foo(a.toShort, b.toShort) // should be a + b * b val v3 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalAsIfIntegral) // should be BigDecimal(a) quot BigDecimal(b) remainder BigDecimal(b) diff --git a/tests/run/instanceof-serializable.check b/tests/run/instanceof-serializable.check new file mode 100644 index 000000000000..bb101b641b9b --- /dev/null +++ b/tests/run/instanceof-serializable.check @@ -0,0 +1,2 @@ +true +true diff --git a/tests/run/instanceof-serializable.scala b/tests/run/instanceof-serializable.scala new file mode 100644 index 000000000000..a80270b82936 --- /dev/null +++ b/tests/run/instanceof-serializable.scala @@ -0,0 +1,10 @@ +// scalajs: --skip +// (this special case is JVM-only) + +object Foo +def foo: Any = Foo + +object Test: + def main(args: Array[String]): Unit = + println(Foo.isInstanceOf[Serializable]) // should not be constant-folded to false, at runtime it is on the JVM + println(foo.isInstanceOf[Serializable]) diff --git a/tests/run/java-package-private-bridge/JavaBase.java b/tests/run/java-package-private-bridge/JavaBase.java new file mode 100644 index 000000000000..4691e6f7126c --- /dev/null +++ b/tests/run/java-package-private-bridge/JavaBase.java @@ -0,0 +1,7 @@ +package jpkg; + +public class JavaBase { + Object packagePrivate() { + return "base"; + } +} diff --git a/tests/run/java-package-private-bridge/JavaChild.java b/tests/run/java-package-private-bridge/JavaChild.java new file mode 100644 index 000000000000..cad821819c90 --- /dev/null +++ b/tests/run/java-package-private-bridge/JavaChild.java @@ -0,0 +1,8 @@ +package jpkg; + +public class JavaChild extends JavaBase { + @Override + String packagePrivate() { + return "child"; + } +} diff --git a/tests/run/java-package-private-bridge/MyJTable.scala b/tests/run/java-package-private-bridge/MyJTable.scala new file mode 100644 index 000000000000..4729e27135b2 --- /dev/null +++ b/tests/run/java-package-private-bridge/MyJTable.scala @@ -0,0 +1,3 @@ +package other + +class MyJTable extends javax.swing.JTable diff --git a/tests/run/java-package-private-bridge/ScalaChild.scala b/tests/run/java-package-private-bridge/ScalaChild.scala new file mode 100644 index 000000000000..d55eac3165eb --- /dev/null +++ b/tests/run/java-package-private-bridge/ScalaChild.scala @@ -0,0 +1,3 @@ +package other + +class ScalaChild extends jpkg.JavaChild diff --git a/tests/run/java-package-private-bridge/Test.scala b/tests/run/java-package-private-bridge/Test.scala new file mode 100644 index 000000000000..7df6b3a2a639 --- /dev/null +++ b/tests/run/java-package-private-bridge/Test.scala @@ -0,0 +1,20 @@ +// scalajs: --skip + +object Test { + def main(args: Array[String]): Unit = { + val hasBridge1 = classOf[other.ScalaChild].getDeclaredMethods.exists { m => + m.getName == "packagePrivate" && + m.getReturnType == classOf[Object] + } + assert(!hasBridge1, + s"ScalaChild should not have bridge for packagePrivate") + + // JTable case + val hasBridge2 = classOf[other.MyJTable].getDeclaredMethods.exists { m => + m.getName == "dropLocationForPoint" && + m.getReturnType == classOf[javax.swing.TransferHandler.DropLocation] + } + assert(!hasBridge2, + s"MyJTable should not have bridge for dropLocationForPoint") + } +} diff --git a/tests/run/overload_repeated/A_1.java b/tests/run/overload_repeated/A_1.java index 8ef89f1aaf3f..14f75be0dbc2 100644 --- a/tests/run/overload_repeated/A_1.java +++ b/tests/run/overload_repeated/A_1.java @@ -11,13 +11,54 @@ class A_1 { public static int foo4(T x) { return 1; } public static int foo4(T x, T... y) { return 2; } + // https://github.com/scala/scala3/issues/24072 + public static int foo5(Class a) { return 1; } + public static int foo5(Class a, int... ints) { return 2; } + + public static int foo6(Class a, int... ints) { return 1; } + public static int foo6(int a) { return 2; } + + public static int foo7(Class a) { return 1; } + public static int foo7(Class a, int... ints) { return 2; } + + public static int foo8(Class a) { return 1; } // (a) + public static int foo8(Class a, int... ints) { return 2; } // (b) + + public static int foo9(Class a) { return 1; } // (a) + public static int foo9(Class a, int... ints) { return 2; } // (b) + + // https://github.com/scala/scala3/issues/25000 + // Mixed wildcard/concrete type args - tests that wildcardArgOK handles + // non-TypeBounds args correctly + public static int foo10(java.util.Map a) { return 1; } + public static int foo10(java.util.Map a, int... ints) { return 2; } + + public static int foo11(java.util.Map a) { return 1; } + public static int foo11(java.util.Map a, int... ints) { return 2; } + + public static int foo12(java.util.Map a) { return 1; } + public static int foo12(java.util.Map a, int... ints) { return 2; } + public static boolean check() { // Java prefers non-varargs to varargs: // https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2 + + java.util.Map map10 = new java.util.HashMap(); + java.util.Map map11 = new java.util.HashMap(); + java.util.Map map12 = new java.util.HashMap(); + return foo1("") == 1 && foo2("") == 1 && foo3("") == 1 && - foo4("") == 1; + foo4("") == 1 && + foo5(Object.class) == 1 && + foo6(Object.class) == 1 && + foo7(Integer.class) == 1 && + foo8(Integer.class) == 1 && + foo9(Integer.class) == 1 && + foo10(map10) == 1 && + foo11(map11) == 1 && + foo12(map12) == 2; // returns 2 because arg's wildcard has no lower bound, fails lower bound check } } diff --git a/tests/run/overload_repeated/B_2.scala b/tests/run/overload_repeated/B_2.scala index 89452a2ed92d..c80b308a9185 100644 --- a/tests/run/overload_repeated/B_2.scala +++ b/tests/run/overload_repeated/B_2.scala @@ -13,6 +13,34 @@ object Test { def bar4[T](x: T): Int = 1 def bar4[T](x: T, xs: T*): Int = 2 + // https://github.com/scala/scala3/issues/24072 + def bar5[T](a: Class[? <: T]): Int = 1 + def bar5[T](a: Class[T], ints: Int*): Int = 2 + + def bar6[T](a: Class[T], ints: Int*): Int = 1 + def bar6[T](a: Int): Int = 2 + + def bar7[T <: Number](a: Class[? <: T]): Int = 1 + def bar7[T <: Number](a: Class[T], ints: Int*): Int = 2 + + def bar8[T <: Number](a: Class[? <: T]): Int = 1 // (a) + def bar8[T](a: Class[T], ints: Int*): Int = 2 // (b) + + def bar9[T](a: Class[? <: T]): Int = 1 // (a) + def bar9[T <: Number](a: Class[T], ints: Int*): Int = 2 // (b) + + // Mixed wildcard/concrete type args - tests that wildcardArgOK handles + // non-TypeBounds args correctly, using java.util.Map which is invariant + // see: https://github.com/scala/scala3/issues/25000 + def bar10[V](a: java.util.Map[String, ? <: V]): Int = 1 + def bar10[V](a: java.util.Map[String, V], ints: Int*): Int = 2 + + def bar11[V](a: java.util.Map[? >: Int, ? <: V]): Int = 1 + def bar11[V](a: java.util.Map[? >: Number, ? <: V], ints: Int*): Int = 2 + + def bar12[V](a: java.util.Map[Int, ? >: String <: V]): Int = 1 + def bar12[V](a: java.util.Map[Int, V], ints: Int*): Int = 2 + def main(args: Array[String]): Unit = { // In Java, varargs are always less specific than non-varargs (see // https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2), @@ -22,11 +50,27 @@ object Test { assert(A_1.foo2("") == 1) // Same as in Java and Scala 2 assert(A_1.foo3("") == 1) // Same as in Java and Scala 2 assert(A_1.foo4("") == 1) // Same as in Java and Scala 2 + assert(A_1.foo5(classOf[Object]: Class[? <: Object]) == 1) // Same as in Java and Scala 2 + assert(A_1.foo6(classOf[Object]) == 1) // Same as in Java and Scala 2 + assert(A_1.foo7(classOf[Integer]: Class[? <: Integer]) == 1) // Same as in Java and Scala 2 + assert(A_1.foo8(classOf[Integer]) == 1) // Same as in Java and Scala 2 + // assert(A_1.foo9(classOf[Integer]) == 1) // Works in Java, ambiguous in Scala 2 and 3 + assert(A_1.foo10(new java.util.HashMap(): java.util.Map[String, ? <: Object]) == 1) // Same as in Java + assert(A_1.foo11(new java.util.HashMap(): java.util.Map[Integer, ? <: Object]) == 1) // Same as in Java and Scala 2 + assert(A_1.foo12(new java.util.HashMap(): java.util.Map[Integer, ? <: Object]) == 2) // Same as in Java and Scala 2 // Same with Scala varargs: // assert(bar1("") == 1) // Works in Java, ambiguous in Scala 2 and Dotty assert(bar2("") == 1) // same in Scala 2 assert(bar3("") == 1) // same in Scala 2 assert(bar4("") == 1) // same in Scala 2 + assert(bar5(classOf[Object]: Class[? <: Object]) == 1) // same in Scala2 + assert(bar6(classOf[Object]: Class[? <: Object]) == 1) // same in Scala2 + assert(bar7(classOf[Integer]: Class[? <: Integer]) == 1) // same in Scala2 + assert(bar8(classOf[Integer]: Class[? <: Integer]) == 1) // same in Scala2 + // assert(bar9(classOf[Integer]) == 1) Works in Java, ambiguous in Scala 2 and 3 + assert(bar10(new java.util.HashMap(): java.util.Map[String, ? <: Object]) == 1) // same in Scala2 + assert(bar11(new java.util.HashMap(): java.util.Map[Int, ? <: Object]) == 1) // same in Scala2 + assert(bar12(new java.util.HashMap(): java.util.Map[Int, ? <: Object]) == 2) // same in Scala2 } } diff --git a/tests/run/parameterized-type.check b/tests/run/parameterized-type.check new file mode 100644 index 000000000000..46c11ab71304 --- /dev/null +++ b/tests/run/parameterized-type.check @@ -0,0 +1,2 @@ +private scala.Option ClassWithOption.optionInTrait parametrised true +private final scala.Option ClassWithOption.optionInClass parametrised true diff --git a/tests/run/parameterized-type.scala b/tests/run/parameterized-type.scala new file mode 100644 index 000000000000..9e375edfaeda --- /dev/null +++ b/tests/run/parameterized-type.scala @@ -0,0 +1,15 @@ +// scalajs: --skip + +class ClassWithOption extends WithMap { + val optionInClass: Option[String] = None +} +trait WithMap { + val optionInTrait: Option[String] = None +} + +object Test { + def main(args: Array[String]) = + classOf[ClassWithOption].getDeclaredFields.foreach { field => + println(field.toString + " parametrised " + field.getGenericType.isInstanceOf[java.lang.reflect.ParameterizedType]) + } +} diff --git a/tests/run/stable-enum-hashcodes.check b/tests/run/stable-enum-hashcodes.check new file mode 100644 index 000000000000..2a3cb493c4c9 --- /dev/null +++ b/tests/run/stable-enum-hashcodes.check @@ -0,0 +1,12 @@ +65 +65 +66 +66 +67 +67 +68 +68 +-1449359058 +-1449359058 +194551161 +194551161 diff --git a/tests/run/stable-enum-hashcodes.scala b/tests/run/stable-enum-hashcodes.scala new file mode 100644 index 000000000000..60b5af11e437 --- /dev/null +++ b/tests/run/stable-enum-hashcodes.scala @@ -0,0 +1,23 @@ +enum Enum: + case A + case B + case C() + case D() + case E(x: Int) + +@main def Test = + // Enum values (were not stable from run to run before #23218) + println(Enum.A.hashCode) + println(Enum.A.hashCode) + println(Enum.B.hashCode) + println(Enum.B.hashCode) + + // Other enum cases (were already stable from run to run) + println(Enum.C().hashCode) + println(Enum.C().hashCode) + println(Enum.D().hashCode) + println(Enum.D().hashCode) + println(Enum.E(1).hashCode) + println(Enum.E(1).hashCode) + println(Enum.E(2).hashCode) + println(Enum.E(2).hashCode) diff --git a/tests/run/t13033.scala b/tests/run/t13033.scala new file mode 100644 index 000000000000..43f227990011 --- /dev/null +++ b/tests/run/t13033.scala @@ -0,0 +1,69 @@ +// This method will be in the 2.13.17 standard library. Until this test declares a copy of it. +// import scala.util.hashing.MurmurHash3.caseClassHash +def caseClassHash(x: Product, caseClassName: String = null): Int = + import scala.runtime.Statics._ + val arr = x.productArity + val aye = (if (caseClassName != null) caseClassName else x.productPrefix).hashCode + if (arr == 0) aye + else { + var h = 0xcafebabe + h = mix(h, aye) + var i = 0 + while (i < arr) { + h = mix(h, x.productElement(i).##) + i += 1 + } + finalizeHash(h, arr) + } + + +case class C1(a: Int) +class C2(a: Int) extends C1(a) { override def productPrefix = "C2" } +class C3(a: Int) extends C1(a) { override def productPrefix = "C3" } +case class C4(a: Int) { override def productPrefix = "Sea4" } +case class C5() +case object C6 +case object C6b { override def productPrefix = "Sea6b" } +case class C7(s: String) // hashCode forwards to ScalaRunTime._hashCode if there are no primitives +class C8(s: String) extends C7(s) { override def productPrefix = "C8" } + +case class VCC(x: Int) extends AnyVal + +object Test extends App { + val c1 = C1(1) + val c2 = new C2(1) + val c3 = new C3(1) + assert(c1 == c2) + assert(c2 == c1) + assert(c2 == c3) + assert(c1.hashCode == c2.hashCode) + assert(c2.hashCode == c3.hashCode) + + assert(c1.hashCode == caseClassHash(c1)) + // `caseClassHash` mixes in the `productPrefix.hashCode`, while `hashCode` mixes in the case class name statically + assert(c2.hashCode != caseClassHash(c2)) + assert(c2.hashCode == caseClassHash(c2, c1.productPrefix)) + + val c4 = C4(1) + assert(c4.hashCode != caseClassHash(c4)) + assert(c4.hashCode == caseClassHash(c4, "C4")) + + assert((1, 2).hashCode == caseClassHash(1 -> 2)) + assert(("", "").hashCode == caseClassHash("" -> "")) + + assert(C5().hashCode == caseClassHash(C5())) + assert(C6.hashCode == caseClassHash(C6)) + assert(C6b.hashCode == caseClassHash(C6b, "C6b")) + + val c7 = C7("hi") + val c8 = new C8("hi") + assert(c7.hashCode == caseClassHash(c7)) + assert(c7 == c8) + assert(c7.hashCode == c8.hashCode) + assert(c8.hashCode != caseClassHash(c8)) + assert(c8.hashCode == caseClassHash(c8, "C7")) + + + assert(VCC(1).canEqual(VCC(1))) + assert(!VCC(1).canEqual(1)) +} diff --git a/tests/run/t7396.scala b/tests/run/t7396.scala new file mode 100644 index 000000000000..a744e0e53f54 --- /dev/null +++ b/tests/run/t7396.scala @@ -0,0 +1,42 @@ +class L(val x: Int) extends AnyVal +class M(val s: String) extends AnyVal +class N[T](val t: T) extends AnyVal +case class O(s: String) extends AnyVal + +object Test { + def main(args: Array[String]): Unit = { + val l = new L(0) + assert(l.hashCode == 0) + assert(l.toString == "L@0") + + val m = new M(null) + assert(m.hashCode == 0) + assert(m.toString == "M@0") + assert(m == new M(null)) + val mm = new M("") + assert(mm.toString == "M@0") + assert(mm.hashCode == 0) + assert(m != mm) + + val n = new N(new N(new M(null))) + assert(n.hashCode == 0) + assert(n.toString == "N@0") + assert(n != new N(new M(null))) + + val o = O(null) + assert(o.hashCode == 0) + assert(o.toString == "O(null)") + assert(o == O(null)) + val oo = O("") + assert(oo.hashCode == 0) + assert(oo.toString == "O()") + assert(o != oo) + + val map = collection.mutable.HashMap.empty[M, Int] + map(m) = 1 + map(mm) = 2 + assert(map(m) == 1) + assert(map(new M(null)) == 1) + assert(map(new M("")) == 2) + } +} diff --git a/tests/run/typetest.check b/tests/run/typetest.check new file mode 100644 index 000000000000..bb101b641b9b --- /dev/null +++ b/tests/run/typetest.check @@ -0,0 +1,2 @@ +true +true diff --git a/tests/run/typetest.scala b/tests/run/typetest.scala new file mode 100644 index 000000000000..14ba5a5fbe20 --- /dev/null +++ b/tests/run/typetest.scala @@ -0,0 +1,6 @@ +object Test: + def main(args: Array[String]): Unit = + val i: Int = 1 + println(i.isInstanceOf[Object]) + println(1.isInstanceOf[Object]) + diff --git a/tests/run/xml.scala b/tests/run/xml.scala index a1ff9998ec93..ffcb24bdb412 100644 --- a/tests/run/xml.scala +++ b/tests/run/xml.scala @@ -1,12 +1,21 @@ object Test { - import scala.xml.NodeBuffer + import scala.xml._ def main(args: Array[String]): Unit = { val xml = world assert(xml.toString == "helloworld") - val nodeBuffer: NodeBuffer = - assert(nodeBuffer.mkString == "helloworld") + val sq: NodeBuffer = + assert(sq.mkString == "helloworld") + + val subSq: Node = + assert(subSq.child.toString == "Vector(b, c)") // implementation detail + + val attrSeq: Elem = + assert(attrSeq.attributes.asInstanceOf[UnprefixedAttribute].value.toString == "Vector(txt, &entityref;, txt)") + + val g: Group = + assert(g.nodes.toString == "Vector(a, b, c)") } } package scala.xml { @@ -20,7 +29,7 @@ package scala.xml { def child: Seq[Node] override def toString = label + child.mkString } - class Elem(prefix: String, val label: String, attributes1: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, val child: Node*) extends Node + class Elem(prefix: String, val label: String, val attributes: MetaData, scope: NamespaceBinding, minimizeEmpty: Boolean, val child: Node*) extends Node class NodeBuffer extends Seq[Node] { val nodes = scala.collection.mutable.ArrayBuffer.empty[Node] def &+(o: Any): NodeBuffer = @@ -39,4 +48,12 @@ package scala.xml { def label = t.text def child = Nil } + + case class UnprefixedAttribute(key: String, value: Seq[Node], next: MetaData) + case class EntityRef(entityName: String) extends Node { + def label = s"&$entityName;" + def child = Nil + } + + case class Group(nodes: Seq[Node]) } diff --git a/tests/semanticdb/expect/Advanced.expect.scala b/tests/semanticdb/expect/Advanced.expect.scala index d36fcd611eef..137410de5456 100644 --- a/tests/semanticdb/expect/Advanced.expect.scala +++ b/tests/semanticdb/expect/Advanced.expect.scala @@ -25,11 +25,11 @@ class Wildcards/*<-advanced::Wildcards#*/ { object Test/*<-advanced::Test.*/ { val s/*<-advanced::Test.s.*/ = new Structural/*->advanced::Structural#*/ val s1/*<-advanced::Test.s1.*/ = s/*->advanced::Test.s.*/.s1/*->advanced::Structural#s1().*/ - val s1x/*<-advanced::Test.s1x.*/ = s/*->advanced::Test.s.*/.s1/*->advanced::Structural#s1().*/.x/*->scala::reflect::Selectable#selectDynamic().*/ + val s1x/*<-advanced::Test.s1x.*/ = s/*->advanced::Test.s.*/.s1/*->advanced::Structural#s1().*/.x/*->local13*/ val s2/*<-advanced::Test.s2.*/ = s/*->advanced::Test.s.*/.s2/*->advanced::Structural#s2().*/ - val s2x/*<-advanced::Test.s2x.*/ = s/*->advanced::Test.s.*/.s2/*->advanced::Structural#s2().*/.x/*->scala::reflect::Selectable#selectDynamic().*/ + val s2x/*<-advanced::Test.s2x.*/ = s/*->advanced::Test.s.*/.s2/*->advanced::Structural#s2().*/.x/*->local14*/ val s3/*<-advanced::Test.s3.*/ = s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*/ - val s3x/*<-advanced::Test.s3x.*/ = s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*/.m/*->scala::reflect::Selectable#applyDynamic().*/(???/*->scala::Predef.`???`().*/) + val s3x/*<-advanced::Test.s3x.*/ = s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*/.m/*->local15*/(???/*->scala::Predef.`???`().*/) val e/*<-advanced::Test.e.*/ = new Wildcards/*->advanced::Wildcards#*/ val e1/*<-advanced::Test.e1.*/ = e/*->advanced::Test.e.*/.e1/*->advanced::Wildcards#e1().*/ @@ -37,15 +37,15 @@ object Test/*<-advanced::Test.*/ { { (???/*->scala::Predef.`???`().*/ : Any/*->scala::Any#*/) match { - case e3/*<-local13*/: List/*->scala::package.List#*/[_] => - val e3x/*<-local15*/ = e3/*->local13*/.head/*->scala::collection::IterableOps#head().*/ + case e3/*<-local16*/: List/*->scala::package.List#*/[_] => + val e3x/*<-local18*/ = e3/*->local16*/.head/*->scala::collection::IterableOps#head().*/ () } } // see: https://github.com/lampepfl/dotty/pull/14608#discussion_r835642563 - lazy val foo/*<-advanced::Test.foo.*/: (reflect.Selectable/*->scala::reflect::Selectable#*/ { type A/*<-local16*/ = Int/*->scala::Int#*/ }) &/*->scala::`&`#*/ (reflect.Selectable/*->scala::reflect::Selectable#*/ { type A/*<-local17*/ = Int/*->scala::Int#*/; val a/*<-local18*/: A/*->local17*/ }) = ???/*->scala::Predef.`???`().*/ - def bar/*<-advanced::Test.bar().*/: foo/*->advanced::Test.foo.*/.A/*->local17*/ = foo/*->advanced::Test.foo.*/.a/*->scala::reflect::Selectable#selectDynamic().*/ + lazy val foo/*<-advanced::Test.foo.*/: (reflect.Selectable/*->scala::reflect::Selectable#*/ { type A/*<-local19*/ = Int/*->scala::Int#*/ }) &/*->scala::`&`#*/ (reflect.Selectable/*->scala::reflect::Selectable#*/ { type A/*<-local20*/ = Int/*->scala::Int#*/; val a/*<-local21*/: A/*->local20*/ }) = ???/*->scala::Predef.`???`().*/ + def bar/*<-advanced::Test.bar().*/: foo/*->advanced::Test.foo.*/.A/*->local20*/ = foo/*->advanced::Test.foo.*/.a/*->scala::reflect::Selectable#selectDynamic().*/ } diff --git a/tests/semanticdb/expect/InfoMacro.expect.scala b/tests/semanticdb/expect/InfoMacro.expect.scala new file mode 100644 index 000000000000..67122d6cbf37 --- /dev/null +++ b/tests/semanticdb/expect/InfoMacro.expect.scala @@ -0,0 +1,14 @@ +import scala.quoted.* + +object InfoMacro/*<-_empty_::InfoMacro.*/ { + inline def reportInfo/*<-_empty_::InfoMacro.reportInfo().*/(msg/*<-_empty_::InfoMacro.reportInfo().(msg)*/: String/*->scala::Predef.String#*/): Unit/*->scala::Unit#*/ = ${ reportInfoMacro/*->_empty_::InfoMacro.reportInfoMacro().*/('msg) } + + def reportInfoMacro/*<-_empty_::InfoMacro.reportInfoMacro().*/(msg/*<-_empty_::InfoMacro.reportInfoMacro().(msg)*/: Expr/*->scala::quoted::Expr#*/[String/*->scala::Predef.String#*/])(using Quotes/*->scala::quoted::Quotes#*/): Expr/*->scala::quoted::Expr#*/[Unit/*->scala::Unit#*/] = { + import quotes/*->scala::quoted::Quotes$package.quotes().*/.reflect/*->scala::quoted::Quotes#reflect.*/.report/*->scala::quoted::Quotes#reflectModule#report.*/ + + // Report an info diagnostic + report/*->scala::quoted::Quotes#reflectModule#report.*/.info/*->scala::quoted::Quotes#reflectModule#reportModule#info().*/(s/*->scala::StringContext#s().*/"Info from macro: ${msg/*->_empty_::InfoMacro.reportInfoMacro().(msg)*/.valueOrAbort/*->scala::quoted::Quotes#valueOrAbort().*/}") + + '{ () } + } +} diff --git a/tests/semanticdb/expect/InfoMacro.scala b/tests/semanticdb/expect/InfoMacro.scala new file mode 100644 index 000000000000..9248d51dd03a --- /dev/null +++ b/tests/semanticdb/expect/InfoMacro.scala @@ -0,0 +1,14 @@ +import scala.quoted.* + +object InfoMacro { + inline def reportInfo(msg: String): Unit = ${ reportInfoMacro('msg) } + + def reportInfoMacro(msg: Expr[String])(using Quotes): Expr[Unit] = { + import quotes.reflect.report + + // Report an info diagnostic + report.info(s"Info from macro: ${msg.valueOrAbort}") + + '{ () } + } +} diff --git a/tests/semanticdb/expect/InfoMacroTest.expect.scala b/tests/semanticdb/expect/InfoMacroTest.expect.scala new file mode 100644 index 000000000000..19977da0c79f --- /dev/null +++ b/tests/semanticdb/expect/InfoMacroTest.expect.scala @@ -0,0 +1,7 @@ + +object InfoMacroTest/*<-_empty_::InfoMacroTest.*/ { + def main/*<-_empty_::InfoMacroTest.main().*/(): Unit/*->scala::Unit#*/ = { + InfoMacro/*->_empty_::InfoMacro.*/.reportInfo/*->_empty_::InfoMacro.reportInfo().*/("This is a test info message") + InfoMacro/*->_empty_::InfoMacro.*/.reportInfo/*->_empty_::InfoMacro.reportInfo().*/("Another info message") + } +} diff --git a/tests/semanticdb/expect/InfoMacroTest.scala b/tests/semanticdb/expect/InfoMacroTest.scala new file mode 100644 index 000000000000..e53a840efc8a --- /dev/null +++ b/tests/semanticdb/expect/InfoMacroTest.scala @@ -0,0 +1,7 @@ + +object InfoMacroTest { + def main(): Unit = { + InfoMacro.reportInfo("This is a test info message") + InfoMacro.reportInfo("Another info message") + } +} diff --git a/tests/semanticdb/expect/JavaStaticVar.expect.scala b/tests/semanticdb/expect/JavaStaticVar.expect.scala new file mode 100644 index 000000000000..171596bc9519 --- /dev/null +++ b/tests/semanticdb/expect/JavaStaticVar.expect.scala @@ -0,0 +1,7 @@ +package example + +import com.javacp.JavaStaticVar/*->com::javacp::JavaStaticVar#*/ + +class ScalaFoo/*<-example::ScalaFoo#*/ { + val javaStaticVarFoo/*<-example::ScalaFoo#javaStaticVarFoo.*/ = JavaStaticVar/*->com::javacp::JavaStaticVar#*/.foo/*->com::javacp::JavaStaticVar#foo.*/ +} diff --git a/tests/semanticdb/expect/JavaStaticVar.scala b/tests/semanticdb/expect/JavaStaticVar.scala new file mode 100644 index 000000000000..7a7efeaa670a --- /dev/null +++ b/tests/semanticdb/expect/JavaStaticVar.scala @@ -0,0 +1,7 @@ +package example + +import com.javacp.JavaStaticVar + +class ScalaFoo { + val javaStaticVarFoo = JavaStaticVar.foo +} diff --git a/tests/semanticdb/expect/RecOrRefined.expect.scala b/tests/semanticdb/expect/RecOrRefined.expect.scala index 3c32d22506ef..8b6feab3b7f1 100644 --- a/tests/semanticdb/expect/RecOrRefined.expect.scala +++ b/tests/semanticdb/expect/RecOrRefined.expect.scala @@ -1,34 +1,34 @@ package example -def m1/*<-example::RecOrRefined$package.m1().*/(a/*<-example::RecOrRefined$package.m1().(a)*/: Int/*->scala::Int#*/ { val x/*<-local4*/: Int/*->scala::Int#*/ }) = ???/*->scala::Predef.`???`().*/ -def m2/*<-example::RecOrRefined$package.m2().*/(x/*<-example::RecOrRefined$package.m2().(x)*/: { val x/*<-local5*/: Int/*->scala::Int#*/; def y/*<-local6*/: Int/*->scala::Int#*/ }) = ???/*->scala::Predef.`???`().*/ -def m3/*<-example::RecOrRefined$package.m3().*/(x/*<-example::RecOrRefined$package.m3().(x)*/: { val x/*<-local7*/: Int/*->scala::Int#*/; def y/*<-local8*/: Int/*->scala::Int#*/; type z/*<-local9*/ }) = ???/*->scala::Predef.`???`().*/ +def m1/*<-example::RecOrRefined$package.m1().*/(a/*<-example::RecOrRefined$package.m1().(a)*/: Int/*->scala::Int#*/ { val x/*<-local1*/: Int/*->scala::Int#*/ }) = ???/*->scala::Predef.`???`().*/ +def m2/*<-example::RecOrRefined$package.m2().*/(x/*<-example::RecOrRefined$package.m2().(x)*/: { val x/*<-local2*/: Int/*->scala::Int#*/; def y/*<-local3*/: Int/*->scala::Int#*/ }) = ???/*->scala::Predef.`???`().*/ +def m3/*<-example::RecOrRefined$package.m3().*/(x/*<-example::RecOrRefined$package.m3().(x)*/: { val x/*<-local4*/: Int/*->scala::Int#*/; def y/*<-local5*/: Int/*->scala::Int#*/; type z/*<-local6*/ }) = ???/*->scala::Predef.`???`().*/ trait PolyHolder/*<-example::PolyHolder#*/ { def foo/*<-example::PolyHolder#foo().*/[T/*<-example::PolyHolder#foo().[T]*/](t/*<-example::PolyHolder#foo().(t)*/: T/*->example::PolyHolder#foo().[T]*/): Any/*->scala::Any#*/ } -def m4/*<-example::RecOrRefined$package.m4().*/(x/*<-example::RecOrRefined$package.m4().(x)*/: PolyHolder/*->example::PolyHolder#*/ { def foo/*<-local12*/[T/*<-local10*/](t/*<-local11*/: T/*->local10*/): T/*->local10*/ }) = ???/*->scala::Predef.`???`().*/ -def m5/*<-example::RecOrRefined$package.m5().*/[Z/*<-example::RecOrRefined$package.m5().[Z]*/](x/*<-example::RecOrRefined$package.m5().(x)*/: Int/*->scala::Int#*/): PolyHolder/*->example::PolyHolder#*/ { def foo/*<-local15*/[T/*<-local13*/](t/*<-local14*/: T/*->local13*/): T/*->local13*/ } = ???/*->scala::Predef.`???`().*/ +def m4/*<-example::RecOrRefined$package.m4().*/(x/*<-example::RecOrRefined$package.m4().(x)*/: PolyHolder/*->example::PolyHolder#*/ { def foo/*<-local9*/[T/*<-local7*/](t/*<-local8*/: T/*->local7*/): T/*->local7*/ }) = ???/*->scala::Predef.`???`().*/ +def m5/*<-example::RecOrRefined$package.m5().*/[Z/*<-example::RecOrRefined$package.m5().[Z]*/](x/*<-example::RecOrRefined$package.m5().(x)*/: Int/*->scala::Int#*/): PolyHolder/*->example::PolyHolder#*/ { def foo/*<-local12*/[T/*<-local10*/](t/*<-local11*/: T/*->local10*/): T/*->local10*/ } = ???/*->scala::Predef.`???`().*/ -type m6/*<-example::RecOrRefined$package.m6#*/ = [X/*<-example::RecOrRefined$package.m6#[X]*/] =>> PolyHolder/*->example::PolyHolder#*/ { def foo/*<-local18*/[T/*<-local16*/](t/*<-local17*/: T/*->local16*/): T/*->local16*/ } +type m6/*<-example::RecOrRefined$package.m6#*/ = [X/*<-example::RecOrRefined$package.m6#[X]*/] =>> PolyHolder/*->example::PolyHolder#*/ { def foo/*<-example::RecOrRefined$package.m6#foo().*/[T/*<-example::RecOrRefined$package.m6#foo().[T]*/](t/*<-example::RecOrRefined$package.m6#foo().(t)*/: T/*->example::RecOrRefined$package.m6#foo().[T]*/): T/*->example::RecOrRefined$package.m6#foo().[T]*/ } class Record/*<-example::Record#*/(elems/*<-example::Record#elems.*/: (String/*->scala::Predef.String#*/, Any/*->scala::Any#*/)*) extends Selectable/*->scala::Selectable#*/: private val fields/*<-example::Record#fields.*/ = elems/*->example::Record#elems.*/.toMap/*->scala::collection::IterableOnceOps#toMap().*/ def selectDynamic/*<-example::Record#selectDynamic().*/(name/*<-example::Record#selectDynamic().(name)*/: String/*->scala::Predef.String#*/): Any/*->scala::Any#*/ = fields/*->example::Record#fields.*/(name/*->example::Record#selectDynamic().(name)*/) type Person/*<-example::RecOrRefined$package.Person#*/ = Record/*->example::Record#*/ { - val name/*<-local19*/: String/*->scala::Predef.String#*/ - val age/*<-local20*/: Int/*->scala::Int#*/ + val name/*<-example::RecOrRefined$package.Person#name.*/: String/*->scala::Predef.String#*/ + val age/*<-example::RecOrRefined$package.Person#age.*/: Int/*->scala::Int#*/ } // RecType class C/*<-example::C#*/ { type T1/*<-example::C#T1#*/; type T2/*<-example::C#T2#*/ } -type C2/*<-example::RecOrRefined$package.C2#*/ = C/*->example::C#*/ { type T1/*<-local21*/; type T2/*<-local22*/ = T1/*->local21*/ } +type C2/*<-example::RecOrRefined$package.C2#*/ = C/*->example::C#*/ { type T1/*<-example::RecOrRefined$package.C2#T1#*/; type T2/*<-example::RecOrRefined$package.C2#T2#*/ = T1/*->example::RecOrRefined$package.C2#T1#*/ } trait SpecialRefinement/*<-example::SpecialRefinement#*/ { def pickOne/*<-example::SpecialRefinement#pickOne().*/[T/*<-example::SpecialRefinement#pickOne().[T]*/](as/*<-example::SpecialRefinement#pickOne().(as)*/: T/*->example::SpecialRefinement#pickOne().[T]*/*): Option/*->scala::Option#*/[Any/*->scala::Any#*/] } -class PickOneRefinement_1/*<-example::PickOneRefinement_1#*/[S/*<-example::PickOneRefinement_1#[S]*/ <: SpecialRefinement/*->example::SpecialRefinement#*/ { def pickOne/*<-local3*/[T/*<-local1*/](as/*<-local2*/: T/*->local1*/*): Option/*->scala::Option#*/[String/*->scala::Predef.String#*/] }] { +class PickOneRefinement_1/*<-example::PickOneRefinement_1#*/[S/*<-example::PickOneRefinement_1#[S]*/ <: SpecialRefinement/*->example::SpecialRefinement#*/ { def pickOne/*<-example::PickOneRefinement_1#``().[S]pickOne().*/[T/*<-example::PickOneRefinement_1#``().[S]pickOne().[T]*/](as/*<-example::PickOneRefinement_1#``().[S]pickOne().(as)*/: T/*->example::PickOneRefinement_1#``().[S]pickOne().[T]*/*): Option/*->scala::Option#*/[String/*->scala::Predef.String#*/] }] { def run/*<-example::PickOneRefinement_1#run().*/(s/*<-example::PickOneRefinement_1#run().(s)*/: S/*->example::PickOneRefinement_1#[S]*/, as/*<-example::PickOneRefinement_1#run().(as)*/: String/*->scala::Predef.String#*/*): Option/*->scala::Option#*/[String/*->scala::Predef.String#*/] = s/*->example::PickOneRefinement_1#run().(s)*/.pickOne/*->example::SpecialRefinement#pickOne().*/(as/*->example::PickOneRefinement_1#run().(as)*/:_*) } diff --git a/tests/semanticdb/expect/ShadowedParameters.expect.scala b/tests/semanticdb/expect/ShadowedParameters.expect.scala new file mode 100644 index 000000000000..10cc77d08c44 --- /dev/null +++ b/tests/semanticdb/expect/ShadowedParameters.expect.scala @@ -0,0 +1,27 @@ +package example + +class Shadow/*<-example::Shadow#*/{ + + def shadowParam/*<-example::Shadow#shadowParam().*/(x/*<-example::Shadow#shadowParam().(x)*/: Int/*->scala::Int#*/)= { + val x/*<-local0*/ = true + } + + def curriedParams/*<-example::Shadow#curriedParams().*/(x/*<-example::Shadow#curriedParams().(x)*/: Int/*->scala::Int#*/)(y/*<-example::Shadow#curriedParams().(y)*/: List/*->scala::package.List#*/[Int/*->scala::Int#*/]) = { + val x/*<-local1*/ = "shadow" + val y/*<-local2*/ = 1 + } + + + def multiParams/*<-example::Shadow#multiParams().*/(x/*<-example::Shadow#multiParams().(x)*/: List/*->scala::package.List#*/[Int/*->scala::Int#*/], y/*<-example::Shadow#multiParams().(y)*/: String/*->scala::Predef.String#*/) = { + val x/*<-local3*/ = 1 + val y/*<-local4*/ = List/*->scala::package.List.*/(1,2,3) + } + + + def shadowInParamBlock/*<-example::Shadow#shadowInParamBlock().*/(x/*<-example::Shadow#shadowInParamBlock().(x)*/: Int/*->scala::Int#*/, y/*<-example::Shadow#shadowInParamBlock().(y)*/: Int/*->scala::Int#*/) = { + val y/*<-local5*/ = 1 + { + val x/*<-local6*/ = 2 + } + } +} \ No newline at end of file diff --git a/tests/semanticdb/expect/ShadowedParameters.scala b/tests/semanticdb/expect/ShadowedParameters.scala new file mode 100644 index 000000000000..3b191e1b1c3f --- /dev/null +++ b/tests/semanticdb/expect/ShadowedParameters.scala @@ -0,0 +1,27 @@ +package example + +class Shadow{ + + def shadowParam(x: Int)= { + val x = true + } + + def curriedParams(x: Int)(y: List[Int]) = { + val x = "shadow" + val y = 1 + } + + + def multiParams(x: List[Int], y: String) = { + val x = 1 + val y = List(1,2,3) + } + + + def shadowInParamBlock(x: Int, y: Int) = { + val y = 1 + { + val x = 2 + } + } +} \ No newline at end of file diff --git a/tests/semanticdb/expect/StructuralTypes.expect.scala b/tests/semanticdb/expect/StructuralTypes.expect.scala index 98aaca95f880..d66d513266c0 100644 --- a/tests/semanticdb/expect/StructuralTypes.expect.scala +++ b/tests/semanticdb/expect/StructuralTypes.expect.scala @@ -4,19 +4,21 @@ import reflect.Selectable/*->scala::reflect::Selectable.*/.reflectiveSelectable/ object StructuralTypes/*<-example::StructuralTypes.*/: type User/*<-example::StructuralTypes.User#*/ = { - def name/*<-local0*/: String/*->scala::Predef.String#*/ - def age/*<-local1*/: Int/*->scala::Int#*/ - def foo/*<-local3*/(x/*<-local2*/: Int/*->scala::Int#*/): Int/*->scala::Int#*/ + def name/*<-example::StructuralTypes.User#name().*/: String/*->scala::Predef.String#*/ + def age/*<-example::StructuralTypes.User#age().*/: Int/*->scala::Int#*/ } - val user/*<-example::StructuralTypes.user.*/ = null.asInstanceOf/*->scala::Any#asInstanceOf().*/[User/*->example::StructuralTypes.User#*/] - user/*->example::StructuralTypes.user.*/.name/*->scala::reflect::Selectable#selectDynamic().*/ - user/*->example::StructuralTypes.user.*/.age/*->scala::reflect::Selectable#selectDynamic().*/ - val fooBar/*<-example::StructuralTypes.fooBar.*/ = user/*->example::StructuralTypes.user.*/ foo/*->scala::reflect::Selectable#applyDynamic().*/ 123 + type FooUser/*<-example::StructuralTypes.FooUser#*/ = User/*->example::StructuralTypes.User#*/ { + def foo/*<-example::StructuralTypes.FooUser#foo().*/(x/*<-example::StructuralTypes.FooUser#foo().(x)*/: Int/*->scala::Int#*/): Int/*->scala::Int#*/ + } + val user/*<-example::StructuralTypes.user.*/ = null.asInstanceOf/*->scala::Any#asInstanceOf().*/[FooUser/*->example::StructuralTypes.FooUser#*/] + user/*->example::StructuralTypes.user.*/.name/*->example::StructuralTypes.User#name().*/ + user/*->example::StructuralTypes.user.*/.age/*->example::StructuralTypes.User#age().*/ + val fooBar/*<-example::StructuralTypes.fooBar.*/ = user/*->example::StructuralTypes.user.*/ foo/*->example::StructuralTypes.FooUser#foo().*/ 123 val V/*<-example::StructuralTypes.V.*/: Object/*->java::lang::Object#*/ { - def scalameta/*<-local4*/: String/*->scala::Predef.String#*/ - } = new: - /*<-local6*/def scalameta/*<-local5*/ = "4.0" - V/*->example::StructuralTypes.V.*/.scalameta/*->scala::reflect::Selectable#selectDynamic().*/ + def scalameta/*<-local0*/: String/*->scala::Predef.String#*/ + } = /*<-local2*/new: + def scalameta/*<-local1*/ = "4.0" + V/*->example::StructuralTypes.V.*/.scalameta/*->local4*/ end StructuralTypes/*->example::StructuralTypes.*/ \ No newline at end of file diff --git a/tests/semanticdb/expect/StructuralTypes.scala b/tests/semanticdb/expect/StructuralTypes.scala index 5d10dbe67224..782aa152cb90 100644 --- a/tests/semanticdb/expect/StructuralTypes.scala +++ b/tests/semanticdb/expect/StructuralTypes.scala @@ -6,14 +6,16 @@ object StructuralTypes: type User = { def name: String def age: Int + } + + type FooUser = User { def foo(x: Int): Int } - val user = null.asInstanceOf[User] + val user = null.asInstanceOf[FooUser] user.name user.age val fooBar = user foo 123 - val V: Object { def scalameta: String } = new: diff --git a/tests/semanticdb/expect/StructuralTypesParams.expect.scala b/tests/semanticdb/expect/StructuralTypesParams.expect.scala new file mode 100644 index 000000000000..b0345aacaf4d --- /dev/null +++ b/tests/semanticdb/expect/StructuralTypesParams.expect.scala @@ -0,0 +1,18 @@ +package example + +import reflect.Selectable/*->scala::reflect::Selectable.*/.reflectiveSelectable/*->scala::reflect::Selectable.reflectiveSelectable().*/ + +object StructuralTypesParams/*<-example::StructuralTypesParams.*/: + type User/*<-example::StructuralTypesParams.User#*/ = { + def foo/*<-example::StructuralTypesParams.User#foo().*/(arg1/*<-example::StructuralTypesParams.User#foo().(arg1)*/: Int/*->scala::Int#*/, arg2/*<-example::StructuralTypesParams.User#foo().(arg2)*/: String/*->scala::Predef.String#*/): String/*->scala::Predef.String#*/ + def age/*<-example::StructuralTypesParams.User#age().*/: Int/*->scala::Int#*/ + + } + val user/*<-example::StructuralTypesParams.user.*/ = null.asInstanceOf/*->scala::Any#asInstanceOf().*/[User/*->example::StructuralTypesParams.User#*/] + val num/*<-example::StructuralTypesParams.num.*/ = 123 + val str/*<-example::StructuralTypesParams.str.*/ = "abc" + val fooBar/*<-example::StructuralTypesParams.fooBar.*/ = user/*->example::StructuralTypesParams.user.*/.foo/*->example::StructuralTypesParams.User#foo().*/(num/*->example::StructuralTypesParams.num.*/, str/*->example::StructuralTypesParams.str.*/) + val fooBaz/*<-example::StructuralTypesParams.fooBaz.*/ = user/*->example::StructuralTypesParams.user.*/.foo/*->example::StructuralTypesParams.User#foo().*/(arg1 = num/*->example::StructuralTypesParams.num.*/, arg2 = str/*->example::StructuralTypesParams.str.*/) + val age/*<-example::StructuralTypesParams.age.*/ = user/*->example::StructuralTypesParams.user.*/.age/*->example::StructuralTypesParams.User#age().*/ + +end StructuralTypesParams/*->example::StructuralTypesParams.*/ \ No newline at end of file diff --git a/tests/semanticdb/expect/StructuralTypesParams.scala b/tests/semanticdb/expect/StructuralTypesParams.scala new file mode 100644 index 000000000000..6b8c8005533c --- /dev/null +++ b/tests/semanticdb/expect/StructuralTypesParams.scala @@ -0,0 +1,18 @@ +package example + +import reflect.Selectable.reflectiveSelectable + +object StructuralTypesParams: + type User = { + def foo(arg1: Int, arg2: String): String + def age: Int + + } + val user = null.asInstanceOf[User] + val num = 123 + val str = "abc" + val fooBar = user.foo(num, str) + val fooBaz = user.foo(arg1 = num, arg2 = str) + val age = user.age + +end StructuralTypesParams \ No newline at end of file diff --git a/tests/semanticdb/expect/dep-match.expect.scala b/tests/semanticdb/expect/dep-match.expect.scala index 4a4a953ee04d..f31f4af2b685 100644 --- a/tests/semanticdb/expect/dep-match.expect.scala +++ b/tests/semanticdb/expect/dep-match.expect.scala @@ -1,9 +1,9 @@ object Test_depmatch/*<-_empty_::Test_depmatch.*/ { - type Foo/*<-_empty_::Test_depmatch.Foo#*/ = Int/*->scala::Int#*/ { type U/*<-local0*/ } + type Foo/*<-_empty_::Test_depmatch.Foo#*/ = Int/*->scala::Int#*/ { type U/*<-_empty_::Test_depmatch.Foo#U#*/ } type Bar/*<-_empty_::Test_depmatch.Bar#*/[T/*<-_empty_::Test_depmatch.Bar#[T]*/] = T/*->_empty_::Test_depmatch.Bar#[T]*/ match { case Unit/*->scala::Unit#*/ => Unit/*->scala::Unit#*/ } inline def baz/*<-_empty_::Test_depmatch.baz().*/(foo/*<-_empty_::Test_depmatch.baz().(foo)*/: Foo/*->_empty_::Test_depmatch.Foo#*/): Unit/*->scala::Unit#*/ = { - val v/*<-local1*/: Bar/*->_empty_::Test_depmatch.Bar#*/[foo/*->_empty_::Test_depmatch.baz().(foo)*/.U/*->local0*/] = ???/*->scala::Predef.`???`().*/ + val v/*<-local0*/: Bar/*->_empty_::Test_depmatch.Bar#*/[foo/*->_empty_::Test_depmatch.baz().(foo)*/.U/*->_empty_::Test_depmatch.Foo#U#*/] = ???/*->scala::Predef.`???`().*/ } } diff --git a/tests/semanticdb/javacp/com/javacp/JavaStaticVar.java b/tests/semanticdb/javacp/com/javacp/JavaStaticVar.java new file mode 100644 index 000000000000..1124c8768421 --- /dev/null +++ b/tests/semanticdb/javacp/com/javacp/JavaStaticVar.java @@ -0,0 +1,5 @@ +package com.javacp; + +public class JavaStaticVar { + public static int foo = 0; +} diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 7153edac00ab..ed3e3c73e505 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -8,7 +8,6 @@ Text => empty Language => Scala Symbols => 9 entries Occurrences => 19 entries -Diagnostics => 2 entries Symbols: example/Access# => class Access extends Object { self: Access => +8 decls } @@ -42,10 +41,6 @@ Occurrences: [9:6..9:8): m7 <- example/Access#m7(). [9:11..9:14): ??? -> scala/Predef.`???`(). -Diagnostics: -[3:14..3:16): [warning] unused private member -[4:20..4:22): [warning] unused private member - expect/Advanced.scala --------------------- @@ -116,11 +111,11 @@ local8 => method m (param x: Int): Int local9 => final class $anon extends Object { self: $anon => +2 decls } local11 => abstract val method x Int local12 => type B = A -local13 => val local e3: List[local14] -local15 => val local e3x: local14 -local16 => type A = Int -local17 => type A = Int -local18 => abstract val method a A +local16 => val local e3: List[local17] +local18 => val local e3x: local17 +local19 => type A = Int +local20 => type A = Int +local21 => abstract val method a A Occurrences: [0:8..0:16): advanced <- advanced/ @@ -199,21 +194,21 @@ Occurrences: [27:6..27:9): s1x <- advanced/Test.s1x. [27:12..27:13): s -> advanced/Test.s. [27:14..27:16): s1 -> advanced/Structural#s1(). -[27:17..27:18): x -> scala/reflect/Selectable#selectDynamic(). +[27:17..27:18): x -> local13 [28:6..28:8): s2 <- advanced/Test.s2. [28:11..28:12): s -> advanced/Test.s. [28:13..28:15): s2 -> advanced/Structural#s2(). [29:6..29:9): s2x <- advanced/Test.s2x. [29:12..29:13): s -> advanced/Test.s. [29:14..29:16): s2 -> advanced/Structural#s2(). -[29:17..29:18): x -> scala/reflect/Selectable#selectDynamic(). +[29:17..29:18): x -> local14 [30:6..30:8): s3 <- advanced/Test.s3. [30:11..30:12): s -> advanced/Test.s. [30:13..30:15): s3 -> advanced/Structural#s3(). [31:6..31:9): s3x <- advanced/Test.s3x. [31:12..31:13): s -> advanced/Test.s. [31:14..31:16): s3 -> advanced/Structural#s3(). -[31:17..31:18): m -> scala/reflect/Selectable#applyDynamic(). +[31:17..31:18): m -> local15 [31:19..31:22): ??? -> scala/Predef.`???`(). [33:6..33:7): e <- advanced/Test.e. [33:14..33:23): Wildcards -> advanced/Wildcards# @@ -226,27 +221,27 @@ Occurrences: [35:17..35:21): head -> scala/collection/IterableOps#head(). [38:5..38:8): ??? -> scala/Predef.`???`(). [38:11..38:14): Any -> scala/Any# -[39:11..39:13): e3 <- local13 +[39:11..39:13): e3 <- local16 [39:15..39:19): List -> scala/package.List# -[40:12..40:15): e3x <- local15 -[40:18..40:20): e3 -> local13 +[40:12..40:15): e3x <- local18 +[40:18..40:20): e3 -> local16 [40:21..40:25): head -> scala/collection/IterableOps#head(). [46:11..46:14): foo <- advanced/Test.foo. [46:17..46:24): reflect -> scala/reflect/ [46:25..46:35): Selectable -> scala/reflect/Selectable# -[46:43..46:44): A <- local16 +[46:43..46:44): A <- local19 [46:47..46:50): Int -> scala/Int# [46:54..46:55): & -> scala/`&`# [46:57..46:64): reflect -> scala/reflect/ [46:65..46:75): Selectable -> scala/reflect/Selectable# -[46:83..46:84): A <- local17 +[46:83..46:84): A <- local20 [46:87..46:90): Int -> scala/Int# -[46:96..46:97): a <- local18 -[46:99..46:100): A -> local17 +[46:96..46:97): a <- local21 +[46:99..46:100): A -> local20 [46:106..46:109): ??? -> scala/Predef.`???`(). [47:6..47:9): bar <- advanced/Test.bar(). [47:11..47:14): foo -> advanced/Test.foo. -[47:15..47:16): A -> local17 +[47:15..47:16): A -> local20 [47:19..47:22): foo -> advanced/Test.foo. [47:23..47:24): a -> scala/reflect/Selectable#selectDynamic(). [52:6..52:13): HKClass <- advanced/HKClass# @@ -505,6 +500,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 } @@ -549,6 +545,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 -------------------- @@ -559,8 +559,8 @@ Text => empty Language => Scala Symbols => 108 entries Occurrences => 127 entries -Diagnostics => 4 entries -Synthetics => 2 entries +Diagnostics => 9 entries +Synthetics => 3 entries Symbols: classes/C1# => final class C1 extends AnyVal { self: C1 => +2 decls } @@ -802,14 +802,20 @@ Occurrences: [53:10..53:11): + -> scala/Int#`+`(+4). Diagnostics: +[13:20..13:21): [warning] unused explicit parameter [18:9..18:10): [warning] unused explicit parameter [20:27..20:28): [warning] unused explicit parameter [22:27..22:28): [warning] unused explicit parameter [24:10..24:11): [warning] unused explicit parameter +[36:11..36:12): [warning] unused explicit parameter +[39:11..39:12): [warning] unused explicit parameter +[39:19..39:20): [warning] unused explicit parameter +[51:30..51:31): [warning] unused explicit parameter 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 ----------------------- @@ -899,6 +905,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 } @@ -988,6 +995,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 ------------------------ @@ -1070,7 +1081,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 } @@ -1420,11 +1431,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 @@ -1437,7 +1451,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 } @@ -1458,9 +1472,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 -------------------- @@ -1473,6 +1489,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 } @@ -1509,6 +1526,9 @@ Occurrences: Diagnostics: [2:24..2:30): [warning] unused import +Synthetics: +[9:37..9:37): => ClassTag.apply[Int](*) + expect/Extension.scala ---------------------- @@ -1519,7 +1539,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 } @@ -1624,6 +1644,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 @@ -1636,7 +1657,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 } @@ -1710,11 +1731,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 ------------------- @@ -1726,7 +1786,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 } @@ -1838,6 +1898,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] @@ -1852,7 +1915,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 } @@ -1934,12 +1997,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(*) @@ -1980,6 +2048,90 @@ Occurrences: Diagnostics: [0:26..0:34): [warning] unused import +expect/InfoMacro.scala +---------------------- + +Summary: +Schema => SemanticDB v4 +Uri => InfoMacro.scala +Text => empty +Language => Scala +Symbols => 7 entries +Occurrences => 23 entries +Synthetics => 7 entries + +Symbols: +_empty_/InfoMacro. => final object InfoMacro extends Object { self: InfoMacro.type => +3 decls } +_empty_/InfoMacro.reportInfo(). => inline macro reportInfo (param msg: String): Unit +_empty_/InfoMacro.reportInfo().(msg) => param msg: String +_empty_/InfoMacro.reportInfoMacro(). => method reportInfoMacro (param msg: Expr[String])(implicit given param x$2: Quotes): Expr[Unit] +_empty_/InfoMacro.reportInfoMacro().(msg) => param msg: Expr[String] +_empty_/InfoMacro.reportInfoMacro().(x$2) => implicit given param x$2: Quotes +local0 => implicit given param evidence$1: Quotes + +Occurrences: +[0:7..0:12): scala -> scala/ +[0:13..0:19): quoted -> scala/quoted/ +[2:7..2:16): InfoMacro <- _empty_/InfoMacro. +[3:13..3:23): reportInfo <- _empty_/InfoMacro.reportInfo(). +[3:24..3:27): msg <- _empty_/InfoMacro.reportInfo().(msg) +[3:29..3:35): String -> scala/Predef.String# +[3:38..3:42): Unit -> scala/Unit# +[3:48..3:63): reportInfoMacro -> _empty_/InfoMacro.reportInfoMacro(). +[5:6..5:21): reportInfoMacro <- _empty_/InfoMacro.reportInfoMacro(). +[5:22..5:25): msg <- _empty_/InfoMacro.reportInfoMacro().(msg) +[5:27..5:31): Expr -> scala/quoted/Expr# +[5:32..5:38): String -> scala/Predef.String# +[5:47..5:53): Quotes -> scala/quoted/Quotes# +[5:56..5:60): Expr -> scala/quoted/Expr# +[5:61..5:65): Unit -> scala/Unit# +[6:11..6:17): quotes -> scala/quoted/Quotes$package.quotes(). +[6:18..6:25): reflect -> scala/quoted/Quotes#reflect. +[6:26..6:32): report -> scala/quoted/Quotes#reflectModule#report. +[9:4..9:10): report -> scala/quoted/Quotes#reflectModule#report. +[9:11..9:15): info -> scala/quoted/Quotes#reflectModule#reportModule#info(). +[9:16..9:17): s -> scala/StringContext#s(). +[9:37..9:40): msg -> _empty_/InfoMacro.reportInfoMacro().(msg) +[9:41..9:53): valueOrAbort -> scala/quoted/Quotes#valueOrAbort(). + +Synthetics: +[3:48..3:69):reportInfoMacro('msg) => *(evidence$1) +[3:64..3:68):'msg => orig()(evidence$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) + +expect/InfoMacroTest.scala +-------------------------- + +Summary: +Schema => SemanticDB v4 +Uri => InfoMacroTest.scala +Text => empty +Language => Scala +Symbols => 2 entries +Occurrences => 7 entries +Diagnostics => 2 entries + +Symbols: +_empty_/InfoMacroTest. => final object InfoMacroTest extends Object { self: InfoMacroTest.type => +2 decls } +_empty_/InfoMacroTest.main(). => method main (): Unit + +Occurrences: +[1:7..1:20): InfoMacroTest <- _empty_/InfoMacroTest. +[2:6..2:10): main <- _empty_/InfoMacroTest.main(). +[2:14..2:18): Unit -> scala/Unit# +[3:4..3:13): InfoMacro -> _empty_/InfoMacro. +[3:14..3:24): reportInfo -> _empty_/InfoMacro.reportInfo(). +[4:4..4:13): InfoMacro -> _empty_/InfoMacro. +[4:14..4:24): reportInfo -> _empty_/InfoMacro.reportInfo(). + +Diagnostics: +[3:4..3:55): [info] Info from macro: This is a test info message +[4:4..4:48): [info] Info from macro: Another info message + expect/InstrumentTyper.scala ---------------------------- @@ -1990,13 +2142,13 @@ 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 } example/InstrumentTyper#AnnotatedType# => type AnnotatedType = Int @param example/InstrumentTyper#``(). => primary ctor (): InstrumentTyper -example/InstrumentTyper#all(). => method all => List[Float | Double | List[Nothing] | Boolean | Unit | Char | String | LinkOption | Int | Long | Class[Option[Int]]] +example/InstrumentTyper#all(). => method all => List[Int | Long | Float | Double | List[Nothing] | Char | String | Boolean | Unit | LinkOption | Class[Option[Int]]] example/InstrumentTyper#clazzOf. => final val method clazzOf Option[Int] example/InstrumentTyper#singletonType(). => method singletonType (param x: Predef.type): Nothing example/InstrumentTyper#singletonType().(x) => param x: Predef.type @@ -2058,8 +2210,23 @@ Occurrences: [24:37..24:40): Int -> scala/Int# Synthetics: -[8:12..8:16):List => *.apply[Float | Double | List[Nothing] | Boolean | Unit | Char | String | LinkOption | Int | Long | Class[Option[Int]]] +[8:12..8:16):List => *.apply[Int | Long | Float | Double | List[Nothing] | Char | String | Boolean | Unit | LinkOption | Class[Option[Int]]] +[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[Int | Long | Float | Double | List[Nothing] | Char | String | Boolean | Unit | LinkOption | Class[Option[Int]]](*) [20:4..20:8):List => *.apply[Nothing] +[20:4..20:10):List() => List.apply[Nothing](*) expect/InventedNames.scala -------------------------- @@ -2071,6 +2238,7 @@ Text => empty Language => Scala Symbols => 45 entries Occurrences => 66 entries +Diagnostics => 1 entries Synthetics => 3 entries Symbols: @@ -2188,6 +2356,9 @@ Occurrences: [41:8..41:17): given_Z_T -> givens/InventedNames$package.given_Z_T(). [41:18..41:24): String -> scala/Predef.String# +Diagnostics: +[24:13..24:13): [warning] unused implicit parameter + Synthetics: [24:0..24:0): => *(x$1) [34:8..34:20):given_Double => *(intValue) @@ -2203,7 +2374,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 } @@ -2242,8 +2413,37 @@ 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 +-------------------------- + +Summary: +Schema => SemanticDB v4 +Uri => JavaStaticVar.scala +Text => empty +Language => Scala +Symbols => 3 entries +Occurrences => 9 entries + +Symbols: +example/ScalaFoo# => class ScalaFoo extends Object { self: ScalaFoo => +2 decls } +example/ScalaFoo#``(). => primary ctor (): ScalaFoo +example/ScalaFoo#javaStaticVarFoo. => val method javaStaticVarFoo Int + +Occurrences: +[0:8..0:15): example <- example/ +[2:7..2:10): com -> com/ +[2:11..2:17): javacp -> com/javacp/ +[2:18..2:31): JavaStaticVar -> com/javacp/JavaStaticVar# +[4:6..4:14): ScalaFoo <- example/ScalaFoo# +[5:2..5:2): <- example/ScalaFoo#``(). +[5:6..5:22): javaStaticVarFoo <- example/ScalaFoo#javaStaticVarFoo. +[5:25..5:38): JavaStaticVar -> com/javacp/JavaStaticVar# +[5:39..5:42): foo -> com/javacp/JavaStaticVar#foo. expect/Local.scala ------------------ @@ -2291,7 +2491,7 @@ Text => empty Language => Scala Symbols => 3 entries Occurrences => 6 entries -Synthetics => 1 entries +Synthetics => 2 entries Symbols: local0 => val local x: Int @@ -2308,6 +2508,7 @@ Occurrences: Synthetics: [5:4..5:8):List => *.apply[Int] +[5:4..5:11):List(x) => List.apply[Int](*) expect/MatchType.scala ---------------------- @@ -2818,6 +3019,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 } @@ -2905,6 +3107,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 --------------------------- @@ -2915,6 +3120,7 @@ Text => empty Language => Scala Symbols => 16 entries Occurrences => 12 entries +Synthetics => 1 entries Symbols: example/NamedArguments# => class NamedArguments extends Object { self: NamedArguments => +4 decls } @@ -2948,6 +3154,9 @@ Occurrences: [5:7..5:12): apply -> example/NamedArguments#User.apply(). [5:13..5:17): name -> example/NamedArguments#User.apply().(name) +Synthetics: +[4:2..4:21):User(name = "John") => apply(*) + expect/NewModifiers.scala ------------------------- @@ -3139,7 +3348,7 @@ Text => empty Language => Scala Symbols => 68 entries Occurrences => 115 entries -Synthetics => 3 entries +Synthetics => 4 entries Symbols: example/C# => class C extends Object { self: C => +3 decls } @@ -3151,6 +3360,9 @@ example/PickOneRefinement_1#[S] => typeparam S <: SpecialRefinement { abstract example/PickOneRefinement_1#[S](as) => param as: T* example/PickOneRefinement_1#[S][T] => typeparam T example/PickOneRefinement_1#``(). => primary ctor [typeparam S <: SpecialRefinement { abstract method pickOne [typeparam T ](param as: T*): Option[String] }](): PickOneRefinement_1[S] +example/PickOneRefinement_1#``().[S]pickOne(). => abstract method pickOne [typeparam T ](param as: T*): Option[String] <: example/SpecialRefinement#pickOne(). +example/PickOneRefinement_1#``().[S]pickOne().(as) => param as: T* +example/PickOneRefinement_1#``().[S]pickOne().[T] => typeparam T example/PickOneRefinement_1#run(). => method run (param s: S, param as: String*): Option[String] example/PickOneRefinement_1#run().(as) => param as: String* example/PickOneRefinement_1#run().(s) => param s: S @@ -3161,7 +3373,11 @@ example/PolyHolder#foo().(t) => param t: T example/PolyHolder#foo().[T] => typeparam T example/RecOrRefined$package. => final package object example extends Object { self: example.type => +9 decls } example/RecOrRefined$package.C2# => type C2 = C { type T2 = T1 <: example/C#T2#; type T1 <: example/C#T1# } +example/RecOrRefined$package.C2#T1# => type T1 <: example/C#T1# +example/RecOrRefined$package.C2#T2# => type T2 = T1 <: example/C#T2# example/RecOrRefined$package.Person# => type Person = Record { abstract val method age Int; abstract val method name String } +example/RecOrRefined$package.Person#age. => abstract val method age Int +example/RecOrRefined$package.Person#name. => abstract val method name String example/RecOrRefined$package.m1(). => method m1 (param a: Int { abstract val method x Int }): Nothing example/RecOrRefined$package.m1().(a) => param a: Int { abstract val method x Int } example/RecOrRefined$package.m2(). => method m2 (param x: Object { abstract method y => Int; abstract val method x Int }): Nothing @@ -3175,6 +3391,9 @@ example/RecOrRefined$package.m5().(x) => param x: Int example/RecOrRefined$package.m5().[Z] => typeparam Z example/RecOrRefined$package.m6# => type m6 [typeparam X ] = PolyHolder { abstract method foo [typeparam T ](param t: T): T <: example/PolyHolder#foo(). } example/RecOrRefined$package.m6#[X] => typeparam X +example/RecOrRefined$package.m6#foo(). => abstract method foo [typeparam T ](param t: T): T <: example/PolyHolder#foo(). +example/RecOrRefined$package.m6#foo().(t) => param t: T +example/RecOrRefined$package.m6#foo().[T] => typeparam T example/Record# => class Record extends Object with Selectable { self: Record => +4 decls } example/Record#``(). => primary ctor (param elems: Tuple2[String, Any]*): Record example/Record#``().(elems) => param elems: Tuple2[String, Any]* @@ -3188,51 +3407,41 @@ example/SpecialRefinement#pickOne(). => abstract method pickOne [typeparam T ](p example/SpecialRefinement#pickOne().(as) => param as: T* example/SpecialRefinement#pickOne().[T] => typeparam T local0 => abstract method pickOne [typeparam T ](param as: T*): Option[String] -local1 => typeparam T -local2 => param as: T* -local3 => abstract method pickOne [typeparam T ](param as: T*): Option[String] <: example/SpecialRefinement#pickOne(). +local1 => abstract val method x Int +local2 => abstract val method x Int +local3 => abstract method y => Int local4 => abstract val method x Int -local5 => abstract val method x Int -local6 => abstract method y => Int -local7 => abstract val method x Int -local8 => abstract method y => Int -local9 => type z +local5 => abstract method y => Int +local6 => type z +local7 => typeparam T +local8 => param t: T +local9 => abstract method foo [typeparam T ](param t: T): T <: example/PolyHolder#foo(). local10 => typeparam T local11 => param t: T local12 => abstract method foo [typeparam T ](param t: T): T <: example/PolyHolder#foo(). -local13 => typeparam T -local14 => param t: T -local15 => abstract method foo [typeparam T ](param t: T): T <: example/PolyHolder#foo(). -local16 => typeparam T -local17 => param t: T -local18 => abstract method foo [typeparam T ](param t: T): T <: example/PolyHolder#foo(). -local19 => abstract val method name String -local20 => abstract val method age Int -local21 => type T1 <: example/C#T1# -local22 => type T2 = T1 <: example/C#T2# Occurrences: [0:8..0:15): example <- example/ [2:4..2:6): m1 <- example/RecOrRefined$package.m1(). [2:7..2:8): a <- example/RecOrRefined$package.m1().(a) [2:10..2:13): Int -> scala/Int# -[2:20..2:21): x <- local4 +[2:20..2:21): x <- local1 [2:23..2:26): Int -> scala/Int# [2:32..2:35): ??? -> scala/Predef.`???`(). [3:4..3:6): m2 <- example/RecOrRefined$package.m2(). [3:7..3:8): x <- example/RecOrRefined$package.m2().(x) -[3:16..3:17): x <- local5 +[3:16..3:17): x <- local2 [3:19..3:22): Int -> scala/Int# -[3:28..3:29): y <- local6 +[3:28..3:29): y <- local3 [3:31..3:34): Int -> scala/Int# [3:40..3:43): ??? -> scala/Predef.`???`(). [4:4..4:6): m3 <- example/RecOrRefined$package.m3(). [4:7..4:8): x <- example/RecOrRefined$package.m3().(x) -[4:16..4:17): x <- local7 +[4:16..4:17): x <- local4 [4:19..4:22): Int -> scala/Int# -[4:28..4:29): y <- local8 +[4:28..4:29): y <- local5 [4:31..4:34): Int -> scala/Int# -[4:41..4:42): z <- local9 +[4:41..4:42): z <- local6 [4:48..4:51): ??? -> scala/Predef.`???`(). [5:6..5:16): PolyHolder <- example/PolyHolder# [6:2..6:2): <- example/PolyHolder#``(). @@ -3244,31 +3453,31 @@ Occurrences: [9:4..9:6): m4 <- example/RecOrRefined$package.m4(). [9:7..9:8): x <- example/RecOrRefined$package.m4().(x) [9:10..9:20): PolyHolder -> example/PolyHolder# -[9:27..9:30): foo <- local12 -[9:31..9:32): T <- local10 -[9:34..9:35): t <- local11 -[9:37..9:38): T -> local10 -[9:41..9:42): T -> local10 +[9:27..9:30): foo <- local9 +[9:31..9:32): T <- local7 +[9:34..9:35): t <- local8 +[9:37..9:38): T -> local7 +[9:41..9:42): T -> local7 [9:48..9:51): ??? -> scala/Predef.`???`(). [10:4..10:6): m5 <- example/RecOrRefined$package.m5(). [10:7..10:8): Z <- example/RecOrRefined$package.m5().[Z] [10:10..10:11): x <- example/RecOrRefined$package.m5().(x) [10:13..10:16): Int -> scala/Int# [10:19..10:29): PolyHolder -> example/PolyHolder# -[10:36..10:39): foo <- local15 -[10:40..10:41): T <- local13 -[10:43..10:44): t <- local14 -[10:46..10:47): T -> local13 -[10:50..10:51): T -> local13 +[10:36..10:39): foo <- local12 +[10:40..10:41): T <- local10 +[10:43..10:44): t <- local11 +[10:46..10:47): T -> local10 +[10:50..10:51): T -> local10 [10:56..10:59): ??? -> scala/Predef.`???`(). [12:5..12:7): m6 <- example/RecOrRefined$package.m6# [12:11..12:12): X <- example/RecOrRefined$package.m6#[X] [12:18..12:28): PolyHolder -> example/PolyHolder# -[12:35..12:38): foo <- local18 -[12:39..12:40): T <- local16 -[12:42..12:43): t <- local17 -[12:45..12:46): T -> local16 -[12:49..12:50): T -> local16 +[12:35..12:38): foo <- example/RecOrRefined$package.m6#foo(). +[12:39..12:40): T <- example/RecOrRefined$package.m6#foo().[T] +[12:42..12:43): t <- example/RecOrRefined$package.m6#foo().(t) +[12:45..12:46): T -> example/RecOrRefined$package.m6#foo().[T] +[12:49..12:50): T -> example/RecOrRefined$package.m6#foo().[T] [14:6..14:12): Record <- example/Record# [14:12..14:12): <- example/Record#``(). [14:13..14:18): elems <- example/Record#elems. @@ -3286,9 +3495,9 @@ Occurrences: [16:48..16:52): name -> example/Record#selectDynamic().(name) [18:5..18:11): Person <- example/RecOrRefined$package.Person# [18:14..18:20): Record -> example/Record# -[19:6..19:10): name <- local19 +[19:6..19:10): name <- example/RecOrRefined$package.Person#name. [19:12..19:18): String -> scala/Predef.String# -[20:6..20:9): age <- local20 +[20:6..20:9): age <- example/RecOrRefined$package.Person#age. [20:11..20:14): Int -> scala/Int# [24:6..24:7): C <- example/C# [24:10..24:10): <- example/C#``(). @@ -3296,9 +3505,9 @@ Occurrences: [24:24..24:26): T2 <- example/C#T2# [25:5..25:7): C2 <- example/RecOrRefined$package.C2# [25:10..25:11): C -> example/C# -[25:19..25:21): T1 <- local21 -[25:28..25:30): T2 <- local22 -[25:33..25:35): T1 -> local21 +[25:19..25:21): T1 <- example/RecOrRefined$package.C2#T1# +[25:28..25:30): T2 <- example/RecOrRefined$package.C2#T2# +[25:33..25:35): T1 -> example/RecOrRefined$package.C2#T1# [27:6..27:23): SpecialRefinement <- example/SpecialRefinement# [28:2..28:2): <- example/SpecialRefinement#``(). [28:6..28:13): pickOne <- example/SpecialRefinement#pickOne(). @@ -3311,10 +3520,10 @@ Occurrences: [31:25..31:25): <- example/PickOneRefinement_1#``(). [31:26..31:27): S <- example/PickOneRefinement_1#[S] [31:31..31:48): SpecialRefinement -> example/SpecialRefinement# -[31:55..31:62): pickOne <- local3 -[31:63..31:64): T <- local1 -[31:66..31:68): as <- local2 -[31:70..31:71): T -> local1 +[31:55..31:62): pickOne <- example/PickOneRefinement_1#``().[S]pickOne(). +[31:63..31:64): T <- example/PickOneRefinement_1#``().[S]pickOne().[T] +[31:66..31:68): as <- example/PickOneRefinement_1#``().[S]pickOne().(as) +[31:70..31:71): T -> example/PickOneRefinement_1#``().[S]pickOne().[T] [31:75..31:81): Option -> scala/Option# [31:82..31:88): String -> scala/Predef.String# [32:6..32:9): run <- example/PickOneRefinement_1#run(). @@ -3331,6 +3540,7 @@ Occurrences: 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 @@ -3343,6 +3553,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 } @@ -3365,6 +3576,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 ------------------ @@ -3415,6 +3629,94 @@ Occurrences: [13:17..13:17): <- selfs/C6#``(). [13:27..13:28): B -> selfs/B# +expect/ShadowedParameters.scala +------------------------------- + +Summary: +Schema => SemanticDB v4 +Uri => ShadowedParameters.scala +Text => empty +Language => Scala +Symbols => 20 entries +Occurrences => 31 entries +Diagnostics => 14 entries +Synthetics => 2 entries + +Symbols: +example/Shadow# => class Shadow extends Object { self: Shadow => +5 decls } +example/Shadow#``(). => primary ctor (): Shadow +example/Shadow#curriedParams(). => method curriedParams (param x: Int)(param y: List[Int]): Unit +example/Shadow#curriedParams().(x) => param x: Int +example/Shadow#curriedParams().(y) => param y: List[Int] +example/Shadow#multiParams(). => method multiParams (param x: List[Int], param y: String): Unit +example/Shadow#multiParams().(x) => param x: List[Int] +example/Shadow#multiParams().(y) => param y: String +example/Shadow#shadowInParamBlock(). => method shadowInParamBlock (param x: Int, param y: Int): Unit +example/Shadow#shadowInParamBlock().(x) => param x: Int +example/Shadow#shadowInParamBlock().(y) => param y: Int +example/Shadow#shadowParam(). => method shadowParam (param x: Int): Unit +example/Shadow#shadowParam().(x) => param x: Int +local0 => val local x: Boolean +local1 => val local x: String +local2 => val local y: Int +local3 => val local x: Int +local4 => val local y: List[Int] +local5 => val local y: Int +local6 => val local x: Int + +Occurrences: +[0:8..0:15): example <- example/ +[2:6..2:12): Shadow <- example/Shadow# +[4:4..4:4): <- example/Shadow#``(). +[4:8..4:19): shadowParam <- example/Shadow#shadowParam(). +[4:20..4:21): x <- example/Shadow#shadowParam().(x) +[4:23..4:26): Int -> scala/Int# +[5:12..5:13): x <- local0 +[8:8..8:21): curriedParams <- example/Shadow#curriedParams(). +[8:22..8:23): x <- example/Shadow#curriedParams().(x) +[8:25..8:28): Int -> scala/Int# +[8:30..8:31): y <- example/Shadow#curriedParams().(y) +[8:33..8:37): List -> scala/package.List# +[8:38..8:41): Int -> scala/Int# +[9:12..9:13): x <- local1 +[10:12..10:13): y <- local2 +[14:8..14:19): multiParams <- example/Shadow#multiParams(). +[14:20..14:21): x <- example/Shadow#multiParams().(x) +[14:23..14:27): List -> scala/package.List# +[14:28..14:31): Int -> scala/Int# +[14:34..14:35): y <- example/Shadow#multiParams().(y) +[14:37..14:43): String -> scala/Predef.String# +[15:12..15:13): x <- local3 +[16:12..16:13): y <- local4 +[16:16..16:20): List -> scala/package.List. +[20:8..20:26): shadowInParamBlock <- example/Shadow#shadowInParamBlock(). +[20:27..20:28): x <- example/Shadow#shadowInParamBlock().(x) +[20:30..20:33): Int -> scala/Int# +[20:35..20:36): y <- example/Shadow#shadowInParamBlock().(y) +[20:38..20:41): Int -> scala/Int# +[21:12..21:13): y <- local5 +[23:16..23:17): x <- local6 + +Diagnostics: +[4:20..4:21): [warning] unused explicit parameter +[5:12..5:13): [warning] unused local definition +[8:22..8:23): [warning] unused explicit parameter +[8:30..8:31): [warning] unused explicit parameter +[9:12..9:13): [warning] unused local definition +[10:12..10:13): [warning] unused local definition +[14:20..14:21): [warning] unused explicit parameter +[14:34..14:35): [warning] unused explicit parameter +[15:12..15:13): [warning] unused local definition +[16:12..16:13): [warning] unused local definition +[20:27..20:28): [warning] unused explicit parameter +[20:35..20:36): [warning] unused explicit parameter +[21:12..21:13): [warning] unused local definition +[23:16..23:17): [warning] unused local definition + +Synthetics: +[16:16..16:20):List => *.apply[Int] +[16:16..16:27):List(1,2,3) => List.apply[Int](*) + expect/StructuralTypes.scala ---------------------------- @@ -3423,23 +3725,24 @@ Schema => SemanticDB v4 Uri => StructuralTypes.scala Text => empty Language => Scala -Symbols => 12 entries -Occurrences => 33 entries +Symbols => 13 entries +Occurrences => 35 entries Synthetics => 4 entries Symbols: -example/StructuralTypes. => final object StructuralTypes extends Object { self: StructuralTypes.type => +5 decls } -example/StructuralTypes.User# => type User = Object { abstract method foo (param x: Int): Int; abstract method age => Int; abstract method name => String } +example/StructuralTypes. => final object StructuralTypes extends Object { self: StructuralTypes.type => +6 decls } +example/StructuralTypes.FooUser# => type FooUser = User { abstract method foo (param x: Int): Int } +example/StructuralTypes.FooUser#foo(). => abstract method foo (param x: Int): Int +example/StructuralTypes.FooUser#foo().(x) => param x: Int +example/StructuralTypes.User# => type User = Object { abstract method age => Int; abstract method name => String } +example/StructuralTypes.User#age(). => abstract method age => Int +example/StructuralTypes.User#name(). => abstract method name => String example/StructuralTypes.V. => val method V Object { abstract method scalameta => String } example/StructuralTypes.fooBar. => val method fooBar Int -example/StructuralTypes.user. => val method user User -local0 => abstract method name => String -local1 => abstract method age => Int -local2 => param x: Int -local3 => abstract method foo (param x: Int): Int -local4 => abstract method scalameta => String -local5 => method scalameta => String -local6 => final class $anon extends Object { self: $anon => +2 decls } +example/StructuralTypes.user. => val method user FooUser +local0 => abstract method scalameta => String +local1 => method scalameta => String +local2 => final class $anon extends Object { self: $anon => +2 decls } Occurrences: [0:8..0:15): example <- example/ @@ -3448,39 +3751,107 @@ Occurrences: [2:26..2:46): reflectiveSelectable -> scala/reflect/Selectable.reflectiveSelectable(). [4:7..4:22): StructuralTypes <- example/StructuralTypes. [5:7..5:11): User <- example/StructuralTypes.User# -[6:8..6:12): name <- local0 +[6:8..6:12): name <- example/StructuralTypes.User#name(). [6:14..6:20): String -> scala/Predef.String# -[7:8..7:11): age <- local1 +[7:8..7:11): age <- example/StructuralTypes.User#age(). [7:13..7:16): Int -> scala/Int# -[8:8..8:11): foo <- local3 -[8:12..8:13): x <- local2 -[8:15..8:18): Int -> scala/Int# -[8:21..8:24): Int -> scala/Int# -[11:6..11:10): user <- example/StructuralTypes.user. -[11:18..11:30): asInstanceOf -> scala/Any#asInstanceOf(). -[11:31..11:35): User -> example/StructuralTypes.User# -[12:2..12:6): user -> example/StructuralTypes.user. -[12:7..12:11): name -> scala/reflect/Selectable#selectDynamic(). -[13:2..13:6): user -> example/StructuralTypes.user. -[13:7..13:10): age -> scala/reflect/Selectable#selectDynamic(). -[14:6..14:12): fooBar <- example/StructuralTypes.fooBar. -[14:15..14:19): user -> example/StructuralTypes.user. -[14:20..14:23): foo -> scala/reflect/Selectable#applyDynamic(). -[16:6..16:7): V <- example/StructuralTypes.V. -[16:9..16:15): Object -> java/lang/Object# -[17:8..17:17): scalameta <- local4 -[17:19..17:25): String -> scala/Predef.String# -[19:4..19:4): <- local6 -[19:8..19:17): scalameta <- local5 -[20:2..20:3): V -> example/StructuralTypes.V. -[20:4..20:13): scalameta -> scala/reflect/Selectable#selectDynamic(). -[21:4..21:19): StructuralTypes -> example/StructuralTypes. +[10:7..10:14): FooUser <- example/StructuralTypes.FooUser# +[10:17..10:21): User -> example/StructuralTypes.User# +[11:8..11:11): foo <- example/StructuralTypes.FooUser#foo(). +[11:12..11:13): x <- example/StructuralTypes.FooUser#foo().(x) +[11:15..11:18): Int -> scala/Int# +[11:21..11:24): Int -> scala/Int# +[14:6..14:10): user <- example/StructuralTypes.user. +[14:18..14:30): asInstanceOf -> scala/Any#asInstanceOf(). +[14:31..14:38): FooUser -> example/StructuralTypes.FooUser# +[15:2..15:6): user -> example/StructuralTypes.user. +[15:7..15:11): name -> example/StructuralTypes.User#name(). +[16:2..16:6): user -> example/StructuralTypes.user. +[16:7..16:10): age -> example/StructuralTypes.User#age(). +[17:6..17:12): fooBar <- example/StructuralTypes.fooBar. +[17:15..17:19): user -> example/StructuralTypes.user. +[17:20..17:23): foo -> example/StructuralTypes.FooUser#foo(). +[18:6..18:7): V <- example/StructuralTypes.V. +[18:9..18:15): Object -> java/lang/Object# +[19:8..19:17): scalameta <- local0 +[19:19..19:25): String -> scala/Predef.String# +[20:6..20:6): <- local2 +[21:8..21:17): scalameta <- local1 +[22:2..22:3): V -> example/StructuralTypes.V. +[22:4..22:13): scalameta -> local4 +[23:4..23:19): StructuralTypes -> example/StructuralTypes. Synthetics: -[12:2..12:6):user => reflectiveSelectable(*) -[13:2..13:6):user => reflectiveSelectable(*) +[15:2..15:6):user => reflectiveSelectable(*) +[16:2..16:6):user => reflectiveSelectable(*) +[17:15..17:19):user => reflectiveSelectable(*) +[22:2..22:3):V => reflectiveSelectable(*) + +expect/StructuralTypesParams.scala +---------------------------------- + +Summary: +Schema => SemanticDB v4 +Uri => StructuralTypesParams.scala +Text => empty +Language => Scala +Symbols => 12 entries +Occurrences => 33 entries +Synthetics => 3 entries + +Symbols: +example/StructuralTypesParams. => final object StructuralTypesParams extends Object { self: StructuralTypesParams.type => +8 decls } +example/StructuralTypesParams.User# => type User = Object { abstract method age => Int; abstract method foo (param arg1: Int, param arg2: String): String } +example/StructuralTypesParams.User#age(). => abstract method age => Int +example/StructuralTypesParams.User#foo(). => abstract method foo (param arg1: Int, param arg2: String): String +example/StructuralTypesParams.User#foo().(arg1) => param arg1: Int +example/StructuralTypesParams.User#foo().(arg2) => param arg2: String +example/StructuralTypesParams.age. => val method age Int +example/StructuralTypesParams.fooBar. => val method fooBar String +example/StructuralTypesParams.fooBaz. => val method fooBaz String +example/StructuralTypesParams.num. => val method num Int +example/StructuralTypesParams.str. => val method str String +example/StructuralTypesParams.user. => val method user User + +Occurrences: +[0:8..0:15): example <- example/ +[2:7..2:14): reflect -> scala/reflect/ +[2:15..2:25): Selectable -> scala/reflect/Selectable. +[2:26..2:46): reflectiveSelectable -> scala/reflect/Selectable.reflectiveSelectable(). +[4:7..4:28): StructuralTypesParams <- example/StructuralTypesParams. +[5:7..5:11): User <- example/StructuralTypesParams.User# +[6:8..6:11): foo <- example/StructuralTypesParams.User#foo(). +[6:12..6:16): arg1 <- example/StructuralTypesParams.User#foo().(arg1) +[6:18..6:21): Int -> scala/Int# +[6:23..6:27): arg2 <- example/StructuralTypesParams.User#foo().(arg2) +[6:29..6:35): String -> scala/Predef.String# +[6:38..6:44): String -> scala/Predef.String# +[7:8..7:11): age <- example/StructuralTypesParams.User#age(). +[7:13..7:16): Int -> scala/Int# +[10:6..10:10): user <- example/StructuralTypesParams.user. +[10:18..10:30): asInstanceOf -> scala/Any#asInstanceOf(). +[10:31..10:35): User -> example/StructuralTypesParams.User# +[11:6..11:9): num <- example/StructuralTypesParams.num. +[12:6..12:9): str <- example/StructuralTypesParams.str. +[13:6..13:12): fooBar <- example/StructuralTypesParams.fooBar. +[13:15..13:19): user -> example/StructuralTypesParams.user. +[13:20..13:23): foo -> example/StructuralTypesParams.User#foo(). +[13:24..13:27): num -> example/StructuralTypesParams.num. +[13:29..13:32): str -> example/StructuralTypesParams.str. +[14:6..14:12): fooBaz <- example/StructuralTypesParams.fooBaz. +[14:15..14:19): user -> example/StructuralTypesParams.user. +[14:20..14:23): foo -> example/StructuralTypesParams.User#foo(). +[14:31..14:34): num -> example/StructuralTypesParams.num. +[14:43..14:46): str -> example/StructuralTypesParams.str. +[15:6..15:9): age <- example/StructuralTypesParams.age. +[15:12..15:16): user -> example/StructuralTypesParams.user. +[15:17..15:20): age -> example/StructuralTypesParams.User#age(). +[17:4..17:25): StructuralTypesParams -> example/StructuralTypesParams. + +Synthetics: +[13:15..13:19):user => reflectiveSelectable(*) [14:15..14:19):user => reflectiveSelectable(*) -[20:2..20:3):V => reflectiveSelectable(*) +[15:12..15:16):user => reflectiveSelectable(*) expect/Synthetic.scala ---------------------- @@ -3492,7 +3863,8 @@ Text => empty Language => Scala Symbols => 62 entries Occurrences => 164 entries -Synthetics => 39 entries +Diagnostics => 3 entries +Synthetics => 48 entries Symbols: example/Synthetic# => class Synthetic extends Object { self: Synthetic => +23 decls } @@ -3724,14 +4096,22 @@ Occurrences: [68:18..68:24): impure -> local20 [68:30..68:31): s -> local16 +Diagnostics: +[19:21..19:22): [warning] unused pattern variable +[41:4..41:5): [warning] unused pattern variable +[63:10..63:11): [warning] unused explicit parameter + 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] @@ -3740,6 +4120,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] @@ -3751,8 +4132,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) @@ -3775,7 +4161,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 } @@ -3792,6 +4178,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 ----------------------- @@ -3886,7 +4273,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 } @@ -3965,17 +4352,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 ----------------- @@ -4209,23 +4620,22 @@ Text => empty Language => Scala Symbols => 8 entries Occurrences => 18 entries -Diagnostics => 1 entries Symbols: _empty_/Test_depmatch. => final object Test_depmatch extends Object { self: Test_depmatch.type => +4 decls } _empty_/Test_depmatch.Bar# => type Bar [typeparam T ] = T match { Unit => Unit } _empty_/Test_depmatch.Bar#[T] => typeparam T _empty_/Test_depmatch.Foo# => type Foo = Int { type U } +_empty_/Test_depmatch.Foo#U# => type U _empty_/Test_depmatch.baz(). => inline macro baz (param foo: Foo): Unit _empty_/Test_depmatch.baz().(foo) => param foo: Foo -local0 => type U -local1 => val local v: Bar[foo.U] +local0 => val local v: Bar[foo.U] Occurrences: [0:7..0:20): Test_depmatch <- _empty_/Test_depmatch. [1:7..1:10): Foo <- _empty_/Test_depmatch.Foo# [1:13..1:16): Int -> scala/Int# -[1:24..1:25): U <- local0 +[1:24..1:25): U <- _empty_/Test_depmatch.Foo#U# [2:7..2:10): Bar <- _empty_/Test_depmatch.Bar# [2:11..2:12): T <- _empty_/Test_depmatch.Bar#[T] [2:16..2:17): T -> _empty_/Test_depmatch.Bar#[T] @@ -4235,15 +4645,12 @@ Occurrences: [5:17..5:20): foo <- _empty_/Test_depmatch.baz().(foo) [5:22..5:25): Foo -> _empty_/Test_depmatch.Foo# [5:28..5:32): Unit -> scala/Unit# -[6:8..6:9): v <- local1 +[6:8..6:9): v <- local0 [6:11..6:14): Bar -> _empty_/Test_depmatch.Bar# [6:15..6:18): foo -> _empty_/Test_depmatch.baz().(foo) -[6:19..6:20): U -> local0 +[6:19..6:20): U -> _empty_/Test_depmatch.Foo#U# [6:24..6:27): ??? -> scala/Predef.`???`(). -Diagnostics: -[6:8..6:9): [warning] unused local definition - expect/example-dir/FileInDir.scala ---------------------------------- @@ -4343,6 +4750,7 @@ Text => empty Language => Scala Symbols => 7 entries Occurrences => 5 entries +Diagnostics => 1 entries Symbols: exports/`exports-package$package`. => final package object exports extends Object { self: exports.type => +4 decls } @@ -4360,6 +4768,9 @@ Occurrences: [2:25..2:32): Encoder <- exports/`exports-package$package`.Encoder# [2:34..2:39): Codec <- exports/`exports-package$package`.Codec# +Diagnostics: +[0:0..2:40): [warning] The package name `exports-package$package` will be encoded on the classpath, and can lead to undefined behaviour. + expect/filename with spaces.scala --------------------------------- @@ -4562,6 +4973,8 @@ Text => empty 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 } @@ -4586,8 +4999,8 @@ _empty_/Txn# => trait Txn [typeparam T <: Txn[T]] extends Object { self: Txn[T] _empty_/Txn#[T] => typeparam T <: Txn[T] _empty_/Txn#``(). => primary ctor [typeparam T <: Txn[T]](): Txn[T] local0 => val local out: Repr[Out] -local1 => val local inObj: Repr[In] & Obj[In] -local2 => val local outObj: Repr[Out] & Obj[Out] +local1 => val local inObj: Obj[In] & Repr[In] +local2 => val local outObj: Obj[Out] & Repr[Out] Occurrences: [1:6..1:9): Txn <- _empty_/Txn# @@ -4654,6 +5067,13 @@ Occurrences: [14:8..14:15): println -> scala/Predef.println(+1). [17:4..17:7): out -> local0 +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 -------------------------- @@ -4748,7 +5168,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 } @@ -4804,6 +5224,7 @@ Occurrences: Synthetics: [13:17..13:21):List => *.apply[Int] +[13:17..13:28):List(1,2,3) => List.apply[Int](*) expect/recursion.scala ---------------------- @@ -4815,6 +5236,7 @@ Text => empty Language => Scala Symbols => 36 entries Occurrences => 48 entries +Synthetics => 2 entries Symbols: local0 => type N$1 <: Nat @@ -4904,6 +5326,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 ----------------------------------- @@ -4948,7 +5374,7 @@ Text => empty Language => Scala Symbols => 50 entries Occurrences => 78 entries -Diagnostics => 4 entries +Diagnostics => 5 entries Synthetics => 2 entries Symbols: @@ -5088,6 +5514,7 @@ Diagnostics: [9:36..9:37): [warning] unused explicit parameter [9:42..9:43): [warning] unused explicit parameter [21:11..21:12): [warning] unused explicit parameter +[24:24..24:27): [warning] unused pattern variable Synthetics: [23:6..23:10):List => *.unapplySeq[Nothing] @@ -5230,7 +5657,7 @@ types/Test.Literal.clazzOf. => final val method clazzOf Option[Int] types/Test.Literal.double. => final val method double 2.0 types/Test.Literal.float. => final val method float 1.0f types/Test.Literal.int. => final val method int 1 -types/Test.Literal.javaEnum. => final val method javaEnum LinkOption +types/Test.Literal.javaEnum. => final val method javaEnum NOFOLLOW_LINKS.type types/Test.Literal.long. => final val method long 1L types/Test.Literal.nil. => final val method nil Null types/Test.Literal.string. => final val method string "a" @@ -5500,7 +5927,7 @@ Occurrences: [119:39..119:42): Int -> scala/Int# Diagnostics: -[5:13..5:14): [warning] unused explicit parameter +[96:13..96:14): [warning] unused explicit parameter Synthetics: [68:20..68:24):@ann => *[Int] @@ -5515,7 +5942,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 } @@ -5562,8 +5989,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/sjs-junit/test/org/scalajs/testsuite/compiler/RegressionTestScala3.scala b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/RegressionTestScala3.scala index 02cb08789232..452ff70ae0d3 100644 --- a/tests/sjs-junit/test/org/scalajs/testsuite/compiler/RegressionTestScala3.scala +++ b/tests/sjs-junit/test/org/scalajs/testsuite/compiler/RegressionTestScala3.scala @@ -156,6 +156,46 @@ class RegressionTestScala3 { val c = Array.ofDim[Unit](0, 0) assertSame(classOf[Array[Array[Unit]]], c.getClass()) } + + @Test def issue25097(): Unit = { + val foo: Any = 1.0 + assertTrue(foo.isInstanceOf[Int]) + + // should not get constant-folded incorrectly earlier in the pipeline: + // (which is why we cannot use fancier asserts here, having the specific 'constant.isInstanceOf[Number]' shape is the point) + + assertTrue("1.0 instanceof Byte", 1.0.isInstanceOf[Byte]) + assertTrue("1.0 instanceof Short", 1.0.isInstanceOf[Short]) + assertTrue("1.0 instanceof Int", 1.0.isInstanceOf[Int]) + assertTrue("1.0 instanceof Float", 1.0.isInstanceOf[Float]) + assertTrue("1.0 instanceof Double", 1.0.isInstanceOf[Double]) + + assertFalse("128 instanceof Byte", 128.isInstanceOf[Byte]) + assertTrue("128 instanceof Short", 128.isInstanceOf[Short]) + assertTrue("128 instanceof Int", 128.isInstanceOf[Int]) + assertTrue("128 instanceof Float", 128.isInstanceOf[Float]) + assertTrue("128 instanceof Double", 128.isInstanceOf[Double]) + + assertFalse("32768 instanceof Byte", 32768.isInstanceOf[Byte]) + assertFalse("32768 instanceof Short", 32768.isInstanceOf[Short]) + assertTrue("32768 instanceof Int", 32768.isInstanceOf[Int]) + assertTrue("32768 instanceof Float", 32768.isInstanceOf[Float]) + assertTrue("32768 instanceof Double", 32768.isInstanceOf[Double]) + + assertFalse("2147483647 instanceof Byte", 2147483647.isInstanceOf[Byte]) + assertFalse("2147483647 instanceof Short", 2147483647.isInstanceOf[Short]) + assertTrue("2147483647 instanceof Int", 2147483647.isInstanceOf[Int]) + assertFalse("2147483647 instanceof Float", 2147483647.isInstanceOf[Float]) // cannot be represented exactly as a float + assertTrue("2147483647 instanceof Double", 2147483647.isInstanceOf[Double]) + + assertFalse("2147483648.0 instanceof Byte", 2147483648.0.isInstanceOf[Byte]) + assertFalse("2147483648.0 instanceof Short", 2147483648.0.isInstanceOf[Short]) + assertFalse("2147483648.0 instanceof Int", 2147483648.0.isInstanceOf[Int]) + assertTrue("2147483648.0 instanceof Float", 2147483648.0.isInstanceOf[Float]) + assertTrue("2147483648.0 instanceof Double", 2147483648.0.isInstanceOf[Double]) + + assertTrue("1: jl.Integer instanceof jl.Double", (1: java.lang.Integer).isInstanceOf[java.lang.Double]) // ensure the target being boxed also works + } } object RegressionTestScala3 { diff --git a/tests/untried/neg/class-of-double-targs.check b/tests/untried/neg/class-of-double-targs.check deleted file mode 100644 index f7e2094f9778..000000000000 --- a/tests/untried/neg/class-of-double-targs.check +++ /dev/null @@ -1,4 +0,0 @@ -class-of-double-targs.scala:2: error: expression of type Class[Int](classOf[scala.Int]) does not take type parameters. - classOf[Int][Int] - ^ -one error found diff --git a/tests/untried/neg/class-of-double-targs.scala b/tests/untried/neg/class-of-double-targs.scala deleted file mode 100644 index 26a2fa838136..000000000000 --- a/tests/untried/neg/class-of-double-targs.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Test { - classOf[Int][Int] -} diff --git a/tests/untried/neg/forward.check b/tests/untried/neg/forward.check deleted file mode 100644 index 252c990370e0..000000000000 --- a/tests/untried/neg/forward.check +++ /dev/null @@ -1,10 +0,0 @@ -forward.scala:6: error: forward reference extends over definition of value x - def f: Int = x; - ^ -forward.scala:10: error: forward reference extends over definition of value x - def f: Int = g; - ^ -forward.scala:15: error: forward reference extends over definition of variable x - def f: Int = g; - ^ -three errors found diff --git a/tests/untried/neg/forward.scala b/tests/untried/neg/forward.scala deleted file mode 100644 index d5c0851f09e3..000000000000 --- a/tests/untried/neg/forward.scala +++ /dev/null @@ -1,24 +0,0 @@ -object Test { - def f: Int = x; - val x: Int = f; - - { - def f: Int = x; - val x: Int = f; - } - { - def f: Int = g; - val x: Int = f; - def g: Int = x; - } - { - def f: Int = g; - var x: Int = f; - def g: Int = x; - } - { - def f: Int = g; - Console.println("foo"); - def g: Int = f; - } -} diff --git a/tests/untried/neg/t0842.check b/tests/untried/neg/t0842.check deleted file mode 100644 index 3351aa1174ce..000000000000 --- a/tests/untried/neg/t0842.check +++ /dev/null @@ -1,4 +0,0 @@ -t0842.scala:1: error: A.this.type does not take type parameters -trait A[T] { def m: this.type[T] = this } - ^ -one error found diff --git a/tests/untried/neg/t0842.scala b/tests/untried/neg/t0842.scala deleted file mode 100644 index f32c2ba26d71..000000000000 --- a/tests/untried/neg/t0842.scala +++ /dev/null @@ -1 +0,0 @@ -trait A[T] { def m: this.type[T] = this } diff --git a/tests/untried/neg/t1701.check b/tests/untried/neg/t1701.check deleted file mode 100644 index d603e62e5a6a..000000000000 --- a/tests/untried/neg/t1701.check +++ /dev/null @@ -1,4 +0,0 @@ -t1701.scala:1: error: Cloneable does not take type parameters -class A extends java.lang.Cloneable[String, Option, Int] - ^ -one error found diff --git a/tests/untried/neg/t1701.scala b/tests/untried/neg/t1701.scala deleted file mode 100644 index 7cd6ff9953be..000000000000 --- a/tests/untried/neg/t1701.scala +++ /dev/null @@ -1 +0,0 @@ -class A extends java.lang.Cloneable[String, Option, Int] diff --git a/tests/untried/neg/t278.check b/tests/untried/neg/t278.check deleted file mode 100644 index 405f7d225c17..000000000000 --- a/tests/untried/neg/t278.check +++ /dev/null @@ -1,11 +0,0 @@ -t278.scala:5: error: overloaded method value a with alternatives: - => C.this.A => Unit - => () => Unit - does not take type parameters - println(a[A]) - ^ -t278.scala:4: error: method a is defined twice - conflicting symbols both originated in file 't278.scala' - def a = (p:A) => () - ^ -two errors found diff --git a/tests/untried/neg/t278.scala b/tests/untried/neg/t278.scala deleted file mode 100644 index 39a711bb0985..000000000000 --- a/tests/untried/neg/t278.scala +++ /dev/null @@ -1,6 +0,0 @@ -class C { - class A - def a = () => () - def a = (p:A) => () - println(a[A]) -} diff --git a/tests/untried/neg/t2918.check b/tests/untried/neg/t2918.check deleted file mode 100644 index aae3045e8af7..000000000000 --- a/tests/untried/neg/t2918.check +++ /dev/null @@ -1,10 +0,0 @@ -t2918.scala:2: error: illegal cyclic reference involving type A - def g[X, A[X] <: A[X]](x: A[X]) = x - ^ -t2918.scala:2: error: cyclic aliasing or subtyping involving type A - def g[X, A[X] <: A[X]](x: A[X]) = x - ^ -t2918.scala:2: error: A does not take type parameters - def g[X, A[X] <: A[X]](x: A[X]) = x - ^ -three errors found diff --git a/tests/untried/neg/t2918.scala b/tests/untried/neg/t2918.scala deleted file mode 100644 index ff2be39ae0c4..000000000000 --- a/tests/untried/neg/t2918.scala +++ /dev/null @@ -1,3 +0,0 @@ -object Test { - def g[X, A[X] <: A[X]](x: A[X]) = x -} diff --git a/tests/untried/neg/t4727.check b/tests/untried/neg/t4727.check deleted file mode 100644 index a17cdde04417..000000000000 --- a/tests/untried/neg/t4727.check +++ /dev/null @@ -1,5 +0,0 @@ -t4727.scala:5: error: an expression of type Null is ineligible for implicit conversion -Error occurred in an application involving default arguments. - new C[Int] - ^ -one error found diff --git a/tests/untried/neg/t4727.scala b/tests/untried/neg/t4727.scala deleted file mode 100644 index 40c06713caa6..000000000000 --- a/tests/untried/neg/t4727.scala +++ /dev/null @@ -1,7 +0,0 @@ -class C[T](x : T = null) - -object Test { - def main(args: Array[String]): Unit = { - new C[Int] - } -} diff --git a/tests/untried/neg/t5093.check b/tests/untried/neg/t5093.check deleted file mode 100644 index daba46001153..000000000000 --- a/tests/untried/neg/t5093.check +++ /dev/null @@ -1,10 +0,0 @@ -t5093.scala:2: error: illegal cyclic reference involving type C - def f[C[X] <: C[X]](l: C[_]) = l.x - ^ -t5093.scala:2: error: cyclic aliasing or subtyping involving type C - def f[C[X] <: C[X]](l: C[_]) = l.x - ^ -t5093.scala:2: error: C does not take type parameters - def f[C[X] <: C[X]](l: C[_]) = l.x - ^ -three errors found diff --git a/tests/untried/neg/t5093.scala b/tests/untried/neg/t5093.scala deleted file mode 100644 index 9cde364dee4c..000000000000 --- a/tests/untried/neg/t5093.scala +++ /dev/null @@ -1,3 +0,0 @@ -class T { - def f[C[X] <: C[X]](l: C[_]) = l.x -} diff --git a/tests/untried/neg/t8219-any-any-ref-equals.check b/tests/untried/neg/t8219-any-any-ref-equals.check deleted file mode 100644 index 95d2536fba06..000000000000 --- a/tests/untried/neg/t8219-any-any-ref-equals.check +++ /dev/null @@ -1,10 +0,0 @@ -t8219-any-any-ref-equals.scala:5: error: method ==: (x$1: Any)Boolean does not take type parameters. - "".==[Int] - ^ -t8219-any-any-ref-equals.scala:6: error: method ==: (x$1: Any)Boolean does not take type parameters. - ("": AnyRef).==[Int] - ^ -t8219-any-any-ref-equals.scala:7: error: method ==: (x$1: Any)Boolean does not take type parameters. - ("": Object).==[Int] - ^ -three errors found diff --git a/tests/untried/neg/t8219-any-any-ref-equals.scala b/tests/untried/neg/t8219-any-any-ref-equals.scala deleted file mode 100644 index f1b81fa734c1..000000000000 --- a/tests/untried/neg/t8219-any-any-ref-equals.scala +++ /dev/null @@ -1,8 +0,0 @@ -object Test { - // The error message tells us that AnyRef#== and Any#== are overloaded. - // A real class couldn't define such an overload, why do we allow AnyRef - // to do so? - "".==[Int] - ("": AnyRef).==[Int] - ("": Object).==[Int] -} diff --git a/tests/untried/neg/warn-unused-imports.check b/tests/untried/neg/warn-unused-imports.check deleted file mode 100644 index 36c6dd03c38d..000000000000 --- a/tests/untried/neg/warn-unused-imports.check +++ /dev/null @@ -1,33 +0,0 @@ -warn-unused-imports.scala:57: warning: Unused import - import p1.A // warn - ^ -warn-unused-imports.scala:62: warning: Unused import - import p1.{ A, B } // warn on A - ^ -warn-unused-imports.scala:67: warning: Unused import - import p1.{ A, B } // warn on both - ^ -warn-unused-imports.scala:67: warning: Unused import - import p1.{ A, B } // warn on both - ^ -warn-unused-imports.scala:73: warning: Unused import - import c._ // warn - ^ -warn-unused-imports.scala:78: warning: Unused import - import p1._ // warn - ^ -warn-unused-imports.scala:85: warning: Unused import - import c._ // warn - ^ -warn-unused-imports.scala:91: warning: Unused import - import p1.c._ // warn - ^ -warn-unused-imports.scala:98: warning: Unused import - import p1._ // warn - ^ -warn-unused-imports.scala:118: warning: Unused import - import p1.A // warn - ^ -error: No warnings can be incurred under -Xfatal-warnings. -10 warnings found -one error found diff --git a/tests/untried/neg/warn-unused-imports.flags b/tests/untried/neg/warn-unused-imports.flags deleted file mode 100644 index 24db705df164..000000000000 --- a/tests/untried/neg/warn-unused-imports.flags +++ /dev/null @@ -1 +0,0 @@ --Xfatal-warnings -Ywarn-unused-import diff --git a/tests/untried/neg/warn-unused-imports.scala b/tests/untried/neg/warn-unused-imports.scala deleted file mode 100644 index 4b1c1183dcba..000000000000 --- a/tests/untried/neg/warn-unused-imports.scala +++ /dev/null @@ -1,125 +0,0 @@ -class Bippo { - def length: Int = 123 - class Tree -} - -package object p1 { - class A - implicit class B(val s: String) { def bippy = s } - val c: Bippo = new Bippo - type D = String -} -package object p2 { - class A - implicit class B(val s: String) { def bippy = s } - val c: Bippo = new Bippo - type D = Int -} - -trait NoWarn { - { - import p1._ // no warn - println("abc".bippy) - } - - { - import p1._ // no warn - println(new A) - } - - { - import p1.B // no warn - println("abc".bippy) - } - - { - import p1._ // no warn - import c._ // no warn - println(length) - } - - { - import p1._ // no warn - import c._ // no warn - val x: Tree = null - println(x) - } - - { - import p1.D // no warn - val x: D = null - println(x) - } -} - -trait Warn { - { - import p1.A // warn - println(123) - } - - { - import p1.{ A, B } // warn on A - println("abc".bippy) - } - - { - import p1.{ A, B } // warn on both - println(123) - } - - { - import p1._ // no warn (technically this could warn, but not worth the effort to unroll unusedness transitively) - import c._ // warn - println(123) - } - - { - import p1._ // warn - println(123) - } - - { - class Tree - import p1._ // no warn - import c._ // warn - val x: Tree = null - println(x) - } - - { - import p1.c._ // warn - println(123) - } -} - -trait Nested { - { - import p1._ // warn - trait Warn { // warn about unused local trait for good measure - import p2.* - println(new A) - println("abc".bippy) - } - println("") - } - - { - import p1._ // no warn - trait NoWarn { - import p2.B // no warn - println("abc".bippy) - println(new A) - } - println(new NoWarn { }) - } - - { - import p1.A // warn - trait Warn { - import p2.A - println(new A) - } - println(new Warn { }) - } -} diff --git a/tests/untried/neg/warn-unused-privates.check b/tests/untried/neg/warn-unused-privates.check deleted file mode 100644 index d012869c934a..000000000000 --- a/tests/untried/neg/warn-unused-privates.check +++ /dev/null @@ -1,66 +0,0 @@ -warn-unused-privates.scala:2: warning: private constructor in class Bippy is never used - private def this(c: Int) = this(c, c) // warn - ^ -warn-unused-privates.scala:4: warning: private method in class Bippy is never used - private def boop(x: Int) = x+a+b // warn - ^ -warn-unused-privates.scala:6: warning: private val in class Bippy is never used - final private val MILLIS2: Int = 1000 // warn - ^ -warn-unused-privates.scala:13: warning: private val in object Bippy is never used - private val HEY_INSTANCE: Int = 1000 // warn - ^ -warn-unused-privates.scala:35: warning: private val in class Boppy is never used - private val hummer = "def" // warn - ^ -warn-unused-privates.scala:42: warning: private var in trait Accessors is never used - private var v1: Int = 0 // warn - ^ -warn-unused-privates.scala:42: warning: private setter in trait Accessors is never used - private var v1: Int = 0 // warn - ^ -warn-unused-privates.scala:43: warning: private setter in trait Accessors is never used - private var v2: Int = 0 // warn, never set - ^ -warn-unused-privates.scala:44: warning: private var in trait Accessors is never used - private var v3: Int = 0 // warn, never got - ^ -warn-unused-privates.scala:56: warning: private default argument in trait DefaultArgs is never used - private def bippy(x1: Int, x2: Int = 10, x3: Int = 15): Int = x1 + x2 + x3 - ^ -warn-unused-privates.scala:56: warning: private default argument in trait DefaultArgs is never used - private def bippy(x1: Int, x2: Int = 10, x3: Int = 15): Int = x1 + x2 + x3 - ^ -warn-unused-privates.scala:67: warning: local var in method f0 is never used - var x = 1 // warn - ^ -warn-unused-privates.scala:74: warning: local val in method f1 is never used - val b = new Outer // warn - ^ -warn-unused-privates.scala:84: warning: private object in object Types is never used - private object Dongo { def f = this } // warn - ^ -warn-unused-privates.scala:94: warning: local object in method l1 is never used - object HiObject { def f = this } // warn - ^ -warn-unused-privates.scala:78: warning: local var x in method f2 is never set - it could be a val - var x = 100 // warn about it being a var - ^ -warn-unused-privates.scala:85: warning: private class Bar1 in object Types is never used - private class Bar1 // warn - ^ -warn-unused-privates.scala:87: warning: private type Alias1 in object Types is never used - private type Alias1 = String // warn - ^ -warn-unused-privates.scala:95: warning: local class Hi is never used - class Hi { // warn - ^ -warn-unused-privates.scala:99: warning: local class DingDongDoobie is never used - class DingDongDoobie // warn - ^ -warn-unused-privates.scala:102: warning: local type OtherThing is never used - type OtherThing = String // warn - ^ -error: No warnings can be incurred under -Xfatal-warnings. -21 warnings found -one error found diff --git a/tests/untried/neg/warn-unused-privates.flags b/tests/untried/neg/warn-unused-privates.flags deleted file mode 100644 index 25474aefb362..000000000000 --- a/tests/untried/neg/warn-unused-privates.flags +++ /dev/null @@ -1 +0,0 @@ --Ywarn-unused -Xfatal-warnings diff --git a/tests/untried/neg/warn-unused-privates.scala b/tests/untried/neg/warn-unused-privates.scala deleted file mode 100644 index 64e7679f37ac..000000000000 --- a/tests/untried/neg/warn-unused-privates.scala +++ /dev/null @@ -1,105 +0,0 @@ -class Bippy(a: Int, b: Int) { - private def this(c: Int) = this(c, c) // warn - private def bippy(x: Int): Int = bippy(x) // TODO: could warn - private def boop(x: Int) = x + a + b // warn - final private val MILLIS1 = 2000 // no warn, might have been inlined - final private val MILLIS2: Int = 1000 // warn - final private val HI_COMPANION: Int = 500 // no warn, accessed from companion - def hi() = Bippy.HI_INSTANCE -} -object Bippy { - def hi(x: Bippy) = x.HI_COMPANION - private val HI_INSTANCE: Int = 500 // no warn, accessed from instance - private val HEY_INSTANCE: Int = 1000 // warn -} - -class A(val msg: String) -class B1(msg: String) extends A(msg) -class B2(msg0: String) extends A(msg0) -class B3(msg0: String) extends A("msg") - -/*** Early defs warnings disabled primarily due to SI-6595. - * The test case is here to assure we aren't issuing false positives; - * the ones labeled "warn" don't warn. - ***/ -class Boppy extends { - private val hmm: String = "abc" // no warn, used in early defs - private val hom: String = "def" // no warn, used in body - private final val him = "ghi" // no warn, might have been (was) inlined - final val him2 = "ghi" // no warn, same - final val himinline = him - private val hum: String = "jkl" // warn - final val ding = hmm.length -} with Mutable { - val dinger = hom - private val hummer = "def" // warn - - private final val bum = "ghi" // no warn, might have been (was) inlined - final val bum2 = "ghi" // no warn, same -} - -trait Accessors { - private var v1: Int = 0 // warn - private var v2: Int = 0 // warn, never set - private var v3: Int = 0 // warn, never got - private var v4: Int = 0 // no warn - - def bippy(): Int = { - v3 = 5 - v4 = 6 - v2 + v4 - } -} - -trait DefaultArgs { - // warn about default getters for x2 and x3 - private def bippy(x1: Int, x2: Int = 10, x3: Int = 15): Int = x1 + x2 + x3 - - def boppy() = bippy(5, 100, 200) -} - -class Outer { - class Inner -} - -trait Locals { - def f0 = { - var x = 1 // warn - var y = 2 - y = 3 - y + y - } - def f1 = { - val a = new Outer // no warn - val b = new Outer // warn - new a.Inner - } - def f2 = { - var x = 100 // warn about it being a var - x - } -} - -object Types { - private object Dongo { def f = this } // warn - private class Bar1 // warn - private class Bar2 // no warn - private type Alias1 = String // warn - private type Alias2 = String // no warn - def bippo = (new Bar2).toString - - def f(x: Alias2) = x.length - - def l1() = { - object HiObject { def f = this } // warn - class Hi { // warn - def f1: Hi = new Hi - def f2(x: Hi) = x - } - class DingDongDoobie // warn - class Bippy // no warn - type Something = Bippy // no warn - type OtherThing = String // warn - (new Bippy): Something - } -} diff --git a/tests/untried/neg/wellkinded_app.check b/tests/untried/neg/wellkinded_app.check deleted file mode 100644 index d57a0e4b5648..000000000000 --- a/tests/untried/neg/wellkinded_app.check +++ /dev/null @@ -1,4 +0,0 @@ -wellkinded_app.scala:3: error: x does not take type parameters - type t = x[x] - ^ -one error found diff --git a/tests/untried/neg/wellkinded_app.scala b/tests/untried/neg/wellkinded_app.scala deleted file mode 100644 index 7fa3f95a9848..000000000000 --- a/tests/untried/neg/wellkinded_app.scala +++ /dev/null @@ -1,4 +0,0 @@ -// test well-kindedness checks -class WellKinded[x] { - type t = x[x] -} diff --git a/tests/untried/neg/wellkinded_app2.check b/tests/untried/neg/wellkinded_app2.check deleted file mode 100644 index 20a177ea590e..000000000000 --- a/tests/untried/neg/wellkinded_app2.check +++ /dev/null @@ -1,4 +0,0 @@ -wellkinded_app2.scala:3: error: s does not take type parameters - val foo: s[Int] - ^ -one error found diff --git a/tests/untried/neg/wellkinded_app2.scala b/tests/untried/neg/wellkinded_app2.scala deleted file mode 100644 index 7bb4c87f6b17..000000000000 --- a/tests/untried/neg/wellkinded_app2.scala +++ /dev/null @@ -1,4 +0,0 @@ -// test well-kindedness checks -class WellKinded[s <: Throwable] { - val foo: s[Int] -} diff --git a/tests/untried/neg/wellkinded_bounds.check b/tests/untried/neg/wellkinded_bounds.check deleted file mode 100644 index 806eb09a76d7..000000000000 --- a/tests/untried/neg/wellkinded_bounds.check +++ /dev/null @@ -1,4 +0,0 @@ -wellkinded_bounds.scala:2: error: type List takes type parameters -class WellKindedWrongSyntax[s <: List] { // must be s[x] <: List[x] - ^ -one error found diff --git a/tests/untried/neg/wellkinded_bounds.scala b/tests/untried/neg/wellkinded_bounds.scala deleted file mode 100644 index cfa5e74c115f..000000000000 --- a/tests/untried/neg/wellkinded_bounds.scala +++ /dev/null @@ -1,3 +0,0 @@ -// test well-kindedness checks -- syntax error -class WellKindedWrongSyntax[s <: List] { // must be s[x] <: List[x] -} diff --git a/tests/untried/neg/wellkinded_wrongarity.check b/tests/untried/neg/wellkinded_wrongarity.check deleted file mode 100644 index b9f033b4536b..000000000000 --- a/tests/untried/neg/wellkinded_wrongarity.check +++ /dev/null @@ -1,4 +0,0 @@ -wellkinded_wrongarity.scala:5: error: Tuple2 takes two type parameters, expected: one -object mp extends Monad[Tuple2] - ^ -one error found diff --git a/tests/untried/neg/wellkinded_wrongarity.scala b/tests/untried/neg/wellkinded_wrongarity.scala deleted file mode 100644 index 39c7601d53a3..000000000000 --- a/tests/untried/neg/wellkinded_wrongarity.scala +++ /dev/null @@ -1,5 +0,0 @@ -// test well-kindedness checks -- arity error - -class Monad[m[x]] - -object mp extends Monad[Tuple2] diff --git a/tests/untried/neg/wellkinded_wrongarity2.check b/tests/untried/neg/wellkinded_wrongarity2.check deleted file mode 100644 index 922f73381e29..000000000000 --- a/tests/untried/neg/wellkinded_wrongarity2.check +++ /dev/null @@ -1,13 +0,0 @@ -wellkinded_wrongarity2.scala:5: error: String takes no type parameters, expected: one -trait ms1 extends Monad[String] // wrong - ^ -wellkinded_wrongarity2.scala:6: error: t takes no type parameters, expected: one -trait ms2[t] extends Monad[t] // wrong - ^ -wellkinded_wrongarity2.scala:7: error: m[t] takes no type parameters, expected: one -trait ms3[m[_], t] extends Monad[m[t]] // wrong -- added to check regression on bug - ^ -wellkinded_wrongarity2.scala:12: error: type m takes type parameters -trait Bar2[m[_]] extends Foo[m] // check that m is properly recognized as kind *->*, while * is expected - ^ -four errors found diff --git a/tests/untried/neg/wellkinded_wrongarity2.scala b/tests/untried/neg/wellkinded_wrongarity2.scala deleted file mode 100644 index aac617bd8770..000000000000 --- a/tests/untried/neg/wellkinded_wrongarity2.scala +++ /dev/null @@ -1,12 +0,0 @@ -// test well-kindedness checks - -// expecting types of kind *->* -class Monad[m[x]] -trait ms1 extends Monad[String] // wrong -trait ms2[t] extends Monad[t] // wrong -trait ms3[m[_], t] extends Monad[m[t]] // wrong -- added to check regression on bug - -// expecting types of kind * -trait Foo[x] -trait Bar1[m[_]] extends Foo[m[Int]] // check that m[Int] is properly recognized as kind-* -trait Bar2[m[_]] extends Foo[m] // check that m is properly recognized as kind *->*, while * is expected diff --git a/tests/warn/deprecated-origin-verbose.check b/tests/warn/deprecated-origin-verbose.check index e67efaf8668d..db0ee26fd779 100644 --- a/tests/warn/deprecated-origin-verbose.check +++ b/tests/warn/deprecated-origin-verbose.check @@ -2,13 +2,13 @@ 12 | class D extends C // warn | ^ | class C in package p is deprecated since 1.0: Old style -Matching filters for @nowarn or -Wconf: - - cat=deprecation - - origin=p.C + |Matching filters for @nowarn or -Wconf: + | - cat=deprecation + | - origin=p.C -- Deprecation Warning: tests/warn/deprecated-origin-verbose.scala:13:20 ----------------------------------------------- 13 | class Oil extends Crude // warn | ^^^^^ | class Crude in package p is deprecated since 1.0: Bad style -Matching filters for @nowarn or -Wconf: - - cat=deprecation - - origin=p.Crude + |Matching filters for @nowarn or -Wconf: + | - cat=deprecation + | - origin=p.Crude diff --git a/tests/warn/deprecated-override.check b/tests/warn/deprecated-override.check new file mode 100644 index 000000000000..ba30cbbc3fac --- /dev/null +++ b/tests/warn/deprecated-override.check @@ -0,0 +1,20 @@ +-- Deprecation Warning: tests/warn/deprecated-override.scala:13:15 ----------------------------------------------------- +13 | override def f = 2 // warn + | ^ + | overriding method f in class B of type => Int is deprecated; + | method f of type => Int should be removed or renamed. +-- Deprecation Warning: tests/warn/deprecated-override.scala:14:15 ----------------------------------------------------- +14 | override def g() = println("goodbye, cruel world") // warn + | ^ + | overriding method g in class B of type (): Unit is deprecated since now; + | Please don't. +-- Deprecation Warning: tests/warn/deprecated-override.scala:20:7 ------------------------------------------------------ +20 |object E extends B, D // warn + | ^ + | overriding method f in class B of type => Int is deprecated; + | method f in trait D of type => Int should be removed or renamed. +-- Deprecation Warning: tests/warn/deprecated-override.scala:30:15 ----------------------------------------------------- +30 | override def f = "hello, world" // warn + | ^ + | overriding method f in trait X of type => String is deprecated; + | method f of type => String should be removed or renamed. diff --git a/tests/warn/deprecated-override.scala b/tests/warn/deprecated-override.scala index 2d0b0ceb4aea..763256e89e4d 100644 --- a/tests/warn/deprecated-override.scala +++ b/tests/warn/deprecated-override.scala @@ -1,15 +1,30 @@ -//> using options -source future -deprecation +//> using options -deprecation trait A: def f: Int + def g(): Unit class B extends A: @deprecatedOverriding def f = 1 + @deprecatedOverriding("Please don't.", since = "now") + def g() = println("hello, world") class C extends B: override def f = 2 // warn + override def g() = println("goodbye, cruel world") // warn trait D extends A: override def f = 3 + override def g() = () -object E extends B, D // warn \ No newline at end of file +object E extends B, D // warn + +@deprecated +class F extends B: + override def g() = println("goodbye, cruel world") // nowarn + +trait X[T]: + @deprecatedOverriding def f: T + +class Y extends X[String]: + override def f = "hello, world" // warn diff --git a/tests/pos/ext-override.scala b/tests/warn/ext-override.scala similarity index 83% rename from tests/pos/ext-override.scala rename to tests/warn/ext-override.scala index d08439e13c9a..32277083bd60 100644 --- a/tests/pos/ext-override.scala +++ b/tests/warn/ext-override.scala @@ -1,4 +1,4 @@ -//> using options -Xfatal-warnings +//> using options -Werror trait Foo[T]: extension (x: T) diff --git a/tests/warn/i12460.check b/tests/warn/i12460.check new file mode 100644 index 000000000000..ba44bed1a18f --- /dev/null +++ b/tests/warn/i12460.check @@ -0,0 +1,24 @@ +-- [E208] Potential Issue Warning: tests/warn/i12460.scala:3:23 -------------------------------------------------------- +3 |extension (s: String = "hello, world") def invert = s.reverse.toUpperCase // warn + | ^ + | Extension method invert should not have a default argument for its receiver. + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | The receiver cannot be omitted when an extension method is invoked as a selection. + | A default argument for that parameter would never be used in that case. + | An extension method can be invoked as a regular method, but if that is the intended usage, + | it should not be defined as an extension. + --------------------------------------------------------------------------------------------------------------------- +-- [E208] Potential Issue Warning: tests/warn/i12460.scala:5:37 -------------------------------------------------------- +5 |extension (using String)(s: String = "hello, world") def revert = s.reverse.toUpperCase // warn + | ^ + | Extension method revert should not have a default argument for its receiver. + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | The receiver cannot be omitted when an extension method is invoked as a selection. + | A default argument for that parameter would never be used in that case. + | An extension method can be invoked as a regular method, but if that is the intended usage, + | it should not be defined as an extension. + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/warn/i12460.scala b/tests/warn/i12460.scala new file mode 100644 index 000000000000..0e37c6aa7de7 --- /dev/null +++ b/tests/warn/i12460.scala @@ -0,0 +1,9 @@ +//> using options -explain -Ystop-after:refchecks + +extension (s: String = "hello, world") def invert = s.reverse.toUpperCase // warn + +extension (using String)(s: String = "hello, world") def revert = s.reverse.toUpperCase // warn + +extension (s: String) + def divert(m: String = "hello, world") = (s+m).reverse.toUpperCase // ok + def divertimento(using String)(m: String = "hello, world") = (s+m).reverse.toUpperCase // ok diff --git a/tests/patmat/i14579.scala b/tests/warn/i14579.scala similarity index 82% rename from tests/patmat/i14579.scala rename to tests/warn/i14579.scala index f20ecde2292b..ec4b39d594c5 100644 --- a/tests/patmat/i14579.scala +++ b/tests/warn/i14579.scala @@ -4,7 +4,7 @@ trait A { private trait X2 extends X sealed trait X3 extends X - def f(x: X) = x match { + def f(x: X) = x match { // warn case _: X1 => 0 } } \ No newline at end of file diff --git a/tests/pos/i15226.scala b/tests/warn/i15226.scala similarity index 100% rename from tests/pos/i15226.scala rename to tests/warn/i15226.scala diff --git a/tests/warn/i15503a.scala b/tests/warn/i15503a.scala index df8691c21a13..795873b53e73 100644 --- a/tests/warn/i15503a.scala +++ b/tests/warn/i15503a.scala @@ -1,5 +1,4 @@ -//> using options -Wunused:imports - +//> using options -Wunused:imports -Wconf:origin=Suppressed.*:s object FooUnused: import collection.mutable.Set // warn @@ -40,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. @@ -68,9 +71,9 @@ object InlineChecks: inline def getSet = Set(1) object InlinedBar: - import collection.mutable.Set // ok + 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: @@ -86,34 +89,36 @@ object InnerMostCheck: val a = Set(1) object IgnoreExclusion: - import collection.mutable.{Set => _} // OK - import collection.mutable.{Map => _} // OK + import collection.mutable.{Map => _, Set => _, *} // OK?? import collection.mutable.{ListBuffer} // warn 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 testsamepackageimport: - package p { +package nestedpackageimport: + package p: class C - } - - package p { - import p._ // warn - package q { - class U { + package p: + package q: + import p.* // warn + class U: def f = new C - } - } - } -// ----------------------- +package unnestedpackageimport: + package p: + class C + package p.q: + import p.* // nowarn + class U: + def f = new C + +package redundancy: + object redundant: + def f = 42 + import redundancy.* // warn superseded by def in scope + class R: + def g = redundant.f package testpackageimport: package a: @@ -213,7 +218,8 @@ package testImportsInImports: package c: import a.b // OK import b.x // OK - val y = x + import b.x as z // OK + val y = x + z //------------------------------------- package testOnOverloadedMethodsImports: @@ -265,4 +271,198 @@ package foo.test.typeapply.hklamdba.i16680: import foo.IO // OK def f[F[_]]: String = "hello" - def go = f[IO] \ No newline at end of file + def go = f[IO] + +object Selections: + def f(list: List[Int]): Int = + import list.{head => first} // OK + first + + def f2(list: List[Int]): Int = + import list.head // OK + head + + def f3(list: List[Int]): Int = + import list.head // warn + list.head + + object N: + val ns: List[Int] = Nil + + def g(): Int = + import N.ns // OK + ns.head +end Selections + +object `more nestings`: + object Outer: + object Inner: + val thing = 42 + def j() = + import Inner.thing // warn + thing + def k() = + import Inner.thing // warn + Inner.thing + + object Thing: + object Inner: + val thing = 42 + import Inner.thing // warn + def j() = + thing + def k() = + Inner.thing + +object Suppressed: + val suppressed = 42 +object Suppressing: + import Suppressed.* // no warn, see options + def f = 42 + +package i22692: + import javax.swing.* + import javax.swing.event as swingEvent // no warn, regression test for warning in 3.6 + + type b = AbstractButton + type t = swingEvent.AncestorListener + +package ancient: + package p { + class Bippo { + def length: Int = 123 + class Tree + } + } + + package object p1 { + import p._ + class A + implicit class B(val s: String) { def bippy = s } + val c: Bippo = new Bippo + type D = String + } + package object p2 { + import p._ + class A + implicit class B(val s: String) { def bippy = s } + val c: Bippo = new Bippo + type D = Int + } + + trait NoWarn { + { + import p1._ // no warn + println("abc".bippy) + } + + { + import p1._ // no warn + println(new A) + } + + { + import p1.B // no warn + println("abc".bippy) + } + + { + import p1._ // no warn + import c._ // no warn + println(length) + } + + { + import p1._ // no warn + import c._ // no warn + val x: Tree = null + println(x) + } + + { + import p1.D // no warn + val x: D = null + println(x) + } + } + + trait Warn { + { + import p1.A // warn + println(123) + } + + { + import p1.{ A, B } // warn on A + println("abc".bippy) + } + + { + import p1.{ A, B } //warn // warn on both + println(123) + } + + { + import p1._ // no warn (technically this could warn, but not worth the effort to unroll unusedness transitively) + import c._ // warn + println(123) + } + + { + import p1._ // warn + println(123) + } + + { + class Tree + import p1._ // no warn + import c._ // warn + val x: Tree = null + println(x) + } + + { + import p1.c._ // warn + println(123) + } + } + + trait Nested { + { + import p1._ // warn + trait Warn { + import p2.* + println(new A) + println("abc".bippy) + } + println("") + } + + { + import p1._ // no warn + trait NoWarn { + import p2.B // no warn + println("abc".bippy) + println(new A) + } + println(new NoWarn { }) + } + + { + import p1.A // warn + trait Warn { + import p2.A + println(new A) + } + println(new Warn { }) + } + } +end ancient + +object `i22970 assign lhs was ignored`: + object X: + var global = 0 + object Main: + import X.global // no warn + def main(args: Array[String]): Unit = + global = 1 diff --git a/tests/warn/i15503b.scala b/tests/warn/i15503b.scala index 7ab86026ff00..0ea110f833f1 100644 --- a/tests/warn/i15503b.scala +++ b/tests/warn/i15503b.scala @@ -117,7 +117,7 @@ package foo.scala2.tests: object Types { def l1() = { - object HiObject { def f = this } // OK + object HiObject { def f = this } // warn class Hi { // warn def f1: Hi = new Hi def f2(x: Hi) = x @@ -141,4 +141,4 @@ package test.foo.twisted.i16682: } isInt - def f = myPackage("42") \ No newline at end of file + def f = myPackage("42") diff --git a/tests/warn/i15503c.scala b/tests/warn/i15503c.scala index a813329da89b..a78b9c837dbd 100644 --- a/tests/warn/i15503c.scala +++ b/tests/warn/i15503c.scala @@ -1,4 +1,4 @@ -//> using options -Wunused:privates -source:3.3 +//> using options -Wunused:privates -source:3.3 trait C class A: @@ -33,17 +33,22 @@ class A: var w = 2 // OK package foo.test.constructors: - case class A private (x:Int) // OK + case class A private (x: Int) // OK class B private (val x: Int) // OK - class C private (private val x: Int) // warn + object B { def default = B(42) } + class C private (private val xy: Int) // warn + object C { def default = C(42) } class D private (private val x: Int): // OK def y = x + object D { def default = D(42) } class E private (private var x: Int): // warn not set def y = x + object E { def default = E(42) } class F private (private var x: Int): // OK def y = x = 3 x + object F { def default = F(42) } package test.foo.i16682: object myPackage: @@ -55,4 +60,20 @@ package test.foo.i16682: case _ => println("NaN") } - def f = myPackage.isInt("42") \ No newline at end of file + def f = myPackage.isInt("42") + +object LazyVals: + import java.util.concurrent.CountDownLatch + + // This trait extends Serializable to fix #16806 that caused a race condition + sealed trait LazyValControlState extends Serializable + + final class Waiting extends CountDownLatch(1), LazyValControlState: + private def writeReplace(): Any = null + +package i24235: + object Test: + private case class Unused() // warn + private class Regular // nowarn + private object Regular: // warn + def r = Regular() // usage diff --git a/tests/warn/i15503d.scala b/tests/warn/i15503d.scala index 9a0ba9b2f8dc..948694df5b10 100644 --- a/tests/warn/i15503d.scala +++ b/tests/warn/i15503d.scala @@ -1,5 +1,4 @@ -//> using options -Wunused:unsafe-warn-patvars -// todo : change to :patvars +//> using options -Wunused:patvars sealed trait Calc sealed trait Const extends Calc @@ -8,23 +7,120 @@ case class S(pred: Const) extends Const case object Z extends Const val a = Sum(S(S(Z)),Z) match { - case Sum(a,Z) => Z // warn + case Sum(x,Z) => Z // warn // case Sum(a @ _,Z) => Z // todo : this should pass in the future - case Sum(a@S(_),Z) => Z // warn - case Sum(a@S(_),Z) => a // warn unreachable - case Sum(a@S(b@S(_)), Z) => a // warn - case Sum(a@S(b@S(_)), Z) => a // warn - case Sum(a@S(b@(S(_))), Z) => Sum(a,b) // warn unreachable + case Sum(x@S(_),Z) => Z // warn + case Sum(x@S(_),Z) => x // warn unreachable + case Sum(x@S(y@S(_)), Z) => x // warn + case Sum(x@S(y@S(_)), Z) => x // warn + case Sum(x@S(y@(S(_))), Z) => Sum(x,y) // warn unreachable case Sum(_,_) => Z // OK case _ => Z // warn unreachable } -// todo : This should pass in the future -// val b = for { -// case Some(x) <- Option(Option(1)) -// } println(s"$x") +case class K(i: Int, j: Int) -// todo : This should *NOT* pass in the future -// val c = for { -// case Some(x) <- Option(Option(1)) -// } println(s"hello world") \ No newline at end of file +class C(c0: Option[Int], k0: K): + private val Some(c) = c0: @unchecked // warn valdef from pattern + private val K(i, j) = k0 // nowarn (name of case class element is nowarn) + val K(v, w) = k0 // nowarn nonprivate + private val K(r, s) = k0 // warn // warn valdefs from pattern + def f(x: Option[Int]) = x match + case Some(y) => true // warn Bind in pattern + case _ => false + def g(ns: List[Int]) = + for x <- ns do println() // warn valdef function param from for + def g1(ns: List[Int]) = + for x <- ns do println(x) // x => println(x) + def h(ns: List[Int]) = + for x <- ns; y = x + 1 // warn tupling from for; x is used, y is unused + do println() + def k(x: Option[K]) = + x match + case Some(K(i, j)) => // nowarn canonical names + case _ => + + val m = Map( + "first" -> Map((true, 1), (false, 2), (true, 3)), + "second" -> Map((true, 1), (false, 2), (true, 3)), + ) + def guardedUse = + m.map: (a, m1) => + for (status, lag) <- m1 if status + yield (a, status, lag) + def guardedUseOnly = + m.map: (a, m1) => + for (status, lag) <- m1 if status + yield (a, lag) + def guardedUseMissing = + m.map: (a, m1) => + for (status, lag) <- m1 // warn + yield (a, lag) + def flatGuardedUse = + for (a, m1) <- m; (status, lag) <- m1 if status + yield (a, status, lag) + def leading = + for _ <- List("42"); i = 1; _ <- List("0", "27")(i) + yield () + def optional = + for case Some(x) <- List(Option(42)) + yield x + def nonoptional = + for case Some(x) <- List(Option(42)) // warn + yield 27 + def optionalName = + for case Some(value) <- List(Option(42)) + yield 27 + +class Wild: + def f(x: Any) = + x match + case _: Option[?] => true + case _ => false + +def untuple(t: Tuple) = + t match + case Tuple() => + case h *: t => // no warn canonical names taken from tuple element types, (Head, Tail) -> (head, tail) + //case head *: tail => // no warn canonical names taken from tuple element types, (Head, Tail) -> (head, tail) + +// empty case class: +// def equals(other) = other match { case other => true } // exonerated name +object i15967: + sealed trait A[-Z] + final case class B[Y]() extends A[Y] + +object `patvar is assignable`: + var (i, j) = (42, 27) // no warn nonprivate + j += 1 + println((i, j)) + +object `privy patvar is assignable`: + private var (i, j) = (42, 27) // warn + j += 1 + println((i, j)) + +object `local patvar is assignable`: + def f() = + var (i, j) = (42, 27) // warn + j += 1 + println((i, j)) + +object `mutable patvar in for`: + def f(xs: List[Int]) = + for x <- xs; y = x + 1 if y > 10 yield + var z :: Nil = y :: Nil: @unchecked // warn + z + 10 + +class `unset var requires -Wunused`: + private var i = 0 // no warn as we didn't ask for it + def f = println(i) + +class `i22743 lazy vals are defs`: + def f: (Int, String) = (42, "hello, world") + lazy val (i, s) = f // no warn because def is neither local nor private + val (j, t) = f // existing no warn for val with attachment + private lazy val (k, u) = f // warn // warn a warning so nice, they warn it twice + +def `i25100 type vars are also pat vars` = + ??? match { case _: List[t] => ??? } diff --git a/tests/warn/i15503e.scala b/tests/warn/i15503e.scala index 46d73a4945cd..0a7fadc7189a 100644 --- a/tests/warn/i15503e.scala +++ b/tests/warn/i15503e.scala @@ -1,4 +1,6 @@ -//> using options -Wunused:explicits +//> using options -Wunused:explicits + +import annotation.* object Foo { /* This goes around the "trivial method" detection */ @@ -31,16 +33,17 @@ package scala3main: package foo.test.lambda.param: val default_val = 1 val a = (i: Int) => i // OK - val b = (i: Int) => default_val // OK + val b = (i: Int) => default_val // warn val c = (_: Int) => default_val // OK package foo.test.trivial: /* A twisted test from Scala 2 */ - class C { + class C(val value: Int) { def answer: 42 = 42 object X private def g0(x: Int) = ??? // OK private def f0(x: Int) = () // OK + private def f00(x: Int) = {} // OK private def f1(x: Int) = throw new RuntimeException // OK private def f2(x: Int) = 42 // OK private def f3(x: Int): Option[Int] = None // OK @@ -50,13 +53,16 @@ package foo.test.trivial: private def f7(x: Int) = Y // OK private def f8(x: Int): List[C] = Nil // OK private def f9(x: Int): List[Int] = List(1,2,3,4) // warn - private def foo:Int = 32 // OK + private def foo: Int = 32 // OK private def f77(x: Int) = foo // warn + private def self(x: Int): C = this // no warn + private def unwrap(x: Int): Int = value // no warn } object Y package foo.test.i16955: - class S(var r: String) // OK + class S(var rrr: String) // OK + class T(rrr: String) // warn package foo.test.i16865: trait Foo: @@ -64,7 +70,26 @@ package foo.test.i16865: trait Bar extends Foo object Ex extends Bar: - def fn(a: Int, b: Int): Int = b + 3 // OK + def fn(a: Int, b: Int): Int = b + 3 // no warn (override) object Ex2 extends Bar: - override def fn(a: Int, b: Int): Int = b + 3 // OK \ No newline at end of file + override def fn(a: Int, b: Int): Int = b + 3 // no warn (override) + +final class alpha(externalName: String) extends StaticAnnotation // no warn annotation arg + +object Unimplemented: + import compiletime.* + inline def f(inline x: Int | Double): Unit = error("unimplemented") // no warn param of trivial method + +def `trivially wrapped`(x: String): String ?=> String = "hello, world" // no warn param of trivial method + +object UnwrapTyped: + import compiletime.error + inline def requireConst(inline x: Boolean | Byte | Short | Int | Long | Float | Double | Char | String): Unit = + error("Compiler bug: `requireConst` was not evaluated by the compiler") + + transparent inline def codeOf(arg: Any): String = + error("Compiler bug: `codeOf` was not evaluated by the compiler") + +object `default usage`: + def f(i: Int)(j: Int = i * 2) = j // ~warn~ I guess (see tests/warn/i23349.scala) diff --git a/tests/warn/i15503f.scala b/tests/warn/i15503f.scala index ccf0b7e74065..684bee3313e8 100644 --- a/tests/warn/i15503f.scala +++ b/tests/warn/i15503f.scala @@ -1,4 +1,4 @@ -//> using options -Wunused:implicits +//> using options -Wunused:implicits /* This goes around the "trivial method" detection */ val default_int = 1 @@ -6,9 +6,93 @@ val default_int = 1 object Xd { private def f1(a: Int) = a // OK private def f2(a: Int) = 1 // OK - private def f3(a: Int)(using Int) = a // OK - private def f4(a: Int)(using Int) = default_int // OK + private def f3(a: Int)(using Int) = a // warn + private def f4(a: Int)(using Int) = default_int // warn private def f6(a: Int)(using Int) = summon[Int] // OK private def f7(a: Int)(using Int) = summon[Int] + a // OK private def f8(a: Int)(using foo: Int) = a // warn + private def f9(a: Int)(using Int) = ??? // OK trivial + private def g1(a: Int)(implicit foo: Int) = a // warn } + +trait T +object T: + def hole(using T) = () + +class C(using T) // no warn marker trait is evidence only + +class D(using T): + def t = T.hole // nowarn + +object Example: + import scala.quoted.* + given OptionFromExpr[T](using Type[T], FromExpr[T]): FromExpr[Option[T]] with + def unapply(x: Expr[Option[T]])(using Quotes) = x match + case '{ Option[T](${Expr(y)}) } => Some(Option(y)) + case '{ None } => Some(None) + case '{ ${Expr(opt)} : Some[T] } => Some(opt) + case _ => None + +//absolving names on matches of quote trees requires consulting non-abstract types in QuotesImpl +object Unmatched: + import scala.quoted.* + def transform[T](e: Expr[T])(using Quotes): Expr[T] = + import quotes.reflect.* + def f(tree: Tree) = + tree match + case Ident(name) => + case _ => + e + +trait Ctx: + val state: Int +case class K(i: Int)(using val ctx: Ctx) // nowarn +class L(val i: Int)(using val ctx: Ctx) // nowarn +class M(val i: Int)(using ctx: Ctx) // warn + +package givens: + + trait X: + def doX: Int + + trait Y: + def doY: String + + trait Z + + given X with + def doX = 7 + + given (using X): Y with // warn protected param to given class + def doY = "7" + /* desugared. It is protected so that its type can be used in member defs without leaking. + * possibly it should be protected only for named parameters. + given class given_Y(using x$1: givens.X) extends Object(), givens.Y { + protected given val x$1: givens.X + def doY: String = "7" + } + final given def given_Y(using x$1: givens.X): givens.given_Y = + new givens.given_Y(using x$1)() + */ + + given namely (using x: X): Y with // warn protected param to given class + def doY = "8" + + def f(using => X) = println() // warn + def g(using => Z) = println() // nowarn marker trait +end givens + +object i22895: + trait Test[F[_], Ev] { + def apply[A, B](fa: F[A])(f: A => B)(using ev: Ev): F[B] + } + given testId: Test[[a] =>> a, Unit] = + new Test[[a] =>> a, Unit] { + def apply[A, B](fa: A)(f: A => B)(using ev: Unit): B = f(fa) // nowarn override + } + class C: + def f(using s: String) = s.toInt + class D(i: Int) extends C: + override def f(using String) = compute(i) // nowarn override + def g(using sss: String) = compute(i) // warn + def compute(i: Int) = i * 42 // returning a class param is deemed trivial, make it non-trivial diff --git a/tests/warn/i15503g.check b/tests/warn/i15503g.check new file mode 100644 index 000000000000..105df82e8a1a --- /dev/null +++ b/tests/warn/i15503g.check @@ -0,0 +1,28 @@ +-- [E198] Unused Symbol Warning: tests/warn/i15503g.scala:8:17 --------------------------------------------------------- +8 | private def f2(a: Int) = default_int // warn + | ^ + | unused explicit parameter +-- [E198] Unused Symbol Warning: tests/warn/i15503g.scala:9:31 --------------------------------------------------------- +9 | private def f3(a: Int)(using Int) = a // warn + | ^ + | unused implicit parameter +-- [E198] Unused Symbol Warning: tests/warn/i15503g.scala:10:17 -------------------------------------------------------- +10 | private def f4(a: Int)(using Int) = default_int // warn // warn + | ^ + | unused explicit parameter +-- [E198] Unused Symbol Warning: tests/warn/i15503g.scala:10:31 -------------------------------------------------------- +10 | private def f4(a: Int)(using Int) = default_int // warn // warn + | ^ + | unused implicit parameter +-- [E198] Unused Symbol Warning: tests/warn/i15503g.scala:11:17 -------------------------------------------------------- +11 | private def f6(a: Int)(using Int) = summon[Int] // warn + | ^ + | unused explicit parameter +-- [E198] Unused Symbol Warning: tests/warn/i15503g.scala:23:18 -------------------------------------------------------- +23 | def isAnIssue(y: A): Boolean = x == x // warn + | ^ + | unused explicit parameter in extension method isAnIssue +-- [E198] Unused Symbol Warning: tests/warn/i15503g.scala:29:30 -------------------------------------------------------- +29 | extension (s: String)(using Show) // warn not used in repeat + | ^ + | unused implicit parameter in extension method repeat diff --git a/tests/warn/i15503g.scala b/tests/warn/i15503g.scala index fbd9f3c1352c..f63f7ea73475 100644 --- a/tests/warn/i15503g.scala +++ b/tests/warn/i15503g.scala @@ -1,4 +1,4 @@ -//> using options -Wunused:params +//> using options -Wunused:params /* This goes around the "trivial method" detection */ object Foo { @@ -6,8 +6,8 @@ object Foo { private def f1(a: Int) = a // OK private def f2(a: Int) = default_int // warn - private def f3(a: Int)(using Int) = a // OK - private def f4(a: Int)(using Int) = default_int // warn + private def f3(a: Int)(using Int) = a // warn + private def f4(a: Int)(using Int) = default_int // warn // warn private def f6(a: Int)(using Int) = summon[Int] // warn private def f7(a: Int)(using Int) = summon[Int] + a // OK /* --- Trivial method check --- */ @@ -15,9 +15,17 @@ object Foo { private def g2(x: Int) = ??? // OK } -package foo.test.i17101: +object i17101: type Test[A] = A extension[A] (x: Test[A]) { // OK def value: A = x def causesIssue: Unit = println("oh no") - } \ No newline at end of file + def isAnIssue(y: A): Boolean = x == x // warn + } + +object i23125: + trait Show: + def show(s: String) = s + extension (s: String)(using Show) // warn not used in repeat + def echo = println(summon[Show].show(s)) + def repeat = s * 2 diff --git a/tests/warn/i15503h.scala b/tests/warn/i15503h.scala index 854693981488..a2bf47c843dd 100644 --- a/tests/warn/i15503h.scala +++ b/tests/warn/i15503h.scala @@ -1,4 +1,4 @@ -//> using options -Wunused:linted +//> using options -Wunused:linted import collection.mutable.Set // warn @@ -7,7 +7,7 @@ class A { val b = 2 // OK private def c = 2 // warn - def d(using x:Int): Int = b // ok + def d(using x: Int): Int = b // warn def e(x: Int) = 1 // OK def f = val x = 1 // warn @@ -15,6 +15,6 @@ class A { 3 def g(x: Int): Int = x match - case x:1 => 0 // OK + case x: 1 => 0 // OK case _ => 1 -} \ No newline at end of file +} diff --git a/tests/warn/i15503i.scala b/tests/warn/i15503i.scala index b7981e0e4206..5b64f037d443 100644 --- a/tests/warn/i15503i.scala +++ b/tests/warn/i15503i.scala @@ -1,4 +1,4 @@ -//> using options -Wunused:all +//> using options -Wunused:all -Wconf:name=InlinedAnonClassWarning:s import collection.mutable.{Map => MutMap} // warn import collection.mutable.Set // warn @@ -17,10 +17,12 @@ class A { private def c2 = 2 // OK def c3 = c2 - def d1(using x:Int): Int = default_int // ok - def d2(using x:Int): Int = x // OK + def d1(using x: Int): Int = default_int // warn param + def d2(using x: Int): Int = x // OK + def d3(using Int): Int = summon[Int] // OK + def d4(using Int): Int = default_int // warn - def e1(x: Int) = default_int // ok + def e1(x: Int) = default_int // warn param def e2(x: Int) = x // OK def f = val x = 1 // warn @@ -29,11 +31,15 @@ class A { def g = 4 // OK y + g - // todo : uncomment once patvars is fixed - // def g(x: Int): Int = x match - // case x:1 => 0 // ?error - // case x:2 => x // ?OK - // case _ => 1 // ?OK + def g(x: Int): Int = x match + case x: 1 => 0 // no warn same name as selector (for shadowing or unused) + case x: 2 => x // OK + case _ => 1 // OK + + def h(x: Int): Int = x match + case y: 1 => 0 // warn unused despite trivial type and RHS + case y: 2 => y // OK + case _ => 1 // OK } /* ---- CHECK scala.annotation.unused ---- */ @@ -44,7 +50,7 @@ package foo.test.scala.annotation: val default_int = 12 def a1(a: Int) = a // OK - def a2(a: Int) = default_int // ok + def a2(a: Int) = default_int // warn def a3(@unused a: Int) = default_int //OK @@ -82,8 +88,8 @@ package foo.test.i16678: def run = println(foo(number => number.toString, value = 5)) // OK println(foo(number => "", value = 5)) // warn - println(foo(func = number => "", value = 5)) // warn println(foo(func = number => number.toString, value = 5)) // OK + println(foo(func = number => "", value = 5)) // warn println(foo(func = _.toString, value = 5)) // OK package foo.test.possibleclasses: @@ -91,7 +97,7 @@ package foo.test.possibleclasses: k: Int, // OK private val y: Int // OK /* Kept as it can be taken from pattern */ )( - s: Int, + s: Int, // warn val t: Int, // OK private val z: Int // warn ) @@ -130,22 +136,22 @@ package foo.test.possibleclasses: package foo.test.possibleclasses.withvar: case class AllCaseClass( k: Int, // OK - private var y: Int // OK /* Kept as it can be taken from pattern */ + private var y: Int // warn unset )( - s: Int, - var t: Int, // OK - private var z: Int // warn + s: Int, // warn + var ttt: Int, // OK + private var zzz: Int // warn ) case class AllCaseUsed( k: Int, // OK - private var y: Int // OK + private var y: Int // warn unset )( s: Int, // OK - var t: Int, // OK global scope can be set somewhere else - private var z: Int // warn not set + var tt: Int, // OK global scope can be set somewhere else + private var zz: Int // warn not set ) { - def a = k + y + s + t + z + def a = k + y + s + tt + zz } class AllClass( @@ -199,14 +205,14 @@ package foo.test.i16877: package foo.test.i16926: def hello(): Unit = for { - i <- (0 to 10).toList + i <- (0 to 10).toList // warn patvar (a, b) = "hello" -> "world" // OK } yield println(s"$a $b") package foo.test.i16925: def hello = for { - i <- 1 to 2 if true + i <- 1 to 2 if true // OK _ = println(i) // OK } yield () @@ -247,7 +253,7 @@ package foo.test.i16679a: import scala.deriving.Mirror object CaseClassByStringName: inline final def derived[A](using inline A: Mirror.Of[A]): CaseClassByStringName[A] = - new CaseClassByStringName[A]: // warn + new CaseClassByStringName[A]: def name: String = A.toString object secondPackage: @@ -263,7 +269,7 @@ package foo.test.i16679b: object CaseClassName: import scala.deriving.Mirror inline final def derived[A](using inline A: Mirror.Of[A]): CaseClassName[A] = - new CaseClassName[A]: // warn + new CaseClassName[A]: def name: String = A.toString object Foo: @@ -279,7 +285,7 @@ package foo.test.i17156: package a: trait Foo[A] object Foo: - inline def derived[T]: Foo[T] = new Foo{} // warn + inline def derived[T]: Foo[T] = new Foo {} package b: import a.Foo @@ -313,3 +319,52 @@ package foo.test.i17117: val test = t1.test } } + +// manual testing of cached look-ups +package deeply: + object Deep: + def f(): Unit = + def g(): Unit = + def h(): Unit = + println(Deep) + println(Deep) + println(Deep) + h() + g() + override def toString = "man, that is deep!" +/* result cache saves before context traversal and import/member look-up +CHK object Deep +recur * 10 = context depth is 10 between reference and definition +CHK method println +recur = was 19 at predef where max root is 21 +CHK object Deep +recur = cached result +CHK method println +recur +CHK object Deep +recur +*/ + +package constructors: + class C private (i: Int): // warn param + def this() = this(27) + private def this(s: String) = this(s.toInt) // warn ctor + def c = new C(42) + private def f(i: Int) = i // warn overloaded member + private def f(s: String) = s + def g = f("hello") // use one of overloaded member + + class D private (i: Int): + private def this() = this(42) // no warn used by companion + def d = i + object D: + def apply(): D = new D() + +package reversed: // reverse-engineered + class C: + def c: scala.Int = 42 // Int marked used; lint does not look up .scala + class D: + def d: Int = 27 // Int is found in root import scala.* + class E: + import scala.* // no warn because root import (which is cached! by previous lookup) is lower precedence + def e: Int = 27 diff --git a/tests/warn/i15503j.scala b/tests/warn/i15503j.scala index fa30601d8960..62e3557fc0d3 100644 --- a/tests/warn/i15503j.scala +++ b/tests/warn/i15503j.scala @@ -1,4 +1,4 @@ -//> using options -Wunused:strict-no-implicit-warn +//> using options -Wunused:imports package foo.unused.strict.test: package a: @@ -7,15 +7,15 @@ package foo.unused.strict.test: val z: Int = 2 def f: Int = 3 package b: - import a.given // OK - import a._ // OK - import a.* // OK - import a.x // OK - import a.y // OK + import a.given // warn + import a._ // warn + import a.* // warn + import a.x // warn + import a.y // warn import a.z // warn import a.f // warn package c: - import a.given // OK + import a.given // warn import a.x // OK import a.y // OK import a.z // OK @@ -28,8 +28,8 @@ package foo.implicits.resolution: object A { implicit val x: X = new X } object B { implicit val y: Y = new Y } class C { - import A._ // OK - import B._ // OK + import A.given // warn + import B.given // OK def t = implicitly[X] } @@ -44,7 +44,7 @@ package foo.unused.summon.inlines: given willBeUsed: (A & B) = new A with B {} package use: - import lib.{A, B, C, willBeUnused, willBeUsed} //OK + import lib.{A, B, C, willBeUnused, willBeUsed} // warn import compiletime.summonInline //OK transparent inline given conflictInside: C = @@ -56,4 +56,4 @@ package foo.unused.summon.inlines: ??? val b: B = summon[B] - val c: C = summon[C] \ No newline at end of file + val c: C = summon[C] diff --git a/tests/warn/i15503k.scala b/tests/warn/i15503k.scala new file mode 100644 index 000000000000..8148de44c588 --- /dev/null +++ b/tests/warn/i15503k.scala @@ -0,0 +1,43 @@ + +//> using options -Wunused:imports + +import scala.compiletime.ops.int.* // no warn + +object TupleOps: + /** Type of the element at position N in the tuple X */ + type Elem[X <: Tuple, N <: Int] = X match { + case x *: xs => + N match { + case 0 => x + case S[n1] => Elem[xs, n1] + } + } + + /** Literal constant Int size of a tuple */ + type Size[X <: Tuple] <: Int = X match { + case EmptyTuple => 0 + case x *: xs => S[Size[xs]] + } + +object Summoner: + transparent inline def summoner[T](using x: T): x.type = x + +object `Summoner's Tale`: + import compiletime.summonFrom // no warn + inline def valueOf[T]: T = summonFrom: // implicit match + case ev: ValueOf[T] => ev.value + import Summoner.* // no warn + def f[T](using T): T = summoner[T] // Inlined + +class C: + private def m: Int = 42 // no warn +object C: + class D: + private val c: C = C() // no warn + export c.m // no work to do, expanded member is non-private and uses the select expr + +object UsefulTypes: + trait T +object TypeUser: + import UsefulTypes.* + def f(x: => T) = x diff --git a/tests/warn/i15503kb/power.scala b/tests/warn/i15503kb/power.scala new file mode 100644 index 000000000000..f7e5ccce6d58 --- /dev/null +++ b/tests/warn/i15503kb/power.scala @@ -0,0 +1,14 @@ + +object Power: + import scala.math.pow as power + import scala.quoted.* + inline def powerMacro(x: Double, inline n: Int): Double = ${ powerCode('x, 'n) } + def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = + n match + case Expr(m) => unrolledPowerCode(x, m) + case _ => '{ power($x, $n.toDouble) } + def unrolledPowerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] = + n match + case 0 => '{ 1.0 } + case 1 => x + case _ => '{ $x * ${ unrolledPowerCode(x, n - 1) } } diff --git a/tests/warn/i15503kb/square.scala b/tests/warn/i15503kb/square.scala new file mode 100644 index 000000000000..2a5f76e9be83 --- /dev/null +++ b/tests/warn/i15503kb/square.scala @@ -0,0 +1,5 @@ +//> using options -Werror -Wunused:all + +object PowerUser: + import Power.* + def square(x: Double): Double = powerMacro(x, 2) diff --git a/tests/pos/i15967.scala b/tests/warn/i15967.scala similarity index 100% rename from tests/pos/i15967.scala rename to tests/warn/i15967.scala diff --git a/tests/warn/i16639a.scala b/tests/warn/i16639a.scala index 9fe4efe57d7b..ca7798297aea 100644 --- a/tests/warn/i16639a.scala +++ b/tests/warn/i16639a.scala @@ -1,7 +1,7 @@ //> using options -Wunused:all -source:3.3 class Bippy(a: Int, b: Int) { - private def this(c: Int) = this(c, c) + private def this(c: Int) = this(c, c) // warn private def boop(x: Int) = x+a+b // warn private def bippy(x: Int): Int = bippy(x) // warn TODO: could warn final private val MILLIS1 = 2000 // warn no warn, /Dotty:Warn @@ -24,13 +24,13 @@ class B3(msg0: String) extends A("msg") // warn /Dotty: unused explicit paramete trait Bing trait Accessors { - private var v1: Int = 0 // warn warn - private var v2: Int = 0 // warn warn, never set - private var v3: Int = 0 + private var v1: Int = 0 // warn + private var v2: Int = 0 // warn, never set + private var v3: Int = 0 // warn, never got private var v4: Int = 0 // no warn - private[this] var v5 = 0 // warn warn, never set - private[this] var v6 = 0 + private[this] var v5 = 0 // warn, never set + private[this] var v6 = 0 // warn, never got private[this] var v7 = 0 // no warn def bippy(): Int = { @@ -43,13 +43,13 @@ trait Accessors { } class StableAccessors { - private var s1: Int = 0 // warn warn - private var s2: Int = 0 // warn warn, never set - private var s3: Int = 0 + private var s1: Int = 0 // warn + private var s2: Int = 0 // warn, never set + private var s3: Int = 0 // warn, never got private var s4: Int = 0 // no warn - private[this] var s5 = 0 // warn warn, never set - private[this] var s6 = 0 // no warn, limitation /Dotty: Why limitation ? + private[this] var s5 = 0 // warn, never set + private[this] var s6 = 0 // warn, never got private[this] var s7 = 0 // no warn def bippy(): Int = { @@ -62,7 +62,7 @@ class StableAccessors { } trait DefaultArgs { - private def bippy(x1: Int, x2: Int = 10, x3: Int = 15): Int = x1 + x2 + x3 // no more warn warn since #17061 + private def bippy(x1: Int, x2: Int = 10, x3: Int = 15): Int = x1 + x2 + x3 // warn // warn def boppy() = bippy(5, 100, 200) } @@ -91,7 +91,7 @@ trait Locals { } object Types { - private object Dongo { def f = this } // no more warn since #17061 + private object Dongo { def f = this } // warn private class Bar1 // warn warn private class Bar2 // no warn private type Alias1 = String // warn warn @@ -101,7 +101,7 @@ object Types { def f(x: Alias2) = x.length def l1() = { - object HiObject { def f = this } // no more warn since #17061 + object HiObject { def f = this } // warn class Hi { // warn warn def f1: Hi = new Hi def f2(x: Hi) = x @@ -124,9 +124,9 @@ trait Underwarn { } class OtherNames { - private def x_=(i: Int): Unit = () // no more warn since #17061 - private def x: Int = 42 // warn Dotty triggers unused private member : To investigate - private def y_=(i: Int): Unit = () // // no more warn since #17061 + private def x_=(i: Int): Unit = () // warn + private def x: Int = 42 // warn + private def y_=(i: Int): Unit = () // warn private def y: Int = 42 def f = y @@ -145,7 +145,7 @@ trait Forever { val t = Option((17, 42)) for { ns <- t - (i, j) = ns // no warn + (i, j) = ns // warn // warn -Wunused:patvars is in -Wunused:all } yield 42 // val emitted only if needed, hence nothing unused } } @@ -158,14 +158,14 @@ trait CaseyKasem { def f = 42 match { case x if x < 25 => "no warn" case y if toString.nonEmpty => "no warn" + y - case z => "warn" + case z => "warn" // warn patvar } } trait CaseyAtTheBat { def f = Option(42) match { case Some(x) if x < 25 => "no warn" - case Some(y @ _) if toString.nonEmpty => "no warn" - case Some(z) => "warn" + case Some(y @ _) if toString.nonEmpty => "no warn" // warn todo whether to use name @ _ to suppress + case Some(z) => "warn" // warn patvar case None => "no warn" } } @@ -173,7 +173,7 @@ trait CaseyAtTheBat { class `not even using companion privates` object `not even using companion privates` { - private implicit class `for your eyes only`(i: Int) { // no more warn since #17061 + private implicit class `for your eyes only`(i: Int) { // warn def f = i } } @@ -202,4 +202,4 @@ trait `short comings` { val x = 42 // warn /Dotty only triggers in dotty 17 } -} \ No newline at end of file +} diff --git a/tests/warn/i16743.check b/tests/warn/i16743.check deleted file mode 100644 index 6fa1f2c83357..000000000000 --- a/tests/warn/i16743.check +++ /dev/null @@ -1,84 +0,0 @@ --- [E194] Potential Issue Warning: tests/warn/i16743.scala:30:6 -------------------------------------------------------- -30 | def t = 27 // warn - | ^ - | Extension method t will never be selected from type T - | because T already has a member with the same name and compatible parameter types. - | - | longer explanation available when compiling with `-explain` --- [E194] Potential Issue Warning: tests/warn/i16743.scala:32:6 -------------------------------------------------------- -32 | def g(x: String)(i: Int): String = x*i // warn - | ^ - | Extension method g will never be selected from type T - | because T already has a member with the same name and compatible parameter types. - | - | longer explanation available when compiling with `-explain` --- [E194] Potential Issue Warning: tests/warn/i16743.scala:33:6 -------------------------------------------------------- -33 | def h(x: String): String = x // warn - | ^ - | Extension method h will never be selected from type T - | because T already has a member with the same name and compatible parameter types. - | - | longer explanation available when compiling with `-explain` --- [E194] Potential Issue Warning: tests/warn/i16743.scala:35:6 -------------------------------------------------------- -35 | def j(x: Any, y: Int): String = (x.toString)*y // warn - | ^ - | Extension method j will never be selected from type T - | because T already has a member with the same name and compatible parameter types. - | - | longer explanation available when compiling with `-explain` --- [E194] Potential Issue Warning: tests/warn/i16743.scala:36:6 -------------------------------------------------------- -36 | def k(x: String): String = x // warn - | ^ - | Extension method k will never be selected from type T - | because T already has a member with the same name and compatible parameter types. - | - | longer explanation available when compiling with `-explain` --- [E194] Potential Issue Warning: tests/warn/i16743.scala:38:6 -------------------------------------------------------- -38 | def m(using String): String = "m" + summon[String] // warn - | ^ - | Extension method m will never be selected from type T - | because T already has a member with the same name and compatible parameter types. - | - | longer explanation available when compiling with `-explain` --- [E194] Potential Issue Warning: tests/warn/i16743.scala:39:6 -------------------------------------------------------- -39 | def n(using String): String = "n" + summon[String] // warn - | ^ - | Extension method n will never be selected from type T - | because T already has a member with the same name and compatible parameter types. - | - | longer explanation available when compiling with `-explain` --- [E194] Potential Issue Warning: tests/warn/i16743.scala:40:6 -------------------------------------------------------- -40 | def o: String = "42" // warn - | ^ - | Extension method o will never be selected from type T - | because T already has a member with the same name and compatible parameter types. - | - | longer explanation available when compiling with `-explain` --- [E194] Potential Issue Warning: tests/warn/i16743.scala:41:6 -------------------------------------------------------- -41 | def u: Int = 27 // warn - | ^ - | Extension method u will never be selected from type T - | because T already has a member with the same name and compatible parameter types. - | - | longer explanation available when compiling with `-explain` --- [E194] Potential Issue Warning: tests/warn/i16743.scala:44:6 -------------------------------------------------------- -44 | def at: Int = 42 // warn - | ^ - | Extension method at will never be selected from type T - | because T already has a member with the same name and compatible parameter types. - | - | longer explanation available when compiling with `-explain` --- [E194] Potential Issue Warning: tests/warn/i16743.scala:46:6 -------------------------------------------------------- -46 | def x(using String)(n: Int): Int = summon[String].toInt + n // warn - | ^ - | Extension method x will never be selected from type T - | because T already has a member with the same name and compatible parameter types. - | - | longer explanation available when compiling with `-explain` --- [E194] Potential Issue Warning: tests/warn/i16743.scala:47:6 -------------------------------------------------------- -47 | def y(using String)(s: String): String = s + summon[String] // warn - | ^ - | Extension method y will never be selected from type T - | because T already has a member with the same name and compatible parameter types. - | - | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i16743.scala b/tests/warn/i16743.scala index 4c9c99cf30d0..c347ed64d284 100644 --- a/tests/warn/i16743.scala +++ b/tests/warn/i16743.scala @@ -25,6 +25,7 @@ trait T: def x(n: Int): Int = 42 + n def y(n: Int): Int = u + n def y(s: String): String = s + u + def z(s: String): String = s + m extension (_t: T) def t = 27 // warn @@ -45,6 +46,7 @@ extension (_t: T) def w(using String)(n: String): Int = (summon[String] + n).toInt def x(using String)(n: Int): Int = summon[String].toInt + n // warn def y(using String)(s: String): String = s + summon[String] // warn + def z(): String = "extension" // deferred extension is defined in subclass trait Foo: @@ -66,7 +68,7 @@ trait DungeonDweller: trait SadDungeonDweller: def f[A](x: Dungeon.IArray[A]) = 27 // x.length // just to confirm, length is not a member -trait Quote: +trait Quote: // see tests/warn/ext-override.scala type Tree <: AnyRef given TreeMethods: TreeMethods trait TreeMethods: @@ -87,10 +89,15 @@ class Depends: object Depending: extension (using depends: Depends)(x: depends.Thing) def y = 42 - def length() = 42 // nowarn see Quote above + def length() = 42 // warn This extension method will be shadowed by .length() on String. def f(using d: Depends) = d.thing.y def g(using d: Depends) = d.thing.length() +class i24198: + def m(ss: String*) = ss.foldLeft("")(_ + _) +object i24198: + extension (r: i24198) def m(ss: Seq[String]) = ss.foldRight("!")(_ + _) + @main def test() = val x = new T {} println(x.f(42)) // OK! @@ -117,3 +124,6 @@ object Depending: given String = "42" x.w("27") } + println: + val sut = i24198() + sut.m(List("hello", ", ", "world")) diff --git a/tests/pos/i17230.min1.scala b/tests/warn/i17230.min1.scala similarity index 100% rename from tests/pos/i17230.min1.scala rename to tests/warn/i17230.min1.scala diff --git a/tests/pos/i17230.orig.scala b/tests/warn/i17230.orig.scala similarity index 100% rename from tests/pos/i17230.orig.scala rename to tests/warn/i17230.orig.scala diff --git a/tests/pos/i17314.scala b/tests/warn/i17314.scala similarity index 84% rename from tests/pos/i17314.scala rename to tests/warn/i17314.scala index 8ece4a3bd7ac..3b5a486a6785 100644 --- a/tests/pos/i17314.scala +++ b/tests/warn/i17314.scala @@ -1,4 +1,4 @@ -//> using options -Xfatal-warnings -Wunused:all -deprecation -feature +//> using options -Wunused:all -deprecation -feature -Werror import java.net.URI @@ -10,9 +10,7 @@ object circelike { type Configuration trait ConfiguredCodec[T] object ConfiguredCodec: - inline final def derived[A](using conf: Configuration)(using - inline mirror: Mirror.Of[A] - ): ConfiguredCodec[A] = + inline final def derived[A](using conf: Configuration)(using inline mirror: Mirror.Of[A]): ConfiguredCodec[A] = class InlinedConfiguredCodec extends ConfiguredCodec[A]: val codec = summonInline[Codec[URI]] // simplification new InlinedConfiguredCodec diff --git a/tests/pos/i17314a.scala b/tests/warn/i17314a.scala similarity index 70% rename from tests/pos/i17314a.scala rename to tests/warn/i17314a.scala index 4bce56d8bbed..14ae96848d63 100644 --- a/tests/pos/i17314a.scala +++ b/tests/warn/i17314a.scala @@ -1,4 +1,4 @@ -//> using options -Xfatal-warnings -Wunused:all -deprecation -feature +//> using options -Werror -Wunused:all -deprecation -feature package foo: class Foo[T] diff --git a/tests/warn/i17314b.scala b/tests/warn/i17314b.scala index e1500028ca93..ad4c8f1e4a31 100644 --- a/tests/warn/i17314b.scala +++ b/tests/warn/i17314b.scala @@ -1,4 +1,4 @@ -//> using options -Wunused:all +//> using options -Wunused:all package foo: class Foo[T] diff --git a/tests/warn/i17318.scala b/tests/warn/i17318.scala new file mode 100644 index 000000000000..d242c96484a7 --- /dev/null +++ b/tests/warn/i17318.scala @@ -0,0 +1,33 @@ + +//> using options -Werror -Wunused:all + +object events { + final val PollOut = 0x002 + transparent inline def POLLIN = 0x001 +} + +def withShort(v: Short): Unit = ??? +def withInt(v: Int): Unit = ??? + +def usage() = + import events.POLLIN // reports unused + def v: Short = POLLIN + println(v) + +def usage2() = + import events.POLLIN // reports unused + withShort(POLLIN) + +def usage3() = + import events.POLLIN // does not report unused + withInt(POLLIN) + +def usage4() = + import events.POLLIN // reports unused + withShort(POLLIN) + +def value = 42 +def withDouble(v: Double): Unit = ??? +def usage5() = withDouble(value) +def usage6() = withShort(events.POLLIN) +def usage7() = withShort(events.PollOut) diff --git a/tests/warn/i17371.scala b/tests/warn/i17371.scala new file mode 100644 index 000000000000..f9be76bfed07 --- /dev/null +++ b/tests/warn/i17371.scala @@ -0,0 +1,39 @@ +//> using options -Wunused:all + +class A +class B + +def Test() = + val ordA: Ordering[A] = ??? + val ordB: Ordering[B] = ??? + val a: A = ??? + val b: B = ??? + + import ordA.given + val _ = a > a + + import ordB.given + val _ = b < b + +// unminimized OP +trait Circular[T] extends Ordering[T] +trait Turns[C: Circular, T] extends Ordering[T]: // warn Circular is not a marker interface + extension (turns: T) def extract: C + +def f[K, T](start: T, end: T)(using circular: Circular[K], turns: Turns[K, T]): Boolean = + import turns.given + if start > end then throw new IllegalArgumentException("start must be <= end") + + import circular.given + start.extract < end.extract + +// -Wunused:implicits warns for unused implicit evidence unless it is an empty interface (only universal members). +// scala 2 also offers -Wunused:synthetics for whether to warn for synthetic implicit params. +object ContextBounds: + class C[A: Ordered](a: A): // warn + def f = a + + trait T[A] + + class D[A: T](a: A): // no warn + def f = a diff --git a/tests/warn/i17667.scala b/tests/warn/i17667.scala new file mode 100644 index 000000000000..cc3f6bfc8472 --- /dev/null +++ b/tests/warn/i17667.scala @@ -0,0 +1,10 @@ + +//> using options -Wunused:imports + +object MyImplicits: + extension (a: Int) def print: Unit = println(s"Hello, I am $a") + +import MyImplicits.print //Global import of extension +object Foo: + def printInt(a: Int): Unit = a.print + import MyImplicits.* // warn //Local import of extension diff --git a/tests/warn/i17667b.scala b/tests/warn/i17667b.scala new file mode 100644 index 000000000000..ba8fc7219945 --- /dev/null +++ b/tests/warn/i17667b.scala @@ -0,0 +1,22 @@ + +//> using options -Wunused:all + +import scala.util.Try +import scala.concurrent.* // warn +import scala.collection.Set +class C { + def ss[A](using Set[A]) = println() // warn + private def f = Try(42).get + private def z: Int = // warn + Try(27 + z).get + def g = f + f + def k = + val i = g + g + val j = i + 2 // warn + i + 1 + def c = C() + import scala.util.Try // warn +} +class D { + def d = C().g +} diff --git a/tests/warn/i17753.scala b/tests/warn/i17753.scala new file mode 100644 index 000000000000..66e4fdb8727b --- /dev/null +++ b/tests/warn/i17753.scala @@ -0,0 +1,10 @@ +//> using options -Wunused:all + +class PartiallyApplied[A] { + transparent inline def func[B](): Nothing = ??? +} + +def call[A] = new PartiallyApplied[A] + +def good = call[Int].func[String]() // no warn inline proxy +def bad = { call[Int].func[String]() } // no warn inline proxy diff --git a/tests/warn/i17762.scala b/tests/warn/i17762.scala new file mode 100644 index 000000000000..7f9e85a7b108 --- /dev/null +++ b/tests/warn/i17762.scala @@ -0,0 +1,33 @@ +//> using options -Wunused:all + +class SomeType + +def testIt(st1: SomeType, st2: SomeType): Boolean = + given CanEqual[SomeType, SomeType] = CanEqual.derived + st1 == st2 + +object HasCanEqual: + given f: CanEqual[SomeType, SomeType] = CanEqual.derived + +object UsesCanEqual: + import HasCanEqual.given + def testIt(st1: SomeType, st2: SomeType): Boolean = + st1 == st2 + +object UsesCanEqual2: + import HasCanEqual.f + def testIt(st1: SomeType, st2: SomeType): Boolean = + st1 != st2 + +object UsesCanEqual3: + import HasCanEqual.f as g + def testIt(st1: SomeType, st2: SomeType): Boolean = + st1 != st2 + +def warnable(st1: SomeType, st2: SomeType): Boolean = + given CanEqual[SomeType, SomeType] = CanEqual.derived // warn + st1.toString == st2.toString + +def importable(st1: SomeType, st2: SomeType): Boolean = + import HasCanEqual.given // warn + st1.toString == st2.toString diff --git a/tests/warn/i18313.scala b/tests/warn/i18313.scala new file mode 100644 index 000000000000..44b005b313e4 --- /dev/null +++ b/tests/warn/i18313.scala @@ -0,0 +1,14 @@ +//> using options -Werror -Wunused:imports + +import scala.deriving.Mirror + +case class Test(i: Int, d: Double) +case class Decoder(d: Product => Test) + +// OK, no warning returned +//val ok = Decoder(summon[Mirror.Of[Test]].fromProduct) +// +// returns warning: +// [warn] unused import +// [warn] import scala.deriving.Mirror +val d = Decoder(d = summon[Mirror.Of[Test]].fromProduct) // no warn diff --git a/tests/warn/i18341.scala b/tests/warn/i18341.scala new file mode 100644 index 000000000000..63b725df000f --- /dev/null +++ b/tests/warn/i18341.scala @@ -0,0 +1,21 @@ + +//> using options -Wunused:params,nowarn + +import annotation.* + +class B(@nowarn useless: Int) + +class C(@nowarn("msg=unused") useless: Int) + +class D(useless: Int) // warn + +class E(@nowarn useful: Int): // warn + def e = useful * 10 // 10x useful + +class X: + def extensionInCompanion: String = ??? +@nowarn // extensionInCompanion +object X: + implicit def companionConversion(x: X): B = ??? + + extension (x: X) def extensionInCompanion: String = ??? diff --git a/tests/warn/i18366.scala b/tests/warn/i18366.scala new file mode 100644 index 000000000000..b6385b5bbb59 --- /dev/null +++ b/tests/warn/i18366.scala @@ -0,0 +1,19 @@ +//> using options -Werror -Wunused:all + +trait Builder { + def foo(): Unit +} + +def `i18366` = + val builder: Builder = ??? + import builder.{foo => bar} + bar() + +import java.io.DataOutputStream + +val buffer: DataOutputStream = ??? + +import buffer.{write => put} + +def `i17315` = + put(0: Byte) diff --git a/tests/warn/i18564.scala b/tests/warn/i18564.scala new file mode 100644 index 000000000000..19682b7955f9 --- /dev/null +++ b/tests/warn/i18564.scala @@ -0,0 +1,39 @@ + +//> using options -Wunused:imports + +import scala.compiletime.* +import scala.deriving.* + +trait Foo + +trait HasFoo[A]: + /** true if A contains a Foo */ + val hasFoo: Boolean + +// given instances that need to be imported to be in scope +object HasFooInstances: + given defaultHasFoo[A]: HasFoo[A] with + val hasFoo: Boolean = false + given HasFoo[Foo] with + val hasFoo: Boolean = true + +object HasFooDeriving: + + inline private def tupleHasFoo[T <: Tuple]: Boolean = + inline erasedValue[T] match + case _: EmptyTuple => false + case _: (t *: ts) => summonInline[HasFoo[t]].hasFoo || tupleHasFoo[ts] + + inline def deriveHasFoo[T](using p: Mirror.ProductOf[T]): HasFoo[T] = + // falsely reported as unused even though it has influence on this code + import HasFooInstances.given // no warn at inline method + val pHasFoo = tupleHasFoo[p.MirroredElemTypes] + new HasFoo[T]: // warn New anonymous class definition will be duplicated at each inline site + val hasFoo: Boolean = pHasFoo + +/* the import is used upon inline elaboration +object Test: + import HasFooDeriving.* + case class C(x: Foo, y: Int) + def f: HasFoo[C] = deriveHasFoo[C] +*/ diff --git a/tests/warn/i19077.check b/tests/warn/i19077.check new file mode 100644 index 000000000000..646e529c38f3 --- /dev/null +++ b/tests/warn/i19077.check @@ -0,0 +1,44 @@ +-- Deprecation Warning: tests/warn/i19077.scala:26:14 ------------------------------------------------------------------ +26 | def g = f(y = 42) // warn but omit empty since + | ^^^^^^ + | the parameter name y is deprecated: use x instead +-- Deprecation Warning: tests/warn/i19077.scala:12:6 ------------------------------------------------------------------- +12 | f(x = 1, 2, 3) // warn + | ^^^^^ + | naming parameter x is deprecated +-- Deprecation Warning: tests/warn/i19077.scala:13:9 ------------------------------------------------------------------- +13 | f(1, y = 2, 3) // warn + | ^^^^^ + | naming parameter y is deprecated +-- Deprecation Warning: tests/warn/i19077.scala:14:12 ------------------------------------------------------------------ +14 | f(1, 2, w = 3) // warn + | ^^^^^ + | the parameter name w is deprecated: use z instead +-- Deprecation Warning: tests/warn/i19077.scala:15:6 ------------------------------------------------------------------- +15 | f(x = 1, w = 3, y = 2) // warn // warn // warn + | ^^^^^ + | naming parameter x is deprecated +-- Deprecation Warning: tests/warn/i19077.scala:15:20 ------------------------------------------------------------------ +15 | f(x = 1, w = 3, y = 2) // warn // warn // warn + | ^^^^^ + | naming parameter y is deprecated +-- Deprecation Warning: tests/warn/i19077.scala:15:13 ------------------------------------------------------------------ +15 | f(x = 1, w = 3, y = 2) // warn // warn // warn + | ^^^^^ + | the parameter name w is deprecated: use z instead +-- Deprecation Warning: tests/warn/i19077.scala:16:13 ------------------------------------------------------------------ +16 | f(w = 3, x = 1, y = 2) // warn // warn // warn + | ^^^^^ + | naming parameter x is deprecated +-- Deprecation Warning: tests/warn/i19077.scala:16:20 ------------------------------------------------------------------ +16 | f(w = 3, x = 1, y = 2) // warn // warn // warn + | ^^^^^ + | naming parameter y is deprecated +-- Deprecation Warning: tests/warn/i19077.scala:16:6 ------------------------------------------------------------------- +16 | f(w = 3, x = 1, y = 2) // warn // warn // warn + | ^^^^^ + | the parameter name w is deprecated: use z instead +-- Deprecation Warning: tests/warn/i19077.scala:20:8 ------------------------------------------------------------------- +20 | X.f(v = 42) // warn + | ^^^^^^ + | the parameter name v is deprecated (since 3.3): use x instead diff --git a/tests/warn/i19077.scala b/tests/warn/i19077.scala new file mode 100644 index 000000000000..0c60cfb0d9ff --- /dev/null +++ b/tests/warn/i19077.scala @@ -0,0 +1,26 @@ +//> using options -deprecation + +def f(@deprecatedName("x") x: Int, @deprecatedName y: Int, @deprecatedName("w") z: Int) = x+y+z + +object X: + def f(@deprecatedName("v", since="3.3") x: Int) = x + def f(@deprecatedName("v") x: Int, y: Int) = x+y + +@main def Test = + f(1, 2, 3) // nowarn + f(1, 2, z = 3) // nowarn + f(x = 1, 2, 3) // warn + f(1, y = 2, 3) // warn + f(1, 2, w = 3) // warn + f(x = 1, w = 3, y = 2) // warn // warn // warn + f(w = 3, x = 1, y = 2) // warn // warn // warn + + X.f(42) + X.f(x = 42) + X.f(v = 42) // warn + X.f(x = 42, y = 27) + X.f(y = 42, x = 27) + +object empty: + def f(@deprecatedName("y", since="") x: Int) = x + def g = f(y = 42) // warn but omit empty since diff --git a/tests/warn/i19252.scala b/tests/warn/i19252.scala new file mode 100644 index 000000000000..42ca99208299 --- /dev/null +++ b/tests/warn/i19252.scala @@ -0,0 +1,13 @@ +//> using options -Werror -Wunused:all +object Deps: + trait D1 + object D2 +end Deps + +object Bug: + import Deps.D1 // no warn + + class Cl(d1: D1): + import Deps.* + def f = (d1, D2) +end Bug diff --git a/tests/warn/i19657-mega.scala b/tests/warn/i19657-mega.scala new file mode 100644 index 000000000000..eeeb33ab3da7 --- /dev/null +++ b/tests/warn/i19657-mega.scala @@ -0,0 +1,4 @@ +//> using options -Xlint:type-parameter-shadow -Wunused:all + +class F[X, M[N[X]]]: // warn + private def x[X] = toString // warn // warn diff --git a/tests/warn/i19657.scala b/tests/warn/i19657.scala new file mode 100644 index 000000000000..2caa1c832abe --- /dev/null +++ b/tests/warn/i19657.scala @@ -0,0 +1,117 @@ +//> using options -Wunused:imports -Ystop-after:checkUnusedPostInlining + +trait Schema[A] + +case class Foo() +case class Bar() + +trait SchemaGenerator[A] { + given Schema[A] = new Schema[A]{} +} + +object FooCodec extends SchemaGenerator[Foo] +object BarCodec extends SchemaGenerator[Bar] + +def summonSchemas(using Schema[Foo], Schema[Bar]) = () + +def summonSchema(using Schema[Foo]) = () + +def `i19657 check prefix to pick selector`: Unit = + import FooCodec.given + import BarCodec.given + summonSchemas + +def `i19657 regression test`: Unit = + import FooCodec.given + import BarCodec.given // warn + summonSchema + +def `i19657 check prefix to pick specific selector`: Unit = + import FooCodec.given_Schema_A + import BarCodec.given_Schema_A + summonSchemas + +def `same symbol different names`: Unit = + import FooCodec.given_Schema_A + import FooCodec.given_Schema_A as AThing + summonSchema(using given_Schema_A) + summonSchema(using AThing) + +package i17156: + package a: + trait Foo[A] + object Foo: + class Food[A] extends Foo[A] + inline def derived[T]: Foo[T] = Food() + + package b: + import a.Foo + type Xd[A] = Foo[A] + + package c: + import b.Xd + trait Z derives Xd // checks if dealiased import is prefix a.Foo + class Bar extends Xd[Int] // checks if import qual b is prefix of b.Xd + +object Coll: + class C: + type HM[K, V] = scala.collection.mutable.HashMap[K, V] +object CC extends Coll.C +import CC.* + +def `param type is imported`(map: HM[String, String]): Unit = println(map("hello, world")) + +object Constants: + final val i = 42 + def extra = 3 +def `old-style constants are usages`: Unit = + object Local: + final val j = 27 + import Constants.i + println(i + Local.j) + +object Constantinople: + val k = 42 +class `scope of super`: + import Constants.i // was bad warn + class C(x: Int): + def y = x + class D(j: Int) extends C(i + j): + import Constants.* // does not resolve i in C(i) and does not shadow named import + def m = i // actually picks the higher-precedence import + def f = + import Constantinople.* + class E(e: Int) extends C(i + k): + def g = e + y + k + 1 + E(0).g + def consume = extra // use the wildcard import from Constants + +import scala.annotation.meta.* +object Alias { + type A = Deprecated @param +} + +// avoid reporting on runtime (nothing to do with transparent inline) +import scala.runtime.EnumValue + +trait Lime + +enum Color(val rgb: Int): + case Red extends Color(0xFF0000) with EnumValue + case Green extends Color(0x00FF00) with Lime + case Blue extends Color(0x0000FF) + +object prefixes: + class C: + object N: + type U + object Test: + val c: C = ??? + def k2: c.N.U = ??? + import c.N.* + def k3: U = ??? // TypeTree if not a select + object Alt: + val c: C = ??? + import c.N + def k4: N.U = ??? +end prefixes diff --git a/tests/warn/i19687.check b/tests/warn/i19687.check new file mode 100644 index 000000000000..526860b3612f --- /dev/null +++ b/tests/warn/i19687.check @@ -0,0 +1,8 @@ +-- [E029] Pattern Match Exhaustivity Warning: tests/warn/i19687.scala:10:2 --------------------------------------------- +10 | w match // warn + | ^ + | match may not be exhaustive. + | + | It would fail on pattern case: B1(_) + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i19687.scala b/tests/warn/i19687.scala new file mode 100644 index 000000000000..3170b4c8ae03 --- /dev/null +++ b/tests/warn/i19687.scala @@ -0,0 +1,11 @@ +// Both fields inhabitable, one case missing +enum Eq[A, B]: + case Refl[A]() extends Eq[A, A] + +sealed trait W[A] +case class A1[A](witness: Eq[A, Int]) extends W[A] +case class B1[A](witness: Eq[A, Int]) extends W[A] + +def test(w: W[Int]): Int = + w match // warn + case A1(Eq.Refl()) => 0 diff --git a/tests/warn/i19687a.check b/tests/warn/i19687a.check new file mode 100644 index 000000000000..ca0c876295f5 --- /dev/null +++ b/tests/warn/i19687a.check @@ -0,0 +1,8 @@ +-- [E029] Pattern Match Exhaustivity Warning: tests/warn/i19687a.scala:11:2 -------------------------------------------- +11 | w match // warn + | ^ + | match may not be exhaustive. + | + | It would fail on pattern case: C1(_) + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i19687a.scala b/tests/warn/i19687a.scala new file mode 100644 index 000000000000..f57cde5dc56e --- /dev/null +++ b/tests/warn/i19687a.scala @@ -0,0 +1,12 @@ +// Some cases eliminated by uninhabitable fields, but inhabitable case still missing +enum Eq[A, B]: + case Refl[A]() extends Eq[A, A] + +sealed trait W[A] +case class A1[A](w: Eq[A, String]) extends W[A] +case class B1[A](w: Eq[A, Int]) extends W[A] +case class C1[A](w: Eq[A, Int]) extends W[A] + +def test(w: W[Int]): Int = + w match // warn + case B1(Eq.Refl()) => 0 diff --git a/tests/warn/i19687b.check b/tests/warn/i19687b.check new file mode 100644 index 000000000000..76a293594bb0 --- /dev/null +++ b/tests/warn/i19687b.check @@ -0,0 +1,8 @@ +-- [E029] Pattern Match Exhaustivity Warning: tests/warn/i19687b.scala:12:2 -------------------------------------------- +12 | w match // warn + | ^ + | match may not be exhaustive. + | + | It would fail on pattern case: P1(_) + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i19687b.scala b/tests/warn/i19687b.scala new file mode 100644 index 000000000000..2b9d55583dd5 --- /dev/null +++ b/tests/warn/i19687b.scala @@ -0,0 +1,13 @@ +// Multiple fields all inhabitable, case still missing +enum Eq[A, B]: + case Refl[A]() extends Eq[A, A] + +case class Multi[A](x: Int, witness: Eq[A, Int], y: String) + +sealed trait W[A] +case class P1[A](m: Multi[A]) extends W[A] +case class Q1[A](w: Eq[A, Int]) extends W[A] + +def test(w: W[Int]): Int = + w match // warn + case Q1(Eq.Refl()) => 0 diff --git a/tests/warn/i19687c.check b/tests/warn/i19687c.check new file mode 100644 index 000000000000..8f592a821b48 --- /dev/null +++ b/tests/warn/i19687c.check @@ -0,0 +1,8 @@ +-- [E029] Pattern Match Exhaustivity Warning: tests/warn/i19687c.scala:9:2 --------------------------------------------- +9 | w match // warn + | ^ + | match may not be exhaustive. + | + | It would fail on pattern case: A1(_) + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i19687c.scala b/tests/warn/i19687c.scala new file mode 100644 index 000000000000..99de2217b343 --- /dev/null +++ b/tests/warn/i19687c.scala @@ -0,0 +1,10 @@ +// Recursive case class, non-exhaustive (should not cause infinite loop) +case class Rec(r: Rec, x: Int) + +sealed trait W +case class A1(rec: Rec) extends W +case class B1(x: Int) extends W + +def test(w: W): Int = + w match // warn + case B1(_) => 0 diff --git a/tests/warn/i20121.check b/tests/warn/i20121.check new file mode 100644 index 000000000000..b248da82a74a --- /dev/null +++ b/tests/warn/i20121.check @@ -0,0 +1,4 @@ +-- [E030] Match case Unreachable Warning: tests/warn/i20121.scala:8:7 -------------------------------------------------- +8 | case CC_B(_) => 0 // warn: unreachable + | ^^^^^^^ + | Unreachable case diff --git a/tests/warn/i20122.check b/tests/warn/i20122.check new file mode 100644 index 000000000000..4ad0f3dd9c96 --- /dev/null +++ b/tests/warn/i20122.check @@ -0,0 +1,4 @@ +-- [E030] Match case Unreachable Warning: tests/warn/i20122.scala:10:7 ------------------------------------------------- +10 | case CC_B(CC_E(CC_C(_))) => 0 // warn: unreachable + | ^^^^^^^^^^^^^^^^^^^ + | Unreachable case diff --git a/tests/warn/i20520.scala b/tests/warn/i20520.scala new file mode 100644 index 000000000000..e09d16c27af2 --- /dev/null +++ b/tests/warn/i20520.scala @@ -0,0 +1,11 @@ + +//> using options -Wunused:all + +@main def run = + val veryUnusedVariable: Int = value // warn local + +package i20520: + private def veryUnusedMethod(x: Int): Unit = println() // warn param + private val veryUnusedVariableToplevel: Unit = println() // package members are accessible under separate compilation + +def value = 42 diff --git a/tests/pos/i20860.scala b/tests/warn/i20860.scala similarity index 74% rename from tests/pos/i20860.scala rename to tests/warn/i20860.scala index 1e1ddea11b75..b318d861fce0 100644 --- a/tests/pos/i20860.scala +++ b/tests/warn/i20860.scala @@ -1,3 +1,5 @@ +//> using options -Werror -Wunused:imports + def `i20860 use result to check selector bound`: Unit = import Ordering.Implicits.given Ordering[?] summon[Ordering[Seq[Int]]] diff --git a/tests/warn/i20951.scala b/tests/warn/i20951.scala new file mode 100644 index 000000000000..0ca82e1b8d66 --- /dev/null +++ b/tests/warn/i20951.scala @@ -0,0 +1,7 @@ +//> using options -Wunused:all +object Foo { + val dummy = 42 + def f(): Unit = Option(1).map((x: Int) => dummy) // warn + def g(): Unit = Option(1).map((x: Int) => ???) // warn + def main(args: Array[String]): Unit = {} +} 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/i21190.scala b/tests/warn/i21190.scala new file mode 100644 index 000000000000..652a4ad36853 --- /dev/null +++ b/tests/warn/i21190.scala @@ -0,0 +1,23 @@ + +//> using options -Werror + +opaque type FromEnd = Int +object FromEnd: + inline def apply(i: Int): FromEnd = i + extension (fe: FromEnd) + inline def value: Int = fe + +// Warning appears when extension is in same namespace as opaque type +extension [A](a: Array[A]) + inline def apply(fe: FromEnd): A = + a(a.length - 1 - FromEnd.value(fe)) + +class R: + def run(): String = + val xs = Array(1, 2, 3) + + s"""First element = ${xs(0)} + |Last element = ${xs(FromEnd(0))}""".stripMargin + +@main def test = println: + R().run() diff --git a/tests/warn/i21207/Over.java b/tests/warn/i21207/Over.java new file mode 100644 index 000000000000..16e18d8def79 --- /dev/null +++ b/tests/warn/i21207/Over.java @@ -0,0 +1,8 @@ + +interface I { + default int f() { return 42; } +} + +public class Over implements I { + public int f(int i) { return 42 + i; } +} diff --git a/tests/warn/i21207/usage.scala b/tests/warn/i21207/usage.scala new file mode 100644 index 000000000000..a5b6b6904cc1 --- /dev/null +++ b/tests/warn/i21207/usage.scala @@ -0,0 +1,17 @@ +//> using options -source:3.0-migration + +// Hope to see a migration warning! with quickfix. + +class P { + def g(): Int = 42 +} +class C extends P { + def g(i: Int): Int = 42 + i +} + +object Test extends App { + val over = Over() + println(over.f) // nowarn Java + val c = C() + println(c.g) // warn migration +} diff --git a/tests/warn/i21420.scala b/tests/warn/i21420.scala index 0ee4aa3f28f6..65a288c7ae5b 100644 --- a/tests/warn/i21420.scala +++ b/tests/warn/i21420.scala @@ -1,4 +1,4 @@ -//> using options -Wunused:imports +//> using options -Werror -Wunused:imports object decisions4s{ trait HKD @@ -7,7 +7,7 @@ object decisions4s{ object DiagnosticsExample { import decisions4s.HKD - val _ = new HKD {} + case class Input[F[_]]() extends HKD import decisions4s.* - val _ = new DecisionTable {} + val decisionTable: DecisionTable = ??? } diff --git a/tests/warn/i21525.scala b/tests/warn/i21525.scala new file mode 100644 index 000000000000..7fecb14a9673 --- /dev/null +++ b/tests/warn/i21525.scala @@ -0,0 +1,35 @@ +//> using options -Werror -Wunused:imports + +import scala.reflect.{Typeable, TypeTest} + +trait A { + type B + type C <: B + + given instance: TypeTest[B, C] +} + +def f(a: A, b: a.B): Boolean = { + import a.C + b match { + case _: C => + true + case _ => + false + } +} + +trait T: + type X + given tpe: Typeable[X] + +def g(t: T, x: Any) = + import t.X + x match + case _: X => true + case _ => false + +def typer[T: Typeable](x: Any) = + x match + case _: T => 1 + case _ => 0 diff --git a/tests/warn/i21649.scala b/tests/warn/i21649.scala new file mode 100644 index 000000000000..d63a7f3a7ecb --- /dev/null +++ b/tests/warn/i21649.scala @@ -0,0 +1,16 @@ +//> using options -explain +// do warn if function literal adaptation has match in non-empty block + +import scala.util.* +import PartialFunction.condOpt + +val f: PartialFunction[String, Boolean] = _.toUpperCase match { case "TRUE" => true } + +val g: PartialFunction[String, Boolean] = x => + val uc = x.toUpperCase + uc match // warn + case "TRUE" => true + +def kollekt = List("1", "two", "3").collect(x => Try(x.toInt) match { case Success(i) => i }) + +def lesen = List("1", "two", "3").flatMap(x => condOpt(Try(x.toInt)) { case Success(i) => i }) diff --git a/tests/warn/i21805.scala b/tests/warn/i21805.scala new file mode 100644 index 000000000000..9d991d35efc8 --- /dev/null +++ b/tests/warn/i21805.scala @@ -0,0 +1,31 @@ +//> using options -Wunused:all + +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.* // nowarn, unit is nullified + import p.* // 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/i21809.scala b/tests/warn/i21809.scala new file mode 100644 index 000000000000..91e7a334b13b --- /dev/null +++ b/tests/warn/i21809.scala @@ -0,0 +1,17 @@ +//> using options -Wunused:imports + +package p { + package q { + import q.* // warn so long as we pass typer + class Test { + //override def toString = new C().toString + " for Test" + def d = D() + } + class D + } +} +package q { + class C { + override def toString = "q.C" + } +} diff --git a/tests/warn/i21860.unenum.scala b/tests/warn/i21860.unenum.scala index 7335e1b6851d..e4b282e35e76 100644 --- a/tests/warn/i21860.unenum.scala +++ b/tests/warn/i21860.unenum.scala @@ -3,7 +3,7 @@ sealed trait Corners { self: Figure => } sealed abstract class Shape extends Figure object Shape: - case object Triange extends Shape with Corners + case object Triangle extends Shape with Corners case object Square extends Shape with Corners case object Circle extends Shape case object Ellipsis extends Shape diff --git a/tests/warn/i21917.scala b/tests/warn/i21917.scala new file mode 100644 index 000000000000..cade4e90db3d --- /dev/null +++ b/tests/warn/i21917.scala @@ -0,0 +1,27 @@ +//> using options -Wunused:imports + +import Pet.Owner + +class Dog(owner: Owner) extends Pet(owner) { + import Pet.* // warn although unambiguous (i.e., it was disambiguated) + //import Car.* // ambiguous + + def bark(): String = "bite" + + def this(owner: Owner, goodDog: Boolean) = { + this(owner) + if (goodDog) println(s"$owner's dog is a good boy") + } + + val getOwner: Owner = owner +} + +class Pet(val owner: Owner) + +object Pet { + class Owner +} + +object Car { + class Owner +} diff --git a/tests/warn/i22232.check b/tests/warn/i22232.check new file mode 100644 index 000000000000..d7c1bbd318c0 --- /dev/null +++ b/tests/warn/i22232.check @@ -0,0 +1,28 @@ +-- [E194] Potential Issue Warning: tests/warn/i22232.scala:3:23 -------------------------------------------------------- +3 | extension (c: C) def equals(that: Any): Boolean = false // warn + | ^ + | Extension method equals will never be selected from type C + | because C already has a member with the same name and compatible parameter types. + | + | longer explanation available when compiling with `-explain` +-- [E194] Potential Issue Warning: tests/warn/i22232.scala:9:25 -------------------------------------------------------- +9 | extension (d: D) def equals(that: Any): Boolean = false // warn + | ^ + | Extension method equals will never be selected from type D + | because D already has a member with the same name and compatible parameter types. + | + | longer explanation available when compiling with `-explain` +-- [E194] Potential Issue Warning: tests/warn/i22232.scala:13:38 ------------------------------------------------------- +13 | extension (arr: MyString[Byte]) def length: Int = 0 // warn + | ^ + | Extension method length will never be selected from type MyString + | because MyString already has a member with the same name and compatible parameter types. + | + | longer explanation available when compiling with `-explain` +-- [E194] Potential Issue Warning: tests/warn/i22232.scala:17:46 ------------------------------------------------------- +17 | extension [T <: MyString[Byte]](arr: T) def length: Int = 0 // warn + | ^ + | Extension method length will never be selected from type MyString + | because MyString already has a member with the same name and compatible parameter types. + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i22232.scala b/tests/warn/i22232.scala new file mode 100644 index 000000000000..f94e413920a2 --- /dev/null +++ b/tests/warn/i22232.scala @@ -0,0 +1,37 @@ +class C +object C: + extension (c: C) def equals(that: Any): Boolean = false // warn + +object X: + class C + opaque type D <: C = C + object D: + extension (d: D) def equals(that: Any): Boolean = false // warn + +object Upperbound1: + opaque type MyString[+T] <: String = String + extension (arr: MyString[Byte]) def length: Int = 0 // warn + +object Upperbound2: + opaque type MyString[+T] <: String = String + extension [T <: MyString[Byte]](arr: T) def length: Int = 0 // warn + +object Upperbound3: + opaque type MyString[+T] <: String = String + extension [T](arr: T) def length: Int = 0 // nowarn + +object NonUpperbound1: + opaque type MyString[+T] = String + extension (arr: MyString[Byte]) def length: Int = 0 // nowarn + +object NonUpperbound2: + opaque type MyString[+T] = String + extension [T <: MyString[Byte]](arr: T) def length2: Int = 0 // nowarn + +object NonUpperbound3: + opaque type MyString[+T] = String + extension [T](arr: T) def length: Int = 0 // nowarn + +object NonUpperbound4: + opaque type MyString = String + extension (arr: MyString) def length: Int = 0 // nowarn diff --git a/tests/warn/i22233.scala b/tests/warn/i22233.scala new file mode 100644 index 000000000000..08caea1c25fb --- /dev/null +++ b/tests/warn/i22233.scala @@ -0,0 +1 @@ +extension (s: String) def length = 42 // warn diff --git a/tests/warn/i22412.check b/tests/warn/i22412.check new file mode 100644 index 000000000000..adbe698db058 --- /dev/null +++ b/tests/warn/i22412.check @@ -0,0 +1,10 @@ +-- [E002] Syntax Warning: tests/warn/i22412.scala:3:34 ----------------------------------------------------------------- +3 | @annotation.nowarn("v") def f = try 1 // warn + | ^^^^^ + | A try without catch or finally is equivalent to putting + | its body in a block; no exceptions are handled. + |Matching filters for @nowarn or -Wconf: + | - id=E2 + | - name=EmptyCatchAndFinallyBlock + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i22412.scala b/tests/warn/i22412.scala new file mode 100644 index 000000000000..870a264c1df1 --- /dev/null +++ b/tests/warn/i22412.scala @@ -0,0 +1,3 @@ + +class C: + @annotation.nowarn("v") def f = try 1 // warn diff --git a/tests/warn/i22507.scala b/tests/warn/i22507.scala new file mode 100644 index 000000000000..e350cf9b23ff --- /dev/null +++ b/tests/warn/i22507.scala @@ -0,0 +1,13 @@ +//> using options -Werror -Wunused:privates + +case class BinaryFen(value: Array[Byte]) extends AnyVal: + + def read: Unit = + val reader = new Iterator[Byte]: + val inner = value.iterator + override inline def hasNext: Boolean = inner.hasNext // nowarn + override inline def next: Byte = if hasNext then inner.next() else 0.toByte // nowarn + + if reader.hasNext then + val b: Byte = reader.next() + println(b) diff --git a/tests/warn/i22527.scala b/tests/warn/i22527.scala new file mode 100644 index 000000000000..2e6411e87136 --- /dev/null +++ b/tests/warn/i22527.scala @@ -0,0 +1,81 @@ + +def f: Unit = + identity( + identity: + class X extends AnyRef, Serializable + 42 + ) + +def test: Unit = + assert( + identity: + true, + "ok" + ) + +def toss: Unit = + assert( + throw + null, + "ok" + ) +def raise: Unit = + assert( + throw + null, "ok" // ok now + ) + +def callme[A](x: => A, msg: String) = try x.toString catch case t: RuntimeException => msg + +def orElse(x: Int): Unit = + callme( + if x > 0 then + true + else + false, "fail") + +def onlyIf(x: Int): Unit = + callme( + if x > 0 then + true, "fail") // warn value discard + +def h(xs: List[Int]) = + xs.foldLeft(0) + ( + (acc, x) => + acc + + x, + ) + +def sum(x: Int, y: Int, z: Int) = x+y+z + +def k(xs: List[Int], y: Int, z: Int) = + xs.foldLeft(0) + ( + (acc, x) => + sum( + x + + y + + z, + y, // NO warn + z, // NO warn + ) + ) + +object `arrow eol`: + def f(g: Int => Int, i: Int): Int = g(i) + def g(map: Int => Int): Int => Int = map + def k(i: => Int) = i + def test = + f( + g(_ + 1), + 42 // NO warn + ) + def test2 = + f( + g: + x => + x + 1, +k: // NO warn + 42 + ) diff --git a/tests/warn/i22629.scala b/tests/warn/i22629.scala new file mode 100644 index 000000000000..53d17ec4da3b --- /dev/null +++ b/tests/warn/i22629.scala @@ -0,0 +1,42 @@ +//> using options -Wunused:all -Yno-deep-subtypes "-Wconf:msg=set repeatedly:s" + +//import either.* + +trait ResultMapper[A] { + final def map[B](f: A => B): ResultMapper[B] = ??? + + infix final def and[B](other: ResultMapper[B]): ResultMapper[(A, B)] = ??? +} + +trait BoilerplateResultMappers { + + def and[B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W]( + b: ResultMapper[B], c: ResultMapper[C], d: ResultMapper[D], e: ResultMapper[E], f: ResultMapper[F], g: ResultMapper[G], h: ResultMapper[H], i: ResultMapper[I], j: ResultMapper[J], k: ResultMapper[K], l: ResultMapper[L], m: ResultMapper[M], n: ResultMapper[N], o: ResultMapper[O], p: ResultMapper[P], q: ResultMapper[Q], r: ResultMapper[R], s: ResultMapper[S], t: ResultMapper[T], u: ResultMapper[U], v: ResultMapper[V], w: ResultMapper[W] + ): ResultMapper[(B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W)] = + (b and c and d and e and f and g and h and i and j and k and l and m and n and o and p and q and r and s and t and u and v and w).map { + case ((((((((((((((((((((((b), c), d), e), f), g), h), i), j), k), l), m), n), o), p), q), r), s), t), u), v), w) => + (b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w) + } +} + +/* +object either { + type ResultMapperException = RuntimeException + implicit class EitherOps[A](private val ea: Either[ResultMapperException, A]) extends AnyVal { + def and[B](eb: Either[ResultMapperException, B]): Either[ResultMapperException, (A, B)] = + (ea, eb) match { + case (Right(a), Right(b)) => + Right((a, b)) + + case (Right(_), Left(ex)) => + Left(ex) + + case (Left(ex), Right(_)) => + Left(ex) + + case (Left(_), Left(_)) => + Left(RuntimeException()) + } + } +} +*/ diff --git a/tests/warn/i22670-test.scala b/tests/warn/i22670-test.scala new file mode 100644 index 000000000000..3097f0040b77 --- /dev/null +++ b/tests/warn/i22670-test.scala @@ -0,0 +1,5 @@ +//> using options -Werror -Wconf:id=E222:e + +// note: ensure warning is not suppressed by -Wconf, see TestConfiguration +// don't warn about file package object with special char in name when in empty package +def f = 42 diff --git a/tests/warn/i22672/lib_1.scala b/tests/warn/i22672/lib_1.scala new file mode 100644 index 000000000000..722f2ae03899 --- /dev/null +++ b/tests/warn/i22672/lib_1.scala @@ -0,0 +1,16 @@ + +package p + +import annotation.{unchecked as _, *} + +@deprecated("old api", since="1.0") +def g = 42 + +//@deprecated("new api", since="1.0") +@nowarn("cat=deprecation") +inline def f = + g + +transparent inline def body = + g: @nowarn @unchecked + g: @unchecked @nowarn diff --git a/tests/warn/i22672/usage_2.scala b/tests/warn/i22672/usage_2.scala new file mode 100644 index 000000000000..39c0550b8fd4 --- /dev/null +++ b/tests/warn/i22672/usage_2.scala @@ -0,0 +1,13 @@ + +//> using options -deprecation + +package q + +def test = p.f // inline f is nowarn + +def bodily = p.body // transparent inline with annotated body + +@deprecated("do I even know how it works", since="0.1") +def huh = "hello" + +def failing = huh // warn diff --git a/tests/warn/i22681.scala b/tests/warn/i22681.scala new file mode 100644 index 000000000000..81f12923d4d6 --- /dev/null +++ b/tests/warn/i22681.scala @@ -0,0 +1,15 @@ + +//> using options -Wunused:all + +trait T: + def t: Int + +class C: + def f: Runnable { def u: Int } = new Runnable with T: + def run() = () + def g = 42 // warn effectively private member is unused + def t = 42 // nowarn + def u = 42 // nowarn because leaked by refinement + val v: Runnable { def u: Int } = new Runnable: + def run() = () + def u = 42 // nowarn because leaked by refinement diff --git a/tests/warn/i22705.scala b/tests/warn/i22705.scala new file mode 100644 index 000000000000..d30c1b310201 --- /dev/null +++ b/tests/warn/i22705.scala @@ -0,0 +1,28 @@ +//> using options -Werror + +object Native { + class Obj: + def f: String = "F" +} + +object Types { + + opaque type Node = Native.Obj + + type S = Node + + object S: + def apply(): S = new Node + + extension (s: S) + def f: String = "S" +} + +import Types.* + +object Main { + def main(args: Array[String]): Unit = { + val v: S = S() + println(v.f) + } +} diff --git a/tests/warn/i22706.scala b/tests/warn/i22706.scala new file mode 100644 index 000000000000..5bd642020e1c --- /dev/null +++ b/tests/warn/i22706.scala @@ -0,0 +1,30 @@ +//> using options -Werror + +object Native { + class O { + def f: String = "F" + } + class M extends O +} + +object Types { + opaque type N = Native.O + opaque type GS = Native.M + + type S = N | GS + + object S: + def apply(): S = new N + + extension (s: S) + def f: String = "S" +} + +import Types.* + +object Main { + def main(args: Array[String]): Unit = { + val v: S = S() + println(v.f) + } +} diff --git a/tests/warn/i22727.scala b/tests/warn/i22727.scala new file mode 100644 index 000000000000..c7b1240c7e6b --- /dev/null +++ b/tests/warn/i22727.scala @@ -0,0 +1,14 @@ +//> using options -Werror + +object Main { + type IXY = (Int, Int) + + extension (xy: IXY) { + def map(f: Int => Int): (Int, Int) = (f(xy._1), f(xy._2)) + } + + def main(args: Array[String]): Unit = { + val a = (0, 1) + println(a) + } +} diff --git a/tests/warn/i22742.scala b/tests/warn/i22742.scala new file mode 100644 index 000000000000..0927d29e1a05 --- /dev/null +++ b/tests/warn/i22742.scala @@ -0,0 +1,10 @@ +//> using options -Wunused:all -Werror + +trait Foldable[F[_]]: + def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B + +type Id[A] = A + +given foldableId: Foldable[Id] = + new Foldable[Id]: + def foldLeft[A, B](fa: Id[A], b: B)(f: (B, A) => B): B = b diff --git a/tests/warn/i22744.scala b/tests/warn/i22744.scala new file mode 100644 index 000000000000..655a14b162ce --- /dev/null +++ b/tests/warn/i22744.scala @@ -0,0 +1,35 @@ + +//> using options -Wunused:privates -Werror + +object test { + private trait Foo[A] { val value: A } + + private object Foo { // no warn prefix of implicit value + given int: Foo[Int] = new Foo[Int] { val value = 1 } + } + + val i = summon[Foo[Int]].value +} + +object supplement { + private trait Foo[A] { val value: A } + + private object Foo { // no warn prefix of implicit value + given int: Foo[Int] = new Foo[Int] { val value = 1 } + } + + private def fooValue[A](using f: Foo[A]): A = f.value + + val i = fooValue[Int] +} + +package p: + private trait Foo[A] { val value: A } + + private object Foo { // no warn prefix of implicit value + given int: Foo[Int] = new Foo[Int] { val value = 1 } + } + + private def fooValue[A](using f: Foo[A]): A = f.value + + val i = fooValue[Int] diff --git a/tests/warn/i22746.scala b/tests/warn/i22746.scala new file mode 100644 index 000000000000..79576f5924be --- /dev/null +++ b/tests/warn/i22746.scala @@ -0,0 +1,21 @@ + +//> using options -Wunused:all -Werror + +import java.time.ZonedDateTime + +trait Foo[A] { + def apply(a: A, t: ZonedDateTime): A +} + +extension [A](a: A)(using f: Foo[A]) { + def foo(t: ZonedDateTime = ZonedDateTime.now): A = f(a, t) +} + +def test[I, A](in: I)( + run: I => Either[Throwable, A], + onErr: Throwable => Throwable = identity[Throwable] +): Either[Throwable, A] = + run(in) match { + case Left(t) => Left(onErr(t)) + case r @ Right(_) => r + } diff --git a/tests/warn/i22787.scala b/tests/warn/i22787.scala new file mode 100644 index 000000000000..2d01f12c5f12 --- /dev/null +++ b/tests/warn/i22787.scala @@ -0,0 +1,19 @@ +//> using options -Wunused:imports -Werror + +object tpd: + type Block = Trees.Block + +object Trees: + abstract case class Block(x: Int) + private object Block + val block = new Block(42) {} + +def f(x: Any) = + import tpd.Block + //import Trees.Block + x match + case Block(_) => "yes" + case _ => "no" + +@main def test = println: + f(Trees.block) diff --git a/tests/warn/i22795.check b/tests/warn/i22795.check new file mode 100644 index 000000000000..7e20928d8563 --- /dev/null +++ b/tests/warn/i22795.check @@ -0,0 +1,11 @@ +-- Deprecation Warning: tests/warn/i22795/test_1.scala:4:7 ------------------------------------------------------------- +4 | lib.m() // warn + | ^^^^^^^ + | object A is deprecated + |--------------------------------------------------------------------------------------------------------------------- + |Inline stack trace + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from test_1.scala:5 +5 | + | ^^^^^^^^ + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/warn/i22795/lib_0.scala b/tests/warn/i22795/lib_0.scala new file mode 100644 index 000000000000..13225a865dc4 --- /dev/null +++ b/tests/warn/i22795/lib_0.scala @@ -0,0 +1,11 @@ + +import scala.quoted._ + +@deprecated object A + +object lib: + inline def m() = ${mImpl} + + def mImpl(using Quotes): Expr[Any] = + import quotes.reflect._ + Ref(Symbol.classSymbol("A$").companionModule).asExpr diff --git a/tests/warn/i22795/test_1.scala b/tests/warn/i22795/test_1.scala new file mode 100644 index 000000000000..e75ca03d5cf6 --- /dev/null +++ b/tests/warn/i22795/test_1.scala @@ -0,0 +1,4 @@ +//> using options -deprecation + +@main def Test = println: + lib.m() // warn diff --git a/tests/warn/i22896/J.java b/tests/warn/i22896/J.java new file mode 100644 index 000000000000..d110dd16c8fd --- /dev/null +++ b/tests/warn/i22896/J.java @@ -0,0 +1,6 @@ + +public class J { + private int i = 42; + public int i() { return 27; } + public String i(int j) { return "hello, world"; } +} diff --git a/tests/warn/i22896/s.scala b/tests/warn/i22896/s.scala new file mode 100644 index 000000000000..678c77a9bb3e --- /dev/null +++ b/tests/warn/i22896/s.scala @@ -0,0 +1,13 @@ +//> using options -Werror -Wunused:privates + +def f = + new J: + override val i = -1 // nowarn, trust override + +def g = + new J: + override def i = -1 // nowarn, trust override + +def h = + new J: + override def i() = -1 // nowarn correctly matches signature diff --git a/tests/warn/i22969.scala b/tests/warn/i22969.scala new file mode 100644 index 000000000000..ed0f38f1aba1 --- /dev/null +++ b/tests/warn/i22969.scala @@ -0,0 +1,10 @@ +//> using options -Werror -Wunused:all +import scala.util.NotGiven + +object Test { + def f[T](a: Int)(using NotGiven[T <:< Int]) = a + 2 +} + +trait Furthermore: + type Intish[A] = A <:< Int + def f[A: Intish] = () diff --git a/tests/warn/i22971.scala b/tests/warn/i22971.scala new file mode 100644 index 000000000000..63eee06a8c52 --- /dev/null +++ b/tests/warn/i22971.scala @@ -0,0 +1,34 @@ +//> using options -Wunused:imports + +package p: + + trait Base + class Class extends Base + + abstract class Entity[T: GetType] + + class Thing extends Entity[Class] + + trait GetType[T] + + object GetType { + //implicit object GetTypeClass extends GetType[Class] + implicit val GetTypeClass: GetType[Class] = new GetType[Class] {} + } + object Main { + def main(args: Array[String]): Unit = { + import GetType.* + val e = GetTypeClass + } + } + +package q: + + class C: + def f = + import p.* + GetType.GetTypeClass + def g = + import p.GetType.* + GetTypeClass + class D extends p.Entity[p.Class] diff --git a/tests/warn/i22983.scala b/tests/warn/i22983.scala new file mode 100644 index 000000000000..673ed4b45f67 --- /dev/null +++ b/tests/warn/i22983.scala @@ -0,0 +1,10 @@ +//> using options -Werror -Wunused:imports + +object test1: + object foo: + type X = Int + +object test2: + import test1.foo + + export foo.X diff --git a/tests/warn/i23018.check b/tests/warn/i23018.check new file mode 100644 index 000000000000..c17905524c7e --- /dev/null +++ b/tests/warn/i23018.check @@ -0,0 +1,32 @@ +-- [E190] Potential Issue Warning: tests/warn/i23018.scala:6:17 -------------------------------------------------------- +6 |def tata: Unit = toto // warn pure Discard + | ^^^^ + | Discarded non-Unit value of type Int. Add `: Unit` to discard silently. + | + | longer explanation available when compiling with `-explain` +-- [E175] Potential Issue Warning: tests/warn/i23018.scala:19:37 ------------------------------------------------------- +19 |transparent inline def even: Unit = (42: C) // warn impure discard + | ^^^^^ + | discarded non-Unit value of type C. Add `: Unit` to discard silently. +-- [E190] Potential Issue Warning: tests/warn/i23018.scala:21:22 ------------------------------------------------------- +21 |def literally: Unit = 42 // warn pure Discard + | ^^ + | Discarded non-Unit value of type Int. Add `: Unit` to discard silently. + | + | longer explanation available when compiling with `-explain` +-- [E175] Potential Issue Warning: tests/warn/i23018.scala:24:21 ------------------------------------------------------- +24 |def impurely: Unit = impure // warn impure discard + | ^^^^^^ + | discarded non-Unit value of type Int. Add `: Unit` to discard silently. +-- [E129] Potential Issue Warning: tests/warn/i23018.scala:28:2 -------------------------------------------------------- +28 | () // warn pure expression (but not Discard) + | ^^ + | A pure expression does nothing in statement position + | + | longer explanation available when compiling with `-explain` +-- [E190] Potential Issue Warning: tests/warn/i23018.scala:37:27 ------------------------------------------------------- +37 |def embodied: Unit = bodily(toto) // warn pure Discard + | ^^^^^^^^^^^^ + | Discarded non-Unit value of type Int. Add `: Unit` to discard silently. + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i23018.scala b/tests/warn/i23018.scala new file mode 100644 index 000000000000..3f6b78e08d8b --- /dev/null +++ b/tests/warn/i23018.scala @@ -0,0 +1,39 @@ +//> using options -Wvalue-discard + +import scala.language.experimental.genericNumberLiterals + +transparent inline def toto: Any = 1 +def tata: Unit = toto // warn pure Discard + +transparent inline def uhoh = 42: Unit // nowarn +def hmm: Unit = uhoh + +class C +object C: + val K = C() +class Converter extends scala.util.FromDigits[C]: + inline transparent def fromDigits(digits: String): C = C.K +given Converter() + +transparent inline def odd = (42: C): Unit // nowarn +transparent inline def even: Unit = (42: C) // warn impure discard + +def literally: Unit = 42 // warn pure Discard +def funnily = 42: Unit // nowarn +def impure = ("*" * 42).length +def impurely: Unit = impure // warn impure discard + +def i: Int = ??? +def parenthetically: Int = + () // warn pure expression (but not Discard) + i +transparent inline def reduced = () +def reductively: Int = + reduced // no warn inlined literal unit, e.g., assert(2 + 2 == 4) + i + +// three different expansions, but warning is early at typer, so elaboration doesn't matter +transparent inline def bodily[A](body: => A): A = body +def embodied: Unit = bodily(toto) // warn pure Discard +def disembodied: Unit = bodily(reduced) // nowarn +def um: Unit = bodily(uhoh) // nowarn diff --git a/tests/warn/i23033.scala b/tests/warn/i23033.scala new file mode 100644 index 000000000000..a2a1c515c2e1 --- /dev/null +++ b/tests/warn/i23033.scala @@ -0,0 +1,13 @@ +//> using options -Werror -Wunused:all + +import scala.annotation.unused +import scala.concurrent.ExecutionContext +import scala.util.NotGiven + +object Test { + given [T](using @unused ev: NotGiven[T <:< Int]): AnyRef with {} +} +object Useful: + given [T](using @unused ec: ExecutionContext): AnyRef with {} + +class i23122(@unused param: AnyRef) diff --git a/tests/warn/i23047.scala b/tests/warn/i23047.scala new file mode 100644 index 000000000000..1922797c3185 --- /dev/null +++ b/tests/warn/i23047.scala @@ -0,0 +1,25 @@ +//> using options -Wunused:imports + +package some.example: + package demo: + + import some.example // no warn because enclosing package example is not available as a simple name in some + + object Main { + + def generic[T](x: Any): T = null.asInstanceOf[T] + + def main(args: Array[String]): Unit = { + generic[example.Util](0) + + import some.example.demo.Main // warn + println(Main) + + import some.example.demo // warn because enclosing package demo is available as a simple name + println(demo.Main) + } + } + +package some.example: + + class Util diff --git a/tests/warn/i23164.scala b/tests/warn/i23164.scala new file mode 100644 index 000000000000..b82e21d8d7bb --- /dev/null +++ b/tests/warn/i23164.scala @@ -0,0 +1,72 @@ +class T1[F[_]] +class T2[F[_]] +class T3[F[_]] +class T4[F[_]] +class T5[F[_]] +class T6[F[_]] +class T7[F[_]] +class T8[F[_]] +class T9[F[_]] +class T10[F[_]] +class T11[F[_]] +class T12[F[_]] +class T13[F[_]] +class T14[F[_]] +class T15[F[_]] +class T16[F[_]] +class T17[F[_]] +class T18[F[_]] +class T19[F[_]] +class T20[F[_]] +class T21[F[_]] +class T22[F[_]] + +class Result[F[_]: T1 :T2 :T3 :T4 :T5 :T6 :T7 :T8 :T9 :T10 :T11 :T12 :T13 :T14 :T15 :T16 :T17 :T18 :T19 :T20 :T21 :T22] + +val r = for + t1 <- Option(new T1[Option]) + t2 <- Option(new T2[Option]) + t3 <- Option(new T3[Option]) + t4 <- Option(new T4[Option]) + t5 <- Option(new T5[Option]) + t6 <- Option(new T6[Option]) + t7 <- Option(new T7[Option]) + t8 <- Option(new T8[Option]) + t9 <- Option(new T9[Option]) + t10 <- Option(new T10[Option]) + t11 <- Option(new T11[Option]) + t12 <- Option(new T12[Option]) + t13 <- Option(new T13[Option]) + t14 <- Option(new T14[Option]) + t15 <- Option(new T15[Option]) + t16 <- Option(new T16[Option]) + t17 <- Option(new T17[Option]) + t18 <- Option(new T18[Option]) + t19 <- Option(new T19[Option]) + t20 <- Option(new T20[Option]) + t21 <- Option(new T21[Option]) + t22 <- Option(new T22[Option]) + given T1[Option] = t1 + given T2[Option] = t2 + given T3[Option] = t3 + given T4[Option] = t4 + given T5[Option] = t5 + given T6[Option] = t6 + given T7[Option] = t7 + given T8[Option] = t8 + given T9[Option] = t9 + given T10[Option] = t10 + given T11[Option] = t11 + given T12[Option] = t12 + given T13[Option] = t13 + given T14[Option] = t14 + given T15[Option] = t15 + given T16[Option] = t16 + given T17[Option] = t17 + given T18[Option] = t18 + given T19[Option] = t19 + given T20[Option] = t20 + given T21[Option] = t21 + given T22[Option] = t22 + result <- Option(new Result[Option]) +yield result \ No newline at end of file diff --git a/tests/warn/i23200.check b/tests/warn/i23200.check new file mode 100644 index 000000000000..75d663f0f338 --- /dev/null +++ b/tests/warn/i23200.check @@ -0,0 +1,8 @@ +-- [E198] Unused Symbol Warning: tests/warn/i23200.scala:8:8 ----------------------------------------------------------- +8 | var x: Int = 42 // warn + | ^ + | unset private variable, consider using an immutable val instead +-- [E198] Unused Symbol Warning: tests/warn/i23200.scala:40:6 ---------------------------------------------------------- +40 | var x: Int = 42 // warn local var + | ^ + | unset local variable, consider using an immutable val instead diff --git a/tests/warn/i23200.scala b/tests/warn/i23200.scala new file mode 100644 index 000000000000..613e8df32262 --- /dev/null +++ b/tests/warn/i23200.scala @@ -0,0 +1,41 @@ +//> using options -Wunused:all + +trait Foo +trait Bar + +def `anon not updated` = + new Foo { + var x: Int = 42 // warn + val _ = new Bar: + println(x) + //x = 27 + //x_=(27) + } +def `anon yes updated` = + new Foo { + var x: Int = 42 // nowarn + val _ = new Bar: + println(x) + x = 27 + //x_=(27) + } +def `anon yes updated from nested context` = + new Foo { + var x: Int = 42 // nowarn + val _ = new Bar: + println(x) + x = 27 + //x_=(27) + } +def `anon yes updated in daring use of setter` = + new Foo { + var x: Int = 42 // nowarn + val _ = new Bar: + println(x) + //x = 27 + x_=(27) + } + +def f: Unit = + var x: Int = 42 // warn local var + println(x) diff --git a/tests/warn/i23250.scala b/tests/warn/i23250.scala new file mode 100644 index 000000000000..6ea33b6b0b79 --- /dev/null +++ b/tests/warn/i23250.scala @@ -0,0 +1,17 @@ +//> using options -Wunused:all + +trait MonadError[F[_], E] +type MonadThrow[F[_]] = MonadError[F, Throwable] + +trait MetaStreams[F[_]]: + def use[A]: F[A] = ??? +trait WriteResult + +trait MetaStreamsSyntax: + extension [F[_]](ms: MetaStreams[F])(using MonadThrow[F]) + def setMaxAge(): F[WriteResult] = + summon[MonadThrow[F]] // warn pure expr + ms.use[WriteResult] + + def setTruncateBefore(): F[WriteResult] = + ms.use[WriteResult] diff --git a/tests/warn/i23250b.scala b/tests/warn/i23250b.scala new file mode 100644 index 000000000000..f9e18559fb46 --- /dev/null +++ b/tests/warn/i23250b.scala @@ -0,0 +1,18 @@ +//> using options -Wunused:all + +trait Memberly: + def member: Int + +object Members: + type MemberType <: Memberly + type Empty + +object Test: + import Members.* + + type MT = MemberType + def membered(using MT) = println() // warn abstract type offers member in upper bound + def remembered(using mt: MT) = mt.member + + type Ignore = Empty + def emptily(using Ignore) = println() diff --git a/tests/warn/i23277.scala b/tests/warn/i23277.scala new file mode 100644 index 000000000000..f99f5a373d43 --- /dev/null +++ b/tests/warn/i23277.scala @@ -0,0 +1,22 @@ + +enum Test: + case One + case Two(i: Int) + +object Test: + object Two: + def apply(i: Int): Test.Two = Test.Two(i) // warn + def apply(i: Int, j: Int): Test.Two = new Test.Two(i+j) + def apply(i: Int, s: String): Two = Two(i, s) // warn because unprefixed Two is deemed pure + def apply(): Test.Two = other.apply() // nowarn prefix is method call + def other: this.type = this + +object R extends Runnable: + def r: this.type = this + override def run() = r.run() + +final class C(c: C): + def f(i: Int): Int = c.f(i) + +@main def main = println: + Test.Two(1) diff --git a/tests/warn/i23323.scala b/tests/warn/i23323.scala new file mode 100644 index 000000000000..85a5b0646b6e --- /dev/null +++ b/tests/warn/i23323.scala @@ -0,0 +1,14 @@ +//> using options -Wunused:all + +class C: + val x = new reflect.Selectable: + def f = 42 + def g = 27 + val y: Selectable = new reflect.Selectable: + def f = 42 // warn + def g = 27 // warn + val z = new scala.Selectable: + def f = 42 + def g = 27 + def selectDynamic(name: String): Any = ??? + def applyDynamic(name: String)(args: Any*): Any = ??? diff --git a/tests/warn/i23347.scala b/tests/warn/i23347.scala new file mode 100644 index 000000000000..82e362070f03 --- /dev/null +++ b/tests/warn/i23347.scala @@ -0,0 +1,22 @@ +//> using options -Wunused:all + +object USED { + case class A(value: Int) +} + +object UNUSED { + // In reality UNUSED would contain several other necessary members! + private type A = USED.A // warn private + class B +} + +object Test { + import USED.* + import UNUSED.* + + def foo(a: A): Int = a.value + + def g(b: B) = () + +} + diff --git a/tests/warn/i23349.scala b/tests/warn/i23349.scala new file mode 100644 index 000000000000..4d0fcfc759db --- /dev/null +++ b/tests/warn/i23349.scala @@ -0,0 +1,30 @@ +//> using options -Wunused:explicits,implicits + +// An external class that doesn't get its own `copy` method. +class Foo(val a: String, val b: Int) + +// +// Example 1: add `copy` method via an extension method. +// +extension (self: Foo) + def copy(a: String = self.a, b: Int = self.b): Foo = Foo(a, b) // nowarn + +// +// Example 2: implement `copyFoo` with parameter groups. +// +def copyFoo(foo: Foo)(a: String = foo.a, b: Int = foo.b): Foo = Foo(a, b) + +class C: + def copyFoo(foo: Foo, bar: String)(a: String = foo.a, b: Int = foo.b)(c: String = bar): Foo = Foo(a, b) // warn c + def copyUsing(using foo: Foo, bar: String)(a: String = foo.a, b: Int = foo.b)(c: String = bar): Foo = // warn c + Foo(a, b) + +class K(k: Int)(s: String = "*"*k): + override val toString = s + +class KU(using k: Int)(s: String = "*"*k): + override val toString = s + +class KK(s: String): + def this(k: Int)(s: String = "*"*k) = this(s) + override val toString = s diff --git a/tests/warn/i23494.scala b/tests/warn/i23494.scala new file mode 100644 index 000000000000..c3c0c5a2e0bc --- /dev/null +++ b/tests/warn/i23494.scala @@ -0,0 +1,15 @@ +//> using options -Wunused:implicits + +import scala.deriving.Mirror + +abstract class EnumerationValues[A]: + type Out + +object EnumerationValues: + type Aux[A, B] = EnumerationValues[A] { type Out = B } + + def apply[A, B](): EnumerationValues.Aux[A, B] = new EnumerationValues[A]: + override type Out = B + + given sum[A, B <: Tuple](using mirror: Mirror.SumOf[A] { type MirroredElemTypes = B }): EnumerationValues.Aux[A, A] = + EnumerationValues[A, A]() diff --git a/tests/warn/i23541.check b/tests/warn/i23541.check new file mode 100644 index 000000000000..64ffbd9808d2 --- /dev/null +++ b/tests/warn/i23541.check @@ -0,0 +1,18 @@ +-- [E220] Type Warning: tests/warn/i23541.scala:29:13 ------------------------------------------------------------------ +29 | println(f(using s = "ab")) // warn uses default instead of given // prints "ab" + | ^^^^^^^^^^^^^^^^^ + | Argument for implicit parameter i was supplied using a default argument. + | + | longer explanation available when compiling with `-explain` +-- [E221] Type Warning: tests/warn/i23541.scala:5:17 ------------------------------------------------------------------- +5 | else fun(x - 1)(using p = p + x) // warn recurse uses default (instead of given passed down the stack) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Recursive call used a default argument for parameter q. + | + | longer explanation available when compiling with `-explain` +-- [E221] Type Warning: tests/warn/i23541.scala:9:17 ------------------------------------------------------------------- +9 | else gun(x - 1)(p = p + x) // warn recurse uses default (value not passed down the stack) + | ^^^^^^^^^^^^^^^^^^^^^ + | Recursive call used a default argument for parameter q. + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i23541.scala b/tests/warn/i23541.scala new file mode 100644 index 000000000000..7f374b421ba3 --- /dev/null +++ b/tests/warn/i23541.scala @@ -0,0 +1,30 @@ +//> using options -Wrecurse-with-default + +def fun(x: Int)(using p: Int, q: Int = 0): Int = + if x <= 0 then p * q + else fun(x - 1)(using p = p + x) // warn recurse uses default (instead of given passed down the stack) + +def gun(x: Int)(p: Int, q: Int = 0): Int = + if x <= 0 then p * q + else gun(x - 1)(p = p + x) // warn recurse uses default (value not passed down the stack) + +def nested(using x: Int, y: Int = 42): Int = + def f: Int = nested(using x) // nowarn only self-recursive tailrec is eligible for warning + f + +def f(using s: String, i: Int = 1): String = s * i +def g(using s: String)(using i: Int = 1): String = s * i + +@main def Test = + println(fun(3)(using p = 0, q = 1)) + locally: + given String = "ab" + println(f) // prints "ab" + println(g) // prints "ab" + locally: + println(f(using s = "ab")) // prints "ab" + println(g(using s = "ab")) // prints "ab" + locally: + given Int = 2 + println(f(using s = "ab")) // warn uses default instead of given // prints "ab" + println(g(using s = "ab")) // prints "abab" diff --git a/tests/warn/i23620b.check b/tests/warn/i23620b.check new file mode 100644 index 000000000000..556448701026 --- /dev/null +++ b/tests/warn/i23620b.check @@ -0,0 +1,24 @@ +-- [E029] Pattern Match Exhaustivity Warning: tests/warn/i23620b.scala:20:2 -------------------------------------------- +20 | p match // warn + | ^ + | match may not be exhaustive. + | + | It would fail on pattern case: _: Bar + | + | longer explanation available when compiling with `-explain` +-- [E029] Pattern Match Exhaustivity Warning: tests/warn/i23620b.scala:23:2 -------------------------------------------- +23 | p2 match { // warn + | ^^ + | match may not be exhaustive. + | + | It would fail on pattern case: _: Bar + | + | longer explanation available when compiling with `-explain` +-- [E029] Pattern Match Exhaustivity Warning: tests/warn/i23620b.scala:37:2 -------------------------------------------- +37 | x match // warn + | ^ + | match may not be exhaustive. + | + | It would fail on pattern case: B + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i23620b.scala b/tests/warn/i23620b.scala new file mode 100644 index 000000000000..dfdac45347a2 --- /dev/null +++ b/tests/warn/i23620b.scala @@ -0,0 +1,38 @@ +trait Foo +trait Bar + +type FooOrBar = FooOrBar.Type +object FooOrBar: + opaque type Type <: (Foo | Bar) = Foo | Bar + + def bar: FooOrBar = new Bar {} + +type OnlyFoo = OnlyFoo.Type +object OnlyFoo: + opaque type Type <: (Foo | Bar) = Foo + + def foo: OnlyFoo = new Foo {} + +@main def main = + val p: FooOrBar= FooOrBar.bar + val p2: OnlyFoo = OnlyFoo.foo + + p match // warn + case _: Foo => println("foo") + + p2 match { // warn + case _: Foo => println("foo") + } + +sealed trait S +trait Z + +case object A extends S, Z +case object B extends S, Z + +trait HasT: + type T <: S & Z + +def nonExhaustive(h: HasT, x: h.T) = + x match // warn + case A => () diff --git a/tests/warn/i23651.scala b/tests/warn/i23651.scala new file mode 100644 index 000000000000..c81ddb4249f8 --- /dev/null +++ b/tests/warn/i23651.scala @@ -0,0 +1,27 @@ +//> using options -deprecation -Wunused:nowarn + +import scala.annotation.nowarn + +@deprecated +class A + +@deprecated +class B + +@nowarn("msg=trait C is deprecated") // warn + // @nowarn annotation does not suppress any warnings +@nowarn("msg=class A is deprecated") +@nowarn("cat=deprecation&msg=class A is deprecated") // warn + // @nowarn annotation does not suppress any warnings but matches a diagnostic +@nowarn("cat=deprecation&msg=class B is deprecated") +trait C1: + def a: A + def b: B + +@nowarn("cat=deprecation&msg=class B is deprecated") +@nowarn("cat=deprecation&msg=class B is deprecated") // warn + // @nowarn annotation does not suppress any warnings but matches a diagnostic +@nowarn("cat=deprecation&msg=class A is deprecated") +trait C2: + def a: A + def b: B diff --git a/tests/warn/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/i23694.scala b/tests/warn/i23694.scala new file mode 100644 index 000000000000..e407a2955dc9 --- /dev/null +++ b/tests/warn/i23694.scala @@ -0,0 +1,44 @@ +//> using options -Wunused:privates + +trait A: + def get: Any = new A.C // select has type [T] =>> C[T] + def cc: Any = new A.CC // select has type CC + +object A: + private class C[T] + private type CC[T] = C[T] + +trait B: + def get: Any = new B.C + +object B: + private class C + +// duplicate issue #23960 +package net.marek.tyre.automaton: + + private[tyre] class TyreCompiler[IN <: Tuple, R]: + private def compile[IS <: Tuple, T](xs: List[T]): String = xs match // warn + case _: List[t] => + Loop[IS, t](null.asInstanceOf[t]).build + private class Loop[IS <: Tuple, T]( + val i: T, + ): + def build = "27" + +// duplicate issue #24326 + +trait Local[F[_], E] { + + def liftTo[G[_]]: Local[G, E] = + new Local.Lifted(this) +} + +object Local { + private final class Lifted[F[_], G[_], E]( + val underlying: Local[F, E], + ) extends Local[G, E] { + override def liftTo[H[_]]: Local[H, E] = + new Lifted(underlying) + } +} 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/i23758.scala b/tests/warn/i23758.scala new file mode 100644 index 000000000000..08acb8aa588c --- /dev/null +++ b/tests/warn/i23758.scala @@ -0,0 +1,11 @@ +//> using options -Wunused:imports + +import scala.util.Try as _ // warn + +class Promise(greeting: String): + override def toString = greeting + +@main def test = println: + import scala.concurrent.{Promise as _, *}, ExecutionContext.Implicits.given + val promise = new Promise("world") + Future(s"hello, $promise") diff --git a/tests/warn/i23905.check b/tests/warn/i23905.check new file mode 100644 index 000000000000..0a456ea319b0 --- /dev/null +++ b/tests/warn/i23905.check @@ -0,0 +1,16 @@ +-- Deprecation Warning: tests/warn/i23905/Test_2.scala:3:0 ------------------------------------------------------------- +3 |@DeprecatedAnnotation // warn + |^^^^^^^^^^^^^^^^^^^^^ + |class DeprecatedAnnotation is deprecated +-- Deprecation Warning: tests/warn/i23905/Test_2.scala:6:0 ------------------------------------------------------------- +6 |@DeprecatedAnnotation // warn + |^^^^^^^^^^^^^^^^^^^^^ + |class DeprecatedAnnotation is deprecated +-- Deprecation Warning: tests/warn/i23905/Test_2.scala:9:0 ------------------------------------------------------------- +9 |@DeprecatedAnnotation // warn + |^^^^^^^^^^^^^^^^^^^^^ + |class DeprecatedAnnotation is deprecated +-- Deprecation Warning: tests/warn/i23905/Test_2.scala:12:0 ------------------------------------------------------------ +12 |@DeprecatedAnnotation // warn + |^^^^^^^^^^^^^^^^^^^^^ + |class DeprecatedAnnotation is deprecated diff --git a/tests/warn/i23905/Annot_1.scala b/tests/warn/i23905/Annot_1.scala new file mode 100644 index 000000000000..e13664c67ebd --- /dev/null +++ b/tests/warn/i23905/Annot_1.scala @@ -0,0 +1,2 @@ +@deprecated +class DeprecatedAnnotation extends annotation.StaticAnnotation diff --git a/tests/warn/i23905/Test_2.scala b/tests/warn/i23905/Test_2.scala new file mode 100644 index 000000000000..248e923a5d83 --- /dev/null +++ b/tests/warn/i23905/Test_2.scala @@ -0,0 +1,13 @@ +//> using options -deprecation + +@DeprecatedAnnotation // warn +object Test1 + +@DeprecatedAnnotation // warn +class Test2 + +@DeprecatedAnnotation // warn +def Test3 = ??? + +@DeprecatedAnnotation // warn +val Test4 = ??? diff --git a/tests/warn/i24034/circe.scala b/tests/warn/i24034/circe.scala new file mode 100644 index 000000000000..f8db6bffad69 --- /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 '{ // LTS specific warnings + ${ _ }: Mirror.ProductOf[A] { + type MirroredLabel = l // warn + type MirroredElemLabels = el // warn + type MirroredElemTypes = et // warn + } + } => + '{ + 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/i24246.check b/tests/warn/i24246.check new file mode 100644 index 000000000000..fa197419d86d --- /dev/null +++ b/tests/warn/i24246.check @@ -0,0 +1,8 @@ +-- [E029] Pattern Match Exhaustivity Warning: tests/warn/i24246.scala:8:2 ---------------------------------------------- +8 | x match { // warn + | ^ + | match may not be exhaustive. + | + | It would fail on pattern case: ZZ + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i24246.scala b/tests/warn/i24246.scala new file mode 100644 index 000000000000..f5bd02692774 --- /dev/null +++ b/tests/warn/i24246.scala @@ -0,0 +1,10 @@ +trait X + +sealed trait Y +case object YY extends Y, X +case object ZZ extends Y, X + +def foo[A <: X & Y](x: A): Unit = + x match { // warn + case YY => () + } 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/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] + } +} diff --git a/tests/warn/i24322.scala b/tests/warn/i24322.scala new file mode 100644 index 000000000000..a57cf48dbf7c --- /dev/null +++ b/tests/warn/i24322.scala @@ -0,0 +1,13 @@ +trait A[+T]: + def x: T +trait B[+T] extends A[T]: + def y: T + +object Troll extends A[Int] with B[Any]: + def x: Int = 0 + def y: Any = "" + +def f[T](a: A[T]): T = a match { + case b: B[T] => b.y // warn + case _ => a.x +} diff --git a/tests/warn/i24457.scala b/tests/warn/i24457.scala new file mode 100644 index 000000000000..76b7df8ed9c9 --- /dev/null +++ b/tests/warn/i24457.scala @@ -0,0 +1,14 @@ +//> using options -Werror -Wunused:all +///> using scala 3.8.0-RC1 + +object test { + private case class Impl() + inline def makeImpl(): Any = Impl() // inline is not germane + + def seth = + case class Loop() + println(Loop()) +} + +@main def main = println: + test.makeImpl() diff --git a/tests/warn/i24459.scala b/tests/warn/i24459.scala new file mode 100644 index 000000000000..06aba7348398 --- /dev/null +++ b/tests/warn/i24459.scala @@ -0,0 +1,17 @@ +//> using options -Werror -Wunused:all +///> using scala 3.8.0-RC1 + +trait Suite { + inline def test(body: => Any): Any = body +} + +object Test { + private case class I() +} + +class Test extends Suite { + test { + val i = Test.I() + i.hashCode + } +} diff --git a/tests/warn/i24562.scala b/tests/warn/i24562.scala new file mode 100644 index 000000000000..d025cd41155c --- /dev/null +++ b/tests/warn/i24562.scala @@ -0,0 +1,42 @@ +//> using options -Wunused:imports + +package util: + class Random(s: String): + def this() = this("") + +package bar: + export util.Random + +package foo: + import bar.Random + def test = Random(null) + +package baz: + import bar.* + def test = Random(null) + +// cf tests/pos/i12299a.scala +package aliased: + object Outer: + + object Wrap: + export Outer.Bar + + class Bar + + def test = + import Wrap.* // warn! not a naming error because a benign alias + val localBar = Bar() // not Wrap.Bar! + val localNewBar = new Bar + +package i24879: + class C: + class D + + class Baz: + val c = new C + export c.* + + def test = + val baz = Baz() + baz.D() // no warn and no crash diff --git a/tests/warn/i24633.scala b/tests/warn/i24633.scala new file mode 100644 index 000000000000..b23c573e27da --- /dev/null +++ b/tests/warn/i24633.scala @@ -0,0 +1,20 @@ +//> using options -Werror -Wunused:imports + +object foo { + val min = 0 + val max = 1 +} + +def test(max: Int) = { + import foo.{max as _, *} + s"$min - $max" +} + +def local = + val max = 42 + import foo.{max as _, *} + s"$min - $max" + +class Limit(max: Int): + import foo.{max as _, *} + def test = s"$min - $max" diff --git a/tests/warn/i24646.scala b/tests/warn/i24646.scala new file mode 100644 index 000000000000..5bcfd5ffa7fc --- /dev/null +++ b/tests/warn/i24646.scala @@ -0,0 +1,13 @@ +//> using options -Wunused:all + +import Test.OnlyFirst +import Test.OnlyThird + +enum Test: + case First extends Test with OnlyFirst + case Second extends Test + case Third(s: String) extends Test with OnlyThird + +object Test: + sealed trait OnlyFirst + sealed trait OnlyThird diff --git a/tests/warn/i24665.scala b/tests/warn/i24665.scala new file mode 100644 index 000000000000..c01271a6f3be --- /dev/null +++ b/tests/warn/i24665.scala @@ -0,0 +1,5 @@ +//> using options -Werror -Wunused:all + +def method[A](f: [g[_]] => Unit => g[A]) = () + +@main def main = method([g[_]] => (_: Unit) => null.asInstanceOf[g[Int]]) diff --git a/tests/warn/i24698.scala b/tests/warn/i24698.scala new file mode 100644 index 000000000000..d03238018652 --- /dev/null +++ b/tests/warn/i24698.scala @@ -0,0 +1,22 @@ +//> using options -Wunused:all + +trait TC[X] { + def notTrivial: Int +} + +class C[T: TC]() { // warn implicit TC used only for new instance of owner + def mod: C[T] = new C +} + +class D(val i: Int): + def this(s: String) = this(s.toInt) // not new + +class E[T](tc: TC[T]): // warn + def mod: E[T] = new E(tc) + +class F[T: TC](i: Int): // warn // warn + def mod: F[T] = new F(i) + +class G(i: Int): // warn + class Inner(i: Int): + def g = new G(i) diff --git a/tests/warn/i24750.check b/tests/warn/i24750.check new file mode 100644 index 000000000000..c35c60d6d369 --- /dev/null +++ b/tests/warn/i24750.check @@ -0,0 +1,8 @@ +-- [E029] Pattern Match Exhaustivity Warning: tests/warn/i24750.scala:6:13 --------------------------------------------- +6 | duration.unit match // warn + | ^^^^^^^^^^^^^ + | match may not be exhaustive. + | + | It would fail on pattern case: DAYS + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/i24750.scala b/tests/warn/i24750.scala new file mode 100644 index 000000000000..4ed92e8d18e7 --- /dev/null +++ b/tests/warn/i24750.scala @@ -0,0 +1,12 @@ +import scala.concurrent.duration.* + +object i24750: + + def f(duration: FiniteDuration): Unit = + duration.unit match // warn + case NANOSECONDS => + case MICROSECONDS => + case MILLISECONDS => + case SECONDS => + case MINUTES => + case HOURS => diff --git a/tests/warn/i25004/macro_1.scala b/tests/warn/i25004/macro_1.scala new file mode 100644 index 000000000000..f2adef8225a5 --- /dev/null +++ b/tests/warn/i25004/macro_1.scala @@ -0,0 +1,12 @@ +import scala.quoted.* + +object TestBuilder: + transparent inline def test(inline expr: Unit): Any = + ${ TestBuilder.processTests('expr) } + + def processTests(using Quotes)(body: Expr[Unit]): Expr[Any] = + import quotes.reflect.* + val treeMap = new TreeMap {} + val mapped = + treeMap.transformTerm(body.asTerm)(Symbol.spliceOwner) + mapped.asExpr diff --git a/tests/warn/i25004/test_2.scala b/tests/warn/i25004/test_2.scala new file mode 100644 index 000000000000..5b22b772421f --- /dev/null +++ b/tests/warn/i25004/test_2.scala @@ -0,0 +1,6 @@ +//> using options -Werror -Wunused:all -Xcheck-macros +@main def Test = + TestBuilder.test: + val start @ _: String = "" // Converting this to a match expression resolves the error + // Alternative: val Seq(start) = Seq("") + print(start) diff --git a/tests/warn/i25004b/macro_1.scala b/tests/warn/i25004b/macro_1.scala new file mode 100644 index 000000000000..bc00ffa80f19 --- /dev/null +++ b/tests/warn/i25004b/macro_1.scala @@ -0,0 +1,12 @@ +import scala.quoted.* + +object TestBuilder: + inline def test(expr: Unit): Any = + ${ TestBuilder.processTests('expr) } + + def processTests(using Quotes)(body: Expr[Unit]): Expr[Any] = + import quotes.reflect.* + val treeMap = new TreeMap {} + val mapped = + treeMap.transformTerm(body.asTerm)(Symbol.spliceOwner) + mapped.asExpr diff --git a/tests/warn/i25004b/test_2.scala b/tests/warn/i25004b/test_2.scala new file mode 100644 index 000000000000..baef613c2b47 --- /dev/null +++ b/tests/warn/i25004b/test_2.scala @@ -0,0 +1,6 @@ +//> using options -Werror -Wunused:all +@main def Test = + TestBuilder.test: + val start @ _: String = "" // Converting this to a match expression resolves the error + // Alternative: val Seq(start) = Seq("") + print(start) diff --git a/tests/warn/i25094.check b/tests/warn/i25094.check new file mode 100644 index 000000000000..7be59e2545d9 --- /dev/null +++ b/tests/warn/i25094.check @@ -0,0 +1,5 @@ +-- [E225] Type Warning: tests/warn/i25094.scala:6:19 ------------------------------------------------------------------- +6 | def f2(x: Int) = g(if x > 0 then x else null) // warn + | ^ + | A type argument was inferred to be union type Int | Null + | This may indicate a programming error. diff --git a/tests/warn/i25094.scala b/tests/warn/i25094.scala new file mode 100644 index 000000000000..bcb217b7b39b --- /dev/null +++ b/tests/warn/i25094.scala @@ -0,0 +1,6 @@ +//> using options -Winfer-union + +object TestInferredUnion: + def g[T](x: T) = x + def f1(x: Int | Null) = g(x) + def f2(x: Int) = g(if x > 0 then x else null) // warn \ No newline at end of file diff --git a/tests/warn/i25100.scala b/tests/warn/i25100.scala new file mode 100644 index 000000000000..186482d0d1cc --- /dev/null +++ b/tests/warn/i25100.scala @@ -0,0 +1,30 @@ +//> using options -language:strictEquality -Werror -Wunused:all + +case class Foo(value: String) +case object Bar + +object Import { + given CanEqual[Foo | Bar.type, Foo | Bar.type] = CanEqual.derived +} + +@main +def main(): Unit = { + import Import.given + val i: Foo | Bar.type = Foo("Foo") + + i match { + case i: Foo => println("i is a Foo") + case Bar => println("i is a Long") + } +} + +class StatusCode(val code: Int) extends AnyVal +val Ok = StatusCode(200) + +def amethod = + given CanEqual[StatusCode, StatusCode] = CanEqual.derived + Ok match + case Ok => + "ok" + case _ => + "other" diff --git a/tests/warn/i25158.scala b/tests/warn/i25158.scala new file mode 100644 index 000000000000..73f720e9b47e --- /dev/null +++ b/tests/warn/i25158.scala @@ -0,0 +1,57 @@ +object Math { + class G { + def print(): Unit = ??? + } + class GS extends G + + opaque type Geo = G + opaque type GeoS <: Geo = GS + + object Geo: + def apply(): Geo = new G + extension (g: Geo) def print(): Unit = println("Geo clone called") + extension (g: Geo) def toString(): String = "I am Geo" // warn + + object GeoS: + def apply(): GeoS = new GS + extension (g: GeoS) def print(): Unit = println("GeoS clone called") // no warn +} + +import Math.* + +object Main { + def main(args: Array[String]): Unit = { + val g = Geo() + val s = GeoS() + g.print() + s.print() + println(g.toString()) + } +} + +object Calculus_B: + trait Printable: + def print(): Unit = () + + private class PrintableGeoS extends Printable + + opaque type GeoS <: Printable = PrintableGeoS + + object GeoS: + def apply(): GeoS = PrintableGeoS() + extension (g: GeoS) def print(): Unit = ??? // warn + extension (g: GeoS) /*override*/ def toString(): String = ??? // warn (override is also error) + +object Calculus_C: + trait Printable: + def print(): Unit = () + + private class PrintableGeoS extends Printable + + type P = Printable + opaque type GeoS <: P = PrintableGeoS + + object GeoS: + def apply(): GeoS = PrintableGeoS() + extension (g: GeoS) def print(): Unit = ??? // warn + extension (g: GeoS) /*override*/ def toString(): String = ??? // warn (override is also error) diff --git a/tests/warn/i25227.scala b/tests/warn/i25227.scala new file mode 100644 index 000000000000..df94fdb9a261 --- /dev/null +++ b/tests/warn/i25227.scala @@ -0,0 +1,21 @@ +//> using options -Wunused:all + +import scala.language.strictEquality + +object Givens: + given CanEqual[Any, Null] = CanEqual.derived + given CanEqual[Null, Any] = CanEqual.derived + +object Test: + import Givens.given // no warn: used by `case null =>` + + def m(value: Any): Boolean = value match + case null => true + case _ => false + +object TestUnused: + import Givens.given // warn: not used here + + def m(value: Any): Boolean = value match + case _: String => true + case _ => false diff --git a/tests/warn/i25548.check b/tests/warn/i25548.check new file mode 100644 index 000000000000..01f8d494e0e4 --- /dev/null +++ b/tests/warn/i25548.check @@ -0,0 +1,4 @@ +-- Deprecation Warning: tests/warn/i25548.scala:5:43 ------------------------------------------------------------------- +5 | def apply(value: String): Location = new Location(value) // warn + | ^^^^^^^^ + | constructor Location in class Location is deprecated diff --git a/tests/warn/i25548.scala b/tests/warn/i25548.scala new file mode 100644 index 000000000000..7fd55268d98e --- /dev/null +++ b/tests/warn/i25548.scala @@ -0,0 +1,6 @@ +//> using options -Wconf:cat=deprecation:warning + +class Location @deprecated("", "") (value: String) +object Location { + def apply(value: String): Location = new Location(value) // warn +} diff --git a/tests/warn/i25548b.scala b/tests/warn/i25548b.scala new file mode 100644 index 000000000000..3e7c41ee7dc0 --- /dev/null +++ b/tests/warn/i25548b.scala @@ -0,0 +1,11 @@ +//> using options -Wconf:cat=deprecation&msg=bruh:warning + +class Location @deprecated("No loke", since="1.0") (value: String) +object Location { + def apply(value: String): Location = new Location(value) // nopos-warn there was 1 deprecation warning; re-run with -deprecation for details +} + +class Brocation @deprecated("bruh!", since="1.0") (value: String) +object Brocation { + def apply(value: String): Brocation = new Brocation(value) // warn reported deprecated since 1.0: bruh! +} diff --git a/tests/warn/i25553.scala b/tests/warn/i25553.scala new file mode 100644 index 000000000000..4d5cd7195d22 --- /dev/null +++ b/tests/warn/i25553.scala @@ -0,0 +1,16 @@ +//> using options -Werror -Wvalue-discard + +trait BaseKyoDataTest: + type Assertion + def assertionSuccess: Assertion + transparent inline def typeCheck(inline code: String): Assertion = assertionSuccess + def pendingUntilFixed(f: => Unit): Unit = f + + def test(): Unit = + pendingUntilFixed { + typeCheck("Abort.run(Abort.fail(new Test[Int]))"): Unit + } + +object Test extends BaseKyoDataTest: + type Assertion = Int + def assertionSuccess: Assertion = 1 diff --git a/tests/warn/i3323.scala b/tests/warn/i3323.scala new file mode 100644 index 000000000000..e409134ff0ba --- /dev/null +++ b/tests/warn/i3323.scala @@ -0,0 +1,8 @@ +//> using options -Werror +class Foo { + def foo[A](lss: List[List[A]]): Unit = { + lss match { + case xss: List[List[A]] => // no warn erasure + } + } +} diff --git a/tests/warn/infer-or-type.check b/tests/warn/infer-or-type.check new file mode 100644 index 000000000000..a570f95ba328 --- /dev/null +++ b/tests/warn/infer-or-type.check @@ -0,0 +1,10 @@ +-- [E225] Type Warning: tests/warn/infer-or-type.scala:6:18 ------------------------------------------------------------ +6 | val _ = List(1).contains("") // warn + | ^^^^^^^^^^^^^^^^ + | A type argument was inferred to be union type Int | String + | This may indicate a programming error. +-- [E225] Type Warning: tests/warn/infer-or-type.scala:7:10 ------------------------------------------------------------ +7 | val _ = Pair(1, "") // warn + | ^^^^ + | A type argument was inferred to be union type Int | String + | This may indicate a programming error. diff --git a/tests/warn/infer-or-type.scala b/tests/warn/infer-or-type.scala new file mode 100644 index 000000000000..7b8f2e2b5762 --- /dev/null +++ b/tests/warn/infer-or-type.scala @@ -0,0 +1,8 @@ +//> using options -Winfer-union + +case class Pair[A](x: A, y: A) + +def test = { + val _ = List(1).contains("") // warn + val _ = Pair(1, "") // warn +} \ No newline at end of file diff --git a/tests/warn/infer-union-warning-id.check b/tests/warn/infer-union-warning-id.check new file mode 100644 index 000000000000..310c67f5832a --- /dev/null +++ b/tests/warn/infer-union-warning-id.check @@ -0,0 +1,6 @@ +-- [E225] Type Warning: tests/warn/infer-union-warning-id.scala:3:8 +3 |val _ = Pair(1, "") + | ^^^^ + | A type argument was inferred to be union type Int | String + | This may indicate a programming error. +1 warning found \ No newline at end of file diff --git a/tests/warn/multiple-entry-points.check b/tests/warn/multiple-entry-points.check new file mode 100644 index 000000000000..4c6747e0409a --- /dev/null +++ b/tests/warn/multiple-entry-points.check @@ -0,0 +1,5 @@ +[log genBCode] No Main-Class due to multiple entry points: + Second + First +[log genBCode] Adding static forwarder for 'method main' from First to 'module class First$' +[log genBCode] Adding static forwarder for 'method main' from Second to 'module class Second$' diff --git a/tests/warn/multiple-entry-points/1_First.scala b/tests/warn/multiple-entry-points/1_First.scala new file mode 100644 index 000000000000..940dd7d324f9 --- /dev/null +++ b/tests/warn/multiple-entry-points/1_First.scala @@ -0,0 +1,6 @@ +//> using option -Ylog:genBCode + +object First { + def main(args: Array[String]): Unit = + println("first") +} diff --git a/tests/warn/multiple-entry-points/2_Second.scala b/tests/warn/multiple-entry-points/2_Second.scala new file mode 100644 index 000000000000..2107a606c994 --- /dev/null +++ b/tests/warn/multiple-entry-points/2_Second.scala @@ -0,0 +1,4 @@ +object Second { + def main(args: Array[String]): Unit = + println("second") +} diff --git a/tests/warn/nonunit-statement.check b/tests/warn/nonunit-statement.check index 7e7939e1c163..46a75dfd3065 100644 --- a/tests/warn/nonunit-statement.check +++ b/tests/warn/nonunit-statement.check @@ -56,12 +56,9 @@ 96 | if (b) { // warn, at least one branch looks interesting | ^ | unused value of type Int + | 97 | println("true") - 98 | i - 99 | } -100 | else { -101 | println("false") -102 | j + 98 |... 103 | } -- [E176] Potential Issue Warning: tests/warn/nonunit-statement.scala:116:4 -------------------------------------------- 116 | set += a // warn because cannot know whether the `set` was supposed to be consumed or assigned diff --git a/tests/warn/nowarn.check b/tests/warn/nowarn.check new file mode 100644 index 000000000000..89ff8d51161b --- /dev/null +++ b/tests/warn/nowarn.check @@ -0,0 +1,110 @@ +-- [E002] Syntax Warning: tests/warn/nowarn.scala:5:10 ----------------------------------------------------------------- +5 |def t1a = try 1 // warn (parser) + | ^^^^^ + | A try without catch or finally is equivalent to putting + | its body in a block; no exceptions are handled. + | + | longer explanation available when compiling with `-explain` +-- [E002] Syntax Warning: tests/warn/nowarn.scala:19:25 ---------------------------------------------------------------- +19 |@nowarn(o.inl) def t2d = try 1 // warn // warn (`inl` is not a compile-time constant) + | ^^^^^ + | A try without catch or finally is equivalent to putting + | its body in a block; no exceptions are handled. + | + | longer explanation available when compiling with `-explain` +-- [E002] Syntax Warning: tests/warn/nowarn.scala:27:26 ---------------------------------------------------------------- +27 |@nowarn("id=1") def t4d = try 1 // warn // warn (unused nowarn, wrong id) + | ^^^^^ + | A try without catch or finally is equivalent to putting + | its body in a block; no exceptions are handled. + | + | longer explanation available when compiling with `-explain` +-- [E002] Syntax Warning: tests/warn/nowarn.scala:29:28 ---------------------------------------------------------------- +29 |@nowarn("verbose") def t5 = try 1 // warn with details + | ^^^^^ + | A try without catch or finally is equivalent to putting + | its body in a block; no exceptions are handled. + |Matching filters for @nowarn or -Wconf: + | - id=E2 + | - name=EmptyCatchAndFinallyBlock + | + | longer explanation available when compiling with `-explain` +-- [E129] Potential Issue Warning: tests/warn/nowarn.scala:9:11 -------------------------------------------------------- +9 |def t2 = { 1; 2 } // warn (the invalid nowarn doesn't silence anything) + | ^ + | A pure expression does nothing in statement position + | + | longer explanation available when compiling with `-explain` +-- Warning: tests/warn/nowarn.scala:8:8 -------------------------------------------------------------------------------- +8 |@nowarn("wat?") // warn (typer, invalid filter) + | ^^^^^^ + | Invalid message filter + | unknown filter: wat? +-- [E129] Potential Issue Warning: tests/warn/nowarn.scala:12:12 ------------------------------------------------------- +12 |def t2a = { 1; 2 } // warn (invalid nowarn doesn't silence) + | ^ + | A pure expression does nothing in statement position + | + | longer explanation available when compiling with `-explain` +-- Warning: tests/warn/nowarn.scala:11:8 ------------------------------------------------------------------------------- +11 |@nowarn(t1a.toString) // warn (typer, argument not a compile-time constant) + | ^^^^^^^^^^^^ + | filter needs to be a compile-time constant string +-- Warning: tests/warn/nowarn.scala:19:10 ------------------------------------------------------------------------------ +19 |@nowarn(o.inl) def t2d = try 1 // warn // warn (`inl` is not a compile-time constant) + | ^^^^^ + | filter needs to be a compile-time constant string +-- Deprecation Warning: tests/warn/nowarn.scala:33:10 ------------------------------------------------------------------ +33 |def t6a = f // warn (refchecks, deprecation) + | ^ + | method f is deprecated +-- Deprecation Warning: tests/warn/nowarn.scala:36:30 ------------------------------------------------------------------ +36 |@nowarn("msg=fish") def t6d = f // warn (unused nowarn) // warn (deprecation) + | ^ + | method f is deprecated +-- Deprecation Warning: tests/warn/nowarn.scala:43:10 ------------------------------------------------------------------ +43 |def t7c = f // warn (deprecation) + | ^ + | method f is deprecated +-- [E092] Pattern Match Unchecked Warning: tests/warn/nowarn.scala:49:7 ------------------------------------------------ +49 | case _: List[Int] => 0 // warn (patmat, unchecked) + | ^ + |the type test for List[Int] cannot be checked at runtime because its type arguments can't be determined from Any + | + | longer explanation available when compiling with `-explain` +-- Warning: tests/warn/nowarn.scala:27:1 ------------------------------------------------------------------------------- +27 |@nowarn("id=1") def t4d = try 1 // warn // warn (unused nowarn, wrong id) + |^^^^^^^^^^^^^^^ + |@nowarn annotation does not suppress any warnings +-- Warning: tests/warn/nowarn.scala:36:1 ------------------------------------------------------------------------------- +36 |@nowarn("msg=fish") def t6d = f // warn (unused nowarn) // warn (deprecation) + |^^^^^^^^^^^^^^^^^^^ + |@nowarn annotation does not suppress any warnings +-- Warning: tests/warn/nowarn.scala:44:5 ------------------------------------------------------------------------------- +44 | : @nowarn("msg=fish") // warn (unused nowarn) + | ^^^^^^^^^^^^^^^^^^^ + | @nowarn annotation does not suppress any warnings +-- Warning: tests/warn/nowarn.scala:56:0 ------------------------------------------------------------------------------- +56 |@nowarn def t9a = { 1: @nowarn; 2 } // warn (outer @nowarn is unused) + |^^^^^^^ + |@nowarn annotation does not suppress any warnings but matches a diagnostic +-- Warning: tests/warn/nowarn.scala:57:27 ------------------------------------------------------------------------------ +57 |@nowarn def t9b = { 1: Int @nowarn; 2 } // warn (inner @nowarn is unused, it covers the type, not the expression) + | ^^^^^^^ + | @nowarn annotation does not suppress any warnings +-- Warning: tests/warn/nowarn.scala:62:0 ------------------------------------------------------------------------------- +62 |@nowarn @ann(f) def t10b = 0 // warn (unused nowarn) + |^^^^^^^ + |@nowarn annotation does not suppress any warnings +-- Warning: tests/warn/nowarn.scala:63:8 ------------------------------------------------------------------------------- +63 |@ann(f: @nowarn) def t10c = 0 // warn (unused nowarn), should be silent + | ^^^^^^^ + | @nowarn annotation does not suppress any warnings +-- Warning: tests/warn/nowarn.scala:66:0 ------------------------------------------------------------------------------- +66 |@nowarn class I1a { // warn (unused nowarn) + |^^^^^^^ + |@nowarn annotation does not suppress any warnings but matches a diagnostic +-- Warning: tests/warn/nowarn.scala:71:0 ------------------------------------------------------------------------------- +71 |@nowarn class I1b { // warn (unused nowarn) + |^^^^^^^ + |@nowarn annotation does not suppress any warnings but matches a diagnostic diff --git a/tests/warn/nowarn.scala b/tests/warn/nowarn.scala new file mode 100644 index 000000000000..f6cacdd677d0 --- /dev/null +++ b/tests/warn/nowarn.scala @@ -0,0 +1,83 @@ +//> using options -deprecation -Wunused:nowarn + +import scala.annotation.{nowarn, Annotation} + +def t1a = try 1 // warn (parser) +@nowarn("msg=try without catch") def t1b = try 1 + +@nowarn("wat?") // warn (typer, invalid filter) +def t2 = { 1; 2 } // warn (the invalid nowarn doesn't silence anything) + +@nowarn(t1a.toString) // warn (typer, argument not a compile-time constant) +def t2a = { 1; 2 } // warn (invalid nowarn doesn't silence) + +object o: + final val const = "msg=try" + inline def inl = "msg=try" + +@nowarn(o.const) def t2c = try 1 // no warn +@nowarn(o.inl) def t2d = try 1 // warn // warn (`inl` is not a compile-time constant) + +@nowarn("id=E129") def t3a = { 1; 2 } +@nowarn("name=PureExpressionInStatementPosition") def t3b = { 1; 2 } + +@nowarn("id=E002") def t4a = try 1 +@nowarn("id=E2") def t4b = try 1 +@nowarn("id=2") def t4c = try 1 +@nowarn("id=1") def t4d = try 1 // warn // warn (unused nowarn, wrong id) + +@nowarn("verbose") def t5 = try 1 // warn with details + +@deprecated def f = 0 + +def t6a = f // warn (refchecks, deprecation) +@nowarn("cat=deprecation") def t6b = f +@nowarn("msg=deprecated") def t6c = f +@nowarn("msg=fish") def t6d = f // warn (unused nowarn) // warn (deprecation) +@nowarn("") def t6e = f +@nowarn def t6f = f + +def t7a = f: @nowarn("cat=deprecation") +def t7b = f + : @nowarn("msg=deprecated") +def t7c = f // warn (deprecation) + : @nowarn("msg=fish") // warn (unused nowarn) +def t7d = f: @nowarn("") +def t7e = f: @nowarn + +def t8a(x: Any) = x match + case _: List[Int] => 0 // warn (patmat, unchecked) + case _ => 1 + +@nowarn("cat=unchecked") def t8(x: Any) = x match + case _: List[Int] => 0 + case _ => 1 + +@nowarn def t9a = { 1: @nowarn; 2 } // warn (outer @nowarn is unused) +@nowarn def t9b = { 1: Int @nowarn; 2 } // warn (inner @nowarn is unused, it covers the type, not the expression) + +class ann(a: Any) extends Annotation + +@ann(f) def t10a = 0 // should be a deprecation warning, but currently isn't +@nowarn @ann(f) def t10b = 0 // warn (unused nowarn) +@ann(f: @nowarn) def t10c = 0 // warn (unused nowarn), should be silent + +def forceCompletionOfI1a = (new I1a).m +@nowarn class I1a { // warn (unused nowarn) + @nowarn def m = { 1; 2 } +} + +// completion during type checking +@nowarn class I1b { // warn (unused nowarn) + @nowarn def m = { 1; 2 } +} + +@nowarn class I1c { + def m = { 1; 2 } +} + +trait T { + @nowarn val t1 = { 0; 1 } +} + +class K extends T diff --git a/tests/pos/patmat-exhaustive.scala b/tests/warn/patmat-exhaustive.scala similarity index 56% rename from tests/pos/patmat-exhaustive.scala rename to tests/warn/patmat-exhaustive.scala index 9e3cb7d8f615..a8f057664829 100644 --- a/tests/pos/patmat-exhaustive.scala +++ b/tests/warn/patmat-exhaustive.scala @@ -1,4 +1,4 @@ -//> using options -Xfatal-warnings -deprecation -feature +//> using options -Werror -deprecation -feature def foo: Unit = object O: @@ -8,5 +8,5 @@ def foo: Unit = val x: O.A = ??? x match - case x: B => ??? - case x: C => ??? + case _: B => ??? + case _: C => ??? diff --git a/tests/warn/patmat-type-member-nothing-exhaustive.scala b/tests/warn/patmat-type-member-nothing-exhaustive.scala new file mode 100644 index 000000000000..c04270b78a3a --- /dev/null +++ b/tests/warn/patmat-type-member-nothing-exhaustive.scala @@ -0,0 +1,18 @@ +trait Phase { + type FooTy + type BarTy + sealed trait Adt + case class Foo(x: FooTy) extends Adt + case class Bar(x: BarTy) extends Adt +} + +object Basic extends Phase { + type FooTy = Unit + type BarTy = Nothing +} + + +def test(a: Basic.Adt) = { + a match + case Basic.Foo(x) => +} \ No newline at end of file diff --git a/tests/warn/scala2-t11681.scala b/tests/warn/scala2-t11681.scala index ae2187181ceb..adbf5ea41299 100644 --- a/tests/warn/scala2-t11681.scala +++ b/tests/warn/scala2-t11681.scala @@ -23,7 +23,7 @@ trait BadAPI extends InterFace { a } override def call(a: Int, - b: String, // OK + b: String, // no warn (override) c: Double): Int = { println(c) a @@ -33,7 +33,7 @@ trait BadAPI extends InterFace { override def equals(other: Any): Boolean = true // OK - def i(implicit s: String) = answer // ok + def i(implicit s: String) = answer // warn now /* def future(x: Int): Int = { @@ -59,10 +59,10 @@ class Revaluing(u: Int) { def f = u } // OK case class CaseyKasem(k: Int) // OK -case class CaseyAtTheBat(k: Int)(s: String) // ok +case class CaseyAtTheBat(k: Int)(s: String) // warn unused s trait Ignorance { - def f(readResolve: Int) = answer // ok + def f(readResolve: Int) = answer // warn now } class Reusing(u: Int) extends Unusing(u) // OK @@ -78,30 +78,30 @@ trait Unimplementation { trait DumbStuff { def f(implicit dummy: DummyImplicit) = answer // ok - def g(dummy: DummyImplicit) = answer // ok + def g(dummy: DummyImplicit) = answer // warn now } trait Proofs { def f[A, B](implicit ev: A =:= B) = answer // ok def g[A, B](implicit ev: A <:< B) = answer // ok - def f2[A, B](ev: A =:= B) = answer // ok - def g2[A, B](ev: A <:< B) = answer // ok + def f2[A, B](ev: A =:= B) = answer // warn now + def g2[A, B](ev: A <:< B) = answer // warn now } trait Anonymous { - def f = (i: Int) => answer // ok + def f = (i: Int) => answer // warn now def f1 = (_: Int) => answer // OK def f2: Int => Int = _ + 1 // OK - def g = for (i <- List(1)) yield answer // ok + def g = for (i <- List(1)) yield answer // no warn (that is a patvar) } trait Context[A] trait Implicits { - def f[A](implicit ctx: Context[A]) = answer // ok - def g[A: Context] = answer // OK + def f[A](implicit ctx: Context[A]) = answer // no warn after all; previously, warn implicit param even though marker + def g[A: Context] = answer // no warn bound that is marker only } -class Bound[A: Context] // OK +class Bound[A: Context] // no warn bound that is marker only object Answers { def answer: Int = 42 } diff --git a/tests/warn/symbolic-packages.check b/tests/warn/symbolic-packages.check index c1d56f67eba7..f2d6268f15d4 100644 --- a/tests/warn/symbolic-packages.check +++ b/tests/warn/symbolic-packages.check @@ -1,16 +1,24 @@ --- Warning: tests/warn/symbolic-packages.scala:3:8 --------------------------------------------------------------------- +-- [E222] Syntax Warning: tests/warn/symbolic-packages.scala:3:8 ------------------------------------------------------- 3 |package `with spaces` { // warn | ^^^^^^^^^^^^^ | The package name `with spaces` will be encoded on the classpath, and can lead to undefined behaviour. --- Warning: tests/warn/symbolic-packages.scala:7:10 -------------------------------------------------------------------- + | + | longer explanation available when compiling with `-explain` +-- [E222] Syntax Warning: tests/warn/symbolic-packages.scala:7:10 ------------------------------------------------------ 7 |package +.* { // warn // warn | ^ | The package name `*` will be encoded on the classpath, and can lead to undefined behaviour. --- Warning: tests/warn/symbolic-packages.scala:7:8 --------------------------------------------------------------------- + | + | longer explanation available when compiling with `-explain` +-- [E222] Syntax Warning: tests/warn/symbolic-packages.scala:7:8 ------------------------------------------------------- 7 |package +.* { // warn // warn | ^ | The package name `+` will be encoded on the classpath, and can lead to undefined behaviour. --- Warning: tests/warn/symbolic-packages.scala:11:16 ------------------------------------------------------------------- + | + | longer explanation available when compiling with `-explain` +-- [E222] Syntax Warning: tests/warn/symbolic-packages.scala:11:16 ----------------------------------------------------- 11 |package object `mixed_*` { // warn | ^^^^^^^ | The package name `mixed_*` will be encoded on the classpath, and can lead to undefined behaviour. + | + | longer explanation available when compiling with `-explain` diff --git a/tests/warn/symbolic-packages.scala b/tests/warn/symbolic-packages.scala index 6e0ef04da6bb..501693b9e039 100644 --- a/tests/warn/symbolic-packages.scala +++ b/tests/warn/symbolic-packages.scala @@ -1,4 +1,4 @@ - +//> using options -Wconf:id=E222:w package `with spaces` { // warn class Foo @@ -10,4 +10,4 @@ package +.* { // warn // warn package object `mixed_*` { // warn class Baz -} \ No newline at end of file +} diff --git a/tests/warn/t13095.scala b/tests/warn/t13095.scala new file mode 100644 index 000000000000..0634f4ed903d --- /dev/null +++ b/tests/warn/t13095.scala @@ -0,0 +1,10 @@ +//> using options -Wunused:patvars -Werror + +case class A(x: Int, y: Int) + +object Main { + for { + a <- List.empty[A] + A(x, y) = a + } yield x + y +} diff --git a/tests/warn/tostring-interpolated.scala b/tests/warn/tostring-interpolated.scala new file mode 100644 index 000000000000..a7e9129b162d --- /dev/null +++ b/tests/warn/tostring-interpolated.scala @@ -0,0 +1,36 @@ +//> using options -Wtostring-interpolated +//> abusing options -Wconf:cat=w-flag-tostring-interpolated:e -Wtostring-interpolated + +case class C(x: Int) + +trait T { + def c = C(42) + def f = f"$c" // warn + def s = s"$c" // warn + def r = raw"$c" // warn + + def format = f"${c.x}%d in $c or $c%s" // warn using c.toString // warn + + def bool = f"$c%b" // warn just a null check + + def oops = s"${null} slipped thru my fingers" // warn + + def ok = s"${c.toString}" + + def sb = new StringBuilder().append("hello") + def greeting = s"$sb, world" // warn +} + +class Mitigations { + + val s = "hello, world" + val i = 42 + def shown = println("shown") + + def ok = s"$s is ok" + def jersey = s"number $i" + def unitized = s"unfortunately $shown" // warn + + def nopct = f"$s is ok" + def nofmt = f"number $i" +} diff --git a/tests/warn/tuple-exhaustivity.scala b/tests/warn/tuple-exhaustivity.scala new file mode 100644 index 000000000000..9060d112b197 --- /dev/null +++ b/tests/warn/tuple-exhaustivity.scala @@ -0,0 +1,6 @@ +//> using options -Werror -deprecation -feature + +def test(t: Tuple) = + t match + case Tuple() => + case h *: t => diff --git a/tests/warn/unused-can-equal.scala b/tests/warn/unused-can-equal.scala new file mode 100644 index 000000000000..7c7f0a61e163 --- /dev/null +++ b/tests/warn/unused-can-equal.scala @@ -0,0 +1,16 @@ +//> using options -Wunused:all + +import scala.language.strictEquality + +class Box[T](x: T) derives CanEqual: + def y = x + +def f[A, B](a: A, b: B)(using CanEqual[A, B]) = a == b // no warn +def z[A, B](a: A, b: B)(using ce: CanEqual[A, B]) = a.toString == b.toString // no warn + +def g = + import Box.given // warn + "42".length + +@main def test() = println: + Box(1) == Box(1L) diff --git a/tests/warn/unused-locals.scala b/tests/warn/unused-locals.scala new file mode 100644 index 000000000000..10bf160fb717 --- /dev/null +++ b/tests/warn/unused-locals.scala @@ -0,0 +1,43 @@ +//> using options -Wunused:locals + +class Outer { + class Inner +} + +trait Locals { + def f0 = { + var x = 1 // warn + var y = 2 // no warn + y = 3 + y + y + } + def f1 = { + val a = new Outer // no warn + val b = new Outer // warn + new a.Inner + } + def f2 = { + var x = 100 // warn about it being a var + x + } +} + +object Types { + def l1() = { + object HiObject { def f = this } // warn + class Hi { // warn + def f1: Hi = new Hi + def f2(x: Hi) = x + } + class DingDongDoobie // warn + class Bippy // no warn + type Something = Bippy // no warn + type OtherThing = String // warn + (new Bippy): Something + } +} + +// breakage: local val x$1 in method skolemize is never used +case class SymbolKind(accurate: String, sanitized: String, abbreviation: String) { + def skolemize: SymbolKind = copy(accurate = s"$accurate skolem", abbreviation = s"$abbreviation#SKO") +} diff --git a/tests/warn/unused-params.scala b/tests/warn/unused-params.scala new file mode 100644 index 000000000000..9136da6425a5 --- /dev/null +++ b/tests/warn/unused-params.scala @@ -0,0 +1,167 @@ +//> using options -Wunused:params +// + +import Answers._ + +trait InterFace { + /** Call something. */ + def call(a: Int, b: String, c: Double): Int +} + +trait BadAPI extends InterFace { + def f(a: Int, + b: String, // warn + c: Double): Int = { + println(c) + a + } + @deprecated("no warn in deprecated API", since="yesterday") + def g(a: Int, + b: String, // no warn + c: Double): Int = { + println(c) + a + } + override def call(a: Int, + b: String, // no warn (override) + c: Double): Int = { + println(c) + a + } + + def meth(x: Int) = x + + override def equals(other: Any): Boolean = true // no warn + + def i(implicit s: String) = answer // warn + + /* + def future(x: Int): Int = { + val y = 42 + val x = y // maybe option to warn only if shadowed + x + } + */ +} + +// mustn't alter warnings in super +trait PoorClient extends BadAPI { + override def meth(x: Int) = ??? // no warn + override def f(a: Int, b: String, c: Double): Int = a + b.toInt + c.toInt +} + +class Unusing(u: Int) { // warn + def f = ??? +} + +class Valuing(val u: Int) // no warn + +class Revaluing(u: Int) { def f = u } // no warn + +case class CaseyKasem(k: Int) // no warn + +case class CaseyAtTheBat(k: Int)(s: String) // warn + +trait Ignorance { + def f(readResolve: Int) = answer // warn +} + +class Reusing(u: Int) extends Unusing(u) // no warn + +class Main { + def main(args: Array[String]): Unit = println("hello, args") // no warn +} + +trait Unimplementation { + def f(u: Int): Int = ??? // no warn for param in unimplementation +} + +trait DumbStuff { + def f(implicit dummy: DummyImplicit) = answer + def g(dummy: DummyImplicit) = answer // warn +} +trait Proofs { + def f[A, B](implicit ev: A =:= B) = answer + def g[A, B](implicit ev: A <:< B) = answer + def f2[A, B](ev: A =:= B) = answer // warn + def g2[A, B](ev: A <:< B) = answer // warn +} + +trait Anonymous { + def f = (i: Int) => answer // warn + + def f1 = (_: Int) => answer // no warn underscore parameter (a fresh name) + + def f2: Int => Int = _ + 1 // no warn placeholder syntax (a fresh name and synthetic parameter) + + def g = for (i <- List(1)) yield answer // no warn patvar elaborated as map.(i => 42) +} +trait Context[A] { def m(a: A): A = a } +trait Implicits { + def f[A](implicit ctx: Context[A]) = answer // warn + def g[A: Context] = answer // warn + def h[A](using Context[A]) = answer // warn +} +class Bound[A: Context] // warn +object Answers { + def answer: Int = 42 +} + +trait BadMix { self: InterFace => + def f(a: Int, + b: String, // warn + c: Double): Int = { + println(c) + a + } + @deprecated("no warn in deprecated API", since="yesterday") + def g(a: Int, + b: String, // no warn + c: Double): Int = { + println(c) + a + } + override def call(a: Int, + XXXX: String, // no warn still excused because override (required by self type) + c: Double): Int = { + println(c) + a + } + + def meth(x: Int) = x + + override def equals(other: Any): Boolean = true // no warn + + def i(implicit s: String) = answer // warn +} + +trait ImplFace: + self: InterFace => + def call(a: Int, + b: String, // no warn required by self type + c: Double): Int = + println(c) + a + +class Unequal { + override def equals(other: Any) = toString.nonEmpty // no warn (override) +} + +class Seriously { + def f(s: Serializable) = toString.nonEmpty // warn explicit param of marker trait +} + +class TryStart(start: String) { + def FINALLY(end: END.type) = start // no warn for DSL taking a singleton +} + +object END + +object Optional: + extension (opt: Option.type) // no warn for extension of module + @annotation.experimental + inline def fromNullable[T](t: T | Null): Option[T] = Option(t).asInstanceOf[Option[T]] + +class Nested { + @annotation.unused private def actuallyNotUsed(fresh: Int, stale: Int) = fresh // no warn if owner is unused +} diff --git a/tests/warn/unused-privates.scala b/tests/warn/unused-privates.scala new file mode 100644 index 000000000000..9b461622020f --- /dev/null +++ b/tests/warn/unused-privates.scala @@ -0,0 +1,310 @@ +// +//> using options -deprecation -Wunused:privates,locals +// +class Bippy(a: Int, b: Int) { + private def this(c: Int) = this(c, c) // warn (DO warn, was NO) + private def bippy(x: Int): Int = bippy(x) // warn + private def boop(x: Int) = x+a+b // warn + final private val MILLIS1 = 2000 // warn, scala2: no warn, might have been inlined + final private val MILLIS2: Int = 1000 // warn + final private val HI_COMPANION: Int = 500 // no warn, accessed from companion + def hi() = Bippy.HI_INSTANCE +} +object Bippy { + def hi(x: Bippy) = x.HI_COMPANION + private val HI_INSTANCE: Int = 500 // no warn, accessed from instance + private val HEY_INSTANCE: Int = 1000 // warn + private lazy val BOOL: Boolean = true // warn +} + +class A(val msg: String) +class B1(msg: String) extends A(msg) +class B2(msg0: String) extends A(msg0) +class B3(msg0: String) extends A("msg") + +trait Accessors { + private var v1: Int = 0 // warn + private var v2: Int = 0 // warn, never set + private var v3: Int = 0 // warn, never got + private var v4: Int = 0 // no warn + + private var v5 = 0 // warn, never set + private var v6 = 0 // warn, never got + private var v7 = 0 // no warn + + def bippy(): Int = { + v3 = 3 + v4 = 4 + v6 = 6 + v7 = 7 + v2 + v4 + v5 + v7 + } +} + +class StableAccessors { + private var s1: Int = 0 // warn + private var s2: Int = 0 // warn, never set + private var s3: Int = 0 // warn, never got + private var s4: Int = 0 // no warn + + private var s5 = 0 // warn, never set + private var s6 = 0 // warn, never got + private var s7 = 0 // no warn + + def bippy(): Int = { + s3 = 3 + s4 = 4 + s6 = 6 + s7 = 7 + s2 + s4 + s5 + s7 + } +} + +trait DefaultArgs { + // DO warn about default getters for x2 and x3 + private def bippy(x1: Int, x2: Int = 10, x3: Int = 15): Int = x1 + x2 + x3 // warn // warn + + def boppy() = bippy(5, 100, 200) +} + +/* scala/bug#7707 Both usages warn default arg because using PrivateRyan.apply, not new. +case class PrivateRyan private (ryan: Int = 42) { def f = PrivateRyan() } +object PrivateRyan { def f = PrivateRyan() } +*/ + +class Outer { + class Inner +} + +trait Locals { + def f0 = { + var x = 1 // warn + var y = 2 + y = 3 + y + y + } + def f1 = { + val a = new Outer // no warn + val b = new Outer // warn + new a.Inner + } + def f2 = { + var x = 100 // warn about it being a var + x + } +} + +object Types { + private object Dongo { def f = this } // warn + private class Bar1 // warn + private class Bar2 // no warn + private type Alias1 = String // warn + private type Alias2 = String // no warn + def bippo = (new Bar2).toString + + def f(x: Alias2) = x.length + + def l1() = { + object HiObject { def f = this } // warn + class Hi { // warn + def f1: Hi = new Hi + def f2(x: Hi) = x + } + class DingDongDoobie // warn + class Bippy // no warn + type Something = Bippy // no warn + type OtherThing = String // warn + (new Bippy): Something + } +} + +trait Underwarn { + def f(): Seq[Int] + + def g() = { + val Seq(_, _) = f() // no warn + true + } +} + +class OtherNames { + private def x_=(i: Int): Unit = () // warn + private def x: Int = 42 // warn + private def y_=(i: Int): Unit = () // warn + private def y: Int = 42 + + def f = y +} + +case class C(a: Int, b: String, c: Option[String]) +case class D(a: Int) + +// patvars which used to warn as vals in older scala 2 +trait Boundings { + + def c = C(42, "hello", Some("world")) + def d = D(42) + + def f() = { + val C(x, y, Some(z)) = c: @unchecked // no warn + 17 + } + def g() = { + val C(x @ _, y @ _, Some(z @ _)) = c: @unchecked // no warn + 17 + } + def h() = { + val C(x @ _, y @ _, z @ Some(_)) = c: @unchecked // no warn for z? + 17 + } + + def v() = { + val D(x) = d // no warn + 17 + } + def w() = { + val D(x @ _) = d // no warn + 17 + } + +} + +trait Forever { + def f = { + val t = Option((17, 42)) + for { + ns <- t + (i, j) = ns // no warn + } yield (i + j) + } + def g = { + val t = Option((17, 42)) + for { + ns <- t + (i, j) = ns // no warn + } yield 42 // val emitted only if needed, hence nothing unused + } +} + +trait Ignorance { + private val readResolve = 42 // warn wrong signatured for special members +} + +trait CaseyKasem { + def f = 42 match { + case x if x < 25 => "no warn" + case y if toString.nonEmpty => "no warn" + y + case z => "warn" + } +} +trait CaseyAtTheBat { + def f = Option(42) match { + case Some(x) if x < 25 => "no warn" + case Some(y @ _) if toString.nonEmpty => "no warn" + case Some(z) => "warn" + case None => "no warn" + } +} + +class `not even using companion privates` + +object `not even using companion privates` { + private implicit class `for your eyes only`(i: Int) { // warn + def f = i + } +} + +class `no warn in patmat anonfun isDefinedAt` { + def f(pf: PartialFunction[String, Int]) = pf("42") + def g = f { + case s => s.length // no warn (used to warn case s => true in isDefinedAt) + } +} + +// this is the ordinary case, as AnyRef is an alias of Object +class `nonprivate alias is enclosing` { + class C + type C2 = C + private class D extends C2 // warn +} + +object `classof something` { + private class intrinsically + def f = classOf[intrinsically].toString() +} + +trait `scala 2 short comings` { + def f: Int = { + val x = 42 // warn + 17 + } +} + +class `issue 12600 ignore abstract types` { + type Abs +} + +class `t12992 enclosing def is unused` { + private val n = 42 + @annotation.unused def f() = n + 2 // unused code uses n +} + +class `recursive reference is not a usage` { + private def f(i: Int): Int = // warn + if (i <= 0) i + else f(i-1) + private class P { // warn + def f() = new P() + } +} + +class `absolve serial framework` extends Serializable: + import java.io.{IOException, ObjectInputStream, ObjectOutputStream, ObjectStreamException} + @throws(classOf[IOException]) + private def writeObject(stream: ObjectOutputStream): Unit = () + @throws(classOf[ObjectStreamException]) + private def writeReplace(): Object = ??? + @throws(classOf[ClassNotFoundException]) + @throws(classOf[IOException]) + private def readObject(stream: ObjectInputStream): Unit = () + @throws(classOf[ObjectStreamException]) + private def readObjectNoData(): Unit = () + @throws(classOf[ObjectStreamException]) + private def readResolve(): Object = ??? + +class `absolve ONLY serial framework`: + import java.io.{IOException, ObjectInputStream, ObjectOutputStream, ObjectStreamException} + @throws(classOf[IOException]) + private def writeObject(stream: ObjectOutputStream): Unit = () // warn + @throws(classOf[ObjectStreamException]) + private def writeReplace(): Object = new Object // warn + @throws(classOf[ClassNotFoundException]) + @throws(classOf[IOException]) + private def readObject(stream: ObjectInputStream): Unit = () // warn + @throws(classOf[ObjectStreamException]) + private def readObjectNoData(): Unit = () // warn + @throws(classOf[ObjectStreamException]) + private def readResolve(): Object = new Object // warn + +@throws(classOf[java.io.ObjectStreamException]) +private def readResolve(): Object = ??? // TODO warn +private def print() = println() // TODO warn +private val printed = false // TODO warn + +package locked: + private[locked] def locker(): Unit = () // TODO warn as we cannot distinguish unqualified private at top level + package basement: + private[locked] def unlock(): Unit = () // no warn as it is not top level at boundary + +object `i19998 refinement`: + trait Foo { + type X[a] + } + trait Bar[X[_]] { + private final type SelfX[a] = X[a] // was false positive + val foo: Foo { type X[a] = SelfX[a] } + } + +object `patvar is assignable`: + private var (i, j) = (42, 27) // no warn patvars under -Wunused:privates + println((i, j)) diff --git a/tests/warn/warn-value-discard.check b/tests/warn/warn-value-discard.check index dcd6d62c00e0..03ada5c68621 100644 --- a/tests/warn/warn-value-discard.check +++ b/tests/warn/warn-value-discard.check @@ -2,12 +2,12 @@ 27 | mutable.Set.empty[String].remove("") // warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | discarded non-Unit value of type Boolean. Add `: Unit` to discard silently. --- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:39:41 ------------------------------------------- -39 | mutable.Set.empty[String].subtractOne("") // warn +-- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:37:41 ------------------------------------------- +37 | mutable.Set.empty[String].subtractOne("") // warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | discarded non-Unit value of type scala.collection.mutable.Set[String]. Add `: Unit` to discard silently. --- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:59:4 -------------------------------------------- -59 | mutable.Set.empty[String] += "" // warn +-- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:57:4 -------------------------------------------- +57 | mutable.Set.empty[String] += "" // warn | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | discarded non-Unit value of type scala.collection.mutable.Set[String]. Add `: Unit` to discard silently. -- [E175] Potential Issue Warning: tests/warn/warn-value-discard.scala:15:35 ------------------------------------------- diff --git a/tests/warn/warn-value-discard.scala b/tests/warn/warn-value-discard.scala index 8bb9aebf515b..93c2c056b67c 100644 --- a/tests/warn/warn-value-discard.scala +++ b/tests/warn/warn-value-discard.scala @@ -26,11 +26,9 @@ class ValueDiscardTest: // --> Warning mutable.Set.empty[String].remove("") // warn - // TODO IMHO we don't need to support this, - // as it's just as easy to add a @nowarn annotation as a Unit ascription - //def removeAscribed(): Unit = { - // mutable.Set.empty[String].remove(""): Unit // nowarn - //} + def removeAscribed(): Unit = { + mutable.Set.empty[String].remove(""): Unit // nowarn + } def subtract(): Unit = // - Set#subtractOne returns this.type @@ -63,4 +61,37 @@ class ValueDiscardTest: // - receiver is a local variable // --> No warning val s: mutable.Set[String] = mutable.Set.empty[String] - s += "" \ No newline at end of file + s += "" + +// see also tests/warn/21557.scala +class UnitAscription: + import scala.concurrent.*, ExecutionContext.Implicits.given + + case class C(c: Int): + def f(i: Int, j: Int = c) = i + j + + def f(i: Int, j: Int = 27) = i + j + + def g[A]: List[A] = Nil + + def i: Int = 42 + + def `default arg is inline`: Unit = + f(i = 42): Unit // nowarn + + def `default arg requires block`: Unit = + C(27).f(i = 42): Unit // nowarn + + def `application requires implicit arg`: Unit = + Future(42): Unit // nowarn + + def `application requires inferred type arg`: Unit = + g: Unit // nowarn + + def `implicit selection from this`: Unit = + i: Unit // nowarn + +object UnitAscription: + def g[A]: List[A] = Nil + def `application requires inferred type arg`: Unit = + g: Unit // nowarn UnitAscription.g