FIR: support more precise diagnostics about parameter names

#KT-52762 Fixed
This commit is contained in:
Mikhail Glukhikh
2022-06-17 18:27:04 +02:00
committed by Space
parent 9fbafc3eb9
commit 065e852199
18 changed files with 154 additions and 57 deletions
@@ -34,8 +34,11 @@ import org.jetbrains.kotlin.fir.resolve.dfa.PropertyStability
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.impl.delegatedWrapperData
import org.jetbrains.kotlin.fir.scopes.impl.importedFromObjectData
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.*
@@ -493,7 +496,12 @@ fun FirCallableDeclaration.getContainingClass(session: FirSession): FirRegularCl
session.symbolProvider.getSymbolByLookupTag(lookupTag)?.fir as? FirRegularClass
}
fun FirFunction.getAsForbiddenNamedArgumentsTarget(session: FirSession): ForbiddenNamedArgumentsTarget? {
fun FirFunction.getAsForbiddenNamedArgumentsTarget(
session: FirSession,
// NB: with originScope given this function will try to find overridden declaration with allowed parameter names
// for intersection/substitution overrides
originScope: FirTypeScope? = null
): ForbiddenNamedArgumentsTarget? {
if (this is FirConstructor && this.isPrimary) {
this.getContainingClass(session)?.let { containingClass ->
if (containingClass.classKind == ClassKind.ANNOTATION_CLASS) {
@@ -506,15 +514,21 @@ fun FirFunction.getAsForbiddenNamedArgumentsTarget(session: FirSession): Forbidd
FirDeclarationOrigin.Source, FirDeclarationOrigin.Precompiled, FirDeclarationOrigin.Library -> null
FirDeclarationOrigin.Delegated -> delegatedWrapperData?.wrapped?.getAsForbiddenNamedArgumentsTarget(session)
FirDeclarationOrigin.ImportedFromObject -> importedFromObjectData?.original?.getAsForbiddenNamedArgumentsTarget(session)
// For intersection overrides, the logic in
// org.jetbrains.kotlin.fir.scopes.impl.FirTypeIntersectionScope#selectMostSpecificMember picks the most specific one and store
// it in originalForIntersectionOverrideAttr. This follows from FE1.0 behavior which selects the most specific function
// (org.jetbrains.kotlin.resolve.OverridingUtil#selectMostSpecificMember), from which the `hasStableParameterNames` status is
// copied.
FirDeclarationOrigin.IntersectionOverride -> originalForIntersectionOverrideAttr?.getAsForbiddenNamedArgumentsTarget(session)
is FirDeclarationOrigin.Java, FirDeclarationOrigin.Enhancement -> ForbiddenNamedArgumentsTarget.NON_KOTLIN_FUNCTION
FirDeclarationOrigin.SamConstructor -> null
FirDeclarationOrigin.SubstitutionOverride -> originalForSubstitutionOverrideAttr?.getAsForbiddenNamedArgumentsTarget(session)
FirDeclarationOrigin.IntersectionOverride, FirDeclarationOrigin.SubstitutionOverride -> {
var result: ForbiddenNamedArgumentsTarget? =
originalIfFakeOverride()?.getAsForbiddenNamedArgumentsTarget(session) ?: return null
originScope?.processOverriddenFunctions(symbol as FirNamedFunctionSymbol) {
if (it.fir.getAsForbiddenNamedArgumentsTarget(session) == null) {
result = null
ProcessorAction.STOP
} else {
ProcessorAction.NEXT
}
}
result
}
// referenced function of a Kotlin function type
FirDeclarationOrigin.BuiltIns -> {
if (dispatchReceiverClassOrNull()?.isBuiltinFunctionalType() == true) {
@@ -13,10 +13,17 @@ import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.declarations.utils.isOperator
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildNamedArgumentExpression
import org.jetbrains.kotlin.fir.isIntersectionOverride
import org.jetbrains.kotlin.fir.isSubstitutionOrIntersectionOverride
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.defaultParameterResolver
import org.jetbrains.kotlin.fir.resolve.getAsForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -128,7 +135,7 @@ private class FirCallArgumentsProcessor(
val result: LinkedHashMap<FirValueParameter, ResolvedCallArgument> = LinkedHashMap(function.valueParameters.size)
val forbiddenNamedArgumentsTarget: ForbiddenNamedArgumentsTarget? by lazy {
function.getAsForbiddenNamedArgumentsTarget(useSiteSession)
function.getAsForbiddenNamedArgumentsTarget(useSiteSession, originScope as? FirTypeScope)
}
private enum class State {
@@ -322,29 +329,75 @@ private class FirCallArgumentsProcessor(
}
private fun findParameterByName(argument: FirNamedArgumentExpression): FirValueParameter? {
val parameter = getParameterByName(argument.name)
var parameter = getParameterByName(argument.name)
// TODO
// if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
// if (parameter == null) {
// for (valueParameter in descriptor.valueParameters) {
// val matchedParameter = valueParameter.overriddenDescriptors.firstOrNull {
// it.containingDeclaration.hasStableParameterNames() && it.name == name
// }
// if (matchedParameter != null) {
// addDiagnostic(NamedArgumentReference(argument, valueParameter))
// addDiagnostic(NameForAmbiguousParameter(argument, valueParameter, matchedParameter))
// return valueParameter
// }
// }
// } else {
// parameter.getOverriddenParameterWithOtherName()?.let {
// addDiagnostic(NameForAmbiguousParameter(argument, parameter, it))
// }
// }
// }
//
if (parameter == null) addDiagnostic(NameNotFound(argument, function))
val symbol = function.symbol as? FirNamedFunctionSymbol
var matchedIndex = -1
// Note: should be called when parameter != null && matchedIndex != -1
fun List<FirValueParameterSymbol>.findAndReportValueParameterWithDifferentName(): ProcessorAction {
val someParameter = getOrNull(matchedIndex)?.fir
val someName = someParameter?.name
if (someName != null && someName != argument.name) {
addDiagnostic(
NameForAmbiguousParameter(argument, matchedParameter = parameter!!, someParameter)
)
return ProcessorAction.STOP
}
return ProcessorAction.NEXT
}
if (parameter == null) {
if (symbol != null && function.isSubstitutionOrIntersectionOverride) {
var allowedParameters: List<FirValueParameterSymbol>? = null
(originScope as? FirTypeScope)?.processOverriddenFunctions(symbol) {
if (it.fir.getAsForbiddenNamedArgumentsTarget(useSiteSession) != null) {
return@processOverriddenFunctions ProcessorAction.NEXT
}
val someParameterSymbols = it.valueParameterSymbols
if (matchedIndex != -1) {
someParameterSymbols.findAndReportValueParameterWithDifferentName()
} else {
matchedIndex = someParameterSymbols.indexOfFirst { originalParameter ->
originalParameter.name == argument.name
}
if (matchedIndex != -1) {
parameter = parameters[matchedIndex]
val someParameter = allowedParameters?.getOrNull(matchedIndex)?.fir
if (someParameter != null) {
addDiagnostic(
NameForAmbiguousParameter(argument, matchedParameter = parameter!!, anotherParameter = someParameter)
)
ProcessorAction.STOP
} else {
ProcessorAction.NEXT
}
} else {
allowedParameters = someParameterSymbols
ProcessorAction.NEXT
}
}
}
}
if (parameter == null) {
addDiagnostic(NameNotFound(argument, function))
}
} else {
// TODO: should we check also substitution overrides? Performance!
if (symbol != null && function.isIntersectionOverride) {
matchedIndex = parameters.indexOfFirst { originalParameter ->
originalParameter.name == argument.name
}
if (matchedIndex != -1) {
(originScope as? FirTypeScope)?.processOverriddenFunctions(symbol) {
if (it.fir.getAsForbiddenNamedArgumentsTarget(useSiteSession) != null) {
return@processOverriddenFunctions ProcessorAction.NEXT
}
it.valueParameterSymbols.findAndReportValueParameterWithDifferentName()
}
}
}
}
return parameter
}