Skip to content

Commit

Permalink
Merge pull request scalamacros#69 from xeno-by/master
Browse files Browse the repository at this point in the history
initial unit tests for the converter
  • Loading branch information
xeno-by authored Oct 12, 2016
2 parents 285521b + 4dcdb06 commit 5a38f9d
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 57 deletions.
15 changes: 13 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ lazy val sharedSettings: Seq[Def.Setting[_]] =
resolvers += Resolver.sonatypeRepo("releases"),
publishMavenStyle := true,
publishArtifact := false,
scalacOptions ++= Seq("-deprecation", "-feature"),
scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked"),
logBuffered := false,
triggeredMessage in ThisBuild := Watched.clearWhenTriggered
)
Expand Down Expand Up @@ -117,7 +117,8 @@ lazy val root = project
.aggregate(
plugin,
testsAnnotationsMeta,
testsAnnotationsReflect
testsAnnotationsReflect,
testsConverter
)

// main scala.meta paradise plugin
Expand Down Expand Up @@ -225,3 +226,13 @@ lazy val testsAnnotationsMeta = project
exposePaths("testsAnnotationsMeta", Test)
)
.dependsOn(testsCommon)

lazy val testsConverter = project
.in(file("tests/converter"))
.settings(
sharedSettings,
usePluginSettings,
testSettings,
exposePaths("testsConverter", Test)
)
.dependsOn(testsCommon, plugin)
Original file line number Diff line number Diff line change
Expand Up @@ -541,8 +541,7 @@ trait LogicalTrees { self: ReflectToolkit =>
}.get
val lprimaryctor = gprimaryctor.set(PrimaryCtorRole(l.TypeName(tree)))
val ltparams = applyBounds(tparams, lprimaryctor.vparamss)
val ltempl = l.Template(templ, l.TypeName(tree))
Some((l.Modifiers(tree), l.TypeName(tree), ltparams, lprimaryctor, ltempl))
Some((l.Modifiers(tree), l.TypeName(tree), ltparams, lprimaryctor, l.Template(tree)))
}
}

Expand All @@ -552,17 +551,15 @@ trait LogicalTrees { self: ReflectToolkit =>
if (!tree.is(TraitRole)) return None
val g.ClassDef(_, _, tparams, templ) = tree
val ltparams = applyBounds(tparams, Nil)
val ltempl = l.Template(templ, l.TypeName(tree))
Some((l.Modifiers(tree), l.TypeName(tree), tparams, g.EmptyTree, ltempl))
Some((l.Modifiers(tree), l.TypeName(tree), tparams, g.EmptyTree, l.Template(tree)))
}
}

object ObjectDef {
def unapply(tree: g.ModuleDef): Option[(List[l.Modifier], l.TermName, l.Template)] = {
if (!tree.is(ObjectRole)) return None
val g.ModuleDef(_, name, templ) = tree
val ltempl = l.Template(templ, l.TermName(tree))
Some((l.Modifiers(tree), l.TermName(tree), ltempl))
Some((l.Modifiers(tree), l.TermName(tree), l.Template(tree)))
}
}

