Captured value initialization is no more allowed #KT-10445 Fixed
This commit is contained in:
@@ -391,6 +391,22 @@ public class ControlFlowInformationProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isCapturedWrite(
|
||||||
|
@NotNull VariableDescriptor variableDescriptor,
|
||||||
|
@NotNull WriteValueInstruction writeValueInstruction
|
||||||
|
) {
|
||||||
|
DeclarationDescriptor containingDeclarationDescriptor = variableDescriptor.getContainingDeclaration();
|
||||||
|
if (containingDeclarationDescriptor instanceof ClassDescriptor) return false;
|
||||||
|
KtElement ownerElement = writeValueInstruction.getOwner().getCorrespondingElement();
|
||||||
|
DeclarationDescriptor writeOwnerDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, ownerElement);
|
||||||
|
if (containingDeclarationDescriptor.equals(writeOwnerDescriptor)) return false;
|
||||||
|
if (writeOwnerDescriptor instanceof ClassDescriptor) {
|
||||||
|
ClassDescriptor writeOwnerClass = (ClassDescriptor) writeOwnerDescriptor;
|
||||||
|
if (containingDeclarationDescriptor.equals(writeOwnerClass.getUnsubstitutedPrimaryConstructor())) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean checkValReassignment(
|
private boolean checkValReassignment(
|
||||||
@NotNull VariableInitContext ctxt,
|
@NotNull VariableInitContext ctxt,
|
||||||
@NotNull KtExpression expression,
|
@NotNull KtExpression expression,
|
||||||
@@ -434,7 +450,8 @@ public class ControlFlowInformationProvider {
|
|||||||
}
|
}
|
||||||
boolean isThisOrNoDispatchReceiver =
|
boolean isThisOrNoDispatchReceiver =
|
||||||
PseudocodeUtil.isThisOrNoDispatchReceiver(writeValueInstruction, trace.getBindingContext());
|
PseudocodeUtil.isThisOrNoDispatchReceiver(writeValueInstruction, trace.getBindingContext());
|
||||||
if ((mayBeInitializedNotHere || !hasBackingField || !isThisOrNoDispatchReceiver) && !variableDescriptor.isVar()) {
|
boolean captured = isCapturedWrite(variableDescriptor, writeValueInstruction);
|
||||||
|
if ((mayBeInitializedNotHere || !hasBackingField || !isThisOrNoDispatchReceiver || captured) && !variableDescriptor.isVar()) {
|
||||||
boolean hasReassignMethodReturningUnit = false;
|
boolean hasReassignMethodReturningUnit = false;
|
||||||
KtSimpleNameExpression operationReference = null;
|
KtSimpleNameExpression operationReference = null;
|
||||||
PsiElement parent = expression.getParent();
|
PsiElement parent = expression.getParent();
|
||||||
@@ -465,7 +482,12 @@ public class ControlFlowInformationProvider {
|
|||||||
}
|
}
|
||||||
if (!hasReassignMethodReturningUnit) {
|
if (!hasReassignMethodReturningUnit) {
|
||||||
if (!isThisOrNoDispatchReceiver || !varWithValReassignErrorGenerated.contains(variableDescriptor)) {
|
if (!isThisOrNoDispatchReceiver || !varWithValReassignErrorGenerated.contains(variableDescriptor)) {
|
||||||
report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor), ctxt);
|
if (captured && !mayBeInitializedNotHere && hasBackingField && isThisOrNoDispatchReceiver) {
|
||||||
|
report(Errors.CAPTURED_VAL_INITIALIZATION.on(expression, variableDescriptor), ctxt);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor), ctxt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isThisOrNoDispatchReceiver) {
|
if (isThisOrNoDispatchReceiver) {
|
||||||
// try to get rid of repeating VAL_REASSIGNMENT diagnostic only for vars with no receiver
|
// try to get rid of repeating VAL_REASSIGNMENT diagnostic only for vars with no receiver
|
||||||
|
|||||||
@@ -645,6 +645,7 @@ public interface Errors {
|
|||||||
DiagnosticFactory0<KtLambdaExpression> UNUSED_LAMBDA_EXPRESSION = DiagnosticFactory0.create(WARNING);
|
DiagnosticFactory0<KtLambdaExpression> UNUSED_LAMBDA_EXPRESSION = DiagnosticFactory0.create(WARNING);
|
||||||
|
|
||||||
DiagnosticFactory1<KtExpression, DeclarationDescriptor> VAL_REASSIGNMENT = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<KtExpression, DeclarationDescriptor> VAL_REASSIGNMENT = DiagnosticFactory1.create(ERROR);
|
||||||
|
DiagnosticFactory1<KtExpression, DeclarationDescriptor> CAPTURED_VAL_INITIALIZATION = DiagnosticFactory1.create(ERROR);
|
||||||
DiagnosticFactory1<KtExpression, DeclarationDescriptor> SETTER_PROJECTED_OUT = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<KtExpression, DeclarationDescriptor> SETTER_PROJECTED_OUT = DiagnosticFactory1.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory1<KtExpression, DeclarationDescriptor> INITIALIZATION_BEFORE_DECLARATION = DiagnosticFactory1.create(ERROR);
|
DiagnosticFactory1<KtExpression, DeclarationDescriptor> INITIALIZATION_BEFORE_DECLARATION = DiagnosticFactory1.create(ERROR);
|
||||||
|
|||||||
+1
@@ -268,6 +268,7 @@ public class DefaultErrorMessages {
|
|||||||
MAP.put(UNUSED_LAMBDA_EXPRESSION, "The lambda expression is unused. If you mean a block, you can use 'run { ... }'");
|
MAP.put(UNUSED_LAMBDA_EXPRESSION, "The lambda expression is unused. If you mean a block, you can use 'run { ... }'");
|
||||||
|
|
||||||
MAP.put(VAL_REASSIGNMENT, "Val cannot be reassigned", NAME);
|
MAP.put(VAL_REASSIGNMENT, "Val cannot be reassigned", NAME);
|
||||||
|
MAP.put(CAPTURED_VAL_INITIALIZATION, "Captured values initialization is forbidden due to possible reassignment", NAME);
|
||||||
MAP.put(SETTER_PROJECTED_OUT, "Setter for ''{0}'' is removed by type projection", NAME);
|
MAP.put(SETTER_PROJECTED_OUT, "Setter for ''{0}'' is removed by type projection", NAME);
|
||||||
MAP.put(INVISIBLE_SETTER, "Cannot assign to ''{0}'': the setter is ''{1}'' in {2}", NAME, TO_STRING, NAME_OF_PARENT_OR_FILE);
|
MAP.put(INVISIBLE_SETTER, "Cannot assign to ''{0}'': the setter is ''{1}'' in {2}", NAME, TO_STRING, NAME_OF_PARENT_OR_FILE);
|
||||||
MAP.put(INITIALIZATION_BEFORE_DECLARATION, "Variable cannot be initialized before declaration", NAME);
|
MAP.put(INITIALIZATION_BEFORE_DECLARATION, "Variable cannot be initialized before declaration", NAME);
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
// !DIAGNOSTICS: -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE
|
||||||
|
|
||||||
|
fun ignoreIt(<!UNUSED_PARAMETER!>f<!>: () -> Unit) {}
|
||||||
|
|
||||||
|
fun exec(f: () -> Unit) = f()
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
var x: Int
|
||||||
|
ignoreIt() {
|
||||||
|
// Ok
|
||||||
|
x = 42
|
||||||
|
}
|
||||||
|
// Error!
|
||||||
|
<!UNINITIALIZED_VARIABLE!>x<!>.hashCode()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
val x: Int
|
||||||
|
exec {
|
||||||
|
<!CAPTURED_VAL_INITIALIZATION!>x<!> = 13
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar2() {
|
||||||
|
val x: Int
|
||||||
|
fun foo() {
|
||||||
|
<!CAPTURED_VAL_INITIALIZATION!>x<!> = 3
|
||||||
|
}
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
class My(val cond: Boolean) {
|
||||||
|
|
||||||
|
val y: Int
|
||||||
|
|
||||||
|
init {
|
||||||
|
val x: Int
|
||||||
|
if (cond) {
|
||||||
|
exec {
|
||||||
|
|
||||||
|
}
|
||||||
|
x = 1
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
x = 2
|
||||||
|
}
|
||||||
|
y = x
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(): this(false) {
|
||||||
|
val x: Int
|
||||||
|
x = 2
|
||||||
|
exec {
|
||||||
|
x.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Your {
|
||||||
|
val y = if (true) {
|
||||||
|
val xx: Int
|
||||||
|
exec {
|
||||||
|
<!CAPTURED_VAL_INITIALIZATION!>xx<!> = 42
|
||||||
|
}
|
||||||
|
24
|
||||||
|
}
|
||||||
|
else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
val z = if (true) {
|
||||||
|
val xx: Int
|
||||||
|
exec {
|
||||||
|
<!CAPTURED_VAL_INITIALIZATION!>xx<!> = 24
|
||||||
|
}
|
||||||
|
42
|
||||||
|
}
|
||||||
|
else 0
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public val z: kotlin.Int
|
||||||
|
public fun bar(): kotlin.Unit
|
||||||
|
public fun bar2(): kotlin.Unit
|
||||||
|
public fun exec(/*0*/ f: () -> kotlin.Unit): kotlin.Unit
|
||||||
|
public fun foo(): kotlin.Unit
|
||||||
|
public fun ignoreIt(/*0*/ f: () -> kotlin.Unit): kotlin.Unit
|
||||||
|
|
||||||
|
public final class My {
|
||||||
|
public constructor My()
|
||||||
|
public constructor My(/*0*/ cond: kotlin.Boolean)
|
||||||
|
public final val cond: kotlin.Boolean
|
||||||
|
public final val y: 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
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class Your {
|
||||||
|
public constructor Your()
|
||||||
|
public final val y: 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
|
||||||
|
}
|
||||||
@@ -2889,9 +2889,15 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("initializationInLambda.kt")
|
@TestMetadata("initInLambda.kt")
|
||||||
public void testInitializationInLambda() throws Exception {
|
public void testInitInLambda() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLambda.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/initInLambda.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("initializationInLocalFun.kt")
|
||||||
|
public void testInitializationInLocalFun() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/initializationInLocalFun.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,16 +93,21 @@ object J2KPostProcessingRegistrar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerDiagnosticBasedProcessingFactory(
|
registerDiagnosticBasedProcessingFactory(
|
||||||
Errors.VAL_REASSIGNMENT,
|
Errors.VAL_REASSIGNMENT, Errors.CAPTURED_VAL_INITIALIZATION
|
||||||
fun (element: KtSimpleNameExpression, diagnostic: Diagnostic): (() -> Unit)? {
|
) {
|
||||||
val property = element.mainReference.resolve() as? KtProperty ?: return null
|
element: KtSimpleNameExpression, diagnostic: Diagnostic ->
|
||||||
return {
|
val property = element.mainReference.resolve() as? KtProperty
|
||||||
if (!property.isVar) {
|
if (property == null) {
|
||||||
property.valOrVarKeyword.replace(KtPsiFactory(element.project).createVarKeyword())
|
null
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
{
|
||||||
|
if (!property.isVar) {
|
||||||
|
property.valOrVarKeyword.replace(KtPsiFactory(element.project).createVarKeyword())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified TElement : KtElement, TIntention: SelfTargetingRangeIntention<TElement>> registerIntentionBasedProcessing(
|
private inline fun <reified TElement : KtElement, TIntention: SelfTargetingRangeIntention<TElement>> registerIntentionBasedProcessing(
|
||||||
@@ -132,20 +137,20 @@ object J2KPostProcessingRegistrar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified TElement : KtElement> registerDiagnosticBasedProcessing(
|
private inline fun <reified TElement : KtElement> registerDiagnosticBasedProcessing(
|
||||||
diagnosticFactory: DiagnosticFactory<*>,
|
vararg diagnosticFactory: DiagnosticFactory<*>,
|
||||||
crossinline fix: (TElement, Diagnostic) -> Unit
|
crossinline fix: (TElement, Diagnostic) -> Unit
|
||||||
) {
|
) {
|
||||||
registerDiagnosticBasedProcessingFactory(diagnosticFactory) { element: TElement, diagnostic: Diagnostic -> { fix(element, diagnostic) } }
|
registerDiagnosticBasedProcessingFactory(*diagnosticFactory) { element: TElement, diagnostic: Diagnostic -> { fix(element, diagnostic) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun <reified TElement : KtElement> registerDiagnosticBasedProcessingFactory(
|
private inline fun <reified TElement : KtElement> registerDiagnosticBasedProcessingFactory(
|
||||||
diagnosticFactory: DiagnosticFactory<*>,
|
vararg diagnosticFactory: DiagnosticFactory<*>,
|
||||||
crossinline fixFactory: (TElement, Diagnostic) -> (() -> Unit)?
|
crossinline fixFactory: (TElement, Diagnostic) -> (() -> Unit)?
|
||||||
) {
|
) {
|
||||||
_processings.add(object : J2kPostProcessing {
|
_processings.add(object : J2kPostProcessing {
|
||||||
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
|
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
|
||||||
if (!TElement::class.java.isInstance(element)) return null
|
if (!TElement::class.java.isInstance(element)) return null
|
||||||
val diagnostic = diagnostics.forElement(element).firstOrNull { it.factory == diagnosticFactory } ?: return null
|
val diagnostic = diagnostics.forElement(element).firstOrNull { it.factory in diagnosticFactory } ?: return null
|
||||||
return fixFactory(element as TElement, diagnostic)
|
return fixFactory(element as TElement, diagnostic)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ import com.intellij.codeInsight.intention.IntentionAction
|
|||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -51,14 +53,18 @@ class ChangeVariableMutabilityFix(element: KtValVarKeywordOwner, private val mak
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val VAL_REASSIGNMENT_FACTORY: KotlinSingleIntentionActionFactory = object: KotlinSingleIntentionActionFactory() {
|
class ReassignmentActionFactory(val factory: DiagnosticFactory1<*, DeclarationDescriptor>) : KotlinSingleIntentionActionFactory() {
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
val propertyDescriptor = Errors.VAL_REASSIGNMENT.cast(diagnostic).a
|
val propertyDescriptor = factory.cast(diagnostic).a
|
||||||
val declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor) as? KtValVarKeywordOwner ?: return null
|
val declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor) as? KtValVarKeywordOwner ?: return null
|
||||||
return ChangeVariableMutabilityFix(declaration, true)
|
return ChangeVariableMutabilityFix(declaration, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val VAL_REASSIGNMENT_FACTORY = ReassignmentActionFactory(Errors.VAL_REASSIGNMENT)
|
||||||
|
|
||||||
|
val CAPTURED_VAL_INITIALIZATION_FACTORY = ReassignmentActionFactory(Errors.CAPTURED_VAL_INITIALIZATION)
|
||||||
|
|
||||||
val VAR_OVERRIDDEN_BY_VAL_FACTORY: KotlinSingleIntentionActionFactory = object: KotlinSingleIntentionActionFactory() {
|
val VAR_OVERRIDDEN_BY_VAL_FACTORY: KotlinSingleIntentionActionFactory = object: KotlinSingleIntentionActionFactory() {
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
val element = diagnostic.psiElement
|
val element = diagnostic.psiElement
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ class QuickFixRegistrar : QuickFixContributor {
|
|||||||
|
|
||||||
VAL_WITH_SETTER.registerFactory(ChangeVariableMutabilityFix.VAL_WITH_SETTER_FACTORY)
|
VAL_WITH_SETTER.registerFactory(ChangeVariableMutabilityFix.VAL_WITH_SETTER_FACTORY)
|
||||||
VAL_REASSIGNMENT.registerFactory(ChangeVariableMutabilityFix.VAL_REASSIGNMENT_FACTORY)
|
VAL_REASSIGNMENT.registerFactory(ChangeVariableMutabilityFix.VAL_REASSIGNMENT_FACTORY)
|
||||||
|
CAPTURED_VAL_INITIALIZATION.registerFactory(ChangeVariableMutabilityFix.CAPTURED_VAL_INITIALIZATION_FACTORY)
|
||||||
VAR_OVERRIDDEN_BY_VAL.registerFactory(ChangeVariableMutabilityFix.VAR_OVERRIDDEN_BY_VAL_FACTORY)
|
VAR_OVERRIDDEN_BY_VAL.registerFactory(ChangeVariableMutabilityFix.VAR_OVERRIDDEN_BY_VAL_FACTORY)
|
||||||
|
|
||||||
VAL_OR_VAR_ON_FUN_PARAMETER.registerFactory(RemoveValVarFromParameterFix)
|
VAL_OR_VAR_ON_FUN_PARAMETER.registerFactory(RemoveValVarFromParameterFix)
|
||||||
|
|||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// "Make variable mutable" "true"
|
||||||
|
fun exec(f: () -> Unit) = f()
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val x: Int
|
||||||
|
exec {
|
||||||
|
<caret>x = 42
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// "Make variable mutable" "true"
|
||||||
|
fun exec(f: () -> Unit) = f()
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
var x: Int
|
||||||
|
exec {
|
||||||
|
x = 42
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7728,6 +7728,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/variables/changeMutability"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/variables/changeMutability"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("capturedValInitialization.kt")
|
||||||
|
public void testCapturedValInitialization() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/variables/changeMutability/capturedValInitialization.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("funParameter.kt")
|
@TestMetadata("funParameter.kt")
|
||||||
public void testFunParameter() throws Exception {
|
public void testFunParameter() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/variables/changeMutability/funParameter.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/variables/changeMutability/funParameter.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user