Coder Social home page Coder Social logo

splain's Introduction

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 and this), 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 badge
v1.1.0-RC0
(current)
CI badge
v1.0.3
(current)
CI badge
v1.0.2
(current)
CI badge
v1.0.1
(current)
CI badge
v1.0.0
(current)
CI badge
v1.0.0-RC2
(current)
CI badge
v1.0.0-RC1
(current)
CI badge
v0.x
(maintenance) - latest
CI badge

Usage

v1.x, v0.x

Include this line in your build.sbt (not project/plugins.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:

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:

scalaCompilerPlugins group: 'io.tryp', name: 'splain_${scalaVersion}', version: '0.5.8'

or build.gradle.kts:

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>[:<value>] -<param>[:<value>]

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)

scalacOptions += "-P:splain:implicits:false"

(in gradle with scala plugin)

withType<ScalaCompile> {
    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

special consideration for shapeless.Record:

foundreq_record

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 <none> 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

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

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)

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 <found>|<required> 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)

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
<project root>/src/test/scala/splain <project root>/core/src/test/scala/splain/plugin

The bug can thus be identified by the team quickly on our continuous integration environment. 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, 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. 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 standard.

Communication

  • @tek - reviewer for built-in/v0.x bugfix, new features
  • @tribbloid - reviewer for v1.x bugfix
  • @dwijnand - reviewer for scala compiler integration

splain's People

Contributors

4e6 avatar danilbykov avatar dmytromitin avatar jleider avatar joprice avatar jtjeferreira avatar mrdziuban avatar mszczygiel avatar nathankleyn avatar sethtisue avatar tek avatar tribbloid avatar xplosunn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

splain's Issues

Diverging Implicit error in compact tree mode (without specifying `Vimplicits-verbose-tree`) reports some errors as "unlinked", while in reality they are just compacted

In the following unit test:

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]
  }
}

If we invoke splain without the Vimplicits-verbose-tree option, it will throw the following error:

newSource1.scala:16: error: implicit error;
!I e: DivergingImplicits.C :: DivergingImplicits.D
Diverging.f invalid because
!I ii: scala.Int :: DivergingImplicits.C :: DivergingImplicits.D
⋮
――Diverging.f invalid because
  !I ii:
    scala.Int ::
    scala.Int ::
    scala.Int ::
    DivergingImplicits.C ::
    DivergingImplicits.D
  diverging implicit expansion for type (scala.Int :: scala.Int :: scala.Int :: DivergingImplicits.C :: DivergingImplicits.D)
  starting with method g in object Diverging
[WARNING] The following reported error(s) cannot be linked to any part of the implicit search tree:
――diverging implicit expansion for type (scala.Int :: scala.Int :: DivergingImplicits.C :: DivergingImplicits.D)
starting with method f in object Diverging
    implicitly[C :: D]
              ^

The last part, starting from [WARNING] should totally be removed, as this is the error omitted in the ⋮ section.

Switch to gradle for 1.0.0-2.13.6 release

it appears that the latest sbt 1.5.5 is still using scala 2.12.14. Due to its design flaw it will always cause a classpath contamination issue:

https://stackoverflow.com/questions/27886370/how-to-have-sbt-plugin-exclude-its-dependency

There is no hope to fix it. Since 1.0.0 is targeting scala 2.13.6+, I don't see a point for keep using it in production. We should switch to gradle which is much more stable.

I'll also seek to swap out specs2 with the more stable scalatest if necessary.

Please let me know what you think.

Can it be used in maven or gradle?

If they are possible, should these examples be included in the documentation?

I intend to use it in a gradle project, please let me know if I can help

case class fields with default values break when compiling with 2.12.3

case class Broken(field: Int = 1)

breaks while:

case class Ok(field: Int)

is fine. Both compile fine with 2.12.2 and below.

The error is:

