From 7850de55997ff3e77925a2f7933143a1fc478f09 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 2 Aug 2024 01:41:25 +0100 Subject: [PATCH 1/2] Add optional seenfromClass to ThisTypeSubstitution Pass in the context class of the member whose type is being substituted (analagous to the `seenFromClass` passed to scalac's asSeenFrom). Use this to guide the walk through enclosing classes. Also, fix basetypes handling around ScThisType --- .../impl/toplevel/typedef/MixinNodes.scala | 21 +-- .../scala/lang/psi/types/BaseTypes.scala | 12 +- .../api/designator/ScProjectionType.scala | 2 +- .../types/recursiveUpdate/ScSubstitutor.scala | 12 +- .../ThisTypeSubstitution.scala | 35 +++-- .../lang/resolve/ScalaResolveState.scala | 5 +- .../processor/MethodResolveProcessor.scala | 2 +- .../scala/lang/resolve2/Bug2Test.scala | 3 +- .../plugins/scala/lang/resolve2/BugTest.scala | 20 ++- .../testdata/resolve2/bug/SCL21585B.scala | 22 +++ .../testdata/resolve2/bug/SCL21585C.scala | 49 ++++++ .../testdata/resolve2/bug/SCL21585C2.scala | 24 +++ .../testdata/resolve2/bug/SCL21585Cm.scala | 15 ++ .../testdata/resolve2/bug/SCL21585D.scala | 19 +++ .../testdata/resolve2/bug/SCL21585E.scala | 19 +++ .../testdata/resolve2/bug/SCL21585E2.scala | 12 ++ .../testdata/resolve2/bug/SCL21585E3.scala | 17 ++ .../testdata/resolve2/bug/SCL21585E4.scala | 21 +++ .../testdata/resolve2/bug/SCL21585E5.scala | 146 ++++++++++++++++++ .../testdata/resolve2/bug/SCL21585F.scala | 35 +++++ .../testdata/resolve2/bug/SCL21585G.scala | 29 ++++ .../testdata/resolve2/bug/SCL21585H.scala | 29 ++++ .../testdata/resolve2/bug/SCL21585I.scala | 23 +++ .../testdata/resolve2/bug/SCL21947A.scala | 56 +++++++ .../testdata/resolve2/bug/SCL21947B.scala | 29 ++++ .../resolve2/bug2/DependentEquality2.scala | 19 +++ 26 files changed, 649 insertions(+), 27 deletions(-) create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585B.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585C.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585C2.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585Cm.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585D.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585E.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585E2.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585E3.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585E4.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585E5.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585F.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585G.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585H.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21585I.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21947A.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug/SCL21947B.scala create mode 100644 scala/scala-impl/testdata/resolve2/bug2/DependentEquality2.scala diff --git a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/impl/toplevel/typedef/MixinNodes.scala b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/impl/toplevel/typedef/MixinNodes.scala index b16c2ce4147..3f1f2fda6f2 100644 --- a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/impl/toplevel/typedef/MixinNodes.scala +++ b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/impl/toplevel/typedef/MixinNodes.scala @@ -11,6 +11,7 @@ import com.intellij.util.containers.{ContainerUtil, SmartHashSet} import com.intellij.util.{AstLoadingFilter, SmartList} import it.unimi.dsi.fastutil.Hash import it.unimi.dsi.fastutil.objects.{Object2ObjectMap, Object2ObjectMaps, Object2ObjectOpenCustomHashMap} +import org.jetbrains.annotations.Nullable import org.jetbrains.plugins.scala.caches.{ModTracker, cachedInUserData, cachedWithRecursionGuard} import org.jetbrains.plugins.scala.extensions._ import org.jetbrains.plugins.scala.lang.psi.api.base.ScFieldId @@ -133,31 +134,33 @@ object MixinNodes { case newTd: ScNewTemplateDefinition => MixinNodes.linearization(newTd) case _ => MixinNodes.linearization(thisClass).drop(1) } - val thisTypeSubst = thisClass match { - case td: ScTemplateDefinition => ScSubstitutor(ScThisType(td)) - case _ => ScSubstitutor.empty + val thisType = thisClass match { + case td: ScTemplateDefinition => ScThisType(td) + case _ => null } - SuperTypesData(superTypes, thisTypeSubst) + SuperTypesData(superTypes, thisType) } def apply(cp: ScCompoundType, compoundThisType: Option[ScType]): SuperTypesData = { val superTypes = MixinNodes.linearization(cp) - val thisTypeSubst = ScSubstitutor(compoundThisType.getOrElse(cp)) - SuperTypesData(superTypes, thisTypeSubst) + val thisType = compoundThisType.getOrElse(cp) + SuperTypesData(superTypes, thisType) } - private def apply(superTypes: Seq[ScType], thisTypeSubst: ScSubstitutor): SuperTypesData = { + private def apply(superTypes: Seq[ScType], @Nullable thisType: ScType): SuperTypesData = { val substitutorsBuilder = SeqMap.newBuilder[PsiClass, ScSubstitutor] val refinementsBuilder = List.newBuilder[ScCompoundType] for (superType <- superTypes) { superType.extractClassType match { case Some((superClass, s)) => + val seenFromClass = if (thisType == null) null else superClass val dependentSubst = superType match { - case p @ ScProjectionType(proj, _) => ScSubstitutor(proj).followed(p.actualSubst) - case ParameterizedType(p @ ScProjectionType(proj, _), _) => ScSubstitutor(proj).followed(p.actualSubst) + case p @ ScProjectionType(proj, _) => ScSubstitutor(proj, seenFromClass).followed(p.actualSubst) + case ParameterizedType(p @ ScProjectionType(proj, _), _) => ScSubstitutor(proj, seenFromClass).followed(p.actualSubst) case _ => ScSubstitutor.empty } + val thisTypeSubst = if (thisType == null) ScSubstitutor.empty else ScSubstitutor(thisType, seenFromClass) val newSubst = combine(s, superClass).followed(thisTypeSubst).followed(dependentSubst) substitutorsBuilder += superClass -> newSubst case _ => diff --git a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/BaseTypes.scala b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/BaseTypes.scala index 3f14568640e..9b743ba18bb 100644 --- a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/BaseTypes.scala +++ b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/BaseTypes.scala @@ -132,7 +132,15 @@ private class BaseTypesIterator(tp: ScType) extends Iterator[ScType] { } else None case ScThisType(clazz) => - clazz.getTypeWithProjections().toOption + // Given: + // trait Father[A] { trait Son } + // trait Charles extends Father[Int] { + // trait William extends Father[String] with Son + // } + // then William.this.type.baseType(trait Son) + // should return Charles.this.Son not Charles#Son + // (what `clazz.getTypeWithProjections()` returns) + clazz.`type`().toOption case tpt: TypeParameterType => Some(tpt.upperType) case ScExistentialArgument(_, Nil, _, upper) => @@ -143,4 +151,4 @@ private class BaseTypesIterator(tp: ScType) extends Iterator[ScType] { } } } -} \ No newline at end of file +} diff --git a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/api/designator/ScProjectionType.scala b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/api/designator/ScProjectionType.scala index 0c7d5b65914..1b729ec62c1 100644 --- a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/api/designator/ScProjectionType.scala +++ b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/api/designator/ScProjectionType.scala @@ -150,7 +150,7 @@ final class ScProjectionType private(val projected: ScType, processor.candidates match { case Array(candidate) => candidate.element match { case candidateElement: PsiNamedElement => - val thisSubstitutor = ScSubstitutor(projected) + val thisSubstitutor = ScSubstitutor(projected, candidateElement.findContextOfType(classOf[PsiClass]).orNull) val defaultSubstitutor = projected match { case _: ScThisType => candidate.substitutor diff --git a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/recursiveUpdate/ScSubstitutor.scala b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/recursiveUpdate/ScSubstitutor.scala index 8ad0b3e5ed1..ddf9dce93ec 100644 --- a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/recursiveUpdate/ScSubstitutor.scala +++ b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/recursiveUpdate/ScSubstitutor.scala @@ -1,6 +1,7 @@ package org.jetbrains.plugins.scala.lang.psi.types.recursiveUpdate import com.intellij.openapi.diagnostic.Logger +import com.intellij.psi.PsiClass import org.jetbrains.plugins.scala.extensions.ArrayExt import org.jetbrains.plugins.scala.lang.psi.api.statements.params.{ScParameter, TypeParamId} import org.jetbrains.plugins.scala.lang.psi.types.Compatibility.Expression @@ -116,6 +117,10 @@ final class ScSubstitutor private(_substitutions: Array[Update], //Array is us ScSubstitutor(tp).followed(this) } + def followUpdateThisType(tp: ScType, seenFromClass: PsiClass): ScSubstitutor = { + ScSubstitutor(tp, seenFromClass).followed(this) + } + def withBindings(from: Iterable[TypeParameter], target: Iterable[TypeParameter]): ScSubstitutor = { assertFullSubstitutor() @@ -187,7 +192,10 @@ object ScSubstitutor { } def apply(updateThisType: ScType): ScSubstitutor = - ScSubstitutor(ThisTypeSubstitution(updateThisType)) + ScSubstitutor(ThisTypeSubstitution(updateThisType, null)) + + def apply(updateThisType: ScType, seenFromClass: PsiClass): ScSubstitutor = + ScSubstitutor(ThisTypeSubstitution(updateThisType, seenFromClass)) def paramToExprType(parameters: Seq[Parameter], expressions: Seq[Expression], useExpected: Boolean = true): ScSubstitutor = ScSubstitutor(ParamsToExprs(parameters, expressions, useExpected)) @@ -218,4 +226,4 @@ object ScSubstitutor { } def undefineTypeParams[T](tps: Seq[TypeParameter]): ScSubstitutor = bind(tps)(UndefinedType(_)) -} \ No newline at end of file +} diff --git a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/recursiveUpdate/ThisTypeSubstitution.scala b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/recursiveUpdate/ThisTypeSubstitution.scala index 0db3a75131d..198f9a79be5 100644 --- a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/recursiveUpdate/ThisTypeSubstitution.scala +++ b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/recursiveUpdate/ThisTypeSubstitution.scala @@ -1,6 +1,7 @@ package org.jetbrains.plugins.scala.lang.psi.types.recursiveUpdate import com.intellij.psi.{PsiClass, PsiElement, PsiTypeParameter} +import org.jetbrains.annotations.Nullable import org.jetbrains.plugins.scala.extensions.PsiMemberExt import org.jetbrains.plugins.scala.lang.psi.ScalaPsiUtil.isInheritorDeep import org.jetbrains.plugins.scala.lang.psi.api.base.patterns.ScBindingPattern @@ -11,24 +12,40 @@ import org.jetbrains.plugins.scala.lang.psi.api.toplevel.typedef.{ScTemplateDefi import org.jetbrains.plugins.scala.lang.psi.types.api.designator.{ScProjectionType, ScThisType} import org.jetbrains.plugins.scala.lang.psi.types.api.{ParameterizedType, TypeParameterType} import org.jetbrains.plugins.scala.lang.psi.types.{LeafType, ScCompoundType, ScType} +import org.jetbrains.plugins.scala.lang.psi.types.BaseTypes import scala.annotation.tailrec -private case class ThisTypeSubstitution(target: ScType) extends LeafSubstitution { +private case class ThisTypeSubstitution(target: ScType, @Nullable seenFromClass: PsiClass) extends LeafSubstitution { - override def toString: String = s"`this` -> $target" + override def toString: String = seenFromClass match { + case null => s"`this` -> $target" + case _ => s"`this` -> $target asSeenFrom $seenFromClass" + } override protected val subst: PartialFunction[LeafType, ScType] = { - case th: ScThisType if !hasRecursiveThisType(target, th.element) => doUpdateThisType(th, target) + case th: ScThisType if !hasRecursiveThisType(target, th.element) => doUpdateThisType(th, target, seenFromClass) } @tailrec - private def doUpdateThisType(thisTp: ScThisType, target: ScType): ScType = - if (isMoreNarrow(target, thisTp, Set.empty)) target - else { - containingClassType(target) match { - case Some(targetContext) => doUpdateThisType(thisTp, targetContext) - case _ => thisTp + private def doUpdateThisType(thisTp: ScThisType, target: ScType, @Nullable clazz: PsiClass): ScType = + if (clazz == thisTp.element) target + else if (clazz == null) { + if (isMoreNarrow(target, thisTp, Set.empty)) target + else { + containingClassType(target) match { + case Some(targetContext) => doUpdateThisType(thisTp, targetContext, clazz) + case _ => thisTp + } + } + } else { + val classContext = clazz.containingClass + if (classContext == null) thisTp + else { + BaseTypes.iterator(target).find(_.extractClass.contains(clazz)).flatMap(containingClassType) match { + case Some(targetContext) => doUpdateThisType(thisTp, targetContext, classContext) + case _ => thisTp + } } } diff --git a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/resolve/ScalaResolveState.scala b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/resolve/ScalaResolveState.scala index f4d0ab8c10b..dfdbbbf0f80 100644 --- a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/resolve/ScalaResolveState.scala +++ b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/resolve/ScalaResolveState.scala @@ -1,7 +1,7 @@ package org.jetbrains.plugins.scala.lang.resolve import com.intellij.openapi.util.Key -import com.intellij.psi.ResolveState +import com.intellij.psi.{ PsiClass, ResolveState } import org.jetbrains.plugins.scala.extensions.ObjectExt import org.jetbrains.plugins.scala.lang.psi.ScExportsHolder import org.jetbrains.plugins.scala.lang.psi.api.statements.ScExtension @@ -96,6 +96,9 @@ trait ResolveStateOps extends Any { def substitutorWithThisType: ScSubstitutor = fromType.fold(substitutor)(substitutor.followUpdateThisType) + def substitutorWithThisType(seenFromClass: PsiClass): ScSubstitutor = + fromType.fold(substitutor)(substitutor.followUpdateThisType(_, seenFromClass)) + def fromType: Option[ScType] = option(FROM_TYPE_KEY) diff --git a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/resolve/processor/MethodResolveProcessor.scala b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/resolve/processor/MethodResolveProcessor.scala index d39ba0ec151..16d1b504e70 100644 --- a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/resolve/processor/MethodResolveProcessor.scala +++ b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/resolve/processor/MethodResolveProcessor.scala @@ -67,7 +67,7 @@ class MethodResolveProcessor(override val ref: PsiElement, val accessible = isNamedParameter || isAccessible(namedElement, ref) if (accessibility && !accessible) return true - val s = state.substitutorWithThisType + val s = state.substitutorWithThisType(namedElement.findContextOfType(classOf[PsiClass]).orNull) val resultBuilder: PsiNamedElement => ScalaResolveResult = e => new ScalaResolveResult( e, diff --git a/scala/scala-impl/test/org/jetbrains/plugins/scala/lang/resolve2/Bug2Test.scala b/scala/scala-impl/test/org/jetbrains/plugins/scala/lang/resolve2/Bug2Test.scala index db3aca6f1b1..9075e7f72f6 100644 --- a/scala/scala-impl/test/org/jetbrains/plugins/scala/lang/resolve2/Bug2Test.scala +++ b/scala/scala-impl/test/org/jetbrains/plugins/scala/lang/resolve2/Bug2Test.scala @@ -29,6 +29,7 @@ class Bug2Test extends ResolveTestBase { def testSCL2765(): Unit = {doTest()} def testDependent(): Unit = {doTest()} def testDependentEquality(): Unit = {doTest()} + def testDependentEquality2(): Unit = {doTest()} def testSCL2904(): Unit = {doTest()} def testSCL2827A(): Unit = {doTest()} def testSCL2827B(): Unit = {doTest()} @@ -55,4 +56,4 @@ class Bug2Test extends ResolveTestBase { def testSCL3485(): Unit = {doTest()} def testSCL3539(): Unit = {doTest()} def testSCL3546(): Unit = {doTest()} -} \ No newline at end of file +} diff --git a/scala/scala-impl/test/org/jetbrains/plugins/scala/lang/resolve2/BugTest.scala b/scala/scala-impl/test/org/jetbrains/plugins/scala/lang/resolve2/BugTest.scala index d68c6b258dc..9e2ff1dc927 100644 --- a/scala/scala-impl/test/org/jetbrains/plugins/scala/lang/resolve2/BugTest.scala +++ b/scala/scala-impl/test/org/jetbrains/plugins/scala/lang/resolve2/BugTest.scala @@ -65,4 +65,22 @@ class BugTest extends ResolveTestBase { def testThisTypeSelfType(): Unit = {doTest()} def testImplicitsOverloading(): Unit = {doTest()} -} \ No newline at end of file + + def testSCL21585B(): Unit = doTest() + def testSCL21585C(): Unit = doTest() + def testSCL21585C2(): Unit = doTest() + def testSCL21585Cm(): Unit = doTest() + def testSCL21585D(): Unit = doTest() + def testSCL21585E(): Unit = doTest() + def testSCL21585E2(): Unit = doTest() + def testSCL21585E3(): Unit = doTest() + def testSCL21585E4(): Unit = doTest() + def testSCL21585E5(): Unit = doTest() + def testSCL21585F(): Unit = doTest() + def testSCL21585G(): Unit = doTest() + def testSCL21585H(): Unit = doTest() + def testSCL21585I(): Unit = doTest() + + def testSCL21947A(): Unit = doTest() + def testSCL21947B(): Unit = doTest() +} diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585B.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585B.scala new file mode 100644 index 00000000000..c4267be78e7 --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585B.scala @@ -0,0 +1,22 @@ +trait M { + type A +} + +trait Base { + type T +} +trait B extends Base { + type T + + def t: T +} + + +class Repro { + + type M_A[M1 <: M] = M1#A + + def test(value: (B{ type T = Repro }) with M_A[M { type A = B }]) = value.t./*resolved: true*/ok + + def ok = "" +} diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585C.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585C.scala new file mode 100644 index 00000000000..2fd267b4a5d --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585C.scala @@ -0,0 +1,49 @@ + +object Repro { + trait HasBuilder { + type Builder + } + + trait TList { + type Builder + } + trait TNil extends TList { + type Builder + } + + trait TCons[H <: HasBuilder, T <: TList] extends TList { + type Builder = H#Builder with T#Builder + } + + class Action1 extends HasBuilder { + type Builder = Action1Builder + } + trait Action1Builder { + type TargetType + def target(__DEBUG__ : Any = ""): TargetType = ??? + } + + trait HasTargetType[T] { + type TargetType = T + } + type ActionParam = TCons[Action1, TNil] +// def action1NOK1: ActionParam#Builder { type TargetType = TargetType1 } = ??? + def action1NOK2: HasTargetType[TargetType1] with ActionParam#Builder = ??? + +// def action1OK1: TCons[Action1, TNil]#Builder { type TargetType = TargetType1 } = ??? +// def action1OK2: Action1#Builder { type TargetType = TargetType1 } = ??? +// def action1OK3: HasTargetType[TargetType1] with TCons[Action1, TNil]#Builder = ??? +// def action1OK4: HasTargetType[TargetType1] with Action1#Builder = ??? + + class TargetType1 { + def ok = true + } + +// action1OK1.target.ok +// action1OK2.target.ok +// action1OK3.target.ok +// action1OK4.target.ok + // good code red, "cannot resolve symbol ok" +// action1NOK1.target.ok + action1NOK2.target()./*resolved:true*/ok +} \ No newline at end of file diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585C2.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585C2.scala new file mode 100644 index 00000000000..f1baf01e64b --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585C2.scala @@ -0,0 +1,24 @@ +trait HasTarget { type Target } +trait HasBuilder { type Builder } +trait Builder1 extends HasTarget { def target: Target = ??? } +trait Action1 extends HasBuilder { type Builder = Builder1 } +trait TCons[H <: HasBuilder, T <: HasBuilder] { type Builder = H#Builder with T#Builder } + +trait Foo { def bar = true } +trait MkFoo extends HasTarget { type Target = Foo } + +class Repro { + type Actions = TCons[Action1, HasBuilder] + + def ko1: Actions#Builder with MkFoo = ??? + def ok1: TCons[Action1, HasBuilder]#Builder with MkFoo = ??? + + ko1.target./*resolved:true*/bar // good code red, "cannot resolve symbol bar" + ok1.target./*resolved:true*/bar + + def ko2: MkFoo with Actions#Builder = ??? + def ok2: MkFoo with TCons[Action1, HasBuilder]#Builder = ??? + + ko2.target./*resolved:true*/bar // good code red, "cannot resolve symbol bar" + ok2.target./*resolved:true*/bar +} diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585Cm.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585Cm.scala new file mode 100644 index 00000000000..bf2b98999f9 --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585Cm.scala @@ -0,0 +1,15 @@ +trait HasTarget { type Target } +trait HasBuilder { type Builder } +trait Builder1 extends HasTarget { def target: Target = ??? } +trait Action1 extends HasBuilder { type Builder = Builder1 } +trait TCons[H <: HasBuilder, T <: HasBuilder] { type Builder = H#Builder with T#Builder } + +trait Foo { def bar = true } +trait MkFoo extends HasTarget { type Target = Foo } + +class Repro { + type Actions = TCons[Action1, HasBuilder] + + def ko1: Actions#Builder with MkFoo = ??? + ko1.target./*resolved:true*/bar // good code red, "cannot resolve symbol bar" +} diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585D.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585D.scala new file mode 100644 index 00000000000..459615af62c --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585D.scala @@ -0,0 +1,19 @@ +object Repro { + + trait O1[O1_A] { + type O1_A_Alias = O1_A + trait I1[I1_A] { + type I1_A_Alias = I1_A + def foo(__DEBUG__o1_a: O1_A_Alias): Unit = () + } + } + + trait O2 extends O1[Int] { + trait I2 extends I1[String] { + self: X => + self./*resolved: true*/foo(1) + } + } + trait X + +} diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585E.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585E.scala new file mode 100644 index 00000000000..7cab8e71ffd --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585E.scala @@ -0,0 +1,19 @@ +object Repro { + + trait O1[O1_A] { + type O1_A_Alias = O1_A + trait I1[I1_A] { + type I1_A_Alias = I1_A + def foo(__DEBUG__o1_a: O1_A_Alias): Unit = () + } + } + + trait O2 extends O1[Int] { + trait I2 extends I1[String] with O1[Nothing] { + self: X => + self./*resolved: true*/foo(1) + } + } + trait X + +} diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585E2.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585E2.scala new file mode 100644 index 00000000000..0521b68e444 --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585E2.scala @@ -0,0 +1,12 @@ +trait Father[A] { + type B = A + trait Son { + def set(x: B): Unit + } +} + +trait Charles extends Father[Int] { + trait William extends Father[String] with Son { + def t1() = this./* resolved: true */set(1) + } +} diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585E3.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585E3.scala new file mode 100644 index 00000000000..24a217ea532 --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585E3.scala @@ -0,0 +1,17 @@ +trait Father[A] { + type B = A + + trait Son { + def set(x: B): Unit + } +} + +trait Charles extends Father[Int] { + trait William extends Father[String] with Son { + def t1() = this./* resolved: true */set(1) + + trait George extends Son { + def t2() = this./* resolved: true */set("y") + } + } +} diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585E4.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585E4.scala new file mode 100644 index 00000000000..07b214fe7e1 --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585E4.scala @@ -0,0 +1,21 @@ +trait Father[A] { + type B = A + + trait Son { + def set(x: B): Unit + } +} + +trait Phillip extends Father[Boolean] { + trait Charles extends Father[Int] with Son { + def t0() = this./* resolved: true */set(true) + + trait William extends Father[String] with Son { + def t1() = this./* resolved: true */set(1) + + trait George extends Son { + def t2() = this./* resolved: true */set("y") + } + } + } +} diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585E5.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585E5.scala new file mode 100644 index 00000000000..a73224c2a73 --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585E5.scala @@ -0,0 +1,146 @@ +class P1; object P1 extends P1 +class P2; object P2 extends P2 +class S1; object S1 extends S1 +class S2; object S2 extends S2 + +trait GrandFather[A] { + type B = A + + def putA(x: A): Unit + def putB(x: B): Unit + + trait Father[M] { + type C = A + type D = B + type N = M + + def putA2(x: A): Unit + def putB2(x: B): Unit + def putC(x: C): Unit + def putD(x: D): Unit + def setM(x: M): Unit + def setN(x: N): Unit + + trait Son { + type E = A + type F = B + type G = C + type H = D + type O = M + type P = N + + def putA3(x: A): Unit + def putB3(x: B): Unit + def putC2(x: C): Unit + def putD2(x: D): Unit + def putE(x: E): Unit + def putF(x: F): Unit + def putG(x: G): Unit + def putH(x: H): Unit + def setM2(x: M): Unit + def setN2(x: N): Unit + def setO(x: O): Unit + def setP(x: P): Unit + } + } +} + +trait Phillip extends GrandFather[P1] { + def aA() = this./* resolved: true */putA(P1) + def aB() = this./* resolved: true */putB(P1) + + trait Charles extends GrandFather[P2] with Father[S1] { + def bA() = this./* resolved: true */putA(P2) + def bB() = this./* resolved: true */putB(P2) + + def bA_1() = Phillip.this./* resolved: true */putA(P1) + def bB_1() = Phillip.this./* resolved: true */putB(P1) + + def bA2() = this./* resolved: true */putA2(P1) + def bB2() = this./* resolved: true */putB2(P1) + def bC() = this./* resolved: true */putC(P1) + def bD() = this./* resolved: true */putD(P1) + def bM() = this./* resolved: true */setM(S1) + def bN() = this./* resolved: true */setN(S1) + + trait William extends Father[S2] with Son { + def cA_1() = Charles.this./* resolved: true */putA(P2) + def cA_2() = Phillip.this./* resolved: true */putA(P1) + def cB_1() = Charles.this./* resolved: true */putB(P2) + def cB_2() = Phillip.this./* resolved: true */putB(P1) + + def cA2() = this./* resolved: true */putA2(P2) + def cB2() = this./* resolved: true */putB2(P2) + def cC() = this./* resolved: true */putC(P2) + def cD() = this./* resolved: true */putD(P2) + def cM() = this./* resolved: true */setM(S2) + def cN() = this./* resolved: true */setN(S2) + + def cA2_1() = Charles.this./* resolved: true */putA2(P1) + def cB2_1() = Charles.this./* resolved: true */putB2(P1) + def cC_1() = Charles.this./* resolved: true */putC(P1) + def cD_1() = Charles.this./* resolved: true */putD(P1) + def cM_1() = Charles.this./* resolved: true */setM(S1) + def cN_1() = Charles.this./* resolved: true */setN(S1) + + def cA3() = this./* resolved: true */putA3(P1) + def cB3() = this./* resolved: true */putB3(P1) + def cC2() = this./* resolved: true */putC2(P1) + def cD2() = this./* resolved: true */putD2(P1) + def cE() = this./* resolved: true */putE(P1) + def cF() = this./* resolved: true */putF(P1) + def cG() = this./* resolved: true */putG(P1) + def cH() = this./* resolved: true */putH(P1) + def cM2() = this./* resolved: true */setM2(S1) + def cN2() = this./* resolved: true */setN2(S1) + def cO() = this./* resolved: true */setO(S1) + def cP() = this./* resolved: true */setP(S1) + + trait George extends Son { + def dA_2() = Charles.this./* resolved: true */putA(P2) + def dA_3() = Phillip.this./* resolved: true */putA(P1) + def dB_2() = Charles.this./* resolved: true */putB(P2) + def dB_3() = Phillip.this./* resolved: true */putB(P1) + + def dA2_1() = William.this./* resolved: true */putA2(P2) + def dA2_2() = Charles.this./* resolved: true */putA2(P1) + def dB2_1() = William.this./* resolved: true */putB2(P2) + def dB2_2() = Charles.this./* resolved: true */putB2(P1) + def dC_1() = William.this./* resolved: true */putC(P2) + def dC_2() = Charles.this./* resolved: true */putC(P1) + def dD_1() = William.this./* resolved: true */putD(P2) + def dD_2() = Charles.this./* resolved: true */putD(P1) + def dM_1() = William.this./* resolved: true */setM(S2) + def dM_2() = Charles.this./* resolved: true */setM(S1) + def dN_1() = William.this./* resolved: true */setN(S2) + def dN_2() = Charles.this./* resolved: true */setN(S1) + + def dA3() = this./* resolved: true */putA3(P2) + def dB3() = this./* resolved: true */putB3(P2) + def dC2() = this./* resolved: true */putC2(P2) + def dD2() = this./* resolved: true */putD2(P2) + def dE() = this./* resolved: true */putE(P2) + def dF() = this./* resolved: true */putF(P2) + def dG() = this./* resolved: true */putG(P2) + def dH() = this./* resolved: true */putH(P2) + def dM2() = this./* resolved: true */setM2(S2) + def dN2() = this./* resolved: true */setN2(S2) + def dO() = this./* resolved: true */setO(S2) + def dP() = this./* resolved: true */setP(S2) + + def dA3_1() = William.this./* resolved: true */putA3(P1) + def dB3_1() = William.this./* resolved: true */putB3(P1) + def dC2_1() = William.this./* resolved: true */putC2(P1) + def dD2_1() = William.this./* resolved: true */putD2(P1) + def dE_1() = William.this./* resolved: true */putE(P1) + def dF_1() = William.this./* resolved: true */putF(P1) + def dG_1() = William.this./* resolved: true */putG(P1) + def dH_1() = William.this./* resolved: true */putH(P1) + def dM2_1() = William.this./* resolved: true */setM2(S1) + def dN2_1() = William.this./* resolved: true */setN2(S1) + def dO_1() = William.this./* resolved: true */setO(S1) + def dP_1() = William.this./* resolved: true */setP(S1) + } + } + } +} diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585F.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585F.scala new file mode 100644 index 00000000000..2d11a1f1aaa --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585F.scala @@ -0,0 +1,35 @@ +object Repro { + trait Api extends Api1 + + trait Api1 { + self: Api => + type X <: XApi + + class XApi + + abstract class XExtractor { + def unapply(a: X): Option[String] + } + def apiX: X = ??? + } + + trait Impl extends Impl1 { + + } + + trait Impl1 extends Api { + self: Impl => + case class X(s: String) extends XApi + + object X extends XCompanion + val implX: X = apiX + } + + object Test { + val impl: Impl = ??? + (null: Any) match { + case impl.X(s) => s./*resolved: true*/charAt(0) + } + } +} + diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585G.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585G.scala new file mode 100644 index 00000000000..3420c8f1df8 --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585G.scala @@ -0,0 +1,29 @@ +object Repro { + trait Api extends Api1 + + trait Api1 { + self: Api => + + class XApi + + type X <: XApi + + abstract class XCompanion { + def foo(x: X) = () + } + } + + trait Impl extends Impl1 + + trait Impl1 extends Api { + self: Impl => + class X extends XApi + + object X extends XCompanion { +// override def foo(x: X) = () + this./*resolved: true*/foo(new impl.X) + } + } + +} + diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585H.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585H.scala new file mode 100644 index 00000000000..ebb8fdcb56d --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585H.scala @@ -0,0 +1,29 @@ +object Repro { + trait Api extends Api1 + + trait Api1 { + self: Api => + + class XApi + + type X <: XApi + + abstract class XCompanion { + def foo(x: X) = () + } + } + + trait Impl extends Impl1 + + trait Impl1 extends Api { + self: Impl => + class X extends XApi + + object X extends XCompanion { + override def foo(x: X) = () + this./*resolved: true*/foo(new impl.X)("") + } + } + +} + diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21585I.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21585I.scala new file mode 100644 index 00000000000..d99fdbfdd88 --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21585I.scala @@ -0,0 +1,23 @@ +class B { + trait M { + type A + } + + trait Base { + type T + } +} + +class Repro extends B { + trait B extends Base { + type T + + def t: T + } + + type M_A[M1 <: M] = M1#A + + def test(value: (B{ type T = Repro }) with M_A[M { type A = B }]) = value.t./*resolved: true*/ok + + def ok = "" +} diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21947A.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21947A.scala new file mode 100644 index 00000000000..bfaffbcb55d --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21947A.scala @@ -0,0 +1,56 @@ +package example + +object Hello extends App { + val g = new nsc.Global + println(new C[g.type](g).exprF(new g./* resolved: true */Block(Nil, g.EmptyTree))) + println(g./* resolved: true */O./* resolved: true */treeMethod(g.EmptyTree)) +} + +class C[G <: nsc.Global](val global: G) { + import global._ + def exprF(t: Tree): Int = t match { + case Block(_, e) => e.f + case _ => t.f + } +} + +object api { + trait Trees { + type Tree + + type Block <: Tree + + val Block: BlockExtractor + + trait T { + def treeMethod(t: Tree): Int + } + + abstract class BlockExtractor { + def apply(stats: List[Tree], expr: Tree): Block + def unapply(block: Block): Option[(List[Tree], Tree)] + } + } +} + +object internal { + abstract class SymbolTable extends Trees + + trait Trees extends api.Trees { self: SymbolTable => // removing the self type fixes it + + abstract class Tree { def f = 0 } + + object O extends T { + def treeMethod(t: Tree) = t.f + } + + case class Block(stats: List[Tree], expr: Tree) extends Tree + object Block extends BlockExtractor + + case object EmptyTree extends Tree + } +} + +object nsc { + class Global extends internal.SymbolTable +} diff --git a/scala/scala-impl/testdata/resolve2/bug/SCL21947B.scala b/scala/scala-impl/testdata/resolve2/bug/SCL21947B.scala new file mode 100644 index 00000000000..c92494a69a2 --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug/SCL21947B.scala @@ -0,0 +1,29 @@ +object api { + trait Trees { + type Tree + type Block <: Tree + val Block: BlockExtractor + abstract class BlockExtractor { + def unapply(block: Block): Option[Tree] + } + } +} + +object internal { + abstract class SymbolTable extends Trees + trait Trees extends api.Trees { self: SymbolTable => // removing the self type fixes it + class Tree { def f = 0 } + case class Block(expr: Tree) extends Tree + object Block extends BlockExtractor // Object creation impossible, since member unapply.. is not defined + } +} + +class C(val g: internal.SymbolTable) { + import g._ + def expr(t: Tree): Int = { + t match { + case /* name: unapply */Block(e) => e./* resolved: true */f + case _ => -1 + } + } +} diff --git a/scala/scala-impl/testdata/resolve2/bug2/DependentEquality2.scala b/scala/scala-impl/testdata/resolve2/bug2/DependentEquality2.scala new file mode 100644 index 00000000000..d7b83fd051a --- /dev/null +++ b/scala/scala-impl/testdata/resolve2/bug2/DependentEquality2.scala @@ -0,0 +1,19 @@ +abstract class TreeInfo { + val global: Global + import global._ + def foo(tree: Tree): Boolean = false + def foo(x: Boolean): Boolean = true +} + +class Global { + class Tree + object treeInfo extends TreeInfo { + val global: Global.this.type = Global.this + } +} + +trait Test { + val g: Global + import g._ + def arg(fun: Tree) = treeInfo./* line: 4 */foo(fun) +} From c1426cd8ff5254c565e5f3b0ba01f34a7d1f58ef Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 11 Sep 2024 11:43:38 +0100 Subject: [PATCH 2/2] Fixes to asSeenFrom changes --- .../api/designator/ScProjectionType.scala | 2 +- .../ThisTypeSubstitution.scala | 95 +++++++++++-------- 2 files changed, 58 insertions(+), 39 deletions(-) diff --git a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/api/designator/ScProjectionType.scala b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/api/designator/ScProjectionType.scala index 1b729ec62c1..b58ffe76025 100644 --- a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/api/designator/ScProjectionType.scala +++ b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/api/designator/ScProjectionType.scala @@ -150,7 +150,7 @@ final class ScProjectionType private(val projected: ScType, processor.candidates match { case Array(candidate) => candidate.element match { case candidateElement: PsiNamedElement => - val thisSubstitutor = ScSubstitutor(projected, candidateElement.findContextOfType(classOf[PsiClass]).orNull) + val thisSubstitutor = ScSubstitutor(projected, element.findContextOfType(classOf[PsiClass]).orNull) val defaultSubstitutor = projected match { case _: ScThisType => candidate.substitutor diff --git a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/recursiveUpdate/ThisTypeSubstitution.scala b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/recursiveUpdate/ThisTypeSubstitution.scala index 198f9a79be5..0a5dca541c5 100644 --- a/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/recursiveUpdate/ThisTypeSubstitution.scala +++ b/scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/types/recursiveUpdate/ThisTypeSubstitution.scala @@ -1,18 +1,13 @@ package org.jetbrains.plugins.scala.lang.psi.types.recursiveUpdate -import com.intellij.psi.{PsiClass, PsiElement, PsiTypeParameter} +import com.intellij.psi._ import org.jetbrains.annotations.Nullable -import org.jetbrains.plugins.scala.extensions.PsiMemberExt -import org.jetbrains.plugins.scala.lang.psi.ScalaPsiUtil.isInheritorDeep -import org.jetbrains.plugins.scala.lang.psi.api.base.patterns.ScBindingPattern -import org.jetbrains.plugins.scala.lang.psi.api.statements.ScTypeAliasDeclaration -import org.jetbrains.plugins.scala.lang.psi.api.statements.params.{ScParameter, ScTypeParam} -import org.jetbrains.plugins.scala.lang.psi.api.toplevel.ScTypedDefinition -import org.jetbrains.plugins.scala.lang.psi.api.toplevel.typedef.{ScTemplateDefinition, ScTypeDefinition} -import org.jetbrains.plugins.scala.lang.psi.types.api.designator.{ScProjectionType, ScThisType} -import org.jetbrains.plugins.scala.lang.psi.types.api.{ParameterizedType, TypeParameterType} -import org.jetbrains.plugins.scala.lang.psi.types.{LeafType, ScCompoundType, ScType} -import org.jetbrains.plugins.scala.lang.psi.types.BaseTypes +import org.jetbrains.plugins.scala.extensions._ +import org.jetbrains.plugins.scala.lang.psi.ScalaPsiUtil._ +import org.jetbrains.plugins.scala.lang.psi.api.base.patterns._ +import org.jetbrains.plugins.scala.lang.psi.api.statements._, params._ +import org.jetbrains.plugins.scala.lang.psi.api.toplevel._, typedef._ +import org.jetbrains.plugins.scala.lang.psi.types._, api._, designator._, nonvalue._ import scala.annotation.tailrec @@ -24,28 +19,26 @@ private case class ThisTypeSubstitution(target: ScType, @Nullable seenFromClass: } override protected val subst: PartialFunction[LeafType, ScType] = { - case th: ScThisType if !hasRecursiveThisType(target, th.element) => doUpdateThisType(th, target, seenFromClass) + case th: ScThisType if !hasRecursiveThisType(target, th.element) => doUpdateThisTypeFromClass(th, target, seenFromClass) } @tailrec - private def doUpdateThisType(thisTp: ScThisType, target: ScType, @Nullable clazz: PsiClass): ScType = - if (clazz == thisTp.element) target - else if (clazz == null) { - if (isMoreNarrow(target, thisTp, Set.empty)) target - else { - containingClassType(target) match { - case Some(targetContext) => doUpdateThisType(thisTp, targetContext, clazz) - case _ => thisTp - } + private def doUpdateThisType(thisTp: ScThisType, target: ScType): ScType = + if (isMoreNarrow(target, thisTp, Set.empty)) target + else { + containingClassType(target) match { + case Some(targetContext) => doUpdateThisType(thisTp, targetContext) + case _ => thisTp } - } else { - val classContext = clazz.containingClass - if (classContext == null) thisTp - else { - BaseTypes.iterator(target).find(_.extractClass.contains(clazz)).flatMap(containingClassType) match { - case Some(targetContext) => doUpdateThisType(thisTp, targetContext, classContext) - case _ => thisTp - } + } + + private def doUpdateThisTypeFromClass(thisTp: ScThisType, target: ScType, @Nullable clazz: PsiClass): ScType = + if (clazz == null || clazz == thisTp.element || clazz.containingClass == null) + doUpdateThisType(thisTp, target) + else { + BaseTypes.iterator(target).find(_.extractClass.contains(clazz)).flatMap(containingClassType) match { + case Some(targetContext) => doUpdateThisTypeFromClass(thisTp, targetContext, clazz.containingClass) + case _ => thisTp } } @@ -69,11 +62,12 @@ private case class ThisTypeSubstitution(target: ScType, @Nullable seenFromClass: private def isSameOrInheritor(clazz: PsiClass, thisTp: ScThisType): Boolean = clazz == thisTp.element || isInheritorDeep(clazz, thisTp.element) - private def hasSameOrInheritor(compound: ScCompoundType, thisTp: ScThisType) = { + private def hasSameOrInheritor(compound: ScCompoundType, thisTp: ScThisType): Boolean = { compound.components .exists { - _.extractDesignated(expandAliases = true) + extractAll(_) .exists { + case tp: ScCompoundType => hasSameOrInheritor(tp, thisTp) case tp: ScTypeParam => (for { upper <- tp.upperBound.toOption @@ -87,7 +81,7 @@ private case class ThisTypeSubstitution(target: ScType, @Nullable seenFromClass: @tailrec private def isMoreNarrow(target: ScType, thisTp: ScThisType, visited: Set[PsiElement]): Boolean = { - target.extractDesignated(expandAliases = true) match { + extractAll(target) match { case Some(pat: ScBindingPattern) => if (visited.contains(pat)) false else isMoreNarrow(pat.`type`().getOrAny, thisTp, visited + pat) @@ -117,11 +111,36 @@ private case class ThisTypeSubstitution(target: ScType, @Nullable seenFromClass: case Some(td: ScTypeAliasDeclaration) => isMoreNarrow(td.upperBound.getOrAny, thisTp, visited) case Some(cl: PsiClass) => isSameOrInheritor(cl, thisTp) case Some(named: ScTypedDefinition) => isMoreNarrow(named.`type`().getOrAny, thisTp, visited) - case _ => - target match { - case compound: ScCompoundType => hasSameOrInheritor(compound, thisTp) - case _ => false - } + case Some(compound: ScCompoundType) => hasSameOrInheritor(compound, thisTp) + case _ => false } } + + private def extractAll(tp: ScType) = { + // Like tp.extractDesignated(expandAliases = true) + // But also return non-designator returns + // Such as ScCompoundType, after dealiasing and dereferencing. + + def elem1(tp: DesignatorOwner) = tp match { case tp: ScProjectionType => tp.actualElement case _ => tp.element } + def subst(tp: DesignatorOwner) = tp match { case tp: ScProjectionType => tp.actualSubst case _ => ScSubstitutor.empty } + + def rec(tp: ScType, seen: Set[ScTypeAlias]): Either[ScType, PsiNamedElement] = tp match { + case tp: NonValueType => rec(tp.inferValueType, seen) + case tp: DesignatorOwner => elem1(tp) match { + case ta: ScTypeAliasDefinition if !seen(ta) => ta.aliasedType.map(subst(tp)).fold(_ => Left(tp), rec(_, seen + ta)) + case tp => Right(tp) + } + case tp: ParameterizedType => tp match { + case AliasType(ta: ScTypeAliasDefinition, _, Right(ub)) if !seen(ta) => rec(ub, seen + ta) + case _ => rec(tp.designator, seen) + } + case tp: StdType => tp.syntheticClass.toRight(tp) + case tp: ScExistentialType => rec(tp.quantified, seen) + case tp: TypeParameterType => Right(tp.psiTypeParameter) + case tp: ScCompoundType => Left(tp) + case _ => Left(tp) + } + + rec(tp, Set.empty).fold(Some(_), Some(_)) + } }