diff --git a/airframe-surface/src/main/scala-3/wvlet/airframe/surface/CompileTimeSurfaceFactory.scala b/airframe-surface/src/main/scala-3/wvlet/airframe/surface/CompileTimeSurfaceFactory.scala index 58831ffe91..2cabcf892b 100644 --- a/airframe-surface/src/main/scala-3/wvlet/airframe/surface/CompileTimeSurfaceFactory.scala +++ b/airframe-surface/src/main/scala-3/wvlet/airframe/surface/CompileTimeSurfaceFactory.scala @@ -1,5 +1,6 @@ package wvlet.airframe.surface import java.util.concurrent.atomic.AtomicInteger +import scala.annotation.tailrec import scala.collection.immutable.ListMap import scala.quoted.* import scala.reflect.ClassTag @@ -556,6 +557,7 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q): // println(s"=== target: ${m.name}, ${m.owner.name}") m.name == targetMethodName } + // println(s"MethodArg ${v.name} $resolved") MethodArg(v.name, resolved, defaultValueGetter, defaultMethodArgGetter, isImplicit, isRequired, isSecret) } } @@ -754,17 +756,55 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q): if seenMethodParent.contains(targetType) then sys.error(s"recursive method found in: ${targetType.typeSymbol.fullName}") else + println(s"=== targetType ${targetType.show} ${targetType}") seenMethodParent += targetType val localMethods = localMethodsOf(targetType).distinct.sortBy(_.name) + + // check for a type like OuterType.InnerType and get OuterType from it + val targetTypeParent = targetType match { + case TypeRef(parent, _) => Some(parent) + case _ => None + } + //println(s" ${targetTypeParent.baseClasses}") + def simplifyTypeRef(typeRepr: TypeRepr): TypeRepr = { + println(s"simplifyTypeRef ${typeRepr.show} in ${targetType.show}: ${typeRepr}") + typeRepr match { + // Pattern matching to skip 'Base' and directly access 'InnerType' + case _ if targetTypeParent.exists(_.baseClasses.exists(_.typeRef == typeRepr)) => + println(s" case base ${typeRepr.show} $typeRepr -> $targetTypeParent") + targetTypeParent.get + + case TypeRef(ThisType(parent), _) => + println(s" case non-base ${typeRepr.show} $typeRepr") + val parentRef = simplifyTypeRef(parent) + val result = parentRef.typeSymbol.typeMember(typeRepr.typeSymbol.name).typeRef + println(s" -> parentRef: $result") + println(s" -> result: $result") + result + + case _ => + println(s" case other ${typeRepr.show} $typeRepr") + typeRepr + } + } + val methodSurfaces = localMethods.map(m => (m, m.tree)).collect { case (m, df: DefDef) => val mod = Expr(modifierBitMaskOf(m)) val owner = surfaceOf(targetType) val name = Expr(m.name) - // println(s"======= ${df.returnTpt.show}") - val ret = surfaceOf(df.returnTpt.tpe) - // println(s"==== method of: def ${m.name}") + val tpt = df.returnTpt + println(s"======= tpt ${tpt.show} ${simplifyTypeRef(tpt.tpe).show}") + + println(s"======= dealias: ${tpt.tpe.dealias.show}") + val ret = surfaceOf(tpt.tpe) + println(s"==== method of: def ${m.name}") val params = methodParametersOf(targetType, m) - val args = methodArgsOf(targetType, m) + val args = methodArgsOf(targetType, m).map { list => + list.map { arg => + println(s"======= arg ${arg.name}: ${arg.tpe.show} ==> ${simplifyTypeRef(arg.tpe).show}") + arg.copy(tpe = simplifyTypeRef(arg.tpe)) + } + } val methodCaller = createMethodCaller(targetType, m, args) '{ ClassMethodSurface(${ mod }, ${ owner }, ${ name }, ${ ret }, ${ params }.toIndexedSeq, ${ methodCaller }) diff --git a/airframe-surface/src/test/scala/wvlet/airframe/surface/i3411.scala b/airframe-surface/src/test/scala/wvlet/airframe/surface/i3411.scala new file mode 100644 index 0000000000..937e326051 --- /dev/null +++ b/airframe-surface/src/test/scala/wvlet/airframe/surface/i3411.scala @@ -0,0 +1,51 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package wvlet.airframe.surface + +import wvlet.airspec.AirSpec + +object i3411 extends AirSpec { + + trait Base { + class InnerType { + def compare(that: InnerType): Int = 0 + } + } + + object OuterType extends Base { + def create: InnerType = new InnerType + } + + test("Handle inherited inner class") { + //val mm = Surface.methodsOf[OuterType.type] + val m = Surface.methodsOf[OuterType.InnerType] + //m.map(_.name) shouldContain "compare" + } + + object SomeEnum extends Enumeration { + type SomeEnum = Value + + val A, B, C = Value + } + + import SomeEnum.SomeEnum + + test("Handle a Scala 2 enumeration") { + //val s = Surface.of[SomeEnum] // just check there is no error - no expected properties + //val m = Surface.methodsOf[SomeEnum] + //debug(s.params) + // enumeration type (value) usually contains at least the compare method + //m.map(_.name) shouldContain "compare" + } +}