Report EXPLICIT_DELEGATION_CALL_REQUIRED on relevant element

^KT-38959 Fixed
This commit is contained in:
Vladimir Dolzhenko
2020-10-02 12:31:25 +02:00
committed by Vladimir Dolzhenko
parent 7af564c9f2
commit 5eac949b43
11 changed files with 92 additions and 27 deletions
@@ -385,17 +385,17 @@ public interface Errors {
DiagnosticFactory0<KtDeclaration> CONSTRUCTOR_IN_OBJECT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<KtSuperTypeCallEntry> SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtConstructorDelegationCall> PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED =
DiagnosticFactory0<PsiElement> PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED =
DiagnosticFactory0.create(ERROR, PositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL);
DiagnosticFactory0<KtConstructorDelegationCall> PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED_IN_ENUM =
DiagnosticFactory0<PsiElement> PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED_IN_ENUM =
DiagnosticFactory0.create(WARNING, PositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL);
DiagnosticFactory0<KtConstructorDelegationReferenceExpression> DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR =
DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtConstructorDelegationCall> EXPLICIT_DELEGATION_CALL_REQUIRED =
DiagnosticFactory0<PsiElement> EXPLICIT_DELEGATION_CALL_REQUIRED =
DiagnosticFactory0.create(ERROR, PositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL);
DiagnosticFactory1<PsiElement, DeclarationDescriptor> INSTANCE_ACCESS_BEFORE_SUPER_CALL = DiagnosticFactory1.create(ERROR);
@@ -1108,7 +1108,7 @@ public interface Errors {
DiagnosticFactory1<KtExpression, ClassifierDescriptorWithTypeParameters> NESTED_CLASS_ACCESSED_VIA_INSTANCE_REFERENCE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<KtExpression, ClassDescriptor, String> NESTED_CLASS_SHOULD_BE_QUALIFIED = DiagnosticFactory2.create(ERROR);
DiagnosticFactory1<PsiElement, ClassDescriptor> INACCESSIBLE_OUTER_CLASS_EXPRESSION = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, ClassDescriptor> INACCESSIBLE_OUTER_CLASS_EXPRESSION = DiagnosticFactory1.create(ERROR, SECONDARY_CONSTRUCTOR_DELEGATION_CALL);
DiagnosticFactory1<KtClassOrObject, String> NESTED_CLASS_NOT_ALLOWED = DiagnosticFactory1.create(ERROR, DECLARATION_NAME);
DiagnosticFactory1<KtClassOrObject, String> NESTED_CLASS_DEPRECATED = DiagnosticFactory1.create(WARNING, DECLARATION_NAME);
@@ -46,10 +46,7 @@ object PositioningStrategies {
is KtObjectLiteralExpression -> {
val objectDeclaration = element.objectDeclaration
val objectKeyword = objectDeclaration.getObjectKeyword()!!
val delegationSpecifierList = objectDeclaration.getSuperTypeList()
if (delegationSpecifierList == null) {
return markElement(objectKeyword)
}
val delegationSpecifierList = objectDeclaration.getSuperTypeList() ?: return markElement(objectKeyword)
return markRange(objectKeyword, delegationSpecifierList)
}
is KtObjectDeclaration -> {
@@ -612,15 +609,26 @@ object PositioningStrategies {
}
@JvmField
val SECONDARY_CONSTRUCTOR_DELEGATION_CALL: PositioningStrategy<KtConstructorDelegationCall> =
object : PositioningStrategy<KtConstructorDelegationCall>() {
override fun mark(element: KtConstructorDelegationCall): List<TextRange> {
if (element.isImplicit) {
val constructor = element.getStrictParentOfType<KtSecondaryConstructor>()!!
val valueParameterList = constructor.valueParameterList ?: return markElement(constructor)
return markRange(constructor.getConstructorKeyword(), valueParameterList.lastChild)
val SECONDARY_CONSTRUCTOR_DELEGATION_CALL: PositioningStrategy<PsiElement> =
object : PositioningStrategy<PsiElement>() {
override fun mark(element: PsiElement): List<TextRange> {
when (element) {
is KtSecondaryConstructor -> {
val valueParameterList = element.valueParameterList ?: return markElement(element)
return markRange(element.getConstructorKeyword(), valueParameterList.lastChild)
}
is KtConstructorDelegationCall -> {
if (element.isImplicit) {
// TODO: [VD] FIR collects for some reason implicit KtConstructorDelegationCall
// check(!element.isImplicit) { "Implicit KtConstructorDelegationCall should not be collected directly" }
val constructor = element.getStrictParentOfType<KtSecondaryConstructor>()!!
val valueParameterList = constructor.valueParameterList ?: return markElement(constructor)
return markRange(constructor.getConstructorKeyword(), valueParameterList.lastChild)
}
return markElement(element.calleeExpression ?: element)
}
else -> error("unexpected element $element")
}
return markElement(element.calleeExpression ?: element)
}
}
@@ -42,6 +42,7 @@ import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.CallResolverUtilKt;
import org.jetbrains.kotlin.resolve.calls.components.InferenceSession;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory;
@@ -1371,7 +1372,9 @@ public class DescriptorResolver {
}
if (isStaticNestedClass(classDescriptor)) {
trace.report(INACCESSIBLE_OUTER_CLASS_EXPRESSION.on(reportErrorsOn, classDescriptor));
PsiElement onReport = (reportErrorsOn instanceof KtConstructorDelegationCall)
? CallResolverUtilKt.reportOnElement((KtConstructorDelegationCall) reportErrorsOn) : reportErrorsOn;
trace.report(INACCESSIBLE_OUTER_CLASS_EXPRESSION.on(onReport, classDescriptor));
return false;
}
classDescriptor = getParentOfType(classDescriptor, ClassDescriptor.class, true);
@@ -446,15 +446,15 @@ public class CallResolver {
if (constructorDescriptor.getConstructedClass().getKind() == ClassKind.ENUM_CLASS && call.isImplicit()) {
if (currentClassDescriptor.getUnsubstitutedPrimaryConstructor() != null) {
DiagnosticFactory0<KtConstructorDelegationCall> warningOrError;
DiagnosticFactory0<PsiElement> warningOrError;
if (languageVersionSettings.supportsFeature(LanguageFeature.RequiredPrimaryConstructorDelegationCallInEnums)) {
warningOrError = PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED; // error
} else {
warningOrError = PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED_IN_ENUM; // warning
}
context.trace.report(warningOrError.on((KtConstructorDelegationCall) calleeExpression.getParent()));
PsiElement reportOn = calcReportOn(calleeExpression);
context.trace.report(warningOrError.on(reportOn));
}
return null;
}
@@ -484,9 +484,8 @@ public class CallResolver {
if (!isThisCall && currentClassDescriptor.getUnsubstitutedPrimaryConstructor() != null) {
if (DescriptorUtils.canHaveDeclaredConstructors(currentClassDescriptor)) {
// Diagnostic is meaningless when reporting on interfaces and object
context.trace.report(PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED.on(
(KtConstructorDelegationCall) calleeExpression.getParent()
));
PsiElement reportOn = calcReportOn(calleeExpression);
context.trace.report(PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED.on(reportOn));
}
if (call.isImplicit()) return OverloadResolutionResultsImpl.nameNotFound();
}
@@ -520,6 +519,13 @@ public class CallResolver {
return computeTasksFromCandidatesAndResolvedCall(context, candidates, tracing);
}
@Nullable
private PsiElement calcReportOn(@NotNull KtConstructorDelegationReferenceExpression calleeExpression) {
PsiElement delegationCall = calleeExpression.getParent();
return delegationCall instanceof KtConstructorDelegationCall
? CallResolverUtilKt.reportOnElement((KtConstructorDelegationCall) delegationCall) : delegationCall;
}
@NotNull
private static Pair<Collection<ResolutionCandidate<ConstructorDescriptor>>, BasicCallResolutionContext> prepareCandidatesAndContextForConstructorCall(
@NotNull KotlinType superType,
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.CallTransformer
import org.jetbrains.kotlin.resolve.calls.components.isVararg
@@ -320,3 +321,7 @@ fun createResolutionCandidatesForConstructors(
ResolutionCandidate.create(call, it, dispatchReceiver, receiverKind, knownSubstitutor)
}
}
internal fun KtConstructorDelegationCall.reportOnElement() = if (this.isImplicit) {
this.getStrictParentOfType<KtSecondaryConstructor>()!!
} else this
@@ -22,12 +22,11 @@ import org.jetbrains.kotlin.diagnostics.Errors.UNRESOLVED_REFERENCE
import org.jetbrains.kotlin.diagnostics.Errors.UNRESOLVED_REFERENCE_WRONG_RECEIVER
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.psi.KtConstructorDelegationCall
import org.jetbrains.kotlin.psi.KtLambdaArgument
import org.jetbrains.kotlin.psi.KtSecondaryConstructor
import org.jetbrains.kotlin.resolve.BindingContext.CALL
import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET
import org.jetbrains.kotlin.resolve.BindingContext.RESOLVED_CALL
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.reportOnElement
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
import org.jetbrains.kotlin.resolve.calls.inference.InferenceErrorData
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
@@ -83,8 +82,9 @@ class TracingStrategyForImplicitConstructorDelegationCall(
}
private fun reportError(trace: BindingTrace) {
if (!trace.bindingContext.diagnostics.forElement(delegationCall).any { it.factory == Errors.EXPLICIT_DELEGATION_CALL_REQUIRED }) {
trace.report(Errors.EXPLICIT_DELEGATION_CALL_REQUIRED.on(delegationCall))
val reportOn = delegationCall.reportOnElement()
if (!trace.bindingContext.diagnostics.forElement(reportOn).any { it.factory == Errors.EXPLICIT_DELEGATION_CALL_REQUIRED }) {
trace.report(Errors.EXPLICIT_DELEGATION_CALL_REQUIRED.on(reportOn))
}
}
@@ -274,6 +274,18 @@ abstract class BaseDiagnosticsTest : KotlinMultiFileTestWithJava<TestModule, Tes
whatDiagnosticsToConsider.value(it.diagnostic)
}
filteredDiagnostics.map { it.diagnostic }.forEach { diagnostic ->
val diagnosticElementTextRange = diagnostic.psiElement.textRange
diagnostic.textRanges.forEach {
check(diagnosticElementTextRange.contains(it)) {
"Annotation API violation:" +
" diagnostic text range $it has to be in range of" +
" diagnostic element ${diagnostic.psiElement} '${diagnostic.psiElement.text}'" +
" (factory ${diagnostic.factory.name}): $diagnosticElementTextRange"
}
}
}
actualDiagnostics.addAll(filteredDiagnostics)
val uncheckedDiagnostics = mutableListOf<PositionalTextDiagnostic>()
@@ -40,6 +40,11 @@ public class FirHighlightingTestGenerated extends AbstractFirHighlightingTest {
runTest("idea/testData/highlighter/AutoCreatedItParameter.kt");
}
@TestMetadata("DelegatingCtor.kt")
public void testDelegatingCtor() throws Exception {
runTest("idea/testData/highlighter/DelegatingCtor.kt");
}
@TestMetadata("Destructuring.kt")
public void testDestructuring() throws Exception {
runTest("idea/testData/highlighter/Destructuring.kt");
@@ -38,6 +38,11 @@ public class PerformanceHighlightingTestGenerated extends AbstractPerformanceHig
runTest("idea/testData/highlighter/AutoCreatedItParameter.kt");
}
@TestMetadata("DelegatingCtor.kt")
public void testDelegatingCtor() throws Exception {
runTest("idea/testData/highlighter/DelegatingCtor.kt");
}
@TestMetadata("Destructuring.kt")
public void testDestructuring() throws Exception {
runTest("idea/testData/highlighter/Destructuring.kt");
+16
View File
@@ -0,0 +1,16 @@
<info textAttributesKey="KOTLIN_BUILTIN_ANNOTATION">open</info> class <info textAttributesKey="KOTLIN_CLASS">Foo</info> {
<info textAttributesKey="KOTLIN_KEYWORD">constructor</info>(<warning descr="[UNUSED_PARAMETER] Parameter 'i' is never used" textAttributesKey="NOT_USED_ELEMENT_ATTRIBUTES"><info textAttributesKey="KOTLIN_PARAMETER">i</info></warning>: <info textAttributesKey="KOTLIN_CLASS">Int</info>)
}
class <info textAttributesKey="KOTLIN_CLASS">Bar</info> : <info textAttributesKey="KOTLIN_CLASS">Foo</info> {
<error descr="[EXPLICIT_DELEGATION_CALL_REQUIRED] Explicit 'this' or 'super' call is required. There is no constructor in superclass that can be called without arguments" textAttributesKey="ERRORS_ATTRIBUTES"><info textAttributesKey="KOTLIN_KEYWORD">constructor</info>(s: String)</error>
}
class <info textAttributesKey="KOTLIN_CLASS">F</info>(<warning descr="[UNUSED_PARAMETER] Parameter 'foo' is never used" textAttributesKey="NOT_USED_ELEMENT_ATTRIBUTES"><info textAttributesKey="KOTLIN_PARAMETER">foo</info></warning>: <info textAttributesKey="KOTLIN_CLASS">String</info>) {
<error descr="[PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED] Primary constructor call expected" textAttributesKey="ERRORS_ATTRIBUTES"><info textAttributesKey="KOTLIN_KEYWORD">constructor</info>()</error> {}
}
<info textAttributesKey="KOTLIN_BUILTIN_ANNOTATION">enum</info> class <info textAttributesKey="KOTLIN_ENUM">E</info>(val <info textAttributesKey="KOTLIN_INSTANCE_PROPERTY">a</info>: <info textAttributesKey="KOTLIN_CLASS">String</info>) {
<info textAttributesKey="KOTLIN_ENUM_ENTRY">A</info>;
<warning descr="[PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED_IN_ENUM] Primary constructor call expected. It's going to be an error in 1.5." textAttributesKey="WARNING_ATTRIBUTES"><info textAttributesKey="KOTLIN_KEYWORD">constructor</info>()</warning>
}
@@ -38,6 +38,11 @@ public class HighlightingTestGenerated extends AbstractHighlightingTest {
runTest("idea/testData/highlighter/AutoCreatedItParameter.kt");
}
@TestMetadata("DelegatingCtor.kt")
public void testDelegatingCtor() throws Exception {
runTest("idea/testData/highlighter/DelegatingCtor.kt");
}
@TestMetadata("Destructuring.kt")
public void testDestructuring() throws Exception {
runTest("idea/testData/highlighter/Destructuring.kt");