diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticCode.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticCode.java index 80c03b60ae29..bc362fbc5077 100644 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticCode.java +++ b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticCode.java @@ -73,6 +73,7 @@ public enum DiagnosticCode { GLOBAL_VARIABLE_CYCLIC_DEFINITION("global.variable.cyclic.reference"), INCOMPATIBLE_TYPES("incompatible.types"), + INCOMPATIBLE_TYPES_EXP_RETURN("incompatible.types.exp.return"), INCOMPATIBLE_TYPES_EXP_TUPLE("incompatible.types.exp.tuple"), UNKNOWN_TYPE("unknown.type"), BINARY_OP_INCOMPATIBLE_TYPES("binary.op.incompatible.types"), diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/Compiler.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/Compiler.java index 71128d60cc1d..a0aaa9b78a9a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/Compiler.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/Compiler.java @@ -161,7 +161,8 @@ public List compilePackages(boolean isBuild) { outStream.println("Compiling source"); } List compiledPackages = compilePackages(pkgList.stream(), isBuild); - if (this.dlog.errorCount > 0) { + // If it is a build and dlog is not empty, compilation should fail + if (isBuild && this.dlog.errorCount > 0) { throw new BLangCompilerException("compilation contains errors"); } return compiledPackages; 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 524c58385d05..9a9f35022faa 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 @@ -1890,7 +1890,8 @@ public void visit(BLangWorkerSend workerSendNode) { @Override public void visit(BLangReturn returnNode) { - this.typeChecker.checkExpr(returnNode.expr, this.env, this.env.enclInvokable.returnTypeNode.type); + this.typeChecker.checkExpr(returnNode.expr, this.env, this.env.enclInvokable.returnTypeNode.type, + DiagnosticCode.INCOMPATIBLE_TYPES_EXP_RETURN); } BType analyzeDef(BLangNode node, SymbolEnv env) { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java index 572206771999..bcaed2ca058e 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java @@ -2313,7 +2313,7 @@ private void checkInvocationParamAndReturnType(BLangInvocation iExpr) { actualType = getAccessExprFinalType(iExpr, actualType); } - resultType = types.checkType(iExpr, actualType, this.expType); + resultType = types.checkType(iExpr, actualType, this.expType, this.diagCode); } private BType checkInvocationParam(BLangInvocation iExpr) { diff --git a/compiler/ballerina-lang/src/main/resources/compiler.properties b/compiler/ballerina-lang/src/main/resources/compiler.properties index d14593eff117..9b85525c1220 100644 --- a/compiler/ballerina-lang/src/main/resources/compiler.properties +++ b/compiler/ballerina-lang/src/main/resources/compiler.properties @@ -185,6 +185,9 @@ error.invalid.function.pointer.assignment.for.handler=\ error.incompatible.types=\ incompatible types: expected ''{0}'', found ''{1}'' +error.incompatible.types.exp.return=\ + incompatible return types: expected ''{0}'', found ''{1}'' + error.incompatible.types.exp.tuple=\ incompatible types: expected tuple, found ''{0}'' diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/CommandUtil.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/CommandUtil.java index ff84fe6c1a59..ac39e6430ac8 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/CommandUtil.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/CommandUtil.java @@ -17,6 +17,7 @@ import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; +import org.ballerinalang.langserver.command.executors.FixReturnTypeExecutor; import org.ballerinalang.langserver.common.UtilSymbolKeys; import org.ballerinalang.langserver.common.constants.CommandConstants; import org.ballerinalang.langserver.common.constants.NodeContextKeys; @@ -33,6 +34,8 @@ import org.ballerinalang.langserver.compiler.common.modal.BallerinaPackage; import org.ballerinalang.langserver.compiler.workspace.WorkspaceDocumentManager; import org.ballerinalang.langserver.diagnostic.DiagnosticsHelper; +import org.ballerinalang.model.tree.Node; +import org.ballerinalang.model.tree.TopLevelNode; import org.eclipse.lsp4j.ApplyWorkspaceEditParams; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionParams; @@ -51,18 +54,21 @@ import org.eclipse.lsp4j.WorkspaceEdit; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.eclipse.lsp4j.services.LanguageClient; +import org.wso2.ballerinalang.compiler.tree.BLangCompilationUnit; import org.wso2.ballerinalang.compiler.tree.BLangNode; import org.wso2.ballerinalang.compiler.tree.BLangPackage; import org.wso2.ballerinalang.compiler.tree.BLangSimpleVariable; import org.wso2.ballerinalang.compiler.tree.expressions.BLangInvocation; import org.wso2.ballerinalang.util.Flags; +import java.io.File; import java.net.URI; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.StringJoiner; @@ -211,6 +217,15 @@ public static List> getCommandsByDiagnostic(Diagnost String commandTitle = CommandConstants.PULL_MOD_TITLE; commands.add(Either.forLeft(new Command(commandTitle, CommandConstants.CMD_PULL_MODULE, args))); } + } else if (isIncompatibleTypes(diagnosticMessage)) { + Matcher matcher = CommandConstants.INCOMPATIBLE_TYPE_PATTERN.matcher(diagnosticMessage); + if (matcher.find() && matcher.groupCount() > 1) { + String foundType = matcher.group(2); + CommandArgument typeArg = new CommandArgument(CommandConstants.ARG_KEY_NODE_TYPE, foundType); + List args = Arrays.asList(lineArg, colArg, typeArg, uriArg); + String commandTitle = CommandConstants.CHANGE_RETURN_TYPE_TITLE + foundType; + commands.add(Either.forLeft(new Command(commandTitle, FixReturnTypeExecutor.COMMAND, args))); + } } return commands; } @@ -319,6 +334,60 @@ public static BLangInvocation getFunctionNode(int line, int column, String uri, } } + public static Node getBLangNodeByPosition(int line, int column, String uri, + WorkspaceDocumentManager documentManager, LSCompiler lsCompiler, + LSContext context) { + Position position = new Position(); + position.setLine(line); + position.setCharacter(column + 1); + context.put(DocumentServiceKeys.FILE_URI_KEY, uri); + TextDocumentIdentifier identifier = new TextDocumentIdentifier(uri); + context.put(DocumentServiceKeys.POSITION_KEY, new TextDocumentPositionParams(identifier, position)); + List bLangPackages = lsCompiler.getBLangPackages(context, documentManager, false, + LSCustomErrorStrategy.class, true); + + // Get the current package. + BLangPackage currentPackage = CommonUtil.getCurrentPackageByFileName(bLangPackages, uri); + + if (currentPackage == null) { + return null; + } + context.put(DocumentServiceKeys.CURRENT_BLANG_PACKAGE_CONTEXT_KEY, currentPackage); + + // If package is testable package process as tests + // else process normally + String relativeFilePath = context.get(DocumentServiceKeys.RELATIVE_FILE_PATH_KEY); + BLangCompilationUnit compilationUnit; + if (relativeFilePath.startsWith("tests" + File.separator)) { + compilationUnit = currentPackage.getTestablePkg().getCompilationUnits().stream(). + filter(compUnit -> (relativeFilePath).equals(compUnit.getName())) + .findFirst().orElse(null); + } else { + compilationUnit = currentPackage.getCompilationUnits().stream(). + filter(compUnit -> relativeFilePath.equals(compUnit.getName())).findFirst().orElse(null); + } + if (compilationUnit == null) { + return null; + } + Iterator nodeIterator = compilationUnit.getTopLevelNodes().iterator(); + Node result = null; + TopLevelNode next = (nodeIterator.hasNext()) ? nodeIterator.next() : null; + while (next != null) { + int sLine = next.getPosition().getStartLine(); + int eLine = next.getPosition().getEndLine(); + int sCol = next.getPosition().getStartColumn(); + int eCol = next.getPosition().getEndColumn(); + if ((line > sLine || (line == sLine && column >= sCol)) && + (line < eLine || (line == eLine && column <= eCol))) { + result = next; + break; + } + //TODO: visit functions inside objects as well + next = (nodeIterator.hasNext()) ? nodeIterator.next() : null; + } + return result; + } + public static Pair getBLangNode(int line, int column, String uri, WorkspaceDocumentManager documentManager, LSCompiler lsCompiler, LSContext context) { @@ -356,6 +425,10 @@ private static boolean isUnresolvedPackage(String diagnosticMessage) { return diagnosticMessage.toLowerCase(Locale.ROOT).contains(CommandConstants.UNRESOLVED_MODULE); } + private static boolean isIncompatibleTypes(String diagnosticMessage) { + return diagnosticMessage.toLowerCase(Locale.ROOT).contains(CommandConstants.INCOMPATIBLE_TYPES); + } + private static Either getDocGenerationCommand(String nodeType, String docUri, int line) { CommandArgument nodeTypeArg = new CommandArgument(CommandConstants.ARG_KEY_NODE_TYPE, nodeType); CommandArgument docUriArg = new CommandArgument(CommandConstants.ARG_KEY_DOC_URI, docUri); diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/executors/FixReturnTypeExecutor.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/executors/FixReturnTypeExecutor.java new file mode 100644 index 000000000000..d313e5398937 --- /dev/null +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/executors/FixReturnTypeExecutor.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2019, WSO2 Inc. (http://wso2.com) All Rights Reserved. + * + * Licensed 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.langserver.command.executors; + +import com.google.gson.JsonObject; +import org.ballerinalang.annotation.JavaSPIService; +import org.ballerinalang.langserver.command.CommandUtil; +import org.ballerinalang.langserver.command.ExecuteCommandKeys; +import org.ballerinalang.langserver.command.LSCommandExecutor; +import org.ballerinalang.langserver.command.LSCommandExecutorException; +import org.ballerinalang.langserver.common.UtilSymbolKeys; +import org.ballerinalang.langserver.common.constants.CommandConstants; +import org.ballerinalang.langserver.common.utils.CommonUtil; +import org.ballerinalang.langserver.compiler.DocumentServiceKeys; +import org.ballerinalang.langserver.compiler.LSCompiler; +import org.ballerinalang.langserver.compiler.LSContext; +import org.ballerinalang.langserver.compiler.workspace.WorkspaceDocumentManager; +import org.ballerinalang.model.elements.PackageID; +import org.ballerinalang.model.tree.Node; +import org.ballerinalang.model.types.TypeKind; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.TextDocumentEdit; +import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4j.VersionedTextDocumentIdentifier; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.lsp4j.services.LanguageClient; +import org.wso2.ballerinalang.compiler.tree.BLangFunction; +import org.wso2.ballerinalang.compiler.tree.types.BLangValueType; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.ballerinalang.langserver.command.CommandUtil.applyWorkspaceEdit; + +/** + * Command executor for changing return type of a function. + * + * @since 0.983.0 + */ +@JavaSPIService("org.ballerinalang.langserver.command.LSCommandExecutor") +public class FixReturnTypeExecutor implements LSCommandExecutor { + + public static final String COMMAND = "CHANGE_RETURN_TYPE"; + + private static final Pattern FQ_TYPE_PATTERN = Pattern.compile("(.*)/([^:]*):(?:.*:)?(.*)"); + + /** + * {@inheritDoc} + */ + @Override + public Object execute(LSContext context) throws LSCommandExecutorException { + String documentUri = null; + String type = null; + int sLine = -1; + int sCol = -1; + VersionedTextDocumentIdentifier textDocumentIdentifier = new VersionedTextDocumentIdentifier(); + + for (Object arg : context.get(ExecuteCommandKeys.COMMAND_ARGUMENTS_KEY)) { + String argKey = ((JsonObject) arg).get(ARG_KEY).getAsString(); + String argVal = ((JsonObject) arg).get(ARG_VALUE).getAsString(); + switch (argKey) { + case CommandConstants.ARG_KEY_DOC_URI: + documentUri = argVal; + textDocumentIdentifier.setUri(documentUri); + context.put(DocumentServiceKeys.FILE_URI_KEY, documentUri); + break; + case CommandConstants.ARG_KEY_NODE_LINE: + sLine = Integer.parseInt(argVal); + break; + case CommandConstants.ARG_KEY_NODE_COLUMN: + sCol = Integer.parseInt(argVal); + break; + case CommandConstants.ARG_KEY_NODE_TYPE: + type = argVal; + break; + default: + } + } + + if (sLine == -1 || sCol == -1 || documentUri == null || type == null) { + throw new LSCommandExecutorException("Invalid parameters received for the change return type command!"); + } + + WorkspaceDocumentManager documentManager = context.get(ExecuteCommandKeys.DOCUMENT_MANAGER_KEY); + LSCompiler lsCompiler = context.get(ExecuteCommandKeys.LS_COMPILER_KEY); + + List edits = getReturnTypeTextEdits(sLine, sCol, type, documentUri, documentManager, lsCompiler, + context); + if (edits == null) { + throw new LSCommandExecutorException("Couldn't find the function node!"); + } + + LanguageClient client = context.get(ExecuteCommandKeys.LANGUAGE_SERVER_KEY).getClient(); + TextDocumentEdit textDocumentEdit = new TextDocumentEdit(textDocumentIdentifier, edits); + return applyWorkspaceEdit(Collections.singletonList(Either.forLeft(textDocumentEdit)), client); + } + + private static List getReturnTypeTextEdits(int line, int column, String type, String uri, + WorkspaceDocumentManager documentManager, + LSCompiler lsCompiler, + LSContext context) { + List edits = new ArrayList<>(); + Node bLangNode = CommandUtil.getBLangNodeByPosition(line, column, uri, documentManager, lsCompiler, context); + if (bLangNode instanceof BLangFunction) { + // Process full-qualified BType name eg. ballerina/http:Client and if required; add an auto-import + Matcher matcher = FQ_TYPE_PATTERN.matcher(type); + String editText = type; + if (matcher.find() && matcher.groupCount() > 2) { + String orgName = matcher.group(1); + String alias = matcher.group(2); + String typeName = matcher.group(3); + String pkgId = orgName + "/" + alias; + PackageID currentPkgId = context.get(DocumentServiceKeys.CURRENT_BLANG_PACKAGE_CONTEXT_KEY).packageID; + if (pkgId.equals(currentPkgId.toString()) || ("ballerina".equals(orgName) && "builtin".equals(alias))) { + editText = typeName; + } else { + edits.addAll(CommonUtil.getAutoImportTextEdits(context, orgName, alias)); + editText = alias + UtilSymbolKeys.PKG_DELIMITER_KEYWORD + typeName; + } + } + + // Process function node + Position start = new Position(0, 0); + Position end = new Position(0, 0); + BLangFunction func = (BLangFunction) bLangNode; + if (func.returnTypeNode instanceof BLangValueType + && TypeKind.NIL.equals(((BLangValueType) func.returnTypeNode).getTypeKind()) + && func.returnTypeNode.getWS() == null) { + // eg. function test() {...} + start.setLine(func.returnTypeNode.pos.sLine - 1); + start.setCharacter(func.returnTypeNode.pos.eCol - 1); + end.setLine(func.returnTypeNode.pos.eLine - 1); + end.setCharacter(func.returnTypeNode.pos.eCol - 1); + editText = " returns (" + editText + ")"; + } else { + // eg. function test() returns () {...} + start.setLine(func.returnTypeNode.pos.sLine - 1); + start.setCharacter(func.returnTypeNode.pos.sCol - 1); + end.setLine(func.returnTypeNode.pos.eLine - 1); + end.setCharacter(func.returnTypeNode.pos.eCol - 1); + } + edits.add(new TextEdit(new Range(start, end), editText)); + return edits; + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public String getCommand() { + return COMMAND; + } +} diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/common/constants/CommandConstants.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/common/constants/CommandConstants.java index 174f53789304..ab61f1d2d8ac 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/common/constants/CommandConstants.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/common/constants/CommandConstants.java @@ -28,6 +28,9 @@ public class CommandConstants { public static final String UNRESOLVED_MODULE = "cannot resolve module"; public static final Pattern UNRESOLVED_MODULE_PATTERN = Pattern.compile("cannot resolve module '(.*)'"); public static final Pattern UNDEFINED_FUNCTION_PATTERN = Pattern.compile("undefined function '(.*)'"); + public static final String INCOMPATIBLE_TYPES = "incompatible return types"; + public static final Pattern INCOMPATIBLE_TYPE_PATTERN = Pattern.compile( + "incompatible return types: expected '(.*)', found '(.*)'"); // Command Arguments public static final String ARG_KEY_DOC_URI = "doc.uri"; @@ -67,6 +70,8 @@ public class CommandConstants { public static final String PULL_MOD_TITLE = "Pull from Ballerina Central"; + public static final String CHANGE_RETURN_TYPE_TITLE = "Change Return Type to "; + // Commands List public static final String CMD_IMPORT_MODULE = "IMPORT_MODULE"; diff --git a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/command/CommandExecutionTest.java b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/command/CommandExecutionTest.java index fb22bf716840..bdc70ad3daa7 100644 --- a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/command/CommandExecutionTest.java +++ b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/command/CommandExecutionTest.java @@ -24,6 +24,7 @@ import com.google.gson.JsonParser; import org.ballerinalang.compiler.CompilerPhase; import org.ballerinalang.langserver.command.executors.CreateTestExecutor; +import org.ballerinalang.langserver.command.executors.FixReturnTypeExecutor; import org.ballerinalang.langserver.common.constants.CommandConstants; import org.ballerinalang.langserver.compiler.LSCompiler; import org.ballerinalang.langserver.compiler.LSCompilerUtil; @@ -178,6 +179,24 @@ public void testCreateVariable(String config, String source) { Assert.assertEquals(responseJson, expected, "Test Failed for: " + config); } + @Test(dataProvider = "fix-return-type-data-provider") + public void testFixReturnType(String config, String source) { + String configJsonPath = "command" + File.separator + config; + Path sourcePath = sourcesPath.resolve("source").resolve(source); + JsonObject configJsonObject = FileUtils.fileContentAsObject(configJsonPath); + JsonObject expected = configJsonObject.get("expected").getAsJsonObject(); + List args = new ArrayList<>(); + JsonObject arguments = configJsonObject.get("arguments").getAsJsonObject(); + args.add(new CommandArgument(CommandConstants.ARG_KEY_DOC_URI, sourcePath.toUri().toString())); + args.add(new CommandArgument(CommandConstants.ARG_KEY_NODE_LINE, arguments.get("node.line").getAsString())); + args.add(new CommandArgument(CommandConstants.ARG_KEY_NODE_COLUMN, arguments.get("node.column").getAsString())); + args.add(new CommandArgument(CommandConstants.ARG_KEY_NODE_TYPE, arguments.get("node.type").getAsString())); + JsonObject responseJson = getCommandResponse(args, FixReturnTypeExecutor.COMMAND); + responseJson.get("result").getAsJsonObject().get("edit").getAsJsonObject().getAsJsonArray("documentChanges") + .forEach(element -> element.getAsJsonObject().remove("textDocument")); + Assert.assertEquals(responseJson, expected, "Test Failed for: " + config); + } + @Test(dataProvider = "testgen-fail-data-provider") public void testTestGenerationFailCases(String config, Path source) throws IOException { String configJsonPath = "command" + File.separator + config; @@ -357,6 +376,16 @@ public Object[][] createVariableDataProvider() { }; } + @DataProvider(name = "fix-return-type-data-provider") + public Object[][] fixReturnTypeDataProvider() { + log.info("Test workspace/executeCommand for command {}", FixReturnTypeExecutor.COMMAND); + return new Object[][] { + {"fixReturnType1.json", "fixReturnType.bal"}, + {"fixReturnType2.json", "fixReturnType.bal"}, + {"fixReturnType3.json", "fixReturnType.bal"}, + }; + } + @DataProvider(name = "testgen-data-provider") public Object[][] testGenerationDataProvider() { log.info("Test workspace/executeCommand for command {}", CommandConstants.CMD_CREATE_TEST); diff --git a/language-server/modules/langserver-core/src/test/resources/command/fixReturnType1.json b/language-server/modules/langserver-core/src/test/resources/command/fixReturnType1.json new file mode 100644 index 000000000000..493cbd8ae814 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/command/fixReturnType1.json @@ -0,0 +1,33 @@ +{ + "arguments": { + "node.line": 2, + "node.column": 13, + "node.type": "int" + }, + "expected": { + "result": { + "edit": { + "documentChanges": [ + { + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 37 + }, + "end": { + "line": 0, + "character": 37 + } + }, + "newText": " returns (int)" + } + ] + } + ] + } + }, + "jsonrpc": "2.0" + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/command/fixReturnType2.json b/language-server/modules/langserver-core/src/test/resources/command/fixReturnType2.json new file mode 100644 index 000000000000..c9e8f923a1f3 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/command/fixReturnType2.json @@ -0,0 +1,33 @@ +{ + "arguments": { + "node.line": 6, + "node.column": 14, + "node.type": "(int, string)" + }, + "expected": { + "result": { + "edit": { + "documentChanges": [ + { + "edits": [ + { + "range": { + "start": { + "line": 4, + "character": 49 + }, + "end": { + "line": 4, + "character": 49 + } + }, + "newText": " returns ((int, string))" + } + ] + } + ] + } + }, + "jsonrpc": "2.0" + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/command/fixReturnType3.json b/language-server/modules/langserver-core/src/test/resources/command/fixReturnType3.json new file mode 100644 index 000000000000..9c43ec6ed982 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/command/fixReturnType3.json @@ -0,0 +1,33 @@ +{ + "arguments": { + "node.line": 11, + "node.column": 12, + "node.type": "int[]" + }, + "expected": { + "result": { + "edit": { + "documentChanges": [ + { + "edits": [ + { + "range": { + "start": { + "line": 8, + "character": 48 + }, + "end": { + "line": 8, + "character": 55 + } + }, + "newText": "int[]" + } + ] + } + ] + } + }, + "jsonrpc": "2.0" + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/command/source/fixReturnType.bal b/language-server/modules/langserver-core/src/test/resources/command/source/fixReturnType.bal new file mode 100644 index 000000000000..15f40b44803a --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/command/source/fixReturnType.bal @@ -0,0 +1,12 @@ +function addTwoIntegers(int a, int b) { + return 100; +} + +function addTwoIntegers2(int a, int b) returns () { + return (100, ""); +} + +function addTwoIntegers3(int a, int b) returns (boolean) { + int[] a = []; + return a; +} diff --git a/tests/ballerina-unit-test/src/test/java/org/ballerinalang/test/statements/returnstmt/ReturnStmtNegativeTest.java b/tests/ballerina-unit-test/src/test/java/org/ballerinalang/test/statements/returnstmt/ReturnStmtNegativeTest.java index 234c241f4879..bac6a14ce8a8 100644 --- a/tests/ballerina-unit-test/src/test/java/org/ballerinalang/test/statements/returnstmt/ReturnStmtNegativeTest.java +++ b/tests/ballerina-unit-test/src/test/java/org/ballerinalang/test/statements/returnstmt/ReturnStmtNegativeTest.java @@ -32,14 +32,14 @@ public class ReturnStmtNegativeTest { public void testNotEnoughArgsToReturn1() { CompileResult result = BCompileUtil.compile("test-src/statements/returnstmt/not-enough-args-to-return-1.bal"); Assert.assertEquals(result.getErrorCount(), 1); - BAssertUtil.validateError(result, 0, "incompatible types: expected '(int,int)', found '()'", 2, 5); + BAssertUtil.validateError(result, 0, "incompatible return types: expected '(int,int)', found '()'", 2, 5); } @Test(description = "Test not enough arguments to return") public void testNotEnoughArgsToReturn2() { CompileResult result = BCompileUtil.compile("test-src/statements/returnstmt/not-enough-args-to-return-2.bal"); Assert.assertEquals(result.getErrorCount(), 1); - BAssertUtil.validateError(result, 0, "incompatible types: expected '(string,string)', found 'string'", 2, 12); + BAssertUtil.validateError(result, 0, "incompatible return types: expected '(string,string)', found 'string'", 2, 12); } @Test(description = "Test not enough arguments to return") @@ -60,7 +60,7 @@ public void testTooManyArgsToReturn1() { public void testTooManyArgsToReturn2() { CompileResult result = BCompileUtil.compile("test-src/statements/returnstmt/too-many-args-to-return-2.bal"); Assert.assertEquals(result.getErrorCount(), 1); - BAssertUtil.validateError(result, 0, "incompatible types: expected 'string', found '(string,string)'", 2, 12); + BAssertUtil.validateError(result, 0, "incompatible return types: expected 'string', found '(string,string)'", 2, 12); } @Test(description = "Test type mismatch") @@ -68,7 +68,7 @@ public void testInputTypeMismatch1() { CompileResult result = BCompileUtil.compile("test-src/statements/returnstmt/return-type-mismatch-1.bal"); Assert.assertEquals(result.getErrorCount(), 1); BAssertUtil.validateError(result, - 0, "incompatible types: expected '(string,int,string)', found '(string,string,string)'", + 0, "incompatible return types: expected '(string,int,string)', found '(string,string,string)'", 2, 12); } @@ -76,7 +76,7 @@ public void testInputTypeMismatch1() { public void testInputTypeMismatch2() { CompileResult result = BCompileUtil.compile("test-src/statements/returnstmt/return-type-mismatch-2.bal"); Assert.assertEquals(result.getErrorCount(), 1); - BAssertUtil.validateError(result, 0, "incompatible types: expected 'boolean', found 'int'", 2, 26); + BAssertUtil.validateError(result, 0, "incompatible return types: expected 'boolean', found 'int'", 2, 26); } @Test(description = "Test missing return") @@ -170,7 +170,7 @@ public void testUnreachableReturnStmt5() { public void testMultiValueInSingleContext() { CompileResult result = BCompileUtil.compile("test-src/statements/returnstmt/multi-value-in-single-context.bal"); Assert.assertEquals(result.getErrorCount(), 1); - BAssertUtil.validateError(result, 0, "incompatible types: expected 'string', found '(string,int)'", 2, 13); + BAssertUtil.validateError(result, 0, "incompatible return types: expected 'string', found '(string,int)'", 2, 13); } @Test(description = "Test return statement in resource with mismatching types") @@ -178,8 +178,8 @@ public void testReturnInResourceWithMismatchingTypes() { CompileResult result = BCompileUtil.compile("test-src/statements/returnstmt/return-in-resource-with-" + "mismatching-types.bal"); Assert.assertEquals(result.getErrorCount(), 3); - BAssertUtil.validateError(result, 0, "incompatible types: expected 'string', found 'int'", 22, 16); - BAssertUtil.validateError(result, 1, "incompatible types: expected 'int', found 'string'", 26, 16); - BAssertUtil.validateError(result, 2, "incompatible types: expected '()', found 'string'", 30, 16); + BAssertUtil.validateError(result, 0, "incompatible return types: expected 'string', found 'int'", 22, 16); + BAssertUtil.validateError(result, 1, "incompatible return types: expected 'int', found 'string'", 26, 16); + BAssertUtil.validateError(result, 2, "incompatible return types: expected '()', found 'string'", 30, 16); } }