Skip to content

Commit c32884d

Browse files
committed
Fix #2566: Ycheck that New node is always enclosed in a Select
Tested: Verified that the test case in #2564 triggers the assert. Verified that the check doesn't run when we ycheck a phase before frontend.
1 parent 5d9e31b commit c32884d

File tree

1 file changed

+52
-0
lines changed

1 file changed

+52
-0
lines changed

compiler/src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import ast.Trees._
2626
import ast.{tpd, untpd}
2727
import util.SourcePosition
2828
import util.Chars._
29+
2930
import collection.mutable
3031
import ProtoTypes._
3132
import config.Printers
@@ -122,6 +123,10 @@ class TreeChecker extends Phase with SymTransformer {
122123
val squahsedPhase = ctx.squashed(prevPhase)
123124
ctx.echo(s"checking ${ctx.compilationUnit} after phase ${squahsedPhase}")
124125

126+
if (ctx.isAfterTyper) {
127+
WrappedNewChecker.assertSelectWrapsNew(ctx.compilationUnit.tpdTree)(ctx)
128+
}
129+
125130
val checkingCtx = ctx
126131
.fresh
127132
.setMode(Mode.ImplicitsEnabled)
@@ -449,6 +454,53 @@ class TreeChecker extends Phase with SymTransformer {
449454
tree
450455
}
451456
}
457+
458+
/**
459+
* Checks that `New` nodes are always wrapped inside `Select` nodes.
460+
*/
461+
private object WrappedNewChecker {
462+
463+
// Current state of the search. We return as soon as we see the first improperly wrapped `New`.
464+
sealed trait State
465+
case class NotFound(parent: Option[tpd.Tree]) extends State
466+
case class Found(parent: Option[tpd.Tree], child: tpd.New) extends State
467+
468+
def assertSelectWrapsNew(tree: tpd.Tree)(implicit ctx: Context): Unit = {
469+
val detect = new TreeAccumulator[State] {
470+
override def apply(st: State, tree: tpd.Tree)(implicit ctx: Context): State = {
471+
st match {
472+
case _: Found => st // thread the found pair
473+
case NotFound(oldParent) =>
474+
tree match {
475+
case tree: tpd.New if !canWrapNew(oldParent) => Found(oldParent, tree)
476+
case _ =>
477+
// replace the parent when folding over the children
478+
foldOver(NotFound(Some(tree)), tree) match {
479+
case found: Found => found
480+
case _ => st // return the old parent so that my siblings see it
481+
}
482+
}
483+
}
484+
}
485+
}
486+
487+
detect(NotFound(None), tree) match {
488+
case Found(Some(parent), child) =>
489+
assert(assertion = false, i"`New` node must be wrapped in a `Select`:\n parent = ${parent.show}\n child = ${child.show}")
490+
case Found(None, child) =>
491+
assert(assertion = false, i"`New` node without a parent:\n ${child.show}")
492+
case _ => ()
493+
}
494+
}
495+
496+
def canWrapNew(tree: Option[tpd.Tree]): Boolean = {
497+
tree match {
498+
case Some(_: tpd.Select) => true
499+
case _ => false
500+
}
501+
}
502+
}
503+
452504
}
453505

454506
object TreeChecker {

0 commit comments

Comments
 (0)