Repository: tek/splain Branch: master Commit: 60c92b5e6422 Files: 114 Total size: 179.8 KB Directory structure: gitextract_4lf27vw3/ ├── .gitattribute ├── .github/ │ └── workflows/ │ └── main.yml ├── .gitignore ├── .scalafix.conf ├── .scalafmt.conf ├── .travis.yml ├── LICENSE ├── README.md ├── build.gradle.kts ├── buildSrc/ │ ├── build.gradle.kts │ └── src/ │ └── main/ │ └── kotlin/ │ ├── Versions.kt │ └── init.kt ├── core/ │ ├── build.gradle.kts │ └── src/ │ ├── main/ │ │ ├── resources/ │ │ │ └── scalac-plugin.xml │ │ ├── scala/ │ │ │ └── splain/ │ │ │ ├── Messages.scala │ │ │ ├── PluginSettings.scala │ │ │ ├── SplainInternalError.scala │ │ │ ├── format/ │ │ │ │ └── package.scala │ │ │ └── test/ │ │ │ ├── AutoLift.scala │ │ │ ├── CachingFrontEnd.scala │ │ │ ├── Issue.scala │ │ │ ├── TryCompile.scala │ │ │ └── TryCompileMacros.scala │ │ └── scala-2.13.7+/ │ │ └── latest/ │ │ └── splain/ │ │ ├── ImplicitsExtension.scala │ │ ├── SplainAnalyzer.scala │ │ ├── SplainAnalyzerShim.scala │ │ ├── SplainFormattersExtension.scala │ │ ├── SplainFormattingExtension.scala │ │ ├── SplainPlugin.scala │ │ ├── SplainPluginLike.scala │ │ ├── TyperCompatViews.scala │ │ └── package.scala │ ├── test/ │ │ ├── resources/ │ │ │ └── splain/ │ │ │ ├── builtin/ │ │ │ │ ├── BasicSpec/ │ │ │ │ │ └── __direct/ │ │ │ │ │ └── check │ │ │ │ ├── MaxRefinedSpec/ │ │ │ │ │ └── __direct/ │ │ │ │ │ └── check │ │ │ │ └── VerboseTreeSpec/ │ │ │ │ └── __direct/ │ │ │ │ └── check │ │ │ └── plugin/ │ │ │ ├── ErrorsCompatSpec/ │ │ │ │ └── byname/ │ │ │ │ └── code.scala │ │ │ ├── PluginSpec/ │ │ │ │ ├── ambiguous/ │ │ │ │ │ ├── code.scala │ │ │ │ │ └── error │ │ │ │ ├── bounds/ │ │ │ │ │ ├── code.scala │ │ │ │ │ └── error │ │ │ │ ├── chain/ │ │ │ │ │ ├── code.scala │ │ │ │ │ └── error │ │ │ │ ├── higherKindArg/ │ │ │ │ │ ├── code.scala │ │ │ │ │ └── error │ │ │ │ ├── implicit-ctrl-char/ │ │ │ │ │ ├── code.scala │ │ │ │ │ └── error │ │ │ │ ├── lazy/ │ │ │ │ │ ├── code.scala │ │ │ │ │ └── error │ │ │ │ └── member/ │ │ │ │ ├── code.scala │ │ │ │ └── error │ │ │ ├── ShapelessSpec/ │ │ │ │ ├── lazyImplicit/ │ │ │ │ │ ├── code.scala │ │ │ │ │ └── error │ │ │ │ ├── record/ │ │ │ │ │ ├── code.scala │ │ │ │ │ └── error │ │ │ │ └── witness-value/ │ │ │ │ ├── code.scala │ │ │ │ └── error │ │ │ ├── VImplicitDivergingSpec/ │ │ │ │ ├── circular/ │ │ │ │ │ ├── code.scala │ │ │ │ │ └── error │ │ │ │ ├── circular-recoverable/ │ │ │ │ │ └── code.scala │ │ │ │ ├── diverging/ │ │ │ │ │ ├── code.scala │ │ │ │ │ └── error │ │ │ │ ├── diverging-compact/ │ │ │ │ │ ├── code.scala │ │ │ │ │ └── error │ │ │ │ └── self/ │ │ │ │ ├── code.scala │ │ │ │ └── error │ │ │ ├── VTypeDetailPositionSpec/ │ │ │ │ └── __direct/ │ │ │ │ └── check │ │ │ ├── VTypeDetailReductionSpec/ │ │ │ │ └── __direct/ │ │ │ │ └── check │ │ │ ├── VTypeDetailSpec/ │ │ │ │ └── __direct/ │ │ │ │ └── check │ │ │ ├── VTypeDiffsDetailSpec/ │ │ │ │ └── __direct/ │ │ │ │ └── check │ │ │ └── ZIOSpec/ │ │ │ └── zlayer/ │ │ │ ├── code.scala │ │ │ └── error │ │ └── scala/ │ │ └── splain/ │ │ ├── PlainPrettifier.scala │ │ ├── ScalacticSpike.scala │ │ ├── SpecBase.scala │ │ ├── builtin/ │ │ │ ├── BasicSpec.scala │ │ │ ├── BasicXSource3Spec.scala │ │ │ ├── MaxRefinedSpec.scala │ │ │ └── VerboseTreeSpec.scala │ │ ├── plugin/ │ │ │ ├── ErrorsCompatSpec.scala │ │ │ ├── PluginSpec.scala │ │ │ ├── ShapelessSpec.scala │ │ │ ├── VImplicitDivergingSpec.scala │ │ │ ├── VImplicitDivergingXSource3Spec.scala │ │ │ ├── VTypeDetailPositionSpec.scala │ │ │ ├── VTypeDetailReductionSpec.scala │ │ │ ├── VTypeDetailSpec.scala │ │ │ ├── VTypeDiffsDetailSpec.scala │ │ │ └── ZIOSpec.scala │ │ └── test/ │ │ └── TryCompileSpec.scala │ └── testFixtures/ │ └── scala/ │ └── splain/ │ ├── TestHelpers.scala │ └── builtin/ │ └── BasicFixture.scala ├── dev/ │ ├── .CI.sh │ ├── CI-latest.sh │ ├── format-code.sh │ ├── gradle-versions.sh │ ├── log-make-all.sh │ ├── make-all.sh │ ├── publish-all.sh │ ├── publish.template.sh │ ├── publishM2-all.sh │ ├── publishM2.sh │ └── test.sh ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── img/ │ ├── VtypeDetail.md │ └── VtypeDiffsDetail.md ├── settings.gradle.kts └── testing/ └── acceptance/ ├── build.gradle.kts └── src/ └── test/ ├── resources/ │ └── splain/ │ └── builtin/ │ └── BasicSpec/ │ └── __direct/ │ └── check └── scala/ └── splain/ └── acceptance/ ├── Acceptance.scala └── builtin/ └── StaticBasicSpec.scala ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattribute ================================================ *.bat text eol=crlf *.cmd text eol=crlf *.java text eol=lf *.scala text eol=lf *.xml text eol=lf *.py text eol=lf *.R text eol=lf # from apache spark *.sh text eol=lf * text eol=lf ================================================ FILE: .github/workflows/main.yml ================================================ name: CI on: [push] jobs: vars: runs-on: ubuntu-latest outputs: experimentalScalaVersions: "" steps: - name: dummy run: echo dummy build: needs: vars runs-on: ubuntu-latest strategy: &scala_strategy max-parallel: 2 fail-fast: false matrix: scalaVersion: ["2.13.9", "2.13.10", "2.13.11", "2.13.12", "2.13.13", "2.13.14", "2.13.15", "2.13.16", "2.13.17", "2.13.18"] steps: - uses: actions/checkout@v2 - name: Set up JDK 17 uses: actions/setup-java@v1 with: java-version: 17 - name: Compile run: ./dev/publishM2.sh -PscalaVersion=${{matrix.scalaVersion}} - name: Pre-release on github uses: "marvinpinto/action-automatic-releases@latest" if: contains(github.ref_name, 'Release/') with: repo_token: "${{ secrets.GITHUB_TOKEN }}" automatic_release_tag: "${{github.ref_name}}-${{matrix.scalaVersion}}" title: "${{github.ref_name}}-${{matrix.scalaVersion}}" prerelease: true files: | LICENSE core/build/libs/*.jar test: needs: vars runs-on: ubuntu-latest #TODO: don't repeat yourself strategy: *scala_strategy continue-on-error: ${{ contains(needs.vars.outputs.experimentalScalaVersions, matrix.scalaVersion) }} steps: - name: debug vars run: echo ${{ needs.vars.outputs.experimentalScalaVersions }} - name: debug expression run: echo ${{ job.continue-on-error }} - uses: actions/checkout@v2 - name: Set up JDK 1.8 uses: actions/setup-java@v1 with: java-version: 1.8 # TODO: replica! how to remove? - name: Test run: ./dev/test.sh -PscalaVersion=${{matrix.scalaVersion}} - name: Publish Unit Test Results uses: EnricoMi/publish-unit-test-result-action@v1 if: always() with: files: | **/test-results/test/*.xml check_name: | Test Report (${{matrix.scalaVersion}}) ================================================ FILE: .gitignore ================================================ *.class *.log logs # maven *.versionsBackup # gradle **/.gradle **/build **/bin # sbt .cache/ .history/ .lib/ dist/* target/ lib_managed/ src_managed/ project/boot/ project/plugins/project/ # Scala-IDE .scala_dependencies .worksheet # Eclipse .pmd .classpath .project .settings/ # IntelliJ-IDEA *.iml .idea/ .idea_modules/ # VSCode .vscode/ .obsidian/ # BSP & Metals .bsp/ .bloop/ .metals/ **/metals.sbt # the Singapore stack **/.ammonite/ # Compiler output */target/ #Misc temp **/temp foo #aws key **/rootkey.csv #publish dev/publish.sh ================================================ FILE: .scalafix.conf ================================================ runner.dialect = scala213 // Built in rules rules = [ NoAutoTupling // Rewrite that inserts explicit tuples for adapted argument lists for compatibility with -Yno-adapted-args NoValInForComprehension ProcedureSyntax RemoveUnused LeakingImplicitClassVal // TODO: unrealiable due to lack of speculative modification, disabled // ExplicitResultTypes ] RemoveUnused { imports = true privates = true locals = true patternvars = true params = true } ExplicitResultTypes { rewriteStructuralTypesToNamedSubclass = false skipSimpleDefinitions = false } ================================================ FILE: .scalafmt.conf ================================================ version = "3.7.14" runner.dialect = scala213 maxColumn = 120 lineEndings = unix # Only format files tracked by git. project.git = true align { preset = some tokens = [] } docstrings { blankFirstLine = true } newlines { # topLevelStatements = [before] sometimesBeforeColonInMethodReturnType = false penalizeSingleSelectMultiArgList = false beforeCurlyLambdaParams = multilineWithCaseOnly # // afterCurlyLambdaParams = squash implicitParamListModifierForce = [before, after] } rewrite { rules = [ # // AvoidInfix, Imports, RedundantBraces, RedundantParens, SortModifiers ] imports { sort=original } redundantBraces { generalExpressions = false methodBodies = false includeUnitMethods = false maxLines = 0 } } ================================================ FILE: .travis.yml ================================================ jdk: - openjdk8 language: scala script: 'sbt splain/test' ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 Torsten Schmits Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # A scala compiler plugin for more concise errors This plugin removes some of the redundancy of the compiler output and prints additional info for implicit resolution errors. # Versions TL;DR ``` ┌──────────────────────────┐ │What's your Scala version?│ └─────────┬───────┬────────┘ │ │ │ └───────────────────┐ v │ ┌─────────────────────────────────────────────┐ │ │ ( >= 2.13.6 ) │ │ │ ───────────── │ │ │Do you want experimental features & bugfixes?│ │ └──┬───────────────┬──────────────────────────┘ │ │ │ │ v v v ┌──────────┐ ┌─────────────────┐ ┌───────────────────────────┐ │ ( yes ) │ │ ( no ) │ │( 2.12 / 2.13.0 .. 2.13.5 )│ │ ─────── │ │ ────── │ ├───────────────────────────┤ │Splain 1.x│ │Compiler built-in│ │ Splain 0.5.x │ └──────────┘ └─────────────────┘ └───────────────────────────┘ ``` ### Compiler built-in (main article: https://docs.scala-lang.org/overviews/compiler-options/index.html#Verbose_Settings) The basic Splain features has been integrated into Scala compiler (since 2.13.6, through contributions like [this](https://github.com/scala/scala/pull/7785) and [this](https://github.com/scala/scala/pull/10029)), they can be enabled immediately by using the right compiler options (see **Option** section for detail). ### v1.x (master branch) (Only available for Scala 2.13.6+) Splain 1.x is a simplified rewrite that aims to incrementally introduce enhancement to the already integrated Splain features. Additional features and bugfixes will first be released and refined here, then be periodically contributed back into Scala compiler. Effectively, **Splain 1.x is now a feature preview patch of Scala compiler**, if a relevant compiler built-in option (see **Option** section for detail) malfunctions, it may work with Splain v1.x enabled. It is also the only branch under active development. ### v0.5.x (maintenance branch) (Only available for Scala 2.12 and Scala 2.13.0 .. 2.13.5) The latest v0.x will continue to be maintained and published regularly to stay compatible with the latest Scala 2.12.x release (until it's end-of-life), but no newer version will be published for Scala 2.13, **splain 0.5.x will be the last release for Scala 2.13**. We strongly recommend you to upgrade to Scala 2.13.6+ to benefit from active support and up-to-date features. ### Build Matrix | Version | Status | Compatibility | |----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------| | v1.x
(current) - latest | [![CI](https://github.com/tek/splain/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/tek/splain/actions/workflows/main.yml) | ![badge](https://github-actions.40ants.com/tek/splain/matrix.svg?branch=master) | | v1.1.0
(current) | [![CI](https://github.com/tek/splain/actions/workflows/main.yml/badge.svg?branch=Release/1.1.0)](https://github.com/tek/splain/actions/workflows/main.yml) | ![badge](https://github-actions.40ants.com/tek/splain/matrix.svg?branch=Release/1.1.0) | | v1.1.0-RC0
(current) | [![CI](https://github.com/tek/splain/actions/workflows/main.yml/badge.svg?branch=Release/1.1.0-RC0)](https://github.com/tek/splain/actions/workflows/main.yml) | ![badge](https://github-actions.40ants.com/tek/splain/matrix.svg?branch=Release/1.1.0-RC0) | | v1.0.3
(current) | [![CI](https://github.com/tek/splain/actions/workflows/main.yml/badge.svg?branch=Release/1.0.3)](https://github.com/tek/splain/actions/workflows/main.yml) | ![badge](https://github-actions.40ants.com/tek/splain/matrix.svg?branch=Release/1.0.3) | | v1.0.2
(current) | [![CI](https://github.com/tek/splain/actions/workflows/main.yml/badge.svg?branch=Release/1.0.2)](https://github.com/tek/splain/actions/workflows/main.yml) | ![badge](https://github-actions.40ants.com/tek/splain/matrix.svg?branch=Release/1.0.2) | | v1.0.1
(current) | [![CI](https://github.com/tek/splain/actions/workflows/main.yml/badge.svg?branch=Release/1.0.1)](https://github.com/tek/splain/actions/workflows/main.yml) | ![badge](https://github-actions.40ants.com/tek/splain/matrix.svg?branch=Release/1.0.1) | | v1.0.0
(current) | [![CI](https://github.com/tek/splain/actions/workflows/main.yml/badge.svg?branch=Release/1.0.0)](https://github.com/tek/splain/actions/workflows/main.yml) | ![badge](https://github-actions.40ants.com/tek/splain/matrix.svg?branch=Release/1.0.0) | | v1.0.0-RC2
(current) | [![CI](https://github.com/tek/splain/actions/workflows/main.yml/badge.svg?branch=Release/1.0.0-RC2)](https://github.com/tek/splain/actions/workflows/main.yml) | ![badge](https://github-actions.40ants.com/tek/splain/matrix.svg?branch=Release/1.0.0-RC2) | | v1.0.0-RC1
(current) | [![CI](https://github.com/tek/splain/actions/workflows/main.yml/badge.svg?branch=Release/1.0.0-RC1)](https://github.com/tek/splain/actions/workflows/main.yml) | ![badge](https://github-actions.40ants.com/tek/splain/matrix.svg?branch=Release/1.0.0-RC1) | | v0.x
(maintenance) - latest | [![CI](https://github.com/tek/splain/actions/workflows/main.yml/badge.svg?branch=Maintenance%2Fmaster)](https://github.com/tek/splain/actions/workflows/main.yml) | ![badge](https://github-actions.40ants.com/tek/splain/matrix.svg?branch=Maintenance%2Fmaster) | # Usage ### v1.x, v0.x Include this line in your `build.sbt` (_not_ `project/plugins.sbt`!!): ```sbt addCompilerPlugin("io.tryp" % "splain" % "0.5.8" cross CrossVersion.patch) ``` If you want to support scala versions both newer and older than `2.12.5`, use: ```sbt libraryDependencies += { val v = if (scalaVersion.value.replaceFirst(raw"\.(\d)$$",".0$1") <= "2.12.04") "0.4.1" else "0.5.8" ("io.tryp" %% "splain" % v cross CrossVersion.patch).withConfigurations(Some("plugin->default(compile)")) } ``` If you are using gradle with scala plugin, include this line under the dependency section of your build.gradle: ```groovy scalaCompilerPlugins group: 'io.tryp', name: 'splain_${scalaVersion}', version: '0.5.8' ``` or build.gradle.kts: ```kotlin scalaCompilerPlugins("io.tryp:splain_${scalaVersion}:0.5.8") ``` ### compiler built-in, no plugin declaration required Its effects however still have to be enabled in your compiler options, in minimal case, by the following 2 options (see Options for details): ``` -Vimplicits -Vtype-diffs ``` # Options The plugin can be configured via compiler Options with the format: | v0.x | built-in, v1.x | |:----------------------------- | -------------------- | | `-P:splain:[:]` | `-[:]` | `param` can be one of the following: | v0.x | built-in, v1.x | default value | | ----------------- |-------------------------------------------|------------------| | `all` | `enabled` | true | | `infix` | (dropped) | | | `foundreq` | `Vtype-diffs` | false | | `implicits` | `Vimplicits` | false | | `bounds` | (dropped) | false | | `color` | (dropped) | | | `breakinfix` | (dropped) | 0 | | `tree` | `Vimplicits-verbose-tree` | | | `compact` | (dropped) | false | | `boundsimplicits` | (dropped) | | | `truncrefined` | `Vimplicits-max-refined` | 0 | | `rewrite` | (dropped) | (do not rewrite) | | `keepmodules` | (dropped) | 0 | | (N/A) | `P:splain:Vimplicits-diverging` | false | | (N/A) | `P:splain:Vimplicits-diverging-max-depth` | 100 | | (N/A) | `P:splain:Vtype-detail` | 1 | | (N/A) | `P:splain:Vtype-diffs-detail` | 1 | `value` can either be `true` or `false`. If omitted, the default is `true` for both value and parameter. The parameter `all` can be used to deactivate all features. The parameters can be applied like this: (in sbt) ```sbt scalacOptions += "-P:splain:implicits:false" ``` (in gradle with scala plugin) ```kotlin withType { scalaCompileOptions.apply { additionalParameters = listOf("-P:splain:implicits:false") } } ``` # infix types Instead of `shapeless.::[A, HNil]`, prints `A :: HNil`. # found/required types Rather than printing up to four types, only the dealiased types are shown as a colored diff: ![foundreq](img/foundreq.jpg) special consideration for `shapeless.Record`: ![foundreq_record](img/foundreq_record.jpg) In the case of refined types in the form of `Client with Database with Publisher`, the types will be matched with each other and a missing or surplus type will be indicated by a `` label. # implicit resolution chains When an implicit is not found, only the outermost error at the invocation point is printed. This can be expanded with the compiler flag `-Xlog-implicits`, but that also shows all invalid implicits for parameters that have been resolved successfully. This feature prints a compact list of all involved implicits: ![implicits](img/implicits.jpg) Here, `!I` stands for *could not find implicit value*, the name of the implicit parameter is in yellow, and its type in green. If the parameter `tree` is set, the candidates will be indented according to their nesting level: ![tree](img/tree.jpg) If the parameter `compact` is set, only the first and last implicit in a chain will be printed. If the parameter `boundsimplicits` is set to false, any **nonconformant bounds** errors will be suppressed. For comparison, this is the regular compiler output for this case (with formatted types): ``` [info] unit/src/basic.scala:35: f is not a valid implicit value for splain.ImplicitChain.T2 because: [info] hasMatchingSymbol reported error: could not find implicit value for parameter impPar2: (D *** (C *** String)) >:< ((C,D,C) *** D) [info] implicitly[T1] [info] ^ [info] unit/src/basic.scala:35: g is not a valid implicit value for splain.ImplicitChain.T1 because: [info] hasMatchingSymbol reported error: could not find implicit value for parameter impPar1: D *** ((C >:< C) *** (D => Unit)) [info] implicitly[T1] [info] ^ [error] unit/src/basic.scala:35: could not find implicit value for parameter e: (C *** D) >:< C with D {type A = D; type B = C} [error] implicitly[T1] ``` # infix type and type argument line breaking If the parameter `breakinfix` is given and greater than 0, types longer than that number will be split into multiple lines: ``` implicit error; !I e: String f invalid because !I impPar4: List[ ( VeryLongTypeName :::: VeryLongTypeName :::: VeryLongTypeName :::: VeryLongTypeName ) :::: (Short :::: Short) :::: ( VeryLongTypeName :::: VeryLongTypeName :::: VeryLongTypeName :::: VeryLongTypeName ) :::: VeryLongTypeName :::: VeryLongTypeName :::: VeryLongTypeName :::: VeryLongTypeName ] ``` # truncating refined types A type of the shape `T { type A = X; type B = Y }` will be displayed as `T {...}` if the parameter `truncrefined` is set to a value `/= 0` and the refinement's length is greater than the value. # truncating module paths Default behaviour when printing type names is to omit the whole module path and only print the last segment. Two options modify this behaviour: ## regex rewrite The option `rewrite` takes a string that is parsed as a `;`-delimited list of regexes and optional replacements. For example: ``` -P:splain:rewrite:cats\\.data/cd;.Type ``` This parses as two rewrite items: * transform `cats.data` into `cd` * delete all occurences of `.Type` If a slash is present, the string following it will be used as a replacement for the matched text. If it is absent, the empty string is substituted. ## dropping module segments by count The option `keepmodules` determines how many segments of the module path before the type name will be displayed, but only if the `rewrite` mechanism hasn't changed anything. So with `-P:splain:keepmodules:2`, the qualified type `cats.free.FreeT.Suspend` will be displayed as `free.FreeT.Suspend`, keeping the two segments `free.FreeT` before the type name. The default is `0`, so only the type name itself will be displayed # expanding diverging implicit errors (experimental) A `diverging implicit error` is thrown by compiler if it cannot decide if an implicit search can terminate in polynomial time (e.g. if the search algorithm encounter a loop or infinite expansion). In most cases, such error will cause the entire search to fail immediately, but there are few exceptions to this rule, for which the search can backtrack and try an alternative path to fulfil the implicit argument. Either way, the Scala compiler error is only capable of showing the entry point of such loop or infinite expansion: ``` diverging implicit expansion for type splain.DivergingImplicits.C starting with method f in object Circular ``` If the parameter `-P:splain:Vimplicits-diverging` is enabled, it will instruct the compiler to continue its implicit search process until an implicit resolution chain can be correlated with such error(s): ``` implicit error; !I e: C f invalid because !I c: C diverging implicit expansion for type C starting with method f in object Endo ――f invalid because !I c: C diverging implicit expansion for type C starting with method f in object Endo ``` **EXPERIMENTAL!** sometimes this feature may cause failed implicit resolution to succeed, due to the delay in throwing the diverging implicit error. It may also increase compilation time slightly. If your build has been broken by this feature, please consider simplifying your code base to create a minimal reproducible test case, and submit it with a pull request. # type detail (experimental) The option `-P:splain:Vtype-detail:X` can take an integer from 1 to 6 to attach different kinds of details to type information in any error message. - `1` (DEFAULT) : type info in short form, by using toString (same as pre-1.1.0) - `2` = **long** : type info in long form, by using toLongString - `3` = `2` + (**existential** : existential context) - `4` = `3` + (**reduction** : explain type reduction process) - `5` = `4` + (**position** : type definition position in code) - `6` = `5` + (**alias** : explain type aliases, this generally contains duplicate information with `3`, it is only included for completeness) For example: (`-P:splain:Vtype-detail:1`) ``` XXX.scala:15: error: type mismatch; Test.F[Test.a.type|a.type] ``` (`-P:splain:Vtype-detail:6`) ![](img/bc959e77.png) In addition, multiple names of the detail kind (denoted by bold text in the above list) can be appended to the option value to enable it, e.g. `-P:splain:Vtype-detail:1,reduction,position` can attach type reduction process & type definition position while bypassing **long** and **existential**. # type diffs detail (experimental) The option `-P:splain:Vtype-diffs-detail:X` can take an integer from 1 to 4 to augment type diff errors with different kinds of details. - `1` (DEFAULT) : no augmentation (same as pre-1.1.0) - `2` = **disambiguation** : augment type info with disambiguation for both sides in `|` and infix types (e.g. `A =:= B`, `A <:< B`) in error message - `3` = `2` + (**builtIn** : attach built-in found/required errors emitted by Scala compiler IF AND ONLY IF both sides of the error message are identical) - `4` = `3` + (**builtInAlways** : ALWAYS attach original found/required error info, even if both sides of the error message are different) In addition, multiple names of the detail kind (denoted by bold text in the above list) can be appended to the option value to enable it, e.g. `-P:splain:Vtype-diffs-detail:1,builtIn` can attach built-in errors while bypassing **disambiguation**. For example: (`-P:splain:Vtype-diffs-detail:1`) ``` XXX.scala:16: error: implicit error; !I ev: Long =:= Long Cannot prove that Long =:= Long. ``` (`-P:splain:Vtype-diffs-detail:4`) ![](img/86df485f.png) # Development ## Bugs Due to the nature of the hack that allows _splain_ to hook into the implicit search algorithm, other plugins using the same trick may not work or cause _splain_ to be inactive. Another victim of _splain_ is scaladoc – doc comments might disappear when running the task with _splain_ active, so make sure it is disabled before doing so. Users are encouraged to submit issues and test cases directly through pull requests, by forking the project and adding new test cases under: | v0.x | v1.x | |:-------------------------------------- | -------------------------------------------------- | | `/src/test/scala/splain` | `/core/src/test/scala/splain/plugin` | The bug can thus be identified by the team quickly on our [continuous integration environment](https://github.com/tek/splain/actions). Submission on our GitHub issue tracker is also welcomed, but it generally takes much longer for the team to respond. ## How to compile ### v1.x (from git branch master) Built with the latest [Gradle](https://gradle.org/), to compile and publish locally: ``` ./gradlew clean testClasses publishToMavenLocal ``` to run all tests: ``` ./gradlew test ``` ### v0.x (from git branch Maintenance/master) Built with the latest stable [SBT](https://www.scala-sbt.org/). to compile and publish locally: ``` sbt clean publishM2 ``` to run all tests: ``` sbt test ``` ## How to edit Most project contributors uses neovim, IntelliJ IDEA or visual studio code. The team strive for a strong discipline in software engineering. All commits (including SNAPSHOTs and PRs) will be compliant with [scalalfmt](https://scalameta.org/scalafmt/) standard. ## Communication - @tek - reviewer for built-in/v0.x bugfix, new features - @tribbloid - reviewer for v1.x bugfix - @dwijnand - reviewer for scala compiler integration ================================================ FILE: build.gradle.kts ================================================ import org.gradle.util.internal.VersionNumber import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask import org.gradle.api.specs.Spec val vs = versions() buildscript { repositories { // Add here whatever repositories you're already using mavenCentral() } dependencies { classpath("ch.epfl.scala:gradle-bloop_2.12:1.6.4") // suffix is always 2.12, weird } } tasks.named("dependencyUpdates").configure { filterConfigurations = Spec { !it.name.startsWith("incrementalScalaAnalysis") } } plugins { `java-test-fixtures` scala idea signing `maven-publish` id("io.github.gradle-nexus.publish-plugin") version "2.0.0" id("com.github.ben-manes.versions") version "0.53.0" id("io.github.cosmicsilence.scalafix") version "0.2.6" } val sonatypeApiUser = providers.gradleProperty("sonatypeApiUser") val sonatypeApiKey = providers.gradleProperty("sonatypeApiKey") if (sonatypeApiUser.isPresent && sonatypeApiKey.isPresent) { nexusPublishing { repositories { sonatype { nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) username.set(sonatypeApiUser) password.set(sonatypeApiKey) // useStaging.set(true) } } } } else { logger.warn("Sonatype API key not defined, skipping configuration of Maven Central publishing repository") } allprojects { apply(plugin = "java-library") apply(plugin = "java-test-fixtures") // apply(plugin = "bloop") // DO NOT enable! In VSCode it will cause the conflict: // Cannot add extension with name 'bloop', as there is an extension already registered with that name apply(plugin = "scala") apply(plugin = "idea") apply(plugin = "signing") apply(plugin = "maven-publish") group = vs.projectGroup version = vs.projectV repositories { mavenCentral() // jcenter() maven("https://dl.bintray.com/kotlin/kotlin-dev") maven("https://scala-ci.typesafe.com/artifactory/scala-integration/") // scala SNAPSHOT } fun includeShims(from: String, to: String) { sourceSets { main { scala { setSrcDirs(srcDirs + listOf("src/main/scala-${from}+/${to}")) } resources { setSrcDirs(srcDirs + listOf("src/main/resources-${from}+/${to}")) } } testFixtures { scala { setSrcDirs(srcDirs + listOf("src/testFixtures/scala-${from}+/${to}")) } resources { setSrcDirs(srcDirs + listOf("src/testFixtures/resources-${from}+/${to}")) } } test { scala { setSrcDirs(srcDirs + listOf("src/test/scala-${from}+/${to}")) } resources { setSrcDirs(srcDirs + listOf("src/test/resources-${from}+/${to}")) } } } } val vn = VersionNumber.parse(vs.scala.v) val supportedPatchVs = 7..12 for (from in supportedPatchVs) { if (vn.micro >= from) { includeShims("2.13.${from}", "latest") } for (to in supportedPatchVs) { if (vn.micro <= to) { includeShims("2.13.${from}", "2.13.${to}") } } } dependencies { constraints {} implementation("${vs.scala.group}:scala-library:${vs.scala.v}") val scalaTestV = "3.2.11" testFixturesApi("org.scalatest:scalatest_${vs.scala.artifactSuffix}:${scalaTestV}") testImplementation("org.scalatest:scalatest_${vs.scala.artifactSuffix}:${scalaTestV}") // testFixturesApi("org.scalatest:scalatest-core_${vs.scala.artifactSuffix}:${vs.scalaTestV}") val jUnitV = "5.13.4" val jUnitPlatformV = "1.13.4" testRuntimeOnly("org.junit.platform:junit-platform-engine:$jUnitPlatformV") testRuntimeOnly("org.junit.platform:junit-platform-launcher:$jUnitPlatformV") testImplementation("org.junit.jupiter:junit-jupiter:${jUnitV}") // testRuntimeOnly("org.scalatestplus:junit-5-13_${vs.scala.artifactSuffix}:3.2.19.0") testRuntimeOnly("ai.acyclic.scalatestplus:junit-5-13_${vs.scala.artifactSuffix}:3.2.19.2") // testRuntimeOnly("ai.acyclic.scalatestplus:junit-5-13_${vs.scala.artifactSuffix}:3.3.0.0") } tasks.register("dependencyTree") { dependsOn("dependencies") } val jvmTarget = JavaVersion.VERSION_1_8 java { withSourcesJar() withJavadocJar() sourceCompatibility = jvmTarget targetCompatibility = jvmTarget } tasks { withType { sourceCompatibility = jvmTarget.toString() targetCompatibility = jvmTarget.toString() scalaCompileOptions.apply { // isForce = true loggingLevel = "verbose" val compilerOptions = mutableListOf( "-encoding", "UTF-8", "-deprecation", "-unchecked", "-feature", "-language:higherKinds", "-language:existentials", "-Ywarn-value-discard", "-Ywarn-unused:imports", "-Ywarn-unused:implicits", "-Ywarn-unused:params", "-Ywarn-unused:patvars" // "-Ydebug-error" ) additionalParameters = compilerOptions forkOptions.apply { memoryInitialSize = "1g" memoryMaximumSize = "4g" // this may be over the top but the test code in macro & core frequently run implicit search on church encoded Nat type jvmArgs = listOf( "-Xss256m" ) } } } test { minHeapSize = "1024m" maxHeapSize = "4096m" testLogging { showExceptions = true showCauses = true showStackTraces = true // stdout is used for occasional manual verification showStandardStreams = true } // useJUnit() useJUnitPlatform { includeEngines("scalatest") testLogging { events("passed", "skipped", "failed") } } } } apply(plugin = "io.github.cosmicsilence.scalafix") scalafix { semanticdb.autoConfigure.set(true) semanticdb.version.set("4.9.0") } idea { module { excludeDirs = excludeDirs + files( "target", "out", ".idea", ".vscode", ".bloop", ".bsp", ".metals", "bin", ".ammonite", "logs", ) isDownloadJavadoc = true isDownloadSources = true } } } subprojects { // https://stackoverflow.com/a/66352905/1772342 // val signingKeyID = providers.gradleProperty("signing.gnupg.keyID") val signingSecretKey = providers.gradleProperty("signing.gnupg.secretKey") val signingKeyPassphrase = providers.gradleProperty("signing.gnupg.passphrase") signing { useGpgCmd() if (signingSecretKey.isPresent) { useInMemoryPgpKeys(signingSecretKey.get(), signingKeyPassphrase.get()) // useInMemoryPgpKeys(signingKeyID.get(), signingSecretKey.get(), signingKeyPassphrase.get()) sign(extensions.getByType().publications) } else { logger.warn("PGP signing key not defined, skipping signing configuration") } } publishing { val suffix = "_" + vs.scala.v val rootID = vs.projectRootID val moduleID = if (project.name.equals(rootID))// rootID + "-" + "parent" + suffix throw kotlin.UnsupportedOperationException("root project should not be published") else if (project.name.equals("core")) rootID + suffix else rootID + "-" + project.name + suffix val whitelist = setOf("core") if (whitelist.contains(project.name)) { publications { create("maven") { val javaComponent = components["java"] as AdhocComponentWithVariants from(javaComponent) javaComponent.withVariantsFromConfiguration(configurations["testFixturesApiElements"]) { skip() } javaComponent.withVariantsFromConfiguration(configurations["testFixturesRuntimeElements"]) { skip() } artifactId = moduleID version = project.version.toString() pom { licenses { license { name.set("MIT") url.set("http://opensource.org/licenses/MIT") } } name.set("splain") description.set("A scala compiler plugin for more concise errors") val github = "https://github.com/tek" val repo = github + "/splain" url.set(repo) developers { developer { id.set("tryp") name.set("Torsten Schmits") email.set("torstenschmits@gmail.com") url.set(github) } } scm { connection.set("scm:git@github.com:tek/splain") url.set(repo) } } } } } } } idea { targetVersion = "2020" module { excludeDirs = excludeDirs + files( ".gradle", "gradle", "spike", ".history" ) } } ================================================ FILE: buildSrc/build.gradle.kts ================================================ plugins { `kotlin-dsl` } repositories { mavenLocal() mavenCentral() // jcenter() maven("https://dl.bintray.com/kotlin/kotlin-dev") } ================================================ FILE: buildSrc/src/main/kotlin/Versions.kt ================================================ import org.gradle.api.Project class Versions(private val project: Project) { // TODO : how to group them? val projectGroup = "io.tryp" val projectRootID = "splain" val projectVMajor = "1.2.0" val projectV = projectVMajor + "-SNAPSHOT" inner class Scala { val group: String = project.properties["scalaGroup"]?.toString() ?: "org.scala-lang" val v: String = project.properties["scalaVersion"].toString() protected val vParts: List = v.split('.').also { parts -> require(parts.size == 3) { "Scala version must be in format 'X.Y.Z' but was: $v" } } val majorV: String = vParts[0] val binaryV: String = vParts.subList(0, 2).joinToString(".") val patchV: String = vParts[2] val artifactSuffix = run { if (majorV == "3") majorV else binaryV } val jsV: String? = project.properties.get("scalaJSVersion")?.toString() } val scala: Scala by lazy { Scala() } } ================================================ FILE: buildSrc/src/main/kotlin/init.kt ================================================ import org.gradle.api.Project /** * Configures the current project as a Kotlin project by adding the Kotlin `stdlib` as a dependency. */ fun Project.versions(): Versions { return Versions(this) } ================================================ FILE: core/build.gradle.kts ================================================ val vs: Versions = versions() dependencies { // see https://github.com/gradle/gradle/issues/13067 fun bothImpl(constraintNotation: Any) { implementation(constraintNotation) testFixturesImplementation(constraintNotation) } bothImpl("${vs.scala.group}:scala-compiler:${vs.scala.v}") testFixturesApi("com.chuusai:shapeless_${vs.scala.binaryV}:2.3.7") testFixturesApi("dev.zio:zio_${vs.scala.binaryV}:1.0.18") testFixturesApi("org.slf4j:slf4j-api:2.0.9") testRuntimeOnly("org.slf4j:slf4j-simple:2.0.9") } ================================================ FILE: core/src/main/resources/scalac-plugin.xml ================================================ splain splain.SplainPlugin ================================================ FILE: core/src/main/scala/splain/Messages.scala ================================================ package splain object Messages { val hasMatching = "hasMatchingSymbol reported error: " val typingTypeApply = "typing TypeApply reported errors for the implicit tree: " val lazyDeriv = "could not find Lazy implicit" val WARNING = "[WARNING] " } ================================================ FILE: core/src/main/scala/splain/PluginSettings.scala ================================================ package splain import splain.PluginSettings.Keys.Key import scala.collection.mutable import scala.reflect.NameTransformer import scala.util.{Failure, Success} case class PluginSettings(pluginOpts: mutable.Map[String, String]) {} object PluginSettings { case class IntKey(initV: Int) extends Keys.Key[Int] { override def parse(s: String): Int = s.toInt } case class BooleanKey(initV: Boolean) extends Key[Boolean] { override def parse(s: String): Boolean = s.toBoolean } case class StringKey(initV: String) extends Key[String] { override def parse(s: String): String = s } object Keys extends Enumeration { trait Key[T] extends Val with Product { def initV: T def parse(s: String): T def name: String = NameTransformer.decode(toString) } val enabled: BooleanKey = BooleanKey(true) val debug: BooleanKey = BooleanKey(false) val `Vimplicits-diverging`: BooleanKey = BooleanKey(false) val `Vimplicits-diverging-max-depth`: IntKey = IntKey(100) val `Vtype-detail`: StringKey = StringKey("1") val `Vtype-diffs-detail`: StringKey = StringKey("1") } lazy val nameToKey: List[(String, Key[_])] = { val vs = Keys.values.toList .collect { case v: Keys.Key[_] => v } vs.map { v => v.name -> v } } lazy val nameToInitValue: List[(String, String)] = nameToKey.map { case (k, v) => k -> v.initV.toString } object TypeDetail extends Enumeration { val long: Value = Value(2) val existential: Value = Value(3) val reduction: Value = Value(4) val position: Value = Value(5) val alias: Value = Value(6) } object TypeDiffsDetail extends Enumeration { val disambiguation: Value = Value(2) val `builtin-msg`: Value = Value(3) val `builtin-msg-always`: Value = Value(4) } trait Implicits { def pluginSettings: PluginSettings implicit class KeyOps[T](self: Key[T]) { def get: T = { val key = self.name pluginSettings.pluginOpts .get(key) .map(self.parse) .getOrElse( throw new UnsupportedOperationException(s"$key is not defined") ) } } implicit class BooleanKeyOps(self: BooleanKey) { def isEnabled: Boolean = { Keys.enabled.get && self.get } } case class DetailParsing[T <: Enumeration](key: StringKey, valueEnum: T) { lazy val raw: Seq[String] = key.get .split(',') .toList .map(_.trim) .filter { s => s.nonEmpty } lazy val (number: Int, refined: Seq[valueEnum.Value]) = { val classified = raw.map { s => scala.util.Try(s.toInt) match { case Failure(_) => Left(s) case Success(v) => Right(v) } } val refined: Seq[valueEnum.Value] = classified collect { case Left(v) => valueEnum.withName(v) } val numbers: Seq[Int] = classified collect { case Right(v) => v } require(numbers.size <= 1, "only one numeric value is allowed") numbers.headOption.getOrElse(1) -> refined } trait ValueOps { val self: valueEnum.Value def isEnabled: Boolean = { Keys.enabled.get && (number >= self.id || refined.contains(self)) } } } val typeDetailParsing: DetailParsing[TypeDetail.type] = DetailParsing(Keys.`Vtype-detail`, TypeDetail) implicit class TypeDetailValueOps(val self: TypeDetail.Value) extends typeDetailParsing.ValueOps {} val typeDiffsDetailParsing: DetailParsing[TypeDiffsDetail.type] = DetailParsing(Keys.`Vtype-diffs-detail`, TypeDiffsDetail) implicit class TypeDiffsDetailValueOps(val self: TypeDiffsDetail.Value) extends typeDiffsDetailParsing.ValueOps {} } } ================================================ FILE: core/src/main/scala/splain/SplainInternalError.scala ================================================ package splain class SplainInternalError(detail: String, cause: Throwable = null) extends InternalError( "You've found a bug in splain formatting extension," + " please post this error with stack trace on https://github.com/tek/splain/issues\n\n" + detail, cause ) {} ================================================ FILE: core/src/main/scala/splain/format/package.scala ================================================ package splain package object format { // TODO: this entire package could be gone, already taken over by scalac formatting } ================================================ FILE: core/src/main/scala/splain/test/AutoLift.scala ================================================ package splain.test import java.io.{ByteArrayInputStream, ByteArrayOutputStream, ObjectInputStream, ObjectOutputStream} import java.util.Base64 import scala.reflect.macros.blackbox trait AutoLift { type Bound def asCode(value: Bound): String trait Mixin { val c: blackbox.Context import c.universe._ implicit def _liftable[T <: Bound]: Liftable[T] = Liftable.apply { value: T => val code = asCode(value) val parsed = c.parse(code) parsed } } } object AutoLift { val MAX_LITERAL_LENGTH = 32768 object SerializingLift extends AutoLift { type Bound = Serializable lazy val encoder: Base64.Encoder = Base64.getEncoder lazy val decoder: Base64.Decoder = Base64.getDecoder lazy val fullPath: String = this.getClass.getCanonicalName.stripSuffix("$") override def asCode(value: Bound): String = { val bOStream = new ByteArrayOutputStream() val oOStream = new ObjectOutputStream(bOStream) oOStream.writeObject(value) val serialized = encoder.encodeToString(bOStream.toByteArray) val chunks = serialized.sliding(MAX_LITERAL_LENGTH, MAX_LITERAL_LENGTH).toList val chunkExpr = chunks .map { cc => s"\"$cc\"" } .mkString("(", ", ", ")") val typeStr = { val canonicalName = value.getClass.getCanonicalName if (canonicalName.endsWith("$")) canonicalName.stripSuffix("$") + ".type" else canonicalName } val result = s""" |$fullPath.fromPreviousStage[$typeStr]$chunkExpr |""".stripMargin result } def fromPreviousStage[T <: Serializable](strs: String*): T = { val bytes = strs .map { str => decoder.decode(str) } .reduce(_ ++ _) val bIStream = new ByteArrayInputStream(bytes) val oIStream = new ObjectInputStream(bIStream) val v = oIStream.readObject() v.asInstanceOf[T] } } } ================================================ FILE: core/src/main/scala/splain/test/CachingFrontEnd.scala ================================================ package splain.test import scala.collection.mutable.ArrayBuffer import scala.tools.reflect.FrontEnd // mimic of StoreReporter case class CachingFrontEnd(sourceName: String) extends FrontEnd { val cached: ArrayBuffer[Issue] = ArrayBuffer.empty[Issue] override def display(info: Info): Unit = { val issue = Issue( info.severity.id, info.msg, info.pos, sourceName ) this.cached += issue } override def reset(): Unit = { super.reset() cached.clear() } } object CachingFrontEnd { object NoSource extends CachingFrontEnd("") } ================================================ FILE: core/src/main/scala/splain/test/Issue.scala ================================================ package splain.test import scala.collection.immutable.ArraySeq import scala.reflect.internal.util.{BatchSourceFile, NoPosition, Position} import scala.util.Try @SerialVersionUID(5124466488126506935L) case class Issue( severity: Int, msg: String, @transient pos: Position, sourceName: String = Issue.defaultSrcName, isShortName: Boolean = false ) extends Serializable { // mimic of PrintReporter.display val display: String = { lazy val severityDisplay: String = new CachingFrontEnd.NoSource.Severity(severity).toString.toLowerCase val posWithFileName = Try( pos.withSource( new BatchSourceFile(sourceName, ArraySeq.unsafeWrapArray(pos.source.content)) ) ) val infoStr = s"$severityDisplay: $msg" val result = posWithFileName .map { pos => val formatted = Position.formatMessage(pos, infoStr, shortenFile = true) formatted } .recover { case ee: Exception => val formatted = Position.formatMessage( NoPosition, s"""$infoStr |with error: $ee |""".stripMargin, shortenFile = true ) formatted } .get result } override def toString: String = { display } } object Issue { lazy val defaultSrcName: "newSource1.scala" = "newSource1.scala" } ================================================ FILE: core/src/main/scala/splain/test/TryCompile.scala ================================================ package splain.test import scala.language.experimental.macros import scala.language.implicitConversions import scala.reflect.runtime.{currentMirror, universe} import scala.tools.nsc.reporters.{Reporter, StoreReporter} import scala.tools.nsc.{Global, Settings} import scala.tools.reflect.ToolBox trait TryCompile extends Product with Serializable { def issues: Seq[Issue] case class Level(level: Int) { def filteredIssues: Seq[Issue] = issues.filter { i => i.severity == level } def displayIssues: String = issues .map { i => i.display } .mkString("\n") } object Error extends Level(2) object Warning extends Level(1) object Info extends Level(0) override lazy val toString: String = { s""" |$productPrefix | --- |${issues.mkString("\n\n")} |""".stripMargin } } object TryCompile { trait Resolved extends TryCompile case class Success( issues: Seq[Issue] = Nil ) extends Resolved { abstract class Evaluable extends Success(issues) { def get: Any } } object Empty extends Success() trait Failure extends Resolved case class TypingError(issues: Seq[Issue] = Nil) extends Failure case class ParsingError(issues: Seq[Issue] = Nil) extends Failure case class OtherFailure(e: Throwable) extends Failure { override def issues: Seq[Issue] = Nil } trait Engine { def args: String final def apply(code: String): TryCompile = try { doCompile(code) } catch { case e: Throwable => OtherFailure(e) } def doCompile(code: String): TryCompile } val mirror: universe.Mirror = currentMirror case class UseReflect(args: String, sourceName: String = Issue.defaultSrcName) extends Engine { override def doCompile(code: String): TryCompile = { val frontEnd = CachingFrontEnd(sourceName) val toolBox: ToolBox[universe.type] = mirror.mkToolBox(frontEnd, options = args) def cached: Seq[Issue] = frontEnd.cached.toSeq val parsed = try { toolBox.parse(code.trim) } catch { case _: Throwable => return TryCompile.ParsingError(cached) } val compiled = try { // toolBox.typecheck(parsed, withImplicitViewsDisabled = false) toolBox.compile(parsed) } catch { case _: Throwable => return TryCompile.TypingError(cached) } val success = Success(cached) new success.Evaluable { override def get: Any = compiled() } } } case class UseNSC(args: String, sourceName: String = Issue.defaultSrcName) extends Engine { val global: Global = { val _settings = new Settings() _settings.reporter.value = classOf[StoreReporter].getCanonicalName _settings.usejavacp.value = true _settings.processArgumentString(args) val global: Global = Global(_settings, Reporter(_settings)) global } val reporter: StoreReporter = global.reporter.asInstanceOf[StoreReporter] override def doCompile(code: String): TryCompile = reporter.synchronized { // shared reporter is not thread safe reporter.reset() val tree = global.newCompilationUnit(code.trim, sourceName) val run = new global.Run() val parser = global.newUnitParser(tree) parser.parse() def reports = reporter.infos.toSeq.map { info => Issue(info.severity.id, info.msg, info.pos, sourceName) } val result = if (reports.exists(v => v.severity == Empty.Error.level)) { ParsingError(reports) } else { run.compileUnits(List(tree)) val success = Success(reports) if (success.Error.filteredIssues.nonEmpty) { TypingError(reports) } else { success } } result } } case class Static[N <: String with Singleton](sourceName: N) { type NN = N def apply(code: String): TryCompile = macro TryCompileMacros.compileCodeTree[N] trait FromCodeMixin { implicit def code2TryCompile(code: String): TryCompile = macro TryCompileMacros.compileCodeTree[N] } } object Static { val default: Static["newSource1.scala"] = Static(Issue.defaultSrcName) def apply(): Static["newSource1.scala"] = default } } ================================================ FILE: core/src/main/scala/splain/test/TryCompileMacros.scala ================================================ package splain.test import splain.test.AutoLift.SerializingLift import scala.collection.mutable.ArrayBuffer import scala.reflect.macros.{whitebox, ParseException, TypecheckException} import scala.tools.nsc.Global import scala.tools.nsc.reporters.FilteringReporter class TryCompileMacros(val c: whitebox.Context) extends SerializingLift.Mixin { import c.universe._ lazy val global: Global = c.universe.asInstanceOf[Global] def reporter: FilteringReporter = global.reporter lazy val defaultSrcLit: Literal = Literal(Constant(Issue.defaultSrcName)) type CodeTree = Tree // TODO: from shapeless.test.IllTypedMacros, no idea what it is for def rectifyCode(codeStr: String): String = { val dummy0 = TermName(c.freshName()) val dummy1 = TermName(c.freshName()) s"object $dummy0 { val $dummy1 = { $codeStr } }" } final def tree2Str(code: CodeTree): String = { code match { case Literal(v) => v.value.asInstanceOf[String] case _ => throw new UnsupportedOperationException( s"`$code` (${code.getClass.getName}) is not a Literal, please only use Literal or final val with refined or no type annotation" ) } } final def type2Str(tt: Type): String = { tt.dealias match { case v: ConstantType => v.value.value.asInstanceOf[String] case _ => throw new UnsupportedOperationException( s"cannot parse type $tt : ${tt.getClass}" ) } } def compileCodeTree[N <: String with Singleton: c.WeakTypeTag](code: CodeTree): Tree = { val _code = tree2Str(code).trim val _name = type2Str(implicitly[c.WeakTypeTag[N]].tpe) val result = run(_code, _name) result } def run(codeStr: String, sourceName: String): Tree = { val cached = ArrayBuffer.empty[Issue] val parsed = try { c.parse(codeStr) } catch { case e: ParseException => cached += Issue( TryCompile.Empty.Error.level, e.msg, e.pos.asInstanceOf[scala.reflect.internal.util.Position], sourceName ) val result = TryCompile.ParsingError(cached.toSeq) return q"$result" } val compiled: c.Tree = try { c.typecheck(parsed) } catch { case e: TypecheckException => cached += Issue( TryCompile.Empty.Error.level, e.msg, e.pos.asInstanceOf[scala.reflect.internal.util.Position], sourceName ) // TODO: this can only capture the first error, which makes the result different from runtime compilation // unfortunately there is nothing we can do val result = TryCompile.TypingError(cached.toSeq) return q"$result" } val success = TryCompile.Success(cached.toSeq) // q"$success" q""" val ss = $success new ss.Evaluable { override def get: Any = { $compiled } } """ } } ================================================ FILE: core/src/main/scala-2.13.7+/latest/splain/ImplicitsExtension.scala ================================================ package splain import scala.collection.concurrent.TrieMap import scala.collection.mutable import scala.tools.nsc.typechecker trait ImplicitsExtension extends TyperCompatViews with typechecker.Implicits { self: SplainAnalyzer => import global._ object ImplicitsHistory { lazy val currentGlobal: Global = { val result = Global() result } case class Global() { val localByPosition: TrieMap[PositionIndex, Local] = TrieMap.empty } case class Local() { object DivergingImplicitErrors { val errors: mutable.ArrayBuffer[DivergentImplicitTypeError] = mutable.ArrayBuffer.empty def push(v: DivergentImplicitTypeError): Unit = { errors.addOne(v) } val linkedErrors = mutable.HashSet.empty[DivergentImplicitTypeError] def getUnlinkedMsgs: Seq[String] = { val Seq(msgs, linkedMsgs) = Seq(errors.toSeq, linkedErrors.toSeq).map { seq => seq.map(v => DivergingImplicitErrorView(v).errMsg).distinct } val linkedMsgSet = linkedMsgs.toSet val result = msgs.filterNot { str => linkedMsgSet.contains(str) } result } val logs: mutable.ArrayBuffer[String] = mutable.ArrayBuffer.empty[String] // unused messages & comments will be displayed at the end of the implicit error } } } override def inferImplicit( tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean, pos: Position ): SearchResult = { import ImplicitsHistory._ import PluginSettings.Keys._ def getResult = super.inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent, pos) if (settings.Vimplicits.value && `Vimplicits-diverging`.isEnabled) { val posII = PositionIndex( tree.pos ) val local = currentGlobal.localByPosition.getOrElseUpdate(posII, Local()) val previousSimilarErrors = local.DivergingImplicitErrors.errors.filter { ee => ee.underlyingTree equalsStructure tree } val previousSimilarErrorsN = previousSimilarErrors.size if (previousSimilarErrorsN >= `Vimplicits-diverging-max-depth`.get) { local.DivergingImplicitErrors.logs += s""" |Implicit search for $tree |has reported $previousSimilarErrorsN diverging errors |Terminated | at ${pos.showDebug} |""".stripMargin.trim // s"Terminating implicit search for $tree at ${pos.showDebug} " + // s"after reporting ${settingVImplicitDivergingThreshold} Diverging implicit errors" return SearchFailure } val result = getResult val divergingErrors = context.reporter.errors.collect { case ee: DivergentImplicitTypeError => ee } divergingErrors.foreach { ee => local.DivergingImplicitErrors.push(ee) // require(ee.pt0 == pt, s"mismatch! ${ee.pt0} != $pt") context.reporter.retainDivergentErrorsExcept(ee) } // val cc = result.isSuccess || result == SearchFailure result } else { val result: SearchResult = getResult result } } } ================================================ FILE: core/src/main/scala-2.13.7+/latest/splain/SplainAnalyzer.scala ================================================ package splain import scala.collection.mutable import scala.tools.nsc._ class SplainAnalyzer(val global: Global, val pluginSettings: PluginSettings) extends typechecker.Analyzer with SplainFormattingExtension with ImplicitsExtension with SplainAnalyzerShim with PluginSettings.Implicits { override val specialFormatters: List[SpecialFormatter] = List( FunctionFormatter, TupleFormatter, ShapelessRecordItemFormatter, RefinedFormatterImproved, // RefinedFormatter, ByNameFormatter ) override def splainFoundReqMsg(found: global.Type, req: global.Type): String = { val original = super.splainFoundReqMsg(found, req) val extra = mutable.Buffer.empty[String] if (PluginSettings.Keys.debug.isEnabled) { extra += "===[ ORIGINAL ERROR ]===" + builtinFoundReqMsg(found, req) + "\n" } val result = (Seq(original) ++ extra.toSeq).mkString("\n") result } } ================================================ FILE: core/src/main/scala-2.13.7+/latest/splain/SplainAnalyzerShim.scala ================================================ package splain import scala.tools.nsc.typechecker.Analyzer trait SplainAnalyzerShim { self: SplainAnalyzer => def migrateFrom(old: Analyzer): Unit = { // fix for #81: transfer deferredOpen that are cached before this initializer old.packageObjects.deferredOpen.foreach { v => self.packageObjects.deferredOpen.add(v.asInstanceOf[self.global.Symbol]) } } } ================================================ FILE: core/src/main/scala-2.13.7+/latest/splain/SplainFormattersExtension.scala ================================================ package splain import scala.tools.nsc.typechecker.splain._ object SplainFormattersExtension {} trait SplainFormattersExtension extends SplainFormatters { self: SplainAnalyzer => import global._ object RefinedFormatterImproved extends SpecialFormatter { object DeclSymbol { def unapply(sym: Symbol): Option[(Formatted, Formatted)] = if (sym.hasRawInfo) Some((Simple(sym.simpleName.toString), formatType(sym.rawInfo, top = true))) else None } val ignoredTypes: List[Type] = List(typeOf[Object], typeOf[Any], typeOf[AnyRef]) def sanitizeParents: List[Type] => List[Type] = { ps => val tpes = ps.distinct val result = tpes.filterNot(t => ignoredTypes.exists(_ =:= t)) if (result.isEmpty) tpes.headOption.toList else result } object Refined { def unapply(tpe: Type): Option[(List[Type], Scope)] = tpe match { case TypeRef(pre, sym, List(RefinedType(parents, decls))) if decls.isEmpty && pre.typeSymbol.fullName == "zio" && sym.fullName == "zio.Has" => val sanitized = sanitizeParents(parents) if (sanitized.length == 1) Some((List(TypeRef(pre, sym, sanitized.headOption.toList)), decls)) else None case RefinedType(types, scope) => if (scope.isEmpty) { val subtypes = types.map(v => dealias(v)).flatMap { case Refined(types, _) => types case tpe => List(tpe) } Some((subtypes, scope)) } else Some((types, scope)) case t @ SingleType(_, _) => unapply(t.underlying) case _ => None } } def formatDecl: Symbol => Formatted = { case sym if sym.hasRawInfo && sym.rawInfo.isInstanceOf[TypeBounds] => val name = sym.simpleName.toString val bounds = formatType(sym.rawInfo, top = true) val boundsStr = self.showFormatted(bounds) Simple(s"type $name$boundsStr") case DeclSymbol(n, t) => Decl(n, t) case sym => Simple(sym.toString) } val none: Formatted = Simple("") def separate[A](left: List[A], right: List[A]): (List[A], List[A], List[A]) = { val leftS = Set(left: _*) val rightS = Set(right: _*) val common = leftS.intersect(rightS) val uniqueLeft = leftS -- common val uniqueRight = rightS -- common (common.toList, uniqueLeft.toList, uniqueRight.toList) } def compareTypes(left: List[Type], right: List[Type]): List[Formatted] = { val (common, uniqueLeft, uniqueRight) = separate(left.map(formatType(_, top = true)), right.map(formatType(_, top = true))) val diffs = uniqueLeft .zipAll(uniqueRight, none, none) .map { case (l, r) => Diff(l, r) } common ++ diffs } def filterDecls(syms: List[Symbol]): List[(Formatted, Formatted)] = syms.collect { case DeclSymbol(sym, rhs) => (sym, rhs) } def compareDecls(left: List[Symbol], right: List[Symbol]): List[Formatted] = { val (common, uniqueLeft, uniqueRight) = separate(filterDecls(left), filterDecls(right)) val diffs = uniqueLeft .map(Some(_)) .zipAll(uniqueRight.map(Some(_)), None, None) .collect { case (Some((sym, l)), Some((_, r))) => DeclDiff(sym, l, r) case (None, Some((sym, r))) => DeclDiff(sym, none, r) case (Some((sym, l)), None) => DeclDiff(sym, l, none) } common.map { case (sym, rhs) => Decl(sym, rhs) } ++ diffs } override def apply[A]( tpe: Type, simple: String, args: List[A], formattedArgs: => List[Formatted], top: Boolean )(rec: (A, Boolean) => Formatted): Option[Formatted] = { tpe match { case Refined(parents, decls) => def elements = sanitizeParents(parents).map(formatType(_, top)) val result = Some(RefinedForm(elements, decls.toList.map(formatDecl))) result case _ => None } } override def diff(left: Type, right: Type, top: Boolean): Option[Formatted] = (left, right) match { case (Refined(leftParents, leftDecls), Refined(rightParents, rightDecls)) => val parents = compareTypes(sanitizeParents(leftParents), sanitizeParents(rightParents)).sorted val decls = compareDecls(leftDecls.toList, rightDecls.toList).sorted val result = Some(RefinedForm(parents, decls)) result case _ => None } } object ShapelessRecordItemFormatter extends SpecialFormatter { def keyTagName = "shapeless.labelled.KeyTag" def taggedName = "shapeless.tag.Tagged" def isKeyTag(tpe: Type): Boolean = tpe.typeSymbol.fullName == keyTagName def isTagged(tpe: Type): Boolean = tpe.typeSymbol.fullName == taggedName object extractRecord { def unapply(tpe: Type): Option[(global.Type, global.Type)] = tpe match { case RefinedType(actual :: key :: Nil, _) if isKeyTag(key) => Some((actual, key)) case _ => None } } object extractStringConstant { def unapply(tpe: Type): Option[String] = tpe match { case ConstantType(Constant(a: String)) => Some(a) case _ => None } } def formatConstant(tag: String): PartialFunction[Type, String] = { case a if a == typeOf[scala.Symbol] => s"'$tag" } def formatKeyArg: PartialFunction[List[Type], Option[Formatted]] = { case RefinedType(parents, _) :: _ :: Nil => for { main <- parents.headOption tagged <- parents.find(isTagged) headArg <- tagged.typeArgs.headOption tag <- extractStringConstant.unapply(headArg) repr <- formatConstant(tag).lift(main) } yield Simple(repr) case extractStringConstant(tag) :: _ :: Nil => Some(Simple(s""""$tag"""")) case tag :: _ :: Nil => Some(formatType(tag, top = true)) } def formatKey(tpe: Type): Formatted = formatKeyArg.lift(tpe.typeArgs).flatten.getOrElse(formatType(tpe, top = true)) def recordItem(actual: Type, key: Type): Infix = Infix(Simple("->>"), formatKey(key), formatType(actual, top = true), top = false) def diff(left: Type, right: Type, top: Boolean): Option[Formatted] = left -> right match { case (extractRecord(a1, k1), extractRecord(a2, k2)) => val rec: ((Formatted, Formatted), Boolean) => Formatted = { case ((l, r), _) => if (l == r) l else Diff(l, r) } val left = formatKey(k1) -> formatKey(k2) val right = formatType(a1, top = true) -> formatType(a2, top = true) Some(formatInfix(Nil, "->>", left, right, top)(rec)) case _ => None } override def apply[A]( tpe: Type, simple: String, args: List[A], formattedArgs: => List[Formatted], top: Boolean )(rec: (A, Boolean) => Formatted): Option[Formatted] = { tpe match { case extractRecord(actual, key) => Some(recordItem(actual, key)) case _ => None } } } } ================================================ FILE: core/src/main/scala-2.13.7+/latest/splain/SplainFormattingExtension.scala ================================================ package splain import scala.annotation.tailrec import scala.collection.concurrent.TrieMap import scala.collection.mutable import scala.collection.mutable.ArrayBuffer import scala.tools.nsc.typechecker import scala.tools.nsc.typechecker.splain._ object SplainFormattingExtension { import scala.reflect.internal.TypeDebugging.AnsiColor._ val ELLIPSIS: String = "⋮".blue val | = "┃" val vertical_| = "━━━━━━━━:" } trait SplainFormattingExtension extends typechecker.splain.SplainFormatting with SplainFormattersExtension { self: SplainAnalyzer => import SplainFormattingExtension._ import global._ import PluginSettings._ case class SplainImplicitErrorLink( fromTree: ImplicitError, fromHistory: DivergentImplicitTypeError ) { val sameCandidateTree: Boolean = fromTree.candidate equalsStructure fromHistory.underlyingTree val samePendingType: Boolean = fromTree.specifics match { case ss: ImplicitErrorSpecifics.NotFound => fromHistory.pt0 =:= ss.param.tpe case _ => false } val moreSpecificPendingType: Boolean = fromTree.specifics match { case ss: ImplicitErrorSpecifics.NotFound => fromHistory.pt0 <:< ss.param.tpe case _ => false } val sameStartingWith: Boolean = { fromHistory.sym.fullLocationString == fromTree.candidate.symbol.fullLocationString } // lazy val divergingSearchStartingWithHere: Boolean = sameStartingWith lazy val divergingSearchDiscoveredHere: Boolean = sameCandidateTree && moreSpecificPendingType } case class SplainImplicitErrorTree( error: ImplicitError, children: Seq[SplainImplicitErrorTree] = Nil ) { import SplainImplicitErrorTree._ def doCollectFull(alwaysDisplayRoot: Boolean = false): Seq[NodeForShow] = { if (children.isEmpty) Seq(NodeForShow(error, alwaysShow = true)) else { Seq(NodeForShow(error, alwaysShow = alwaysDisplayRoot)) ++ { if (children.size >= 2) children.flatMap(_.doCollectFull(true)) else children.flatMap(_.doCollectFull()) } } } lazy val collectFull: Seq[NodeForShow] = doCollectFull(true) lazy val collectCompact: Seq[NodeForShow] = { val displayed = collectFull.zipWithIndex.filter { case (v, _) => v.alwaysShow } val ellipsisIndices = displayed.map(_._2 - 1).toSet + (collectFull.size - 1) val withEllipsis = displayed.map { case (v, i) => if (!ellipsisIndices.contains(i)) v.copy(showEllipsis = true) else v } withEllipsis } case class FormattedChain( source: Seq[NodeForShow] ) { val toList: List[String] = { val collected = source.toList val baseIndent = collected.headOption.map(_.nesting).getOrElse(0) val formatted = collected.map { v => val formatted = v.formatted if (v.showEllipsis) formatted.copy(_2 = formatted._2 :+ ELLIPSIS) else formatted } indentTree(formatted, baseIndent) } override lazy val toString: String = toList.mkString("\n") } object FormattedChain { object Full extends FormattedChain(collectFull) object Compact extends FormattedChain(collectCompact) lazy val VimplicitsVerboseTree: Boolean = settings.VimplicitsVerboseTree.value val display: FormattedChain = if (VimplicitsVerboseTree) Full else Compact } override def toString: String = FormattedChain.Full.toString } object SplainImplicitErrorTree { case class NodeForShow( error: ImplicitError, alwaysShow: Boolean, showEllipsis: Boolean = false ) { def nesting: RunId = error.nesting val formatted: (String, List[String], RunId) = formatNestedImplicit(error) } def fromError( error: ImplicitError, offsprings: List[ImplicitError] ): SplainImplicitErrorTree = { val topNesting = error.nesting val children = fromChildren( offsprings, topNesting ) SplainImplicitErrorTree(error, children) } def fromChildren( offsprings: List[ImplicitError], topNesting: Int ): List[SplainImplicitErrorTree] = { if (offsprings.isEmpty) return Nil val minNesting = offsprings.map(v => v.nesting).min if (minNesting < topNesting + 1) throw new SplainInternalError( "Detail: nesting level of offsprings of an implicit search tree node should be higher" ) val wII = offsprings.zipWithIndex val childrenII = wII .filter { case (sub, _) => if (sub.nesting < minNesting) { throw new SplainInternalError( s"Detail: Sub-node in implicit tree can only have nesting level larger than top node," + s" but (${sub.nesting} < $minNesting)" ) } sub.nesting == minNesting } .map(_._2) val ranges = { val seqs = (childrenII ++ Seq(offsprings.size)) .sliding(2) .toList seqs.map { case Seq(from, until) => from -> until case _ => throw new SplainInternalError("Detail: index should not be empty") } } val children = ranges.map { range => val _top = offsprings(range._1) val _offsprings = offsprings.slice(range._1 + 1, range._2) fromError( _top, _offsprings ) } mergeDuplicates(children) // children } def mergeDuplicates(children: List[SplainImplicitErrorTree]): List[SplainImplicitErrorTree] = { val errors = children.map(_.error).distinct val grouped = errors.map { ee => val group = children.filter(c => c.error == ee) val mostSpecificError = group.head.error // TODO: this old design is based on a huge hypothesis, should it be improved // val mostSpecificError = group.map(_.error).maxBy(v => v.candidate.toString.length) val allChildren = group.flatMap(v => v.children) val mergedChildren = mergeDuplicates(allChildren) SplainImplicitErrorTree(mostSpecificError, mergedChildren) } grouped.distinctBy(v => v.FormattedChain.Full.toString) // TODO: this may lose information } } object ImplicitErrorExtension { def unapplyCandidate(e: ImplicitError): Tree = unapplyRecursively(e.candidate) @tailrec private def unapplyRecursively(tree: Tree): Tree = tree match { case TypeApply(fun, _) => unapplyRecursively(fun) case Apply(fun, _) => unapplyRecursively(fun) case a => a } def cleanCandidate(e: ImplicitError): String = unapplyCandidate(e).toString match { case ImplicitError.candidateRegex(suf) => suf case a => a } } override def formatNestedImplicit(err: ImplicitError): (String, List[String], Int) = { val base = super.formatNestedImplicit(err) import scala.reflect.internal.TypeDebugging.AnsiColor._ val candidate = ImplicitErrorExtension.cleanCandidate(err) val problem = s"${candidate.red} invalid because" object ImplicitErrorsInHistory { lazy val posI: self.PositionIndex = PositionIndex( err.candidate.pos ) lazy val localHistoryOpt: Option[ImplicitsHistory.Local] = ImplicitsHistory.currentGlobal.localByPosition.get(posI) lazy val diverging: Seq[DivergentImplicitTypeError] = { localHistoryOpt.toSeq.flatMap { history => history.DivergingImplicitErrors.errors } } } val extra = mutable.Buffer.empty[String] extra ++= base._2 val discoveredHere = ImplicitErrorsInHistory.diverging.find { inHistory => val link = SplainImplicitErrorLink(err, inHistory) link.divergingSearchDiscoveredHere } discoveredHere match { case Some(ee) => ImplicitErrorsInHistory.localHistoryOpt.foreach { history => history.DivergingImplicitErrors.linkedErrors += ee } val text = DivergingImplicitErrorView(ee).errMsg extra ++= text.split('\n').filter(_.trim.nonEmpty) case _ => } (problem, extra.toList, base._3) } override def formatWithInfix[A](tpe: Type, args: List[A], top: Boolean)(rec: (A, Boolean) => Formatted): Formatted = { val (path, simple) = formatSimpleType(tpe) tpe.nameAndArgsString lazy val formattedArgs = args.map(rec(_, true)) val special = formatSpecial(tpe, simple, args, formattedArgs, top)(rec) special.getOrElse { args match { case left :: right :: Nil if isSymbolic(tpe) => formatInfix(path, simple, left, right, top)(rec) case _ :: _ => Applied(Qualified(path, SimpleName(simple)), formattedArgs) case _ => Qualified(path, SimpleName(simple)) } } } override def formatImplicitError( param: Symbol, errors: List[ImplicitError], annotationMsg: String ): String = { val msg = implicitMessage(param, annotationMsg) val errorTrees = SplainImplicitErrorTree.fromChildren(errors, -1) val errorTreesStr = errorTrees.map(_.FormattedChain.display.toString) val addendum = errorTrees.headOption.toSeq.flatMap { head => import ImplicitsHistory._ val pos = head.error.candidate.pos val localHistoryOpt = currentGlobal.localByPosition.get(PositionIndex(pos)) val addendum: Seq[String] = localHistoryOpt.toSeq.flatMap { history => val unlinkedMsgs = history.DivergingImplicitErrors.getUnlinkedMsgs val unlinkedText = if (unlinkedMsgs.nonEmpty) { val indented = unlinkedMsgs.flatMap { str => indentTree(List((str, Nil, 0)), 1) } Seq( Messages.WARNING + "The following reported error(s) cannot be linked to any part of the implicit search tree:" ) ++ indented } else { Nil } val logs = history.DivergingImplicitErrors.logs val logsText = if (logs.nonEmpty) { val indented = logs.flatMap { str => indentTree(List((str, Nil, 0)), 1) } Seq(Messages.WARNING + "Implicit search may be broken:") ++ indented } else { Nil } val text = unlinkedText ++ logsText text } addendum } val components: Seq[String] = Seq("implicit error;") ++ msg ++ errorTreesStr ++ addendum val result = components.mkString("\n") result } override def extractArgs(tpe: Type): List[global.Type] = TypeView(tpe).extractArgs override def stripType(tt: Type): (List[String], String) = { val view = TypeView(tt) view.path -> view.noArgShortName } // new implementation is idempotent and won't lose information override def dealias(tpe: Type): Type = { TypeView(tpe).dealias_normal } case class FormattedIndex( ft: Formatted, idHash: Int ) object FormattedIndex { def apply(ft: Formatted) = new FormattedIndex( ft, System.identityHashCode(ft) ) } trait Based { def element: Formatted protected def formattedHeader_Body(break: Boolean): (String, Seq[TypeRepr]) lazy val flat: String = { val (header, body) = formattedHeader_Body(false) if (body.isEmpty) header else s"$header { ${body.map(v => v.flat).mkString(";")} }" } lazy val broken: Seq[String] = { val (header, body) = formattedHeader_Body(true) val result = indentTree(List((header, body.flatMap(_.lines).toList, 0)), 1) result } } object Based { lazy val lookup: TrieMap[FormattedIndex, ArrayBuffer[Based]] = TrieMap.empty // MultiMap shortcuts def +=(kv: (FormattedIndex, Based)): Unit = { lookup.getOrElseUpdate(kv._1, ArrayBuffer.empty) += kv._2 } def getAll(k: FormattedIndex): List[Based] = lookup.get(k).toList.flatten } case class Reduction( element: Formatted, from: (String, Formatted) ) extends Based { def index(): Unit = { if (TypeDetail.reduction.isEnabled) { Based += FormattedIndex(element) -> this } } override protected def formattedHeader_Body(break: Boolean): (String, Seq[TypeRepr]) = { s"(${from._1})" -> Seq(showFormattedLImpl(from._2, break)) } } case class BuiltInDiffMsg( element: Formatted, msg: String, infixOpt: Option[Formatted] = None ) extends Based { def index(): Unit = { if (TypeDiffsDetail.`builtin-msg`.isEnabled) Based += FormattedIndex(element) -> this } override protected def formattedHeader_Body(break: Boolean): (String, Seq[TypeRepr]) = { lazy val infixText = infixOpt match { case None => | case Some(ii) => " " + showFormattedLImpl(ii, break).flat + " " } val indented = msg .split("\n") .filter(_ != ";") s"(comparing $infixText)" -> indented.toSeq.map(v => FlatType(v)) } } case class DefPosition( element: Formatted, srcInfo: String, quotes: Seq[String] = Nil ) extends Based { def index(): Unit = { if (TypeDetail.position.isEnabled) Based += FormattedIndex(element) -> this } override protected def formattedHeader_Body(break: Boolean): (String, Seq[TypeRepr]) = { s"(defined at $srcInfo)" -> quotes.map(quote => BrokenType(List(quote))) } } def formatTypeRaw(tpe: Type, top: Boolean): Formatted = { formatWithInfix(tpe, extractArgs(tpe), top)(formatType) } override def formatTypeImpl(tpe: Type, top: Boolean): Formatted = { tpe.typeArgs match { case List(t1, t2) => val result = if (TypeDiffsDetail.disambiguation.isEnabled) { withDisambiguation(Nil, t1, t2) { formatTypeImplNoDisambiguation(tpe, top) } } else { formatTypeImplNoDisambiguation(tpe, top) } result match { case Infix(ii, left, right, _) => val noApparentDiff = (left == right) && (t1 != t2) if (noApparentDiff || TypeDiffsDetail.`builtin-msg-always`.isEnabled) { BuiltInDiffMsg( result, TypeDiffView(t1, t2).builtInDiffMsg, Some(ii) ).index() } case _ => } result case _ => formatTypeImplNoDisambiguation(tpe, top) } } protected def formatTypeImplNoDisambiguation(tpe: Type, top: Boolean): Formatted = { val dtpe = dealias(tpe) val results = Seq(tpe, dtpe).distinct.map { t => formatTypeRaw(t, top) }.distinct results match { case Seq(from, reduced) => Reduction(reduced, "reduced from" -> from).index() case _ => } val result = results.last TypeView(tpe).defPositionOpt.foreach { v => DefPosition( result, v.shortText ).index() } result } override def formatDiffImpl(found: Type, req: Type, top: Boolean): Formatted = { if (TypeDiffsDetail.disambiguation.isEnabled) { val result = withDisambiguation(Nil, found, req) { formatDiffImplNoDisambiguation(found, req, top) } result match { case diff: Diff => val noApparentDiff = (diff.left == diff.right) && (found != req) if (noApparentDiff || TypeDiffsDetail.`builtin-msg-always`.isEnabled) { BuiltInDiffMsg( diff, TypeDiffView(found, req).builtInDiffMsg ).index() } case _ => } result } else { formatDiffImplNoDisambiguation(found, req, top) } } protected def formatDiffImplNoDisambiguation(found: Type, req: Type, top: Boolean): Formatted = { val reduced = Seq(found, req).map(dealias) val Seq(left, right) = reduced if (reduced.distinct.size == 1) { val only = reduced.head val result = formatType(only, top) val basedOn = Seq(found, req).distinct .map { tt => formatTypeRaw(tt, top) } .distinct .flatMap { ft => if (ft == result) None else Some("normalized from" -> ft) } basedOn.foreach { v => Reduction( result, v ).index() } result } else { val result = { val noArgs = Seq(left, right).map { tt => TypeView(tt).noArgType } if (noArgs.distinct.size == 1) { formatDiffInfix(left, right, top) } else { formatDiffSpecial(left, right, top).getOrElse { val result = formatDiffSimple(left, right) result } } } val basedOn = Seq( "left side reduced from" -> Seq(found, left), "right side reduced from" -> Seq(req, right) ) .flatMap { case (clause, fts) => if (fts.distinct.size == 1) None else { val formatted = fts.map { ft => formatTypeRaw(ft, top) }.distinct if (formatted.size == 1) None else Some(clause -> formatted.head) } } basedOn.foreach { v => Reduction( result, v ).index() } result } } case class ShowFormattedHelper(break: Boolean) { import scala.reflect.internal.TypeDebugging.AnsiColor._ def _decideBreak(flat: FlatType, broken: BrokenType): TypeRepr = { if (break) decideBreak(flat, broken) else flat } def appendLastLine(lines: List[String], suffix: String): List[String] = { lines match { case Nil => List(suffix) case head :: Nil => List(head + suffix) case head :: tail => head :: appendLastLine(tail, suffix) } } def withVerticalDelimiter(v: TypeRepr): String = { v.lines .map { ll => | + ll } .mkString("\n") } def showDiff(left: Formatted, right: Formatted): TypeRepr = { val (ll, rr) = (left, right) match { case (Qualified(lpath, lname), Qualified(rpath, rname)) if lname == rname => val prefix = lpath.reverseIterator.zip(rpath.reverseIterator).takeWhile { case (l, r) => l == r }.size + 1 FlatType(s"${qualifiedName(lpath.takeRight(prefix), lname).red}") -> FlatType(s"${qualifiedName(rpath.takeRight(prefix), rname).green}") case (left, right) => _showFormattedL(left) -> _showFormattedL(right) } lazy val flat = FlatType(s"${ll.flat}${|}${rr.flat}") lazy val broken: BrokenType = { val Seq(_ll, _rr) = Seq(ll, rr).map { v => v.indent.indent.indent.indent.indent.joinLines.trim } val result = s""" |found : ${_ll} |${vertical_|} |required: ${_rr}""".stripMargin.trim BrokenType(result.split("\n").toList) } val result = _decideBreak(flat, broken) result } case class Tree( header: String, body: Seq[TypeRepr] ) { lazy val flat: String = { if (body.isEmpty) header else s"$header { ${body.map(v => v.flat).mkString(";")} }" } lazy val broken: Seq[String] = { val result = indentTree(List((header, body.flatMap(_.lines).toList, 0)), 1) result } } /** * If the args of an applied type constructor are multiline, create separate lines for the constructor name and the * closing bracket; else return a single line. */ def showEnclosed( base: TypeRepr, args: List[TypeRepr], brackets: (String, String) = "[" -> "]", splitter: String = "," ): TypeRepr = { val flatArgs = args.map(_.flat).mkString(brackets._1, splitter + " ", brackets._2) val flat = FlatType(s"${base.flat}$flatArgs") def brokenArgs = args match { case head :: tail => tail.foldLeft(head.lines)((z, a) => appendLastLine(z, splitter) ::: a.lines) case _ => Nil } def broken = BrokenType(appendLastLine(base.lines, brackets._1) ::: indent(brokenArgs) ::: List(brackets._2)) _decideBreak(flat, broken) } // TODO: let Based use this? def showTree( base: String, body: Seq[TypeRepr], brackets: (String, String) = "" -> "" ): TypeRepr = { def flat = FlatType { if (body.isEmpty) base else s"$base ${brackets._1}${body.map(v => v.flat).mkString(";")}${brackets._2}" } def broken = BrokenType { indentTree(List((base, body.flatMap(_.lines).toList, 0)), 1) } _decideBreak(flat, broken) } def showCompound( types: List[TypeRepr], infixText: String = "with" ): TypeRepr = { val infixWithSpace = s" $infixText " def flat = FlatType(types.map(_.flat).mkString(infixWithSpace)) def broken = BrokenType( types.map(_.lines).reduceLeft((z, a) => appendLastLine(z, infixWithSpace) ::: a) ) _decideBreak(flat, broken) } def _showFormattedL(v: Formatted): TypeRepr = showFormattedL(v, break) def showTuple(elems: List[Formatted]): TypeRepr = { val formattedElems = elems.map(_showFormattedL) elems match { case List(_) => showEnclosed(FlatType("Tuple1"), formattedElems) case _ => showEnclosed(FlatType(""), formattedElems, brackets = "(" -> ")") } } def apply(ft: Formatted): TypeRepr = { val raw = ft match { case Simple(name) => FlatType(name.name) case Qualified(path, name) => showFormattedQualified(path, name) case Applied(cons, args) => showEnclosed(_showFormattedL(cons), args.map(_showFormattedL)) case tpe @ Infix(_, _, _, top) => wrapParensRepr( if (break) breakInfix(flattenInfix(tpe)) else FlatType(flattenInfix(tpe).map(showFormatted).mkString(" ")), top ) case UnitForm => FlatType("Unit") case FunctionForm(args, ret, top) => FlatType(wrapParens(s"${showFuncParams(args.map(showFormatted))} => ${showFormatted(ret)}", top)) case TupleForm(elems) => showTuple(elems) case RefinedForm(elems, decls) => val compound = showCompound(elems.map(_showFormattedL)) val refined = if (decls.isEmpty) compound else if (truncateDecls(decls)) showEnclosed(compound, List(FlatType("...")), brackets = " {" -> "}", splitter = ";") else showEnclosed(compound, decls.map(_showFormattedL), brackets = " {" -> "}", splitter = ";") refined case Diff(left, right) => showDiff(left, right) case Decl(sym, rhs) => showTree( s"type ${showFormatted(sym)} =", Seq(_showFormattedL(rhs)) ) case DeclDiff(sym, left, right) => val diff = showDiff(left, right) showTree( s"type ${showFormatted(sym)} =", Seq(diff) ) case ByName(tpe) => FlatType(s"(=> ${showFormatted(tpe)})") } val index = FormattedIndex(ft) val basedOn = Based.getAll(index) val result = { basedOn match { case Nil => raw case _ => def flat = FlatType(raw.flat + " " + basedOn.map(_.flat).mkString(" ")) def broken = BrokenType(raw.lines ++ basedOn.flatMap(_.broken)) raw match { case _: BrokenType => broken case _ => _decideBreak(flat, broken) } } } result } } override def showFormattedLImpl(ft: Formatted, break: Boolean): TypeRepr = { ShowFormattedHelper(break)(ft) } } ================================================ FILE: core/src/main/scala-2.13.7+/latest/splain/SplainPlugin.scala ================================================ package splain import scala.tools.nsc._ import scala.tools.nsc.typechecker.{Analyzer, MacroAnnotationNamers} class SplainPlugin(val global: Global) extends SplainPluginLike with PluginSettings.Implicits { override lazy val pluginSettings: PluginSettings = PluginSettings(this.opts) lazy val splainAnalyzer: SplainAnalyzer = if (global.settings.YmacroAnnotations.value) new SplainAnalyzer(global, pluginSettings) with MacroAnnotationNamers else new SplainAnalyzer(global, pluginSettings) { val analyzerField = classOf[Global].getDeclaredField("analyzer") analyzerField.setAccessible(true) val oldAnalyzer = analyzerField.get(global).asInstanceOf[Analyzer] analyzerField.set(global, splainAnalyzer) val phasesSetMapGetter = classOf[Global] .getDeclaredMethod("phasesSet") val phasesSet = phasesSetMapGetter .invoke(global) .asInstanceOf[scala.collection.mutable.Set[SubComponent]] if (phasesSet.exists(_.phaseName == "typer")) { def subcomponentNamed(name: String) = phasesSet .find(_.phaseName == name) .head val oldScs @ List(oldNamer @ _, oldPackageobjects @ _, oldTyper @ _) = List( subcomponentNamed("namer"), subcomponentNamed("packageobjects"), subcomponentNamed("typer") ) val newScs = List(splainAnalyzer.namerFactory, splainAnalyzer.packageObjects, splainAnalyzer.typerFactory) splainAnalyzer.migrateFrom(oldAnalyzer) phasesSet --= oldScs phasesSet ++= newScs } // TODO: remove them after AnalyzerPlugin interface becomes stable } override def init(options: List[String], error: String => Unit): Boolean = { def invalid(opt: String): Unit = error( s"splain: invalid option `$opt`, supported options are ${PluginSettings.nameToKey.map(kv => "`" + kv._1 + "`").mkString(", ")}" ) def setOpt(key: String, value: String): Unit = if (opts.contains(key)) opts.update(key, value) else invalid(key) options.foreach { opt => opt.split(":").toList match { case key :: value :: Nil => setOpt(key, value) case key :: Nil => setOpt(key, "true") case _ => invalid(opt) } } PluginSettings.Keys.enabled.get } } ================================================ FILE: core/src/main/scala-2.13.7+/latest/splain/SplainPluginLike.scala ================================================ package splain import scala.collection.mutable import scala.tools.nsc._ import scala.tools.nsc.plugins.PluginComponent trait SplainPluginLike extends plugins.Plugin { val name = "splain" val description = "better types and implicit errors" val components: List[PluginComponent] = Nil val opts: mutable.Map[String, String] = PluginSettings.nameToInitValue.to(mutable.Map) } ================================================ FILE: core/src/main/scala-2.13.7+/latest/splain/TyperCompatViews.scala ================================================ package splain import scala.reflect.internal.util.{NoSourceFile, Position} trait TyperCompatViews { self: SplainAnalyzer => import global._ case class TypeView(self: Type) { lazy val extractArgs: List[global.Type] = { self.typeArgs } lazy val noArgType: Type = if (extractArgs.nonEmpty) { self.typeConstructor } else { self } lazy val dealias_normal: Type = { if (isAux(self)) self else { val result = self.dealias.normalize result match { case p: PolyType => val target = TypeView(p.resultType).dealias_normal val _p = p.copy( resultType = target ) _p case _ => result } } } lazy val definingSymbol: Symbol = { self match { case tt: SingletonType => tt.termSymbol case _ => self.typeSymbolDirect } } private lazy val parts = definingSymbol.ownerChain.reverse .map(_.name.decodedName.toString) .filterNot(part => part.startsWith("<") && part.endsWith(">")) lazy val (path, noArgShortName) = { val (ownerPath, _) = parts.splitAt(Math.max(0, parts.size - 1)) val ownerPathPrefix = ownerPath.mkString(".") val ttString = TypeView(noArgType).typeToString if (ttString.startsWith(ownerPathPrefix)) { ownerPath -> ttString.stripPrefix(ownerPathPrefix).stripPrefix(".") } else { Nil -> ttString } } lazy val prefixFullName: String = { self.prefix.typeSymbol.fullNameString } // probably not useful, withDisambiguation + longString should cover most cases lazy val prefixContextIfNeeded: Option[String] = { prefixFullName.toLowerCase match { case "" | "" | "" => None case _ => if (self.toLongString.startsWith(prefixFullName)) None else { Some(s"(in $prefixFullName)") } } } object _DefPosition { lazy val value: Position = definingSymbol.pos lazy val noSource: Boolean = value.source == NoSourceFile lazy val shortText: String = { val prefix = value.source.file.path + ":" val result = s"$prefix${value.line}:${value.column}" result } lazy val formattedText: String = { Position.formatMessage(value, "", shortenFile = false) } } def defPositionOpt: Option[_DefPosition.type] = Option(_DefPosition).filterNot(_.noSource) def typeToString: String = { import PluginSettings.TypeDetail def short = self.safeToString val base = { if (TypeDetail.long.isEnabled) scala.util.Try(self.toLongString).getOrElse(short) else short } val extraExistential = if (TypeDetail.existential.isEnabled) scala.util.Try(existentialContext(self)).toOption else None val extraAlias = if (TypeDetail.alias.isEnabled) scala.util.Try(explainAlias(self)).toOption else None (Seq(base) ++ extraExistential ++ extraAlias).mkString("") } } case class TypeDiffView( found: Type, req: Type ) { def map(fn: Type => Type): TypeDiffView = TypeDiffView(fn(found), fn(req)) def toTuple[T](fn: Type => T): (T, T) = (fn(found), fn(req)) // copied from eponymous variable in Scala compiler // apparently doesn't work after type arg stripped // lazy val easilyMistakable: Boolean = { // // val foundWiden = found.widen // val reqWiden = req.widen // val sameNamesDifferentPrefixes = // foundWiden.typeSymbol.name == reqWiden.typeSymbol.name && // foundWiden.prefix.typeSymbol != reqWiden.prefix.typeSymbol // val easilyMistakable = // sameNamesDifferentPrefixes && // !req.typeSymbol.isConstant && // finalOwners(foundWiden) && finalOwners(reqWiden) && // !found.typeSymbol.isTypeParameterOrSkolem && !req.typeSymbol.isTypeParameterOrSkolem // // easilyMistakable // } lazy val builtInDiffMsg: String = { val result = builtinFoundReqMsg(found, req) result } } case class DivergingImplicitErrorView(self: DivergentImplicitTypeError) { lazy val errMsg: String = { val formattedPT = showFormatted(formatType(self.pt0, top = false)) s"diverging implicit expansion for type $formattedPT\nstarting with ${self.sym.fullLocationString}" } } case class PositionIndex( pos: Position ) {} } ================================================ FILE: core/src/main/scala-2.13.7+/latest/splain/package.scala ================================================ import scala.tools.nsc.typechecker.splain.SimpleName import scala.language.implicitConversions package object splain { implicit def asSimpleName(s: String): SimpleName = SimpleName(s) } ================================================ FILE: core/src/test/resources/splain/builtin/BasicSpec/__direct/check ================================================ newSource1.scala:13: error: implicit error; !I e: ImplicitChain.II ImplicitChain.g invalid because !I impPar3: ImplicitChain.I1 ――ImplicitChain.i1 invalid because !I impPar7: ImplicitChain.I3 implicitly[II] ^ newSource1.scala:6: error: type mismatch; FoundReq.L┃FoundReq.R f(new L) ^ newSource1.scala:13: error: type mismatch; Long.VeryLong[ Long.VeryLong[ found : Long.VeryLong[ Long.VeryLong[ Long.VeryLong[Long.VeryLong[Long.VeryLong[Long.VeryLong[String]]]] ] ] ━━━━━━━━: required: Long.VeryLong2[ Long.VeryLong[ Long.VeryLong[Long.VeryLong[Long.VeryLong[Long.VeryLong[String]]]] ] ] ] ] val str: Req = ??? : Found ^ newSource1.scala:4: error: implicit error; !I e: Long.VeryLong[ Long.VeryLong[ Long.VeryLong[ Long.VeryLong[ Long.VeryLong[Long.VeryLong[Long.VeryLong[Long.VeryLong[String]]]] ] ] ] ] implicitly[VeryLong[ ^ newSource1.scala:7: error: type mismatch; Compound.y.type┃String f(y) ^ newSource1.scala:10: error: type mismatch; Compound.T┃ with Int┃String f(z) ^ newSource1.scala:4: error: implicit error; !I e: Long.VeryLong[ Long.VeryLong[ Long.VeryLong[ Long.VeryLong[ Long.VeryLong[Long.VeryLong[Long.VeryLong[Long.VeryLong[String]]]] ] ] ] ] { type A = Int; type B = Int; type C = Int; type D = Int; type E = Int; type F = Int; type G = Int; type H = Int } implicitly[VeryLong[ ^ newSource1.scala:4: error: implicit error; !I e: ( Long.VeryLong[Int], Long.VeryLong[ Long.VeryLong[ Long.VeryLong[ Long.VeryLong[ Long.VeryLong[Long.VeryLong[Long.VeryLong[Long.VeryLong[Int]]]] ] ] ] ] ) implicitly[ ^ newSource1.scala:16: error: type mismatch; ( Long.VeryLong[Int], Long.VeryLong[ Long.VeryLong[ Long.VeryLong[ Long.VeryLong[ Long.VeryLong[Long.VeryLong[Long.VeryLong[Long.VeryLong[Int┃String]]]] ] ] ] ] ) ) = x ^ newSource1.scala:11: error: type mismatch; B.this.t1.type┃B.this.t.TT val t2: t.TT = t1 ^ newSource1.scala:7: error: implicit error; !I e: Bounds.F[Bounds.Arg] Bounds.g invalid because nonconformant bounds; [Bounds.Arg, Nothing] [A <: Bounds.Base, B] implicitly[F[Arg]] ^ newSource1.scala:4: error: implicit error; !I ec: scala.concurrent.ExecutionContext Cannot find an implicit ExecutionContext. You might add an (implicit ec: ExecutionContext) parameter to your method. The ExecutionContext is used to configure how and on which thread pools asynchronous tasks (such as Futures) will run, so the specific ExecutionContext that is selected is important. If your application does not define an ExecutionContext elsewhere, consider using Scala's global ExecutionContext by defining the following: implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global long ^ newSource1.scala:10: error: implicit error; !I e: String f invalid because !I impPar4: List[ ( InfixBreak.VeryLongTypeName :::: InfixBreak.VeryLongTypeName :::: InfixBreak.VeryLongTypeName :::: InfixBreak.VeryLongTypeName ) :::: (InfixBreak.Short :::: InfixBreak.Short) :::: ( InfixBreak.VeryLongTypeName :::: InfixBreak.VeryLongTypeName :::: InfixBreak.VeryLongTypeName :::: InfixBreak.VeryLongTypeName ) :::: InfixBreak.VeryLongTypeName :::: InfixBreak.VeryLongTypeName :::: InfixBreak.VeryLongTypeName :::: InfixBreak.VeryLongTypeName ] (No implicit view available from Int => InfixBreak.T2.) implicitly[String] ^ newSource1.scala:11: error: implicit error; !I e: DeepHole.C1[[Z]DeepHole.C2[DeepHole.T1,DeepHole.T2,Z]] implicitly[C1[T3]] ^ newSource1.scala:9: error: implicit error; !I e: Aux.F.Aux[Aux.C, Aux.D] Aux.f invalid because !I impPar10: Aux.C implicitly[F.Aux[C, D]] ^ newSource1.scala:11: error: type mismatch; Refined.A with Refined.B with Refined.E┃Refined.C with Refined.F┃ { type X = Int┃String; type Y = String; type Z = ┃String } f(x) ^ newSource1.scala:21: error: type mismatch; Refined.E┃Refined.C with Refined.F┃ with Refined.Sub1.A with Refined.Sub1.Sub2.B { type X = Int┃String; type Y = String; type Z = ┃String } f(x) ^ newSource1.scala:9: error: implicit error; !I e: Refined.Node {type T <: Int} implicitly[NodeLt[Int]] ^ newSource1.scala:11: error: type mismatch; Int(1)┃Refined.Node {type T <: Int} val k: NodeLt[Int] = 1 ^ newSource1.scala:10: error: implicit error; !I e: Refined.Node{type T = _$2} forSome { type _$2 <: Int } implicitly[NodeLt[Int]] ^ newSource1.scala:12: error: type mismatch; Int(1)┃Refined.Node{type T = _$2} forSome { type _$2 <: Int } val k: NodeLt[Int] = 1 ^ newSource1.scala:25: error: type mismatch; C.X.Y.T┃B.X.Y.T f(x: C.X.Y.T) ^ newSource1.scala:6: error: type mismatch; Int┃(=> Foo.A) => Foo.B f(1: Int) ^ newSource1.scala:3: error: type mismatch; String┃Tuple1[String] val a: Tuple1[String] = "Tuple1": String ^ newSource1.scala:7: error: implicit error; !I e: SingleImp.a.type *** SingleImp.b.type implicitly[a.type *** b.type] ^ newSource1.scala:8: error: implicit error; !I e: a.type *** b.type implicitly[a.type *** b.type] ^ newSource1.scala:6: error: implicit error; !I e: a.type *** b.type implicitly[a.type *** b.type] ^ newSource1.scala:5: error: implicit error; !I ev: scala.math.Ordering[Object] No implicit Ordering[Object] found to build a SortedSet[Object]. You may want to upcast to a Set[Int] first by calling `unsorted`. Ordering.ordered invalid because !I asComparable: Object => Comparable[Any] No implicit view available from Object => Comparable[_ >: Object]. Ordering.comparatorToOrdering invalid because !I cmp: java.util.Comparator[Object] ms.map(_ => o) ^ newSource1.scala:9: error: implicit error; !I e: List[a.TypeA] (No implicit view available from Int => a.TypeA.) implicitly[List[TypeA]] ^ newSource1.scala:10: error: implicit error; !I e: Seq[a.b.TypeB] (No implicit view available from Int => a.b.TypeB.) implicitly[Seq[TypeB]] ^ newSource1.scala:11: error: implicit error; !I e: Iterable[a.b.c.TypeC] implicitly[Traversable[TypeC]] ^ newSource1.scala:12: error: implicit error; !I e: Iterator[a.b.c.d.TypeD] implicitly[Iterator[TypeD]] ^ ================================================ FILE: core/src/test/resources/splain/builtin/MaxRefinedSpec/__direct/check ================================================ newSource1.scala:7: error: type mismatch; TruncRefined.D┃TruncRefined.C { type X = TruncRefined.C; type Y = TruncRefined.D } f(new D { type X = C; type Y = D }) ^ newSource1.scala:7: error: type mismatch; TruncRefined.D┃TruncRefined.C {...} f(new D { type X = C; type Y = D }) ^ ================================================ FILE: core/src/test/resources/splain/builtin/VerboseTreeSpec/__direct/check ================================================ newSource1.scala:28: error: implicit error; !I e: tpes.I1 i1a invalid because !I p: tpes.I2 ――i2 invalid because !I p: tpes.I3 ――――i3a invalid because !I p: tpes.I4 ――――――i4 invalid because !I p: tpes.I5 ――――――――i5 invalid because !I p: tpes.I6 ――――――――――i6a invalid because !I p: tpes.I7 ――――――――――――i7 invalid because !I p: tpes.I8 ――――――――――――――i8 invalid because !I p: tpes.I9 ――――――――――i6b invalid because !I p: tpes.I8 ――――――――――――i8 invalid because !I p: tpes.I9 ――――i3b invalid because !I p: tpes.I4 ――――――i4 invalid because !I p: tpes.I5 ――――――――i5 invalid because !I p: tpes.I6 ――――――――――i6a invalid because !I p: tpes.I7 ――――――――――――i7 invalid because !I p: tpes.I8 ――――――――――――――i8 invalid because !I p: tpes.I9 ――――――――――i6b invalid because !I p: tpes.I8 ――――――――――――i8 invalid because !I p: tpes.I9 i1b invalid because !I p: tpes.I6 ――i6a invalid because !I p: tpes.I7 ――――i7 invalid because !I p: tpes.I8 ――――――i8 invalid because !I p: tpes.I9 ――i6b invalid because !I p: tpes.I8 ――――i8 invalid because !I p: tpes.I9 implicitly[I1] ^ newSource1.scala:28: error: implicit error; !I e: tpes.I1 i1a invalid because !I p: tpes.I2 ⋮ ――i3a invalid because !I p: tpes.I4 ⋮ ――――i6a invalid because !I p: tpes.I7 ⋮ ――――――――i8 invalid because !I p: tpes.I9 ――――i6b invalid because !I p: tpes.I8 ――――――i8 invalid because !I p: tpes.I9 ――i3b invalid because !I p: tpes.I4 ⋮ ――――i6a invalid because !I p: tpes.I7 ⋮ ――――――――i8 invalid because !I p: tpes.I9 ――――i6b invalid because !I p: tpes.I8 ――――――i8 invalid because !I p: tpes.I9 i1b invalid because !I p: tpes.I6 ――i6a invalid because !I p: tpes.I7 ⋮ ――――――i8 invalid because !I p: tpes.I9 ――i6b invalid because !I p: tpes.I8 ――――i8 invalid because !I p: tpes.I9 implicitly[I1] ^ ================================================ FILE: core/src/test/resources/splain/plugin/ErrorsCompatSpec/byname/code.scala ================================================ object ByName { type A def f( implicit a: => A ): Unit = ??? { implicit val a: A = ??? f } } ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/ambiguous/code.scala ================================================ object Ambiguous { implicit val c1: C = ??? implicit val c2: C = ??? implicit def f1: D = ??? implicit def f2: D = ??? implicitly[D] } ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/ambiguous/error ================================================ ambiguous implicit values: both method f1 in object Ambiguous of type types.D and method f2 in object Ambiguous of type types.D match expected type types.D ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/bounds/code.scala ================================================ object NonconformantBounds { trait F[A] implicit def f[A <: C, B]: F[A] = ??? implicitly[F[D *** C]] } ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/bounds/error ================================================ newSource1.scala:12: error: implicit error; !I e: NonconformantBounds.F[types.D *** types.C] NonconformantBounds.f invalid because nonconformant bounds; [types.D *** types.C, Nothing] [A <: types.C, B] implicitly[F[D *** C]] ^ ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/chain/code.scala ================================================ import shapeless.Poly1 object pol extends Poly1 trait Low { trait I1 trait I2 trait I3 trait I4 trait F[X[_]] implicit def lowI1: I1 = ??? implicit def lowI2: I2 = ??? } object ImplicitChain extends Low { type T1 = C *** D >:< (C with D { type A = D; type B = C }) type T2 = D *** ((C >:< C) *** (D => Unit)) type T3 = (D *** (C *** String)) >:< ((C, D, C) *** D) type T4 = C *** D *** C type T5 = D *** C >:< D type T6 = pol.Case.Aux[Int, String] type T7 = D >:< C >:< D implicit def i1( implicit impPar7: I3 ): I1 = ??? implicit def i2a( implicit impPar8: I3 ): I2 = ??? implicit def i2b( implicit impPar8: I3 ): I2 = ??? implicit def i4( implicit impPar9: I2 ): I4 = ??? implicit def t7( implicit impPar14: F[({ type λ[X] = Either[Int, X] })#λ] ): T7 = ??? implicit def t5( implicit impPar13: pol.Case.Aux[Int, String] ): T5 = ??? implicit def t4( implicit impPar12: T5 ): T4 = ??? implicit def t3a( implicit impPar11: T7 ): T3 = ??? implicit def t3b( implicit impPar10: T4 ): T3 = ??? implicit def f( implicit impPar4: I4, impPar2: T3 ): T2 = ??? implicit def g( implicit impPar3: I1, impPar1: T2 ): T1 = ??? implicitly[T1] } ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/chain/error ================================================ newSource1.scala:77: error: implicit error; !I e: (types.C *** types.D) >:< types.C with types.D {type A = types.D; type B = types.C} g invalid because !I impPar1: types.D *** (types.C >:< types.C) *** (types.D => Unit) ――f invalid because !I impPar2: (types.D *** types.C *** String) >:< (types.C, types.D, types.C) *** types.D ――――t3a invalid because !I impPar11: (types.D >:< types.C) >:< types.D ――――――t7 invalid because !I impPar14: ImplicitChain.F[[X]scala.util.Either[Int,X]] ――――t3b invalid because !I impPar10: (types.C *** types.D) *** types.C ――――――t4 invalid because !I impPar12: (types.D *** types.C) >:< types.D ――――――――t5 invalid because !I impPar13: pol.Case.Aux[Int, String] implicitly[T1] ^ ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/higherKindArg/code.scala ================================================ import shapeless.Poly1 object pol extends Poly1 object Functors { trait F[X[_]] implicitly[F[({ type λ[X] = Either[Int, X] })#λ]] } ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/higherKindArg/error ================================================ newSource1.scala:17: error: implicit error; !I e: Functors.F[[X]scala.util.Either[Int,X]] implicitly[F[({ type λ[X] = Either[Int, X] })#λ]] ^ ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/implicit-ctrl-char/code.scala ================================================ import annotation.implicitNotFound object Annotation { trait Arg @implicitNotFound("A\n ┃ B\n ┃ C") trait F[A] trait G[A] implicit def f[A]( implicit ev: F[A] ): G[A] = ??? implicitly[G[Arg]] } ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/implicit-ctrl-char/error ================================================ newSource1.scala:25: error: implicit error; !I e: Annotation.G[Annotation.Arg] Annotation.f invalid because !I ev: Annotation.F[Annotation.Arg] A ┃ B ┃ C implicitly[G[Arg]] ^ ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/lazy/code.scala ================================================ import shapeless._ import shapeless.ops.hlist._ object LazyImp { implicit def dc( implicit a: C *** D ): D *** C = ??? implicit def d( implicit a: D *** C ): D = ??? implicit def c( implicit a: Lazy[D] ): C = ??? implicitly[C] } ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/lazy/error ================================================ newSource1.scala:25: error: implicit error; !I e: types.C c invalid because !I a: shapeless.Lazy[types.D] could not find Lazy implicit value of type types.D ――d invalid because !I a: types.D *** types.C ――――dc invalid because !I a: types.C *** types.D implicitly[C] ^ ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/member/code.scala ================================================ object NotAMember { val a = new (C *** D >:< D *** C) a.attr } ================================================ FILE: core/src/test/resources/splain/plugin/PluginSpec/member/error ================================================ newSource1.scala:11: error: value attr is not a member of types.C *** types.D >:< types.D *** types.C a.attr ^ ================================================ FILE: core/src/test/resources/splain/plugin/ShapelessSpec/lazyImplicit/code.scala ================================================ import shapeless.Lazy object DivergingImplicits { type C type D object Diverging { trait ::[A, B] implicit def f[A, B]( implicit ii: Lazy[Int] ): A :: B = ??? implicitly[C :: D] } } ================================================ FILE: core/src/test/resources/splain/plugin/ShapelessSpec/lazyImplicit/error ================================================ newSource1.scala:25: error: implicit error; !I e: DivergingImplicits.C :: DivergingImplicits.D Diverging.f invalid because !I ii: shapeless.Lazy[Int] could not find Lazy implicit value of type Int implicitly[C :: D] ^ ================================================ FILE: core/src/test/resources/splain/plugin/ShapelessSpec/record/code.scala ================================================ import shapeless._ import shapeless.record._ import shapeless.syntax.singleton._ object ShapelessRecord { object Key object Value type Message = Record.`'sym -> String, "str" -> Value.type, Key -> Int`.T def show(message: Message) = ??? val a = ('sym ->> "value") :: ("str" ->> Value) :: (Key ->> 42L) :: HNil show(a) } ================================================ FILE: core/src/test/resources/splain/plugin/ShapelessSpec/record/error ================================================ newSource1.scala:26: error: type mismatch; ('sym ->> String) :: ("str" ->> ShapelessRecord.Value.type) :: (ShapelessRecord.Key.type ->> Long┃Int) :: shapeless.HNil show(a) ^ ================================================ FILE: core/src/test/resources/splain/plugin/ShapelessSpec/witness-value/code.scala ================================================ import shapeless._ import shapeless.ops.hlist._ object WitnessImp { def fn[A, B](a: A, b: B)( implicit ev: A *** B ) = ??? fn(Witness(3).value, Witness(4).value) } ================================================ FILE: core/src/test/resources/splain/plugin/ShapelessSpec/witness-value/error ================================================ newSource1.scala:18: error: implicit error; !I ev: Int(3) *** Int(4) fn(Witness(3).value, Witness(4).value) ^ ================================================ FILE: core/src/test/resources/splain/plugin/VImplicitDivergingSpec/circular/code.scala ================================================ object DivergingImplicits { type C type D object Circular { implicit def f( implicit c: C ): D = ??? implicit def g( implicit d: D ): C = ??? implicitly[C] } } ================================================ FILE: core/src/test/resources/splain/plugin/VImplicitDivergingSpec/circular/error ================================================ newSource1.scala:17: error: implicit error; !I e: DivergingImplicits.C g invalid because !I d: DivergingImplicits.D diverging implicit expansion for type DivergingImplicits.D starting with method f in object Circular ――f invalid because !I c: DivergingImplicits.C ――――g invalid because !I d: DivergingImplicits.D diverging implicit expansion for type DivergingImplicits.D starting with method f in object Circular implicitly[C] ^ ================================================ FILE: core/src/test/resources/splain/plugin/VImplicitDivergingSpec/circular-recoverable/code.scala ================================================ object DivergingImplicits { type C type D trait LowLevel { implicit def f2: D = ??? } object Circular extends LowLevel { implicit def f( implicit c: C ): D = ??? implicit def g( implicit d: D ): C = ??? implicitly[C] } } ================================================ FILE: core/src/test/resources/splain/plugin/VImplicitDivergingSpec/diverging/code.scala ================================================ object DivergingImplicits { type C type D object Diverging { trait ::[A, B] implicit def f[A, B]( implicit ii: Int :: A :: B ): A :: B = ??? implicit def g[A, B]: Int :: Int :: Int :: A :: B = ??? implicitly[C :: D] } } ================================================ FILE: core/src/test/resources/splain/plugin/VImplicitDivergingSpec/diverging/error ================================================ newSource1.scala:16: error: implicit error; !I e: DivergingImplicits.C :: DivergingImplicits.D Diverging.f invalid because !I ii: Int :: DivergingImplicits.C :: DivergingImplicits.D ――Diverging.f invalid because !I ii: Int :: Int :: DivergingImplicits.C :: DivergingImplicits.D diverging implicit expansion for type (Int :: Int :: DivergingImplicits.C :: DivergingImplicits.D) starting with method f in object Diverging ――――Diverging.f invalid because !I ii: Int :: Int :: Int :: DivergingImplicits.C :: DivergingImplicits.D diverging implicit expansion for type (Int :: Int :: Int :: DivergingImplicits.C :: DivergingImplicits.D) starting with method g in object Diverging implicitly[C :: D] ^ ================================================ FILE: core/src/test/resources/splain/plugin/VImplicitDivergingSpec/diverging-compact/code.scala ================================================ object DivergingImplicits { type C type D object Diverging { trait ::[A, B] implicit def f[A, B]( implicit ii: Int :: A :: B ): A :: B = ??? implicit def g[A, B]: Int :: Int :: Int :: A :: B = ??? implicitly[C :: D] } } ================================================ FILE: core/src/test/resources/splain/plugin/VImplicitDivergingSpec/diverging-compact/error ================================================ newSource1.scala:16: error: implicit error; !I e: DivergingImplicits.C :: DivergingImplicits.D Diverging.f invalid because !I ii: Int :: DivergingImplicits.C :: DivergingImplicits.D ⋮ ――Diverging.f invalid because !I ii: Int :: Int :: Int :: DivergingImplicits.C :: DivergingImplicits.D diverging implicit expansion for type (Int :: Int :: Int :: DivergingImplicits.C :: DivergingImplicits.D) starting with method g in object Diverging implicitly[C :: D] ^ ================================================ FILE: core/src/test/resources/splain/plugin/VImplicitDivergingSpec/self/code.scala ================================================ object DivergingImplicits { type C type D object Endo { implicit def f( implicit c: C ): C = ??? implicitly[C] } } ================================================ FILE: core/src/test/resources/splain/plugin/VImplicitDivergingSpec/self/error ================================================ newSource1.scala:12: error: implicit error; !I e: DivergingImplicits.C f invalid because !I c: DivergingImplicits.C diverging implicit expansion for type DivergingImplicits.C starting with method f in object Endo ――f invalid because !I c: DivergingImplicits.C diverging implicit expansion for type DivergingImplicits.C starting with method f in object Endo implicitly[C] ^ ================================================ FILE: core/src/test/resources/splain/plugin/VTypeDetailPositionSpec/__direct/check ================================================ newSource1.scala:12: error: implicit error; !I e: Diff.e1.VV =:= String Cannot prove that Diff.e1.VV =:= String. implicitly[e1.VV =:= String] ^ newSource1.scala:13: error: type mismatch; String┃Diff.e1.VV val x: e1.VV = ??? : String ^ newSource1.scala:12: error: implicit error; !I e: Diff.e1.VV (defined at newSource1.scala:9:10) =:= String Cannot prove that Diff.e1.VV =:= String. implicitly[e1.VV =:= String] ^ newSource1.scala:13: error: type mismatch; String┃Diff.e1.VV (defined at newSource1.scala:9:10) val x: e1.VV = ??? : String ^ ================================================ FILE: core/src/test/resources/splain/plugin/VTypeDetailReductionSpec/__direct/check ================================================ newSource1.scala:8: error: implicit error; !I e: Option[Int] =:= Option[String] Cannot prove that FoundReqVsImplicit.vecInt.Head =:= Option[String]. implicitly[vecInt.Head =:= Option[String]] ^ newSource1.scala:10: error: type mismatch; Option[String┃Int] val x: vecInt.Head = ??? : Option[String] ^ newSource1.scala:8: error: implicit error; !I e: Option[Int] (reduced from) { FoundReqVsImplicit.vecInt.Head } =:= Option[String] Cannot prove that FoundReqVsImplicit.vecInt.Head =:= Option[String]. implicitly[vecInt.Head =:= Option[String]] ^ newSource1.scala:10: error: type mismatch; Option[String┃Int] ――(right side reduced from) FoundReqVsImplicit.vecInt.Head val x: vecInt.Head = ??? : Option[String] ^ ================================================ FILE: core/src/test/resources/splain/plugin/VTypeDetailSpec/__direct/check ================================================ newSource1.scala:15: error: type mismatch; Test.F[Test.a.type┃a.type] wrongf(new A)(new F[AA]) ^ newSource1.scala:16: error: implicit error; !I b: Test.F[a.type] wrongf(new A) ^ newSource1.scala:15: error: type mismatch; Test.F[ found : Test.a.type (with underlying type Test.A) ━━━━━━━━: required: a.type (with underlying type a.type) ] wrongf(new A)(new F[AA]) ^ newSource1.scala:16: error: implicit error; !I b: Test.F[a.type (with underlying type a.type)] wrongf(new A) ^ newSource1.scala:15: error: type mismatch; Test.F[ found : Test.a.type (with underlying type Test.A) ━━━━━━━━: required: a.type (with underlying type a.type) where val a: Test.A ] wrongf(new A)(new F[AA]) ^ newSource1.scala:16: error: implicit error; !I b: Test.F[a.type (with underlying type a.type) where val a: Test.A] wrongf(new A) ^ newSource1.scala:15: error: type mismatch; Test.F[ found : Test.a.type (with underlying type Test.A) ━━━━━━━━: required: a.type (with underlying type a.type) where val a: Test.A ――(left side reduced from) Test.AA ] wrongf(new A)(new F[AA]) ^ newSource1.scala:16: error: implicit error; !I b: Test.F[a.type (with underlying type a.type) where val a: Test.A] wrongf(new A) ^ newSource1.scala:15: error: type mismatch; Test.F[ found : Test.a.type (with underlying type Test.A) ――(defined at newSource1.scala:10:11) ━━━━━━━━: required: a.type (with underlying type a.type) where val a: Test.A ――(defined at newSource1.scala:13:18) ――(left side reduced from) Test.AA ] wrongf(new A)(new F[AA]) ^ newSource1.scala:16: error: implicit error; !I b: Test.F[ a.type (with underlying type a.type) where val a: Test.A ――(defined at newSource1.scala:13:18) ] ――(defined at newSource1.scala:8:13) wrongf(new A) ^ newSource1.scala:15: error: type mismatch; Test.F[ found : Test.a.type (with underlying type Test.A) ――(defined at newSource1.scala:10:11) ━━━━━━━━: required: a.type (with underlying type a.type) where val a: Test.A ――(defined at newSource1.scala:13:18) ――(left side reduced from) Test.AA (which expands to) Test.a.type ] wrongf(new A)(new F[AA]) ^ newSource1.scala:16: error: implicit error; !I b: Test.F[ a.type (with underlying type a.type) where val a: Test.A ――(defined at newSource1.scala:13:18) ] ――(defined at newSource1.scala:8:13) wrongf(new A) ^ newSource1.scala:5: error: implicit error; !I e: String :: Int :: Boolean implicitly[K] ^ newSource1.scala:7: error: type mismatch; String("abc")┃String :: Int :: Boolean def v: K = "abc" ^ newSource1.scala:5: error: implicit error; !I e: String :: Int :: Boolean (reduced from) { Test.K } implicitly[K] ^ newSource1.scala:7: error: type mismatch; String("abc")┃String :: Int :: Boolean (reduced from) { Test.K } ――(right side reduced from) Test.K def v: K = "abc" ^ ================================================ FILE: core/src/test/resources/splain/plugin/VTypeDiffsDetailSpec/__direct/check ================================================ newSource1.scala:6: error: type mismatch; Long┃Long else add2(x.head, y.head) :: add(x.tail, y.tail) ^ newSource1.scala:6: error: type mismatch; Long┃Long else add2(x.head, y.head) :: add(x.tail, y.tail) ^ newSource1.scala:16: error: implicit error; !I ev: Long =:= Long Cannot prove that Long =:= Long. add2(x.head) ^ newSource1.scala:27: error: implicit error; !I ev: Long <:< Long Cannot prove that Long <:< Long. add2(x.head) ^ newSource1.scala:6: error: type mismatch; Long(in method add)┃scala.Long else add2(x.head, y.head) :: add(x.tail, y.tail) ^ newSource1.scala:6: error: type mismatch; Long(in method add)┃scala.Long else add2(x.head, y.head) :: add(x.tail, y.tail) ^ newSource1.scala:16: error: implicit error; !I ev: Long(in method add) =:= scala.Long Cannot prove that Long =:= Long. add2(x.head) ^ newSource1.scala:27: error: implicit error; !I ev: Long(in method add) <:< scala.Long Cannot prove that Long <:< Long. add2(x.head) ^ newSource1.scala:6: error: type mismatch; Long(in method add)┃scala.Long ――(comparing ) found : Long(in method add) required: scala.Long else add2(x.head, y.head) :: add(x.tail, y.tail) ^ newSource1.scala:6: error: type mismatch; Long(in method add)┃scala.Long ――(comparing ) found : Long(in method add) required: scala.Long else add2(x.head, y.head) :: add(x.tail, y.tail) ^ newSource1.scala:16: error: implicit error; !I ev: Long(in method add) =:= scala.Long ――(comparing =:= ) found : Long(in method add) required: scala.Long Cannot prove that Long =:= Long. add2(x.head) ^ newSource1.scala:27: error: implicit error; !I ev: Long(in method add) <:< scala.Long ――(comparing <:< ) found : Long(in method add) required: scala.Long Cannot prove that Long <:< Long. add2(x.head) ^ newSource1.scala:27: error: implicit error; !I ev: Long(in method add) <:< scala.Long ――(comparing <:<) found : Long(in method add) required: scala.Long Cannot prove that Long <:< Long. add2(x.head) ^ ================================================ FILE: core/src/test/resources/splain/plugin/ZIOSpec/zlayer/code.scala ================================================ import zio._ object layers { trait Service1 trait Service2 trait Service3 trait Service4 val service1 = ZLayer.succeed(new Service1 {}) val service2 = ZLayer.succeed(new Service2 {}) val service3 = ZLayer.fromService((_: Service1) => new Service3 {}) val service4 = ZLayer.succeed(new Service4 {}) val services: ULayer[Has[Service1] with Has[Service2] with Has[Service3] with Has[Service4]] = service1 ++ service2 >+> service3 // ++ service4 } ================================================ FILE: core/src/test/resources/splain/plugin/ZIOSpec/zlayer/error ================================================ newSource1.scala:19: error: type mismatch; zio.ZLayer[ Any, Nothing, ┃zio.Has[layers.Service4] with zio.Has[layers.Service1] with zio.Has[layers.Service2] with zio.Has[layers.Service3] ] service1 ++ service2 >+> service3 // ++ service4 ^ ================================================ FILE: core/src/test/scala/splain/PlainPrettifier.scala ================================================ //package splain // //import org.scalactic.{Prettifier, PrettyPair} // //object PlainPrettifier extends Prettifier { // // override def apply(o: Any): String = Prettifier.basic.apply(o) // // override def apply(left: Any, right: Any): PrettyPair = { // PrettyPair(apply(left), apply(right), None) // } //} ================================================ FILE: core/src/test/scala/splain/ScalacticSpike.scala ================================================ //package splain // //import org.scalatest.Ignore // //@Ignore //class ScalacticSpike extends SpecBase { // // it("can diff") { // // val a1 = // """ // | case (s1: String, s2: String) => StringDiffer.difference(s1, s2, prettifier) // | case (s1: scala.collection.GenMap[Any, Any], s2: scala.collection.GenMap[Any, Any]) => GenMapDiffer.difference(s1, s2, prettifier) // | case (s1: scala.collection.GenSeq[_], s2: scala.collection.GenSeq[_]) => GenSeqDiffer.difference(s1, s2, prettifier) // |""".stripMargin // // val a2 = // """ // | case (s1: String, s2: String) => StringDiffer.difference(s1, s2, prettifier) // | case (s1: scala.collllection.GenMap[Any, Any], s2: scala.collection.GenMap[Any, Any]) => GenMapDiffer.difference(s1, s2, prettifier) // | case (s1: scala.collection.GenSeq[_], s2: scala.collection.GenSeq[_]) => GenSeqDiffer.difference(s1, s2, prettifier) // |""".stripMargin // // val vv = PlainPrettifier.apply(a1, a2) // // println(vv) // } //} ================================================ FILE: core/src/test/scala/splain/SpecBase.scala ================================================ package splain import org.scalatest.funspec.AnyFunSpec import org.slf4j.LoggerFactory import scala.util.Try trait SpecBase extends AnyFunSpec with TestHelpers { protected def _it: ItWord = it } object SpecBase { trait Direct extends SpecBase { // will use reflection to discover all type `() => String` method under this instance lazy val codeToName: Map[String, String] = { val allMethods = this.getClass.getMethods val methods = allMethods.filter { method => method.getParameterCount == 0 && method.getReturnType == classOf[String] } val methodSeq = methods.flatMap { method => Try { val code = method.invoke(this).asInstanceOf[String] code -> method.getName }.toOption }.toSeq Map(methodSeq: _*) } def getName(code: String, nameOverride: String, profile: Profile = Profile.default): String = { val baseName = if (nameOverride.nonEmpty) nameOverride else codeToName(code) val name = if (profile == Profile.default) baseName else s"$baseName - $profile" name } lazy val runner: DirectRunner = DirectRunner() def check( code: String, profile: Profile = Profile.default, nameOverride: String = "", numberOfErrors: Int = 1, verbose: Boolean = false ): Unit = { val name = getName(code, nameOverride, profile) val cc = DirectCase(code, profile) val from = runner.pointer.getAndAdd(numberOfErrors) val until = runner.pointer.get() val groundTruth = runner.groundTruths.slice(from, until).mkString("\n") _it(name) { val error = cc.compileWith.compileError() error must_== groundTruth if (verbose) LoggerFactory .getLogger(this.getClass) .info( "\n" + error ) } } def skip( code: String, profile: Profile = Profile.default, nameOverride: String = "", numberOfBlocks: Int = 1 ): Unit = { val name = getName(code, nameOverride, profile) runner.pointer.getAndAdd(numberOfBlocks) ignore(name) {} } } trait File extends SpecBase { def check( name: String, file: String = "", profile: Profile = Profile.default )( check: CheckFile ): Unit = { val _file = if (file.isEmpty) name else file val testName = Seq(name, file).filter(_.nonEmpty).mkString(" - ") _it(testName) { check(FileCase(_file, profile)) } } def skip( name: String, file: String = "", setting: Profile = Profile.default )( check: CheckFile ): Unit = { val _file = if (file.isEmpty) name else file val testName = Seq(name, file).filter(_.nonEmpty).mkString(" - ") ignore(testName) { check(FileCase(_file, setting)) } } } } ================================================ FILE: core/src/test/scala/splain/builtin/BasicSpec.scala ================================================ package splain.builtin import splain.SpecBase class BasicSpec extends SpecBase.Direct with BasicFixture { check(chain) describe("#121") { check(foundReq) check(longFoundReq) } check(longArg) describe("#34") { check(compoundDiff, numberOfErrors = 2) } describe("#111") { check(LongRefined) check(LongTuple) check(foundReqLongTuple) } check(foundReqSameSymbol) check(bounds) check(longAnnotationMessage) check(longInfix) check(deeplyNestedHole) check(auxType) check(refined1, numberOfErrors = 2) check(refined2, numberOfErrors = 2) check(refined3, numberOfErrors = 2) check(disambiguateQualified) check(bynameParam) check(tuple1) check(singleType) check(singleTypeInFunction) check(singleTypeWithFreeSymbol) check(parameterAnnotation) check(shorthandTypes, numberOfErrors = 4) } ================================================ FILE: core/src/test/scala/splain/builtin/BasicXSource3Spec.scala ================================================ package splain.builtin class BasicXSource3Spec extends BasicSpec { override lazy val suiteCanonicalName: String = classOf[BasicSpec].getCanonicalName override def defaultExtraSetting: String = "-Xsource:3" } ================================================ FILE: core/src/test/scala/splain/builtin/MaxRefinedSpec.scala ================================================ package splain.builtin import splain.SpecBase class MaxRefinedSpec extends SpecBase.Direct { def truncrefined: String = """ object TruncRefined { class C trait D type CAux[A] = C { type X = C; type Y = D } def f(arg1: CAux[D]) = ??? f(new D { type X = C; type Y = D }) } """ check(truncrefined) check(truncrefined, profile = "-Vimplicits-max-refined 5") } ================================================ FILE: core/src/test/scala/splain/builtin/VerboseTreeSpec.scala ================================================ package splain.builtin import splain.SpecBase class VerboseTreeSpec extends SpecBase.Direct { def verboseTree: String = """ object tpes { trait I1 trait I2 trait I3 trait I4 trait I5 trait I6 trait I7 trait I8 trait I9 } import tpes._ object Tree { implicit def i8(implicit p: I9): I8 = ??? implicit def i7(implicit p: I8): I7 = ??? implicit def i6a(implicit p: I7): I6 = ??? implicit def i6b(implicit p: I8): I6 = ??? implicit def i5(implicit p: I6): I5 = ??? implicit def i4(implicit p: I5): I4 = ??? implicit def i3a(implicit p: I4): I3 = ??? implicit def i3b(implicit p: I4): I3 = ??? implicit def i2(implicit p: I3): I2 = ??? implicit def i1a(implicit p: I2): I1 = ??? implicit def i1b(implicit p: I6): I1 = ??? implicitly[I1] } """ check(verboseTree, profile = "-Vimplicits-verbose-tree") check(verboseTree) } ================================================ FILE: core/src/test/scala/splain/plugin/ErrorsCompatSpec.scala ================================================ package splain.plugin import splain.SpecBase class ErrorsCompatSpec extends SpecBase.File { check("byname") { checkSuccess() } } ================================================ FILE: core/src/test/scala/splain/plugin/PluginSpec.scala ================================================ package splain.plugin import splain.SpecBase class PluginSpec extends SpecBase.File { override def basicSetting: String = super.basicSetting + " -Vimplicits-verbose-tree" override lazy val predefCode: String = """ |object types |{ | class ***[A, B] | class >:<[A, B] | class C | trait D |} |import types._ |""".stripMargin.trim check("implicit resolution chains", "chain") { checkError() } check("higher kind argument", "higherKindArg") { checkError() } check("bounds") { checkError() } check("lazy") { checkError() } // TODO: cleanup, breakinfix is gone // skip("linebreak long infix types", "break") { // checkErrorWithBreak() // } // check("deephole") { // checkError() // } // TODO: remove, already in TreeSpec // describe("tree printing") { // check("complete", file = "tree", extra = "-Vimplicits-verbose-tree") { // checkError() // } // // skip("compact", "tree", extra = "-Vimplicits-verbose-tree -P:splain:compact") { // checkError(Some("errorCompact")) // } // } // TODO: feature removed // skip("prefix stripping", "prefix", extra = "-P:splain:keepmodules:2") { // checkError() // } // TODO: feature removed // skip("regex-rewrite", extra = "-P:splain:rewrite:\\.Level;0/5") { // checkError() // } // TODO: remove, already checked in BasicSpec // check("refined type diff", "refined") { // checkError() // } // check("disambiguate types", "disambiguate") { // checkError() // } // TODO: remove, already in TruncRefinedSpec // check("truncate refined type", "truncrefined", extra = "-Vimplicits-max-refined 10") { // checkError() // } // TODO: remove, already checked in BasicSpec // check("byname higher order", "byname-higher") { // checkError() // } // TODO: remove, already checked in BasicSpec // check("tuple1") { // checkError() // } // TODO: remove, already checked in BasicSpec // describe("single types ") { // // check("single") { // checkError() // } // // check("in function", "single-fn") { // checkError() // } // // check("with free symbol", "single-free") { // checkError() // } // } check("not a member", file = "member") { checkError() } check("implicit annotation with control character(s)", "implicit-ctrl-char") { checkError() } } ================================================ FILE: core/src/test/scala/splain/plugin/ShapelessSpec.scala ================================================ package splain.plugin import splain.SpecBase class ShapelessSpec extends SpecBase.File { override def basicSetting: String = super.basicSetting + " -Vimplicits-verbose-tree" override lazy val predefCode: String = """ |object types |{ | class ***[A, B] | class >:<[A, B] | class C | trait D |} |import types._ |""".stripMargin.trim // in all error messages from toolbox, line number has to -8 to get the real line number check("shapeless Record", "record") { checkError() } check("witness value types", "witness-value") { checkError() } check("lazyImplicit") { checkError() } } ================================================ FILE: core/src/test/scala/splain/plugin/VImplicitDivergingSpec.scala ================================================ package splain.plugin import splain.SpecBase class VImplicitDivergingSpec extends SpecBase.File { override def defaultExtraSetting: String = "-Vimplicits-verbose-tree -P:splain:Vimplicits-diverging" check("self") { checkError() } check("circular") { checkError() } check( "... with max depth", "circular", profile = s"${Settings.defaultExtra} -P:splain:Vimplicits-diverging-max-depth:5" ) { checkError() } check("circular-recoverable") { checkSuccess() } check( ".... with max depth", "circular", profile = s"${Settings.defaultExtra} -P:splain:Vimplicits-diverging-max-depth:5" ) { checkError() } check("diverging") { checkError() } check("... without verbose-tree", "diverging-compact", profile = "-P:splain:Vimplicits-diverging") { checkError() } } ================================================ FILE: core/src/test/scala/splain/plugin/VImplicitDivergingXSource3Spec.scala ================================================ package splain.plugin class VImplicitDivergingXSource3Spec extends VImplicitDivergingSpec { override lazy val suiteCanonicalName: String = classOf[VImplicitDivergingSpec].getCanonicalName } ================================================ FILE: core/src/test/scala/splain/plugin/VTypeDetailPositionSpec.scala ================================================ package splain.plugin import splain.SpecBase class VTypeDetailPositionSpec extends SpecBase.Direct { final val diff = """ object Diff { class Example { type VV } val e1 = new Example { type VV <: Int } implicitly[e1.VV =:= String] val x: e1.VV = ??? : String } """ describe("#44") { check(diff, numberOfErrors = 2) check(diff, profile = "-P:splain:Vtype-detail:position", numberOfErrors = 2) } } ================================================ FILE: core/src/test/scala/splain/plugin/VTypeDetailReductionSpec.scala ================================================ package splain.plugin import splain.SpecBase class VTypeDetailReductionSpec extends SpecBase.Direct { final val foundReqVsImplicit = """ object FoundReqVsImplicit { trait Vec[+T] { type Head = Option[T] } val vecInt = new Vec[Int] {} implicitly[vecInt.Head =:= Option[String]] val x: vecInt.Head = ??? : Option[String] } """ describe("#101") { check(foundReqVsImplicit, numberOfErrors = 2) check(foundReqVsImplicit, profile = "-P:splain:Vtype-detail:reduction", numberOfErrors = 2) } } ================================================ FILE: core/src/test/scala/splain/plugin/VTypeDetailSpec.scala ================================================ package splain.plugin import splain.SpecBase class VTypeDetailSpec extends SpecBase.Direct { final val wrongContexts = """ object Test { class A { class B def b: B = new B } class F[T] val a = new A type AA = a.type def wrongf(a: A)(implicit b: (F[a.type])): Unit = {} wrongf(new A)(new F[AA]) wrongf(new A) } """ final val reduceToInfix = """ object Test { trait ::[A, B] type K = String :: Int :: Boolean implicitly[K] def v: K = "abc" } """ describe("#113") { check(wrongContexts, profile = "-P:splain:Vtype-detail:1", numberOfErrors = 2) check(wrongContexts, profile = "-P:splain:Vtype-detail:2", numberOfErrors = 2) check(wrongContexts, profile = "-P:splain:Vtype-detail:3", numberOfErrors = 2) check(wrongContexts, profile = "-P:splain:Vtype-detail:4", numberOfErrors = 2) check(wrongContexts, profile = "-P:splain:Vtype-detail:5", numberOfErrors = 2) check(wrongContexts, profile = "-P:splain:Vtype-detail:6", numberOfErrors = 2) } describe("#119") { check(reduceToInfix, profile = "-P:splain:Vtype-detail:3", numberOfErrors = 2) check(reduceToInfix, profile = "-P:splain:Vtype-detail:4", numberOfErrors = 2) } } ================================================ FILE: core/src/test/scala/splain/plugin/VTypeDiffsDetailSpec.scala ================================================ package splain.plugin import splain.SpecBase class VTypeDiffsDetailSpec extends SpecBase.Direct { final val diff = """ object Diff { def add2(x:Long,y:Long): Long = x + y def add[Long](x: List[Long], y: List[Long]): List[Long] = if (x.isEmpty || y.isEmpty) Nil else add2(x.head, y.head) :: add(x.tail, y.tail) } object DiffInEq { def add2[T](y: T)( implicit ev: T =:= Long ): Long = 1L + ev(y) def add[Long](x: List[Long]): List[Long] = { add2(x.head) } } object DiffInSubtype { def add2[T](y: T)( implicit ev: T <:< Long ): Long = 1L + ev(y) def add[Long](x: List[Long]): List[Long] = { add2(x.head) } } """ describe("#112") { check(diff, numberOfErrors = 4, profile = "-P:splain:Vtype-diffs-detail:1") check(diff, numberOfErrors = 4, profile = "-P:splain:Vtype-diffs-detail:2") check(diff, numberOfErrors = 4, profile = "-P:splain:Vtype-diffs-detail:4") } } ================================================ FILE: core/src/test/scala/splain/plugin/ZIOSpec.scala ================================================ package splain.plugin import splain.SpecBase class ZIOSpec extends SpecBase.File { check("zlayer") { // TODO: is it still incorrect? checkError() } } ================================================ FILE: core/src/test/scala/splain/test/TryCompileSpec.scala ================================================ package splain.test import splain.SpecBase object TryCompileSpec { final val successExample = { """ object Example """ } final val parsingErrorExample = { """ object Example { """ } class L type R final val typingErrorExample = """ import splain.test.TryCompileSpec._ object FoundReq { def f(r: R): Int = ??? f(new L) } """ case class S1(i: Int) object S1 { implicit def default: S1 = S1(0) } case class S2(i: Int) object S2Import { implicit def default: S2 = S2(1) } } class TryCompileSpec extends SpecBase { import TryCompileSpec._ lazy val useReflect = TryCompile.UseReflect("") lazy val useNSC = TryCompile.UseNSC("") lazy val static = TryCompile.Static() describe("success") { val groundTruth = { """ |Success | --- | |""".stripMargin } it(classOf[TryCompile.UseReflect].getName) { useReflect(successExample).toString must_== groundTruth } it(classOf[TryCompile.UseNSC].getName) { useNSC(successExample).toString must_== groundTruth } it(classOf[TryCompile.Static[_]].getName) { static(successExample).toString must_== groundTruth } } describe("parsingError") { val groundTruth = """ |ParsingError | --- |newSource1.scala:1: error: '}' expected but eof found. |object Example { | ^ |""".stripMargin it(classOf[TryCompile.UseReflect].getName) { useReflect(parsingErrorExample).toString must_== groundTruth } it(classOf[TryCompile.UseNSC].getName) { useNSC(parsingErrorExample).toString must_== groundTruth } it(classOf[TryCompile.Static[_]].getName) { static(parsingErrorExample).toString must_== groundTruth } } describe("typing error") { val groundTruth = """ |TypingError | --- |newSource1.scala:5: error: type mismatch; | found : splain.test.TryCompileSpec.L | required: splain.test.TryCompileSpec.R | f(new L) | ^ |""".stripMargin it(classOf[TryCompile.UseReflect].getName) { useReflect(typingErrorExample).toString must_== groundTruth } it(classOf[TryCompile.UseNSC].getName) { useNSC(typingErrorExample).toString must_== groundTruth } it(classOf[TryCompile.Static[_]].getName) { static(typingErrorExample).toString must_== groundTruth } it("... invoke implicitly") { object Scope extends static.FromCodeMixin { val trial: TryCompile = typingErrorExample } Scope.trial.toString must_== groundTruth } } describe("runtime implicit search") { it("default scope") { val result = useReflect( """ |implicitly[splain.test.TryCompileSpec.S1] |""".stripMargin ) result match { case v: TryCompile.Success#Evaluable => assert(v.issues.isEmpty) assert(v.get == S1(0)) case v => throw new AssertionError("Expected success\n" + v) } } it("imported scope") { val compiled = useReflect( """ |import splain.test.TryCompileSpec.S2Import._ | |implicitly[splain.test.TryCompileSpec.S2] |""".stripMargin ) compiled match { case v: TryCompile.Success#Evaluable => assert(v.issues.isEmpty) assert(v.get == S2(1)) case v => throw new AssertionError("Expected success\n" + v) } } } } ================================================ FILE: core/src/testFixtures/scala/splain/TestHelpers.scala ================================================ package splain import org.scalatest.exceptions.TestFailedException import org.scalatest.{Assertion, Suite} import org.slf4j.LoggerFactory import splain.test.{Issue, TryCompile} import java.nio.file.{FileSystems, Files, Path, Paths} import java.util.concurrent.atomic.AtomicInteger import scala.language.implicitConversions import scala.util.Try trait TestHelpers extends Suite { import TestHelpers._ protected def basicSetting: String = "-Vimplicits -Vtype-diffs" protected def defaultExtraSetting: String = "" object Settings { final lazy val basic = basicSetting final lazy val defaultExtra: String = defaultExtraSetting } lazy val suiteCanonicalName: String = this.getClass.getCanonicalName final protected lazy val resourceDir: String = suiteCanonicalName.split('.').mkString("/") def resourcePath(name: String, fname: String): Path = FileSystems.getDefault.getPath(resourceDir, name, fname) def fileContentString(name: String, fname: String): String = { val path = resourcePath(name, fname) val resource = ClassLoader.getSystemClassLoader.getResource(path.toString) require(resource != null, s"Cannot find resource: $path") val actualPath = Paths.get(resource.toURI) new String(Files.readAllBytes(actualPath)) } def groundTruth(name: String, fname: Option[String] = None): String = Try { fileContentString(name, fname.getOrElse("error")).stripLineEnd }.recover { _: Throwable => fileContentString(name, fname.getOrElse("check")).stripLineEnd }.get lazy val predefCode = "" protected lazy val effectivePredef: String = { val trimmed = predefCode.trim if (trimmed.isEmpty) { trimmed } else { trimmed + "\n" } } def getEngine(settings: String): TryCompile.Engine = { // TryCompile.UseReflect(settings) TryCompile.UseNSC(settings) } sealed trait Profile { def text: String } object Profile { case class Splain(extraSetting: String = Settings.defaultExtra) extends Profile { override lazy val text = s"$enableSplainPlugin $basicSetting $extraSetting" } case class BuiltIn(extraSetting: String = Settings.defaultExtra) extends Profile { override lazy val text = s"$basicSetting $extraSetting" } // use the compiler with option but no plugin case object Disabled extends Profile { override def text: String = "" } // just use the plain old compiler as-is implicit def fromString(v: String): Splain = Splain(v) lazy val default: Splain = Splain() } class TestCase(code: String, setting: Profile) { lazy val codeWithPredef: String = effectivePredef + code case class CompileWith(settings: String) { private lazy val engine = getEngine(settings) def compile(): TryCompile = engine(codeWithPredef) def compileError(): String = compile() match { case v: TryCompile.TypingError => v.Error.displayIssues case TryCompile.OtherFailure(ee) => throw new AssertionError(s"Cannot compile: $ee", ee) case ee @ _ => throw new AssertionError(s"Type error not detected: $ee") } } def compileSuccess(): Option[String] = compileWith.compile() match { case _: TryCompile.Success => None case v: TryCompile.Failure => Some(v.Error.displayIssues) } def checkSuccess(): Assertion = assert(compileSuccess().isEmpty) lazy val compileWith: CompileWith = CompileWith(setting.text) } implicit class SpecStringOps(self: String) { def stripSpaceAtEnd(v: String): String = { v.reverse.dropWhile { v => (v == ' ') || (v == '\r') }.reverse } def canonize(v: String): String = { v.split('\n').map(stripSpaceAtEnd).mkString("\n") } def must_==(groundTruth: String): Unit = { val left = canonize(self) val right = canonize(groundTruth) try { assert(left === right) } catch { case e: TestFailedException => // augmenting val result = s""" |expected: < |${right} |> but was: < |${left} |> """.stripMargin.trim val ee = e.modifyMessage { _ => Some(result) } throw ee } () } } case class FileCase(name: String, profile: Profile = Profile.default) extends TestCase(fileContentString(name, "code.scala"), profile) { def checkError(errorFile: Option[String] = None): Unit = { compileWith.compileError() must_== groundTruth(name, errorFile) } } type CheckFile = FileCase => Unit def checkError(errorFile: Option[String] = None): CheckFile = { cc => cc.compileWith.compileError() must_== groundTruth(cc.name, errorFile) } def checkErrorWithBreak(errorFile: Option[String] = None, length: Int = 20): CheckFile = { cc => val withBreak = cc.copy(profile = s"-Vimplicits-breakinfix $length") withBreak.checkError(errorFile) } def checkSuccess(): CheckFile = { cc => assert(cc.compileSuccess().isEmpty) () } case class DirectCase(code: String, setting: Profile = Profile.default) extends TestCase(code, setting) case class DirectRunner() { case class ParseGroundTruths( startsWith: String = Issue.defaultSrcName, fName: Option[String] = None ) { lazy val raw: String = { val gt = groundTruth("__direct", fName) gt } lazy val cases: Seq[String] = { val regex = s"(^|\n)$startsWith" val result = raw .split( regex ) .toSeq .filter(_.trim.nonEmpty) .map { line => (startsWith + line).trim } result } } object DefaultGroundTruths extends ParseGroundTruths() lazy val groundTruths: Seq[String] = DefaultGroundTruths.cases val pointer = new AtomicInteger(0) } } object TestHelpers { lazy val userDir: String = System.getProperty("user.dir").stripSuffix("/") val plugin: String = Option(System.getProperty("splain.jar")).getOrElse { val dir = FileSystems.getDefault.getPath(userDir + "/build/libs") val file = Files .list(dir) .toArray .map(v => v.asInstanceOf[Path]) .filter(v => v.toString.endsWith(".jar")) .sortBy(v => v.toString) .filterNot { v => Seq("-javadoc.jar", "-sources.jar", "-test-fixtures.jar") .map { suffix => v.toString.endsWith(suffix) } .exists(identity) } .head LoggerFactory .getLogger(this.getClass) .debug( s"Using plugin jar: ${file.toString}" ) file.toAbsolutePath.toString } lazy val enableSplainPlugin: String = { val rows = s""" |-Xplugin:$plugin |""".stripMargin.trim rows.split('\n').mkString(" ") } } ================================================ FILE: core/src/testFixtures/scala/splain/builtin/BasicFixture.scala ================================================ package splain.builtin trait BasicFixture { // from scalac tests START HERE final val chain = """ object ImplicitChain { trait I1 trait I2 trait I3 trait I4 trait II implicit def i1(implicit impPar7: I3): I1 = ??? implicit def i2a(implicit impPar8: I3): I2 = ??? implicit def i2b(implicit impPar8: I3): I2 = ??? implicit def i4(implicit impPar9: I2): I4 = ??? implicit def g(implicit impPar3: I1, impPar1: I4): II = ??? implicitly[II] } """ final val foundReq = """ object FoundReq { class L type R def f(r: R): Int = ??? f(new L) } """ final val longArg = """ object Long { class VeryLong[T] implicitly[VeryLong[ VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[String]]]]]]] ]] } """ final val longFoundReq = """ object Long { class VeryLong[T] class VeryLong2[T] type Found = VeryLong[ VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[String]]]]]]] ] type Req = VeryLong[ VeryLong[VeryLong2[VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[String]]]]]]] ] val str: Req = ??? : Found } """ final val compoundDiff = """ object Compound { trait T type F[A] = A def x[A](y: A): F[A with T] = y.asInstanceOf[A with T] def f[A](a: A with String): F[A] = a val y: F[Int with T] = x(x(1)) f(y) val z: Int with T = x(x(1)) f(z) } """ final val LongRefined = """ object Long { class VeryLong[T] implicitly[VeryLong[ VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[String]]]]]]] ] { type A = Int; type B = Int; type C = Int; type D = Int; type E = Int; type F = Int; type G = Int; type H = Int}] } """ final val LongTuple = """ object Long { class VeryLong[T] implicitly[ ( VeryLong[Int], VeryLong[ VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[Int]]]]]]] ] ) ] } """ final val foundReqLongTuple = """ object Long { class VeryLong[T] val x: ( VeryLong[Int], VeryLong[ VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[Int]]]]]]] ] ) = ??? val y: ( VeryLong[Int], VeryLong[ VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[VeryLong[String]]]]]]] ] ) = x } """ final val foundReqSameSymbol = """ object FoundReqSameSymbol { trait T { type TT } trait A { val t: T } trait B { val a: A final val t = a.t val t1: a.t.TT = ??? val t2: t.TT = t1 } } """ final val bounds = """ object Bounds { trait Base trait Arg trait F[A] implicit def g[A <: Base, B]: F[A] = ??? implicitly[F[Arg]] } """ final val longAnnotationMessage = """ object Long { def long(implicit ec: scala.concurrent.ExecutionContext): Unit = ??? long } """ final val longInfix = """ object InfixBreak { type ::::[A, B] trait VeryLongTypeName trait Short type T1 = VeryLongTypeName :::: VeryLongTypeName :::: VeryLongTypeName :::: VeryLongTypeName type T2 = T1 :::: (Short :::: Short) :::: T1 :::: T1 implicit def f(implicit impPar4: List[T2]): String = ??? implicitly[String] } """ final val deeplyNestedHole = """ object DeepHole { trait C1[F[_]] trait C2[F[_], G[_], A] trait C3[A, B] trait C4[A] type Id[A] = A type T1[X] = C3[List[String], X] type T2[Y] = C2[Id, C4, Y] type T3[Z] = C2[T1, T2, Z] implicitly[C1[T3]] } """ final val auxType = """ object Aux { trait C trait D trait F object F { type Aux[A, B] = F { type X = A; type Y = B } } implicit def f[A, B](implicit impPar10: C): F { type X = A; type Y = B } = ??? implicitly[F.Aux[C, D]] } """ final val refined1 = """ object Refined { trait A trait B trait C trait D trait E trait F def f(a: A with B with C { type Y = String; type X = String; type Z = String }): Unit = ??? val x: B with E with A with F { type X = Int; type Y = String } = ??? f(x) object Sub1 { trait A object Sub2 { trait B def f(a: A with B with C { type Y = String; type X = String; type Z = String }): Unit = ??? val x: B with E with A with F { type X = Int; type Y = String } = ??? f(x) } } } """ final val refined2 = """ object Refined { trait Node { type T } type NodeLt[T0] = Node {type T <: T0} implicitly[NodeLt[Int]] val k: NodeLt[Int] = 1 } """ final val refined3 = """ object Refined { trait Node { type T } type NodeAux[T0] = Node { type T = T0 } type NodeLt[T0] = NodeAux[_ <: T0] implicitly[NodeLt[Int]] val k: NodeLt[Int] = 1 } """ final val disambiguateQualified = """ object A { object B { object X { object Y { type T } } } object C { object X { object Y { type T } } } def f(a: B.X.Y.T): Unit = () val x: C.X.Y.T = ??? f(x: C.X.Y.T) } """ final val bynameParam = """ object Foo { type A type B def f(g: (=> A) => B): Unit = () f(1: Int) } """ final val tuple1 = """ object Tup1 { val a: Tuple1[String] = "Tuple1": String } """ final val singleType = """ object SingleImp { class ***[A, B] val a = 1 val b = 2 implicitly[a.type *** b.type] } """ final val singleTypeInFunction = """ object SingleImp { class ***[A, B] def fn(): Unit = { val a = 1 val b = 2 implicitly[a.type *** b.type] } } """ final val singleTypeWithFreeSymbol = """ object SingleImp { class ***[A, B] def fn[A, B](a: A, b: B) = { implicitly[a.type *** b.type] } } """ final val parameterAnnotation = """ import scala.collection.mutable object Test { val o = new Object val ms = scala.collection.mutable.SortedSet(1,2,3) ms.map(_ => o) } """ // from scalac tests END HERE final val shorthandTypes = """ object a { type TypeA object b { type TypeB object c { type TypeC object d { type TypeD implicitly[List[TypeA]] implicitly[Seq[TypeB]] implicitly[Traversable[TypeC]] implicitly[Iterator[TypeD]] } } } } """ } ================================================ FILE: dev/.CI.sh ================================================ #!/usr/bin/env bash CRDIR="$( cd "$(dirname "$0")" || exit pwd )" echo "[COMPILING]" && \ "${CRDIR}"/test.sh "${@}" ================================================ FILE: dev/CI-latest.sh ================================================ #!/usr/bin/env bash CRDIR="$( cd "$(dirname "$0")" || exit pwd )" ARGS=${@} exec "${CRDIR}"/.CI.sh ${ARGS} ================================================ FILE: dev/format-code.sh ================================================ #!/usr/bin/env bash FWDIR="$(cd "`dirname "$0"`"/..; pwd)" cd "${FWDIR}" || exit "${FWDIR}"/gradlew clean scalafix -Dorg.gradle.parallel=false # consumes too much memory to run in parallel scalafmt ================================================ FILE: dev/gradle-versions.sh ================================================ #!/usr/bin/env bash FWDIR="$( cd "$(dirname "$0")"/.. || exit pwd )" ${FWDIR}/gradlew wrapper --gradle-version=8.14.3 ${FWDIR}/gradlew dependencyUpdates "$@" ================================================ FILE: dev/log-make-all.sh ================================================ #!/usr/bin/env bash FWDIR="$( cd "$(dirname "$0")"/.. || exit pwd )" CRDIR="$( cd "$(dirname "$0")" || exit pwd )" DATE=$(date --iso-8601=second) mkdir -p ${FWDIR}/logs/compile ${CRDIR}/make-all.sh --info > ${FWDIR}/logs/compile/"$DATE".log ================================================ FILE: dev/make-all.sh ================================================ #!/usr/bin/env bash FWDIR="$( cd "$(dirname "$0")"/.. || exit pwd )" DATE=$(date --iso-8601=second) mkdir -p ${FWDIR}/logs mkdir -p ${FWDIR}/logs/dependencyTree ${FWDIR}/gradlew -q dependencyTree "${@}" > ${FWDIR}/logs/dependencyTree/"$DATE".log ${FWDIR}/gradlew testClasses assemble "${@}" ================================================ FILE: dev/publish-all.sh ================================================ #!/usr/bin/env bash CRDIR="$( cd "$(dirname "$0")" || exit pwd )" for i in $(seq 9 18); do echo " [PUBLISHING] -PscalaVersion=2.13.$i" $CRDIR/publish.sh -PscalaVersion=2.13.$i done ================================================ FILE: dev/publish.template.sh ================================================ #!/usr/bin/env bash FWDIR="$( cd "$(dirname "$0")"/.. || exit pwd )" ${FWDIR}/gradlew publishToSonatype \ closeSonatypeStagingRepository \ -PsonatypeApiUser=??? \ -PsonatypeApiKey=??? \ -Psigning.gnupg.secretKey=??? \ -Psigning.gnupg.passphrase=??? \ "${@}" ================================================ FILE: dev/publishM2-all.sh ================================================ #!/usr/bin/env bash CRDIR="$( cd "$(dirname "$0")" || exit pwd )" for i in $(seq 9 18); do echo " [PUBLISHING] -PscalaVersion=2.13.$i" $CRDIR/publishM2.sh -PscalaVersion=2.13.$i done ================================================ FILE: dev/publishM2.sh ================================================ #!/usr/bin/env bash CRDIR="$( cd "$(dirname "$0")" || exit pwd )" echo "[COMPILING]" && \ "${CRDIR}"/../gradlew clean && \ "${CRDIR}"/make-all.sh publishToMavenLocal "${@}" ================================================ FILE: dev/test.sh ================================================ #!/usr/bin/env bash FWDIR="$( cd "$(dirname "$0")"/.. || exit pwd )" CRDIR="$( cd "$(dirname "$0")" || exit pwd )" "${CRDIR}"/make-all.sh "${@}" && \ ${FWDIR}/gradlew test "${@}" ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ scalaGroup=org.scala-lang scalaVersion=2.13.18 org.gradle.parallel=true #org.gradle.caching=true ================================================ FILE: gradlew ================================================ #!/bin/sh # # Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # SPDX-License-Identifier: Apache-2.0 # ############################################################################## # # Gradle start up script for POSIX generated by Gradle. # # Important for running: # # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole # command line, like: # # ksh Gradle # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: # * functions; # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; # * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # # (2) This script targets any POSIX shell, so it avoids extensions provided # by Bash, Ksh, etc; in particular arrays are avoided. # # The "traditional" practice of packing multiple parameters into a # space-separated string is a well documented source of bugs and security # problems, so this is (mostly) avoided, by progressively accumulating # options in "$@", and eventually passing that to Java. # # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; # see the in-line comments for details. # # There are tweaks for specific operating systems such as AIX, CygWin, # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. # ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link app_path=$0 # Need this for daisy-chained symlinks. while APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path [ -h "$app_path" ] do ls=$( ls -ld "$app_path" ) link=${ls#*' -> '} case $link in #( /*) app_path=$link ;; #( *) app_path=$APP_HOME$link ;; esac done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum warn () { echo "$*" } >&2 die () { echo echo "$*" echo exit 1 } >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "$( uname )" in #( CYGWIN* ) cygwin=true ;; #( Darwin* ) darwin=true ;; #( MSYS* | MINGW* ) msys=true ;; #( NONSTOP* ) nonstop=true ;; esac CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD=$JAVA_HOME/jre/sh/java else JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD=java if ! command -v java >/dev/null 2>&1 then die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi # Collect all arguments for the java command, stacking in reverse order: # * args from the command line # * the main class name # * -classpath # * -D...appname settings # * --module-path (only if needed) # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) # Now convert the arguments - kludge to limit ourselves to /bin/sh for arg do if case $arg in #( -*) false ;; # don't mess with options #( /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath [ -e "$t" ] ;; #( *) false ;; esac then arg=$( cygpath --path --ignore --mixed "$arg" ) fi # Roll the args list around exactly as many times as the number of # args, so each arg winds up back in the position where it started, but # possibly modified. # # NB: a `for` loop captures its iteration list before it begins, so # changing the positional parameters here affects neither the number of # iterations, nor the values presented in `arg`. shift # remove old arg set -- "$@" "$arg" # push replacement arg done fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. if ! command -v xargs >/dev/null 2>&1 then die "xargs is not available" fi # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. # # In Bash we could simply go: # # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # # but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap # the whole thing up as a single "set" statement. # # This will of course break if any of these variables contains a newline or # an unmatched quote. # eval "set -- $( printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | xargs -n1 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | tr '\n' ' ' )" '"$@"' exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @rem @rem Copyright 2015 the original author or authors. @rem @rem Licensed under the Apache License, Version 2.0 (the "License"); @rem you may not use this file except in compliance with the License. @rem You may obtain a copy of the License at @rem @rem https://www.apache.org/licenses/LICENSE-2.0 @rem @rem Unless required by applicable law or agreed to in writing, software @rem distributed under the License is distributed on an "AS IS" BASIS, @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem @rem SPDX-License-Identifier: Apache-2.0 @rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Resolve any "." and ".." in APP_HOME to make it shorter. for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute echo. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute echo. 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 goto fail :execute @rem Setup the command line set CLASSPATH= @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! set EXIT_CODE=%ERRORLEVEL% if %EXIT_CODE% equ 0 set EXIT_CODE=1 if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: img/VtypeDetail.md ================================================ XXX.scala:15: error: type mismatch; - Test.F\[ - Test.a.type ()|a.type (with underlying type a.type) () - - Test.AA - \] ================================================ FILE: img/VtypeDiffsDetail.md ================================================ XXX.scala:16: error: implicit error; - !I ev: - Long() =:= scala.Long - ――() - found : Long(in method add) - required: scala.Long - Cannot prove that Long =:= Long. ================================================ FILE: settings.gradle.kts ================================================ //val versions = gradle.rootProject.versions() include( ":core", ":testing:acceptance" ) pluginManagement.repositories { gradlePluginPortal() mavenCentral() // maven("https://dl.bintray.com/kotlin/kotlin-dev") } ================================================ FILE: testing/acceptance/build.gradle.kts ================================================ val vs: Versions = versions() dependencies { testImplementation(project(":core")) testFixturesApi(testFixtures(project(":core"))) scalaCompilerPlugins(project(":core")) // testImplementation("com.chuusai:shapeless_${vs.scalaBinaryV}:2.3.7") // testImplementation("dev.zio:zio_${vs.scalaBinaryV}:1.0.4") } tasks { withType { scalaCompileOptions.apply { additionalParameters!!.addAll( listOf( "-Vimplicits", "-Vtype-diffs", "-P:splain:Vimplicits-diverging" ) ) // println("===== SCALAC ARGS ======") // additionalParameters!!.forEach { // v -> // println(v) // } } } } ================================================ FILE: testing/acceptance/src/test/resources/splain/builtin/BasicSpec/__direct/check ================================================ newSource1.scala:13: error: implicit error; !I e: splain.acceptance.builtin.StaticBasicSpec.ImplicitChain.II ImplicitChain.g invalid because !I impPar3: ImplicitChain.I1 ――ImplicitChain.i1 invalid because !I impPar7: ImplicitChain.I3 implicitly[II] ^ newSource1.scala:6: error: type mismatch; found : splain.acceptance.builtin.StaticBasicSpec.FoundReq.L ━━━━━━━━: required: splain.acceptance.builtin.StaticBasicSpec.FoundReq.R f(new L) ^ newSource1.scala:13: error: type mismatch; splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ found : splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[String] ] ] ] ] ] ━━━━━━━━: required: splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong2[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[String] ] ] ] ] ] ] ] val str: Req = ??? : Found ^ newSource1.scala:4: error: implicit error; !I e: splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[String] ] ] ] ] ] ] ] implicitly[VeryLong[ ^ newSource1.scala:7: error: type mismatch; splain.acceptance.builtin.StaticBasicSpec.Compound.y.type┃String f(y) ^ newSource1.scala:4: error: implicit error; !I e: splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[String] ] ] ] ] ] ] ] { type A = Int; type B = Int; type C = Int; type D = Int; type E = Int; type F = Int; type G = Int; type H = Int } implicitly[VeryLong[ ^ newSource1.scala:4: error: implicit error; !I e: ( splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[Int], splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[Int] ] ] ] ] ] ] ] ) implicitly[ ^ newSource1.scala:16: error: type mismatch; ( splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[Int], splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[ splain.acceptance.builtin.StaticBasicSpec.Long.VeryLong[Int┃String] ] ] ] ] ] ] ] ) ) = x ^ newSource1.scala:11: error: type mismatch; B.this.t1.type┃B.this.t.TT val t2: t.TT = t1 ^ newSource1.scala:7: error: implicit error; !I e: splain.acceptance.builtin.StaticBasicSpec.Bounds.F[ splain.acceptance.builtin.StaticBasicSpec.Bounds.Arg ] Bounds.g invalid because nonconformant bounds; [splain.acceptance.builtin.StaticBasicSpec.Bounds.Arg, Nothing] [A <: Bounds.Base, B] implicitly[F[Arg]] ^ newSource1.scala:4: error: implicit error; !I ec: scala.concurrent.ExecutionContext Cannot find an implicit ExecutionContext. You might add an (implicit ec: ExecutionContext) parameter to your method. The ExecutionContext is used to configure how and on which thread pools asynchronous tasks (such as Futures) will run, so the specific ExecutionContext that is selected is important. If your application does not define an ExecutionContext elsewhere, consider using Scala's global ExecutionContext by defining the following: implicit val ec: scala.concurrent.ExecutionContext = scala.concurrent.ExecutionContext.global long ^ newSource1.scala:10: error: implicit error; !I e: String f invalid because !I impPar4: List[ ( splain.acceptance.builtin.StaticBasicSpec.InfixBreak.VeryLongTypeName :::: splain.acceptance.builtin.StaticBasicSpec.InfixBreak.VeryLongTypeName :::: splain.acceptance.builtin.StaticBasicSpec.InfixBreak.VeryLongTypeName :::: splain.acceptance.builtin.StaticBasicSpec.InfixBreak.VeryLongTypeName ) :::: ( splain.acceptance.builtin.StaticBasicSpec.InfixBreak.Short :::: splain.acceptance.builtin.StaticBasicSpec.InfixBreak.Short ) :::: ( splain.acceptance.builtin.StaticBasicSpec.InfixBreak.VeryLongTypeName :::: splain.acceptance.builtin.StaticBasicSpec.InfixBreak.VeryLongTypeName :::: splain.acceptance.builtin.StaticBasicSpec.InfixBreak.VeryLongTypeName :::: splain.acceptance.builtin.StaticBasicSpec.InfixBreak.VeryLongTypeName ) :::: splain.acceptance.builtin.StaticBasicSpec.InfixBreak.VeryLongTypeName :::: splain.acceptance.builtin.StaticBasicSpec.InfixBreak.VeryLongTypeName :::: splain.acceptance.builtin.StaticBasicSpec.InfixBreak.VeryLongTypeName :::: splain.acceptance.builtin.StaticBasicSpec.InfixBreak.VeryLongTypeName ] (No implicit view available from Int => splain.acceptance.builtin.StaticBasicSpec.InfixBreak.T2.) implicitly[String] ^ newSource1.scala:11: error: implicit error; !I e: splain.acceptance.builtin.StaticBasicSpec.DeepHole.C1[ [Z]splain.acceptance.builtin.StaticBasicSpec.DeepHole.C2[splain.acceptance.builtin.StaticBasicSpec.DeepHole.T1,splain.acceptance.builtin.StaticBasicSpec.DeepHole.T2,Z] ] implicitly[C1[T3]] ^ newSource1.scala:9: error: implicit error; !I e: splain.acceptance.builtin.StaticBasicSpec.Aux.F.Aux[ splain.acceptance.builtin.StaticBasicSpec.Aux.C, splain.acceptance.builtin.StaticBasicSpec.Aux.D ] Aux.f invalid because !I impPar10: Aux.C implicitly[F.Aux[C, D]] ^ newSource1.scala:11: error: type mismatch; splain.acceptance.builtin.StaticBasicSpec.Refined.A with splain.acceptance.builtin.StaticBasicSpec.Refined.B with found : splain.acceptance.builtin.StaticBasicSpec.Refined.E ━━━━━━━━: required: splain.acceptance.builtin.StaticBasicSpec.Refined.C with splain.acceptance.builtin.StaticBasicSpec.Refined.F┃ { type X = Int┃String; type Y = String; type Z = ┃String } f(x) ^ newSource1.scala:9: error: implicit error; !I e: splain.acceptance.builtin.StaticBasicSpec.Refined.Node {type T <: Int} implicitly[NodeLt[Int]] ^ newSource1.scala:10: error: implicit error; !I e: splain.acceptance.builtin.StaticBasicSpec.Refined.Node{type T = _$1} forSome { type _$1 <: Int } implicitly[NodeLt[Int]] ^ newSource1.scala:25: error: type mismatch; C.X.Y.T┃B.X.Y.T f(x: C.X.Y.T) ^ newSource1.scala:6: error: type mismatch; found : Int ━━━━━━━━: required: (=> splain.acceptance.builtin.StaticBasicSpec.Foo.A) => splain.acceptance.builtin.StaticBasicSpec.Foo.B f(1: Int) ^ newSource1.scala:3: error: type mismatch; String┃Tuple1[String] val a: Tuple1[String] = "Tuple1": String ^ newSource1.scala:7: error: implicit error; !I e: splain.acceptance.builtin.StaticBasicSpec.SingleImp.a.type *** splain.acceptance.builtin.StaticBasicSpec.SingleImp.b.type implicitly[a.type *** b.type] ^ newSource1.scala:8: error: implicit error; !I e: a.type *** b.type implicitly[a.type *** b.type] ^ newSource1.scala:6: error: implicit error; !I e: a.type *** b.type implicitly[a.type *** b.type] ^ newSource1.scala:5: error: implicit error; !I ev: scala.math.Ordering[Object] No implicit Ordering[Object] found to build a SortedSet[Object]. You may want to upcast to a Set[Int] first by calling `unsorted`. Ordering.ordered invalid because !I asComparable: Object => Comparable[Any] No implicit view available from Object => Comparable[_ >: Object]. Ordering.comparatorToOrdering invalid because !I cmp: java.util.Comparator[Object] ms.map(_ => o) ^ newSource1.scala:9: error: implicit error; !I e: List[splain.acceptance.builtin.StaticBasicSpec.a.TypeA] (No implicit view available from Int => splain.acceptance.builtin.StaticBasicSpec.a.TypeA.) implicitly[List[TypeA]] ^ ================================================ FILE: testing/acceptance/src/test/scala/splain/acceptance/Acceptance.scala ================================================ package splain.acceptance import org.scalatest.funspec.AnyFunSpec import splain.TestHelpers import splain.test.TryCompile import scala.collection.mutable.ArrayBuffer object Acceptance { final val static = TryCompile.Static() trait SpecBase extends AnyFunSpec with TestHelpers with static.FromCodeMixin { lazy val buffer: ArrayBuffer[TryCompile] = ArrayBuffer.empty def check(v: TryCompile, numberOfErrors: Int = 1): Unit = { buffer += v } it("acceptance") { val issues = buffer.flatMap(v => v.issues).mkString("\n") val runner = DirectRunner() val gt = runner.DefaultGroundTruths.raw issues must_== gt } } } ================================================ FILE: testing/acceptance/src/test/scala/splain/acceptance/builtin/StaticBasicSpec.scala ================================================ package splain.acceptance.builtin import splain.acceptance.Acceptance import splain.builtin.BasicFixture object StaticBasicSpec { object Delegate extends BasicFixture } class StaticBasicSpec extends Acceptance.SpecBase { override lazy val suiteCanonicalName: String = "splain.builtin.BasicSpec" import StaticBasicSpec.Delegate._ check(chain) describe("#121") { check(foundReq) check(longFoundReq) } check(longArg) describe("#34") { check(compoundDiff, numberOfErrors = 2) } describe("#111") { check(LongRefined) check(LongTuple) check(foundReqLongTuple) } check(foundReqSameSymbol) check(bounds) check(longAnnotationMessage) check(longInfix) check(deeplyNestedHole) check(auxType) check(refined1, numberOfErrors = 2) check(refined2, numberOfErrors = 2) check(refined3, numberOfErrors = 2) check(disambiguateQualified) check(bynameParam) check(tuple1) check(singleType) check(singleTypeInFunction) check(singleTypeWithFreeSymbol) check(parameterAnnotation) check(shorthandTypes, numberOfErrors = 4) }