From 370eb78f6676c10da429f9e3b0b0f7789b7e800b Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 14 Oct 2016 12:11:31 +0300 Subject: [PATCH 1/4] initial structural tree comparer modulo desugarings This only covers one case now, but that's a start. For motivation, see https://github.com/scalameta/paradise/pull/69#issuecomment-253209271. --- .../src/main/scala/ConverterSuite.scala | 51 ++++++++++++++++--- .../converter/src/test/scala/Syntactic.scala | 2 +- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/tests/converter/src/main/scala/ConverterSuite.scala b/tests/converter/src/main/scala/ConverterSuite.scala index c88d196f..4c086248 100644 --- a/tests/converter/src/main/scala/ConverterSuite.scala +++ b/tests/converter/src/main/scala/ConverterSuite.scala @@ -23,7 +23,43 @@ trait ConverterSuite extends FunSuite { g } - def syntactic(code: String) { + case class MismatchException(details: String) extends Exception + private def checkMismatchesModuloDesugarings(parsed: m.Tree, converted: m.Tree): Unit = { + import scala.meta._ + def loop(x: Any, y: Any): Boolean = { + val ok = (x, y) match { + case (x, y) if x == null || y == null => + x == null && y == null + case (x: Some[_], y: Some[_]) => + loop(x.get, y.get) + case (x: None.type, y: None.type) => + true + case (xs: Seq[_], ys: Seq[_]) => + xs.length == ys.length && xs.zip(ys).forall { case (x, y) => loop(x, y) } + case (x: Tree, y: Tree) => + def sameStructure = + x.productPrefix == y.productPrefix && loop(x.productIterator.toList, + y.productIterator.toList) + def sameDesugaring = (x, y) match { + // TODO: it would be possible to unify these two cases if we could say `q"$ylhs.$yop[...$ytargss](..$yargs)"` + case (q"$xlhs $xop (..$xargs)", q"$ylhs.$yop(..$yargs)") => + loop(xlhs, ylhs) && loop(xop, yop) && loop(xargs, yargs) + case (q"$xlhs $xop [..$xtargs] (..$xargs)", q"$ylhs.$yop[..$ytargs](..$yargs)") => + loop(xlhs, ylhs) && loop(xop, yop) && loop(xtargs, ytargs) && loop(xargs, yargs) + case _ => + false + } + sameStructure || sameDesugaring + case _ => + x == y + } + if (!ok) throw MismatchException(s"$x != $y") + else true + } + loop(parsed, converted) + } + + def syntactic(code: String): Unit = { test(code.trim) { val parsedScalacTree: g.Tree = { import g._ @@ -51,11 +87,14 @@ trait ConverterSuite extends FunSuite { 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}") + try { + checkMismatchesModuloDesugarings(parsedMetaTree, convertedMetaTree) + } catch { + case MismatchException(details) => + val header = s"scala -> meta converter error\n$details" + val fullDetails = + s"parsed tree:\n${parsedMetaTree.structure}\nconverted tree:\n${convertedMetaTree.structure}" + fail(s"$header\n$fullDetails") } } } diff --git a/tests/converter/src/test/scala/Syntactic.scala b/tests/converter/src/test/scala/Syntactic.scala index 49fe9709..4f55044e 100644 --- a/tests/converter/src/test/scala/Syntactic.scala +++ b/tests/converter/src/test/scala/Syntactic.scala @@ -8,5 +8,5 @@ class Syntactic extends ConverterSuite { case (2 | 3 | 4 | 5) => false } """) - syntactic("def add(a: Int)(implicit z: Int = 0) = a.+(z)") + syntactic("def add(a: Int)(implicit z: Int = 0) = a + z") } \ No newline at end of file From 1905875a7232f7ea15534199e4e6feea198b03c9 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 14 Oct 2016 13:02:34 +0300 Subject: [PATCH 2/4] import term tests from the Eden converter suite --- .../paradise/converters/ToMtree.scala | 41 +++++++-- .../paradise/reflect/LogicalTrees.scala | 66 +++++++++++++- .../src/main/scala/ConverterSuite.scala | 55 +++++++++--- .../converter/src/test/scala/Syntactic.scala | 87 +++++++++++++++++++ 4 files changed, 231 insertions(+), 18 deletions(-) diff --git a/plugin/src/main/scala/org/scalameta/paradise/converters/ToMtree.scala b/plugin/src/main/scala/org/scalameta/paradise/converters/ToMtree.scala index cc4a2848..5d4bf631 100644 --- a/plugin/src/main/scala/org/scalameta/paradise/converters/ToMtree.scala +++ b/plugin/src/main/scala/org/scalameta/paradise/converters/ToMtree.scala @@ -48,6 +48,11 @@ trait ToMtree { self: Converter => val mname = lname.toMtree[m.Name.Qualifier] m.Term.This(mname) + case l.TermSuper(lthis, lsuper) => + val mthis = lthis.toMtree[m.Name.Qualifier] + val msuper = lsuper.toMtree[m.Name.Qualifier] + m.Term.Super(mthis, msuper) + case l.TermName(lvalue) => m.Term.Name(lvalue) @@ -74,6 +79,14 @@ trait ToMtree { self: Converter => val mrhs = lrhs.toMtree[m.Term] m.Term.Assign(mlhs, mrhs) + case l.TermReturn(lexpr) => + val mexpr = lexpr.toMtree[m.Term] + m.Term.Return(mexpr) + + case l.TermThrow(lexpr) => + val mexpr = lexpr.toMtree[m.Term] + m.Term.Throw(mexpr) + case l.TermBlock(lstats) => val mstats = lstats.toMtrees[m.Stat] m.Term.Block(mstats) @@ -89,15 +102,26 @@ trait ToMtree { self: Converter => val mcases = lcases.toMtrees[m.Case] m.Term.Match(mscrut, mcases) + case l.TermTryWithCases(lexpr, lcatches, lfinally) => + val mexpr = lexpr.toMtree[m.Term] + val mcatches = lcatches.toMtrees[m.Case] + val mfinally = lfinally.toMtreeopt[m.Term] + m.Term.TryWithCases(mexpr, mcatches, mfinally) + case l.TermFunction(lparams, lbody) => val mparams = lparams.toMtrees[m.Term.Param] val mbody = lbody.toMtree[m.Term] m.Term.Function(mparams, mbody) - case l.TermWhile(lcond, lbody) => - val mcond = lcond.toMtree[m.Term] + case l.TermWhile(lexpr, lbody) => + val mexpr = lexpr.toMtree[m.Term] val mbody = lbody.toMtree[m.Term] - m.Term.While(mcond, mbody) + m.Term.While(mexpr, mbody) + + case l.TermDo(lbody, lexpr) => + val mbody = lbody.toMtree[m.Term] + val mexpr = lexpr.toMtree[m.Term] + m.Term.Do(mbody, mexpr) case l.TermNew(ltempl) => val mtempl = ltempl.toMtree[m.Template] @@ -127,10 +151,15 @@ trait ToMtree { self: Converter => case l.TypeIdent(lname) => lname.toMtree[m.Type.Name] - case l.TypeSelect(lpre, lname) => - val mpre = lpre.toMtree[m.Term.Ref] + case l.TypeSelect(lqual, lname) => + val mqual = lqual.toMtree[m.Term.Ref] + val mname = lname.toMtree[m.Type.Name] + m.Type.Select(mqual, mname) + + case l.TypeProject(lqual, lname) => + val mqual = lqual.toMtree[m.Type] val mname = lname.toMtree[m.Type.Name] - m.Type.Select(mpre, mname) + m.Type.Project(mqual, mname) case l.TypeApply(ltpt, largs) => val mtpt = ltpt.toMtree[m.Type] diff --git a/plugin/src/main/scala/org/scalameta/paradise/reflect/LogicalTrees.scala b/plugin/src/main/scala/org/scalameta/paradise/reflect/LogicalTrees.scala index 882344c0..b5e6d092 100644 --- a/plugin/src/main/scala/org/scalameta/paradise/reflect/LogicalTrees.scala +++ b/plugin/src/main/scala/org/scalameta/paradise/reflect/LogicalTrees.scala @@ -149,13 +149,23 @@ trait LogicalTrees { self: ReflectToolkit => // ============ TERMS ============ object TermThis { - // qual def unapply(tree: g.This): Option[l.QualifierName] = { if (tree.qual == tpnme.EMPTY) Some(l.AnonymousName()) else Some(l.IndeterminateName(tree.displayName)) } } + object TermSuper { + def unapply(tree: g.Super): Option[(l.QualifierName, l.QualifierName)] = { + val g.Super(l.TermThis(lthis), qual) = tree + val lsuper = { + if (qual == tpnme.EMPTY) l.AnonymousName() + else l.IndeterminateName(qual.displayName) + } + Some((lthis, lsuper)) + } + } + case class TermName(value: String) extends Name with TermParamName object TermName { def apply(tree: g.NameTree): l.TermName = { @@ -186,6 +196,7 @@ trait LogicalTrees { self: ReflectToolkit => object TermApply { def unapply(tree: g.Apply): Option[(g.Tree, List[g.Tree])] = { if (!tree.is(TermLoc)) return None + if (TermNew.unapply(tree).isDefined) return None Some((tree.fun, tree.args)) } } @@ -202,8 +213,21 @@ trait LogicalTrees { self: ReflectToolkit => } } + object TermReturn { + def unapply(tree: g.Return): Option[g.Tree] = { + Some(tree.expr) + } + } + + object TermThrow { + def unapply(tree: g.Throw): Option[g.Tree] = { + Some(tree.expr) + } + } + object TermBlock { def unapply(tree: g.Block): Option[List[g.Tree]] = { + if (TermNew.unapply(tree).isDefined) return None val lstats = blockStats(tree.stats :+ tree.expr) Some(lstats) } @@ -221,6 +245,13 @@ trait LogicalTrees { self: ReflectToolkit => } } + object TermTryWithCases { + def unapply(tree: g.Try): Option[(g.Tree, List[g.Tree], Option[g.Tree])] = { + val lfinallyp = if (tree.finalizer != g.EmptyTree) Some(tree.finalizer) else None + Some((tree.block, tree.catches, lfinallyp)) + } + } + object TermFunction { def unapply(tree: g.Function): Option[(List[g.Tree], g.Tree)] = { val g.Function(params, body) = tree @@ -245,6 +276,24 @@ trait LogicalTrees { self: ReflectToolkit => } } + object TermDo { + def unapply(tree: g.LabelDef): Option[(g.Tree, g.Tree)] = { + tree match { + case g.LabelDef(name1, + Nil, + g.Block( + List(body), + g.If(cond, + g.Apply(Ident(name2), Nil), + g.Literal(g.Constant(()))))) + if name1 == name2 && name1.startsWith(nme.DO_WHILE_PREFIX) => + Some((body, cond)) + case _ => + None + } + } + } + object TermNew { def unapply(tree: g.Tree): Option[l.Template] = tree match { case g.Apply(g.Select(g.New(tpt), nme.CONSTRUCTOR), args) => @@ -253,6 +302,11 @@ trait LogicalTrees { self: ReflectToolkit => g.ValDef(g.Modifiers(), g.nme.WILDCARD, g.TypeTree(), g.EmptyTree) .set(SelfRole(g.EmptyTree)) Some(l.Template(Nil, List(lparent), lself, None)) + case g.Block( + List(tree @ g.ClassDef(g.Modifiers(FINAL, g.tpnme.EMPTY, Nil), g.TypeName(anon1), Nil, templ)), + g.Apply(g.Select(g.New(g.Ident(g.TypeName(anon2))), nme.CONSTRUCTOR), args)) + if anon1 == tpnme.ANON_CLASS_NAME.toString && anon2 == tpnme.ANON_CLASS_NAME.toString => + Some(l.Template(tree)) case _ => None } @@ -319,6 +373,13 @@ trait LogicalTrees { self: ReflectToolkit => } } + object TypeProject { + def unapply(tree: g.SelectFromTypeTree): Option[(g.Tree, l.TypeName)] = { + val g.SelectFromTypeTree(qual, name) = tree + Some((qual, l.TypeName(tree))) + } + } + object TypeApply { def unapply(tree: g.AppliedTypeTree): Option[(g.Tree, List[g.Tree])] = { Some((tree.tpt, tree.args)) @@ -1008,6 +1069,7 @@ trait LogicalTrees { self: ReflectToolkit => seenPrimaryCtor = true // skip this case g.DefDef(_, nme.MIXIN_CONSTRUCTOR, _, _, _, _) => // and this case g.ValDef(mods, _, _, _) if mods.hasFlag(PARAMACCESSOR) => // and this + case g.EmptyTree => // and this case _ => lresult += stat } } @@ -1015,7 +1077,7 @@ trait LogicalTrees { self: ReflectToolkit => } private def blockStats(stats: List[g.Tree]): List[g.Tree] = { - stats + stats.filter(_ != g.EmptyTree) } } diff --git a/tests/converter/src/main/scala/ConverterSuite.scala b/tests/converter/src/main/scala/ConverterSuite.scala index 4c086248..1265bc8a 100644 --- a/tests/converter/src/main/scala/ConverterSuite.scala +++ b/tests/converter/src/main/scala/ConverterSuite.scala @@ -1,3 +1,4 @@ +import scala.collection.immutable.Seq import scala.{meta => m} import scala.tools.cmd.CommandLineParser import scala.tools.nsc.{Global, CompilerCommand, Settings} @@ -37,19 +38,53 @@ trait ConverterSuite extends FunSuite { case (xs: Seq[_], ys: Seq[_]) => xs.length == ys.length && xs.zip(ys).forall { case (x, y) => loop(x, y) } case (x: Tree, y: Tree) => + def sameDesugaring = { + // NOTE: Workaround for https://github.com/scalameta/scalameta/issues/519. + object TermApply519 { + def unapply(tree: Tree): Option[(Term, Seq[Seq[Type.Arg]], Seq[Seq[Term.Arg]])] = tree match { + case q"$fun[..$targs](...$argss)" => Some((fun, Seq(targs), argss)) + case q"$fun(...$argss)" => Some((fun, Nil, argss)) + case _ => None + } + } + + // NOTE: This is a desugaring performed by the scala.reflect parser. + // We may want to undo it in the converter. + object TermApplyInfixRightAssoc { + def unapply(tree: Tree): Option[(Term, Term.Name, Seq[Type.Arg], Seq[Term.Arg])] = tree match { + case q"{ val $tmp1 = $lhs; ${TermApply519(q"$rhs.$op", targss, Seq(Seq(tmp2)))} }" + if tmp1.syntax == tmp2.syntax && tmp1.syntax.contains("$") => + val args = rhs match { + case q"$tuple(..$args)" if tuple.syntax.startsWith("scala.Tuple") => args + case arg => Seq(arg) + } + Some((lhs, op, targss.flatten, args)) + case _ => + None + } + } + + (x, y) match { + case (q"$xlhs $xop [..$xtargs] (..$xargs)", TermApply519(q"$ylhs.$yop", ytargss, Seq(yargs))) => + loop(xlhs, ylhs) && loop(xop, yop) && loop(xtargs, ytargss.flatten) && loop(xargs, yargs) + case (q"$xlhs $xop [..$xtargs] (..$xargs)", TermApplyInfixRightAssoc(ylhs, yop, ytargs, yargs)) => + loop(xlhs, ylhs) && loop(xop, yop) && loop(xtargs, ytargs) && loop(xargs, yargs) + case (q"{}", q"()") => + true + case (q"{ $xstat }", q"$ystat") => + loop(xstat, ystat) + case (q"(..$xargs)", q"$tuple(..$yargs)") if tuple.syntax.startsWith("scala.Tuple") => + loop(xargs, yargs) + case (ctor"$xctor(...${Seq()})", ctor"$yctor(...${Seq(Seq())})") => + loop(xctor, yctor) + case _ => + false + } + } def sameStructure = x.productPrefix == y.productPrefix && loop(x.productIterator.toList, y.productIterator.toList) - def sameDesugaring = (x, y) match { - // TODO: it would be possible to unify these two cases if we could say `q"$ylhs.$yop[...$ytargss](..$yargs)"` - case (q"$xlhs $xop (..$xargs)", q"$ylhs.$yop(..$yargs)") => - loop(xlhs, ylhs) && loop(xop, yop) && loop(xargs, yargs) - case (q"$xlhs $xop [..$xtargs] (..$xargs)", q"$ylhs.$yop[..$ytargs](..$yargs)") => - loop(xlhs, ylhs) && loop(xop, yop) && loop(xtargs, ytargs) && loop(xargs, yargs) - case _ => - false - } - sameStructure || sameDesugaring + sameDesugaring || sameStructure case _ => x == y } diff --git a/tests/converter/src/test/scala/Syntactic.scala b/tests/converter/src/test/scala/Syntactic.scala index 4f55044e..7bdd64f8 100644 --- a/tests/converter/src/test/scala/Syntactic.scala +++ b/tests/converter/src/test/scala/Syntactic.scala @@ -1,4 +1,91 @@ +// NOTE: a lot of these tests are taken from https://github.com/liufengyun/eden/blob/master/src/test/scala/dotty/eden/UntpdSuite.scala + class Syntactic extends ConverterSuite { + // terms + syntactic("null") + syntactic("""println("hello, world")""") + syntactic("println(42)") + syntactic("f(this)") + syntactic("f(A.this)") + syntactic("this.age") + syntactic("C.this.age") + syntactic("super.age") + syntactic("super[A].age") + syntactic("C.super[A].age") + syntactic("f[Int](3)") + syntactic("f(x = 3)") + syntactic("f(x = 3, y = 6 * 8)") + syntactic("f(x:_*)") + syntactic("a.f(this.age)") + syntactic("a + b") + syntactic("a + b + c + this.age") + syntactic("a :+ b") + syntactic("a :+ (b, c)") + syntactic("a :+[Int] b") + syntactic("a :+[Int] (b, c)") + syntactic("a +: b") + syntactic("a +: (b, c)") + syntactic("a +:[Int] b") + syntactic("a +:[Int] (b, c)") + syntactic("a*") + syntactic("++a") + syntactic("a++") + syntactic("a = b") + syntactic("{ a = 1; b += 2 }") + syntactic("{ }") + syntactic("()") + syntactic("(2)") + syntactic("(2, 4)") + syntactic("a -> b") + syntactic("if (cond) a else b") + syntactic("if (cond) return a") + syntactic("while (a > 5) { println(a); a++; }") + syntactic("do { println(a); a++; } while (a > 5)") + syntactic("return a") + syntactic("new List(5)") + syntactic("new List[Int](5)") + syntactic("new List[List[Int]](List(5))") + syntactic("new Map[Int, String]") + syntactic("new Map[Int, String]()") + syntactic("new Map[Int, String](a -> b)") + syntactic("new B") + syntactic("new B()") + syntactic("new c.B") + syntactic("new C#B") + syntactic("new o.C#B") + syntactic("new B { }") + syntactic("new B { val a = 3 }") + syntactic("new B { def f(x: Int): Int = x*x }") + syntactic("new B(3) { println(5); def f(x: Int): Int = x*x }") + syntactic("throw new A(4)") + syntactic("try { throw new A(4) } catch { case _: Throwable => 4 } finally { println(6) }") + syntactic("try f(4) catch { case _: Throwable => 4 } finally println(6)") + syntactic("try f(4) catch { case _: Throwable => 4 }") + syntactic("try f(4) finally println(6)") + syntactic("try {} finally println(6)") + // TODO: https://github.com/scalameta/paradise/issues/75 + // syntactic("try foo catch bar") + // TODO: https://github.com/scalameta/paradise/issues/74 + // syntactic("for (arg <- args) result += arg * arg") + // syntactic("for (arg <- args; double = arg * 2) result += arg * arg") + // syntactic(""" + // for { i<-1 until n + // j <- 1 until i + // if isPrime(i+j) } yield (i, j) + // """) + // syntactic(""" + // for { i<-1 until n + // j <- 1 until i + // k = i + j + // if isPrime(i+j) } yield (i, j) + // """) + + // interpolation + // TODO: https://github.com/scalameta/paradise/issues/76 + // syntactic("""s"hello, $world"""") + // syntactic("""s"hello, $world, ${1 + 2}"""") + + // random stuff syntactic("case class C()") syntactic("object M { override val toString = test5 }") syntactic("foo(named = arg)") From 43528eb11197ec0186a7199ee932ebd50e4daf62 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 14 Oct 2016 15:53:00 +0300 Subject: [PATCH 3/4] import pattern tests from the Eden converter suite --- .../org/scalameta/paradise/reflect/LogicalTrees.scala | 2 +- tests/converter/src/main/scala/ConverterSuite.scala | 4 ++++ tests/converter/src/test/scala/Syntactic.scala | 11 +++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/plugin/src/main/scala/org/scalameta/paradise/reflect/LogicalTrees.scala b/plugin/src/main/scala/org/scalameta/paradise/reflect/LogicalTrees.scala index b5e6d092..3d389383 100644 --- a/plugin/src/main/scala/org/scalameta/paradise/reflect/LogicalTrees.scala +++ b/plugin/src/main/scala/org/scalameta/paradise/reflect/LogicalTrees.scala @@ -459,7 +459,7 @@ trait LogicalTrees { self: ReflectToolkit => case head :: Nil => head case trees @ (head :: tail) => g.Alternative(trees) } - Some((llhs, lrhs)) + Some((llhs.set(PatLoc), lrhs.set(PatLoc))) } } diff --git a/tests/converter/src/main/scala/ConverterSuite.scala b/tests/converter/src/main/scala/ConverterSuite.scala index 1265bc8a..1cf304e2 100644 --- a/tests/converter/src/main/scala/ConverterSuite.scala +++ b/tests/converter/src/main/scala/ConverterSuite.scala @@ -77,6 +77,10 @@ trait ConverterSuite extends FunSuite { loop(xargs, yargs) case (ctor"$xctor(...${Seq()})", ctor"$yctor(...${Seq(Seq())})") => loop(xctor, yctor) + case (xpat, p"$ypat @ _") => + loop(xpat, ypat) + case (p"$xlhs: $xtpe", p"$ylhs @ (_: $ytpe)") => + loop(xlhs, ylhs) case _ => false } diff --git a/tests/converter/src/test/scala/Syntactic.scala b/tests/converter/src/test/scala/Syntactic.scala index 7bdd64f8..70f148f5 100644 --- a/tests/converter/src/test/scala/Syntactic.scala +++ b/tests/converter/src/test/scala/Syntactic.scala @@ -85,6 +85,17 @@ class Syntactic extends ConverterSuite { // syntactic("""s"hello, $world"""") // syntactic("""s"hello, $world, ${1 + 2}"""") + // patterns + syntactic("a match { case 5 => ; case 6 => }") + syntactic("a match { case Some(x) => x; case None => y }") + syntactic("a match { case Some(x) => x; case _ => y }") + syntactic("a match { case m @ Some(x) => x; case _ => y }") + syntactic("a match { case m @ Some(t @ Some(x)) => x; case _ => y }") + syntactic("a match { case m : Int => x; case _ => y }") + syntactic("a match { case Some(x: Int) | Some(x: String) => x; case _ => y }") + syntactic("a match { case Some(x: Int) | Some(x: String) | Some(x: Boolean) => x; case _ => y }") + syntactic("a match { case Some(x: Int) | Some(x: String) | x: Boolean => x; case _ => y }") + // random stuff syntactic("case class C()") syntactic("object M { override val toString = test5 }") From b3bc7cfd7f85a3b0f77d6fcd99757a4d1b1c8d6f Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 14 Oct 2016 16:03:35 +0300 Subject: [PATCH 4/4] reformat new code --- .../paradise/converters/ToMtree.scala | 4 +- .../paradise/reflect/LogicalTrees.scala | 12 ++--- .../src/main/scala/ConverterSuite.scala | 48 +++++++++++-------- 3 files changed, 35 insertions(+), 29 deletions(-) diff --git a/plugin/src/main/scala/org/scalameta/paradise/converters/ToMtree.scala b/plugin/src/main/scala/org/scalameta/paradise/converters/ToMtree.scala index 5d4bf631..6848fe86 100644 --- a/plugin/src/main/scala/org/scalameta/paradise/converters/ToMtree.scala +++ b/plugin/src/main/scala/org/scalameta/paradise/converters/ToMtree.scala @@ -49,7 +49,7 @@ trait ToMtree { self: Converter => m.Term.This(mname) case l.TermSuper(lthis, lsuper) => - val mthis = lthis.toMtree[m.Name.Qualifier] + val mthis = lthis.toMtree[m.Name.Qualifier] val msuper = lsuper.toMtree[m.Name.Qualifier] m.Term.Super(mthis, msuper) @@ -103,7 +103,7 @@ trait ToMtree { self: Converter => m.Term.Match(mscrut, mcases) case l.TermTryWithCases(lexpr, lcatches, lfinally) => - val mexpr = lexpr.toMtree[m.Term] + val mexpr = lexpr.toMtree[m.Term] val mcatches = lcatches.toMtrees[m.Case] val mfinally = lfinally.toMtreeopt[m.Term] m.Term.TryWithCases(mexpr, mcatches, mfinally) diff --git a/plugin/src/main/scala/org/scalameta/paradise/reflect/LogicalTrees.scala b/plugin/src/main/scala/org/scalameta/paradise/reflect/LogicalTrees.scala index 3d389383..d951b564 100644 --- a/plugin/src/main/scala/org/scalameta/paradise/reflect/LogicalTrees.scala +++ b/plugin/src/main/scala/org/scalameta/paradise/reflect/LogicalTrees.scala @@ -283,9 +283,7 @@ trait LogicalTrees { self: ReflectToolkit => Nil, g.Block( List(body), - g.If(cond, - g.Apply(Ident(name2), Nil), - g.Literal(g.Constant(()))))) + g.If(cond, g.Apply(Ident(name2), Nil), g.Literal(g.Constant(()))))) if name1 == name2 && name1.startsWith(nme.DO_WHILE_PREFIX) => Some((body, cond)) case _ => @@ -303,9 +301,11 @@ trait LogicalTrees { self: ReflectToolkit => .set(SelfRole(g.EmptyTree)) Some(l.Template(Nil, List(lparent), lself, None)) case g.Block( - List(tree @ g.ClassDef(g.Modifiers(FINAL, g.tpnme.EMPTY, Nil), g.TypeName(anon1), Nil, templ)), - g.Apply(g.Select(g.New(g.Ident(g.TypeName(anon2))), nme.CONSTRUCTOR), args)) - if anon1 == tpnme.ANON_CLASS_NAME.toString && anon2 == tpnme.ANON_CLASS_NAME.toString => + List( + tree @ g + .ClassDef(g.Modifiers(FINAL, g.tpnme.EMPTY, Nil), g.TypeName(anon1), Nil, templ)), + g.Apply(g.Select(g.New(g.Ident(g.TypeName(anon2))), nme.CONSTRUCTOR), args)) + if anon1 == tpnme.ANON_CLASS_NAME.toString && anon2 == tpnme.ANON_CLASS_NAME.toString => Some(l.Template(tree)) case _ => None diff --git a/tests/converter/src/main/scala/ConverterSuite.scala b/tests/converter/src/main/scala/ConverterSuite.scala index 1cf304e2..c452c10f 100644 --- a/tests/converter/src/main/scala/ConverterSuite.scala +++ b/tests/converter/src/main/scala/ConverterSuite.scala @@ -41,41 +41,47 @@ trait ConverterSuite extends FunSuite { def sameDesugaring = { // NOTE: Workaround for https://github.com/scalameta/scalameta/issues/519. object TermApply519 { - def unapply(tree: Tree): Option[(Term, Seq[Seq[Type.Arg]], Seq[Seq[Term.Arg]])] = tree match { - case q"$fun[..$targs](...$argss)" => Some((fun, Seq(targs), argss)) - case q"$fun(...$argss)" => Some((fun, Nil, argss)) - case _ => None - } + def unapply(tree: Tree): Option[(Term, Seq[Seq[Type.Arg]], Seq[Seq[Term.Arg]])] = + tree match { + case q"$fun[..$targs](...$argss)" => Some((fun, Seq(targs), argss)) + case q"$fun(...$argss)" => Some((fun, Nil, argss)) + case _ => None + } } // NOTE: This is a desugaring performed by the scala.reflect parser. // We may want to undo it in the converter. object TermApplyInfixRightAssoc { - def unapply(tree: Tree): Option[(Term, Term.Name, Seq[Type.Arg], Seq[Term.Arg])] = tree match { - case q"{ val $tmp1 = $lhs; ${TermApply519(q"$rhs.$op", targss, Seq(Seq(tmp2)))} }" - if tmp1.syntax == tmp2.syntax && tmp1.syntax.contains("$") => - val args = rhs match { - case q"$tuple(..$args)" if tuple.syntax.startsWith("scala.Tuple") => args - case arg => Seq(arg) - } - Some((lhs, op, targss.flatten, args)) - case _ => - None - } + def unapply(tree: Tree): Option[(Term, Term.Name, Seq[Type.Arg], Seq[Term.Arg])] = + tree match { + case q"{ val $tmp1 = $lhs; ${ TermApply519(q"$rhs.$op", targss, Seq(Seq(tmp2))) } }" + if tmp1.syntax == tmp2.syntax && tmp1.syntax.contains("$") => + val args = rhs match { + case q"$tuple(..$args)" if tuple.syntax.startsWith("scala.Tuple") => args + case arg => Seq(arg) + } + Some((lhs, op, targss.flatten, args)) + case _ => + None + } } (x, y) match { - case (q"$xlhs $xop [..$xtargs] (..$xargs)", TermApply519(q"$ylhs.$yop", ytargss, Seq(yargs))) => - loop(xlhs, ylhs) && loop(xop, yop) && loop(xtargs, ytargss.flatten) && loop(xargs, yargs) - case (q"$xlhs $xop [..$xtargs] (..$xargs)", TermApplyInfixRightAssoc(ylhs, yop, ytargs, yargs)) => + case (q"$xlhs $xop [..$xtargs] (..$xargs)", + TermApply519(q"$ylhs.$yop", ytargss, Seq(yargs))) => + loop(xlhs, ylhs) && loop(xop, yop) && loop(xtargs, ytargss.flatten) && loop(xargs, + yargs) + case (q"$xlhs $xop [..$xtargs] (..$xargs)", + TermApplyInfixRightAssoc(ylhs, yop, ytargs, yargs)) => loop(xlhs, ylhs) && loop(xop, yop) && loop(xtargs, ytargs) && loop(xargs, yargs) case (q"{}", q"()") => true case (q"{ $xstat }", q"$ystat") => loop(xstat, ystat) - case (q"(..$xargs)", q"$tuple(..$yargs)") if tuple.syntax.startsWith("scala.Tuple") => + case (q"(..$xargs)", q"$tuple(..$yargs)") + if tuple.syntax.startsWith("scala.Tuple") => loop(xargs, yargs) - case (ctor"$xctor(...${Seq()})", ctor"$yctor(...${Seq(Seq())})") => + case (ctor"$xctor(...${ Seq() })", ctor"$yctor(...${ Seq(Seq()) })") => loop(xctor, yctor) case (xpat, p"$ypat @ _") => loop(xpat, ypat)