diff --git a/src/main/scala/splain/format/Formatting.scala b/src/main/scala/splain/format/Formatting.scala index 87a6360..f56e0f5 100644 --- a/src/main/scala/splain/format/Formatting.scala +++ b/src/main/scala/splain/format/Formatting.scala @@ -122,15 +122,6 @@ with ImplicitMsgCompat .takeWhile(_ != "type") .filterNot(_.contains("$")) - def modulePath: (Type, Symbol) => List[String] = { - case (TypeRef(pre, _, _), _) if !pre.toString.isEmpty => - sanitizePath(pre.toString.split("\\.").toList) - case (SingleType(_, _), sym) => - symbolPath(sym).dropRight(1) - case (_, _) => - Nil - } - def pathPrefix: List[String] => String = { case Nil => "" @@ -141,7 +132,7 @@ with ImplicitMsgCompat } def qualifiedName(path: List[String], name: String): String = - s"${pathPrefix(path)}$name" + s"${pathPrefix(path)}$name" def stripModules(path: List[String], name: String): Option[Int] => String = { case Some(keep) => @@ -150,14 +141,59 @@ with ImplicitMsgCompat name } + case class TypeParts(sym: Symbol, tt: Type) { + + def modulePath: List[String] = (tt, sym) match { + case (TypeRef(pre, _, _), _) if !pre.toString.isEmpty => + sanitizePath(pre.toString.split("\\.").toList) + case (SingleType(_, _), sym) => + symbolPath(sym).dropRight(1) + case (_, _) => + Nil + } + + def ownerPath: List[String] = { + val chain = sym.ownerChain.reverse + val parts = chain.map(_.name.decodedName.toString) + val (paths, names) = parts.splitAt( + Math.max(0,parts.size - 1) + ) + paths + } + + def shortName: String = { + val prefixes = tt.prefixString.split('.').dropRight(1) + val prefix = prefixes.mkString(".") + "." + val name = tt.safeToString + name.stripPrefix(prefix) + } + } + def stripType(tpe: Type): (List[String], String) = { - val sym = if (tpe.takesTypeArgs) tpe.typeSymbolDirect else tpe.typeSymbol - val symName = sym.name.decodedName.toString - val path = modulePath(tpe, sym) - val name = - if (sym.isModuleClass) s"$symName.type" - else symName - (path, name) + tpe match { + case tt: SingletonType => + val sym = tt.termSymbol + val parts = TypeParts(sym, tt) + + parts.modulePath -> parts.shortName + + case tt: RefinedType => + val sym = tt.typeSymbol + val parts = TypeParts(sym, tt) + + parts.modulePath -> parts.shortName + + case _ => + // TODO: should this also use TypeParts ? + val sym = if (tpe.takesTypeArgs) tpe.typeSymbolDirect else tpe.typeSymbol + val symName = sym.name.decodedName.toString + val parts = TypeParts(sym, tpe) + + val name = + if (sym.isModuleClass) s"$symName.type" + else symName + (parts.modulePath, name) + } } def formatNormalSimple(tpe: Type): (List[String], String) = diff --git a/src/test/scala/splain/ErrorsSpec.scala b/src/test/scala/splain/ErrorsSpec.scala index eae9ac3..da54a05 100644 --- a/src/test/scala/splain/ErrorsSpec.scala +++ b/src/test/scala/splain/ErrorsSpec.scala @@ -10,7 +10,10 @@ import org.specs2._ object Helpers { - def base = System.getProperty("splain.tests") + lazy val userDir = System.getProperty("user.dir").stripSuffix("/") + + def base = Option(System.getProperty("splain.tests")) + .getOrElse(userDir + "/" + "tests") def fileContent(name: String, fname: String): Path = { FileSystems.getDefault.getPath(base, name, fname) @@ -38,7 +41,14 @@ import types._ val cm = universe.runtimeMirror(getClass.getClassLoader) - val plugin = System.getProperty("splain.jar") + val plugin = Option(System.getProperty("splain.jar")).getOrElse { + val dir = FileSystems.getDefault.getPath(userDir + "/target/scala-2.13") + val file = Files.list(dir).toArray + .map(v => v.asInstanceOf[Path]) + .filter(v => v.toString.endsWith(".jar")).head + + file.toAbsolutePath.toString + } val opts = s"-Xplugin:$plugin -P:splain:color:false -P:splain:bounds -P:splain:tree:false" @@ -84,6 +94,7 @@ extends Specification class ErrorsSpec extends SpecBase { + def is = s2""" implicit resolution chains ${checkError("chain")} found/required type diff ${checkError("foundreq")} @@ -102,7 +113,15 @@ extends SpecBase truncate refined type ${checkError("truncrefined", "-P:splain:truncrefined:10")} byname higher order ${checkError("byname-higher")} Tuple1 ${checkError("tuple1")} + single types ${checkError("single")} + single types in function ${checkError("single-fn")} + single types with free symbol ${checkError("single-free")} + witness value types ${checkError("witness-value")} """ + +// def is = s2""" +// single types ${checkError("witness-value")} +// """ } class DevSpec diff --git a/tests/byname-higher/code.scala b/tests/byname-higher/code.scala index 483d09a..41c97d6 100644 --- a/tests/byname-higher/code.scala +++ b/tests/byname-higher/code.scala @@ -3,5 +3,5 @@ object Foo type A type B def f(g: (=> A) => B): Unit = () - f(1) + f(1: Int) } diff --git a/tests/disambiguate/code.scala b/tests/disambiguate/code.scala index 33829d9..c66c8e6 100644 --- a/tests/disambiguate/code.scala +++ b/tests/disambiguate/code.scala @@ -22,5 +22,5 @@ object A } def f(a: B.X.Y.T): Unit = () val x: C.X.Y.T = ??? - f(x) + f(x: C.X.Y.T) } diff --git a/tests/single-fn/code.scala b/tests/single-fn/code.scala new file mode 100644 index 0000000..64ca6e9 --- /dev/null +++ b/tests/single-fn/code.scala @@ -0,0 +1,12 @@ +import shapeless._ +import shapeless.ops.hlist._ + +object SingleImp +{ + def fn(): Unit= { + val a = 1 + val b = 2 + + implicitly[a.type *** b.type] + } +} diff --git a/tests/single-fn/error b/tests/single-fn/error new file mode 100644 index 0000000..ad7c505 --- /dev/null +++ b/tests/single-fn/error @@ -0,0 +1,2 @@ +implicit error; +!I e: a.type *** b.type \ No newline at end of file diff --git a/tests/single-free/code.scala b/tests/single-free/code.scala new file mode 100644 index 0000000..1ff4f1b --- /dev/null +++ b/tests/single-free/code.scala @@ -0,0 +1,10 @@ +import shapeless._ +import shapeless.ops.hlist._ + +object SingleImp +{ + def fn[A, B](a: A, b: B) = { + + implicitly[a.type *** b.type] + } +} diff --git a/tests/single-free/error b/tests/single-free/error new file mode 100644 index 0000000..ad7c505 --- /dev/null +++ b/tests/single-free/error @@ -0,0 +1,2 @@ +implicit error; +!I e: a.type *** b.type \ No newline at end of file diff --git a/tests/single/code.scala b/tests/single/code.scala new file mode 100644 index 0000000..f5a244a --- /dev/null +++ b/tests/single/code.scala @@ -0,0 +1,10 @@ +import shapeless._ +import shapeless.ops.hlist._ + +object SingleImp +{ + val a = 1 + val b = 2 + + implicitly[a.type *** b.type] +} diff --git a/tests/single/error b/tests/single/error new file mode 100644 index 0000000..ad7c505 --- /dev/null +++ b/tests/single/error @@ -0,0 +1,2 @@ +implicit error; +!I e: a.type *** b.type \ No newline at end of file diff --git a/tests/tuple1/code.scala b/tests/tuple1/code.scala index b6d02a0..2718bb7 100644 --- a/tests/tuple1/code.scala +++ b/tests/tuple1/code.scala @@ -1,4 +1,4 @@ object Tup1 { - val a: Tuple1[String] = "Tuple1" + val a: Tuple1[String] = "Tuple1": String } diff --git a/tests/witness-value/code.scala b/tests/witness-value/code.scala new file mode 100644 index 0000000..0a614cc --- /dev/null +++ b/tests/witness-value/code.scala @@ -0,0 +1,9 @@ +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) +} diff --git a/tests/witness-value/error b/tests/witness-value/error new file mode 100644 index 0000000..618cba2 --- /dev/null +++ b/tests/witness-value/error @@ -0,0 +1,2 @@ +implicit error; +!I ev: Int(3) *** Int(4) \ No newline at end of file