private def convertBindToType()

in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/impl/expr/ScReferenceExpressionImpl.scala [320:603]


  private def convertBindToType(bind: ScalaResolveResult): TypeResult = {
    val srr = bind match {
      case ScalaResolveResult.ApplyMethodInnerResolve(inner) => inner
      case _                                                 => bind
    }

    val fromType                 = srr.fromType
    val unresolvedTypeParameters = srr.unresolvedTypeParameters.getOrElse(Seq.empty)
    val matchClauseSubst         = srr.matchClauseSubstitutor
    val extensionOwner           = srr.exportedInExtension

    val inner: ScType = srr match {
      case ScalaResolveResult(fun: ScFun, s) =>
        fun.polymorphicType(s)
      //prevent infinite recursion for recursive pattern reference
      case ScalaResolveResult(self: ScSelfTypeElement, _) =>
        val clazz = PsiTreeUtil.getContextOfType(self, true, classOf[ScTemplateDefinition])
        ScThisReferenceImpl.getThisTypeForTypeDefinition(clazz, this) match {
          case Right(value) => value
          case failure => return failure
        }
      case r@ScalaResolveResult(refPatt: ScBindingPattern, s) =>
        refPatt.nameContext match {
          case pd: ScPatternDefinition if PsiTreeUtil.isContextAncestor(pd, this, true) => pd.declaredType match {
            case Some(t) => t
            case None => return Failure(ScalaBundle.message("no.declared.type.found"))
          }
          case vd: ScVariableDefinition if PsiTreeUtil.isContextAncestor(vd, this, true) => vd.declaredType match {
            case Some(t) => t
            case None => return Failure(ScalaBundle.message("no.declared.type.found"))
          }
          case _ =>
            val result = r.intersectedReturnType.asTypeResult.orElse(refPatt.`type`())

            result.map { tp =>
              if (isStableContext(tp) && refPatt.isStable) {
                r.fromType match {
                  case Some(fT) => ScProjectionType(fT, refPatt)
                  case None     => ScalaType.designator(refPatt)
                }
              } else s(tp)
            }.getOrElse(return result)
        }

      case r @ ScalaResolveResult(param: ScParameter, s) =>
        val owner = param.owner match {
          case f: ScPrimaryConstructor => f.containingClass
          case _: ScFunctionExpr       => null
          case f                       => f
        }

        def isMethodDependent(function: ScFunction): Boolean = {
          def checkte(te: ScTypeElement): Boolean = {
            var res = false
            te.accept(new ScalaRecursiveElementVisitor {
              override def visitReference(ref: ScReference): Unit = {
                if (ref.resolve() == param) res = true
                super.visitReference(ref)
              }
            })
            res
          }

          function.returnTypeElement match {
            case Some(te) if checkte(te) => return true
            case _ =>
          }
          !function.parameters.forall { param =>
            param.typeElement match {
              case Some(te) => !checkte(te)
              case _        => true
            }
          }
        }

        val paramType = param.insideParamType
        val stableTypeRequired = paramType.exists(isStableContext)

        r.fromType match {
          case Some(fT) if param.isVal && stableTypeRequired => ScProjectionType(fT, param)
          case Some(ScThisType(clazz)) if owner != null && PsiTreeUtil.isContextAncestor(owner, this, true) &&
            stableTypeRequired && owner.is[ScTypeDefinition] && owner == clazz => ScalaType.designator(param) //todo: think about projection from this type?
          case _ if owner != null && PsiTreeUtil.isContextAncestor(owner, this, true) &&
            stableTypeRequired && !owner.is[ScTypeDefinition] => ScalaType.designator(param)
          case _ =>
            owner match {
              case function: ScFunction if PsiTreeUtil.isContextAncestor(function, this, true) &&
                isMethodDependent(function) => ScalaType.designator(param)
              case _ =>
                s(paramType match {
                  case Right(tp) => tp
                  case _ => return paramType
                })
            }
        }
      case result @ ScalaResolveResult(fun: ScFunction, s) if fun.isProbablyRecursive =>
        val maybeResult = result.intersectedReturnType.orElse(fun.definedReturnType.toOption)

        fun.polymorphicType(
          s,
          maybeResult,
          dropExtensionClauses = result.shouldDropExtensionClauses,
          extensionOwner       = extensionOwner
        )
      case result @ ScalaResolveResult(fun: ScFunction, s) =>
        fun
          .polymorphicType(
            s,
            result.intersectedReturnType,
            dropExtensionClauses = result.shouldDropExtensionClauses,
            extensionOwner       = extensionOwner
          )
          .updateTypeOfDynamicCall(result.isDynamic)
      case ScalaResolveResult(param: ScParameter, s) if param.isRepeatedParameter =>
        val computeType = param.`type`() match {
          case Right(tp) => s(tp)
          case result => return result
        }
        computeType.tryWrapIntoSeqType
      case ScalaResolveResult(obj: ScObject, _) =>
        def tail =
          fromType match {
            case Some(tp) => ScProjectionType(tp, obj)
            case _        => ScalaType.designator(obj)
          }
        //hack to add Eta expansion for case classes
        if (obj.isSyntheticObject) {
          def canEtaExpandPolymorphicCaseClass =
            this.scalaLanguageLevel.exists(_ >= ScalaLanguageLevel.Scala_2_13)

          ScalaPsiUtil.getCompanionModule(obj) match {
            case Some(clazz) if clazz.isCase && (!clazz.hasTypeParameters || canEtaExpandPolymorphicCaseClass) =>
              this.expectedType() match {
                case Some(tp) =>
                  if (FunctionType.isFunctionType(tp)) {
                    val tp        = tail
                    val processor = new MethodResolveProcessor(this, CommonNames.Apply, Nil, Nil, Nil)
                    processor.processType(tp, this)
                    val candidates = processor.candidates
                    if (candidates.length != 1) tail
                    else convertBindToType(candidates(0)).getOrElse(tail)
                  } else tail
                case _ => tail
              }
            case _ => tail
          }
        } else tail
      case r @ ScalaResolveResult(f: ScFieldId, s) =>
        val result = r.intersectedReturnType.asTypeResult.orElse(f.`type`())

        result.map { tp =>
          if (isStableContext(tp) && f.isStable) {
            r.fromType match {
              case Some(fT) => ScProjectionType(fT, f)
              case None     => ScalaType.designator(f)
            }
          } else s(tp)
        }.getOrElse(return result)
      case ScalaResolveResult(typed: ScTypedDefinition, s) =>
        typed.`type`() match {
          case Right(tp) => s(tp)
          case result => return result
        }
      case ScalaResolveResult(pack: PsiPackage, _) => ScalaType.designator(pack)
      case ScalaResolveResult(clazz: ScClass, s) if clazz.isCase =>
        val constructor =
          clazz.constructor
            .getOrElse(return Failure(ScalaBundle.message("case.class.has.no.primary.constructor")))
        constructor.polymorphicType(s)
      case ScalaResolveResult(clazz: ScTypeDefinition, s) if clazz.typeParameters.nonEmpty =>
        s(ScParameterizedType(ScalaType.designator(clazz),
          clazz.typeParameters.map(TypeParameterType(_))))
      case ScalaResolveResult(clazz: PsiClass, _) => ScDesignatorType.static(clazz) //static Java class
      case ScalaResolveResult(field: PsiField, s) =>
        s(field.getType.toScType())
      case ScalaResolveResult(c: ScNamedTupleComponent, s) =>
        c.`type`() match {
          case Right(tp) if c.is[ScNamedTupleExprComponent] =>
            // even (x = "x").x is String
            s(tp.widenIfLiteral)
          case Right(tp) => s(tp)
          case result => return result
        }
      case srr @ ScalaResolveResult(method: PsiMethod, s) =>
        val returnType = srr.intersectedReturnType.orElse(
          Option(method.containingClass).filter {
          method.getName == "getClass" && _.getQualifiedName == "java.lang.Object"
        }.flatMap { _ =>
          val maybeReference = qualifier.orElse {
            val result: Option[Typeable] = getContext match {
              case infixExpr: ScInfixExpr if infixExpr.operation == this => Some(infixExpr.left)
              case postfixExpr: ScPostfixExpr if postfixExpr.operation == this => Some(postfixExpr.operand)
              case _ => ScalaPsiUtil.drvTemplate(this)
            }
            result
          }

          def getType(element: PsiNamedElement): Option[ScType] = Option(element).collect {
            case pattern: ScBindingPattern => pattern
            case fieldId: ScFieldId => fieldId
            case parameter: ScParameter => parameter
          }.flatMap {
            _.`type`().toOption
          }

          def removeTypeDesignator(`type`: ScType): ScType = {
            val maybeType = `type` match {
              case ScDesignatorType(element) =>
                getType(element)
              case projectionType: ScProjectionType =>
                getType(projectionType.actualElement).map(projectionType.actualSubst)
              case _ => None
            }
            maybeType.map(removeTypeDesignator).getOrElse(`type`)
          }

          def convertQualifier(jlClass: PsiClass): ScType = {
            val maybeType = maybeReference.flatMap {
              _.`type`().toOption
            }

            val upperBound = maybeType.flatMap {
              case ScThisType(clazz) => Some(ScDesignatorType(clazz))
              case ScDesignatorType(_: ScObject) => None
              case ScCompoundType(comps, _, _) => comps.headOption.map(removeTypeDesignator)
              case tp => Some(tp).map(removeTypeDesignator)
            }.getOrElse(Any)

            val argument = ScExistentialArgument("_$1", Nil, Nothing, upperBound)
            ScExistentialType(ScParameterizedType(ScDesignatorType(jlClass), Seq(argument)))
          }

          elementScope.getCachedClass("java.lang.Class")
            .map(convertQualifier)
        })

        method
          .methodTypeProvider(elementScope)
          .polymorphicType(s, returnType)
      case _ => return resolveFailure
    }

    val default =
      if (unresolvedTypeParameters.nonEmpty)
        inner match {
          case ScTypePolymorphicType(internal, typeParams) =>
            ScTypePolymorphicType(internal, unresolvedTypeParameters ++ typeParams)
          case _ =>
            ScTypePolymorphicType(inner, unresolvedTypeParameters)
        }
      else inner

    val withUnresolvedTypeParams = qualifier match {
      case Some(_: ScSuperReference) => default
      case None => //infix, prefix and postfix
        getContext match {
          case sugar: ScSugarCallExpr if sugar.operation == this =>
            sugar.getBaseExpr.getNonValueType() match {
              case Right(ScTypePolymorphicType(_, typeParams)) =>
                inner match {
                  case ScTypePolymorphicType(internal, typeParams2) =>
                    ScalaPsiUtil.removeBadBounds(ScTypePolymorphicType(internal, unresolvedTypeParameters ++ typeParams ++ typeParams2))
                  case _ =>
                    ScTypePolymorphicType(inner, unresolvedTypeParameters ++ typeParams)
                }
              case _ => default
            }
          case _ => default
        }
      case Some(qualifier) =>
        qualifier.getNonValueType() match {
          case Right(ScTypePolymorphicType(_, typeParams)) =>
            inner match {
              case ScTypePolymorphicType(internal, typeParams2) =>
                return Right(ScalaPsiUtil.removeBadBounds(ScTypePolymorphicType(internal, unresolvedTypeParameters ++ typeParams ++ typeParams2)))
              case _ =>
                return Right(ScTypePolymorphicType(inner, unresolvedTypeParameters ++ typeParams))
            }
          case _ => default
        }
    }

    Right(matchClauseSubst(withUnresolvedTypeParams))
  }