Skip to content

Commit 898f952

Browse files
committed
Do not return java outline dummy constructor in primaryConstructor
Java outline parser phase for various reasons adds a dummy constructor to java classes compiling simultenously from scalac. Since they provide no information to the user and are overall misleading (with always having the same fake flags and parameters), we filter them out and return the first constructor that can be found in the source. This is also what happened up to this point when running the macro with a java classfile on the classpath instead, since those dummy constructors cannot be found there.
1 parent 896965c commit 898f952

File tree

9 files changed

+62
-1
lines changed

9 files changed

+62
-1
lines changed

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

+20-1
Original file line numberDiff line numberDiff line change
@@ -2822,7 +2822,26 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
28222822
self.typeRef.info.decls.toList
28232823

28242824
def paramSymss: List[List[Symbol]] = self.denot.paramSymss
2825-
def primaryConstructor: Symbol = self.denot.primaryConstructor
2825+
def primaryConstructor: Symbol =
2826+
val initialPrimary = self.denot.primaryConstructor
2827+
// Java outline parser creates a dummyConstructor. We want to avoid returning it here,
2828+
// instead returning the first non-dummy one, which is what happens when a java classfile
2829+
// is read from classpath instead of using the java outline parser.
2830+
// We check if the constructor is dummy if it has the same parameters as defined in JavaParsers.scala,
2831+
// incliding the private[this] flags and parameter shape with scala.Unit argument.
2832+
val isJavaDummyConstructor =
2833+
val paramSymss = initialPrimary.paramSymss
2834+
initialPrimary.flags.is(Flags.JavaDefined | Flags.Local | Flags.Method | Flags.Private | Flags.PrivateLocal)
2835+
&& {
2836+
paramSymss match
2837+
case List(List(typeTree)) if self.typeRef.memberType(typeTree).typeSymbol == defn.UnitClass => true
2838+
case _ => false
2839+
}
2840+
if isJavaDummyConstructor then
2841+
declarations.filter(sym => sym != initialPrimary && sym.isConstructor).headOption.getOrElse(Symbol.noSymbol)
2842+
else
2843+
initialPrimary
2844+
28262845
def allOverriddenSymbols: Iterator[Symbol] = self.denot.allOverriddenSymbols
28272846
def overridingSymbol(ofclazz: Symbol): Symbol =
28282847
if ofclazz.isClass then self.denot.overridingSymbol(ofclazz.asClass)

tests/run-macros/i20052.check

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
method <init> (Flags.JavaDefined | Flags.Method) List(List((x$0,scala.Int)))
2+
method <init> (Flags.JavaDefined | Flags.Method) List(List())
3+
method <init> (Flags.JavaDefined | Flags.Method | Flags.Private) List(List())
4+
method <init> (Flags.JavaDefined | Flags.Method | Flags.Private) List(List())
5+
method <init> (Flags.JavaDefined | Flags.Method) List(List((A,_ >: scala.Nothing <: <special-ops>.<FromJavaObject>)), List((x$0,A)))
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public class JavaClass {
2+
public JavaClass(int a) {}
3+
public JavaClass(float a) {}
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public class JavaClassEmpty {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
public class JavaClassParam<A> {
2+
public JavaClassParam(A a) {}
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class JavaClassPrivate {
2+
private JavaClassPrivate() {}
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class JavaClassStartsWithPrivate {
2+
private JavaClassStartsWithPrivate() {}
3+
public JavaClassStartsWithPrivate(int a) {}
4+
}

tests/run-macros/i20052/Macro.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import scala.quoted.*
2+
3+
object Macro {
4+
inline def logPrimaryConstructor[A]: String = ${ logPrimaryConstructorImpl[A] }
5+
6+
def logPrimaryConstructorImpl[A](using Type[A], Quotes): Expr[String] = {
7+
import quotes.reflect.*
8+
9+
val primaryConstructor = TypeRepr.of[A].typeSymbol.primaryConstructor
10+
val flags = primaryConstructor.flags.show
11+
val paramSymss = primaryConstructor.paramSymss
12+
val clauses = paramSymss.map(_.map(param => (param.name, TypeRepr.of[A].memberType(param).show)))
13+
val str = s"${primaryConstructor} (${primaryConstructor.flags.show}) ${clauses}"
14+
Expr(str)
15+
}
16+
}

tests/run-macros/i20052/Test_2.scala

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@main def Test() =
2+
println(Macro.logPrimaryConstructor[JavaClass])
3+
println(Macro.logPrimaryConstructor[JavaClassEmpty])
4+
println(Macro.logPrimaryConstructor[JavaClassPrivate])
5+
println(Macro.logPrimaryConstructor[JavaClassStartsWithPrivate])
6+
println(Macro.logPrimaryConstructor[JavaClassParam[Int]])

0 commit comments

Comments
 (0)