Unify reporting resolution errors for empty delegation calls
- Always report "There is no applicable constructor for call without arguments in superclass" under `constructor` keyword by replacing default TracingStrategy - Remove positioning strategy used for reporting non-applicable for empty delegation calls #KT-6971 Fixed
This commit is contained in:
@@ -169,6 +169,7 @@ public interface Errors {
|
||||
DiagnosticFactory0.create(ERROR, PositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL);
|
||||
|
||||
DiagnosticFactory0<PsiElement> PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> EXPLICIT_DELEGATION_CALL_REQUIRED = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
// Trait-specific
|
||||
|
||||
@@ -411,8 +412,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<JetExpression> NOT_A_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> OVERLOAD_RESOLUTION_AMBIGUITY = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> NONE_APPLICABLE =
|
||||
DiagnosticFactory1.create(ERROR, PositioningStrategies.SECONDARY_CONSTRUCTOR_DELEGATION_CALL_OR_DEFAULT);
|
||||
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> NONE_APPLICABLE = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> CANNOT_COMPLETE_RESOLVE = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> UNRESOLVED_REFERENCE_WRONG_RECEIVER = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
|
||||
@@ -62,6 +62,9 @@ public object PositioningStrategies {
|
||||
element.getNameIdentifier() ?: element.getObjectKeyword()
|
||||
)
|
||||
}
|
||||
is JetConstructorDelegationCall -> {
|
||||
return SECONDARY_CONSTRUCTOR_DELEGATION_CALL.mark(element)
|
||||
}
|
||||
else -> {
|
||||
return super.mark(element)
|
||||
}
|
||||
@@ -422,17 +425,4 @@ public object PositioningStrategies {
|
||||
return markElement(element.getCalleeExpression() ?: element)
|
||||
}
|
||||
}
|
||||
|
||||
public val SECONDARY_CONSTRUCTOR_DELEGATION_CALL_OR_DEFAULT: PositioningStrategy<PsiElement> =
|
||||
object : PositioningStrategy<PsiElement>() {
|
||||
override fun mark(element: PsiElement): List<TextRange> {
|
||||
val parent = element.getParent()
|
||||
if (parent is JetConstructorDelegationCall) {
|
||||
return SECONDARY_CONSTRUCTOR_DELEGATION_CALL.mark(parent)
|
||||
}
|
||||
else {
|
||||
return DEFAULT.mark(element)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -397,6 +397,8 @@ public class DefaultErrorMessages {
|
||||
MAP.put(PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED, "Primary constructor call expected");
|
||||
MAP.put(DELEGATION_SUPER_CALL_IN_ENUM_CONSTRUCTOR, "Call to super is not allowed in enum constructor");
|
||||
MAP.put(PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS, "Primary constructor required for data class");
|
||||
MAP.put(EXPLICIT_DELEGATION_CALL_REQUIRED,
|
||||
"Explicit 'this' or 'super' call is required. There is no constructor in superclass that can be called without arguments");
|
||||
|
||||
MAP.put(INIT_KEYWORD_BEFORE_CLASS_INITIALIZER_EXPECTED, "Expecting 'init' keyword before class initializer");
|
||||
|
||||
|
||||
@@ -188,7 +188,17 @@ public class CallResolver {
|
||||
@NotNull Collection<ResolutionCandidate<D>> candidates,
|
||||
@NotNull CallTransformer<D, F> callTransformer
|
||||
) {
|
||||
TracingStrategy tracing = TracingStrategyImpl.create(referenceExpression, context.call);
|
||||
return computeTasksFromCandidatesAndResolvedCall(context, candidates, callTransformer,
|
||||
TracingStrategyImpl.create(referenceExpression, context.call));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private <D extends CallableDescriptor, F extends D> OverloadResolutionResults<F> computeTasksFromCandidatesAndResolvedCall(
|
||||
@NotNull BasicCallResolutionContext context,
|
||||
@NotNull Collection<ResolutionCandidate<D>> candidates,
|
||||
@NotNull CallTransformer<D, F> callTransformer,
|
||||
@NotNull TracingStrategy tracing
|
||||
) {
|
||||
List<ResolutionTask<D, F>> prioritizedTasks =
|
||||
taskPrioritizer.<D, F>computePrioritizedTasksFromCandidates(context, candidates, tracing);
|
||||
return doResolveCallOrGetCachedResults(context, prioritizedTasks, callTransformer, tracing);
|
||||
@@ -313,6 +323,7 @@ public class CallResolver {
|
||||
|
||||
return resolveConstructorDelegationCall(
|
||||
context,
|
||||
call,
|
||||
call.getCalleeExpression(),
|
||||
constructorDescriptor
|
||||
);
|
||||
@@ -321,6 +332,7 @@ public class CallResolver {
|
||||
@NotNull
|
||||
private OverloadResolutionResults<FunctionDescriptor> resolveConstructorDelegationCall(
|
||||
@NotNull BasicCallResolutionContext context,
|
||||
@NotNull JetConstructorDelegationCall call,
|
||||
@NotNull JetConstructorDelegationReferenceExpression calleeExpression,
|
||||
@NotNull ConstructorDescriptor calleeConstructor
|
||||
) {
|
||||
@@ -364,7 +376,11 @@ public class CallResolver {
|
||||
knownTypeParametersSubstitutor));
|
||||
}
|
||||
|
||||
return computeTasksFromCandidatesAndResolvedCall(context, calleeExpression, candidates, CallTransformer.FUNCTION_CALL_TRANSFORMER);
|
||||
TracingStrategy tracing = calleeExpression.isEmpty() ?
|
||||
new TracingStrategyForEmptyConstructorDelegationCall(call, context.call) :
|
||||
TracingStrategyImpl.create(calleeExpression, context.call);
|
||||
|
||||
return computeTasksFromCandidatesAndResolvedCall(context, candidates, CallTransformer.FUNCTION_CALL_TRANSFORMER, tracing);
|
||||
}
|
||||
|
||||
public OverloadResolutionResults<FunctionDescriptor> resolveCallWithKnownCandidate(
|
||||
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.tasks
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
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.JetConstructorDelegationCall
|
||||
import org.jetbrains.kotlin.psi.JetFunctionLiteralArgument
|
||||
import org.jetbrains.kotlin.psi.JetSecondaryConstructor
|
||||
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.inference.InferenceErrorData
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
|
||||
|
||||
public class TracingStrategyForEmptyConstructorDelegationCall(
|
||||
val delegationCall: JetConstructorDelegationCall, call: Call
|
||||
) : AbstractTracingStrategy(delegationCall.getCalleeExpression()!!, call) {
|
||||
|
||||
val calleeExpression = delegationCall.getCalleeExpression()
|
||||
|
||||
override fun bindCall(trace: BindingTrace, call: Call) {
|
||||
trace.record(CALL, call.getCalleeExpression(), call)
|
||||
}
|
||||
|
||||
override fun <D : CallableDescriptor> bindReference(trace: BindingTrace, resolvedCall: ResolvedCall<D>) {
|
||||
val descriptor = resolvedCall.getCandidateDescriptor()
|
||||
val storedReference = trace.get(REFERENCE_TARGET, calleeExpression)
|
||||
if (storedReference == null || !ErrorUtils.isError(descriptor)) {
|
||||
trace.record(REFERENCE_TARGET, calleeExpression, descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun <D : CallableDescriptor> bindResolvedCall(trace: BindingTrace, resolvedCall: ResolvedCall<D>) {
|
||||
trace.record(RESOLVED_CALL, call, resolvedCall)
|
||||
}
|
||||
|
||||
override fun unresolvedReference(trace: BindingTrace) {
|
||||
trace.report(UNRESOLVED_REFERENCE.on(calleeExpression, calleeExpression))
|
||||
}
|
||||
|
||||
override fun <D : CallableDescriptor> unresolvedReferenceWrongReceiver(trace: BindingTrace, candidates: Collection<ResolvedCall<D>>) {
|
||||
trace.report(UNRESOLVED_REFERENCE_WRONG_RECEIVER.on(reference, candidates))
|
||||
}
|
||||
|
||||
override fun noValueForParameter(trace: BindingTrace, valueParameter: ValueParameterDescriptor) {
|
||||
reportError(trace)
|
||||
}
|
||||
|
||||
override fun <D : CallableDescriptor?> ambiguity(trace: BindingTrace, descriptors: MutableCollection<out ResolvedCall<D>>) {
|
||||
reportError(trace)
|
||||
}
|
||||
|
||||
override fun <D : CallableDescriptor?> noneApplicable(trace: BindingTrace, descriptors: MutableCollection<out ResolvedCall<D>>) {
|
||||
reportError(trace)
|
||||
}
|
||||
|
||||
override fun invisibleMember(trace: BindingTrace, descriptor: DeclarationDescriptorWithVisibility) {
|
||||
reportError(trace)
|
||||
}
|
||||
|
||||
private fun reportError(trace: BindingTrace) {
|
||||
val declaration = delegationCall.getParent() as JetSecondaryConstructor
|
||||
trace.report(Errors.EXPLICIT_DELEGATION_CALL_REQUIRED.on(declaration.getConstructorKeyword()))
|
||||
}
|
||||
|
||||
// Underlying methods should not be called because such errors are impossible
|
||||
// when resolving delegation call
|
||||
override fun <D : CallableDescriptor?> cannotCompleteResolve(trace: BindingTrace, descriptors: MutableCollection<out ResolvedCall<D>>) {
|
||||
unexpectedError("cannotCompleteResolve")
|
||||
}
|
||||
|
||||
override fun instantiationOfAbstractClass(trace: BindingTrace) {
|
||||
unexpectedError("instantiationOfAbstractClass")
|
||||
}
|
||||
|
||||
override fun abstractSuperCall(trace: BindingTrace) {
|
||||
unexpectedError("abstractSuperCall")
|
||||
}
|
||||
|
||||
override fun nestedClassAccessViaInstanceReference(
|
||||
trace: BindingTrace, classDescriptor: ClassDescriptor, explicitReceiverKind: ExplicitReceiverKind
|
||||
) {
|
||||
unexpectedError("nestedClassAccessViaInstanceReference")
|
||||
}
|
||||
|
||||
override fun unsafeCall(trace: BindingTrace, type: JetType, isCallForImplicitInvoke: Boolean) {
|
||||
unexpectedError("unsafeCall")
|
||||
}
|
||||
|
||||
override fun unnecessarySafeCall(trace: BindingTrace, type: JetType) {
|
||||
unexpectedError("unnecessarySafeCall")
|
||||
}
|
||||
|
||||
override fun danglingFunctionLiteralArgumentSuspected(
|
||||
trace: BindingTrace, functionLiteralArguments: MutableList<JetFunctionLiteralArgument>
|
||||
) {
|
||||
unexpectedError("danglingFunctionLiteralArgumentSuspected")
|
||||
}
|
||||
|
||||
override fun missingReceiver(trace: BindingTrace, expectedReceiver: ReceiverParameterDescriptor) {
|
||||
unexpectedError("missingReceiver")
|
||||
}
|
||||
|
||||
override fun wrongReceiverType(trace: BindingTrace, receiverParameter: ReceiverParameterDescriptor, receiverArgument: ReceiverValue) {
|
||||
unexpectedError("wrongReceiverType")
|
||||
}
|
||||
|
||||
override fun noReceiverAllowed(trace: BindingTrace) {
|
||||
unexpectedError("noReceiverAllowed")
|
||||
}
|
||||
|
||||
override fun wrongNumberOfTypeArguments(trace: BindingTrace, expectedTypeArgumentCount: Int) {
|
||||
unexpectedError("wrongNumberOfTypeArguments")
|
||||
}
|
||||
|
||||
override fun typeInferenceFailed(trace: BindingTrace, data: InferenceErrorData) {
|
||||
unexpectedError("typeInferenceFailed")
|
||||
}
|
||||
|
||||
private fun unexpectedError(type: String) {
|
||||
throw AssertionError("Unexpected error type: $type")
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
open class B0(x: Int)
|
||||
|
||||
class A0 : B0 {
|
||||
<!EXPLICIT_DELEGATION_CALL_REQUIRED!>constructor<!>()
|
||||
constructor(x: Int) : super(<!NO_VALUE_FOR_PARAMETER!>)<!>
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
|
||||
open class B1 {
|
||||
constructor(x: Int = 1)
|
||||
constructor()
|
||||
}
|
||||
|
||||
class A1 : B1 {
|
||||
<!EXPLICIT_DELEGATION_CALL_REQUIRED!>constructor<!>()
|
||||
constructor(x: Int) : <!OVERLOAD_RESOLUTION_AMBIGUITY!>super<!>()
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
|
||||
open class B2 {
|
||||
constructor(x: Int)
|
||||
constructor(x: String)
|
||||
}
|
||||
|
||||
class A2 : B2 {
|
||||
<!EXPLICIT_DELEGATION_CALL_REQUIRED!>constructor<!>()
|
||||
constructor(x: Int) : <!NONE_APPLICABLE!>super<!>()
|
||||
}
|
||||
|
||||
// --------------------------
|
||||
|
||||
open class B3 {
|
||||
private constructor()
|
||||
}
|
||||
|
||||
class A3 : B3 {
|
||||
<!EXPLICIT_DELEGATION_CALL_REQUIRED!>constructor<!>()
|
||||
constructor(x: Int) : <!INVISIBLE_MEMBER!>super<!>()
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package
|
||||
|
||||
internal final class A0 : B0 {
|
||||
public constructor A0()
|
||||
public constructor A0(/*0*/ x: kotlin.Int)
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal final class A1 : B1 {
|
||||
public constructor A1()
|
||||
public constructor A1(/*0*/ x: kotlin.Int)
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal final class A2 : B2 {
|
||||
public constructor A2()
|
||||
public constructor A2(/*0*/ x: kotlin.Int)
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal final class A3 : B3 {
|
||||
public constructor A3()
|
||||
public constructor A3(/*0*/ x: kotlin.Int)
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal open class B0 {
|
||||
public constructor B0(/*0*/ x: kotlin.Int)
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal open class B1 {
|
||||
public constructor B1()
|
||||
public constructor B1(/*0*/ x: kotlin.Int = ...)
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal open class B2 {
|
||||
public constructor B2(/*0*/ x: kotlin.Int)
|
||||
public constructor B2(/*0*/ x: kotlin.String)
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal open class B3 {
|
||||
private constructor B3()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
open inner class Inner
|
||||
|
||||
class Nested : Inner {
|
||||
<!INACCESSIBLE_OUTER_CLASS_EXPRESSION!>constructor()<!>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package
|
||||
|
||||
internal final class A {
|
||||
public constructor A()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
internal open inner class Inner {
|
||||
public constructor Inner()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
internal final class Nested : A.Inner {
|
||||
public constructor Nested()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -6,5 +6,5 @@ open class B(x: Double) {
|
||||
trait C
|
||||
class A : B, C {
|
||||
constructor(): <!NONE_APPLICABLE!>super<!>(' ')
|
||||
<!NONE_APPLICABLE!>constructor<!>(x: Int)
|
||||
<!EXPLICIT_DELEGATION_CALL_REQUIRED!>constructor<!>(x: Int)
|
||||
}
|
||||
|
||||
@@ -10647,6 +10647,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("errorsOnEmptyDelegationCall.kt")
|
||||
public void testErrorsOnEmptyDelegationCall() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/secondaryConstructors/errorsOnEmptyDelegationCall.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("expectedInitKeywordOnInitializer.kt")
|
||||
public void testExpectedInitKeywordOnInitializer() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/secondaryConstructors/expectedInitKeywordOnInitializer.kt");
|
||||
@@ -10719,6 +10725,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nestedExtendsInner.kt")
|
||||
public void testNestedExtendsInner() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/secondaryConstructors/nestedExtendsInner.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noDefaultIfEmptySecondary.kt")
|
||||
public void testNoDefaultIfEmptySecondary() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/secondaryConstructors/noDefaultIfEmptySecondary.kt");
|
||||
|
||||
Reference in New Issue
Block a user