Skip to content

Commit dde36f6

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 dde36f6

File tree

1 file changed

+51
-0
lines changed

1 file changed

+51
-0
lines changed

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ class TreeChecker extends Phase with SymTransformer {
122122
val squahsedPhase = ctx.squashed(prevPhase)
123123
ctx.echo(s"checking ${ctx.compilationUnit} after phase ${squahsedPhase}")
124124

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

454505
object TreeChecker {

0 commit comments

Comments
 (0)