FIR: support more precise diagnostics about parameter names
#KT-52762 Fixed
This commit is contained in:
@@ -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) {
|
||||
|
||||
+76
-23
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user