diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java index 36d2041bbb0b..bc66a64213e5 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java @@ -1743,7 +1743,18 @@ public BLangNode transform(NamedWorkerDeclarationNode namedWorkerDeclNode) { // Set the function body BLangBlockStmt blockStmt = (BLangBlockStmt) namedWorkerDeclNode.workerBody().apply(this); BLangBlockFunctionBody bodyNode = (BLangBlockFunctionBody) TreeBuilder.createBlockFunctionBodyNode(); - bodyNode.stmts = blockStmt.stmts; + if (namedWorkerDeclNode.onFailClause().isPresent()) { + BLangDo bLDo = (BLangDo) TreeBuilder.createDoNode(); + bLDo.pos = workerBodyPos; + bLDo.setBody(blockStmt); + OnFailClauseNode onFailClauseNode = namedWorkerDeclNode.onFailClause().get(); + bLDo.setOnFailClause( + (org.ballerinalang.model.clauses.OnFailClauseNode) (onFailClauseNode.apply(this))); + bodyNode.addStatement(bLDo); + } else { + bodyNode.stmts = blockStmt.stmts; + } + bodyNode.pos = workerBodyPos; bLFunction.body = bodyNode; bLFunction.internal = true; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java index fe7daa9c6550..42f1e46b58e0 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java @@ -1510,12 +1510,15 @@ public void visit(BLangWhile whileNode, AnalyzerData data) { public void visit(BLangDo doNode, AnalyzerData data) { boolean onFailExists = doNode.onFailClause != null; boolean failureHandled = data.failureHandled; + boolean previousWithinDoBlock = data.withinDoBlock; + data.withinDoBlock = true; if (onFailExists) { data.failureHandled = true; } analyzeNode(doNode.body, data); data.failureHandled = failureHandled; analyseOnFailClause(onFailExists, doNode.onFailClause, data); + data.withinDoBlock = previousWithinDoBlock; } @@ -2002,7 +2005,8 @@ public void visit(BLangWorkerAsyncSendExpr asyncSendExpr, AnalyzerData data) { } String workerName = asyncSendExpr.workerIdentifier.getValue(); - if (data.withinQuery || (!isCommunicationAllowedLocation(data.env) && !data.inInternallyDefinedBlockStmt)) { + if (!data.withinDoBlock && (data.withinQuery || + (!isCommunicationAllowedLocation(data.env) && !data.inInternallyDefinedBlockStmt))) { this.dlog.error(asyncSendExpr.pos, DiagnosticErrorCode.UNSUPPORTED_WORKER_SEND_POSITION); was.hasErrors = true; } @@ -2061,7 +2065,8 @@ public void visit(BLangWorkerSyncSendExpr syncSendExpr, AnalyzerData data) { was.hasErrors = true; } - if (data.withinQuery || (!isCommunicationAllowedLocation(data.env) && !data.inInternallyDefinedBlockStmt)) { + if (!data.withinDoBlock && (data.withinQuery || + (!isCommunicationAllowedLocation(data.env) && !data.inInternallyDefinedBlockStmt))) { this.dlog.error(syncSendExpr.pos, DiagnosticErrorCode.UNSUPPORTED_WORKER_SEND_POSITION); was.hasErrors = true; } @@ -4256,6 +4261,7 @@ public static class AnalyzerData { boolean loopAlterNotAllowed; // Fields related to worker system boolean inInternallyDefinedBlockStmt; + boolean withinDoBlock; int workerSystemMovementSequence; Stack workerActionSystemStack = new Stack<>(); Map> workerReferences = new HashMap<>(); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerOnFailTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerOnFailTest.java new file mode 100644 index 000000000000..883be193b203 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerOnFailTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you 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.ballerinalang.test.worker; + +import org.ballerinalang.test.BCompileUtil; +import org.ballerinalang.test.BRunUtil; +import org.ballerinalang.test.CompileResult; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.util.Arrays; + +/** + * Tests the worker on fail clause. + */ +public class WorkerOnFailTest { + + private CompileResult result; + + @BeforeClass + public void setup() { + this.result = BCompileUtil.compile("test-src/workers/worker-on-fail.bal"); + Assert.assertEquals(result.getErrorCount(), 0, Arrays.asList(result.getDiagnostics()).toString()); + } + + @Test + public void simpleOnFailTest() { + Object returns = BRunUtil.invoke(result, "testOnFailInWorker"); + long ret = (long) returns; + Assert.assertEquals(ret, -1); + } + + @Test + public void doOnFailInsideWorker() { + Object returns = BRunUtil.invoke(result, "testDoOnFailInsideWorker"); + long ret = (long) returns; + Assert.assertEquals(ret, 3); + } + + @Test + public void returnWithinOnFail() { + Object returns = BRunUtil.invoke(result, "testReturnWithinOnFail"); + long ret = (long) returns; + Assert.assertEquals(ret, -1); + } + + @Test + public void onFailWorkerWithVariable() { + Object returns = BRunUtil.invoke(result, "testOnFailWorkerWithVariable"); + long ret = (long) returns; + Assert.assertEquals(ret, 0); + } + + @Test + public void workerOnFailWithSend() { + Object returns = BRunUtil.invoke(result, "testWorkerOnFailWithSend"); + long ret = (long) returns; + Assert.assertEquals(ret, 1); + } + +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/worker-on-fail.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/worker-on-fail.bal new file mode 100644 index 000000000000..4fbf781f33fa --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/worker-on-fail.bal @@ -0,0 +1,89 @@ +function testOnFailInWorker() returns int { + int[] vals = returnIntArr(); + int key = returnOne(); + int val = 0; + + worker A { + int? index = vals.indexOf(key); + if index != () { + val = vals[index]; + } else { + check error("value not found"); + } + } on fail { + val = -1; + } + wait A; + return val; +} + +function testDoOnFailInsideWorker() returns int { + int val = 0; + worker A { + do { + val += 1; + fail error("error in do"); + } on fail { + val += 1; + } + fail error("error for worker"); + } on fail { + val += 1; + } + wait A; + return val; +} + +function testReturnWithinOnFail() returns int { + int x = returnOne(); + worker A returns string { + if (x == 1) { + check error("one"); + } + return "not one"; + } on fail error e { + return e.message(); + } + string str = wait A; + return str == "one" ? -1 : 0; +} + +function testOnFailWorkerWithVariable() returns int { + int x = 0; + worker A { + do { + x += 1; + fail error("error in do"); + } on fail { + x += 1; + } + fail error("error in worker"); + } on fail error e { + if e.message() == "error in worker" { + x -= 2; + } else { + x -= -1; + } + } + wait A; + return x; +} + +function testWorkerOnFailWithSend() returns int { + worker A { + int x = 1; + x -> B; + check error("testWorkerOnFailWithSend"); + } on fail var err { + _ = err.message(); + } + + worker B returns int { + return <- A; + } + return wait B; +} + +function returnOne() returns int => 1; + +function returnIntArr() returns int[] => [2, 3, 4, 5];