Skip to content

Add typeChecks to tasty reflect #6135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ 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
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

Expand Down Expand Up @@ -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
//
Expand Down
12 changes: 12 additions & 0 deletions library/src/scala/tasty/Reflection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
13 changes: 12 additions & 1 deletion library/src/scala/tasty/reflect/Kernel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
//
Expand Down Expand Up @@ -695,7 +706,7 @@ trait Kernel {
//
// PATTERNS
//

/** Pattern tree of the pattern part of a CaseDef */
type Pattern <: AnyRef

Expand Down
16 changes: 16 additions & 0 deletions tests/run-with-compiler/reflect-typeChecks/assert_1.scala
Original file line number Diff line number Diff line change
@@ -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}) }
}
}
40 changes: 40 additions & 0 deletions tests/run-with-compiler/reflect-typeChecks/test_2.scala
Original file line number Diff line number Diff line change
@@ -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
"""
)
}
}