From 2185ac958d86d58e1b93d02dc5003a58dcd31e3b Mon Sep 17 00:00:00 2001 From: LakshanWeerasinghe Date: Thu, 7 Dec 2023 16:10:59 +0530 Subject: [PATCH 1/5] Make function rest param final --- .../compiler/semantics/analyzer/SemanticAnalyzer.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java index 1398f14a277d..e80581b6f04f 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java @@ -497,6 +497,10 @@ public void visit(BLangFunction funcNode, AnalyzerData data) { // TODO: Shouldn't this be done in symbol enter? //set function param flag to final funcNode.symbol.params.forEach(param -> param.flags |= Flags.FUNCTION_FINAL); + + if (funcNode.symbol.restParam != null) { + funcNode.symbol.restParam.flags |= Flags.FUNCTION_FINAL; + } if (!funcNode.flagSet.contains(Flag.WORKER)) { // annotation validation for workers is done for the invocation. From 7c6f5e3afcb912830466e04f5a204cff70ad77fe Mon Sep 17 00:00:00 2001 From: LakshanWeerasinghe Date: Thu, 7 Dec 2023 16:30:51 +0530 Subject: [PATCH 2/5] Add tests for making rest params final --- .../ballerinalang/test/types/finaltypes/FinalAccessTest.java | 4 +++- .../types/finaltypes/test_implicitly_final_negative.bal | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java index db77c3b08be6..e1aa02d9a924 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java @@ -47,7 +47,7 @@ public void setup() { public void testFinalFailCase() { CompileResult compileResultNegative = BCompileUtil.compile( "test-src/types/finaltypes/test_implicitly_final_negative.bal"); - Assert.assertEquals(compileResultNegative.getErrorCount(), 8); + Assert.assertEquals(compileResultNegative.getErrorCount(), 9); BAssertUtil.validateError(compileResultNegative, 0, "cannot assign a value to function argument 'a'", 11, 5); BAssertUtil.validateError(compileResultNegative, 1, "cannot assign a value to function argument 'a'", 17, 5); BAssertUtil.validateError(compileResultNegative, 2, "cannot assign a value to function argument 'f'", 22, 5); @@ -57,6 +57,8 @@ public void testFinalFailCase() { BAssertUtil.validateError(compileResultNegative, 6, "cannot assign a value to function argument 'a'", 38, 5); BAssertUtil.validateError(compileResultNegative, 7, "invalid assignment: 'listener' declaration is final", 45, 5); + BAssertUtil.validateError(compileResultNegative, 8, "cannot assign a value to function argument 'p2'", 49, 5); + } @Test diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal index bc5e6a8db91b..790c6082609a 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal @@ -44,3 +44,7 @@ listener test:MockListener ml = new (8080); public function testChangingListenerVariableAfterDefining() { ml = new test:MockListener(8081); } + +function restFinal(string p1, string... p2) { + p2 = ["a", "b"]; +} From e1f94ba510b75cab2ac1d1b1051d8779394f1858 Mon Sep 17 00:00:00 2001 From: LakshanWeerasinghe Date: Mon, 11 Dec 2023 09:42:52 +0530 Subject: [PATCH 3/5] Add tests for anonymous functions with rest params --- .../types/finaltypes/FinalAccessTest.java | 24 ++++++++++--------- .../test_implicitly_final_negative.bal | 12 +++++++++- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java index e1aa02d9a924..7aa774783043 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java @@ -47,18 +47,20 @@ public void setup() { public void testFinalFailCase() { CompileResult compileResultNegative = BCompileUtil.compile( "test-src/types/finaltypes/test_implicitly_final_negative.bal"); - Assert.assertEquals(compileResultNegative.getErrorCount(), 9); - BAssertUtil.validateError(compileResultNegative, 0, "cannot assign a value to function argument 'a'", 11, 5); - BAssertUtil.validateError(compileResultNegative, 1, "cannot assign a value to function argument 'a'", 17, 5); - BAssertUtil.validateError(compileResultNegative, 2, "cannot assign a value to function argument 'f'", 22, 5); - BAssertUtil.validateError(compileResultNegative, 3, "cannot assign a value to function argument 's'", 23, 5); - BAssertUtil.validateError(compileResultNegative, 4, "cannot assign a value to function argument 'b'", 24, 5); - BAssertUtil.validateError(compileResultNegative, 5, "cannot assign a value to function argument 'j'", 25, 5); - BAssertUtil.validateError(compileResultNegative, 6, "cannot assign a value to function argument 'a'", 38, 5); - BAssertUtil.validateError(compileResultNegative, 7, "invalid assignment: 'listener' declaration is final", + int i = 0; + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'a'", 11, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'a'", 17, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'f'", 22, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 's'", 23, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'b'", 24, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'j'", 25, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'a'", 38, 5); + BAssertUtil.validateError(compileResultNegative, i++, "invalid assignment: 'listener' declaration is final", 45, 5); - BAssertUtil.validateError(compileResultNegative, 8, "cannot assign a value to function argument 'p2'", 49, 5); - + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'p2'", 49, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'b'", 53, 9); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'b'", 58, 9); + Assert.assertEquals(compileResultNegative.getErrorCount(), i); } @Test diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal index 790c6082609a..1ce32d385f8d 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal @@ -45,6 +45,16 @@ public function testChangingListenerVariableAfterDefining() { ml = new test:MockListener(8081); } -function restFinal(string p1, string... p2) { +function testRestParamFinal(string p1, string... p2) { p2 = ["a", "b"]; } + +function (int a, int... b) testModuleLevelRestParamFinal = function (int i, int... b) { + b = []; + }; + +public function testLocalLevelRestParamFinal() { + function (int a, int... b) func = function (int i, int... b) { + b = []; + }; +} From e62b265f7003b2fae35e03a97cdd18f86c4ea808 Mon Sep 17 00:00:00 2001 From: LakshanWeerasinghe Date: Wed, 3 Jan 2024 10:00:39 +0530 Subject: [PATCH 4/5] Address review suggestions --- .../compiler/semantics/analyzer/SemanticAnalyzer.java | 7 ++++--- .../test/types/finaltypes/FinalAccessTest.java | 2 +- .../types/finaltypes/test_implicitly_final_negative.bal | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java index e80581b6f04f..f1a30baf5f2d 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java @@ -497,9 +497,10 @@ public void visit(BLangFunction funcNode, AnalyzerData data) { // TODO: Shouldn't this be done in symbol enter? //set function param flag to final funcNode.symbol.params.forEach(param -> param.flags |= Flags.FUNCTION_FINAL); - - if (funcNode.symbol.restParam != null) { - funcNode.symbol.restParam.flags |= Flags.FUNCTION_FINAL; + + BVarSymbol restParamSym = funcNode.symbol.restParam; + if (restParamSym != null) { + restParamSym.flags |= Flags.FUNCTION_FINAL; } if (!funcNode.flagSet.contains(Flag.WORKER)) { diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java index 7aa774783043..a4f7bae7c03e 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java @@ -59,7 +59,7 @@ public void testFinalFailCase() { 45, 5); BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'p2'", 49, 5); BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'b'", 53, 9); - BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'b'", 58, 9); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'b'", 59, 9); Assert.assertEquals(compileResultNegative.getErrorCount(), i); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal index 1ce32d385f8d..66621e0a1a5d 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal @@ -54,7 +54,8 @@ function (int a, int... b) testModuleLevelRestParamFinal = function (int i, int. }; public function testLocalLevelRestParamFinal() { + int[] arr = []; function (int a, int... b) func = function (int i, int... b) { - b = []; + b = arr; }; } From bf890dc26035cc346247f1d2154c27869439a44f Mon Sep 17 00:00:00 2001 From: LakshanWeerasinghe Date: Wed, 17 Jan 2024 10:54:50 +0530 Subject: [PATCH 5/5] Add negative tests for rest param being final --- .../test/types/finaltypes/FinalAccessTest.java | 1 + .../types/finaltypes/test_implicitly_final_negative.bal | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java index a4f7bae7c03e..6e2c009e072f 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java @@ -60,6 +60,7 @@ public void testFinalFailCase() { BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'p2'", 49, 5); BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'b'", 53, 9); BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'b'", 59, 9); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'b'", 66, 9); Assert.assertEquals(compileResultNegative.getErrorCount(), i); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal index 66621e0a1a5d..d7de92a7a443 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal @@ -59,3 +59,10 @@ public function testLocalLevelRestParamFinal() { b = arr; }; } + +public function testLocalLevelRestParamFinalWithVar() { + int[] arr = []; + var func = function (int i, int... b) { + b = arr; + }; +}