Error:scalac: Error: Method splain/SplainPlugin$$anon$1.CaseApplyDefaultGetters()Lscala/tools/nsc/typechecker/NamesDefaults$CaseApplyDefaultGetters$; is abstract
java.lang.AbstractMethodError: Method splain/SplainPlugin$$anon$1.CaseApplyDefaultGetters()Lscala/tools/nsc/typechecker/NamesDefaults$CaseApplyDefaultGetters$; is abstract
	at splain.SplainPlugin$$anon$1.CaseApplyDefaultGetters(compat.scala)
	at scala.tools.nsc.typechecker.Namers$Namer.$anonfun$addDefaultGetters$17(Namers.scala:1556)
	at scala.tools.nsc.typechecker.Namers$Namer.$anonfun$addDefaultGetters$7(Namers.scala:1555)
	at scala.tools.nsc.typechecker.Namers$Namer.$anonfun$addDefaultGetters$5(Namers.scala:1465)
	at scala.tools.nsc.typechecker.Namers$Namer.addDefaultGetters(Namers.scala:1461)
	at scala.tools.nsc.typechecker.Namers$Namer.methodSig(Namers.scala:1382)
	at scala.tools.nsc.typechecker.Namers$Namer.memberSig(Namers.scala:1785)
	at scala.tools.nsc.typechecker.Namers$Namer.typeSig(Namers.scala:1751)
	at scala.tools.nsc.typechecker.Namers$Namer$MonoTypeCompleter.completeImpl(Namers.scala:836)
	at scala.tools.nsc.typechecker.Namers$LockingTypeCompleter.complete(Namers.scala:1948)
	at scala.tools.nsc.typechecker.Namers$LockingTypeCompleter.complete$(Namers.scala:1946)
	at scala.tools.nsc.typechecker.Namers$TypeCompleterBase.complete(Namers.scala:1941)
	at scala.tools.nsc.typechecker.Namers$CompleterWrapper.complete(Namers.scala:1997)
	at scala.tools.nsc.typechecker.Namers$Namer$$anon$4.complete(Namers.scala:614)
	at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1531)
	at scala.reflect.internal.Symbols$Symbol.initialize(Symbols.scala:1679)
	at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$6(Typers.scala:3172)
	at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$6$adapted(Typers.scala:3170)
	at scala.Option$WithFilter.foreach(Option.scala:257)
	at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$4(Typers.scala:3170)
	at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$4$adapted(Typers.scala:3168)
	at scala.reflect.internal.Scopes$Scope.foreach(Scopes.scala:396)
	at scala.tools.nsc.typechecker.Typers$Typer.addSynthetics$1(Typers.scala:3168)
	at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3230)
	at scala.tools.nsc.typechecker.Typers$Typer.typedTemplate(Typers.scala:1983)
	at scala.tools.nsc.typechecker.Typers$Typer.typedModuleDef(Typers.scala:1854)
	at scala.tools.nsc.typechecker.Typers$Typer.typedMemberDef$1(Typers.scala:5503)
	at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5552)
	at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5589)
	at scala.tools.nsc.typechecker.Typers$Typer.typedInternal(Typers.scala:5619)
	at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5563)
	at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5567)
	at scala.tools.nsc.typechecker.Typers$Typer.typedByValueExpr(Typers.scala:5650)
	at scala.tools.nsc.typechecker.Typers$Typer.typedStat$1(Typers.scala:3075)
	at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$6(Typers.scala:3173)
	at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$6$adapted(Typers.scala:3170)
	at scala.Option$WithFilter.foreach(Option.scala:257)
	at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$4(Typers.scala:3170)
	at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$4$adapted(Typers.scala:3168)
	at scala.reflect.internal.Scopes$Scope.foreach(Scopes.scala:396)
	at scala.tools.nsc.typechecker.Typers$Typer.addSynthetics$1(Typers.scala:3168)
	at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3230)
	at scala.tools.nsc.typechecker.Typers$Typer.typedPackageDef$1(Typers.scala:5202)
	at scala.tools.nsc.typechecker.Typers$Typer.typedMemberDef$1(Typers.scala:5505)
	at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5552)
	at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5589)
	at scala.tools.nsc.typechecker.Typers$Typer.typedInternal(Typers.scala:5619)
	at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5563)
	at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5567)
	at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5646)
	at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.apply(Analyzer.scala:102)
	at scala.tools.nsc.Global$GlobalPhase.$anonfun$applyPhase$1(Global.scala:426)
	at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:419)
	at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.$anonfun$run$1(Analyzer.scala:94)
	at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.$anonfun$run$1$adapted(Analyzer.scala:93)
	at scala.collection.Iterator.foreach(Iterator.scala:929)
	at scala.collection.Iterator.foreach$(Iterator.scala:929)
	at scala.collection.AbstractIterator.foreach(Iterator.scala:1417)
	at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.run(Analyzer.scala:93)
	at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1431)
	at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1416)
	at scala.tools.nsc.Global$Run.compileSources(Global.scala:1412)
	at scala.tools.nsc.Global$Run.compile(Global.scala:1515)
	at xsbt.CachedCompiler0.run(CompilerInterface.scala:115)
	at xsbt.CachedCompiler0.run(CompilerInterface.scala:94)
	at xsbt.CompilerInterface.run(CompilerInterface.scala:22)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sbt.compiler.AnalyzingCompiler.call(AnalyzingCompiler.scala:101)
	at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:47)
	at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:41)
	at org.jetbrains.jps.incremental.scala.local.IdeaIncrementalCompiler.compile(IdeaIncrementalCompiler.scala:32)
	at org.jetbrains.jps.incremental.scala.local.LocalServer.compile(LocalServer.scala:26)
	at org.jetbrains.jps.incremental.scala.remote.Main$.make(Main.scala:68)
	at org.jetbrains.jps.incremental.scala.remote.Main$.nailMain(Main.scala:25)
	at org.jetbrains.jps.incremental.scala.remote.Main.nailMain(Main.scala)
	at sun.reflect.GeneratedMethodAccessor31.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.martiansoftware.nailgun.NGSession.run(NGSession.java:319)

nonconformant bound info sometimes print out full type signature instead of the formatted one, is it deliberate?

Here is an example when I'm debugging shapesafe:

implicit error;
!I prove:
  Proof[
    On[Literal[Int(1)], On[Literal[Int(2)], Literal[Int(4)]]]
    ,
    Aye[O]
  ]
  ¯\_(ツ)_/¯ 1 != 6
  
  ... when proving arity ░▒▓
  
  1 == 6
  
