private def checkOverrideMembers[Res]()

in scala/scala-impl/src/org/jetbrains/plugins/scala/annotator/OverridingAnnotator.scala [145:384]


  private def checkOverrideMembers[Res](
    namedElement:                ScNamedElement,
    member:                      ScMember,
    superSignaturesWithSelfType: Seq[Res],
    superSignatures:             Seq[Res],
    isConcrete:                  Res => Boolean,
    isExtension:                 Res => Boolean,
    memberType:                  String
  )(implicit
    holder: ScalaAnnotationHolder
  ): Unit = {
    implicit val context: Context = Context(namedElement)

    import lang.lexer.{ScalaModifier, ScalaTokenTypes}
    import ScalaModifier.{OVERRIDE, Override}
    import quickfix.ModifierQuickFix._

    val memberNameId = namedElement.nameId
    val isInScala3   = namedElement.isInScala3File

    if (superSignaturesWithSelfType.isEmpty) {
      if (member.hasModifierProperty(OVERRIDE)) {
        holder.createErrorAnnotation(
          memberNameId,
          ScalaBundle.message("member.overrides.nothing", memberType, namedElement.name),
          new Remove(member, memberNameId, Override) +:
            PullUpQuickFix(member, namedElement.name).toSeq
        )
      }
    } else {
      val isMismatchedExtension =
        namedElement match {
          case fn: ScFunction if fn.isExtensionMethod =>
            if (superSignatures.exists(!isExtension(_))) {
              holder.createErrorAnnotation(
                memberNameId,
                ScalaBundle.message("extension.method.overrides.regular", namedElement.name)
              )
              true
            } else false
          case _ =>
            if (superSignatures.exists(isExtension)) {
              holder.createErrorAnnotation(
                memberNameId,
                ScalaBundle.message("regular.method.overrides.extension", namedElement.name)
              )
              true
            } else false
        }

      if (isConcreteElement(namedElement.nameContext)) {
        val hasConcreteSuper = superSignatures.exists(isConcrete)
        // Mill build files make override optional, so don't error if the keyword is missing
        val isMillFile = namedElement.containingScalaFile.exists(_.isMillFile)
        if (!isMillFile && hasConcreteSuper && !member.hasModifierProperty(OVERRIDE)) {
          val maybeQuickFix: Option[Add] = namedElement match {
            case param: ScClassParameter if param.isCaseClassPrimaryParameter && !(param.isVal || param.isVar) =>
              superSignaturesWithSelfType.headOption.collect {
                case signature: TermSignature => signature.namedElement
              }.flatMap { element =>
                import ScalaTokenTypes.{kVAL, kVAR}
                element.nameContext match {
                  case parameter: ScClassParameter =>
                    val keywordElementType =
                      if (parameter.isValEffectively) kVAL
                      else kVAR
                    Some(keywordElementType)
                  case _: ScValue | _: ScFunction => Some(kVAL)
                  case _: ScVariable => Some(kVAR)
                  case _ => None
                }
              }.map {
                new AddOverrideWithKeyword(member, memberNameId, _)
              }
            case _ => Some(new Add(member, memberNameId, Override))
          }

          holder.createErrorAnnotation(
            memberNameId,
            ScalaBundle.message("member.needs.override.modifier", memberType, namedElement.name),
            maybeQuickFix
          )
        }
        //fix for SCL-7831
        var overridesFinal = false
        for (signature <- superSignatures if !overridesFinal) {
          val e =
            signature match {
              case signature: TermSignature => signature.namedElement
              case _ => signature
            }
          e match {
            case member: ScMember if member.isEffectivelyFinal =>
              overridesFinal = true
            case owner1: PsiModifierListOwner if owner1.hasFinalModifier =>
              overridesFinal = true
            case _ =>
          }
        }
        if (overridesFinal) {
          holder.createErrorAnnotation(memberNameId,
            ScalaBundle.message("can.not.override.final", memberType, namedElement.name))
        }

        def annotateVarFromVal(): Unit = {
          def addAnnotation(): Unit = {
            holder.createErrorAnnotation(memberNameId,
              ScalaBundle.message("var.cannot.override.val", namedElement.name))
          }

          for (signature <- superSignatures) {
            signature match {
              case s: TermSignature =>
                s.namedElement match {
                  case f: ScFieldId if f.isVal => addAnnotation()
                  case rp: ScBindingPattern if rp.isVal => addAnnotation()
                  case cp: ScClassParameter if cp.isVal => addAnnotation()
                  case _ =>
                }
              case _ =>
            }
          }
        }

        def annotateFunFromValOrVar(): Unit = {
          def annotVal(): Unit = {
            holder.createErrorAnnotation(memberNameId,
              ScalaBundle.message("member.cannot.override.val", namedElement.name))
          }

          for (signature <- superSignatures) {
            signature match {
              case s: TermSignature =>
                s.namedElement match {
                  case rp: ScBindingPattern if rp.isVal => annotVal()
                  case cp: ScClassParameter if cp.isVal => annotVal()
                  case f: ScFieldId if f.isVal => annotVal()
                  case _ =>
                }
              case _ =>
            }
          }
        }

        namedElement match {
          case _: ScFunctionDefinition          => annotateFunFromValOrVar()
          case inNameContext(_: ScVariable)     => annotateVarFromVal()
          case cp: ScClassParameter if cp.isVar => annotateVarFromVal()
          case _                                =>
        }
      }

      def effectiveParams(fun: ScFunction) = fun.parameterClausesWithExtension().flatMap(_.effectiveParameters)

      def overrideTypeMatchesBase(baseType: ScType, overType: ScType, s: TermSignature, baseName: String): Boolean = {
        val renameSubst = (s.namedElement, namedElement) match {
          case (sFun: ScFunction, mFun: ScFunction) if effectiveParams(sFun).length == effectiveParams(mFun).length &&
            sFun.typeParameters.length == mFun.typeParameters.length =>
            val sParams = effectiveParams(sFun)
            val mParams = effectiveParams(mFun)
            val sTypeParams = sFun.typeParametersWithExtension()
            val mTypeParams = mFun.typeParametersWithExtension()

            if (sParams.size != mParams.size || sTypeParams.size != mTypeParams.size)
              ScSubstitutor.empty
            else {
              val typeParamSubst  = ScSubstitutor.bind(sTypeParams, mTypeParams)(TypeParameterType(_))
              val paramTypesSubst = ScSubstitutor.paramToParam(sParams, mParams)
              typeParamSubst.followed(paramTypesSubst)
            }
          case _ => ScSubstitutor.empty
        }

        val fullSubst = s.substitutor.followed(renameSubst)

        val actualType =
          if (s.name == baseName + "_=") {
            overType match {
              case ParameterizedType(des, args) if des.canonicalText == "_root_.scala.Function1" => args.head
              case _ => return true
            }
          } else renameSubst(overType)

        val actualBase = fullSubst(baseType)

        def allowEmptyParens(e: ScNamedElement): Boolean = e match {
          case _: ScClassParameter => true
          case _ =>
            e.nameContext match {
              case v: ScValueOrVariable =>
                v.typeElement.isDefined || PropertyMethods.isBeanProperty(v)
              case _ => false
            }
        }

        def isBeanProperty(e: ScNamedElement): Boolean =
          e.nameContext match {
            case v: ScValueOrVariable => PropertyMethods.isBeanProperty(v)
            case p: ScClassParameter  => PropertyMethods.isBeanProperty(p)
            case _                    => false
          }

        actualType.conforms(actualBase) || ((actualBase, actualType, namedElement) match {
          /* 5.1.3.3 M defines a parameterless method and M′ defines a method with an empty parameter list () or vice versa. */
          case (ParameterizedType(des, args), _, _: ScFunction) if des.canonicalText == "_root_.scala.Function0" && !isInScala3 =>
            actualType.conforms(args.head)
          case (aType, ParameterizedType(des, args), _: ScFunction) if des.canonicalText == "_root_.scala.Function0" =>
            aType.conforms(args.head)
          case (ParameterizedType(des, args), _, patOrClassParam) =>
            if (des.canonicalText == "_root_.scala.Function0" && allowEmptyParens(patOrClassParam) && !isInScala3)
              actualType.conforms(args.head)
            // @BeanProperty setter SCL-14462
            else if (des.canonicalText == "_root_.scala.Function1" && isBeanProperty(patOrClassParam))
              true // actualType.conforms(args.head) // looks like just "true" is enough
            else false
          case _ => false
        })
      }

      implicit val tpc: TypePresentationContext = TypePresentationContext(memberNameId)

      if (!isMismatchedExtension) {
        for {
          overridingType <- typeForSigElement(namedElement)
          superSig       <- superSignatures.filterByType[TermSignature]
          baseType       <- typeForSigElement(superSig.namedElement)
          if !overrideTypeMatchesBase(baseType, overridingType, superSig, superSig.namedElement.name)
        } {
          holder.createErrorAnnotation(
            memberNameId,
            ScalaBundle.message(
              "override.types.not.conforming",
              overridingType.presentableText,
              baseType.presentableText
            )
          )
        }
      }
    }
  }