diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java index d73d74bd11da..f187da6475b6 100644 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java +++ b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java @@ -119,8 +119,8 @@ public enum DiagnosticErrorCode implements DiagnosticCode { WORKER_SEND_RECEIVE_PARAMETER_COUNT_MISMATCH("BCE2082", "worker.send.receive.parameter.count.mismatch"), INVALID_WORKER_INTERACTION("BCE2083", "worker.invalid.worker.interaction"), WORKER_INTERACTIONS_ONLY_ALLOWED_BETWEEN_PEERS("BCE2084", "worker.interactions.only.allowed.between.peers"), - STREAM_RECEIVE_ACTION_NOT_YET_SUPPORTED("BCE2085", "stream.receive.action.not.yet.supported"), - // VACANT_ERROR("BCE2086", ""), + INVALID_WORKER_SEND_NO_MATCHING_WORKER_RECEIVE("BCE2085", "invalid.worker.send.no.matching.worker.receive"), + INVALID_WORKER_RECEIVE_NO_MATCHING_WORKER_SEND("BCE2086", "invalid.worker.receive.no.matching.worker.send"), EXPLICIT_WORKER_CANNOT_BE_DEFAULT("BCE2087", "explicit.worker.cannot.be.default"), INVALID_MULTIPLE_FORK_JOIN_SEND("BCE2088", "worker.multiple.fork.join.send"), INCOMPATIBLE_TYPE_REFERENCE("BCE2089", "incompatible.type.reference"), @@ -815,7 +815,8 @@ public enum DiagnosticErrorCode implements DiagnosticCode { "cannot.use.alternate.wait.action.within.multiple.wait.action"), EXPRESSION_OF_FUTURE_TYPE_EXPECTED("BCE4057", "future.expression.expected"), INSTANTIATION_ERROR("BCE4058", "instantiation.error"), - INVALID_BINDING_PATTERN_IN_ON_FAIL("BCE4059", "invalid.binding.pattern.in.on.fail") + INVALID_BINDING_PATTERN_IN_ON_FAIL("BCE4059", "invalid.binding.pattern.in.on.fail"), + STREAM_RECEIVE_ACTION_NOT_YET_SUPPORTED("BCE4060", "stream.receive.action.not.yet.supported") ; private String diagnosticId; 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 5f706643c02e..091e009e664f 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 @@ -3667,6 +3667,13 @@ private static boolean isWorkerSyncSend(BLangNode action) { return action.getKind() == NodeKind.WORKER_SYNC_SEND; } + private static boolean isWorkerSendOrReceive(BLangNode action) { + return switch (action.getKind()) { + case WORKER_ASYNC_SEND, WORKER_SYNC_SEND, WORKER_RECEIVE -> true; + default -> false; + }; + } + private static boolean isWaitAction(BLangNode action) { return action.getKind() == NodeKind.WAIT_EXPR; } @@ -3900,8 +3907,23 @@ private boolean isWorkerSymbol(BSymbol symbol) { } private void reportInvalidWorkerInteractionDiagnostics(WorkerActionSystem workerActionSystem) { - this.dlog.error(workerActionSystem.getRootPosition(), DiagnosticErrorCode.INVALID_WORKER_INTERACTION, - workerActionSystem.toString()); + boolean hasSendReceivePairingError = false; + for (WorkerActionStateMachine worker : workerActionSystem.finshedWorkers) { + for (BLangNode action : worker.actions) { + if (isWorkerSendOrReceive(action) && ((BLangWorkerSendReceiveExpr) action).getChannel() == null) { + hasSendReceivePairingError = true; + DiagnosticErrorCode errorCode = action.getKind() == NodeKind.WORKER_RECEIVE ? + DiagnosticErrorCode.INVALID_WORKER_RECEIVE_NO_MATCHING_WORKER_SEND : + DiagnosticErrorCode.INVALID_WORKER_SEND_NO_MATCHING_WORKER_RECEIVE; + dlog.error(action.pos, errorCode); + } + } + } + + if (!hasSendReceivePairingError) { + this.dlog.error(workerActionSystem.getRootPosition(), DiagnosticErrorCode.INVALID_WORKER_INTERACTION, + workerActionSystem.toString()); + } } private void validateWorkerActionParameters(BLangWorkerSendReceiveExpr send, BLangWorkerReceive receive) { diff --git a/compiler/ballerina-lang/src/main/resources/compiler.properties b/compiler/ballerina-lang/src/main/resources/compiler.properties index 12871b613adb..acd4b097ceca 100644 --- a/compiler/ballerina-lang/src/main/resources/compiler.properties +++ b/compiler/ballerina-lang/src/main/resources/compiler.properties @@ -453,6 +453,12 @@ error.invalid.wait.future.expr.mapping.constructors=\ error.invalid.wait.future.expr.actions=\ ''wait'' cannot be used with actions +error.invalid.worker.send.no.matching.worker.receive=\ + invalid worker send, no matching worker receive + +error.invalid.worker.receive.no.matching.worker.send=\ + invalid worker receive, no matching worker send + error.invalid.send.expr=\ expected an expression, but found an action diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WaitActionsNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WaitActionsNegativeTest.java index 559420eaa2a2..c56b0e5d6c61 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WaitActionsNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WaitActionsNegativeTest.java @@ -197,12 +197,13 @@ public void testWaitCausingADeadlockNegative() { String msg = "worker send/receive interactions are invalid; worker(s) cannot move onwards from the state: '%s'"; BAssertUtil.validateError(result, index++, String.format(msg, "[wait v, wait w, FINISHED]"), 19, 14); BAssertUtil.validateError(result, index++, String.format(msg, "[wait v, wait w, wait x, FINISHED]"), 29, 14); - BAssertUtil.validateError(result, index++, String.format(msg, "[wait w2, <- w, wait w1, FINISHED]"), 43, 14); + BAssertUtil.validateError(result, index++, "invalid worker send, no matching worker receive", 46, 9); + BAssertUtil.validateError(result, index++, "invalid worker receive, no matching worker send", 50, 17); BAssertUtil.validateError(result, index++, String.format(msg, "[wait v, wait w, wait x, FINISHED]"), 61, 18); BAssertUtil.validateError(result, index++, String.format(msg, "[wait vi, wait wi, wait xi, FINISHED, FINISHED]"), 78, 23); - BAssertUtil.validateError(result, index++, - String.format(msg, "[11 -> w3, FINISHED, FINISHED, wait {w1,w2,w3}]"), 94, 15); + BAssertUtil.validateError(result, index++, "invalid worker send, no matching worker receive", 95, 9); + BAssertUtil.validateError(result, index++, "invalid worker receive, no matching worker send", 96, 20); Assert.assertEquals(result.getErrorCount(), index); } } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerFailTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerFailTest.java index 948df9f6cb10..c2db0f97e817 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerFailTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerFailTest.java @@ -32,11 +32,29 @@ public class WorkerFailTest { @Test - public void invalidWorkerSendReceive() { + public void testMismatchInSendReceivePairing() { CompileResult result = BCompileUtil.compile("test-src/workers/invalid-worker-send-receive.bal"); - String message = Arrays.toString(result.getDiagnostics()); - Assert.assertEquals(result.getErrorCount(), 1); - Assert.assertTrue(message.contains(" interactions are invalid"), message); + String invalidSendErrMsg = "invalid worker send, no matching worker receive"; + String invalidReceiveErrMsg = "invalid worker receive, no matching worker send"; + int index = 0; + validateError(result, index++, invalidSendErrMsg, 3, 9); + validateError(result, index++, invalidSendErrMsg, 7, 9); + validateError(result, index++, invalidReceiveErrMsg, 13, 17); + validateError(result, index++, invalidReceiveErrMsg, 17, 20); + validateError(result, index++, invalidSendErrMsg, 29, 9); + validateError(result, index++, invalidReceiveErrMsg, 44, 21); + validateError(result, index++, invalidReceiveErrMsg, 54, 34); + validateError(result, index++, invalidReceiveErrMsg, 67, 20); + validateError(result, index++, invalidReceiveErrMsg, 67, 27); + validateError(result, index++, invalidReceiveErrMsg, 67, 34); + validateError(result, index++, invalidSendErrMsg, 71, 9); + validateWarning(result, index++, "unused variable 'b'", 79, 13); + validateError(result, index++, invalidReceiveErrMsg, 81, 17); + validateWarning(result, index++, "unused variable 'b'", 85, 13); + validateWarning(result, index++, "unused variable 'a'", 90, 13); + validateWarning(result, index++, "unused variable 'b'", 91, 13); + Assert.assertEquals(result.getErrorCount(), index - 4); + Assert.assertEquals(result.getWarnCount(), 4); } @Test diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-worker-send-receive.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-worker-send-receive.bal index b1d3899311dd..7721b789372a 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-worker-send-receive.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-worker-send-receive.bal @@ -1,22 +1,95 @@ +function case1() { + worker w1 { + 20 -> w2; + } + + worker w2 { + "xxx" -> w1; + } +} + +function case2() { + worker w1 { + int _ = <- w2; + } + + worker w2 { + string _ = <- w1; + } +} + +function case3() { + boolean foo = true; + worker w1 { + if foo { + 2 -> w2; + } else { + 3 -> w2; + } + 4 -> w2; + } + + worker w2 returns error? { + _ = check <- w1; + _ = check <- w1; + } +} + +function case4() { + worker w1 { + "xxx" -> w2; + } + + worker w2 { + _ = <- w1 | w1; + } +} + +function case5() { + worker w1 { + "xxx" -> w2; + } + + worker w2 { + _ = <- {a: w1, b: w3, c: w3}; + } + + worker w3 { + "yyy" -> w2; + } +} -function invalidWorkerSendReceive() { +function case6() { + worker w1 { + } + + worker w2 { + _ = <- {a: w1, b: w3, c: w3}; + } + + worker w3 { + "yyy" -> w2; + } +} + +function case7() { fork { - worker w1 { - int a = 5; - int b = 0; - a -> w2; - b = <- w3; - } - worker w2 { - int a = 0; - int b = 15; - a = <- w1; - a -> w3; - } - worker w3 { - int a = 0; - int b = 15; - a = <- w2; - } - } -} \ No newline at end of file + worker w1 { + int a = 5; + int b = 0; + a -> w2; + b = <- w3; + } + worker w2 { + int a = 0; + int b = 15; + a = <- w1; + a -> w3; + } + worker w3 { + int a = 0; + int b = 15; + a = <- w2; + } + } +}