Op2.unchecked invalid because
!I domain: UncheckedDomain[Literal[Int(1)], On[Literal[Int(2)], Literal[Int(4)]]]
――UncheckedDomain.d2 invalid because
  !I bound1: Proof[Literal[Int(1)], Aye[_$1]]
    [NO PROOF]: org.shapesafe.core.arity.ConstArity.Literal[Int(1)]	 |- 	??? <: org.shapesafe.core.arity.Unchecked
    
――UncheckedDomain.d1 invalid because
  !I bound2: Proof[On[Literal[Int(2)], Literal[Int(4)]], Aye[_$1]]
    [NO PROOF]: org.shapesafe.core.arity.binary.Op2.Impl[[P1, P2]singleton.ops.impl.OpMacro[singleton.ops.impl.OpId.+,P1,P2,Int(0)],org.shapesafe.core.debugging.Expressions.+]#On[org.shapesafe.core.arity.ConstArity.Literal[Int(2)],org.shapesafe.core.arity.ConstArity.Literal[Int(4)]]	 |- 	??? <: org.shapesafe.core.arity.Unchecked
    
――――Op2.unchecked invalid because
    !I domain: UncheckedDomain[Literal[Int(2)], Literal[Int(4)]]
――――――UncheckedDomain.d2 invalid because
      !I bound1: Proof[Literal[Int(2)], Aye[_$1]]
        [NO PROOF]: org.shapesafe.core.arity.ConstArity.Literal[Int(2)]	 |- 	??? <: org.shapesafe.core.arity.Unchecked
        
――――――UncheckedDomain.d1 invalid because
      !I bound2: Proof[Literal[Int(4)], Aye[_$1]]
        [NO PROOF]: org.shapesafe.core.arity.ConstArity.Literal[Int(4)]	 |- 	??? <: org.shapesafe.core.arity.Unchecked
        
binary.this.Op2.invar[org.shapesafe.core.arity.ConstArity.Literal[Int(1)], org.shapesafe.core.arity.binary.Op2.Impl[[P1, P2]singleton.ops.impl.OpMacro[singleton.ops.impl.OpId.+,P1,P2,Int(0)],org.shapesafe.core.debugging.Expressions.+]#On[org.shapesafe.core.arity.ConstArity.Literal[Int(2)],org.shapesafe.core.arity.ConstArity.Literal[Int(4)]], Int(1), Int(6), org.shapesafe.core.arity.binary.Require2.Impl[[P1, P2]singleton.ops.impl.OpMacro[singleton.ops.impl.OpId.==,P1,P2,Int(0)],org.shapesafe.core.debugging.Expressions.==]](arity.this.VerifiedArity.endo[org.shapesafe.core.arity.ConstArity.Literal[Int(1)]], binary.this.Op2.invar[org.shapesafe.core.arity.ConstArity.Literal[Int(2)], org.shapesafe.core.arity.ConstArity.Literal[Int(4)], Int(2), Int(4), org.shapesafe.core.arity.binary.Op2.Impl[[P1, P2]singleton.ops.impl.OpMacro[singleton.ops.impl.OpId.+,P1,P2,Int(0)],org.shapesafe.core.debugging.Expressions.+]](arity.this.VerifiedArity.endo[org.shapesafe.core.arity.ConstArity.Literal[Int(2)]], arity.this.VerifiedArity.endo[org.shapesafe.core.arity.ConstArity.Literal[Int(4)]], {
  final class $anon extends AnyRef with singleton.ops.impl.OpMacro[singleton.ops.impl.OpId.+,Int(2),Int(4),Int(0)] {
    def <init>(): <$anon: singleton.ops.impl.OpMacro[singleton.ops.impl.OpId.+,Int(2),Int(4),Int(0)]> = {
      $anon.super.<init>();
      ()
    };
    type OutWide = Int;
    type Out = Int(6);
    type OutInt = Int(6);
    final private[this] val value: Int(6) = 6;
    final <stable> <accessor> def value: Int(6) = $anon.this.value;
    final private[this] val isLiteral: Boolean(true) = true;
    final <stable> <accessor> def isLiteral: Boolean(true) = $anon.this.isLiteral;
    final private[this] val valueWide: Int = 6;
    final <stable> <accessor> def valueWide: Int = $anon.this.valueWide
  };
  new $anon()
}), {
  final class $anon extends AnyRef with singleton.ops.impl.OpMacro[singleton.ops.impl.OpId.==,Int(1),Int(6),Int(0)] {
    def <init>(): <$anon: singleton.ops.impl.OpMacro[singleton.ops.impl.OpId.==,Int(1),Int(6),Int(0)]> = {
      $anon.super.<init>();
      ()
    };
    type OutWide = Boolean;
    type Out = Boolean(false);
    type OutBoolean = Boolean(false);
    final private[this] val value: Boolean(false) = false;
    final <stable> <accessor> def value: Boolean(false) = $anon.this.value;
    final private[this] val isLiteral: Boolean(true) = true;
    final <stable> <accessor> def isLiteral: Boolean(true) = $anon.this.isLiteral;
    final private[this] val valueWide: Boolean = false;
    final <stable> <accessor> def valueWide: Boolean = $anon.this.valueWide
  };
  new $anon()
}) invalid because
nonconformant bounds;
[Literal[Int(1)], On[Literal[Int(2)], Literal[Int(4)]], Int(1), Int(6), Impl[OpMacro[==, ?, ?, Int(0)], ==]]
[A1 <: org.shapesafe.core.arity.Arity, A2 <: org.shapesafe.core.arity.Arity, S1, S2, OP <: org.shapesafe.core.arity.binary.Op2]
...
  

