@@ -42,10 +42,6 @@ class TreeChecker extends Phase with SymTransformer {
42
42
private val seenClasses = collection.mutable.HashMap [String , Symbol ]()
43
43
private val seenModuleVals = collection.mutable.HashMap [String , Symbol ]()
44
44
45
- def isValidJVMName (name : Name ): Boolean = name.toString.forall(isValidJVMChar)
46
-
47
- def isValidJVMMethodName (name : Name ): Boolean = name.toString.forall(isValidJVMMethodChar)
48
-
49
45
val NoSuperClassFlags : FlagSet = Trait | Package
50
46
51
47
def testDuplicate (sym : Symbol , registry : mutable.Map [String , Symbol ], typ : String )(using Context ): Unit = {
@@ -109,18 +105,6 @@ class TreeChecker extends Phase with SymTransformer {
109
105
else if (ctx.phase.prev.isCheckable)
110
106
check(ctx.base.allPhases.toIndexedSeq, ctx)
111
107
112
- private def previousPhases (phases : List [Phase ])(using Context ): List [Phase ] = phases match {
113
- case (phase : MegaPhase ) :: phases1 =>
114
- val subPhases = phase.miniPhases
115
- val previousSubPhases = previousPhases(subPhases.toList)
116
- if (previousSubPhases.length == subPhases.length) previousSubPhases ::: previousPhases(phases1)
117
- else previousSubPhases
118
- case phase :: phases1 if phase ne ctx.phase =>
119
- phase :: previousPhases(phases1)
120
- case _ =>
121
- Nil
122
- }
123
-
124
108
def check (phasesToRun : Seq [Phase ], ctx : Context ): Tree = {
125
109
val fusedPhase = ctx.phase.prevMega(using ctx)
126
110
report.echo(s " checking ${ctx.compilationUnit} after phase ${fusedPhase}" )(using ctx)
@@ -134,7 +118,6 @@ class TreeChecker extends Phase with SymTransformer {
134
118
135
119
val checkingCtx = ctx
136
120
.fresh
137
- .addMode(Mode .ImplicitsEnabled )
138
121
.setReporter(new ThrowingReporter (ctx.reporter))
139
122
140
123
val checker = inContext(ctx) {
@@ -150,9 +133,80 @@ class TreeChecker extends Phase with SymTransformer {
150
133
}
151
134
}
152
135
136
+ /**
137
+ * Checks that `New` nodes are always wrapped inside `Select` nodes.
138
+ */
139
+ def assertSelectWrapsNew (tree : Tree )(using Context ): Unit =
140
+ (new TreeAccumulator [tpd.Tree ] {
141
+ override def apply (parent : Tree , tree : Tree )(using Context ): Tree = {
142
+ tree match {
143
+ case tree : New if ! parent.isInstanceOf [tpd.Select ] =>
144
+ assert(assertion = false , i " `New` node must be wrapped in a `Select`: \n parent = ${parent.show}\n child = ${tree.show}" )
145
+ case _ : Annotated =>
146
+ // Don't check inside annotations, since they're allowed to contain
147
+ // somewhat invalid trees.
148
+ case _ =>
149
+ foldOver(tree, tree) // replace the parent when folding over the children
150
+ }
151
+ parent // return the old parent so that my siblings see it
152
+ }
153
+ })(tpd.EmptyTree , tree)
154
+ }
155
+
156
+ object TreeChecker {
157
+ /** - Check that TypeParamRefs and MethodParams refer to an enclosing type.
158
+ * - Check that all type variables are instantiated.
159
+ */
160
+ def checkNoOrphans (tp0 : Type , tree : untpd.Tree = untpd.EmptyTree )(using Context ): Type = new TypeMap () {
161
+ val definedBinders = new java.util.IdentityHashMap [Type , Any ]
162
+ def apply (tp : Type ): Type = {
163
+ tp match {
164
+ case tp : BindingType =>
165
+ definedBinders.put(tp, tp)
166
+ mapOver(tp)
167
+ definedBinders.remove(tp)
168
+ case tp : ParamRef =>
169
+ assert(definedBinders.get(tp.binder) != null , s " orphan param: ${tp.show}, hash of binder = ${System .identityHashCode(tp.binder)}, tree = ${tree.show}, type = $tp0" )
170
+ case tp : TypeVar =>
171
+ assert(tp.isInstantiated, s " Uninstantiated type variable: ${tp.show}, tree = ${tree.show}" )
172
+ apply(tp.underlying)
173
+ case _ =>
174
+ mapOver(tp)
175
+ }
176
+ tp
177
+ }
178
+ }.apply(tp0)
179
+
180
+ /** Run some additional checks on the nodes of the trees. Specifically:
181
+ *
182
+ * - TypeTree can only appear in TypeApply args, New, Typed tpt, Closure
183
+ * tpt, SeqLiteral elemtpt, ValDef tpt, DefDef tpt, and TypeDef rhs.
184
+ */
185
+ object TreeNodeChecker extends untpd.TreeTraverser :
186
+ import untpd ._
187
+ def traverse (tree : Tree )(using Context ) = tree match
188
+ case t : TypeTree => assert(assertion = false , i " TypeTree not expected: $t" )
189
+ case t @ TypeApply (fun, _targs) => traverse(fun)
190
+ case t @ New (_tpt) =>
191
+ case t @ Typed (expr, _tpt) => traverse(expr)
192
+ case t @ Closure (env, meth, _tpt) => traverse(env); traverse(meth)
193
+ case t @ SeqLiteral (elems, _elemtpt) => traverse(elems)
194
+ case t @ ValDef (_, _tpt, _) => traverse(t.rhs)
195
+ case t @ DefDef (_, paramss, _tpt, _) => for params <- paramss do traverse(params); traverse(t.rhs)
196
+ case t @ TypeDef (_, _rhs) =>
197
+ case t @ Template (constr, parents, self, _) => traverse(constr); traverse(parents); traverse(self); traverse(t.body)
198
+ case t => traverseChildren(t)
199
+ end traverse
200
+
201
+ private [TreeChecker ] def isValidJVMName (name : Name ): Boolean = name.toString.forall(isValidJVMChar)
202
+
203
+ private [TreeChecker ] def isValidJVMMethodName (name : Name ): Boolean = name.toString.forall(isValidJVMMethodChar)
204
+
205
+
153
206
class Checker (phasesToCheck : Seq [Phase ]) extends ReTyper with Checking {
207
+ import ast .tpd ._
154
208
155
- private val nowDefinedSyms = util.HashSet [Symbol ]()
209
+ protected val nowDefinedSyms = util.HashSet [Symbol ]()
156
210
private val patBoundSyms = util.HashSet [Symbol ]()
157
211
private val everDefinedSyms = MutableSymbolMap [untpd.Tree ]()
158
212
@@ -658,68 +712,50 @@ class TreeChecker extends Phase with SymTransformer {
658
712
override def simplify (tree : Tree , pt : Type , locked : TypeVars )(using Context ): tree.type = tree
659
713
}
660
714
661
- /**
662
- * Checks that `New` nodes are always wrapped inside `Select` nodes.
663
- */
664
- def assertSelectWrapsNew (tree : Tree )(using Context ): Unit =
665
- (new TreeAccumulator [tpd.Tree ] {
666
- override def apply (parent : Tree , tree : Tree )(using Context ): Tree = {
667
- tree match {
668
- case tree : New if ! parent.isInstanceOf [tpd.Select ] =>
669
- assert(assertion = false , i " `New` node must be wrapped in a `Select`: \n parent = ${parent.show}\n child = ${tree.show}" )
670
- case _ : Annotated =>
671
- // Don't check inside annotations, since they're allowed to contain
672
- // somewhat invalid trees.
673
- case _ =>
674
- foldOver(tree, tree) // replace the parent when folding over the children
675
- }
676
- parent // return the old parent so that my siblings see it
677
- }
678
- })(tpd.EmptyTree , tree)
679
- }
715
+ /** Tree checker that can be applied to a local tree. */
716
+ class LocalChecker (phasesToCheck : Seq [Phase ]) extends Checker (phasesToCheck : Seq [Phase ]):
717
+ override def assertDefined (tree : untpd.Tree )(using Context ): Unit =
718
+ // Only check definitions nested in the local tree
719
+ if nowDefinedSyms.contains(tree.symbol.maybeOwner) then
720
+ super .assertDefined(tree)
680
721
681
- object TreeChecker {
682
- /** - Check that TypeParamRefs and MethodParams refer to an enclosing type.
683
- * - Check that all type variables are instantiated.
684
- */
685
- def checkNoOrphans (tp0 : Type , tree : untpd.Tree = untpd.EmptyTree )(using Context ): Type = new TypeMap () {
686
- val definedBinders = new java.util.IdentityHashMap [Type , Any ]
687
- def apply (tp : Type ): Type = {
688
- tp match {
689
- case tp : BindingType =>
690
- definedBinders.put(tp, tp)
691
- mapOver(tp)
692
- definedBinders.remove(tp)
693
- case tp : ParamRef =>
694
- assert(definedBinders.get(tp.binder) != null , s " orphan param: ${tp.show}, hash of binder = ${System .identityHashCode(tp.binder)}, tree = ${tree.show}, type = $tp0" )
695
- case tp : TypeVar =>
696
- assert(tp.isInstantiated, s " Uninstantiated type variable: ${tp.show}, tree = ${tree.show}" )
697
- apply(tp.underlying)
698
- case _ =>
699
- mapOver(tp)
700
- }
701
- tp
702
- }
703
- }.apply(tp0)
722
+ def checkMacroGeneratedTree (original : tpd.Tree , expansion : tpd.Tree )(using Context ): Unit =
723
+ if ctx.settings.XcheckMacros .value then
724
+ val checkingCtx = ctx
725
+ .fresh
726
+ .setReporter(new ThrowingReporter (ctx.reporter))
727
+ val phases = ctx.base.allPhases.toList
728
+ val treeChecker = new LocalChecker (previousPhases(phases))
729
+
730
+ try treeChecker.typed(expansion)(using checkingCtx)
731
+ catch
732
+ case err : java.lang.AssertionError =>
733
+ report.error(
734
+ s """ Malformed tree was found while expanding macro with -Xcheck-macros.
735
+ |The tree does not conform to the compiler's tree invariants.
736
+ |
737
+ |Macro was:
738
+ | ${scala.quoted.runtime.impl.QuotesImpl .showDecompiledTree(original)}
739
+ |
740
+ |The macro returned:
741
+ | ${scala.quoted.runtime.impl.QuotesImpl .showDecompiledTree(expansion)}
742
+ |
743
+ |Error:
744
+ | ${err.getMessage}
745
+ |
746
+ | """ ,
747
+ original
748
+ )
704
749
705
- /** Run some additional checks on the nodes of the trees. Specifically:
706
- *
707
- * - TypeTree can only appear in TypeApply args, New, Typed tpt, Closure
708
- * tpt, SeqLiteral elemtpt, ValDef tpt, DefDef tpt, and TypeDef rhs.
709
- */
710
- object TreeNodeChecker extends untpd.TreeTraverser :
711
- import untpd ._
712
- def traverse (tree : Tree )(using Context ) = tree match
713
- case t : TypeTree => assert(assertion = false , i " TypeTree not expected: $t" )
714
- case t @ TypeApply (fun, _targs) => traverse(fun)
715
- case t @ New (_tpt) =>
716
- case t @ Typed (expr, _tpt) => traverse(expr)
717
- case t @ Closure (env, meth, _tpt) => traverse(env); traverse(meth)
718
- case t @ SeqLiteral (elems, _elemtpt) => traverse(elems)
719
- case t @ ValDef (_, _tpt, _) => traverse(t.rhs)
720
- case t @ DefDef (_, paramss, _tpt, _) => for params <- paramss do traverse(params); traverse(t.rhs)
721
- case t @ TypeDef (_, _rhs) =>
722
- case t @ Template (constr, parents, self, _) => traverse(constr); traverse(parents); traverse(self); traverse(t.body)
723
- case t => traverseChildren(t)
724
- end traverse
750
+ private [TreeChecker ] def previousPhases (phases : List [Phase ])(using Context ): List [Phase ] = phases match {
751
+ case (phase : MegaPhase ) :: phases1 =>
752
+ val subPhases = phase.miniPhases
753
+ val previousSubPhases = previousPhases(subPhases.toList)
754
+ if (previousSubPhases.length == subPhases.length) previousSubPhases ::: previousPhases(phases1)
755
+ else previousSubPhases
756
+ case phase :: phases1 if phase ne ctx.phase =>
757
+ phase :: previousPhases(phases1)
758
+ case _ =>
759
+ Nil
760
+ }
725
761
}
0 commit comments