FE 1.0: deprecate resolve to property when enum entry is at same level
See also KT-52802 #KT-49200 Fixed
This commit is contained in:
@@ -147,6 +147,8 @@ public interface Errors {
|
||||
|
||||
DiagnosticFactory1<PsiElement, PropertyDescriptor> DEPRECATED_ACCESS_TO_ENUM_COMPANION_PROPERTY = DiagnosticFactory1.create(WARNING);
|
||||
|
||||
DiagnosticFactory2<PsiElement, PropertyDescriptor, ClassDescriptor> DEPRECATED_RESOLVE_WITH_AMBIGUOUS_ENUM_ENTRY = DiagnosticFactory2.create(WARNING);
|
||||
|
||||
DiagnosticFactory1<PsiElement, ConstructorDescriptor> PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
// Exposed visibility group
|
||||
|
||||
+1
@@ -73,6 +73,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(INVISIBLE_MEMBER, "Cannot access ''{0}'': it is {1} in {2}", NAME, VISIBILITY, NAME_OF_CONTAINING_DECLARATION_OR_FILE);
|
||||
MAP.put(DEPRECATED_ACCESS_BY_SHORT_NAME, "Access to this type by short name is deprecated, and soon is going to be removed. Please, add explicit qualifier or import", NAME);
|
||||
MAP.put(DEPRECATED_ACCESS_TO_ENUM_COMPANION_PROPERTY, "Ambiguous access to companion''s property ''{0}'' in enum is deprecated. Please, add explicit Companion qualifier to the class name", NAME);
|
||||
MAP.put(DEPRECATED_RESOLVE_WITH_AMBIGUOUS_ENUM_ENTRY, "Ambiguous access to property ''{0}'' is deprecated because similar enum entry ''{1}'' is available. Please add explicit named import or use fully qualified name", FQ_NAME, FQ_NAME);
|
||||
|
||||
MAP.put(PROTECTED_CONSTRUCTOR_NOT_IN_SUPER_CALL, "Protected constructor ''{0}'' from other classes can only be used in super-call", Renderers.SHORT_NAMES_IN_TYPES);
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.Constrain
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.*
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.getValidityConstraintForConstituentType
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.utils.IDEAPlatforms
|
||||
@@ -71,6 +72,9 @@ object Renderers {
|
||||
@JvmField
|
||||
val NAME = Renderer<Named> { it.name.asString() }
|
||||
|
||||
@JvmField
|
||||
val FQ_NAME = Renderer<MemberDescriptor> { it.fqNameSafe.asString() }
|
||||
|
||||
@JvmField
|
||||
val MODULE_WITH_PLATFORM = Renderer<ModuleDescriptor> { module ->
|
||||
val platform = module.platform
|
||||
|
||||
+9
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.builtins.UnsignedTypes
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
|
||||
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.*
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.BadNamedArgumentsTarget.*
|
||||
import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce
|
||||
@@ -107,6 +108,14 @@ class DiagnosticReporterByTrackingStrategy(
|
||||
CandidateChosenUsingOverloadResolutionByLambdaAnnotation::class.java -> {
|
||||
trace.report(CANDIDATE_CHOSEN_USING_OVERLOAD_RESOLUTION_BY_LAMBDA_ANNOTATION.on(psiKotlinCall.psiCall.callElement))
|
||||
}
|
||||
EnumEntryAmbiguityWarning::class.java -> {
|
||||
val propertyDescriptor = (diagnostic as EnumEntryAmbiguityWarning).property
|
||||
val enumEntryDescriptor = diagnostic.enumEntry
|
||||
val enumCompanionDescriptor = (enumEntryDescriptor.containingDeclaration as? ClassDescriptor)?.companionObjectDescriptor
|
||||
if (enumCompanionDescriptor == null || propertyDescriptor.containingDeclaration != enumCompanionDescriptor) {
|
||||
trace.report(DEPRECATED_RESOLVE_WITH_AMBIGUOUS_ENUM_ENTRY.on(psiKotlinCall.psiCall.callElement, propertyDescriptor, enumEntryDescriptor))
|
||||
}
|
||||
}
|
||||
CompatibilityWarning::class.java -> {
|
||||
val callElement = psiKotlinCall.psiCall.callElement
|
||||
trace.report(
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
package org.jetbrains.kotlin.resolve.calls
|
||||
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
|
||||
import org.jetbrains.kotlin.resolve.calls.components.*
|
||||
import org.jetbrains.kotlin.resolve.calls.components.candidate.ResolutionCandidate
|
||||
@@ -15,6 +17,7 @@ import org.jetbrains.kotlin.resolve.calls.context.CheckArgumentTypesMode
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.*
|
||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.OVERLOAD_RESOLUTION_BY_LAMBDA_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
@@ -218,31 +221,52 @@ class KotlinCallResolver(
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
maximallySpecificCandidates.size > 1 &&
|
||||
callComponents.languageVersionSettings.supportsFeature(LanguageFeature.OverloadResolutionByLambdaReturnType) &&
|
||||
candidates.all { resolutionCallbacks.inferenceSession.shouldRunCompletion(it) } &&
|
||||
kotlinCall.callKind != KotlinCallKind.CALLABLE_REFERENCE
|
||||
) {
|
||||
val candidatesWithAnnotation = candidates.filter {
|
||||
it.resolvedCall.candidateDescriptor.annotations.hasAnnotation(OVERLOAD_RESOLUTION_BY_LAMBDA_ANNOTATION_FQ_NAME)
|
||||
}.toSet()
|
||||
val candidatesWithoutAnnotation = candidates - candidatesWithAnnotation
|
||||
if (candidatesWithAnnotation.isNotEmpty()) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val newCandidates = kotlinCallCompleter.chooseCandidateRegardingOverloadResolutionByLambdaReturnType(
|
||||
maximallySpecificCandidates as Set<SimpleResolutionCandidate>,
|
||||
resolutionCallbacks
|
||||
)
|
||||
maximallySpecificCandidates = overloadingConflictResolver.chooseMaximallySpecificCandidates(
|
||||
newCandidates,
|
||||
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
|
||||
discriminateGenerics = true
|
||||
)
|
||||
if (maximallySpecificCandidates.size > 1) {
|
||||
if (maximallySpecificCandidates.size == 2) {
|
||||
val enumEntryCandidate = maximallySpecificCandidates.find {
|
||||
val descriptor = it.resolvedCall.candidateDescriptor
|
||||
descriptor is FakeCallableDescriptorForObject && descriptor.classDescriptor.kind == ClassKind.ENUM_ENTRY
|
||||
}
|
||||
if (enumEntryCandidate != null) {
|
||||
val otherCandidate = maximallySpecificCandidates.find {
|
||||
val candidateDescriptor = it.resolvedCall.candidateDescriptor
|
||||
candidateDescriptor !is FakeCallableDescriptorForObject
|
||||
}
|
||||
if (otherCandidate != null) {
|
||||
val propertyDescriptor = otherCandidate.resolvedCall.candidateDescriptor
|
||||
if (propertyDescriptor is PropertyDescriptor) {
|
||||
val enumEntryDescriptor =
|
||||
(enumEntryCandidate.resolvedCall.candidateDescriptor as FakeCallableDescriptorForObject).classDescriptor
|
||||
otherCandidate.addDiagnostic(EnumEntryAmbiguityWarning(propertyDescriptor, enumEntryDescriptor))
|
||||
return setOf(otherCandidate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (callComponents.languageVersionSettings.supportsFeature(LanguageFeature.OverloadResolutionByLambdaReturnType) &&
|
||||
candidates.all { resolutionCallbacks.inferenceSession.shouldRunCompletion(it) } &&
|
||||
kotlinCall.callKind != KotlinCallKind.CALLABLE_REFERENCE
|
||||
) {
|
||||
val candidatesWithAnnotation = candidates.filter {
|
||||
it.resolvedCall.candidateDescriptor.annotations.hasAnnotation(OVERLOAD_RESOLUTION_BY_LAMBDA_ANNOTATION_FQ_NAME)
|
||||
}.toSet()
|
||||
val candidatesWithoutAnnotation = candidates - candidatesWithAnnotation
|
||||
if (candidatesWithAnnotation.isNotEmpty()) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val newCandidates = kotlinCallCompleter.chooseCandidateRegardingOverloadResolutionByLambdaReturnType(
|
||||
maximallySpecificCandidates as Set<SimpleResolutionCandidate>,
|
||||
resolutionCallbacks
|
||||
)
|
||||
maximallySpecificCandidates = overloadingConflictResolver.chooseMaximallySpecificCandidates(
|
||||
newCandidates,
|
||||
CheckArgumentTypesMode.CHECK_VALUE_ARGUMENTS,
|
||||
discriminateGenerics = true
|
||||
)
|
||||
|
||||
if (maximallySpecificCandidates.size > 1 && candidatesWithoutAnnotation.any { it in maximallySpecificCandidates }) {
|
||||
maximallySpecificCandidates = maximallySpecificCandidates.toMutableSet().apply { removeAll(candidatesWithAnnotation) }
|
||||
maximallySpecificCandidates.singleOrNull()?.addDiagnostic(CandidateChosenUsingOverloadResolutionByLambdaAnnotation())
|
||||
if (maximallySpecificCandidates.size > 1 && candidatesWithoutAnnotation.any { it in maximallySpecificCandidates }) {
|
||||
maximallySpecificCandidates = maximallySpecificCandidates.toMutableSet().apply { removeAll(candidatesWithAnnotation) }
|
||||
maximallySpecificCandidates.singleOrNull()?.addDiagnostic(CandidateChosenUsingOverloadResolutionByLambdaAnnotation())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.resolve.calls.model
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.components.candidate.ResolutionCandidate
|
||||
@@ -292,6 +294,12 @@ class CandidateChosenUsingOverloadResolutionByLambdaAnnotation : KotlinCallDiagn
|
||||
}
|
||||
}
|
||||
|
||||
class EnumEntryAmbiguityWarning(val property: PropertyDescriptor, val enumEntry: ClassDescriptor) : KotlinCallDiagnostic(RESOLVED) {
|
||||
override fun report(reporter: DiagnosticReporter) {
|
||||
reporter.onCall(this)
|
||||
}
|
||||
}
|
||||
|
||||
class CompatibilityWarning(val candidate: CallableDescriptor) : KotlinCallDiagnostic(RESOLVED) {
|
||||
override fun report(reporter: DiagnosticReporter) {
|
||||
reporter.onCall(this)
|
||||
|
||||
+50
-1
@@ -16,8 +16,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.tower
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.components.candidate.ResolutionCandidate
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.DetailedReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.QualifierReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
|
||||
@@ -43,6 +46,52 @@ class PrioritizedCompositeScopeTowerProcessor<out C>(
|
||||
|
||||
}
|
||||
|
||||
class VariableAndObjectScopeTowerProcessor<out C : Candidate>(
|
||||
private val variableProcessor: ScopeTowerProcessor<C>,
|
||||
private val objectProcessor: ScopeTowerProcessor<C>
|
||||
) : ScopeTowerProcessor<C> {
|
||||
override fun process(data: TowerData): List<Collection<C>> {
|
||||
val variablesResult = variableProcessor.process(data)
|
||||
val objectResult = objectProcessor.process(data)
|
||||
if (objectResult.isEmpty()) return variablesResult
|
||||
if (objectResult.none { level ->
|
||||
level.any {
|
||||
it.isEnumEntryCandidate()
|
||||
}
|
||||
}
|
||||
) return variablesResult + objectResult
|
||||
val result = mutableListOf<List<C>>()
|
||||
result.addAll(variablesResult.map { it.toMutableList() })
|
||||
for ((index, objectLevel) in objectResult.withIndex()) {
|
||||
val enumEntryLevel = objectLevel.filter { it.isEnumEntryCandidate() }
|
||||
if (enumEntryLevel.isEmpty()) continue
|
||||
if (index < variablesResult.size) {
|
||||
// It's guaranteed this element is a mutable list
|
||||
(result[index] as MutableList).addAll(enumEntryLevel)
|
||||
} else {
|
||||
result.add(enumEntryLevel)
|
||||
}
|
||||
}
|
||||
for (objectLevel in objectResult) {
|
||||
val nonEnumEntryLevel = objectLevel.filter { !it.isEnumEntryCandidate() }
|
||||
if (nonEnumEntryLevel.isEmpty()) continue
|
||||
result.add(nonEnumEntryLevel)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun Candidate.isEnumEntryCandidate(): Boolean {
|
||||
if (this !is ResolutionCandidate) return false
|
||||
val callableDescriptor = resolvedCall.candidateDescriptor as? FakeCallableDescriptorForObject ?: return false
|
||||
return callableDescriptor.classDescriptor.kind == ClassKind.ENUM_ENTRY
|
||||
}
|
||||
|
||||
override fun recordLookups(skippedData: Collection<TowerData>, name: Name) {
|
||||
variableProcessor.recordLookups(skippedData, name)
|
||||
objectProcessor.recordLookups(skippedData, name)
|
||||
}
|
||||
}
|
||||
|
||||
// use this if all processors has same priority
|
||||
class SamePriorityCompositeScopeTowerProcessor<out C>(
|
||||
private vararg val processors: SimpleScopeTowerProcessor<C>
|
||||
@@ -244,7 +293,7 @@ fun <C : Candidate> createVariableProcessor(
|
||||
fun <C : Candidate> createVariableAndObjectProcessor(
|
||||
scopeTower: ImplicitScopeTower, name: Name,
|
||||
context: CandidateFactory<C>, explicitReceiver: DetailedReceiver?, classValueReceiver: Boolean = true
|
||||
) = PrioritizedCompositeScopeTowerProcessor(
|
||||
) = VariableAndObjectScopeTowerProcessor(
|
||||
createVariableProcessor(scopeTower, name, context, explicitReceiver),
|
||||
createSimpleProcessor(scopeTower, context, explicitReceiver, classValueReceiver) { getObjects(name, it) }
|
||||
)
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
/test.kt:26:9: warning: ambiguous access to property 'first.KtNodeTypes.SOME' is deprecated because similar enum entry 'second.SomeEnum.SOME' is available. Please add explicit named import or use fully qualified name
|
||||
SOME -> true
|
||||
^
|
||||
@@ -1,4 +1,5 @@
|
||||
// KT-49200
|
||||
// !RENDER_DIAGNOSTICS_FULL_TEXT
|
||||
// FILE: first/KtNodeTypes.java
|
||||
|
||||
package first;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// KT-49200
|
||||
// !RENDER_DIAGNOSTICS_FULL_TEXT
|
||||
// FILE: first/KtNodeTypes.java
|
||||
|
||||
package first;
|
||||
@@ -22,7 +23,7 @@ import second.SomeEnum.*
|
||||
|
||||
fun test(arg: String): Boolean {
|
||||
return when (arg) {
|
||||
SOME -> true
|
||||
<!DEPRECATED_RESOLVE_WITH_AMBIGUOUS_ENUM_ENTRY!>SOME<!> -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user