If this goes through proper formatting, I shouldn't even see the "singleton.ops.impl." package part. Is there a reason they have to be printed? Or is it some kind of fall back mechanism when formatting fails?

@tek Thanks a lot for your input

diff of compound types

When using zio's env, it is common to have errors where the two types differ by a single member such as Logging with UserRepo with Clock with Random vs Logging with UserRepo with Clock. It would be helpful to have the differences between the two types highlighted as with other type parameters.

Splain doesn't respect -Ymacro-annotations compiler flag

When trying to build the project that uses macro annotations, compilation fails with:

[error] splain-macro-annotations/Test.scala:7:23: macro annotation could not be expanded (you cannot use a macro annotation in the same compilation run that defines it)
[error]   @newtype case class Foo(ix: Int)
[error]                       ^
[error] one error found

The error message is the same as if you forget to add -Ymacro-annotations compiler flag.

Example project, that reproduces the issue: 4e6/splain-macro-annotations

scalaVersion: 2.13.1
splainVersion: 0.5.0

Add tree/graph visualisation for divergent/circular implicit expansion

Implicit expansion in all versions of scala uses a complex graph search to find a path to convert one type to another, using all implicit functions in the scope as building blocks of bridges.

When such path cannot be found it will throw an error (e.g. if 2 bridges are both viable options and are equally preferrable, it will throw an ambiguous implicit error). Of all these errors, the "circular / divergent implicit expansion" is the most complex of them all.

  • Circular implicit expansion is encountered when a path leads to a type that has been visited before (a.k.a not making progress):

circular implicit

in both scala 2 and 3, the search algorithm will silently abandon the path and backtrack to the entry point of the circle

  • Divergent implicit expansion is encountered when a path leads to a different type, yet from the same type constructor, and has become more complex or inductive, in which case it poses a certain risk of visiting infinitely inductive type(s):

divergent impliciit

Scala 2 will fail immediately, scala 3 will fail silently and backtrack. Both shapeless Lazy and implicit-by-name feature was invented to suppress such behaviour of risk aversion, as a successful implicit conversion between inductive types may still happen afterwards

Unfortunately, such complexity is aggravated by the extremely brief default error message, usually only contains:

diverging implicit expansion for type xxx.XXX
starting with method yyy in trait ZZZ

The current splain 0.5.x did very little to improve it.

Since the error is caused by an anomaly in graph search, the error message should also contains a graph or at least its tree variant, indicating the incomplete path between the visited type when the error was encountered.

I have done some work in tree and graph visualisation for the type heyting algebra in scala, so after a compatibility patch for scala 2.13.6 I can start applying my work immediately

splain 0.4.1 on scala 2.13... breaks by-name implicits ?

Hello,

I'm somewhat confused, but I think I have a weird one here.

Apparently, using splain breaks the new in Scala 2.13 "by-name implicits", such as used for generic derivation in Typelevel Kittens (https://github.com/typelevel/kittens)

A short example such as:

import cats.Show
import cats.derived.semi
import cats.instances.string._

case class Inner( x: Option[Int] )

object Inner {
  implicit val innerShow: Show[Inner] =
    Show.show( _.x.fold( "*" )( _.toString ) )
}

case class Outer( name: String, inner: Inner )

object Outer {
  implicit val outerShow: Show[Outer] = semi.show[Outer]
}

compiles fine without splain but fails with splain. The failing output is:

[error] !I ev: MkShow[Outer]
[error] Lazy.instance invalid because
[error] !I ev: <byname>[MkShow[Outer]]
[error]     implicit val outerShow: Show[Outer] = semi.show[Outer]

which seems to point towards a by-name implicit in kittens (which should indeed occur somewhere in the expected derivation).

Should it be useful, I've put a minimal-ish reproduction at https://github.com/chwthewke/splain-by-name (not so minimal because it starts from my usual template sadly, but I took care to disable anything that could interfere so it should reproduce nicely). The project has two modules with the same source, one with splain active, the other without. It targets scala 2.13.1 but from other testing I think 2.13.0 is similarly affected.

Thank you

unit tests in splain 0.5.9 and scala 2.13.6 are different. Should they be unified?

Just one quick example, for the test "chain":

In scala 2.13.6:

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]
}

In splain 0.5.9:

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]
}

I assume this is caused by a few bugfixes introduced after 0.5.8.

Now the problem becomes: should the latest tests in our plugin BE A SUPERSET of scalac test?

If so, our tests should have identical format with tests in scalac, afterwards we can easily include their tests without rewriting them

The plugin is already in production, people expect upcoming changes not breaking any existing behaviour (unless for a good reason)

possibly odd output

I've given splain a go on a project where I'm having problems with implicits. Here is some of the output:

