diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/AddCheckCodeAction.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/AddCheckCodeAction.java index 0da60e38fab2..078481fa9548 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/AddCheckCodeAction.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/AddCheckCodeAction.java @@ -20,6 +20,8 @@ import io.ballerina.compiler.api.symbols.UnionTypeSymbol; import io.ballerina.compiler.syntax.tree.BracedExpressionNode; import io.ballerina.compiler.syntax.tree.ExpressionNode; +import io.ballerina.compiler.syntax.tree.ExpressionStatementNode; +import io.ballerina.compiler.syntax.tree.NonTerminalNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.compiler.syntax.tree.WaitActionNode; import io.ballerina.tools.diagnostics.Diagnostic; @@ -57,9 +59,10 @@ public class AddCheckCodeAction extends TypeCastCodeAction { public static final String NAME = "Add Check"; private static final String DIAGNOSTIC_CODE_3998 = "BCE3998"; private static final String DIAGNOSTIC_CODE_2068 = "BCE2068"; + private static final String DIAGNOSTIC_CODE_2526 = "BCE2526"; private static final String DIAGNOSTIC_CODE_2800 = "BCE2800"; public static final Set DIAGNOSTIC_CODES = Set.of("BCE2652", "BCE2066", - DIAGNOSTIC_CODE_2068, DIAGNOSTIC_CODE_2800, DIAGNOSTIC_CODE_3998); + DIAGNOSTIC_CODE_2068, DIAGNOSTIC_CODE_2526, DIAGNOSTIC_CODE_2800, DIAGNOSTIC_CODE_3998); public AddCheckCodeAction() { super(); @@ -75,30 +78,40 @@ public boolean validate(Diagnostic diagnostic, DiagBasedPositionDetails position @Override public List getCodeActions(Diagnostic diagnostic, DiagBasedPositionDetails positionDetails, CodeActionContext context) { - //Check if there is a check expression already present. - MatchedExpressionNodeResolver expressionResolver = - new MatchedExpressionNodeResolver(positionDetails.matchedNode()); - Optional expressionNode = expressionResolver.findExpression(positionDetails.matchedNode()); - if (expressionNode.isEmpty() || expressionNode.get().kind() == SyntaxKind.CHECK_EXPRESSION) { - return Collections.emptyList(); + String diagnosticCode = diagnostic.diagnosticInfo().code(); + NonTerminalNode matchedNode = positionDetails.matchedNode(); + Optional expressionNode; + + if (diagnosticCode.equals(DIAGNOSTIC_CODE_2526)) { + if (matchedNode instanceof ExpressionStatementNode matchedExpressionStatementNode) { + expressionNode = Optional.of(matchedExpressionStatementNode.expression()); + } else if (matchedNode instanceof ExpressionNode matchedExpressionNode) { + expressionNode = Optional.of(matchedExpressionNode); + } else { + return Collections.emptyList(); + } + } else { + MatchedExpressionNodeResolver expressionResolver = new MatchedExpressionNodeResolver(matchedNode); + expressionNode = expressionResolver.findExpression(matchedNode); + if (expressionNode.isEmpty() || expressionNode.get().kind() == SyntaxKind.CHECK_EXPRESSION) { + return Collections.emptyList(); + } } - Optional foundType; - if (DIAGNOSTIC_CODE_2068.equals(diagnostic.diagnosticInfo().code())) { - foundType = positionDetails.diagnosticProperty( + Optional foundType = switch (diagnosticCode) { + case DIAGNOSTIC_CODE_2068 -> positionDetails.diagnosticProperty( CodeActionUtil.getDiagPropertyFilterFunction( DiagBasedPositionDetails.DIAG_PROP_INCOMPATIBLE_TYPES_FOUND_SYMBOL_INDEX)); - } else if (DIAGNOSTIC_CODE_2800.equals(diagnostic.diagnosticInfo().code())) { - foundType = positionDetails.diagnosticProperty( + case DIAGNOSTIC_CODE_2526 -> positionDetails.diagnosticProperty( + CodeActionUtil.getDiagPropertyFilterFunction( + DiagBasedPositionDetails.DIAG_PROP_VAR_ASSIGN_SYMBOL_INDEX)); + case DIAGNOSTIC_CODE_2800 -> positionDetails.diagnosticProperty( DiagBasedPositionDetails.DIAG_PROP_INCOMPATIBLE_TYPES_FOR_ITERABLE_FOUND_SYMBOL_INDEX); - } else if (DIAGNOSTIC_CODE_3998.equals(diagnostic.diagnosticInfo().code())) { - - foundType = context.currentSemanticModel() + case DIAGNOSTIC_CODE_3998 -> context.currentSemanticModel() .flatMap(semanticModel -> semanticModel.typeOf(expressionNode.get())); - } else { - foundType = positionDetails.diagnosticProperty( + default -> positionDetails.diagnosticProperty( DiagBasedPositionDetails.DIAG_PROP_INCOMPATIBLE_TYPES_FOUND_SYMBOL_INDEX); - } + }; if (foundType.isEmpty()) { return Collections.emptyList(); } @@ -115,7 +128,7 @@ public List getCodeActions(Diagnostic diagnostic, DiagBasedPositionD if (expressionNode.get().kind() == SyntaxKind.BRACED_EXPRESSION) { BracedExpressionNode bracedExpressionNode = (BracedExpressionNode) expressionNode.get(); pos = PositionUtil.toRange(bracedExpressionNode.expression().location().lineRange()).getStart(); - } else if (DIAGNOSTIC_CODE_3998.equals(diagnostic.diagnosticInfo().code())) { + } else if (DIAGNOSTIC_CODE_3998.equals(diagnosticCode)) { // In the case of "BCE3998", we have to consider the position as the position of the initializer // because the diagnostic range is provided for the variable declaration statement instead of the // initializer expression @@ -128,7 +141,7 @@ public List getCodeActions(Diagnostic diagnostic, DiagBasedPositionD ImportsAcceptor acceptor = new ImportsAcceptor(context); List edits = new ArrayList<>(CodeActionUtil.getAddCheckTextEdits( - pos, positionDetails.matchedNode(), context, errorTypeSymbols, acceptor)); + pos, matchedNode, context, errorTypeSymbols, acceptor)); edits.addAll(acceptor.getNewImportTextEdits()); if (edits.isEmpty()) { return Collections.emptyList(); diff --git a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/AddCheckCodeActionTest.java b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/AddCheckCodeActionTest.java index a6a36bcacee3..3282a95d7991 100644 --- a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/AddCheckCodeActionTest.java +++ b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/AddCheckCodeActionTest.java @@ -66,7 +66,15 @@ public Object[][] dataProvider() { {"add_check_in_local_var2.json"}, {"add_check_in_module_var.json"}, {"add_check_on_field_access_config1.json"}, - {"add_check_on_field_access_config2.json"} + {"add_check_on_field_access_config2.json"}, + {"add_check_on_call_statement1.json"}, + {"add_check_on_call_statement2.json"}, + {"add_check_on_call_statement3.json"}, + {"add_check_on_call_statement4.json"}, + {"add_check_on_call_statement5.json"}, + {"add_check_on_call_statement6.json"}, + {"add_check_on_call_statement7.json"}, + {"add_check_on_call_statement8.json"} }; } diff --git a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/exprscheme/TestExpressionFileScheme.java b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/exprscheme/TestExpressionFileScheme.java index bbd3be54e7ac..2e82bdb06db8 100644 --- a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/exprscheme/TestExpressionFileScheme.java +++ b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/exprscheme/TestExpressionFileScheme.java @@ -130,7 +130,7 @@ public void testExprSchemeCodeActions() throws IOException { JsonArray exprCodeActions = JsonParser.parseString(exprCodeActionResponse).getAsJsonObject() .get("result").getAsJsonArray(); Assert.assertEquals(originalCodeActions.size(), 0); - Assert.assertEquals(exprCodeActions.size(), 3); + Assert.assertEquals(exprCodeActions.size(), 4); TestUtil.openDocument(serviceEndpoint, originalUri.toString(), originalContent); TestUtil.openDocument(serviceEndpoint, exprUri.toString(), originalContent); diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement1.json b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement1.json new file mode 100644 index 000000000000..e9017ccee86c --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement1.json @@ -0,0 +1,42 @@ +{ + "position": { + "line": 1, + "character": 4 + }, + "source": "add_check_on_call_statement.bal", + "expected": [ + { + "title": "Add 'check' error", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 1, + "character": 4 + }, + "end": { + "line": 1, + "character": 4 + } + }, + "newText": "check " + }, + { + "range": { + "start": { + "line": 0, + "character": 16 + }, + "end": { + "line": 0, + "character": 16 + } + }, + "newText": " returns error?" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement2.json b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement2.json new file mode 100644 index 000000000000..fb1eb08282a5 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement2.json @@ -0,0 +1,42 @@ +{ + "position": { + "line": 6, + "character": 9 + }, + "source": "add_check_on_call_statement.bal", + "expected": [ + { + "title": "Add 'check' error", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 6, + "character": 8 + }, + "end": { + "line": 6, + "character": 8 + } + }, + "newText": "check " + }, + { + "range": { + "start": { + "line": 4, + "character": 33 + }, + "end": { + "line": 4, + "character": 33 + } + }, + "newText": " returns error?" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement3.json b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement3.json new file mode 100644 index 000000000000..82ce923e4133 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement3.json @@ -0,0 +1,42 @@ +{ + "position": { + "line": 14, + "character": 17 + }, + "source": "add_check_on_call_statement.bal", + "expected": [ + { + "title": "Add 'check' error", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 14, + "character": 16 + }, + "end": { + "line": 14, + "character": 16 + } + }, + "newText": "check " + }, + { + "range": { + "start": { + "line": 10, + "character": 23 + }, + "end": { + "line": 10, + "character": 23 + } + }, + "newText": " returns error?" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement4.json b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement4.json new file mode 100644 index 000000000000..86cc7cc363c0 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement4.json @@ -0,0 +1,42 @@ +{ + "position": { + "line": 22, + "character": 9 + }, + "source": "add_check_on_call_statement.bal", + "expected": [ + { + "title": "Add 'check' error", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 22, + "character": 8 + }, + "end": { + "line": 22, + "character": 8 + } + }, + "newText": "check " + }, + { + "range": { + "start": { + "line": 20, + "character": 16 + }, + "end": { + "line": 20, + "character": 16 + } + }, + "newText": " returns error?" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement5.json b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement5.json new file mode 100644 index 000000000000..3c56dc5e52c4 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement5.json @@ -0,0 +1,42 @@ +{ + "position": { + "line": 28, + "character": 10 + }, + "source": "add_check_on_call_statement.bal", + "expected": [ + { + "title": "Add 'check' error", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 28, + "character": 4 + }, + "end": { + "line": 28, + "character": 4 + } + }, + "newText": "check " + }, + { + "range": { + "start": { + "line": 26, + "character": 16 + }, + "end": { + "line": 26, + "character": 16 + } + }, + "newText": " returns error?" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement6.json b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement6.json new file mode 100644 index 000000000000..c0def599fbbd --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement6.json @@ -0,0 +1,42 @@ +{ + "position": { + "line": 29, + "character": 9 + }, + "source": "add_check_on_call_statement.bal", + "expected": [ + { + "title": "Add 'check' error", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 29, + "character": 4 + }, + "end": { + "line": 29, + "character": 4 + } + }, + "newText": "check " + }, + { + "range": { + "start": { + "line": 26, + "character": 16 + }, + "end": { + "line": 26, + "character": 16 + } + }, + "newText": " returns error?" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement7.json b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement7.json new file mode 100644 index 000000000000..31bca670f1c0 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement7.json @@ -0,0 +1,42 @@ +{ + "position": { + "line": 30, + "character": 12 + }, + "source": "add_check_on_call_statement.bal", + "expected": [ + { + "title": "Add 'check' error", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 30, + "character": 4 + }, + "end": { + "line": 30, + "character": 4 + } + }, + "newText": "check " + }, + { + "range": { + "start": { + "line": 26, + "character": 16 + }, + "end": { + "line": 26, + "character": 16 + } + }, + "newText": " returns error?" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement8.json b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement8.json new file mode 100644 index 000000000000..1faab080736a --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/config/add_check_on_call_statement8.json @@ -0,0 +1,42 @@ +{ + "position": { + "line": 34, + "character": 12 + }, + "source": "add_check_on_call_statement.bal", + "expected": [ + { + "title": "Add 'check' error", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 34, + "character": 4 + }, + "end": { + "line": 34, + "character": 4 + } + }, + "newText": "check " + }, + { + "range": { + "start": { + "line": 33, + "character": 16 + }, + "end": { + "line": 33, + "character": 16 + } + }, + "newText": " returns error?" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/source/add_check_on_call_statement.bal b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/source/add_check_on_call_statement.bal new file mode 100644 index 000000000000..5af77b71c52e --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/add-check/source/add_check_on_call_statement.bal @@ -0,0 +1,53 @@ +function test1() { + fn(); +} + +function test2(boolean condition) { + if condition { + fn(); + } +} + +function test3(int val) { + if val > 10 { + if val > 100 { + do { + fn() + } + } + } +} + +function test4() { + var anonFn = function() { + fn(); + }; +} + +function test5() { + MyClass myCl = new (); + myCl->fn1(); + myCl.fn2(); + myCl->/path.accessor(); +} + +function test6() { + error:cause(error("error")); +} + +client class MyClass { + remote function fn1() returns error? { + + } + + isolated function fn2() returns error? { + + } + + resource function accessor path() returns error? { + + } +} + +function fn() returns error? { +}