Skip to content

Commit

Permalink
Merge pull request #42637 from nipunayf/fix-42507
Browse files Browse the repository at this point in the history
Support `Add 'check' error` code action for call statements
  • Loading branch information
nipunayf authored Apr 29, 2024
2 parents b1f278e + 4bb1e1f commit 3f05eb0
Show file tree
Hide file tree
Showing 12 changed files with 432 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String> 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();
Expand All @@ -75,30 +78,40 @@ public boolean validate(Diagnostic diagnostic, DiagBasedPositionDetails position
@Override
public List<CodeAction> getCodeActions(Diagnostic diagnostic, DiagBasedPositionDetails positionDetails,
CodeActionContext context) {
//Check if there is a check expression already present.
MatchedExpressionNodeResolver expressionResolver =
new MatchedExpressionNodeResolver(positionDetails.matchedNode());
Optional<ExpressionNode> 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> 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<TypeSymbol> foundType;
if (DIAGNOSTIC_CODE_2068.equals(diagnostic.diagnosticInfo().code())) {
foundType = positionDetails.diagnosticProperty(
Optional<TypeSymbol> 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();
}
Expand All @@ -115,7 +128,7 @@ public List<CodeAction> 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
Expand All @@ -128,7 +141,7 @@ public List<CodeAction> getCodeActions(Diagnostic diagnostic, DiagBasedPositionD

ImportsAcceptor acceptor = new ImportsAcceptor(context);
List<TextEdit> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
]
}
Original file line number Diff line number Diff line change
@@ -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
}
]
}
Original file line number Diff line number Diff line change
@@ -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
}
]
}
Original file line number Diff line number Diff line change
@@ -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
}
]
}
Original file line number Diff line number Diff line change
@@ -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
}
]
}
Original file line number Diff line number Diff line change
@@ -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
}
]
}
Loading

0 comments on commit 3f05eb0

Please sign in to comment.