[error] /home/nmrp3/devel/turingatemyhamster/trefoil/src/main/scala/uk/co/turingatemyhamster/trefoil/TestEnc.scala:46: implicit error;
[error] !I e: RdfTagless[Prod]
[error]     implicitly[RdfTagless[Prod[cats.Id, Encoding, ?]]]

The unaltered output is:

[error] /home/nmrp3/devel/turingatemyhamster/trefoil/src/main/scala/uk/co/turingatemyhamster/trefoil/TestEnc.scala:46: could not find implicit value for parameter e: uk.co.turingatemyhamster.trefoil.RdfTagless[[γ$1$]cats.data.Prod[[A]A,uk.co.turingatemyhamster.trefoil.Encoding,γ$1$]]
[error]     implicitly[RdfTagless[Prod[cats.Id, Encoding, ?]]]

Perhaps I'm reading it wrong, but it looks to me like the splain output is trunchating the printing of Prod.

Related to this, what I really need to understand what's going wrong is what implicits it tried here but failed to use. In a verbose output, I really want to see a list of all the failed candidate implicits with why they failed to match. I only really need that for the deepest failing implicit in the chain. Is that possible?

Configuration for shortening types / better support for newtypes

Would it be possible to add configuration for how much to shorten types in errors? Maybe just an int to denote the minimum number of package/path parts to keep, defaulting to 1? e.g. for a type foo.bar.baz.Thing, setting the value to 2 would print baz.Thing instead of just Thing.

This would make it a lot easier to use splain along with newtypes, which generate code like this:

@newtype case class WidgetId(toInt: Int)
// becomes
type WidgetId = WidgetId.Type
object WidgetId {
  type Base = Any { type WidgetId$newtype }
  trait Tag extends Any
  type Type <: Base with Tag
}

so any errors for the WidgetId type printed by splain always end up just saying Type.

Usage example for old scala versions does not work

Hi,
I have in the code the code from https://github.com/tek/splain#usage:

libraryDependencies += {
  val v = if (scalaVersion.value <= "2.12.4") "0.4.1" else "0.5.5"
  ("io.tryp" %% "splain" % v cross CrossVersion.patch).withConfigurations(Some("plugin->default(compile)"))
}

However it does not work for Scala 2.12.10 and 2.12.11. The code sample compares strings, so "1" is < "4" so it tries to use "0.4.1".

The problem appears explicitly on 2.12.11, because splain 0.4.1 was still released for 2.12.10.

Weird interaction with refined

I recently stumbled upon a weird interaction between this plugin and refined.

The problem is referenced in a comment of this issue.

To put it simply, when splain is active, the macro expansion from refined start failing (with a weird ClassCastException) when used in the REPL (but everything works fine in regular compiation).

To reproduce the problem, one can define the following build.sbt

scalaVersion in ThisBuild := "2.12.4"

libraryDependencies += "eu.timepit" %% "refined" % "0.8.7"

addCompilerPlugin("io.tryp" %% "splain" % "0.2.7" cross CrossVersion.patch)

and past the following lines in the console:

import eu.timepit.refined._, api._, auto._, char._, generic._, collection._
val str: String Refined Forall[UpperCase] = "FOO"

Scala 2.12 support

(In case it’s not clear, I am very excited about this plugin.)

Just noticed that there’s no 2.12 support yet (so I still can’t add it unconditionally to ~/.sbt).

Implicit resolution error in REPR

I've found that I got compilation error in REPR when the splain plugin is enabled:

scala> import shapeless._, me.limansky._, cats.instances.all._
import shapeless._
import me.limansky._
import cats.instances.all._

scala> case class Foo(n: Int); case class Bar(s: String, f: Foo)
defined class Foo
defined class Bar

scala> MapReader[Bar].read(Map("s" -> "b", "n" -> "6"))
<console>:25: error: implicit error;
!I ev: MapReader[Bar]
MapReader.genericReader invalid because
!I mapReader: MapReader[R]
Generic.materialize invalid because
type parameters weren't correctly instantiated outside of the implicit tree: inferred type arguments [shapeless.::[String,shapeless.::[Foo,shapeless.HNil]],Nothing] do not conform to method materializeCoproduct's type parameter bounds [V <: shapeless.Coproduct,R <: shapeless.Coproduct]
       MapReader[Bar].read(Map("s" -> "b", "n" -> "6"))

The same code without plugin works fine:

scala> import shapeless._, me.limansky._
import shapeless._
import me.limansky._

scala> import shapeless._, me.limansky._, cats.instances.all._
import shapeless._
import me.limansky._
import cats.instances.all._

scala> case class Foo(n: Int); case class Bar(s: String, f: Foo)
defined class Foo
defined class Bar

scala> MapReader[Bar].read(Map("s" -> "b", "n" -> "6"))
res0: Bar = Bar(b,Foo(6))

The code is available in this gist: https://gist.github.com/limansky/7c91a3deaa25fc0ec7339ea191d4a54a

It looks like the problem is reproducible only in REPR, at least the tests are compiled and passed successfully.

Broken on 2.12.4

From a discussion on gitter.im/scala/contributors:

nafg @nafg October 31, 2017 10:14 PM
I'm trying to use the tek/splain compiler plugin and it works fine with 2.12.3 but with 2.12.4 I get java.lang.NoClassDefFoundError: scala/reflect/internal/util/Statistics$

