diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 22be293c3562..fd5b635e11e2 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2822,7 +2822,26 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler self.typeRef.info.decls.toList def paramSymss: List[List[Symbol]] = self.denot.paramSymss - def primaryConstructor: Symbol = self.denot.primaryConstructor + def primaryConstructor: Symbol = + val initialPrimary = self.denot.primaryConstructor + // Java outline parser creates a dummyConstructor. We want to avoid returning it here, + // instead returning the first non-dummy one, which is what happens when a java classfile + // is read from classpath instead of using the java outline parser. + // We check if the constructor is dummy if it has the same parameters as defined in JavaParsers.scala, + // incliding the private[this] flags and parameter shape with scala.Unit argument. + val isJavaDummyConstructor = + val paramSymss = initialPrimary.paramSymss + initialPrimary.flags.is(Flags.JavaDefined | Flags.Local | Flags.Method | Flags.Private | Flags.PrivateLocal) + && { + paramSymss match + case List(List(typeTree)) if self.typeRef.memberType(typeTree).typeSymbol == defn.UnitClass => true + case _ => false + } + if isJavaDummyConstructor then + declarations.filter(sym => sym != initialPrimary && sym.isConstructor).headOption.getOrElse(Symbol.noSymbol) + else + initialPrimary + def allOverriddenSymbols: Iterator[Symbol] = self.denot.allOverriddenSymbols def overridingSymbol(ofclazz: Symbol): Symbol = if ofclazz.isClass then self.denot.overridingSymbol(ofclazz.asClass) diff --git a/tests/run-macros/i20052.check b/tests/run-macros/i20052.check new file mode 100644 index 000000000000..fd8918d0e54c --- /dev/null +++ b/tests/run-macros/i20052.check @@ -0,0 +1,4 @@ +method (Flags.JavaDefined | Flags.Method) List(List((x$0,scala.Int))) +method (Flags.JavaDefined | Flags.Method) List(List()) +method (Flags.JavaDefined | Flags.Method | Flags.Private) List(List()) +method (Flags.JavaDefined | Flags.Method | Flags.Private) List(List()) diff --git a/tests/run-macros/i20052/JavaClass.java b/tests/run-macros/i20052/JavaClass.java new file mode 100644 index 000000000000..368bbf47830a --- /dev/null +++ b/tests/run-macros/i20052/JavaClass.java @@ -0,0 +1,4 @@ +public class JavaClass { + public JavaClass(int a) {} + public JavaClass(float a) {} +} diff --git a/tests/run-macros/i20052/JavaClassEmpty.java b/tests/run-macros/i20052/JavaClassEmpty.java new file mode 100644 index 000000000000..22710a8b7337 --- /dev/null +++ b/tests/run-macros/i20052/JavaClassEmpty.java @@ -0,0 +1 @@ +public class JavaClassEmpty {} diff --git a/tests/run-macros/i20052/JavaClassPrivate.java b/tests/run-macros/i20052/JavaClassPrivate.java new file mode 100644 index 000000000000..d2f0b3cda023 --- /dev/null +++ b/tests/run-macros/i20052/JavaClassPrivate.java @@ -0,0 +1,3 @@ +class JavaClassPrivate { + private JavaClassPrivate() {} +} diff --git a/tests/run-macros/i20052/JavaClassTest.java b/tests/run-macros/i20052/JavaClassTest.java new file mode 100644 index 000000000000..ba83a56cf0fc --- /dev/null +++ b/tests/run-macros/i20052/JavaClassTest.java @@ -0,0 +1,4 @@ +class JavaClassTest { + private JavaClassTest() {} + public JavaClassTest(int a) {} +} diff --git a/tests/run-macros/i20052/Macro.scala b/tests/run-macros/i20052/Macro.scala new file mode 100644 index 000000000000..e38bb63257a3 --- /dev/null +++ b/tests/run-macros/i20052/Macro.scala @@ -0,0 +1,16 @@ +import scala.quoted.* + +object Macro { + inline def logPrimaryConstructor[A]: String = ${ logPrimaryConstructorImpl[A] } + + def logPrimaryConstructorImpl[A](using Type[A], Quotes): Expr[String] = { + import quotes.reflect.* + + val primaryConstructor = TypeRepr.of[A].typeSymbol.primaryConstructor + val flags = primaryConstructor.flags.show + val paramSymss = primaryConstructor.paramSymss + val clauses = paramSymss.map(_.map(param => (param.name, TypeRepr.of[A].memberType(param).show))) + val str = s"${primaryConstructor} (${primaryConstructor.flags.show}) ${clauses}" + Expr(str) + } +} diff --git a/tests/run-macros/i20052/Test_2.scala b/tests/run-macros/i20052/Test_2.scala new file mode 100644 index 000000000000..90afdb59690a --- /dev/null +++ b/tests/run-macros/i20052/Test_2.scala @@ -0,0 +1,5 @@ +@main def Test() = + println(Macro.logPrimaryConstructor[JavaClass]) + println(Macro.logPrimaryConstructor[JavaClassEmpty]) + println(Macro.logPrimaryConstructor[JavaClassPrivate]) + println(Macro.logPrimaryConstructor[JavaClassTest])