diff --git a/core/src/main/scala-2.13.7+/latest/splain/SplainFormattingExtension.scala b/core/src/main/scala-2.13.7+/latest/splain/SplainFormattingExtension.scala index 7efc512..f23672a 100644 --- a/core/src/main/scala-2.13.7+/latest/splain/SplainFormattingExtension.scala +++ b/core/src/main/scala-2.13.7+/latest/splain/SplainFormattingExtension.scala @@ -493,44 +493,62 @@ trait SplainFormattingExtension extends typechecker.splain.SplainFormatting with } } + case class DefPosition( + element: Formatted, + msg: String + ) extends Based { + + def index(): Unit = { + + if (pluginSettings.showTypeDefPosition) + Based += FormattedIndex(element) -> this + } + + override protected def formattedHeader_Body(break: Boolean): (String, Seq[TypeRepr]) = { + + s"(defined at)" -> Seq(BrokenType(List(msg))) + } + } + def formatTypeRaw(tpe: Type, top: Boolean): Formatted = { formatWithInfix(tpe, extractArgs(tpe), top)(formatType) } override def formatTypeImpl(tpe: Type, top: Boolean): Formatted = { - if (pluginSettings.TypeDiffsDetail.disambiguation) { + tpe.typeArgs match { + case List(t1, t2) => + val result = + if (pluginSettings.TypeDiffsDetail.disambiguation) { + + withDisambiguation(Nil, t1, t2) { + formatTypeImplNoDisambiguation(tpe, top) + } + } else { - tpe.typeArgs match { - case List(t1, t2) => - val result = withDisambiguation(Nil, t1, t2) { formatTypeImplNoDisambiguation(tpe, top) } - result match { - case Infix(ii, left, right, _) => - val noApparentDiff = (left == right) && (t1 != t2) - - if (noApparentDiff || pluginSettings.TypeDiffsDetail.builtInMsgAlways) { + result match { + case Infix(ii, left, right, _) => + val noApparentDiff = (left == right) && (t1 != t2) - BuiltInDiffMsg( - result, - TypeDiffView(t1, t2).builtInDiffMsg, - Some(ii) - ).index() - } - case _ => - } + if (noApparentDiff || pluginSettings.TypeDiffsDetail.builtInMsgAlways) { - result - case _ => - formatTypeImplNoDisambiguation(tpe, top) - } - - } else { + BuiltInDiffMsg( + result, + TypeDiffView(t1, t2).builtInDiffMsg, + Some(ii) + ).index() + } + case _ => + } - formatTypeImplNoDisambiguation(tpe, top) + result + case _ => + formatTypeImplNoDisambiguation(tpe, top) } + } protected def formatTypeImplNoDisambiguation(tpe: Type, top: Boolean): Formatted = { @@ -548,6 +566,13 @@ trait SplainFormattingExtension extends typechecker.splain.SplainFormatting with val result = results.last + TypeView(tpe).defPositionOpt.foreach { msg => + DefPosition( + result, + msg + ).index() + } + result } diff --git a/core/src/main/scala-2.13.7+/latest/splain/TyperCompatViews.scala b/core/src/main/scala-2.13.7+/latest/splain/TyperCompatViews.scala index 98b9809..263ce59 100644 --- a/core/src/main/scala-2.13.7+/latest/splain/TyperCompatViews.scala +++ b/core/src/main/scala-2.13.7+/latest/splain/TyperCompatViews.scala @@ -1,5 +1,7 @@ package splain +import scala.reflect.internal.util.{NoSourceFile, Position} + trait TyperCompatViews { self: SplainAnalyzer => @@ -66,18 +68,35 @@ trait TyperCompatViews { } } + lazy val defPositionOpt: Option[String] = { + + val pos = this.definingSymbol.pos + + pos.source match { + case NoSourceFile => None + case s => + val prefix = s.file.path + ":" + + // val msg = Position.formatMessage(pos, "", shortenFile = true) + // val result = " ... " + msg.split("\n").dropRight(1).map(_.trim).mkString(" ") + val result = s"$prefix${pos.line}:${pos.column}" + Some(result) + } + } + def typeToString: String = { - val detailLvl = pluginSettings.typeDetail + val typeDetail = pluginSettings.typeDetail def short = self.safeToString + def long = scala.util.Try(self.toLongString).getOrElse(short) def maybeContext = scala.util.Try(existentialContext(self)).toOption def maybeAlias = scala.util.Try(explainAlias(self)).toOption - detailLvl match { + typeDetail match { case i if i <= 1 => short case 2 => long case 3 => @@ -85,7 +104,6 @@ trait TyperCompatViews { case i if i >= 4 => (Seq(long) ++ maybeContext ++ maybeAlias).mkString("") - } } } diff --git a/core/src/main/scala/splain/PluginSettings.scala b/core/src/main/scala/splain/PluginSettings.scala index ca9097e..991a4eb 100644 --- a/core/src/main/scala/splain/PluginSettings.scala +++ b/core/src/main/scala/splain/PluginSettings.scala @@ -40,6 +40,8 @@ case class PluginSettings(pluginOpts: mutable.Map[String, String]) { def showTypeReduction: Boolean = boolean(PluginSettings.Key.typeReduction) + def showTypeDefPosition: Boolean = boolean(PluginSettings.Key.typeDefPosition) + def typeDiffsDetail: Int = int(PluginSettings.Key.typeDiffsDetail) object TypeDiffsDetail { @@ -66,6 +68,8 @@ object PluginSettings { val typeReduction = "Vtype-reduction" + val typeDefPosition = "Vtype-def-position" + val typeDetail = "Vtype-detail" val typeDiffsDetail = "Vtype-diffs-detail" @@ -79,6 +83,7 @@ object PluginSettings { Key.implicitDiverging -> "false", Key.implicitDivergingMaxDepth -> "100", Key.typeReduction -> "false", + Key.typeDefPosition -> "false", Key.typeDetail -> "1", Key.typeDiffsDetail -> "1", Key.debug -> "false" diff --git a/core/src/test/resources/splain/plugin/VTypeDefPositionSpec/__direct/check b/core/src/test/resources/splain/plugin/VTypeDefPositionSpec/__direct/check new file mode 100644 index 0000000..db89a50 --- /dev/null +++ b/core/src/test/resources/splain/plugin/VTypeDefPositionSpec/__direct/check @@ -0,0 +1,20 @@ +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 + ^ diff --git a/core/src/test/scala/splain/plugin/VTypeDefPositionSpec.scala b/core/src/test/scala/splain/plugin/VTypeDefPositionSpec.scala new file mode 100644 index 0000000..0b22cb8 --- /dev/null +++ b/core/src/test/scala/splain/plugin/VTypeDefPositionSpec.scala @@ -0,0 +1,31 @@ +package splain.plugin + +import splain.SpecBase + +class VTypeDefPositionSpec 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-def-position", numberOfErrors = 2) + } +} diff --git a/core/src/testFixtures/scala/splain/TestHelpers.scala b/core/src/testFixtures/scala/splain/TestHelpers.scala index d044e9c..2bbdb63 100644 --- a/core/src/testFixtures/scala/splain/TestHelpers.scala +++ b/core/src/testFixtures/scala/splain/TestHelpers.scala @@ -3,7 +3,7 @@ package splain import org.scalatest.exceptions.TestFailedException import org.scalatest.{Assertion, Suite} import org.slf4j.LoggerFactory -import splain.test.TryCompile +import splain.test.{Issue, TryCompile} import java.nio.file.{FileSystems, Files, Path, Paths} import java.util.concurrent.atomic.AtomicInteger @@ -197,7 +197,7 @@ trait TestHelpers extends Suite { case class DirectRunner() { case class ParseGroundTruths( - startsWith: String = "newSource1.scala:", + startsWith: String = Issue.defaultSrcName, fName: Option[String] = None ) { @@ -208,9 +208,11 @@ trait TestHelpers extends Suite { } lazy val cases: Seq[String] = { + val regex = s"(^|\n)$startsWith" + val result = raw .split( - startsWith + regex ) .toSeq .filter(_.nonEmpty)