Harrison Houghton @hrhino October 31, 2017 10:30 PM
I think jvican removed that and redid the statistics infra.
tek might need to publish a 2.12.4+ version of the plugin

Jorge @jvican November 2, 2017 4:54 AM
@nafg: @hrhino is correct. Statistics are no longer objects, they are in the compiler cake, so that's why you get that classloading issue. The issue here is that compiler plugins are often cross-compiled to the Scala partial version (2.10, 2.11, 2.12), when they should instead be cross-compiled to the full scala version (2.12.1, 2.12.2, 2.12.3, 2.12.4). The reason why this is the only reasonable way to do it is because the compiler API does not have any bincompat guarantees.

Print full path / truncated path of a path-dependent type?

Here is a simple example:

    class Example {

      type VV
    }

    val e1 = new Example {

      type VV <: Int
    }

    implicitly[e1.VV =:= String]

The error message is (I have set keepmodules to 2 for better demonstraction):

[Error] /....../Spec.scala:61: implicit error;
!I e (Cannot prove that e1.VV =:= String.): e1.VV scala.=:= java.lang.String

So what exactly is e1? It is clearly not global, so if there are many variables e1 scattered around the code. How to find it? An option similar to keepmodules should theoretically solve this problem, by partially displaying preceding package/module/path of a path-dependent type. Or if necessary, display the line of code of which such variable is created.

I haven't played with the scala reflection library for path-dependent type, so I don't know if it is physically possible. If you think this could be helpful, I'll definitely spend more time on it to figure it out

built-in and Scala 3

Hey folks, sorry for bothering you with an issue, I just have a few questions:

  1. I saw some activity on porting some feature flags to the built-in version, like the infix option. Is that on the roadmap? cc @tribbloid
  2. What will the future of splain be for Scala 3? I use splain mainly along with Shapeless. I'm wondering if splain's features are compatible with the Dotty compiler or are they superseded by some Dotty features?

Thanks for taking the time to answer!

enabling Plugin causes all members of package object "scala" to be undiscoverable in scala 2.13.7+

As indicated in the following test results:

https://github.com/tek/splain/actions/runs/1552235511

Even a simple line like:

List(1)

will confuses the modified typer, making it to throw some weird information:

object List is not a member of package scala

The defective typer was introduced by the following legacy code in ScalaPlugin.scala:

  val analyzerField = classOf[Global].getDeclaredField("analyzer")
  analyzerField.setAccessible(true)
  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)
    phasesSet --= oldScs
    phasesSet ++= newScs
  }

I wonder if there is a more elegant way to inject the splainAnalyzer without all these covfefe. But I can't remember their purpose when they were written.

@tek I wonder if you could give me a hint on which part of them is rendered redundant by the AnalyzerPlugin patch?

No output from plugin

I'm trying out splain plugin for troubleshooting an issue with implicits.
I've tried following the usage notes in the README, but I can't get any output when compiling the code.

Should splain output debug info on STDOUT when the plugin is properly set up and I do issue compile for sbt?

I'm using sbt v1.3 and Scala v2.12 and added the following bits in my build.sbt

scalacOptions ++= Seq(
  "-P:splain:implicits:true"
)

addCompilerPlugin("io.tryp" % "splain" % "0.5.6" cross CrossVersion.patch)

Should this be enough to set up the plugin or is something else needed?

My project setup can be found here:
https://github.com/marko-asplund/play-json-derived-codecs-issue/tree/0db017b110f3ea71987a7269804443785fcde459/play-json-1

Splain breaks Scaladoc

Splain appears to break Scaladoc

Minimal reproduction:

  1. Create a seed project with sbt new scala/scala-seed.g8
  2. Add some documentation to the Greeting trait (a top-level description suffices)
  3. Run scaladoc with sbt doc
  4. Note that your Greeting documentation is present
  5. Add Splain 0.5.8 to build.sbt
  6. Re-run scaladoc
  7. Note that your Greeting documentation is now gone

Reproduced here with sbt 1.4.7 and Scala 2.13.4, though I've seen this behavior across several real projects with sbt 1.3.x and Scala 2.12, so it doesn't appear to be compiler/build related.

Error message when assigning value of type A to Tuple[A]

Hi there :)

Thank you very much for this plugin, it is really helpful!

I've stumbled upon one thing today, though:

val a: Tuple1[String] = "blar"

leads to the error message

type mismatch; String|String

Is there any way that this could be improved by noting that one of those is in a tuple, using (String,) or Tuple1[String] maybe?

Feature freeze for 1.0.0-RC1

Just proposing.

Also, can you compile on your computer? I can't do release on my own. I don't have the GPG key for the maven repo

Scals 2.13 support

Hi, thanks for this great plugin! Any change to support scala 2.13.0-M5?

Add `word-wrapping` for long infix types

