diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala index f976ee86e132..85c4cc64c9e3 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala @@ -3,6 +3,7 @@ package tastyreflect import dotty.tools.dotc.ast.{Trees, tpd, untpd} import dotty.tools.dotc.ast.tpd.TreeOps +import dotty.tools.dotc.typer.Typer import dotty.tools.dotc.core._ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.StdNames.nme @@ -10,6 +11,8 @@ import dotty.tools.dotc.core.quoted.PickledQuotes import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.tastyreflect.FromSymbol.{definitionFromSym, packageDefFromSym} +import dotty.tools.dotc.parsing.Parsers.Parser +import dotty.tools.dotc.util.SourceFile import scala.tasty.reflect.Kernel @@ -47,6 +50,26 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util. def Settings_color(self: Settings): Boolean = self.color.value(rootContext) == "always" + // + // MISC + // + /** Whether the code type checks in the given context? + * + * @param code The code to be type checked + * + * The code should be a sequence of expressions or statements that may appear in a block. + */ + def typeChecks(code: String)(implicit ctx: Context): Boolean = { + val ctx2 = ctx.fresh.setNewTyperState().setTyper(new Typer) + val tree = new Parser(SourceFile.virtual("tasty-reflect", code))(ctx2).block() + + if (ctx2.reporter.hasErrors) false + else { + ctx2.typer.typed(tree)(ctx2) + !ctx2.reporter.hasErrors + } + } + // // TREES // diff --git a/library/src/scala/tasty/Reflection.scala b/library/src/scala/tasty/Reflection.scala index b7fecff9a9d7..61ad3db52d3e 100644 --- a/library/src/scala/tasty/Reflection.scala +++ b/library/src/scala/tasty/Reflection.scala @@ -27,6 +27,18 @@ abstract class Reflection def typeOf[T: scala.quoted.Type]: Type = implicitly[scala.quoted.Type[T]].unseal.tpe + object typing { + /** Whether the code type checks in the given context? + * + * @param code The code to be type checked + * + * @return false if the code has syntax error or type error in the given context, otherwise returns true. + * + * The code should be a sequence of expressions or statements that may appear in a block. + */ + def typeChecks(code: String)(implicit ctx: Context): Boolean = kernel.typeChecks(code)(ctx) + } + val util: reflect.utils.TreeUtils { val reflect: self.type } = new reflect.utils.TreeUtils { val reflect: self.type = self } diff --git a/library/src/scala/tasty/reflect/Kernel.scala b/library/src/scala/tasty/reflect/Kernel.scala index 44d8e163bd4b..b07ace18853e 100644 --- a/library/src/scala/tasty/reflect/Kernel.scala +++ b/library/src/scala/tasty/reflect/Kernel.scala @@ -153,6 +153,17 @@ trait Kernel { def Settings_color(self: Settings): Boolean + // + // MISC + // + /** Whether the code type checks in the given context? + * + * @param code The code to be type checked + * + * The code should be a sequence of expressions or statements that may appear in a block. + */ + def typeChecks(code: String)(implicit ctx: Context): Boolean + // // TREES // @@ -695,7 +706,7 @@ trait Kernel { // // PATTERNS // - + /** Pattern tree of the pattern part of a CaseDef */ type Pattern <: AnyRef diff --git a/tests/run-with-compiler/reflect-typeChecks/assert_1.scala b/tests/run-with-compiler/reflect-typeChecks/assert_1.scala new file mode 100644 index 000000000000..b9ab4733bbfe --- /dev/null +++ b/tests/run-with-compiler/reflect-typeChecks/assert_1.scala @@ -0,0 +1,16 @@ +import scala.quoted._ +import scala.tasty._ + +object scalatest { + + inline def assertCompile(inline code: String): Unit = ${ assertImpl(code, true) } + inline def assertNotCompile(inline code: String): Unit = ${ assertImpl(code, false) } + + def assertImpl(code: String, expect: Boolean)(implicit refl: Reflection): Expr[Unit] = { + import refl._ + + val actual = typing.typeChecks(code) + + '{ assert(${expect.toExpr} == ${actual.toExpr}) } + } +} diff --git a/tests/run-with-compiler/reflect-typeChecks/test_2.scala b/tests/run-with-compiler/reflect-typeChecks/test_2.scala new file mode 100644 index 000000000000..e83eb0c9503b --- /dev/null +++ b/tests/run-with-compiler/reflect-typeChecks/test_2.scala @@ -0,0 +1,40 @@ +object Test { + import scalatest._ + + trait Eq[T] + implicit val eq: Eq[Int] = new Eq[Int] {} + + implicit class AnyOps[T](x: T) { + def === (y: T)(implicit c: Eq[T]) = x == y + } + + def main(args: Array[String]): Unit = { + assertCompile("5 === 5") + assertNotCompile("5.6 === 7.7") + + val x: Int = 5 + assertCompile("x + 3") + assertNotCompile("y + 3") + import scala.util.Left + assertCompile("Left(3)") + assertNotCompile("Rigth(3)") + + def f(x: Int): Int = x * x + assertCompile("f(3)") + assertNotCompile("g(3)") + + type T + assertCompile("def foo(x: T): T = x") + assertNotCompile("foo(???)") + assertNotCompile("def foo(x: S): S = x") + + assertNotCompile("def test(x: Int) =") + + assertCompile( + """ + class EqString extends Eq[String] + new EqString + """ + ) + } +}