Expand All @@ -579,9 +576,9 @@ trait LogicalTrees { self: ReflectToolkit =>
object PackageObjectDef {
def unapply(tree: g.PackageDef): Option[(List[l.Modifier], l.TermName, l.Template)] = {
if (!tree.is(PackageObjectPackageRole)) return None
val PackageObjectPackageRole(g.ModuleDef(_, _, templ)) = tree.get(PackageObjectPackageRole)
val ltempl = l.Template(templ, l.TermName(tree))
Some((Nil, ???, ltempl))
val PackageObjectPackageRole(module @ g.ModuleDef(_, _, templ)) =
tree.get(PackageObjectPackageRole)
Some((Nil, ???, l.Template(module)))
}
}

Expand Down Expand Up @@ -651,15 +648,19 @@ trait LogicalTrees { self: ReflectToolkit =>
stats: Option[List[g.Tree]])
extends Tree
object Template {
def apply(tree0: g.Template, lowner: g.Tree): l.Template = {
def containsSyntheticAnyRef(tree: g.Tree): Boolean = tree match {
case g.Select(scala: g.Ident, g.TypeName("AnyRef"))
if scala.symbol == definitions.ScalaPackage =>
true
case _ => false
def apply(tree: g.ImplDef): l.Template = {
def removeSyntheticParents(parents: List[g.Tree]): List[g.Tree] = parents match {
case List(anyRef) if anyRef.toString == "scala.AnyRef" =>
Nil
case parents :+ product :+ serializable
if tree.mods
.hasFlag(CASE) && product.toString == "scala.Product" && serializable.toString == "scala.Serializable" =>
parents
case other =>
other
}
val tree =
g.Template(tree0.parents.filterNot(containsSyntheticAnyRef), tree0.self, tree0.body)
val template =
g.Template(removeSyntheticParents(tree.impl.parents), tree.impl.self, tree.impl.body)
def indexOfFirstCtor(trees: List[g.Tree]) = trees.indexWhere {
case LowlevelCtor(_, _, _) => true; case _ => false
}
Expand All @@ -680,13 +681,14 @@ trait LogicalTrees { self: ReflectToolkit =>
None
}
}
val g.Template(parents, self, stats) = tree
val g.Template(parents, self, stats) = template
val lself = {
val result = {
if (self != noSelfType) self
else g.ValDef(g.Modifiers(), g.nme.WILDCARD, g.TypeTree(), g.EmptyTree)
}
result.set(SelfRole(lowner))
val owner = if (tree.name.isTermName) l.TermName(tree) else l.TypeName(tree)
result.set(SelfRole(owner))
}
val (rawEdefs, rest) = stats.span(isEarlyDef)
val (gvdefs, etdefs) = rawEdefs.partition(isEarlyValDef)
Expand All @@ -713,7 +715,7 @@ trait LogicalTrees { self: ReflectToolkit =>
val lparent = argss.foldLeft(applied.callee)((curr, args) => g.Apply(curr, args))
lparent.set(new SupercallRole)
}
val lstats = Some(templateStats(userDefinedStats)) // TODO: somehow guess Some vs None
val lstats = if (userDefinedStats.nonEmpty) Some(templateStats(userDefinedStats)) else None
l.Template(edefs, lparents, lself, lstats)
}
}
Expand Down
35 changes: 0 additions & 35 deletions tests/annotations-meta/src/test/scala/compile/Identity.scala

This file was deleted.

62 changes: 62 additions & 0 deletions tests/converter/src/main/scala/ConverterSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import scala.{meta => m}
import scala.tools.cmd.CommandLineParser
import scala.tools.nsc.{Global, CompilerCommand, Settings}
import scala.tools.nsc.reporters.StoreReporter
import org.scalatest._
import org.scalameta.paradise.converters.Converter

trait ConverterSuite extends FunSuite {
private lazy val g: Global = {
def fail(msg: String) = sys.error(s"ReflectToMeta initialization failed: $msg")
val classpath = System.getProperty("sbt.paths.testsConverter.test.classes")
val pluginpath = System.getProperty("sbt.paths.plugin.jar")
val options = "-cp " + classpath + " -Xplugin:" + pluginpath + ":" + classpath + " -Xplugin-require:macroparadise"
val args = CommandLineParser.tokenize(options)
val emptySettings = new Settings(error => fail(s"couldn't apply settings because $error"))
val reporter = new StoreReporter()
val command = new CompilerCommand(args, emptySettings)
val settings = command.settings
val g = new Global(settings, reporter)
val run = new g.Run
g.phase = run.parserPhase
g.globalPhase = run.parserPhase
g
}

def syntactic(code: String) {
test(code.trim) {
val parsedScalacTree: g.Tree = {
import g._
val reporter = new StoreReporter()
g.reporter = reporter
val tree = gen.mkTreeOrBlock(newUnitParser(code, "<toolbox>").parseStatsOrPackages())
val errors = reporter.infos.filter(_.severity == g.reporter.ERROR)
errors.foreach(error => fail(s"scalac parse error: ${error.msg} at ${error.pos}"))
tree
}

val parsedMetaTree: m.Stat = {
import scala.meta._
code.parse[m.Stat] match {
case scala.meta.parsers.Parsed.Success(tree) => tree
case scala.meta.parsers.Parsed.Error(pos, message, _) =>
fail(s"meta parse error: $pos at $message")
}
}
val convertedMetaTree: m.Stat = {
object converter extends Converter {
lazy val global: ConverterSuite.this.g.type = ConverterSuite.this.g
def apply(gtree: g.Tree): m.Stat = gtree.toMtree[m.Stat]
}
converter(parsedScalacTree)
}

// TODO: account for the fact that scala.reflect desugars stuff (e.g. for loops) even during parsing
// TODO: alternatively, we can just go ahead and undesugar for loops, because for syntactic APIs that's actually easy
if (parsedMetaTree.structure != convertedMetaTree.structure) {
fail(
s"scala -> meta converter error\nparsed tree:\n${parsedMetaTree.structure}\nconverted tree\n${convertedMetaTree.structure}")
}
}
}
}
12 changes: 12 additions & 0 deletions tests/converter/src/test/scala/Syntactic.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class Syntactic extends ConverterSuite {
syntactic("case class C()")
syntactic("object M { override val toString = test5 }")
syntactic("foo(named = arg)")
syntactic("""
1 match {
case 0 | 1 => true
case (2 | 3 | 4 | 5) => false
}
""")
syntactic("def add(a: Int)(implicit z: Int = 0) = a.+(z)")
}

0 comments on commit 5a38f9d

Please sign in to comment.