Long type signatures (like shapeless's records) looks clumsy even with infix printing. How about supporting optional word-wrapping by type name?

E.g.

Int :: String :: HNil

will become

Int ::
String ::
HNil

Singleton types are erased at display.

Here is a simple example:


    val a = 1
    val b = 2

    implicitly[a.type =:= b.type]

Generates the error::

[Error] /......:46: implicit error;
!I e (Cannot prove that a.type =:= b.type.): Int =:= Int
one error found

The first section Cannot prove that a.type =:= b.type is a customised implicitNotFound message, which is helpful, the second part Int =:= Int is added by splain, which is clueless.

If the ImplicitNotFound message hasn't been defined, or it is buried deep in a chain implicit resolution tree, there will be no way to figure out the cause.

My proposed fix is to show the full inheritance tree for not just the type, but all its parameters, e.g. for type a.type =:= b.type, the result should look like this:

-+ a.type =:= b.type
 :       `-+ [ 2 ARGS ] :
 :         !-+ a.type .................................................................................................................. [0]
 :         : !-+ Int ..................................................................................................................... [1]
 :         :   !-+ AnyVal
 :         :     !-- Any ..................................................................................................................... [2]
 :         !-+ b.type .................................................................................................................. [3]
 :           !-- Int ..................................................................................................................... [1]
 !-+ a.type <:< b.type
   :       `-+ [ 2 ARGS ] :
   :         !-- a.type .................................................................................................................. [0]
   :         !-- b.type .................................................................................................................. [3]
   !-+ java.io.Serializable
   : !-- Any ..................................................................................................................... [2]
   !-+ a.type => b.type
     :       `-+ [ 2 ARGS ] :
     :         !-- a.type .................................................................................................................. [0]
     :         !-- b.type .................................................................................................................. [3]

I already have the code extract the tree, I just don't know where to inject into your library. Could you point me to the right part of your code?

0.1.16 significantly slows down compilation

After updating to 0.1.16 I experience a 4x compilation slowdown on my work project: clean compilation with 0.1.15 took 49 secods, now it takes 203 seconds (without any errors).

I use the following configuration:

  "-P:splain:implicits:false",
  "-P:splain:breakinfix:12",

Unfortunately, I cannot reproduce this issue with any of my open projects :(

diverging implicits

I've got a bunch of 'diverging implicit expansion' problems. Is there any way that splain could show me clearer details about this? The stack of implicits up to the divergence, and the divergent options?

project is not clonable on windows

Try to check out the project on Windows and get error:

git checkout master
fatal: cannot create directory at 'tests/aux': Invalid argument

This is because aux is a reserved name on windows.
The best fix is to rename the folder.

See:
git-for-windows/git#2777 (comment)
https://www.howtogeek.com/fyi/windows-10-still-wont-let-you-use-these-file-names-reserved-in-1974/
swcarpentry/DEPRECATED-bc#464
https://stackoverflow.com/questions/63727594/github-git-checkout-returns-error-invalid-path-on-windows

Splain conflicting with paradise 3.0.0-M10

Great plugin - it has helped me a load.
Unfortunately it has stopped working since I introduced v3 of the paradise plugin

Scala version

  • 2.12.4

Config 1

Causes macro expansion to fail with:
"macro annotation could not be expanded (the most common reason for that is that you need to enable the macro paradise plugin; another possibility is that you try to use macro annotation in the same compilation run that defines it)"

 .settings(addCompilerPlugin("org.scalameta" % "paradise" % "3.0.0-M10" cross CrossVersion.full))
 .settings(addCompilerPlugin("io.tryp" % "splain" % "0.2.7" cross CrossVersion.patch))

Config 2

Macro expansion works but I'm back to the standard implicit failure output when something cannot be summoned

  .settings(addCompilerPlugin("io.tryp" % "splain" % "0.2.7" cross CrossVersion.patch))
  .settings(addCompilerPlugin("org.scalameta" % "paradise" % "3.0.0-M10" cross CrossVersion.full))

Run after macro expansion

With Splain I’m getting an error saying that an implicit I reference doesn’t exist. My guess is that this is because the implicit in question is generated by Simulacrum’s @typeclass macro, and Splain is running before it generates the implicit.

Support a global sbt setting.

Ok, supporting anything but the latest compiler is not usually something I would suggest, but currently to enable splain globally, I have to put the following in my ~/.sbt/0.13/global.sbt:

libraryDependencies ++= (scalaBinaryVersion.value match {
  case "2.11" | "2.12" => Seq(compilerPlugin("tryp" %% "splain" % "0.1.11"))
  case _               => Nil
})

This is because global settings get picked up by everything including sbt itself, which uses Scala 2.10. So, without a 2.10 release of splain, I need to conditionalize loading it.

This isn’t a terrible workaround, but it’d be nice to be able to just drop addCompilerPlugin("tryp" %% "splain" % "0.1.11") into the global config and have it work. Also, if Splain is just integrated into an upcoming 2.12 release, then I don’t care about it working on 2.10 at all.

use in interactive compiler?

This looks very useful. He you think it'd be possible to have the implicit messages wired up to the reporter to use range positions? I'd like to see if we could get this working in ensime for instant feedback.

Scala 2.13.7 release

Hi,
Is there any plans to release a version which is compatible with Scala 2.13.7?

Thanks!

Rafael

`noImplicitFoundError` API in AnalyzerPlugin doesn't have annotation message

When I try to enable 2 tests that uses implicit error messages. I found the following design flaw in the AnalyzerPlugin interface:

    override def noImplicitFoundError(param: Symbol, errors: List[ImplicitError], previous: String): String = {

      val convertedErrors = errors.map(convertError)

      val result = splainAnalyzer.formatImplicitError(
        param.asInstanceOf[splainAnalyzer.global.Symbol],
        convertedErrors,
        ""
      )

      result
    }
  }

As can be seen, the annotation argument in formatImplicitError is hardcoded to empty, causing all such messages to be ignored

This is because noImplicitFoundError doesn't provide it, furthermore it cannot be found anywhere. This is an example of a degrading quality of source code caused by premature integration of API

Release for 2.13.5

A project that I work on loves using splain, but we're also eagerly awaiting a few fixes that have come with Scala 2.13.5. It'd be great if a splain release could be cut that is compatible with 2.13.5!

Are there any plans for doing so soon, or is more work needed? We can, of course, upgrade without splain, but it'd be better if we didn't have to.

Add more verbose mode for implicits

I just come with the following observation when playing with it for different edge cases:

-Vimplicits-verbose-candidate

In the following example:

  trait F[I, O]

  trait G1[A, B]
  trait G2[A, B]

  implicit def f[I, M, O](
      implicit
      g1: G1[I, M],
      g2: G2[M, O]
  ): F[I, O] = ???

  implicit def g1: G1[Int, String] = ???

  implicitly[F[Int, Char]]

this trigger the error report:

newSource1.scala:26: error: implicit error;
!I e: F[Int, Char]
f invalid because:
!I g2: G2[M, Char]
  implicitly[F[Int, Char]]
            ^

Turns out that the default implementation of:

    def unapplyCandidate(e: ImplicitError): Tree =
      e.candidate match {
        case TypeApply(fun, _) => fun
        case a                 => a
      }

shrug off type signatures of f (it should be f[Int, String, Char]). debugging of this error becomes much harder, considering that the following line:

!I g2: G2[M, Char]

Shows only the unreified type signature, the reified version:

!I g2: G2[String, Char]

Cannot be found anywhere.

-Vimplicits-verbose-position

Since the parameter keepmodules is discarded in scala 2.13.6. Package or owner information won't be shown in the message. This means if we have multiple functions like f or g1 with similar signature, it will be difficult to determine which one is the real candidate.

This new option should display the line number for each candidate. Making it much harder to cause confusion, e.g. for the above example, the output may become:

newSource1.scala:26: error: implicit error;
!I e: F[Int, Char]
Annotation.f[Int, String, Char] invalid because ...... source-<toolbox>,line-18,offset=218
!I g2: G2[M, Char]
  implicitly[F[Int, Char]]
            ^

The source-<toolbox>,line-18,offset=218 is generated by candidate.symbol.pos.showDebug, obviously this format can be improved.

splain hiding original compiler information in cases of simple type mismatch.

This is a problem that manifests for different kind of errors. The most simple one being a type signature mismatch WITHOUT implicit search. E.g.:

(before applying splain):


[Error] /home/peng/git/shapesafe/macro/src/main/scala/com/tribbloids/shapesafe/m/shape/op/EinSumOps.scala:16: value GetField is not a member of object com.tribbloids.shapesafe.m.util.RecordUtils
[Error] /home/peng/git/shapesafe/macro/src/main/scala/com/tribbloids/shapesafe/m/util/RecordUtils.scala:20: type mismatch;
 found   : shapeless.ops.record.Selector[H,W#T]
 required: shapeless.ops.record.Selector[H,w.T]
Note: W#T >: w.T, but trait Selector is invariant in type K.
You may wish to define K as -K instead. (SLS 4.5)
 Note: implicit method getter is not applicable here because it comes after the application point and it lacks an explicit result type
two errors foun

(after):

[Error] /home/peng/git/shapesafe/macro/src/main/scala/com/tribbloids/shapesafe/m/shape/op/EinSumOps.scala:16: value GetField is not a member of object com.tribbloids.shapesafe.m.util.RecordUtils
[Error] /home/peng/git/shapesafe/macro/src/main/scala/com/tribbloids/shapesafe/m/util/RecordUtils.scala:20: type mismatch;
  ops.record.Selector[H, W.T]
 Note: implicit method getter is not applicable here because it comes after the application point and it lacks an explicit result type

The problem is that scala compiler verbosity has gradually improve over time (as well as capabilities, so old presentations of advanced types gradually became obsolete)

found/required types fails to correctly diff records

Example:

  import shapeless._
  import shapeless.record._
  import shapeless.syntax.singleton._

  type Message = Record.`'title -> String, 'text -> String, 'receiver -> Int`.T

  def show(message: Message) = {
    println(message('title) + " " + message('text))
  }

  val mess1 =
    ('title ->> "Title:") ::
      ('text ->> "Sample text") ::
      ('receiver ->> 42L) :: HNil

  show(mess1)

Splain prints:

 String with KeyTag[Symbol with Tagged[String] {}, String] {}|String with KeyTag[Symbol with Tagged[String] {}, String] {} :: String with KeyTag[Symbol with Tagged[String] {}, String] {}|String with KeyTag[Symbol with Tagged[String] {}, String] {} :: Long with KeyTag[Symbol with Tagged[String] {}, Long] {}|Int with KeyTag[Symbol with Tagged[String] {}, Int] {} :: HNil

though the only diffenrence is 'receiver's type

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.