diff --git a/.codecov.yml b/.codecov.yml index df4b673ce290..72dcaaf4c845 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -3,7 +3,7 @@ codecov: max_report_age: off fixes: - - "ballerina/lang$0046*/*/::langlib/lang.*/src/main/ballerina/" + - "ballerina/lang&0046*/*/::langlib/lang.*/src/main/ballerina/" ignore: - "**/tests" diff --git a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/ProfilerMethodWrapper.java b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/ProfilerMethodWrapper.java index 239d3f1da78d..b2c4be7eab31 100644 --- a/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/ProfilerMethodWrapper.java +++ b/bvm/ballerina-profiler/src/main/java/io/ballerina/runtime/profiler/codegen/ProfilerMethodWrapper.java @@ -55,7 +55,7 @@ public class ProfilerMethodWrapper extends ClassLoader { public void invokeMethods(String debugArg) throws IOException, InterruptedException { String balJarArgs = Main.getBalJarArgs(); List commands = new ArrayList<>(); - commands.add("java"); + commands.add(System.getenv("java.command")); commands.add("-jar"); if (debugArg != null) { commands.add(debugArg); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/PredefinedTypes.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/PredefinedTypes.java index a9635fbc87d9..72b1d0d946b9 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/PredefinedTypes.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/PredefinedTypes.java @@ -151,6 +151,10 @@ public class PredefinedTypes { TYPE_TEXT)), EMPTY_MODULE); public static final Type TYPE_READONLY_XML = ReadOnlyUtils.setImmutableTypeAndGetEffectiveType(TYPE_XML); + public static final Type TYPE_XML_ELEMENT_SEQUENCE = new BXmlType(PredefinedTypes.TYPE_ELEMENT, false); + public static final Type TYPE_XML_COMMENT_SEQUENCE = new BXmlType(PredefinedTypes.TYPE_COMMENT, false); + public static final Type TYPE_XML_PI_SEQUENCE = new BXmlType(PredefinedTypes.TYPE_PROCESSING_INSTRUCTION, false); + public static final Type TYPE_XML_TEXT_SEQUENCE = new BXmlType(PredefinedTypes.TYPE_TEXT, false); public static final AnyType TYPE_ANY = new BAnyType(TypeConstants.ANY_TNAME, EMPTY_MODULE, false); public static final AnyType TYPE_READONLY_ANY = new BAnyType(TypeConstants.READONLY_ANY_TNAME, EMPTY_MODULE, true); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java index 54f15bf537a3..6d1abcd9e60b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/Type.java @@ -31,6 +31,49 @@ */ public interface Type { + // TODO: remove default implementations when standard library types are updated + /** + * Set the referred type for this type once it has been calculated. This must be called the first time this + * calculation is done for {@code Type#getReferredTypeCache()} to work properly. This is non-blocking and + * will eventually become consistent. Expect {@code TypeUtils#getReferredType(Type)} to be referentially + * transparent. + * + * @param type Type referred by this type. For non-reference types, this is the same type. + */ + default void setCachedReferredType(Type type) { + } + /** + * Get the type referred by this type if it has been already calculated. If it has not been already calculated, it + * will return null. For non-reference types, this will return the same type. This is non-blocking and will + * eventually become consistent. Expect {@code TypeUtils#getReferredType(Type)} to be referentially transparent. + * + * @return Referred type of the type + */ + default Type getCachedReferredType() { + return null; + } + + /** + * Set the implied type for this type once it has been calculated. This must be called the first time this + * calculation is done for {@code Type#getImpliedTypeCache()} to work properly. This is non-blocking and + * will eventually become consistent. Expect {@code TypeUtils#getImpliedType(Type)} to be referentially transparent. + * + * @param type Type implied by this type. For non-intersection types, this is the same type. + */ + default void setCachedImpliedType(Type type) { + } + + /** + * Get the type implied by this type if it has been already calculated. If it has not been already calculated, it + * will return null. For non-intersection types, this will return the same type. This is non-blocking and will + * eventually become consistent. Expect {@code TypeUtils#getImpliedType(Type)} to be referentially transparent. + * + * @return Implied type of the type + */ + default Type getCachedImpliedType() { + return null; + } + /** * Get the default value of the type. This is the value of an uninitialized variable of this type. * For value types, this is same as the value get from {@code BType#getInitValue()}. diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/utils/TypeUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/utils/TypeUtils.java index 9485472eafcf..731b73b35e2d 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/utils/TypeUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/utils/TypeUtils.java @@ -152,10 +152,17 @@ public static boolean isSameType(Type sourceType, Type targetType) { * @return the referred type if provided with a type reference type, else returns the original type */ public static Type getReferredType(Type type) { + Type referredType = type.getCachedReferredType(); + if (referredType != null) { + return referredType; + } if (type.getTag() == TypeTags.TYPE_REFERENCED_TYPE_TAG) { - return getReferredType(((ReferenceType) type).getReferredType()); + referredType = getReferredType(((ReferenceType) type).getReferredType()); + } else { + referredType = type; } - return type; + type.setCachedReferredType(referredType); + return referredType; } /** @@ -167,12 +174,18 @@ public static Type getReferredType(Type type) { * else returns the original type */ public static Type getImpliedType(Type type) { + Type impliedType = type.getCachedImpliedType(); + if (impliedType != null) { + return impliedType; + } type = getReferredType(type); if (type.getTag() == TypeTags.INTERSECTION_TAG) { - return getImpliedType(((IntersectionType) type).getEffectiveType()); + impliedType = getImpliedType(((IntersectionType) type).getEffectiveType()); + } else { + impliedType = type; } - - return type; + type.setCachedImpliedType(impliedType); + return impliedType; } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java index fc5a48f60dce..8487c7254435 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/JsonInternalUtils.java @@ -326,7 +326,7 @@ public static Object convertJSON(Object jsonValue, Type targetType) { if (jsonValue instanceof BString) { return jsonValue; } - return jsonValue.toString(); + return StringUtils.fromString(jsonValue.toString()); case TypeTags.BOOLEAN_TAG: return jsonNodeToBoolean(jsonValue); case TypeTags.JSON_TAG: diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java index 1a68e9d8dc61..f5727580ab9b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java @@ -1953,6 +1953,7 @@ public static boolean isInherentlyImmutableType(Type sourceType) { case TypeTags.TYPEDESC_TAG: case TypeTags.FUNCTION_POINTER_TAG: case TypeTags.HANDLE_TAG: + case TypeTags.REG_EXP_TYPE_TAG: return true; case TypeTags.XML_TAG: return ((BXmlType) sourceType).constraint.getTag() == TypeTags.NEVER_TAG; @@ -2979,8 +2980,8 @@ private static boolean checkValueEquals(Object lhsValue, Object rhsValue, Set(unresolvedValues), errors, false); } } for (Type memType : memberTypes) { Type convertibleTypeInUnion = getConvertibleType(inputValue, memType, varName, - unresolvedValues, errors, allowNumericConversion); + new HashSet<>(unresolvedValues), errors, allowNumericConversion); if (convertibleTypeInUnion != null) { return convertibleTypeInUnion; } @@ -391,7 +392,7 @@ private static Type getConvertibleStructuredTypeInUnion(Object inputValue, Strin for (Type memType : memberTypes) { initialErrorCount = errors.size(); Type convertibleTypeInUnion = getConvertibleType(inputValue, memType, varName, - unresolvedValues, errors, allowNumericConversion); + new HashSet<>(unresolvedValues), errors, allowNumericConversion); currentErrorListSize = errors.size(); if (convertibleTypeInUnion != null) { errors.subList(initialErrorListSize - 1, currentErrorListSize).clear(); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/configurable/providers/toml/TomlProvider.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/configurable/providers/toml/TomlProvider.java index c372c8be93d1..949ebf33b0d1 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/configurable/providers/toml/TomlProvider.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/configurable/providers/toml/TomlProvider.java @@ -618,14 +618,14 @@ private List getRootModuleNode(Toml baseToml) { Optional table = baseToml.getTable(moduleKey); List moduleNodes = new ArrayList<>(); if (table.isPresent()) { - moduleNodes.add(table.get().rootNode()); + addToModuleNodesList(table.get(), moduleNodes); } else if (moduleInfo.hasModuleAmbiguity()) { throw new ConfigException(ErrorCodes.CONFIG_TOML_MODULE_AMBIGUITY, getLineRange(baseToml.rootNode()), moduleName, moduleKey); } table = baseToml.getTable(moduleName); table.ifPresent(toml -> addToModuleNodesList(toml, moduleNodes)); - moduleNodes.add(baseToml.rootNode()); + addToModuleNodesList(baseToml, moduleNodes); return moduleNodes; } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Scheduler.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Scheduler.java index 1a82952d48f8..112832b002bf 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Scheduler.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Scheduler.java @@ -455,7 +455,6 @@ public void unblockStrand(Strand strand) { } private void cleanUp(Strand justCompleted) { - justCompleted.scheduler = null; justCompleted.frames = null; justCompleted.waitingContexts = null; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMethodType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMethodType.java index 7d59045957be..360829a47f11 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMethodType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMethodType.java @@ -63,8 +63,7 @@ public String getName() { @Override public String getAnnotationKey() { - return Utils.decodeIdentifier(parentObjectType.getAnnotationKey()) + "." + - Utils.decodeIdentifier(funcName); + return Utils.decodeIdentifier(parentObjectType.getAnnotationKey()) + "." + Utils.decodeIdentifier(funcName); } @Override diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java index d83b84517f7f..866432570c59 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java @@ -42,6 +42,8 @@ public abstract class BType implements Type { protected Module pkg; protected Class valueClass; private int hashCode; + private Type cachedReferredType = null; + private Type cachedImpliedType = null; protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -195,4 +197,19 @@ public long getFlags() { return 0; } + public void setCachedReferredType(Type type) { + this.cachedReferredType = type; + } + + public Type getCachedReferredType() { + return this.cachedReferredType; + } + + public void setCachedImpliedType(Type type) { + this.cachedImpliedType = type; + } + + public Type getCachedImpliedType() { + return this.cachedImpliedType; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/XmlSequence.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/XmlSequence.java index 8c7dd1e628f3..1fa8c0dffdc7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/XmlSequence.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/XmlSequence.java @@ -35,7 +35,6 @@ import io.ballerina.runtime.internal.errors.ErrorHelper; import io.ballerina.runtime.internal.types.BArrayType; import io.ballerina.runtime.internal.types.BUnionType; -import io.ballerina.runtime.internal.types.BXmlType; import java.util.ArrayList; import java.util.HashSet; @@ -628,17 +627,12 @@ public Object next() { } private Type getSequenceType(Type tempExprType) { - switch (tempExprType.getTag()) { - case TypeTags.XML_ELEMENT_TAG: - return new BXmlType(PredefinedTypes.TYPE_ELEMENT, false); - case TypeTags.XML_COMMENT_TAG: - return new BXmlType(PredefinedTypes.TYPE_COMMENT, false); - case TypeTags.XML_PI_TAG: - return new BXmlType(PredefinedTypes.TYPE_PROCESSING_INSTRUCTION, false); - default: - // Since 'xml:Text is same as xml<'xml:Text> - return PredefinedTypes.TYPE_TEXT; - } + return switch (tempExprType.getTag()) { + case TypeTags.XML_ELEMENT_TAG -> PredefinedTypes.TYPE_XML_ELEMENT_SEQUENCE; + case TypeTags.XML_COMMENT_TAG -> PredefinedTypes.TYPE_XML_COMMENT_SEQUENCE; + case TypeTags.XML_PI_TAG -> PredefinedTypes.TYPE_XML_PI_SEQUENCE; + default -> PredefinedTypes.TYPE_XML_TEXT_SEQUENCE; + }; } private void initializeIteratorNextReturnType() { diff --git a/cli/ballerina-cli/src/main/java/io/ballerina/cli/cmd/PushCommand.java b/cli/ballerina-cli/src/main/java/io/ballerina/cli/cmd/PushCommand.java index 37aba77ce417..3ae1b62eaaba 100644 --- a/cli/ballerina-cli/src/main/java/io/ballerina/cli/cmd/PushCommand.java +++ b/cli/ballerina-cli/src/main/java/io/ballerina/cli/cmd/PushCommand.java @@ -17,6 +17,8 @@ */ package io.ballerina.cli.cmd; +import com.google.gson.Gson; +import com.google.gson.JsonObject; import io.ballerina.cli.BLauncherCmd; import io.ballerina.cli.utils.FileUtils; import io.ballerina.projects.DependencyManifest; @@ -44,14 +46,18 @@ import org.wso2.ballerinalang.util.RepoUtils; import picocli.CommandLine; +import java.io.BufferedReader; import java.io.File; +import java.io.FileWriter; import java.io.IOException; import java.io.PrintStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.Arrays; +import java.util.Optional; import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -60,6 +66,7 @@ import static io.ballerina.cli.utils.CentralUtils.authenticate; import static io.ballerina.cli.utils.CentralUtils.getBallerinaCentralCliTokenUrl; import static io.ballerina.cli.utils.CentralUtils.getCentralPackageURL; +import static io.ballerina.projects.util.ProjectConstants.LOCAL_TOOLS_JSON; import static io.ballerina.projects.util.ProjectConstants.SETTINGS_FILE_NAME; import static io.ballerina.projects.util.ProjectUtils.getAccessTokenOfCLI; import static io.ballerina.projects.util.ProjectUtils.initializeProxy; @@ -73,6 +80,11 @@ @CommandLine.Command(name = PUSH_COMMAND, description = "Publish a package to Ballerina Central") public class PushCommand implements BLauncherCmd { + private static final String TOOL_DIR = "tool"; + private static final String BAL_TOOL_JSON = "bal-tool.json"; + private static final String TOOL_ID = "tool_id"; + private static final String ORG = "org"; + private static final String PACKAGE_NAME = "name"; @CommandLine.Parameters (arity = "0..1") private Path balaPath; @@ -391,9 +403,11 @@ private void pushBalaToCustomRepo(Path balaFilePath) { ProjectUtils.deleteDirectory(balaCachesPath); } ProjectUtils.extractBala(balaFilePath, balaDestPath); + createLocalToolsJsonIfLocalTool(balaDestPath, org, packageName, repoPath.resolve( + ProjectConstants.BALA_DIR_NAME)); } catch (IOException e) { throw new ProjectException("error while pushing bala file '" + balaFilePath + "' to '" - + ProjectConstants.LOCAL_REPOSITORY_NAME + "' repository. " + e.getMessage()); + + ProjectConstants.LOCAL_REPOSITORY_NAME + "' repository: " + e.getMessage()); } Path relativePathToBalaFile; @@ -406,6 +420,51 @@ private void pushBalaToCustomRepo(Path balaFilePath) { + " to '" + repositoryName + "' repository."); } + private void createLocalToolsJsonIfLocalTool(Path balaDestPath, String org, String packageName, + Path localRepoBalaPath) { + Path balToolJsonPath = balaDestPath.resolve(TOOL_DIR).resolve(BAL_TOOL_JSON); + JsonObject balToolJson; + JsonObject localToolJson; + Gson gson = new Gson(); + if (!balToolJsonPath.toFile().exists()) { + return; + } + try (BufferedReader bufferedReader = Files.newBufferedReader(balToolJsonPath, StandardCharsets.UTF_8)) { + balToolJson = gson.fromJson(bufferedReader, JsonObject.class); + } catch (IOException e) { + throw new ProjectException("Failed to read bal-tools.json file: " + e.getMessage()); + } + Optional optionalToolId = Optional.ofNullable(balToolJson.get(TOOL_ID).getAsString()); + if (optionalToolId.isEmpty()) { + return; + } + String toolId = optionalToolId.get(); + JsonObject packageDesc = new JsonObject(); + packageDesc.addProperty(ORG, org); + packageDesc.addProperty(PACKAGE_NAME, packageName); + Path localToolJsonPath = localRepoBalaPath.resolve(LOCAL_TOOLS_JSON); + if (localToolJsonPath.toFile().exists()) { + try (BufferedReader bufferedReader = Files.newBufferedReader(localToolJsonPath, StandardCharsets.UTF_8)) { + localToolJson = gson.fromJson(bufferedReader, JsonObject.class); + if (localToolJson.has(toolId)) { + localToolJson.remove(toolId); + } + localToolJson.add(toolId, packageDesc); + } catch (IOException e) { + throw new ProjectException("Failed to read local-tools.json file: " + e.getMessage()); + } + } else { + localToolJson = new JsonObject(); + localToolJson.add(toolId, packageDesc); + } + + try (FileWriter writer = new FileWriter(localToolJsonPath.toFile(), StandardCharsets.UTF_8)) { + writer.write(gson.toJson(localToolJson)); + } catch (IOException e) { + throw new ProjectException("Failed to write local-tools.json file: " + e.getMessage()); + } + } + /** * Push a bala file to remote repository. * diff --git a/cli/ballerina-cli/src/main/java/io/ballerina/cli/cmd/ToolCommand.java b/cli/ballerina-cli/src/main/java/io/ballerina/cli/cmd/ToolCommand.java index 509c966daf05..d8385e3a2b74 100644 --- a/cli/ballerina-cli/src/main/java/io/ballerina/cli/cmd/ToolCommand.java +++ b/cli/ballerina-cli/src/main/java/io/ballerina/cli/cmd/ToolCommand.java @@ -18,6 +18,8 @@ package io.ballerina.cli.cmd; +import com.google.gson.Gson; +import com.google.gson.JsonObject; import io.ballerina.cli.BLauncherCmd; import io.ballerina.cli.utils.PrintUtils; import io.ballerina.projects.BalToolsManifest; @@ -42,8 +44,10 @@ import org.wso2.ballerinalang.util.RepoUtils; import picocli.CommandLine; +import java.io.BufferedReader; import java.io.IOException; import java.io.PrintStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -51,6 +55,7 @@ import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -61,6 +66,7 @@ import static io.ballerina.projects.util.ProjectConstants.BAL_TOOLS_TOML; import static io.ballerina.projects.util.ProjectConstants.CENTRAL_REPOSITORY_CACHE_NAME; import static io.ballerina.projects.util.ProjectConstants.CONFIG_DIR; +import static io.ballerina.projects.util.ProjectConstants.LOCAL_REPOSITORY_NAME; import static io.ballerina.projects.util.ProjectConstants.REPOSITORIES_DIR; import static io.ballerina.projects.util.ProjectUtils.getAccessTokenOfCLI; import static io.ballerina.projects.util.ProjectUtils.initializeProxy; @@ -88,6 +94,7 @@ public class ToolCommand implements BLauncherCmd { private static final String TOOL_REMOVE_USAGE_TEXT = "bal tool remove :[]"; private static final String TOOL_SEARCH_USAGE_TEXT = "bal tool search [|]"; private static final String TOOL_UPDATE_USAGE_TEXT = "bal tool update "; + private static final String EMPTY_STRING = Names.EMPTY.getValue(); private final boolean exitWhenFinish; private final PrintStream outStream; @@ -101,6 +108,9 @@ public class ToolCommand implements BLauncherCmd { @CommandLine.Option(names = {"--help", "-h"}, hidden = true) private boolean helpFlag; + @CommandLine.Option(names = "--repository") + private String repositoryName; + private String toolId; private String org; private String name; @@ -151,6 +161,14 @@ public void execute() { return; } + if (repositoryName != null && !repositoryName.equals(LOCAL_REPOSITORY_NAME)) { + String errMsg = "unsupported repository '" + repositoryName + "' found. Only '" + + LOCAL_REPOSITORY_NAME + "' repository is supported."; + CommandUtil.printError(this.errStream, errMsg, null, false); + CommandUtil.exitError(this.exitWhenFinish); + return; + } + String command = argList.get(0); switch (command) { case TOOL_PULL_COMMAND -> handlePullCommand(); @@ -208,6 +226,13 @@ private void handlePullCommand() { return; } + if (LOCAL_REPOSITORY_NAME.equals(repositoryName) && EMPTY_STRING.equals(version)) { + CommandUtil.printError(errStream, "tool version should be provided when pulling a tool from local " + + "repository", null, false); + CommandUtil.exitError(this.exitWhenFinish); + return; + } + if (!validateToolName(toolId)) { CommandUtil.printError(errStream, "invalid tool id.", TOOL_PULL_USAGE_TEXT, false); CommandUtil.exitError(this.exitWhenFinish); @@ -269,19 +294,28 @@ private void handleUseCommand() { BalToolsToml balToolsToml = BalToolsToml.from(balToolsTomlPath); BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); - Optional tool = balToolsManifest.getTool(toolId, version); + Optional tool = balToolsManifest.getTool(toolId, version, repositoryName); if (tool.isEmpty()) { + boolean isLocalTool = isToolAvailableInLocalRepo(toolId, version); + if (isLocalTool) { + CommandUtil.printError(errStream, "tool '" + toolId + ":" + version + "' is not found. " + + "Run 'bal tool pull " + toolId + ":" + version + + "' or 'bal tool pull " + toolId + ":" + version + + " --repository=local' to fetch and set as the active version.", null, false); + CommandUtil.exitError(this.exitWhenFinish); + return; + } CommandUtil.printError(errStream, "tool '" + toolId + ":" + version + "' is not found. " + - "Run 'bal tool pull " + toolId + ":" + version - + "' to fetch and set as the active version.", null, false); + "Run 'bal tool pull " + toolId + ":" + version + + "' to fetch and set as the active version.", null, false); CommandUtil.exitError(this.exitWhenFinish); return; } this.org = tool.get().org(); this.name = tool.get().name(); - Optional currentActiveTool = balToolsManifest.getActiveTool(toolId); - if (currentActiveTool.isPresent() && currentActiveTool.get().version().equals(tool.get().version())) { + if (currentActiveTool.isPresent() && currentActiveTool.get().version().equals(tool.get().version()) && + Objects.equals(currentActiveTool.get().repository(), tool.get().repository())) { outStream.println("tool '" + toolId + ":" + version + "' is the current active version."); return; } @@ -292,7 +326,7 @@ private void handleUseCommand() { return; } - balToolsManifest.setActiveToolVersion(toolId, version); + balToolsManifest.setActiveToolVersion(toolId, version, repositoryName); balToolsToml.modify(balToolsManifest); outStream.println("tool '" + toolId + ":" + version + "' successfully set as the active version."); } @@ -324,6 +358,12 @@ private void handleSearchCommand() { CommandUtil.exitError(this.exitWhenFinish); return; } + if (LOCAL_REPOSITORY_NAME.equals(repositoryName)) { + CommandUtil.printError(errStream, "Local repository option is not supported with tool search command", + null, false); + CommandUtil.exitError(this.exitWhenFinish); + return; + } String searchArgs = argList.get(1); searchToolsInCentral(searchArgs); @@ -392,9 +432,14 @@ private void handleUpdateCommand() { CommandUtil.exitError(this.exitWhenFinish); return; } + if (LOCAL_REPOSITORY_NAME.equals(repositoryName)) { + CommandUtil.printError(errStream, "tool update command is not supported for local repository.", + null, false); + CommandUtil.exitError(this.exitWhenFinish); + return; + } toolId = argList.get(1); - if (!validateToolName(toolId)) { CommandUtil.printError(errStream, "invalid tool id.", TOOL_UPDATE_USAGE_TEXT, false); CommandUtil.exitError(this.exitWhenFinish); @@ -406,13 +451,22 @@ private void handleUpdateCommand() { public void pullToolAndUpdateBalToolsToml(String toolIdArg, String versionArg) { toolId = toolIdArg; version = versionArg; - Path balaCacheDirPath = ProjectUtils.createAndGetHomeReposPath() + Path balaCacheDirPath = RepoUtils.createAndGetHomeReposPath() .resolve(REPOSITORIES_DIR).resolve(CENTRAL_REPOSITORY_CACHE_NAME) .resolve(ProjectConstants.BALA_DIR_NAME); String supportedPlatform = Arrays.stream(JvmTarget.values()) .map(JvmTarget::code) .collect(Collectors.joining(",")); + if (LOCAL_REPOSITORY_NAME.equals(repositoryName)) { + if (!isToolAvailableInLocalRepo(toolId, version)) { + errStream.println("tool '" + toolId + ":" + version + "' is not available in local repository." + + "\nUse 'bal push --repository=local' to publish it."); + CommandUtil.exitError(this.exitWhenFinish); + } + addToBalToolsToml(); + return; + } try { if (isToolAvailableLocally(toolId, version)) { outStream.println("tool '" + toolId + ":" + version + "' is already available locally."); @@ -430,6 +484,29 @@ public void pullToolAndUpdateBalToolsToml(String toolIdArg, String versionArg) { } } + private boolean isToolAvailableInLocalRepo(String toolId, String version) { + JsonObject localToolJson; + Gson gson = new Gson(); + Path localBalaPath = RepoUtils.createAndGetHomeReposPath().resolve(Path.of(REPOSITORIES_DIR, + LOCAL_REPOSITORY_NAME, BALA_DIR_NAME)); + Path localToolJsonPath = localBalaPath.resolve(ProjectConstants.LOCAL_TOOLS_JSON); + if (!Files.exists(localToolJsonPath)) { + return false; + } + try (BufferedReader bufferedReader = Files.newBufferedReader(localToolJsonPath, StandardCharsets.UTF_8)) { + localToolJson = gson.fromJson(bufferedReader, JsonObject.class); + JsonObject pkgDesc = localToolJson.get(toolId).getAsJsonObject(); + if (pkgDesc.isEmpty()) { + return false; + } + org = pkgDesc.get(ProjectConstants.ORG).getAsString(); + name = pkgDesc.get(ProjectConstants.PACKAGE_NAME).getAsString(); + return Files.exists(localBalaPath.resolve(org).resolve(name).resolve(version)); + } catch (IOException e) { + throw new ProjectException("Failed to read local-tools.json file: " + e.getMessage()); + } + } + private void pullToolFromCentral(String supportedPlatform, Path balaCacheDirPath) throws CentralClientException { Settings settings; try { @@ -474,8 +551,7 @@ private void addToBalToolsToml() { CommandUtil.exitError(this.exitWhenFinish); return; } - - balToolsManifest.addTool(toolId, org, name, version, true); + balToolsManifest.addTool(toolId, org, name, version, true, repositoryName); balToolsToml.modify(balToolsManifest); outStream.println("tool '" + toolId + ":" + version + "' successfully set as the active version."); } @@ -485,7 +561,7 @@ private List listBalToolsTomlFile() { BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); List flattenedTools = new ArrayList<>(); balToolsManifest.tools().values().stream() - .flatMap(map -> map.values().stream()) + .flatMap(map -> map.values().stream()).flatMap(map -> map.values().stream()) .sorted(Comparator.comparing(BalToolsManifest.Tool::id) .thenComparing(BalToolsManifest.Tool::version).reversed()) .forEach(flattenedTools::add); @@ -496,17 +572,21 @@ private void removeAllToolVersions() { BalToolsToml balToolsToml = BalToolsToml.from(balToolsTomlPath); BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); - Optional> toolVersions = + Optional>> toolVersions = Optional.ofNullable(balToolsManifest.tools().get(toolId)); if (toolVersions.isEmpty() || toolVersions.get().isEmpty()) { CommandUtil.printError(errStream, "tool '" + toolId + "' not found.", null, false); CommandUtil.exitError(this.exitWhenFinish); return; } - balToolsManifest.removeTool(toolId); balToolsToml.modify(balToolsManifest); - Optional tool = toolVersions.get().values().stream().findAny(); + if (repositoryName != null) { + outStream.println("tool '" + toolId + "' successfully removed."); + return; + } + Optional tool = toolVersions.get().values().stream().findAny() + .flatMap(value -> value.values().stream().findAny()); tool.ifPresent(value -> deleteAllCachedToolVersions(value.org(), value.name())); outStream.println("tool '" + toolId + "' successfully removed."); } @@ -515,7 +595,7 @@ private void removeSpecificToolVersion() { BalToolsToml balToolsToml = BalToolsToml.from(balToolsTomlPath); BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); - Optional tool = balToolsManifest.getTool(toolId, version); + Optional tool = balToolsManifest.getTool(toolId, version, repositoryName); if (tool.isEmpty()) { CommandUtil.printError(errStream, "tool '" + toolId + ":" + version + "' not found.", null, false); CommandUtil.exitError(this.exitWhenFinish); @@ -527,7 +607,6 @@ private void removeSpecificToolVersion() { CommandUtil.exitError(this.exitWhenFinish); return; } - org = tool.get().org(); name = tool.get().name(); boolean isDistsCompatible = checkToolDistCompatibility(); @@ -535,9 +614,12 @@ private void removeSpecificToolVersion() { CommandUtil.exitError(this.exitWhenFinish); return; } - - balToolsManifest.removeToolVersion(toolId, version); + balToolsManifest.removeToolVersion(toolId, version, repositoryName); balToolsToml.modify(balToolsManifest); + if (repositoryName != null) { + outStream.println("tool '" + toolId + ":" + version + "' successfully removed."); + return; + } deleteCachedToolVersion(tool.get().org(), tool.get().name(), version); outStream.println("tool '" + toolId + ":" + version + "' successfully removed."); } @@ -597,7 +679,7 @@ private void searchToolsInCentral(String keyword) { } } catch (CentralClientException e) { String errorMessage = e.getMessage(); - if (null != errorMessage && !"".equals(errorMessage.trim())) { + if (null != errorMessage && !EMPTY_STRING.equals(errorMessage.trim())) { // removing the error stack if (errorMessage.contains("\n\tat")) { errorMessage = errorMessage.substring(0, errorMessage.indexOf("\n\tat")); @@ -617,7 +699,7 @@ private boolean isToolAvailableLocally(String toolId, String version) { } BalToolsToml balToolsToml = BalToolsToml.from(balToolsTomlPath); BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); - Optional toolOptional = balToolsManifest.getTool(toolId, version); + Optional toolOptional = balToolsManifest.getTool(toolId, version, repositoryName); if (toolOptional.isEmpty()) { return false; } @@ -625,7 +707,7 @@ private boolean isToolAvailableLocally(String toolId, String version) { org = tool.org(); name = tool.name(); - Path toolCacheDir = ProjectUtils.createAndGetHomeReposPath() + Path toolCacheDir = RepoUtils.createAndGetHomeReposPath() .resolve(REPOSITORIES_DIR).resolve(CENTRAL_REPOSITORY_CACHE_NAME) .resolve(ProjectConstants.BALA_DIR_NAME).resolve(tool.org()).resolve(tool.name()); if (toolCacheDir.toFile().isDirectory()) { @@ -640,7 +722,9 @@ private boolean isToolAvailableLocally(String toolId, String version) { private boolean checkToolDistCompatibility() { SemanticVersion currentDistVersion = SemanticVersion.from(RepoUtils.getBallerinaShortVersion()); - SemanticVersion toolDistVersion = getToolDistVersionFromCentralCache(); + SemanticVersion toolDistVersion = LOCAL_REPOSITORY_NAME.equals(repositoryName) + ? getToolDistVersionFromCache(LOCAL_REPOSITORY_NAME) + : getToolDistVersionFromCache(CENTRAL_REPOSITORY_CACHE_NAME); if (!isCompatibleWithLocalDistVersion(currentDistVersion, toolDistVersion)) { CommandUtil.printError(errStream, "tool '" + toolId + ":" + version + "' is not compatible with the " + "current Ballerina distribution '" + RepoUtils.getBallerinaShortVersion() + @@ -651,11 +735,11 @@ private boolean checkToolDistCompatibility() { return true; } - private SemanticVersion getToolDistVersionFromCentralCache() { - Path centralBalaDirPath = ProjectUtils.createAndGetHomeReposPath() - .resolve(REPOSITORIES_DIR).resolve(CENTRAL_REPOSITORY_CACHE_NAME) + private SemanticVersion getToolDistVersionFromCache(String repositoryName) { + Path balaDirPath = RepoUtils.createAndGetHomeReposPath() + .resolve(REPOSITORIES_DIR).resolve(repositoryName) .resolve(ProjectConstants.BALA_DIR_NAME); - Path balaPath = CommandUtil.getPlatformSpecificBalaPath(org, name, version, centralBalaDirPath); + Path balaPath = CommandUtil.getPlatformSpecificBalaPath(org, name, version, balaDirPath); PackageJson packageJson = BalaFiles.readPackageJson(balaPath); return SemanticVersion.from(packageJson.getBallerinaVersion()); } @@ -673,8 +757,9 @@ private boolean isToolVersionAlreadyActive(String toolId, String version) { BalToolsToml balToolsToml = BalToolsToml.from(balToolsTomlPath); BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); if (balToolsManifest.tools().containsKey(toolId)) { - Map toolVersions = balToolsManifest.tools().get(toolId); - return toolVersions.containsKey(version) && toolVersions.get(version).active(); + Map> toolVersions = balToolsManifest.tools().get(toolId); + return toolVersions.containsKey(version) && toolVersions.get(version).containsKey(repositoryName) && + toolVersions.get(version).get(repositoryName).active(); } return false; } @@ -688,8 +773,13 @@ private void updateToolToLatestVersion() { CommandUtil.exitError(this.exitWhenFinish); return; } + if (LOCAL_REPOSITORY_NAME.equals(tool.get().repository())) { + CommandUtil.printError(errStream, "tools from local repository can not be updated. ", + null, false); + CommandUtil.exitError(this.exitWhenFinish); + } - Path balaCacheDirPath = ProjectUtils.createAndGetHomeReposPath() + Path balaCacheDirPath = RepoUtils.createAndGetHomeReposPath() .resolve(REPOSITORIES_DIR).resolve(CENTRAL_REPOSITORY_CACHE_NAME) .resolve(ProjectConstants.BALA_DIR_NAME); diff --git a/cli/ballerina-cli/src/main/java/io/ballerina/cli/launcher/CustomToolClassLoader.java b/cli/ballerina-cli/src/main/java/io/ballerina/cli/launcher/CustomToolClassLoader.java index 12acd8baa60a..55085f587b37 100644 --- a/cli/ballerina-cli/src/main/java/io/ballerina/cli/launcher/CustomToolClassLoader.java +++ b/cli/ballerina-cli/src/main/java/io/ballerina/cli/launcher/CustomToolClassLoader.java @@ -18,8 +18,14 @@ package io.ballerina.cli.launcher; +import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; +import java.util.Collections; +import java.util.Enumeration; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; /** * Custom class loader used to load the tool implementation classes. @@ -28,23 +34,76 @@ * @since 2201.8.0 */ public class CustomToolClassLoader extends URLClassLoader { + private final ClassLoader system; public CustomToolClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); + system = getSystemClassLoader(); } @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - try { - // Load from parent if cli or picocli classes. This is to avoid SPI and class loading issues - if (name.startsWith("io.ballerina.cli") || name.startsWith("picocli")) { - return super.loadClass(name, resolve); + Class loadedClass = findLoadedClass(name); + if (loadedClass == null) { + try { + // Load from parent if cli or picocli classes. This is to avoid SPI and class loading issues + if (name.startsWith("io.ballerina.cli") || name.startsWith("picocli")) { + loadedClass = super.loadClass(name, resolve); + } else { + // Try to load the class from the URLs + loadedClass = findClass(name); + } + } catch (ClassNotFoundException e) { + try { + // If not found, delegate to the parent + loadedClass = super.loadClass(name, resolve); + } catch (ClassNotFoundException e2) { + // If not found, delegate to the system class loader + if (system != null) { + loadedClass = system.loadClass(name); + } + } } - // First, try to load the class from the URLs - return findClass(name); - } catch (ClassNotFoundException e) { - // If not found, delegate to the parent - return super.loadClass(name, resolve); } + if (resolve) { + resolveClass(Objects.requireNonNull(loadedClass)); + } + return loadedClass; + } + + @Override + public Enumeration getResources(String name) throws IOException { + List allResources = new LinkedList<>(); + Enumeration sysResource; + if (system != null) { + sysResource = system.getResources(name); + while (sysResource.hasMoreElements()) { + allResources.add(sysResource.nextElement()); + } + } + Enumeration thisResource = findResources(name); + while (thisResource.hasMoreElements()) { + allResources.add(thisResource.nextElement()); + } + Enumeration parentResource; + if (getParent() != null) { + parentResource = getParent().getResources(name); + while (parentResource.hasMoreElements()) { + allResources.add(parentResource.nextElement()); + } + } + return Collections.enumeration(allResources); + } + + @Override + public URL getResource(String name) { + URL resource = findResource(name); + if (resource == null) { + resource = super.getResource(name); + } + if (resource == null && system != null) { + resource = system.getResource(name); + } + return resource; } } diff --git a/cli/ballerina-cli/src/main/java/io/ballerina/cli/launcher/LauncherUtils.java b/cli/ballerina-cli/src/main/java/io/ballerina/cli/launcher/LauncherUtils.java index 06e98741e9b4..bec861a1fe25 100644 --- a/cli/ballerina-cli/src/main/java/io/ballerina/cli/launcher/LauncherUtils.java +++ b/cli/ballerina-cli/src/main/java/io/ballerina/cli/launcher/LauncherUtils.java @@ -19,7 +19,11 @@ import io.ballerina.cli.BLauncherCmd; import io.ballerina.cli.launcher.util.BalToolsUtil; +import io.ballerina.projects.BalToolsManifest; +import io.ballerina.projects.BalToolsToml; +import io.ballerina.projects.internal.BalToolsManifestBuilder; import io.ballerina.runtime.api.values.BError; +import org.wso2.ballerinalang.util.RepoUtils; import picocli.CommandLine; import java.io.IOException; @@ -28,10 +32,13 @@ import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.HashMap; import java.util.List; import java.util.Map; import static io.ballerina.cli.launcher.BallerinaCliCommands.HELP; +import static io.ballerina.projects.util.ProjectConstants.BAL_TOOLS_TOML; +import static io.ballerina.projects.util.ProjectConstants.CONFIG_DIR; /** * Contains utility methods for executing a Ballerina program. @@ -103,13 +110,26 @@ static String generateGeneralHelp(Map subCommands) { StringBuilder helpBuilder = new StringBuilder(); helpBuilder.append(BLauncherCmd.getCommandUsageInfo(HELP)); + Path balToolsTomlPath = RepoUtils.createAndGetHomeReposPath().resolve(CONFIG_DIR).resolve(BAL_TOOLS_TOML); + BalToolsToml balToolsToml = BalToolsToml.from(balToolsTomlPath); + BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); + Map activeToolsVsRepos = new HashMap<>(); + // if there are any tools, add Tool Commands section List toolNames = subCommands.keySet().stream() .filter(BalToolsUtil::isNonBuiltInToolCommand) .sorted().toList(); + if (!toolNames.isEmpty()) { + toolNames.forEach(toolName -> { + balToolsManifest.getActiveTool(toolName).ifPresent(tool -> { + activeToolsVsRepos.put(toolName, tool.repository() == null ? "" : "[" + tool.repository() + .toUpperCase() + "] "); + }); + }); helpBuilder.append("\n\n Tool Commands:"); - toolNames.forEach(key -> generateCommandDescription(subCommands.get(key), helpBuilder)); + toolNames.forEach(key -> generateCommandDescription(subCommands.get(key), helpBuilder, + activeToolsVsRepos.get(key))); } return helpBuilder.toString(); } @@ -124,7 +144,8 @@ static String generateCommandHelp(String commandName, Map s return commandUsageInfo.toString(); } - private static void generateCommandDescription(CommandLine command, StringBuilder stringBuilder) { + private static void generateCommandDescription(CommandLine command, StringBuilder stringBuilder, + String repository) { String commandName = command.getCommandName(); BLauncherCmd bLauncherCmd = (BLauncherCmd) command.getCommandSpec().userObject(); CommandLine.Command annotation = bLauncherCmd.getClass().getAnnotation(CommandLine.Command.class); @@ -138,7 +159,7 @@ private static void generateCommandDescription(CommandLine command, StringBuilde } stringBuilder.append("\n") .append(" ") - .append(String.format("%-15s %s", commandName, commandDescription)); + .append(String.format("%-15s %s", commandName, repository + commandDescription)); } static String wrapString(String str, int wrapLength, int indent) { diff --git a/cli/ballerina-cli/src/main/java/io/ballerina/cli/launcher/util/BalToolsUtil.java b/cli/ballerina-cli/src/main/java/io/ballerina/cli/launcher/util/BalToolsUtil.java index 5b7393419c79..59bb175aadbf 100644 --- a/cli/ballerina-cli/src/main/java/io/ballerina/cli/launcher/util/BalToolsUtil.java +++ b/cli/ballerina-cli/src/main/java/io/ballerina/cli/launcher/util/BalToolsUtil.java @@ -168,16 +168,21 @@ private static List getToolCommandJarAndDependencyJars(String commandName) Path balToolsTomlPath = userHomeDirPath.resolve(Path.of(CONFIG_DIR, BAL_TOOLS_TOML)); Path centralBalaDirPath = userHomeDirPath.resolve( Path.of(REPOSITORIES_DIR, CENTRAL_REPOSITORY_CACHE_NAME, BALA_DIR_NAME)); + Path localBalaDirPath = userHomeDirPath.resolve( + Path.of(REPOSITORIES_DIR, ProjectConstants.LOCAL_REPOSITORY_NAME, BALA_DIR_NAME)); BalToolsToml balToolsToml = BalToolsToml.from(balToolsTomlPath); BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); // we load all tool jars for the help, default commands and --help, -h options if (HELP_COMMAND.equals(commandName)) { return balToolsManifest.tools().values().stream() + .flatMap(map -> map.values().stream()) .flatMap(map -> map.values().stream()) .filter(BalToolsManifest.Tool::active) .map(tool1 -> findJarFiles(CommandUtil.getPlatformSpecificBalaPath( - tool1.org(), tool1.name(), tool1.version(), centralBalaDirPath).resolve(TOOL).resolve(LIBS) + tool1.org(), tool1.name(), tool1.version(), ProjectConstants.LOCAL_REPOSITORY_NAME + .equals(tool1.repository()) ? localBalaDirPath : centralBalaDirPath) + .resolve(TOOL).resolve(LIBS) .toFile())) .flatMap(List::stream) .collect(Collectors.toList()); @@ -195,7 +200,8 @@ private static List getToolCommandJarAndDependencyJars(String commandName) throw LauncherUtils.createLauncherException(errMsg); } Path platformPath = CommandUtil.getPlatformSpecificBalaPath( - tool.org(), tool.name(), tool.version(), centralBalaDirPath); + tool.org(), tool.name(), tool.version(), ProjectConstants.LOCAL_REPOSITORY_NAME + .equals(tool.repository()) ? localBalaDirPath : centralBalaDirPath); File libsDir = platformPath.resolve(Path.of(TOOL, LIBS)).toFile(); return findJarFiles(libsDir); } @@ -211,8 +217,11 @@ private static boolean isToolDistCompatibilityWithCurrentDist(BalToolsManifest.T private static SemanticVersion getToolDistVersionFromCentralCache(BalToolsManifest.Tool tool) { Path centralBalaDirPath = ProjectUtils.createAndGetHomeReposPath().resolve( Path.of(REPOSITORIES_DIR, CENTRAL_REPOSITORY_CACHE_NAME, ProjectConstants.BALA_DIR_NAME)); + Path localBalaPath = ProjectUtils.createAndGetHomeReposPath().resolve( + Path.of(REPOSITORIES_DIR, ProjectConstants.LOCAL_REPOSITORY_NAME, ProjectConstants.BALA_DIR_NAME)); Path balaPath = CommandUtil.getPlatformSpecificBalaPath( - tool.org(), tool.name(), tool.version(), centralBalaDirPath); + tool.org(), tool.name(), tool.version(), ProjectConstants.LOCAL_REPOSITORY_NAME + .equals(tool.repository()) ? localBalaPath : centralBalaDirPath); PackageJson packageJson = BalaFiles.readPackageJson(balaPath); return SemanticVersion.from(packageJson.getBallerinaVersion()); } @@ -270,8 +279,8 @@ public static void updateOldBalToolsToml() { boolean isActive = balToolsManifest.getActiveTool(tool.id()).isEmpty() && latestVersion.isPresent() && latestVersion.get().equals(version); - if (balToolsManifest.getTool(tool.id(), version).isEmpty()) { - balToolsManifest.addTool(tool.id(), tool.org(), tool.name(), version, isActive); + if (balToolsManifest.getTool(tool.id(), version, null).isEmpty()) { + balToolsManifest.addTool(tool.id(), tool.org(), tool.name(), version, isActive, null); } }); } diff --git a/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/CompileTask.java b/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/CompileTask.java index 7ae41cd32e13..b05b681c99fa 100644 --- a/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/CompileTask.java +++ b/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/CompileTask.java @@ -29,6 +29,7 @@ import io.ballerina.projects.ProjectException; import io.ballerina.projects.ProjectKind; import io.ballerina.projects.SemanticVersion; +import io.ballerina.projects.buildtools.ToolContext; import io.ballerina.projects.directory.SingleFileProject; import io.ballerina.projects.environment.ResolutionOptions; import io.ballerina.projects.internal.PackageDiagnostic; @@ -47,6 +48,7 @@ import static io.ballerina.cli.launcher.LauncherUtils.createLauncherException; import static io.ballerina.projects.util.ProjectConstants.DOT; +import static io.ballerina.projects.util.ProjectConstants.TOOL_DIAGNOSTIC_CODE_PREFIX; /** * Task for compiling a package. @@ -185,7 +187,11 @@ public void execute(Project project) { diagnostics.addAll(project.currentPackage().manifest().diagnostics().diagnostics()); // add dependency manifest diagnostics diagnostics.addAll(project.currentPackage().dependencyManifest().diagnostics().diagnostics()); - diagnostics.forEach(d -> err.println(d.toString())); + diagnostics.forEach(d -> { + if (!d.diagnosticInfo().code().startsWith(TOOL_DIAGNOSTIC_CODE_PREFIX)) { + err.println(d); + } + }); throw createLauncherException("package resolution contains errors"); } @@ -205,15 +211,24 @@ public void execute(Project project) { // Report package compilation and backend diagnostics diagnostics.addAll(jBallerinaBackend.diagnosticResult().diagnostics(false)); + diagnostics.forEach(d -> { + if (d.diagnosticInfo().code() == null || (!d.diagnosticInfo().code().equals( + ProjectDiagnosticErrorCode.BUILT_WITH_OLDER_SL_UPDATE_DISTRIBUTION.diagnosticId()) && + !d.diagnosticInfo().code().startsWith(TOOL_DIAGNOSTIC_CODE_PREFIX))) { + err.println(d); + } + }); + // Report build tool execution diagnostics + if (project.getToolContextMap() != null) { + for (ToolContext tool : project.getToolContextMap().values()) { + diagnostics.addAll(tool.diagnostics()); + } + } boolean hasErrors = false; for (Diagnostic d : diagnostics) { if (d.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) { hasErrors = true; } - if (d.diagnosticInfo().code() == null || !d.diagnosticInfo().code().equals( - ProjectDiagnosticErrorCode.BUILT_WITH_OLDER_SL_UPDATE_DISTRIBUTION.diagnosticId())) { - err.println(d); - } } if (hasErrors) { throw createLauncherException("compilation contains errors"); diff --git a/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/CreateExecutableTask.java b/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/CreateExecutableTask.java index d4c9835a940e..cd88a3262533 100644 --- a/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/CreateExecutableTask.java +++ b/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/CreateExecutableTask.java @@ -29,7 +29,6 @@ import io.ballerina.projects.ProjectException; import io.ballerina.projects.ProjectKind; import io.ballerina.projects.internal.model.Target; -import io.ballerina.tools.diagnostics.Diagnostic; import org.ballerinalang.compiler.plugins.CompilerPlugin; import java.io.File; @@ -38,10 +37,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; import java.util.ServiceLoader; -import java.util.stream.Collectors; import static io.ballerina.cli.launcher.LauncherUtils.createLauncherException; import static io.ballerina.cli.utils.FileUtils.getFileNameWithoutExtension; @@ -127,16 +123,9 @@ public void execute(Project project) { } } - List diagnostics = new ArrayList<>(emitResult.diagnostics().diagnostics()); - if (!diagnostics.isEmpty()) { - // TODO: When deprecating the lifecycle compiler plugin, we can remove this check for duplicates - // in JBallerinaBackend diagnostics and the diagnostics added to EmitResult. - diagnostics = diagnostics.stream() - .filter(diagnostic -> !jBallerinaBackend.diagnosticResult().diagnostics().contains(diagnostic)) - .collect(Collectors.toList()); - if (!diagnostics.isEmpty()) { - diagnostics.forEach(d -> out.println("\n" + d.toString())); - } + // Print diagnostics found during emit executable + if (!emitResult.diagnostics().diagnostics().isEmpty()) { + emitResult.diagnostics().diagnostics().forEach(d -> out.println("\n" + d.toString())); } } catch (ProjectException e) { diff --git a/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/RunBallerinaPreBuildToolsTask.java b/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/RunBallerinaPreBuildToolsTask.java index 2eab2bdefbb5..5ed6a6144e56 100644 --- a/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/RunBallerinaPreBuildToolsTask.java +++ b/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/RunBallerinaPreBuildToolsTask.java @@ -18,23 +18,31 @@ package io.ballerina.cli.task; -import io.ballerina.cli.tool.CodeGeneratorTool; import io.ballerina.cli.utils.FileUtils; +import io.ballerina.projects.Diagnostics; import io.ballerina.projects.Project; -import io.ballerina.projects.ProjectException; -import io.ballerina.projects.ToolContext; +import io.ballerina.projects.buildtools.CodeGeneratorTool; +import io.ballerina.projects.buildtools.ToolContext; +import io.ballerina.projects.internal.PackageDiagnostic; +import io.ballerina.projects.internal.ProjectDiagnosticErrorCode; import io.ballerina.toml.api.Toml; +import io.ballerina.toml.validator.schema.Schema; import io.ballerina.tools.diagnostics.Diagnostic; +import io.ballerina.tools.diagnostics.DiagnosticInfo; import io.ballerina.tools.diagnostics.DiagnosticSeverity; import java.io.IOException; import java.io.PrintStream; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.ServiceLoader; +import java.util.stream.Collectors; import static io.ballerina.cli.launcher.LauncherUtils.createLauncherException; import static io.ballerina.projects.PackageManifest.Tool; +import static io.ballerina.projects.util.ProjectConstants.TOOL_DIAGNOSTIC_CODE_PREFIX; /** * Task for running tools integrated with the build. @@ -42,7 +50,6 @@ * @since 2201.9.0 */ public class RunBallerinaPreBuildToolsTask implements Task { - private final PrintStream outStream; public RunBallerinaPreBuildToolsTask(PrintStream out) { @@ -51,41 +58,74 @@ public RunBallerinaPreBuildToolsTask(PrintStream out) { @Override public void execute(Project project) { - Collection toolDiagnostics = project.currentPackage().manifest().diagnostics().diagnostics(); - boolean hasTomlErrors = project.currentPackage().manifest().diagnostics().hasErrors(); - if (hasTomlErrors) { - toolDiagnostics.forEach(outStream::println); - throw createLauncherException("compilation contains errors"); - } + // Print all build tool manifest diagnostics + Collection toolManifestDiagnostics = project.currentPackage().manifest().diagnostics() + .diagnostics().stream().filter(diagnostic -> diagnostic.diagnosticInfo().code() + .startsWith(TOOL_DIAGNOSTIC_CODE_PREFIX)).collect(Collectors.toList()); + toolManifestDiagnostics.forEach(outStream::println); + + // Build tool execution + Map toolContextMap = new HashMap<>(); List tools = project.currentPackage().manifest().tools(); + if (!tools.isEmpty()) { + this.outStream.println("\nExecuting Build Tools"); + } ServiceLoader buildRunners = ServiceLoader.load(CodeGeneratorTool.class); for (Tool tool : tools) { + String commandName = tool.type(); + ToolContext toolContext = ToolContext.from(tool, project.currentPackage()); + boolean hasOptionErrors = false; try { - String commandName = tool.getType(); - CodeGeneratorTool targetTool = getTargetTool(commandName, buildRunners); - if (targetTool == null) { - // TODO: Install tool if not found - outStream.println("Command not found: " + commandName); - return; + hasOptionErrors = validateOptionsToml(tool.optionsToml(), commandName); + if (hasOptionErrors) { + DiagnosticInfo diagnosticInfo = new DiagnosticInfo( + ProjectDiagnosticErrorCode.TOOL_OPTIONS_VALIDATION_FAILED.diagnosticId(), + ProjectDiagnosticErrorCode.TOOL_OPTIONS_VALIDATION_FAILED.messageKey(), + DiagnosticSeverity.ERROR); + PackageDiagnostic diagnostic = new PackageDiagnostic(diagnosticInfo, + tool.type()); + toolContext.reportDiagnostic(diagnostic); + toolContextMap.put(tool.id(), toolContext); } + } catch (IOException e) { + outStream.println(String.format("WARNING: Skipping validation of tool options for tool %s(%s) " + + "due to: %s", tool.type(), tool.id(), e.getMessage())); + } + if (!tool.hasErrorDiagnostic() && !hasOptionErrors) { try { - validateOptionsToml(tool.getOptionsToml(), commandName); - } catch (IOException e) { - outStream.println("WARNING: Skipping the validation of tool options due to: " + - e.getMessage()); - } - ToolContext toolContext = ToolContext.from(tool, project.currentPackage()); - targetTool.execute(toolContext); - toolContext.diagnostics().forEach(outStream::println); - for (Diagnostic d : toolContext.diagnostics()) { - if (d.diagnosticInfo().severity().equals(DiagnosticSeverity.ERROR)) { - throw new ProjectException("compilation contains errors"); + CodeGeneratorTool targetTool = getTargetTool(commandName, buildRunners); + if (targetTool == null) { + // TODO: Installing tool if not found to be implemented at a later phase + DiagnosticInfo diagnosticInfo = new DiagnosticInfo( + ProjectDiagnosticErrorCode.BUILD_TOOL_NOT_FOUND.diagnosticId(), + "Build tool '" + tool.type() + "' not found", + DiagnosticSeverity.ERROR); + PackageDiagnostic diagnostic = new PackageDiagnostic(diagnosticInfo, + tool.type()); + this.outStream.println(diagnostic); + toolContext.reportDiagnostic(diagnostic); + toolContextMap.put(tool.id(), toolContext); + continue; + } + this.outStream.println(String.format("\t%s(%s)%n", tool.type(), tool.id())); + targetTool.execute(toolContext); + for (Diagnostic d : toolContext.diagnostics()) { + if (d.toString().contains("(1:1,1:1)")) { + outStream.println(new PackageDiagnostic(d.diagnosticInfo(), toolContext.toolId())); + } else { + outStream.println(new PackageDiagnostic(d.diagnosticInfo(), d.location())); + } } + toolContextMap.put(tool.id(), toolContext); + } catch (Exception e) { + throw createLauncherException(e.getMessage()); } - } catch (ProjectException e) { - throw createLauncherException(e.getMessage()); + } else { + outStream.println(String.format("WARNING: Skipping execution of build tool %s(%s) as Ballerina.toml " + + "contains errors%n", tool.type(), tool.id() != null ? tool.id() : "")); } } + project.setToolContextMap(toolContextMap); } private CodeGeneratorTool getTargetTool(String commandName, ServiceLoader buildRunners) { @@ -97,16 +137,33 @@ private CodeGeneratorTool getTargetTool(String commandName, ServiceLoader requiredFields = schema.required(); + if (!requiredFields.isEmpty()) { + for (String field: requiredFields) { + DiagnosticInfo diagnosticInfo = new DiagnosticInfo( + ProjectDiagnosticErrorCode.MISSING_TOOL_PROPERTIES_IN_BALLERINA_TOML.diagnosticId(), + String.format("missing required optional field '%s'", field), + DiagnosticSeverity.ERROR); + PackageDiagnostic diagnostic = new PackageDiagnostic(diagnosticInfo, + toolName); + this.outStream.println(diagnostic); } + return true; } + this.outStream.println(String.format("WARNING: Skipping validation of tool options for tool %s due to: " + + "No tool options found for", toolName)); + return false; } } diff --git a/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/RunProfilerTask.java b/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/RunProfilerTask.java index f20a22f0f8f2..f1c375b01cb0 100644 --- a/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/RunProfilerTask.java +++ b/cli/ballerina-cli/src/main/java/io/ballerina/cli/task/RunProfilerTask.java @@ -83,6 +83,7 @@ private void initiateProfiler(Project project) { pb.environment().put(JAVA_OPTS, getAgentArgs()); pb.environment().put(BALLERINA_HOME, System.getProperty(BALLERINA_HOME)); pb.environment().put(CURRENT_DIR_KEY, System.getProperty(USER_DIR)); + pb.environment().put("java.command", System.getProperty("java.command")); pb.directory(new File(getProfilerPath(project).toUri())); Process process = pb.start(); process.waitFor(); diff --git a/cli/ballerina-cli/src/main/java/io/ballerina/cli/tool/CodeGeneratorTool.java b/cli/ballerina-cli/src/main/java/io/ballerina/cli/tool/CodeGeneratorTool.java deleted file mode 100644 index 123e9d4fc183..000000000000 --- a/cli/ballerina-cli/src/main/java/io/ballerina/cli/tool/CodeGeneratorTool.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.ballerina.cli.tool; - -import io.ballerina.projects.ToolContext; - -/** - * {@code CodeGeneratorTool} represents a Ballerina build tool. - * - * @since 2201.9.0 - */ -public interface CodeGeneratorTool { - /** - * Execute the command. - * - * @param toolContext the {@link ToolContext} of the build tool - */ - void execute(ToolContext toolContext); - - /** - * Retrieve the tool name. - * - * @return the name of the tool. - */ - String toolName(); -} diff --git a/cli/ballerina-cli/src/main/java/io/ballerina/cli/utils/PrintUtils.java b/cli/ballerina-cli/src/main/java/io/ballerina/cli/utils/PrintUtils.java index 9668dd7a2158..54ae8cda1855 100644 --- a/cli/ballerina-cli/src/main/java/io/ballerina/cli/utils/PrintUtils.java +++ b/cli/ballerina-cli/src/main/java/io/ballerina/cli/utils/PrintUtils.java @@ -19,6 +19,7 @@ package io.ballerina.cli.utils; import io.ballerina.projects.BalToolsManifest; +import io.ballerina.projects.util.ProjectConstants; import org.ballerinalang.central.client.model.Package; import org.ballerinalang.central.client.model.Tool; @@ -56,30 +57,35 @@ public static void printLocalTools(List tools, String ter } } int versionColWidth = 15; + int repositoryColWidth = 10; int padding = 2; int minimumToolIdColWidth = 20; int remainingWidth = Math.max(minimumToolIdColWidth, width - versionColWidth); int toolIdColWidth = Math.max(minimumToolIdColWidth, Math.min(maxToolIdNameLength, remainingWidth)) + padding; - printListLocalTableHeader(toolIdColWidth, versionColWidth); + printListLocalTableHeader(toolIdColWidth, versionColWidth, repositoryColWidth); for (BalToolsManifest.Tool tool: tools) { + String repository = ProjectConstants.LOCAL_REPOSITORY_NAME.equals(tool.repository()) ? "local" : "central"; String activeIndicator = tool.active() ? "* " : " "; printInCLI("|" + tool.id(), toolIdColWidth); printInCLI(activeIndicator + tool.version(), versionColWidth); + printInCLI(repository, repositoryColWidth); outStream.println(); } outStream.println(); outStream.println(tools.size() + " tools found."); } - private static void printListLocalTableHeader(int toolIdColWidth, int versionColWidth) { + private static void printListLocalTableHeader(int toolIdColWidth, int versionColWidth, int repositoryColWidth) { printInCLI("|TOOL ID", toolIdColWidth); printInCLI("VERSION", versionColWidth); + printInCLI("REPO", repositoryColWidth); outStream.println(); printCharacter("|-", toolIdColWidth, "-", true); printCharacter("-", versionColWidth, "-", true); + printCharacter("-", repositoryColWidth, "-", true); outStream.println(); } diff --git a/cli/ballerina-cli/src/main/java/module-info.java b/cli/ballerina-cli/src/main/java/module-info.java index 27c54d8c248e..40fddd6af9aa 100644 --- a/cli/ballerina-cli/src/main/java/module-info.java +++ b/cli/ballerina-cli/src/main/java/module-info.java @@ -4,7 +4,6 @@ exports io.ballerina.cli.launcher; exports io.ballerina.cli.utils; exports io.ballerina.cli.cmd; - exports io.ballerina.cli.tool; requires io.ballerina.runtime; requires io.ballerina.lang; diff --git a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-deprecate.help b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-deprecate.help index 93fba94ff9fd..8242045cdb3a 100644 --- a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-deprecate.help +++ b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-deprecate.help @@ -2,11 +2,14 @@ NAME ballerina-deprecate - Deprecates a published package SYNOPSIS - bal deprecate [OPTIONS] + bal deprecate [OPTIONS] /[:] DESCRIPTION - The deprecate command marks the specified package as deprecated in Ballerina central. + Deprecate a package published in Ballerina central. + + If a specific version of a package is provided, that version will be deprecated. + If no version is specified, all versions of the package will be deprecated. A deprecated package will not be used as a dependency of a package unless the deprecated package is already recorded in the `Dependencies.toml` and the `--sticky` option is used to build the package. Also, a deprecated package will be used as @@ -14,16 +17,25 @@ DESCRIPTION A warning diagnostic will be issued whenever a deprecated package is used as a dependency. - This command does not delete the package from Ballerina central, and the package can be undeprecated using the `--undo` option. + This command does not delete the package from Ballerina central, and the package deprecation can be undone using the `--undo` option. OPTIONS --message= Use the given as the deprecation message --undo - Undeprecate a deprecated package + Undo deprecation of a package EXAMPLES - Deprecates the package ballerina/io:1.1.1 - bal deprecate ballerina/io:1.1.1 + Deprecate all versions of the package ballerina/io + $ bal deprecate ballerina/io + + Deprecate a specific version of the package ballerina/io + $ bal deprecate ballerina/io:1.1.1 + + Deprecate a specific version of the package with an optional warning message + $ bal deprecate ballerina/io:1.1.1 --message="deprecated due to a security vulnerability" + + Undo deprecation of a package + $ bal deprecate ballerina/io --undo diff --git a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-graphql.help b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-graphql.help index 9224928d3bb8..7b866880b920 100644 --- a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-graphql.help +++ b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-graphql.help @@ -1,12 +1,10 @@ NAME - ballerina-graphql - Generate the Ballerina client sources for a GraphQL config file, - generate the GraphQL schema for a Ballerina GraphQL service, - and generate the Ballerina service sources for a GraphQL schema. + ballerina-graphql - Generate the GraphQL schema for a Ballerina GraphQL service, + generate the Ballerina service sources for a GraphQL schema, and + generate the Ballerina client sources for a GraphQL config file [Experimental]. SYNOPSIS - bal graphql [-i | --input] - [-o | --output] bal graphql [-i | --input] [-o | --output] [-s | --service] @@ -14,25 +12,28 @@ SYNOPSIS [-o | --output] [-m | --mode] [-r | --use-records-for-objects] + bal graphql [-i | --input] + [-o | --output] DESCRIPTION + Export a GraphQL schema (SDL) for a given Ballerina GraphQL service or generate the + Ballerina sources for a given GraphQL schema. The generated Ballerina sources or + GraphQL schema files will be written into the provided output location. + Generate the Ballerina GraphQL client sources for a given GraphQL config file configured with GraphQL schemas specified by GraphQL Schema Definition Language(SDL) and GraphQL - queries or export a GraphQL schema (SDL) for a given Ballerina GraphQL service. - - The generated Ballerina sources or GraphQL schema files will be written into the provided - output location. + queries. GraphQL client generation is an experimental feature which supports only a limited + set of functionality. OPTIONS - -i, --input - This is mandatory input. The given GraphQL config file which is configured with - GraphQL schemas (SDL) and queries, will generate the Ballerina GraphQL client sources. - The given Ballerina GraphQL service file will generate the GraphQL schema (SDL) file - relevant to the service. + -i, --input + This is mandatory input. The given Ballerina GraphQL service file will generate + the GraphQL schema (SDL) file relevant to the service. The given GraphQL schema file will generate the Ballerina GraphQL service sources. + The given GraphQL config file which is configured with GraphQL schemas (SDL) + and queries, will generate the Ballerina GraphQL client sources. -o, --output Location of the generated Ballerina source code or GraphQL schema. If this path is not specified, the output will be written to the same directory from which the command is @@ -43,31 +44,32 @@ OPTIONS If this base path is not specified, schemas will be generated for each of the GraphQL services in the input file. -m, --mode - This mode is used to identify the operation mode. It can be `client`, `schema`, or - `service`. The `client` argument indicates the Ballerina client source code - generation, the `schema` argument indicates the GraphQL schema generation, and the - `service` argument indicates the Ballerina GraphQL service source code generation. - If the `mode` flag is not specified, the `graphql` tool will infer the mode from the + This mode is used to identify the operation mode. It can be `schema`, `service`, + or `client`. The `schema` argument indicates the GraphQL schema generation, + the `service` argument indicates the Ballerina GraphQL service source code generation, + and the `client` argument indicates the Ballerina client source code generation. + If the `mode` flag is not specified, the `graphql` tool will infer the mode from the `input` file extension. -r, --use-records-for-objects - This flag is used without an argument. It is used only in the Ballerina GraphQL - service generation. It will make the Ballerina CLI tool to use record types for + This flag is used without an argument. It is used only in the Ballerina GraphQL + service generation. It will make the Ballerina CLI tool to use record types for GraphQL object types whenever possible. EXAMPLES - Generate Ballerina Graphql clients using a GraphQL config file (`graphql.config.yaml`). - $ bal graphql -i graphql.config.yaml - - Generate Ballerina Graphql clients using a GraphQL config file (`graphql.config.yaml`) - and write the output to the given directory. - $ bal graphql -i graphql.config.yaml -o ./output_path - Generate a GraphQL schema for a selected GraphQL service from the given input file. $ bal graphql -i graphql_service.bal -o ./output_path -s /service_base_path Generate a Ballerina GraphQL service using a GraphQL schema file (`schema.graphql`). $ bal graphql -i schema.graphql -m service -o ./output_path - Generate a Ballerina GraphQL service using a GraphQL schema file (`schema.graphql`) + Generate a Ballerina GraphQL service using a GraphQL schema file (`schema.graphql`) including record types whenever possible. $ bal graphql -i schema.graphql -m service -o ./output_path -r + + Generate Ballerina Graphql clients using a GraphQL config file (`graphql.config.yaml`) + [Experimental]. + $ bal graphql -i graphql.config.yaml + + Generate Ballerina Graphql clients using a GraphQL config file (`graphql.config.yaml`) + and write the output to the given directory [Experimental]. + $ bal graphql -i graphql.config.yaml -o ./output_path diff --git a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-help.help b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-help.help index 93d96b8eb3aa..ba1503f2fbab 100755 --- a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-help.help +++ b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-help.help @@ -40,9 +40,10 @@ COMMANDS format Format Ballerina source files grpc Generate the Ballerina sources for a given Protocol Buffer definition - graphql Generate the Ballerina client sources for a GraphQL config file, - generate the GraphQL schema for a Ballerina GraphQL service, and - generate the Ballerina GraphQL service for a GraphQL schema + graphql Generate the GraphQL schema for a Ballerina GraphQL service, + generate the Ballerina GraphQL service for a GraphQL schema, + and generate the Ballerina client sources for a GraphQL config + file [Experimental] openapi Generate the Ballerina sources for a given OpenAPI definition and vice versa asyncapi Generate the Ballerina sources for a given AsyncAPI definition diff --git a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-semver.help b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-semver.help index a889a7fed04f..0e5b6ba1b156 100755 --- a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-semver.help +++ b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-semver.help @@ -6,14 +6,14 @@ SYNOPSIS DESCRIPTION - Compare the local package changes with any previously released package + Compare the local changes in the package with any previously released package version available in Ballerina Central. Provide suggestions for the next version based on the source code compatibility between the local changes and any specified previous release version. - Note: Validating SemVer compliance of standalone '.bal' files is not allowed. - + Note: This feature is experimental and has limited support for some advanced + language constructs. OPTIONS -d, --show-diff diff --git a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-tool-pull.help b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-tool-pull.help index f04ba3388a98..90f2f61efa36 100644 --- a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-tool-pull.help +++ b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-tool-pull.help @@ -8,6 +8,9 @@ OPTIONS -h, --help Print the usage details of tool pull command. + --repository + Pull a tool from local repository. + DESCRIPTION Fetch a given tool from the Ballerina Central and install it as a Ballerina CLI command. @@ -16,3 +19,7 @@ DESCRIPTION distribution version. If the tool version is not specified, the latest version compatible with the current distribution will be fetched. + +EXAMPLES + Pull the '1.1.0' version of the 'eod' tool from local repository. + $ bal tool pull eod:1.1.0 --repository=local diff --git a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-tool-remove.help b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-tool-remove.help index 18671ce2ae0b..962c53e53d8f 100644 --- a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-tool-remove.help +++ b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-tool-remove.help @@ -8,6 +8,9 @@ OPTIONS -h, --help Print the usage details of tool remove command. + --repository + Remove a tool from a custom repository. + DESCRIPTION Remove a tool that was installed into to the local environment. @@ -16,3 +19,7 @@ DESCRIPTION If the version is not specified, all versions of the tool will be removed including the active version. + +EXAMPLES + Remove the '1.1.0' version of the 'eod' tool from local repository. + $ bal tool remove eod:1.1.0 --repository=local diff --git a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-tool-use.help b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-tool-use.help index 737351170118..012a4dfc3a47 100644 --- a/cli/ballerina-cli/src/main/resources/cli-help/ballerina-tool-use.help +++ b/cli/ballerina-cli/src/main/resources/cli-help/ballerina-tool-use.help @@ -8,6 +8,9 @@ OPTIONS -h, --help Print the usage details of tool use command. + --repository + Use a tool from a custom repository. + DESCRIPTION Mark a specified version of a tool available in the local environment as the active version. @@ -17,3 +20,7 @@ DESCRIPTION EXAMPLES Change the active version of a tool. $ bal tool use health:1.0.0 + + Change the active version to a local repository tool. + $ bal tool use eod:1.1.0 --repository=local + diff --git a/cli/ballerina-cli/src/main/resources/create_cmd_templates/lib/lib.bal b/cli/ballerina-cli/src/main/resources/create_cmd_templates/lib/lib.bal index 7a0681e5344c..ae6ddb6448cb 100644 --- a/cli/ballerina-cli/src/main/resources/create_cmd_templates/lib/lib.bal +++ b/cli/ballerina-cli/src/main/resources/create_cmd_templates/lib/lib.bal @@ -1,10 +1,10 @@ # Returns the string `Hello` with the input string name. # -# + name - name as a string +# + name - name as a string or nil # + return - "Hello, " with the input string name -public function hello(string name) returns string { - if !(name is "") { - return "Hello, " + name; +public function hello(string? name) returns string { + if name !is () { + return string `Hello, ${name}`; } return "Hello, World!"; } diff --git a/cli/ballerina-cli/src/main/resources/create_cmd_templates/lib/tests/lib_test.bal b/cli/ballerina-cli/src/main/resources/create_cmd_templates/lib/tests/lib_test.bal index 31ce10dfbb64..4b725d91de93 100644 --- a/cli/ballerina-cli/src/main/resources/create_cmd_templates/lib/tests/lib_test.bal +++ b/cli/ballerina-cli/src/main/resources/create_cmd_templates/lib/tests/lib_test.bal @@ -21,8 +21,7 @@ function testFunction() { @test:Config {} function negativeTestFunction() { - string name = ""; - string welcomeMsg = hello(name); + string welcomeMsg = hello(()); test:assertEquals("Hello, World!", welcomeMsg); } diff --git a/cli/ballerina-cli/src/main/resources/create_cmd_templates/service/service.bal b/cli/ballerina-cli/src/main/resources/create_cmd_templates/service/service.bal index 90f6a0cc7979..597a58d2d1fa 100644 --- a/cli/ballerina-cli/src/main/resources/create_cmd_templates/service/service.bal +++ b/cli/ballerina-cli/src/main/resources/create_cmd_templates/service/service.bal @@ -5,13 +5,13 @@ import ballerina/http; service / on new http:Listener(9090) { # A resource for generating greetings - # + name - the input string name + # + name - name as a string or nil # + return - string name with hello message or error - resource function get greeting(string name) returns string|error { + resource function get greeting(string? name) returns string|error { // Send a response back to the caller. - if name is "" { + if name is () { return error("name should not be empty!"); } - return "Hello, " + name; + return string `Hello, ${name}`; } } diff --git a/cli/ballerina-cli/src/main/resources/create_cmd_templates/service/tests/service_test.bal b/cli/ballerina-cli/src/main/resources/create_cmd_templates/service/tests/service_test.bal index 1e2c013c0280..0d85eb25166e 100644 --- a/cli/ballerina-cli/src/main/resources/create_cmd_templates/service/tests/service_test.bal +++ b/cli/ballerina-cli/src/main/resources/create_cmd_templates/service/tests/service_test.bal @@ -23,7 +23,7 @@ function testServiceWithProperName() { @test:Config {} function testServiceWithEmptyName() returns error? { - http:Response response = check testClient->get("/greeting/?name="); + http:Response response = check testClient->get("/greeting/"); test:assertEquals(response.statusCode, 500); json errorPayload = check response.getJsonPayload(); test:assertEquals(errorPayload.message, "name should not be empty!"); diff --git a/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/BuildCommandTest.java b/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/BuildCommandTest.java index 540f936c341c..564e2e9008a3 100644 --- a/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/BuildCommandTest.java +++ b/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/BuildCommandTest.java @@ -1203,9 +1203,7 @@ public void testBuildProjectWithBuildToolTomlPropertyDiagnostics(String projectN String buildLog = readOutput(true); Assert.assertEquals(buildLog.replaceAll("\r", ""), getOutput(outputFile)); - Assert.assertTrue(e.getDetailedMessages().get(0) - .equals(error)); - + Assert.assertEquals(error, e.getDetailedMessages().get(0)); } } @@ -1218,7 +1216,23 @@ public void testBuildProjectWithBuildTool() throws IOException { buildCommand.execute(); String buildLog = readOutput(true); Assert.assertEquals(buildLog.replaceAll("\r", ""), - getOutput("build-bal-project.txt")); + getOutput("build-bal-project-with-build-tool.txt")); + } + + @Test(description = "Build a project with a build tool not found") + public void testBuildProjectWithBuildToolNotFound() throws IOException { + Path projectPath = this.testResources.resolve("build-tool-not-found"); + System.setProperty(USER_DIR_PROPERTY, projectPath.toString()); + BuildCommand buildCommand = new BuildCommand(projectPath, printStream, printStream, false); + new CommandLine(buildCommand).parseArgs(); + try { + buildCommand.execute(); + } catch (BLauncherException e) { + String buildLog = readOutput(true); + Assert.assertEquals(buildLog.replaceAll("\r", ""), + getOutput("build-bal-project-with-build-tool-not-found.txt")); + Assert.assertEquals("error: compilation contains errors", e.getDetailedMessages().get(0)); + } } private String getNewVersionForOldDistWarning() { diff --git a/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/PushCommandTest.java b/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/PushCommandTest.java index 5b482b2b0a1f..ed0c74200b32 100644 --- a/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/PushCommandTest.java +++ b/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/PushCommandTest.java @@ -18,6 +18,8 @@ package io.ballerina.cli.cmd; +import com.google.gson.Gson; +import com.google.gson.JsonObject; import io.ballerina.projects.ProjectException; import io.ballerina.projects.Settings; import io.ballerina.projects.TomlDocument; @@ -34,9 +36,11 @@ import org.wso2.ballerinalang.util.RepoUtils; import picocli.CommandLine; +import java.io.BufferedReader; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -203,6 +207,58 @@ public void testPushWithCustomPath() throws IOException { } catch (ProjectException e) { Assert.fail(e.getMessage()); } + } + + @Test(description = "Push a tool to local repository") + public void testPushToolToLocal() throws IOException { + Path validBalProject = Paths.get("build").resolve("tool-gayals"); + + FileUtils.copyDirectory( + this.testResources.resolve("tool-gayals").toFile(), validBalProject.toFile()); + FileUtils.moveDirectory( + validBalProject.resolve("target-dir").toFile(), validBalProject.resolve("custom").toFile()); + + Path customTargetDirBalaPath = validBalProject.resolve("custom").resolve("bala") + .resolve("gayaldassanayake-tool_gayal-java17-1.1.0.bala"); + PushCommand pushCommand = new PushCommand(validBalProject, printStream, printStream, false, + customTargetDirBalaPath); + String[] args = { "--repository=local" }; + new CommandLine(pushCommand).parse(args); + + Path mockRepo = Paths.get("build").resolve("ballerina-home"); + + try (MockedStatic repoUtils = Mockito.mockStatic(RepoUtils.class)) { + repoUtils.when(RepoUtils::createAndGetHomeReposPath).thenReturn(mockRepo); + repoUtils.when(RepoUtils::getBallerinaShortVersion).thenReturn("1.0.0"); + repoUtils.when(RepoUtils::readSettings).thenReturn(Settings.from()); + pushCommand.execute(); + } + + String buildLog = readOutput(true); + String actual = buildLog.replaceAll("\r", ""); + String expected = "Successfully pushed " + customTargetDirBalaPath.toString() + " to 'local' repository."; + Assert.assertTrue(actual.contains(expected)); + + try { + ProjectFiles.validateBalaProjectPath(mockRepo.resolve("repositories").resolve("local") + .resolve("bala").resolve("gayaldassanayake").resolve("tool_gayal") + .resolve("1.1.0").resolve("java17")); + } catch (ProjectException e) { + Assert.fail(e.getMessage()); + } + + Path localToolJsonPath = mockRepo.resolve("repositories").resolve("local").resolve("bala") + .resolve("local-tools.json"); + + Assert.assertTrue(Files.exists(localToolJsonPath)); + + try (BufferedReader bufferedReader = Files.newBufferedReader(localToolJsonPath, StandardCharsets.UTF_8)) { + JsonObject localToolJson = new Gson().fromJson(bufferedReader, JsonObject.class); + JsonObject pkgDesc = localToolJson.get("luhee").getAsJsonObject(); + + Assert.assertEquals(pkgDesc.get("org").getAsString(), "gayaldassanayake"); + Assert.assertEquals(pkgDesc.get("name").getAsString(), "tool_gayal"); + } } diff --git a/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/ToolCommandTest.java b/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/ToolCommandTest.java index 79949bab2a2c..cc57ff9fdc56 100644 --- a/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/ToolCommandTest.java +++ b/cli/ballerina-cli/src/test/java/io/ballerina/cli/cmd/ToolCommandTest.java @@ -18,12 +18,27 @@ package io.ballerina.cli.cmd; +import io.ballerina.projects.BalToolsManifest; +import io.ballerina.projects.BalToolsToml; +import io.ballerina.projects.Settings; +import io.ballerina.projects.internal.BalToolsManifestBuilder; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import org.testng.Assert; +import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import org.wso2.ballerinalang.util.RepoUtils; import picocli.CommandLine; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Objects; +import java.util.Optional; import static io.ballerina.cli.cmd.CommandOutputUtils.getOutput; @@ -33,6 +48,144 @@ * @since 2201.6.0 */ public class ToolCommandTest extends BaseCommandTest { + + private Path testResources; + + @BeforeClass + public void setup() throws IOException { + super.setup(); + try { + this.testResources = super.tmpDir.resolve("build-test-resources"); + URI testResourcesURI = Objects.requireNonNull(getClass().getClassLoader().getResource("test-resources")) + .toURI(); + Files.walkFileTree(Paths.get(testResourcesURI), + new BuildCommandTest.Copy(Paths.get(testResourcesURI), this.testResources)); + } catch (URISyntaxException e) { + Assert.fail("error loading resources"); + } + } + + @Test(description = "Pull a tool from local repository") + public void testPullToolFromLocal() throws IOException { + Path mockHomeRepo = testResources.resolve("local-tool-test").resolve("ballerina-cache"); + try (MockedStatic repoUtils = Mockito.mockStatic(RepoUtils.class)) { + repoUtils.when(RepoUtils::createAndGetHomeReposPath).thenReturn(mockHomeRepo); + repoUtils.when(RepoUtils::getBallerinaShortVersion).thenReturn("2201.9.0"); + repoUtils.when(RepoUtils::readSettings).thenReturn(Settings.from()); + + ToolCommand toolCommand = new ToolCommand(printStream, printStream, false); + new CommandLine(toolCommand).parseArgs("pull", "luhee:1.1.0", "--repository=local"); + toolCommand.execute(); + } + + String buildLog = readOutput(true); + Assert.assertEquals(buildLog, "tool 'luhee:1.1.0' successfully set as the active version.\n"); + Assert.assertTrue(Files.exists(mockHomeRepo.resolve(".config").resolve("bal-tools.toml"))); + BalToolsToml balToolsToml = BalToolsToml.from(mockHomeRepo.resolve(".config").resolve("bal-tools.toml")); + BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); + Optional tool = balToolsManifest.getActiveTool("luhee"); + Assert.assertTrue(tool.isPresent()); + Assert.assertEquals(tool.get().version(), "1.1.0"); + Assert.assertEquals(tool.get().repository(), "local"); + Assert.assertEquals(tool.get().org(), "gayaldassanayake"); + Assert.assertEquals(tool.get().name(), "tool_gayal"); + } + + @Test(description = "Switch active version from local") + public void testUseToolFromLocal() throws IOException { + Path mockHomeRepo = testResources.resolve("local-tool-test").resolve("ballerina-cache"); + try (MockedStatic repoUtils = Mockito.mockStatic(RepoUtils.class)) { + repoUtils.when(RepoUtils::createAndGetHomeReposPath).thenReturn(mockHomeRepo); + repoUtils.when(RepoUtils::getBallerinaShortVersion).thenReturn("2201.9.0"); + repoUtils.when(RepoUtils::readSettings).thenReturn(Settings.from()); + + ToolCommand toolCommand = new ToolCommand(printStream, printStream, false); + new CommandLine(toolCommand).parseArgs("pull", "luhee:1.1.0", "--repository=local"); + toolCommand.execute(); + new CommandLine(toolCommand).parseArgs("pull", "luhee:1.2.0", "--repository=local"); + toolCommand.execute(); + + String buildLog = readOutput(true); + Assert.assertTrue(buildLog.contains("tool 'luhee:1.2.0' successfully set as the active version.\n")); + Assert.assertTrue(Files.exists(mockHomeRepo.resolve(".config").resolve("bal-tools.toml"))); + BalToolsToml balToolsToml = BalToolsToml.from(mockHomeRepo.resolve(".config").resolve("bal-tools.toml")); + BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); + Optional tool = balToolsManifest.getActiveTool("luhee"); + Assert.assertTrue(tool.isPresent()); + Assert.assertEquals(tool.get().version(), "1.2.0"); + Assert.assertEquals(tool.get().repository(), "local"); + Assert.assertEquals(tool.get().org(), "gayaldassanayake"); + Assert.assertEquals(tool.get().name(), "tool_gayal"); + + toolCommand = new ToolCommand(printStream, printStream, false); + new CommandLine(toolCommand).parseArgs("use", "luhee:1.1.0", "--repository=local"); + toolCommand.execute(); + buildLog = readOutput(true); + Assert.assertTrue(buildLog.contains("tool 'luhee:1.1.0' successfully set as the active version.\n")); + balToolsToml = BalToolsToml.from(mockHomeRepo.resolve(".config").resolve("bal-tools.toml")); + balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); + tool = balToolsManifest.getActiveTool("luhee"); + Assert.assertTrue(tool.isPresent()); + Assert.assertEquals(tool.get().version(), "1.1.0"); + Assert.assertEquals(tool.get().repository(), "local"); + Assert.assertEquals(tool.get().org(), "gayaldassanayake"); + Assert.assertEquals(tool.get().name(), "tool_gayal"); + } + } + + @Test(description = "Remove a tool from local repository") + public void testRemoveToolFromLocal() throws IOException { + Path mockHomeRepo = testResources.resolve("local-tool-test").resolve("ballerina-cache"); + try (MockedStatic repoUtils = Mockito.mockStatic(RepoUtils.class)) { + repoUtils.when(RepoUtils::createAndGetHomeReposPath).thenReturn(mockHomeRepo); + repoUtils.when(RepoUtils::getBallerinaShortVersion).thenReturn("2201.9.0"); + repoUtils.when(RepoUtils::readSettings).thenReturn(Settings.from()); + + ToolCommand toolCommand = new ToolCommand(printStream, printStream, false); + new CommandLine(toolCommand).parseArgs("pull", "luhee:1.1.0", "--repository=local"); + toolCommand.execute(); + new CommandLine(toolCommand).parseArgs("pull", "luhee:1.2.0", "--repository=local"); + toolCommand.execute(); + + String buildLog = readOutput(true); + Assert.assertTrue(buildLog.contains("tool 'luhee:1.2.0' successfully set as the active version.\n")); + Assert.assertTrue(Files.exists(mockHomeRepo.resolve(".config").resolve("bal-tools.toml"))); + BalToolsToml balToolsToml = BalToolsToml.from(mockHomeRepo.resolve(".config") + .resolve("bal-tools.toml")); + BalToolsManifest balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); + Optional tool = balToolsManifest.getActiveTool("luhee"); + Assert.assertTrue(tool.isPresent()); + Assert.assertEquals(tool.get().version(), "1.2.0"); + Assert.assertEquals(tool.get().repository(), "local"); + Assert.assertEquals(tool.get().org(), "gayaldassanayake"); + Assert.assertEquals(tool.get().name(), "tool_gayal"); + + toolCommand = new ToolCommand(printStream, printStream, false); + new CommandLine(toolCommand).parseArgs("use", "luhee:1.1.0", "--repository=local"); + toolCommand.execute(); + buildLog = readOutput(true); + Assert.assertTrue(buildLog.contains("tool 'luhee:1.1.0' successfully set as the active version.\n")); + balToolsToml = BalToolsToml.from(mockHomeRepo.resolve(".config").resolve("bal-tools.toml")); + balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); + tool = balToolsManifest.getActiveTool("luhee"); + Assert.assertTrue(tool.isPresent()); + Assert.assertEquals(tool.get().version(), "1.1.0"); + Assert.assertEquals(tool.get().repository(), "local"); + Assert.assertEquals(tool.get().org(), "gayaldassanayake"); + Assert.assertEquals(tool.get().name(), "tool_gayal"); + + toolCommand = new ToolCommand(printStream, printStream, false); + new CommandLine(toolCommand).parseArgs("remove", "luhee:1.2.0", "--repository=local"); + toolCommand.execute(); + buildLog = readOutput(true); + Assert.assertTrue(buildLog.contains("tool 'luhee:1.2.0' successfully removed.\n")); + balToolsToml = BalToolsToml.from(mockHomeRepo.resolve(".config").resolve("bal-tools.toml")); + balToolsManifest = BalToolsManifestBuilder.from(balToolsToml).build(); + tool = balToolsManifest.getTool("luhee", "1.2.0", "local"); + Assert.assertTrue(tool.isEmpty()); + } + } + @Test(description = "Test tool command with the help flag") public void testToolCommandWithHelpFlag() throws IOException { String expected = getOutput("tool-help.txt"); diff --git a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-not-found/Ballerina.toml b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-not-found/Ballerina.toml new file mode 100644 index 000000000000..c1676c237575 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-not-found/Ballerina.toml @@ -0,0 +1,9 @@ +[package] +org = "foo" +name = "winery" +version = "0.1.0" + +[[tool.grpc]] +id = "generate-grpc-client" +filePath = "delivery_grpc.json" +targetModule = "delivery" diff --git a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-not-found/delivery.json b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-not-found/delivery.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-not-found/main.bal b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-not-found/main.bal new file mode 100644 index 000000000000..fd36f05068fe --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-not-found/main.bal @@ -0,0 +1,3 @@ +public function main() { + return; +} diff --git a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-diagnostics/main.bal b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-diagnostics/main.bal index 31e8785bf98f..06a591055bc7 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-diagnostics/main.bal +++ b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-diagnostics/main.bal @@ -1,5 +1,3 @@ -import ballerina/io; - -public function main() { - io:println("Hello, World!"); +public function hello() returns string { + return "Hello, World!"; } diff --git a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-optional-toml-properties/Ballerina.toml b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-optional-toml-properties/Ballerina.toml index 9026646c5944..93319729eac8 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-optional-toml-properties/Ballerina.toml +++ b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-optional-toml-properties/Ballerina.toml @@ -8,3 +8,8 @@ id = "generate-delivery-client" filePath = "delivery.json" targetModule = "delivery" options.limit = "2" + +[[tool.openapi]] +id = "generate-delivery-client-1" +filePath = "delivery.json" +targetModule = "client" diff --git a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-optional-toml-properties/main.bal b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-optional-toml-properties/main.bal index 31e8785bf98f..06a591055bc7 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-optional-toml-properties/main.bal +++ b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-optional-toml-properties/main.bal @@ -1,5 +1,3 @@ -import ballerina/io; - -public function main() { - io:println("Hello, World!"); +public function hello() returns string { + return "Hello, World!"; } diff --git a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-toml-properties/Ballerina.toml b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-toml-properties/Ballerina.toml index 34a80d602b2e..df3adfc52c0d 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-toml-properties/Ballerina.toml +++ b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-toml-properties/Ballerina.toml @@ -6,4 +6,9 @@ version = "0.1.0" [[tool.openapi]] id = "generate-delivery-client" filePath = "" +targetModule = "delivery" +options.mode = "client" + +[[tool.openapi]] +id = "generate-client" options.mode = "client" diff --git a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-toml-properties/main.bal b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-toml-properties/main.bal index 31e8785bf98f..06a591055bc7 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-toml-properties/main.bal +++ b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-invalid-missing-toml-properties/main.bal @@ -1,5 +1,3 @@ -import ballerina/io; - -public function main() { - io:println("Hello, World!"); +public function hello() returns string { + return "Hello, World!"; } diff --git a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-recurring-tool-properties/Ballerina.toml b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-recurring-tool-properties/Ballerina.toml index 137618bdc710..8377990902fe 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-recurring-tool-properties/Ballerina.toml +++ b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-recurring-tool-properties/Ballerina.toml @@ -6,15 +6,12 @@ version = "0.1.0" [[tool.openapi]] id = "generate-delivery-client" filePath = "delivery.json" -targetModule = "delivery" +targetModule = "" options.mode = "client" [[tool.openapi]] id = "generate-delivery-client" -filePath = "delivery1.json" +filePath = "delivery.yml" options.mode = "client" -[[tool.grpc]] -id = "generate-grpc-client" -filePath = "delivery2.json" -targetModule = "delivery" + diff --git a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-recurring-tool-properties/delivery.json b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-recurring-tool-properties/delivery.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-recurring-tool-properties/delivery.yml b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-recurring-tool-properties/delivery.yml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-recurring-tool-properties/main.bal b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-recurring-tool-properties/main.bal index 0a995997c547..06a591055bc7 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-recurring-tool-properties/main.bal +++ b/cli/ballerina-cli/src/test/resources/test-resources/build-tool-with-recurring-tool-properties/main.bal @@ -1,3 +1,3 @@ -public function main() { +public function hello() returns string { return "Hello, World!"; } diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-bal-project-with-build-tool-not-found.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-bal-project-with-build-tool-not-found.txt new file mode 100644 index 000000000000..44095dec267f --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-bal-project-with-build-tool-not-found.txt @@ -0,0 +1,6 @@ + +Executing Build Tools +WARNING: Skipping validation of tool options for tool grpc(generate-grpc-client) due to: Schema file not found: grpc-options-schema.json +ERROR [grpc] Build tool 'grpc' not found +Compiling source + foo/winery:0.1.0 diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-bal-project-with-build-tool.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-bal-project-with-build-tool.txt new file mode 100644 index 000000000000..ae435fd052a3 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-bal-project-with-build-tool.txt @@ -0,0 +1,9 @@ + +Executing Build Tools + openapi(generate-delivery-client) + +Compiling source + foo/winery:0.1.0 + +Generating executable + target/bin/winery.jar diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-diagnostics.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-diagnostics.txt index c2ca71c46587..fe850a2e70fe 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-diagnostics.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-diagnostics.txt @@ -1 +1,7 @@ -ERROR [openAPI sample build tool:(1:1,1:1)] The provided filePath does not exist + +Executing Build Tools + openapi(generate-delivery-client) + +ERROR [generate-delivery-client] The provided filePath does not exist +Compiling source + foo/winery:0.1.0 diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-invalid-missing-optional.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-invalid-missing-optional.txt index c0b09dd05759..0294e2a26a97 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-invalid-missing-optional.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-invalid-missing-optional.txt @@ -1,2 +1,11 @@ + +Executing Build Tools ERROR [Ballerina.toml:(10:1,10:20)] missing required field 'mode' ERROR [Ballerina.toml:(10:17,10:20)] incompatible type for key 'limit': expected 'INTEGER', found 'STRING' +WARNING: Skipping execution of build tool openapi(generate-delivery-client) as Ballerina.toml contains errors + +ERROR [openapi] missing required optional field 'mode' +WARNING: Skipping execution of build tool openapi(generate-delivery-client-1) as Ballerina.toml contains errors + +Compiling source + foo/winery:0.1.0 diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-invalid-missing-toml-properties.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-invalid-missing-toml-properties.txt index 6d004ad5967f..ebfb9b7e0c04 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-invalid-missing-toml-properties.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-invalid-missing-toml-properties.txt @@ -1,2 +1,10 @@ -ERROR [Ballerina.toml:(6:1,9:24)] empty string found for key '[filePath]' in table '[tool.openapi]'. -WARNING [Ballerina.toml:(6:1,9:24)] missing key '[targetModule]' in table '[tool.openapi]'. Default module will be taken as target module. +ERROR [Ballerina.toml:(6:1,10:24)] empty string found for key '[filePath]' in table '[tool.openapi]'. +ERROR [Ballerina.toml:(12:1,14:24)] missing key '[filePath]' in table '[tool.openapi]'. + +Executing Build Tools +WARNING: Skipping execution of build tool openapi(generate-delivery-client) as Ballerina.toml contains errors + +WARNING: Skipping execution of build tool openapi(generate-client) as Ballerina.toml contains errors + +Compiling source + foo/winery:0.1.0 diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-recurring-tool-properties.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-recurring-tool-properties.txt index ac6e2e79166d..20cfc5ad2359 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-recurring-tool-properties.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/build-tool-with-recurring-tool-properties.txt @@ -1,4 +1,10 @@ -ERROR [Ballerina.toml:(6:1,10:24)] recurring target module 'delivery' found in Ballerina.toml. Target module must be unique for each tool -ERROR [Ballerina.toml:(6:1,10:24)] recurring tool id 'generate-delivery-client' found in Ballerina.toml. Tool id must be unique for each tool -WARNING [Ballerina.toml:(12:1,15:24)] missing key '[targetModule]' in table '[tool.openapi]'. Default module will be taken as target module. -WARNING [Ballerina.toml:(17:1,20:26)] tool options validation skipped due to: Schema file not found: grpc-options-schema.json +ERROR [Ballerina.toml:(12:1,15:24)] recurring target module found in Ballerina.toml. Target module must be unique for each tool +ERROR [Ballerina.toml:(12:1,15:24)] recurring tool id 'generate-delivery-client' found in Ballerina.toml. Tool id must be unique for each tool + +Executing Build Tools + openapi(generate-delivery-client) + +WARNING: Skipping execution of build tool openapi(generate-delivery-client) as Ballerina.toml contains errors + +Compiling source + foo/winery:0.1.0 diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/tool-help.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/tool-help.txt index 831c6a3e49c2..fd0068a761de 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/tool-help.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/tool-help.txt @@ -1,48 +1,55 @@ NAME - ballerina-tool - Manage the tools provided by Ballerina + ballerina-tool - Extend the Ballerina CLI with custom commands. SYNOPSIS bal tool [<-h> |<--help>] bal tool [args] DESCRIPTION - Manage Ballerina command line tools via bal tool chain. + Register and manage custom commands for the Ballerina CLI. - Facilitate pulling tools from Ballerina Central and removing - previously pulled tools. + This command facilitates searching, pulling, and updating tools from the + Ballerina Central, switching between installed versions, and listing and + removing installed tools. - Provide subcommands to list all the locally available tools and search - for tools that are available in Ballerina Central. OPTIONS -h, --help Print the usage details of all tool commands. -TOOL COMMANDS +SUBCOMMANDS The following is a list of available subcommands: - pull Pull a tool from Ballerina Central. - list List all the tools available locally. - search Search Ballerina Central for tools. - remove Remove a tool. + pull Pull a tool from the Ballerina Central. + remove Remove a tool from the local environment. + update Update a tool to the latest version. + use Set a tool version as the active version. + list List all tools available in the local environment. + search Search the Ballerina Central for tools. EXAMPLES - Pull a tool from Ballerina Central. - $ bal tool pull openapi + Pull a tool from the Ballerina Central. + $ bal tool pull health - Pull a specific version of a tool from Ballerina Central. - $ bal tool pull openapi:1.5.0 + Pull a specific version of a tool from the Ballerina Central. + $ bal tool pull health:1.0.0 - List all the tools available locally. - $ bal tool list + Remove a specific version of an installed tool. + $ bal tool remove health:1.0.0 + + Remove all the versions of an installed tool. + $ bal tool remove health - Search Ballerina Central for tools. - $ bal tool search openapi + Update a tool to the latest version compatible with the current distribution. + $ bal tool update health - Remove a specific version of previously pulled tool. - $ bal tool remove openapi:1.5.0 + Change the active version of a tool. + $ bal tool use health:1.0.0 + + List all tools available in the local environment. + $ bal tool list - Remove all the versions of a previously pulled tool. - $ bal tool remove openapi + Search the Ballerina Central for tools. + $ bal tool search health -Use 'bal tool --help' for more information on a specific command. +Use 'bal tool --help' for more information on a specific tool subcommand. diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/tool-search-with-no-args.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/tool-search-with-no-args.txt index d8786ac17268..2e97de5247d3 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/tool-search-with-no-args.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/tool-search-with-no-args.txt @@ -1,4 +1,4 @@ ballerina: no keyword given. USAGE: - bal tool search [|||] + bal tool search [|] diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/tool-search-with-too-many-args.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/tool-search-with-too-many-args.txt index 88756253cc5e..5edd841119c7 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/tool-search-with-too-many-args.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/unix/tool-search-with-too-many-args.txt @@ -1,4 +1,4 @@ ballerina: too many arguments. USAGE: - bal tool search [|||] + bal tool search [|] diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-bal-project-with-build-tool-not-found.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-bal-project-with-build-tool-not-found.txt new file mode 100644 index 000000000000..44095dec267f --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-bal-project-with-build-tool-not-found.txt @@ -0,0 +1,6 @@ + +Executing Build Tools +WARNING: Skipping validation of tool options for tool grpc(generate-grpc-client) due to: Schema file not found: grpc-options-schema.json +ERROR [grpc] Build tool 'grpc' not found +Compiling source + foo/winery:0.1.0 diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-bal-project-with-build-tool.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-bal-project-with-build-tool.txt new file mode 100644 index 000000000000..ae435fd052a3 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-bal-project-with-build-tool.txt @@ -0,0 +1,9 @@ + +Executing Build Tools + openapi(generate-delivery-client) + +Compiling source + foo/winery:0.1.0 + +Generating executable + target/bin/winery.jar diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-diagnostics.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-diagnostics.txt index c2ca71c46587..0919dc3c6b45 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-diagnostics.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-diagnostics.txt @@ -1 +1,7 @@ + +Executing Build Tools + openapi(generate-delivery-client) + ERROR [openAPI sample build tool:(1:1,1:1)] The provided filePath does not exist +Compiling source + foo/winery:0.1.0 diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-invalid-missing-optional.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-invalid-missing-optional.txt index c0b09dd05759..0294e2a26a97 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-invalid-missing-optional.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-invalid-missing-optional.txt @@ -1,2 +1,11 @@ + +Executing Build Tools ERROR [Ballerina.toml:(10:1,10:20)] missing required field 'mode' ERROR [Ballerina.toml:(10:17,10:20)] incompatible type for key 'limit': expected 'INTEGER', found 'STRING' +WARNING: Skipping execution of build tool openapi(generate-delivery-client) as Ballerina.toml contains errors + +ERROR [openapi] missing required optional field 'mode' +WARNING: Skipping execution of build tool openapi(generate-delivery-client-1) as Ballerina.toml contains errors + +Compiling source + foo/winery:0.1.0 diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-invalid-missing-toml-properties.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-invalid-missing-toml-properties.txt index 6d004ad5967f..3c1973af6a65 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-invalid-missing-toml-properties.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-invalid-missing-toml-properties.txt @@ -1,2 +1,7 @@ ERROR [Ballerina.toml:(6:1,9:24)] empty string found for key '[filePath]' in table '[tool.openapi]'. -WARNING [Ballerina.toml:(6:1,9:24)] missing key '[targetModule]' in table '[tool.openapi]'. Default module will be taken as target module. + +Executing Build Tools +WARNING: Skipping execution of build tool openapi(generate-delivery-client) as Ballerina.toml contains errors + +Compiling source + foo/winery:0.1.0 diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-recurring-tool-properties.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-recurring-tool-properties.txt index ac6e2e79166d..6e99dd72e6cd 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-recurring-tool-properties.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/build-tool-with-recurring-tool-properties.txt @@ -1,4 +1,14 @@ -ERROR [Ballerina.toml:(6:1,10:24)] recurring target module 'delivery' found in Ballerina.toml. Target module must be unique for each tool -ERROR [Ballerina.toml:(6:1,10:24)] recurring tool id 'generate-delivery-client' found in Ballerina.toml. Tool id must be unique for each tool -WARNING [Ballerina.toml:(12:1,15:24)] missing key '[targetModule]' in table '[tool.openapi]'. Default module will be taken as target module. -WARNING [Ballerina.toml:(17:1,20:26)] tool options validation skipped due to: Schema file not found: grpc-options-schema.json +ERROR [Ballerina.toml:(12:1,15:24)] recurring tool id 'generate-delivery-client' found in Ballerina.toml. Tool id must be unique for each tool +ERROR [Ballerina.toml:(17:1,20:26)] recurring target module found in Ballerina.toml. Target module must be unique for each tool + +Executing Build Tools + openapi(generate-delivery-client) + +ERROR [openAPI sample build tool:(1:1,1:1)] The provided filePath does not exist +WARNING: Skipping execution of build tool openapi(generate-delivery-client) as Ballerina.toml contains errors + +WARNING: Skipping validation of tool options for tool grpc(generate-grpc-client) due to: Schema file not found: grpc-options-schema.json +WARNING: Skipping execution of build tool grpc(generate-grpc-client) as Ballerina.toml contains errors + +Compiling source + foo/winery:0.1.0 diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/tool-help.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/tool-help.txt index 831c6a3e49c2..fd0068a761de 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/tool-help.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/tool-help.txt @@ -1,48 +1,55 @@ NAME - ballerina-tool - Manage the tools provided by Ballerina + ballerina-tool - Extend the Ballerina CLI with custom commands. SYNOPSIS bal tool [<-h> |<--help>] bal tool [args] DESCRIPTION - Manage Ballerina command line tools via bal tool chain. + Register and manage custom commands for the Ballerina CLI. - Facilitate pulling tools from Ballerina Central and removing - previously pulled tools. + This command facilitates searching, pulling, and updating tools from the + Ballerina Central, switching between installed versions, and listing and + removing installed tools. - Provide subcommands to list all the locally available tools and search - for tools that are available in Ballerina Central. OPTIONS -h, --help Print the usage details of all tool commands. -TOOL COMMANDS +SUBCOMMANDS The following is a list of available subcommands: - pull Pull a tool from Ballerina Central. - list List all the tools available locally. - search Search Ballerina Central for tools. - remove Remove a tool. + pull Pull a tool from the Ballerina Central. + remove Remove a tool from the local environment. + update Update a tool to the latest version. + use Set a tool version as the active version. + list List all tools available in the local environment. + search Search the Ballerina Central for tools. EXAMPLES - Pull a tool from Ballerina Central. - $ bal tool pull openapi + Pull a tool from the Ballerina Central. + $ bal tool pull health - Pull a specific version of a tool from Ballerina Central. - $ bal tool pull openapi:1.5.0 + Pull a specific version of a tool from the Ballerina Central. + $ bal tool pull health:1.0.0 - List all the tools available locally. - $ bal tool list + Remove a specific version of an installed tool. + $ bal tool remove health:1.0.0 + + Remove all the versions of an installed tool. + $ bal tool remove health - Search Ballerina Central for tools. - $ bal tool search openapi + Update a tool to the latest version compatible with the current distribution. + $ bal tool update health - Remove a specific version of previously pulled tool. - $ bal tool remove openapi:1.5.0 + Change the active version of a tool. + $ bal tool use health:1.0.0 + + List all tools available in the local environment. + $ bal tool list - Remove all the versions of a previously pulled tool. - $ bal tool remove openapi + Search the Ballerina Central for tools. + $ bal tool search health -Use 'bal tool --help' for more information on a specific command. +Use 'bal tool --help' for more information on a specific tool subcommand. diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/tool-search-with-no-args.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/tool-search-with-no-args.txt index d8786ac17268..2e97de5247d3 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/tool-search-with-no-args.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/tool-search-with-no-args.txt @@ -1,4 +1,4 @@ ballerina: no keyword given. USAGE: - bal tool search [|||] + bal tool search [|] diff --git a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/tool-search-with-too-many-args.txt b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/tool-search-with-too-many-args.txt index 88756253cc5e..5edd841119c7 100644 --- a/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/tool-search-with-too-many-args.txt +++ b/cli/ballerina-cli/src/test/resources/test-resources/command-outputs/windows/tool-search-with-too-many-args.txt @@ -1,4 +1,4 @@ ballerina: too many arguments. USAGE: - bal tool search [|||] + bal tool search [|] diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/bala.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/bala.json new file mode 100644 index 000000000000..fd0a01921270 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/bala.json @@ -0,0 +1,4 @@ +{ + "bala_version": "2.0.0", + "built_by": "WSO2" +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/compiler-plugin/compiler-plugin.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/compiler-plugin/compiler-plugin.json new file mode 100644 index 000000000000..d5949dc5f5e0 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/compiler-plugin/compiler-plugin.json @@ -0,0 +1,6 @@ +{ + "plugin_class": "io.gayal.combined.CombinedCompilerPlugin", + "dependency_paths": [ + "compiler-plugin/libs/a.jar" + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/dependency-graph.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/dependency-graph.json new file mode 100644 index 000000000000..f4a609f60ccb --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/dependency-graph.json @@ -0,0 +1,88 @@ +{ + "packages": [ + { + "org": "gayaldassanayake", + "name": "tool_gayal", + "version": "1.1.0", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "io", + "version": "1.6.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + }, + { + "org": "ballerina", + "name": "io", + "version": "1.6.0", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + }, + { + "org": "ballerina", + "name": "lang.value", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + }, + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + }, + { + "org": "ballerina", + "name": "lang.value", + "version": "0.0.0", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + } + ], + "modules": [ + { + "org": "gayaldassanayake", + "package_name": "tool_gayal", + "version": "1.1.0", + "module_name": "tool_gayal", + "dependencies": [ + { + "org": "ballerina", + "package_name": "io", + "version": "1.6.0", + "module_name": "io", + "dependencies": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/docs/Package.md b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/docs/Package.md new file mode 100644 index 000000000000..43fd57fbd954 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/docs/Package.md @@ -0,0 +1,4 @@ +# Gayals Tool + +1. Run `bal pack` with the provided ballerina distribution pack +2. Extract the .bala file to `~.ballerina/repositories/central.ballerina.io/bala/ballerina/gayalstool/0.1.0/any/` diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/modules/tool_gayal/main.bal b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/modules/tool_gayal/main.bal new file mode 100644 index 000000000000..31e8785bf98f --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/modules/tool_gayal/main.bal @@ -0,0 +1,5 @@ +import ballerina/io; + +public function main() { + io:println("Hello, World!"); +} diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/package.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/package.json new file mode 100644 index 000000000000..25e7a82eb4cf --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/package.json @@ -0,0 +1,14 @@ +{ + "organization": "gayaldassanayake", + "name": "tool_gayal", + "version": "1.1.0", + "export": [ + "tool_gayal" + ], + "ballerina_version": "2201.9.0-SNAPSHOT", + "implementation_vendor": "WSO2", + "language_spec_version": "2023R1", + "platform": "java17", + "graalvmCompatible": true, + "template": false +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/tool/bal-tool.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/tool/bal-tool.json new file mode 100644 index 000000000000..bb78684d86b9 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/tool/bal-tool.json @@ -0,0 +1,6 @@ +{ + "tool_id": "luhee", + "dependency_paths": [ + "tool/libs/GayalsCommand-1.0-SNAPSHOT.jar" + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar new file mode 100644 index 000000000000..dcb434b1ce56 Binary files /dev/null and b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.1.0/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar differ diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/bala.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/bala.json new file mode 100644 index 000000000000..fd0a01921270 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/bala.json @@ -0,0 +1,4 @@ +{ + "bala_version": "2.0.0", + "built_by": "WSO2" +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/compiler-plugin/compiler-plugin.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/compiler-plugin/compiler-plugin.json new file mode 100644 index 000000000000..d5949dc5f5e0 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/compiler-plugin/compiler-plugin.json @@ -0,0 +1,6 @@ +{ + "plugin_class": "io.gayal.combined.CombinedCompilerPlugin", + "dependency_paths": [ + "compiler-plugin/libs/a.jar" + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/dependency-graph.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/dependency-graph.json new file mode 100644 index 000000000000..5956fbe2398c --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/dependency-graph.json @@ -0,0 +1,88 @@ +{ + "packages": [ + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + }, + { + "org": "ballerina", + "name": "io", + "version": "1.6.0", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + }, + { + "org": "ballerina", + "name": "lang.value", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + }, + { + "org": "gayaldassanayake", + "name": "tool_gayal", + "version": "1.2.0", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "io", + "version": "1.6.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + }, + { + "org": "ballerina", + "name": "lang.value", + "version": "0.0.0", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + } + ], + "modules": [ + { + "org": "gayaldassanayake", + "package_name": "tool_gayal", + "version": "1.2.0", + "module_name": "tool_gayal", + "dependencies": [ + { + "org": "ballerina", + "package_name": "io", + "version": "1.6.0", + "module_name": "io", + "dependencies": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/docs/Package.md b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/docs/Package.md new file mode 100644 index 000000000000..43fd57fbd954 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/docs/Package.md @@ -0,0 +1,4 @@ +# Gayals Tool + +1. Run `bal pack` with the provided ballerina distribution pack +2. Extract the .bala file to `~.ballerina/repositories/central.ballerina.io/bala/ballerina/gayalstool/0.1.0/any/` diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/modules/tool_gayal/main.bal b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/modules/tool_gayal/main.bal new file mode 100644 index 000000000000..31e8785bf98f --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/modules/tool_gayal/main.bal @@ -0,0 +1,5 @@ +import ballerina/io; + +public function main() { + io:println("Hello, World!"); +} diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/package.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/package.json new file mode 100644 index 000000000000..03ed7d045493 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/package.json @@ -0,0 +1,14 @@ +{ + "organization": "gayaldassanayake", + "name": "tool_gayal", + "version": "1.2.0", + "export": [ + "tool_gayal" + ], + "ballerina_version": "2201.9.0-SNAPSHOT", + "implementation_vendor": "WSO2", + "language_spec_version": "2023R1", + "platform": "java17", + "graalvmCompatible": true, + "template": false +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/tool/bal-tool.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/tool/bal-tool.json new file mode 100644 index 000000000000..bb78684d86b9 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/tool/bal-tool.json @@ -0,0 +1,6 @@ +{ + "tool_id": "luhee", + "dependency_paths": [ + "tool/libs/GayalsCommand-1.0-SNAPSHOT.jar" + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar new file mode 100644 index 000000000000..dcb434b1ce56 Binary files /dev/null and b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.2.0/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar differ diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/bala.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/bala.json new file mode 100644 index 000000000000..fd0a01921270 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/bala.json @@ -0,0 +1,4 @@ +{ + "bala_version": "2.0.0", + "built_by": "WSO2" +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/compiler-plugin/compiler-plugin.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/compiler-plugin/compiler-plugin.json new file mode 100644 index 000000000000..d5949dc5f5e0 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/compiler-plugin/compiler-plugin.json @@ -0,0 +1,6 @@ +{ + "plugin_class": "io.gayal.combined.CombinedCompilerPlugin", + "dependency_paths": [ + "compiler-plugin/libs/a.jar" + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/dependency-graph.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/dependency-graph.json new file mode 100644 index 000000000000..d79edcfd7999 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/dependency-graph.json @@ -0,0 +1,88 @@ +{ + "packages": [ + { + "org": "gayaldassanayake", + "name": "tool_gayal", + "version": "1.3.0", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "io", + "version": "1.6.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + }, + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + }, + { + "org": "ballerina", + "name": "io", + "version": "1.6.0", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + }, + { + "org": "ballerina", + "name": "lang.value", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + }, + { + "org": "ballerina", + "name": "lang.value", + "version": "0.0.0", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + } + ], + "modules": [ + { + "org": "gayaldassanayake", + "package_name": "tool_gayal", + "version": "1.3.0", + "module_name": "tool_gayal", + "dependencies": [ + { + "org": "ballerina", + "package_name": "io", + "version": "1.6.0", + "module_name": "io", + "dependencies": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/docs/Package.md b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/docs/Package.md new file mode 100644 index 000000000000..43fd57fbd954 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/docs/Package.md @@ -0,0 +1,4 @@ +# Gayals Tool + +1. Run `bal pack` with the provided ballerina distribution pack +2. Extract the .bala file to `~.ballerina/repositories/central.ballerina.io/bala/ballerina/gayalstool/0.1.0/any/` diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/modules/tool_gayal/main.bal b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/modules/tool_gayal/main.bal new file mode 100644 index 000000000000..31e8785bf98f --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/modules/tool_gayal/main.bal @@ -0,0 +1,5 @@ +import ballerina/io; + +public function main() { + io:println("Hello, World!"); +} diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/package.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/package.json new file mode 100644 index 000000000000..3854d0043286 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/package.json @@ -0,0 +1,14 @@ +{ + "organization": "gayaldassanayake", + "name": "tool_gayal", + "version": "1.3.0", + "export": [ + "tool_gayal" + ], + "ballerina_version": "2201.9.0-SNAPSHOT", + "implementation_vendor": "WSO2", + "language_spec_version": "2023R1", + "platform": "java17", + "graalvmCompatible": true, + "template": false +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/tool/bal-tool.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/tool/bal-tool.json new file mode 100644 index 000000000000..bb78684d86b9 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/tool/bal-tool.json @@ -0,0 +1,6 @@ +{ + "tool_id": "luhee", + "dependency_paths": [ + "tool/libs/GayalsCommand-1.0-SNAPSHOT.jar" + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar new file mode 100644 index 000000000000..dcb434b1ce56 Binary files /dev/null and b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/1.3.0/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar differ diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/bala.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/bala.json new file mode 100644 index 000000000000..fd0a01921270 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/bala.json @@ -0,0 +1,4 @@ +{ + "bala_version": "2.0.0", + "built_by": "WSO2" +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/compiler-plugin/compiler-plugin.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/compiler-plugin/compiler-plugin.json new file mode 100644 index 000000000000..d5949dc5f5e0 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/compiler-plugin/compiler-plugin.json @@ -0,0 +1,6 @@ +{ + "plugin_class": "io.gayal.combined.CombinedCompilerPlugin", + "dependency_paths": [ + "compiler-plugin/libs/a.jar" + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/dependency-graph.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/dependency-graph.json new file mode 100644 index 000000000000..0ea50a01b58a --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/dependency-graph.json @@ -0,0 +1,88 @@ +{ + "packages": [ + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + }, + { + "org": "ballerina", + "name": "lang.value", + "version": "0.0.0", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + }, + { + "org": "gayaldassanayake", + "name": "tool_gayal", + "version": "2.2.4", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "io", + "version": "1.6.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + }, + { + "org": "ballerina", + "name": "io", + "version": "1.6.0", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + }, + { + "org": "ballerina", + "name": "lang.value", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + } + ], + "modules": [ + { + "org": "gayaldassanayake", + "package_name": "tool_gayal", + "version": "2.2.4", + "module_name": "tool_gayal", + "dependencies": [ + { + "org": "ballerina", + "package_name": "io", + "version": "1.6.0", + "module_name": "io", + "dependencies": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/docs/Package.md b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/docs/Package.md new file mode 100644 index 000000000000..43fd57fbd954 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/docs/Package.md @@ -0,0 +1,4 @@ +# Gayals Tool + +1. Run `bal pack` with the provided ballerina distribution pack +2. Extract the .bala file to `~.ballerina/repositories/central.ballerina.io/bala/ballerina/gayalstool/0.1.0/any/` diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/modules/tool_gayal/main.bal b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/modules/tool_gayal/main.bal new file mode 100644 index 000000000000..31e8785bf98f --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/modules/tool_gayal/main.bal @@ -0,0 +1,5 @@ +import ballerina/io; + +public function main() { + io:println("Hello, World!"); +} diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/package.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/package.json new file mode 100644 index 000000000000..32939af72c39 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/package.json @@ -0,0 +1,14 @@ +{ + "organization": "gayaldassanayake", + "name": "tool_gayal", + "version": "2.2.4", + "export": [ + "tool_gayal" + ], + "ballerina_version": "2201.9.0-SNAPSHOT", + "implementation_vendor": "WSO2", + "language_spec_version": "2023R1", + "platform": "java17", + "graalvmCompatible": true, + "template": false +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/tool/bal-tool.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/tool/bal-tool.json new file mode 100644 index 000000000000..300d1b1a0c10 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/tool/bal-tool.json @@ -0,0 +1,6 @@ +{ + "tool_id": "gayals", + "dependency_paths": [ + "tool/libs/GayalsCommand-1.0-SNAPSHOT.jar" + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar new file mode 100644 index 000000000000..777a206b284e Binary files /dev/null and b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.4/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar differ diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/bala.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/bala.json new file mode 100644 index 000000000000..fd0a01921270 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/bala.json @@ -0,0 +1,4 @@ +{ + "bala_version": "2.0.0", + "built_by": "WSO2" +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/compiler-plugin/compiler-plugin.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/compiler-plugin/compiler-plugin.json new file mode 100644 index 000000000000..d5949dc5f5e0 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/compiler-plugin/compiler-plugin.json @@ -0,0 +1,6 @@ +{ + "plugin_class": "io.gayal.combined.CombinedCompilerPlugin", + "dependency_paths": [ + "compiler-plugin/libs/a.jar" + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/dependency-graph.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/dependency-graph.json new file mode 100644 index 000000000000..67966f371406 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/dependency-graph.json @@ -0,0 +1,88 @@ +{ + "packages": [ + { + "org": "ballerina", + "name": "lang.value", + "version": "0.0.0", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + }, + { + "org": "gayaldassanayake", + "name": "tool_gayal", + "version": "2.2.5", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "io", + "version": "1.6.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + }, + { + "org": "ballerina", + "name": "io", + "version": "1.6.0", + "transitive": false, + "dependencies": [ + { + "org": "ballerina", + "name": "lang.value", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + }, + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [] + }, + { + "org": "ballerina", + "name": "jballerina.java", + "version": "0.0.0", + "transitive": false, + "dependencies": [], + "modules": [] + } + ], + "modules": [ + { + "org": "gayaldassanayake", + "package_name": "tool_gayal", + "version": "2.2.5", + "module_name": "tool_gayal", + "dependencies": [ + { + "org": "ballerina", + "package_name": "io", + "version": "1.6.0", + "module_name": "io", + "dependencies": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/docs/Package.md b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/docs/Package.md new file mode 100644 index 000000000000..43fd57fbd954 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/docs/Package.md @@ -0,0 +1,4 @@ +# Gayals Tool + +1. Run `bal pack` with the provided ballerina distribution pack +2. Extract the .bala file to `~.ballerina/repositories/central.ballerina.io/bala/ballerina/gayalstool/0.1.0/any/` diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/modules/tool_gayal/main.bal b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/modules/tool_gayal/main.bal new file mode 100644 index 000000000000..31e8785bf98f --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/modules/tool_gayal/main.bal @@ -0,0 +1,5 @@ +import ballerina/io; + +public function main() { + io:println("Hello, World!"); +} diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/package.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/package.json new file mode 100644 index 000000000000..959fd9a8722b --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/package.json @@ -0,0 +1,14 @@ +{ + "organization": "gayaldassanayake", + "name": "tool_gayal", + "version": "2.2.5", + "export": [ + "tool_gayal" + ], + "ballerina_version": "2201.9.0-SNAPSHOT", + "implementation_vendor": "WSO2", + "language_spec_version": "2023R1", + "platform": "java17", + "graalvmCompatible": true, + "template": false +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/tool/bal-tool.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/tool/bal-tool.json new file mode 100644 index 000000000000..bb78684d86b9 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/tool/bal-tool.json @@ -0,0 +1,6 @@ +{ + "tool_id": "luhee", + "dependency_paths": [ + "tool/libs/GayalsCommand-1.0-SNAPSHOT.jar" + ] +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar new file mode 100644 index 000000000000..dcb434b1ce56 Binary files /dev/null and b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/gayaldassanayake/tool_gayal/2.2.5/java17/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar differ diff --git a/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/local-tools.json b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/local-tools.json new file mode 100644 index 000000000000..3e8e08bb5768 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/local-tool-test/ballerina-cache/repositories/local/bala/local-tools.json @@ -0,0 +1 @@ +{"gayal":{"org":"gayaldassanayake","name":"tool_gayal"},"gayals":{"org":"gayaldassanayake","name":"tool_gayal"},"luhee":{"org":"gayaldassanayake","name":"tool_gayal"}} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/.DS_Store b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/.DS_Store new file mode 100644 index 000000000000..1ecf824cf4e0 Binary files /dev/null and b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/.DS_Store differ diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/.devcontainer.json b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/.devcontainer.json new file mode 100644 index 000000000000..a78c901fb8f0 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.4.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/.gitignore b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/.gitignore new file mode 100644 index 000000000000..7512ebe2325f --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/BalTool.toml b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/BalTool.toml new file mode 100644 index 000000000000..63969e4c3238 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/BalTool.toml @@ -0,0 +1,5 @@ +[tool] +id = "gayals" # should be the same as Picocli command name + +[[dependency]] +path = "./tool/libs/GayalsCommand-1.0-SNAPSHOT.jar" diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/Ballerina.toml b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/Ballerina.toml new file mode 100644 index 000000000000..b2803582a216 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "gayaldassanayake" +name = "tool_gayal" +version = "1.1.0" +distribution = "2201.4.0" + +[build-options] +observabilityIncluded = true diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/CompilerPlugin.toml b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/CompilerPlugin.toml new file mode 100644 index 000000000000..ae97900a6cd4 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/CompilerPlugin.toml @@ -0,0 +1,6 @@ +[plugin] +class = "io.gayal.combined.CombinedCompilerPlugin" + +[[dependency]] +path = "./tool/libs/compiler-plugin-with-analyzer-generator-modifier-1.0.0.jar" + diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/Dependencies.toml b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/Dependencies.toml new file mode 100644 index 000000000000..8919ebe4db37 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/Dependencies.toml @@ -0,0 +1,45 @@ +# AUTO-GENERATED FILE. DO NOT MODIFY. + +# This file is auto-generated by Ballerina for managing dependency versions. +# It should not be modified by hand. + +[ballerina] +dependencies-toml-version = "2" +distribution-version = "2201.9.0-SNAPSHOT" + +[[package]] +org = "ballerina" +name = "io" +version = "1.6.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] +modules = [ + {org = "ballerina", packageName = "io", moduleName = "io"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "gayaldassanayake" +name = "tool_gayal" +version = "1.1.0" +dependencies = [ + {org = "ballerina", name = "io"} +] +modules = [ + {org = "gayaldassanayake", packageName = "tool_gayal", moduleName = "tool_gayal"} +] + diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/Package.md b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/Package.md new file mode 100644 index 000000000000..43fd57fbd954 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/Package.md @@ -0,0 +1,4 @@ +# Gayals Tool + +1. Run `bal pack` with the provided ballerina distribution pack +2. Extract the .bala file to `~.ballerina/repositories/central.ballerina.io/bala/ballerina/gayalstool/0.1.0/any/` diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/README.md b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/README.md new file mode 100644 index 000000000000..43fd57fbd954 --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/README.md @@ -0,0 +1,4 @@ +# Gayals Tool + +1. Run `bal pack` with the provided ballerina distribution pack +2. Extract the .bala file to `~.ballerina/repositories/central.ballerina.io/bala/ballerina/gayalstool/0.1.0/any/` diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/main.bal b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/main.bal new file mode 100644 index 000000000000..31e8785bf98f --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/main.bal @@ -0,0 +1,5 @@ +import ballerina/io; + +public function main() { + io:println("Hello, World!"); +} diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/target-dir/.DS_Store b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/target-dir/.DS_Store new file mode 100644 index 000000000000..9dae4ac68983 Binary files /dev/null and b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/target-dir/.DS_Store differ diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/target-dir/bala/compiler-plugin/libs/compiler-plugin-with-analyzer-generator-modifier-1.0.0.jar b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/target-dir/bala/compiler-plugin/libs/compiler-plugin-with-analyzer-generator-modifier-1.0.0.jar new file mode 100644 index 000000000000..5ed0821d57dd Binary files /dev/null and b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/target-dir/bala/compiler-plugin/libs/compiler-plugin-with-analyzer-generator-modifier-1.0.0.jar differ diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/target-dir/bala/gayaldassanayake-tool_gayal-java17-1.1.0.bala b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/target-dir/bala/gayaldassanayake-tool_gayal-java17-1.1.0.bala new file mode 100644 index 000000000000..a16fe484cdc8 Binary files /dev/null and b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/target-dir/bala/gayaldassanayake-tool_gayal-java17-1.1.0.bala differ diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/test.json b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/test.json new file mode 100644 index 000000000000..c9674935d95f --- /dev/null +++ b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/test.json @@ -0,0 +1,10 @@ +{ + "gayals": { + "org": "org.gayals", + "name": "gayals" + }, + "gayals2": { + "org": "org.gayals", + "name": "gayals" + } +} \ No newline at end of file diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar new file mode 100644 index 000000000000..dcb434b1ce56 Binary files /dev/null and b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/tool/libs/GayalsCommand-1.0-SNAPSHOT.jar differ diff --git a/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/tool/libs/compiler-plugin-with-analyzer-generator-modifier-1.0.0.jar b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/tool/libs/compiler-plugin-with-analyzer-generator-modifier-1.0.0.jar new file mode 100644 index 000000000000..5ed0821d57dd Binary files /dev/null and b/cli/ballerina-cli/src/test/resources/test-resources/tool-gayals/tool/libs/compiler-plugin-with-analyzer-generator-modifier-1.0.0.jar differ diff --git a/cli/central-client/src/main/java/org/ballerinalang/central/client/CentralAPIClient.java b/cli/central-client/src/main/java/org/ballerinalang/central/client/CentralAPIClient.java index a2e036d963bf..401be1ce7fd6 100644 --- a/cli/central-client/src/main/java/org/ballerinalang/central/client/CentralAPIClient.java +++ b/cli/central-client/src/main/java/org/ballerinalang/central/client/CentralAPIClient.java @@ -828,7 +828,7 @@ public PackageNameResolutionResponse resolvePackageNames(PackageNameResolutionRe // Unauthorized access token if (packageResolutionResponse.code() == HTTP_UNAUTHORIZED) { - handleUnauthorizedResponse(body); + handleUnauthorizedResponse(contentType.get(), resolvePackageNamesBody); } // If search request was sent wrongly @@ -905,7 +905,7 @@ public PackageResolutionResponse resolveDependencies(PackageResolutionRequest re // Unauthorized access token if (packageResolutionResponse.code() == HTTP_UNAUTHORIZED) { - handleUnauthorizedResponse(body); + handleUnauthorizedResponse(contentType.get(), packageResolutionResponseBody); } // If search request was sent wrongly @@ -973,7 +973,7 @@ public PackageSearchResult searchPackage(String query, String supportedPlatform, // Unauthorized access token if (searchResponse.code() == HTTP_UNAUTHORIZED) { - handleUnauthorizedResponse(body); + handleUnauthorizedResponse(contentType.get(), searchResponseBody); } // If search request was sent wrongly @@ -1043,7 +1043,7 @@ public ToolSearchResult searchTool(String keyword, String supportedPlatform, Str // Unauthorized access token if (searchResponse.code() == HTTP_UNAUTHORIZED) { - handleUnauthorizedResponse(body); + handleUnauthorizedResponse(contentType.get(), searchResponseBody); } // If search request was sent wrongly @@ -1152,7 +1152,7 @@ public void deprecatePackage(String packageInfo, String deprecationMsg, String s // Unauthorized access token if (deprecationResponse.code() == HTTP_UNAUTHORIZED) { - handleUnauthorizedResponse(body); + handleUnauthorizedResponse(contentType.get(), deprecationResponseBody); } // If deprecation request was sent wrongly @@ -1474,8 +1474,10 @@ protected void handleResponseErrors(Response response, String msg) throws Centra Optional body = Optional.ofNullable(response.body()); if (body.isPresent()) { // If search request was sent wrongly + String responseBody = body.get().string(); + MediaType contentType = body.get().contentType(); if (response.code() == HTTP_BAD_REQUEST || response.code() == HTTP_NOT_FOUND) { - Error error = new Gson().fromJson(body.get().string(), Error.class); + Error error = new Gson().fromJson(responseBody, Error.class); if (error.getMessage() != null && !"".equals(error.getMessage())) { throw new CentralClientException(error.getMessage()); } @@ -1483,14 +1485,14 @@ protected void handleResponseErrors(Response response, String msg) throws Centra // Unauthorized access token if (response.code() == HTTP_UNAUTHORIZED) { - handleUnauthorizedResponse(body); + handleUnauthorizedResponse(contentType, responseBody); } // If error occurred at remote repository or invalid/no response received at the // gateway server if (response.code() == HTTP_INTERNAL_ERROR || response.code() == HTTP_UNAVAILABLE || response.code() == HTTP_BAD_GATEWAY || response.code() == HTTP_GATEWAY_TIMEOUT) { - Error error = new Gson().fromJson(body.get().string(), Error.class); + Error error = new Gson().fromJson(responseBody, Error.class); if (error.getMessage() != null && !"".equals(error.getMessage())) { throw new CentralClientException(msg + " reason:" + error.getMessage()); } @@ -1711,23 +1713,21 @@ private void handleUnauthorizedResponse(Optional body, String resp /** * Handle unauthorized response. * - * @param body response body + * @param mediaType mediaType + * @param responseBody response body * @throws IOException when accessing response body * @throws CentralClientException with unauthorized error message */ - private void handleUnauthorizedResponse(Optional body) + private void handleUnauthorizedResponse(MediaType mediaType, String responseBody) throws IOException, CentralClientException { - if (body.isPresent()) { - Optional contentType = Optional.ofNullable(body.get().contentType()); - if (contentType.isPresent() && isApplicationJsonContentType(contentType.get().toString())) { - Error error = new Gson().fromJson(body.get().string(), Error.class); - throw new CentralClientException("unauthorized access token. " + - "check access token set in 'Settings.toml' file. reason: " + error.getMessage()); - } else { - throw new CentralClientException("unauthorized access token. " + - "check access token set in 'Settings.toml' file."); - } + Optional contentType = Optional.ofNullable(mediaType); + StringBuilder message = new StringBuilder("unauthorized access token. " + + "check access token set in 'Settings.toml' file."); + if (contentType.isPresent() && isApplicationJsonContentType(contentType.get().toString())) { + Error error = new Gson().fromJson(responseBody, Error.class); + message.append("reason: ").append(error.getMessage()); } + throw new CentralClientException(message.toString()); } private void logResponseVerbose(Response response, String bodyContent) { diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/BalToolsManifest.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/BalToolsManifest.java index fecdb77a02e9..8164aece980f 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/BalToolsManifest.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/BalToolsManifest.java @@ -27,9 +27,9 @@ * @since 2201.6.0 */ public class BalToolsManifest { - private final Map> tools; + private final Map>> tools; - private BalToolsManifest(Map> tools) { + private BalToolsManifest(Map>> tools) { this.tools = tools; } @@ -37,42 +37,46 @@ public static BalToolsManifest from() { return new BalToolsManifest(new HashMap<>()); } - public static BalToolsManifest from(Map> tools) { + public static BalToolsManifest from(Map>> tools) { return new BalToolsManifest(tools); } - public Map> tools() { + public Map>> tools() { return tools; } - public void addTool(String id, String org, String name, String version, Boolean active) { + public void addTool(String id, String org, String name, String version, Boolean active, String repository) { if (!tools.containsKey(id)) { tools.put(id, new HashMap<>()); } + if (!tools.get(id).containsKey(version)) { + tools.get(id).put(version, new HashMap<>()); + } + if (active) { flipCurrentActiveToolVersion(id); } - tools.get(id).put(version, new Tool(id, org, name, version, active)); + tools.get(id).get(version).put(repository, new Tool(id, org, name, version, active, repository)); } - public Optional getTool(String id, String version) { - if (tools.containsKey(id)) { - return Optional.ofNullable(tools.get(id).get(version)); + public Optional getTool(String id, String version, String repository) { + if (tools.containsKey(id) && tools.get(id).containsKey(version)) { + return Optional.ofNullable(tools.get(id).get(version).get(repository)); } return Optional.empty(); } public Optional getActiveTool(String id) { if (tools.containsKey(id)) { - return tools.get(id).values().stream().filter(Tool::active).findFirst(); + return tools.get(id).values().stream().flatMap(v -> v.values().stream()).filter(Tool::active).findFirst(); } return Optional.empty(); } - public void setActiveToolVersion(String id, String version) { + public void setActiveToolVersion(String id, String version, String repository) { if (tools.containsKey(id)) { flipCurrentActiveToolVersion(id); - tools.get(id).get(version).setActive(true); + tools.get(id).get(version).get(repository).setActive(true); } } @@ -83,14 +87,14 @@ public void removeTool(String id) { tools.remove(id); } - public void removeToolVersion(String id, String version) { - if (tools.containsKey(id)) { - tools.get(id).remove(version); + public void removeToolVersion(String id, String version, String repository) { + if (tools.containsKey(id) && tools.get(id).containsKey(version)) { + tools.get(id).get(version).remove(repository); } } private void flipCurrentActiveToolVersion(String id) { - tools.get(id).forEach((k, v) -> v.setActive(false)); + tools.get(id).forEach((k, v) -> v.forEach((k1, v1) -> v1.setActive(false))); } /** @@ -104,13 +108,15 @@ public static class Tool { private final String name; private final String version; private Boolean active; + private String repository; - public Tool(String id, String org, String name, String version, Boolean active) { + public Tool(String id, String org, String name, String version, Boolean active, String repository) { this.id = id; this.org = org; this.name = name; this.version = version; this.active = active; + this.repository = repository; } public String id() { @@ -133,6 +139,10 @@ public Boolean active() { return active; } + public String repository() { + return repository; + } + public void setActive(boolean active) { this.active = active; } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/BalToolsToml.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/BalToolsToml.java index 34515b7ec492..10820cd1d1ac 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/BalToolsToml.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/BalToolsToml.java @@ -102,17 +102,26 @@ public void modify(BalToolsManifest balToolsManifest) { private String generateContent(BalToolsManifest balToolsManifest) { StringBuilder content = new StringBuilder(); content.append(getAutoGenCode()); - for (Map.Entry> toolVersions: balToolsManifest.tools().entrySet()) { - for (Map.Entry tool : toolVersions.getValue().entrySet()) { - content.append("[[tool]]\n"); - content.append("id = \"").append(tool.getValue().id()).append("\"\n"); - content.append("org = \"").append(tool.getValue().org()).append("\"\n"); - content.append("name = \"").append(tool.getValue().name()).append("\"\n"); - content.append("version = \"").append(tool.getValue().version()).append("\"\n"); - content.append("active = ").append(tool.getValue().active()).append("\n"); - content.append("\n"); + for (Map.Entry>> toolEntry : balToolsManifest.tools() + .entrySet()) { + for (Map.Entry> toolVersions : toolEntry.getValue().entrySet()) { + for (Map.Entry tool : toolVersions.getValue().entrySet()) { + content.append("[[tool]]\n"); + content.append("id = \"").append(tool.getValue().id()).append("\"\n"); + content.append("org = \"").append(tool.getValue().org()).append("\"\n"); + content.append("name = \"").append(tool.getValue().name()).append("\"\n"); + content.append("version = \"").append(tool.getValue().version()).append("\"\n"); + content.append("active = ").append(tool.getValue().active()).append("\n"); + if (tool.getValue().repository() != null) { + content.append("repository = \"").append(tool.getValue().repository()).append("\"\n"); + } + content.append("\n"); + } } } + + + return String.valueOf(content); } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/JBallerinaBackend.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/JBallerinaBackend.java index 2bba26323976..97ff8b0ba202 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/JBallerinaBackend.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/JBallerinaBackend.java @@ -205,12 +205,11 @@ public DiagnosticResult diagnosticResult() { return diagnosticResult; } - // TODO EmitResult should not contain compilation diagnostics. public EmitResult emit(OutputType outputType, Path filePath) { Path generatedArtifact = null; if (diagnosticResult.hasErrors()) { - return new EmitResult(false, diagnosticResult, generatedArtifact); + return new EmitResult(false, new DefaultDiagnosticResult(new ArrayList<>()), generatedArtifact); } switch (outputType) { @@ -227,19 +226,23 @@ public EmitResult emit(OutputType outputType, Path filePath) { throw new RuntimeException("Unexpected output type: " + outputType); } - ArrayList diagnostics = new ArrayList<>(diagnosticResult.allDiagnostics); + ArrayList allDiagnostics = new ArrayList<>(diagnosticResult.allDiagnostics); + List emitResultDiagnostics = new ArrayList<>(); + // Add lifecycle plugin diagnostics. List pluginDiagnostics = packageCompilation.notifyCompilationCompletion(filePath); if (!pluginDiagnostics.isEmpty()) { - diagnostics.addAll(pluginDiagnostics); + emitResultDiagnostics.addAll(pluginDiagnostics); } - diagnosticResult = new DefaultDiagnosticResult(diagnostics); - - List allDiagnostics = new ArrayList<>(diagnostics); + // Add jar resolver diagnostics. jarResolver().diagnosticResult().diagnostics().stream().forEach( - diagnostic -> allDiagnostics.add(diagnostic)); + diagnostic -> emitResultDiagnostics.add(diagnostic)); + allDiagnostics.addAll(emitResultDiagnostics); + // JBallerinaBackend diagnostics contains all diagnostics. + // EmitResult will only contain diagnostics related to emitting the executable. + diagnosticResult = new DefaultDiagnosticResult(allDiagnostics); // TODO handle the EmitResult properly - return new EmitResult(true, new DefaultDiagnosticResult(allDiagnostics), generatedArtifact); + return new EmitResult(true, new DefaultDiagnosticResult(emitResultDiagnostics), generatedArtifact); } private Path emitBala(Path filePath) { diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/PackageContext.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/PackageContext.java index 7cfc92426279..9cfb76791adf 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/PackageContext.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/PackageContext.java @@ -52,6 +52,7 @@ class PackageContext { private final CompilationOptions compilationOptions; private ModuleContext defaultModuleContext; + /** * This variable holds the dependency graph cached in a project. * At the moment, we cache the dependency graph in a bala file. @@ -96,7 +97,6 @@ class PackageContext { this.moduleCompilationMap = new HashMap<>(); this.packageDependencies = Collections.emptySet(); this.pkgDescDependencyGraph = pkgDescDependencyGraph; - } static PackageContext from(Project project, PackageConfig packageConfig, CompilationOptions compilationOptions) { diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/PackageManifest.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/PackageManifest.java index 08bca3b29f52..7dcac8382206 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/PackageManifest.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/PackageManifest.java @@ -415,50 +415,93 @@ public Optional location() { } } + /** + * Represents the build tool configurations in Ballerina.toml file. + * + * @since 2201.9.0 + */ public static class Tool { private final String id; private final TomlTableNode optionsTable; + private final String filePath; + private final String targetModule; + private final String type; + private final Toml optionsToml; + private final boolean hasErrorDiagnostic; + + public Tool(String type, String id, String filePath, String targetModule, Toml optionsToml, + TomlTableNode optionsTable, boolean hasErrorDiagnostic) { + this.type = type; + this.id = id; + this.filePath = filePath; + this.targetModule = targetModule; + this.optionsTable = optionsTable; + this.optionsToml = optionsToml; + this.hasErrorDiagnostic = hasErrorDiagnostic; + } - public String getId() { + /** + * Returns the tool id. + * + * @return the tool id. + */ + public String id() { return id; } - public String getFilePath() { + /** + * Returns the filepath. + * + * @return the filepath. + */ + public String filePath() { return this.filePath; } - public String getTargetModule() { + /** + * Returns the target module. + * + * @return the tool's target module. + */ + public String targetModule() { return this.targetModule; } - public TomlTableNode getOptionsTable() { + /** + * Returns the tool-specific options as a TomlTableNode. + * + * @return the tool options table. + */ + public TomlTableNode optionsTable() { return this.optionsTable; } - public String getType() { + /** + * Returns the type of the tool. + * + * @return the tool type. + */ + public String type() { return type; } - private final String filePath; - private final String targetModule; - private final String type; - - public Toml getOptionsToml() { + /** + * Returns the tool-specific options as a Toml. + * + * @return the options toml. + */ + public Toml optionsToml() { return optionsToml; } - private final Toml optionsToml; - - public Tool(String type, String id, String filePath, String targetModule, Toml optionsToml, - TomlTableNode optionsTable) { - this.type = type; - this.id = id; - this.filePath = filePath; - this.targetModule = targetModule; - this.optionsTable = optionsTable; - this.optionsToml = optionsToml; + /** + * Returns a flag indicating whether the tool has error diagnostics. + * + * @return whether the tool has error diagnostics. + */ + public boolean hasErrorDiagnostic() { + return hasErrorDiagnostic; } - } private List getExport(PackageDescriptor packageDesc, List export) { diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/Project.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/Project.java index c539eb65b357..bfb31196a631 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/Project.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/Project.java @@ -17,11 +17,13 @@ */ package io.ballerina.projects; +import io.ballerina.projects.buildtools.ToolContext; import io.ballerina.projects.environment.ProjectEnvironment; import org.wso2.ballerinalang.compiler.util.CompilerContext; import org.wso2.ballerinalang.compiler.util.CompilerOptions; import java.nio.file.Path; +import java.util.Map; import java.util.Optional; import static org.ballerinalang.compiler.CompilerOptionName.PROJECT_DIR; @@ -37,6 +39,7 @@ public abstract class Project { private BuildOptions buildOptions; protected ProjectEnvironment projectEnvironment; private final ProjectKind projectKind; + private Map toolContextMap; protected Project(ProjectKind projectKind, Path projectPath, @@ -93,6 +96,23 @@ public BuildOptions buildOptions() { return buildOptions; } + /** + * Returns a map of build tools. + * + * @return map of {@code ToolContext} + */ + public Map getToolContextMap() { + return toolContextMap; + } + + /** + * Assigns a map of build tools. + * @param toolContextMap map of {@code ToolContext} + */ + public void setToolContextMap(Map toolContextMap) { + this.toolContextMap = toolContextMap; + } + // Following project path was added to support old compiler extensions. // Currently this method is only called from Build and Single File projects // todo remove after introducing extension model diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ToolContext.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ToolContext.java deleted file mode 100644 index 56abbc166512..000000000000 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ToolContext.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. 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 io.ballerina.projects; - -import io.ballerina.toml.semantic.ast.TomlTableNode; -import io.ballerina.tools.diagnostics.Diagnostic; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - -import static io.ballerina.projects.util.ProjectConstants.GENERATED_MODULES_ROOT; -import static io.ballerina.projects.util.ProjectConstants.TARGET_DIR_NAME; -import static io.ballerina.projects.util.ProjectConstants.TOOL_CACHE_DIR; - -/** - * Represents a build tool instance. - * - * @since 22201.9.0 - */ -public class ToolContext { - - private final Package currentPackage; - private final String toolType; - private final String toolId; - private final String filePath; - private final String targetModule; - private final TomlTableNode optionsTable; - private final Path cachePath; - private final Path outputPath; - private List diagnostics = new ArrayList<>(); - - ToolContext(Package currentPackage, String type, String toolId, String filePath, - String targetModule, TomlTableNode optionsTable) { - this.currentPackage = currentPackage; - this.toolType = type; - this.toolId = toolId; - this.filePath = filePath; - this.targetModule = targetModule; - this.optionsTable = optionsTable; - Path sourceRoot = currentPackage.project().sourceRoot(); - this.cachePath = sourceRoot.resolve(TARGET_DIR_NAME).resolve(TOOL_CACHE_DIR).resolve(toolId); - if (targetModule == null || targetModule.isEmpty()) { - this.outputPath = sourceRoot.resolve(GENERATED_MODULES_ROOT); - } else { - this.outputPath = sourceRoot.resolve(GENERATED_MODULES_ROOT).resolve(targetModule); - } - } - - public static ToolContext from(PackageManifest.Tool tool, Package currentPackage) { - return new ToolContext(currentPackage, tool.getType(), tool.getId(), - tool.getFilePath(), tool.getTargetModule(), - tool.getOptionsTable()); - } - - public String toolId() { - return this.toolId; - } - - public String toolType() { - return this.toolType; - } - - public String filePath() { - return this.filePath; - } - - public String targetModule() { - return targetModule; - } - - public TomlTableNode optionsTable() { - return this.optionsTable; - } - - public Path cachePath() { - return cachePath; - } - - public Path outputPath() { - return outputPath; - } - - public Package currentPackage() { - return currentPackage; - } - public List diagnostics() { - return diagnostics; - } - - public void reportDiagnostic(Diagnostic diagnostic) { - diagnostics.add(diagnostic); - } -} - diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/buildtools/CodeGeneratorTool.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/buildtools/CodeGeneratorTool.java new file mode 100644 index 000000000000..cdac78ac6332 --- /dev/null +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/buildtools/CodeGeneratorTool.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 io.ballerina.projects.buildtools; + +/** + * {@code CodeGeneratorTool} represents a Ballerina build tool. + * + * @since 2201.9.0 + */ +public interface CodeGeneratorTool { + /** + * Execute the command. + * + * @param toolContext the {@link ToolContext} of the build tool + */ + void execute(ToolContext toolContext); + + /** + * Retrieve the tool name. + * + * @return the name of the tool. + */ + String toolName(); +} diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/buildtools/ToolContext.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/buildtools/ToolContext.java new file mode 100644 index 000000000000..e778db0a0274 --- /dev/null +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/buildtools/ToolContext.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2023, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 io.ballerina.projects.buildtools; + +import io.ballerina.projects.Package; +import io.ballerina.projects.PackageManifest; +import io.ballerina.toml.semantic.ast.TomlTableNode; +import io.ballerina.toml.semantic.ast.TopLevelNode; +import io.ballerina.toml.semantic.diagnostics.TomlNodeLocation; +import io.ballerina.tools.diagnostics.Diagnostic; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static io.ballerina.projects.util.ProjectConstants.GENERATED_MODULES_ROOT; +import static io.ballerina.projects.util.ProjectConstants.TARGET_DIR_NAME; +import static io.ballerina.projects.util.ProjectConstants.TOOL_CACHE_DIR; + +/** + * Represents a build tool instance. + * + * @since 22201.9.0 + */ +public class ToolContext { + + private final Package currentPackage; + private final String toolId; + private final String filePath; + private final String targetModule; + private final Map options; + private final List diagnostics = new ArrayList<>(); + + ToolContext(Package currentPackage, String toolId, String filePath, + String targetModule, TomlTableNode optionsTable) { + this.currentPackage = currentPackage; + this.toolId = toolId; + this.filePath = filePath; + this.targetModule = targetModule; + this.options = getOptions(optionsTable); + } + + public static ToolContext from(PackageManifest.Tool tool, Package currentPackage) { + return new ToolContext(currentPackage, tool.id(), + tool.filePath(), tool.targetModule(), + tool.optionsTable()); + } + + /** + * Returns the tool id. + * + * @return the id of the tool configuration. + */ + public String toolId() { + return this.toolId; + } + + /** + * Returns the filepath. + * + * @return the filepath extracted from tool configuration. + */ + public String filePath() { + return this.filePath; + } + + /** + * Returns the target module. + * + * @return the target module extracted from tool configuration. + */ + public String targetModule() { + return targetModule; + } + + /** + * Returns the tool-specific configurations. + * + * @return a map of the optional tool configurations. + */ + public Map options() { + return this.options; + } + + /** + * Returns the cache path. + * + * @return the cache path derived using the tool id. + */ + public Path cachePath() { + Path sourceRoot = currentPackage.project().sourceRoot(); + return sourceRoot.resolve(TARGET_DIR_NAME).resolve(TOOL_CACHE_DIR).resolve(toolId); + } + + /** + * Returns the output path. + * + * @return the output path derived using the target module + */ + public Path outputPath() { + Path sourceRoot = currentPackage.project().sourceRoot(); + if (targetModule == null || targetModule.isEmpty()) { + return sourceRoot.resolve(GENERATED_MODULES_ROOT); + } else { + return sourceRoot.resolve(GENERATED_MODULES_ROOT).resolve(targetModule); + } + } + + /** + * Returns the current package instance. + * + * @return the current package instance. + */ + public Package currentPackage() { + return currentPackage; + } + + /** + * Returns the tool diagnostics list. + * + * @return a list of tool diagnostics. + */ + public List diagnostics() { + return diagnostics; + } + + /** + * Reports a diagnostic against the build tool executed. + * + * @param diagnostic the {@code Diagnostic} to be reported + */ + public void reportDiagnostic(Diagnostic diagnostic) { + diagnostics.add(diagnostic); + } + + private Map getOptions(TomlTableNode optionsTable) { + Map options = new HashMap<>(); + if (null == optionsTable) { + return options; + } + for (String option: optionsTable.entries().keySet()) { + options.put(option, new Option(optionsTable.entries().get(option))); + } + return options; + } + + /** + * Represents a single option Toml node in Ballerina.toml file. + * + * @since 2201.9.0 + */ + public static class Option { + private final Object value; + private final TomlNodeLocation location; + + public Option(TopLevelNode optionNode) { + this.value = optionNode.toNativeObject(); + this.location = optionNode.location(); + } + + /** + * Returns the value of the option. + * + * @return the option value. + */ + public Object value() { + return value; + } + + /** + * Returns the location of the option node in Ballerina.toml. + * + * @return the option location. + */ + public TomlNodeLocation location() { + return location; + } + } +} + diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/BalToolsManifestBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/BalToolsManifestBuilder.java index dd16749c360a..0ec73e7c110f 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/BalToolsManifestBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/BalToolsManifestBuilder.java @@ -84,7 +84,7 @@ private BalToolsManifest parseAsBalToolsManifest() { return BalToolsManifest.from(); } validateBalToolsTomlAgainstSchema(); - Map> tools = getTools(); + Map>> tools = getTools(); return BalToolsManifest.from(tools); } @@ -102,7 +102,7 @@ private void validateBalToolsTomlAgainstSchema() { balToolsTomlValidator.validate(balToolsToml.get().toml()); } - private Map> getTools() { + private Map>> getTools() { if (balToolsToml.isEmpty()) { return new HashMap<>(); } @@ -118,7 +118,7 @@ private Map> getTools() { return new HashMap<>(); } - Map> tools = new HashMap<>(); + Map>> tools = new HashMap<>(); if (toolEntries.kind() == TomlType.TABLE_ARRAY) { TomlTableArrayNode toolTableArray = (TomlTableArrayNode) toolEntries; @@ -128,6 +128,7 @@ private Map> getTools() { String name = getStringValueFromToolNode(toolNode, "name"); String version = getStringValueFromToolNode(toolNode, "version"); Optional active = getBooleanFromToolNode(toolNode, "active"); + String repository = getStringValueFromToolNode(toolNode, "repository"); // If id, org or name, one of the value is null, ignore tool record if (id == null || org == null || name == null) { @@ -148,7 +149,11 @@ private Map> getTools() { if (!tools.containsKey(id)) { tools.put(id, new HashMap<>()); } - tools.get(id).put(version, new BalToolsManifest.Tool(id, org, name, version, active.get())); + if (!tools.get(id).containsKey(version)) { + tools.get(id).put(version, new HashMap<>()); + } + tools.get(id).get(version).put(repository, new BalToolsManifest.Tool(id, org, name, version, + active.get(), repository)); } } return tools; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/ManifestBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/ManifestBuilder.java index e5f56405621d..3eac5055afb9 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/ManifestBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/ManifestBuilder.java @@ -20,6 +20,7 @@ import io.ballerina.projects.BuildOptions; import io.ballerina.projects.DiagnosticResult; +import io.ballerina.projects.Diagnostics; import io.ballerina.projects.PackageDescriptor; import io.ballerina.projects.PackageManifest; import io.ballerina.projects.PackageName; @@ -88,6 +89,8 @@ public class ManifestBuilder { private static final String PACKAGE = "package"; private static final String VERSION = "version"; + public static final String ORG = "org"; + public static final String NAME = "name"; private static final String LICENSE = "license"; private static final String AUTHORS = "authors"; private static final String REPOSITORY = "repository"; @@ -106,6 +109,8 @@ public class ManifestBuilder { private static final String URL = "url"; private static final String DEPENDENCY = "dependency"; private static final String ID = "id"; + private static final String TARGETMODULE = "targetModule"; + private static final String OPTIONS = "options"; private ManifestBuilder(TomlDocument ballerinaToml, TomlDocument compilerPluginToml, @@ -233,23 +238,21 @@ private PackageManifest parseAsPackageManifest() { } private List getTools() { - TomlTableNode rootNode = ballerinaToml.toml().rootNode(); if (rootNode.entries().isEmpty()) { return Collections.emptyList(); } - TopLevelNode toolEntries = rootNode.entries().get("tool"); - List tools = new ArrayList<>(); if (toolEntries == null || toolEntries.kind() != TomlType.TABLE) { return Collections.emptyList(); } TomlTableNode toolTable = (TomlTableNode) toolEntries; Set toolCodes = toolTable.entries().keySet(); - List toolIds = new ArrayList<>(); - List toolTargetModules = new ArrayList<>(); + Set toolIdsSet = new HashSet<>(); + Set targetModuleSet = new HashSet<>(); + // Gather tool configurations and add the tools to a list for (String toolCode : toolCodes) { TopLevelNode toolCodeNode = toolTable.entries().get(toolCode); if (toolCodeNode.kind() != TomlType.TABLE_ARRAY) { @@ -264,44 +267,38 @@ private List getTools() { String filePath = getStringValueFromPreBuildToolNode(dependencyNode, "filePath", toolCode); String targetModule = getStringValueFromPreBuildToolNode(dependencyNode, - "targetModule", toolCode); - Toml optionsToml = getToml(dependencyNode, "options"); - TopLevelNode topLevelNode = dependencyNode.entries().get("options"); - if (topLevelNode == null) { - try { - validateEmptyOptionsToml(dependencyNode, toolCode); - } catch (IOException e) { - reportDiagnostic(dependencyNode, - "tool options validation skipped due to: " + e.getMessage(), - ProjectDiagnosticErrorCode.TOOL_OPTIONS_VALIDATION_SKIPPED.diagnosticId(), - DiagnosticSeverity.WARNING); - } - } + TARGETMODULE, toolCode); + Toml optionsToml = getToml(dependencyNode, OPTIONS); + TopLevelNode topLevelNode = dependencyNode.entries().get(OPTIONS); TomlTableNode optionsNode = null; if (topLevelNode != null && topLevelNode.kind() == TomlType.TABLE) { optionsNode = (TomlTableNode) topLevelNode; } + + // Validate recurring tool ids and target modules + if (!toolIdsSet.add(id)) { + reportDiagnostic(dependencyNode, "recurring tool id '" + id + "' found in Ballerina.toml. " + + "Tool id must be unique for each tool", + ProjectDiagnosticErrorCode.RECURRING_TOOL_PROPERTIES, + DiagnosticSeverity.ERROR); + } + if (!targetModuleSet.add(targetModule)) { + reportDiagnostic(dependencyNode, "recurring target module found in Ballerina.toml. Target " + + "module must be unique for each tool", + ProjectDiagnosticErrorCode.RECURRING_TOOL_PROPERTIES, + DiagnosticSeverity.ERROR); + } + + // Add a flag for tools with error diagnostics + boolean hasErrorDiagnostic = !Diagnostics.filterErrors(dependencyNode.diagnostics()).isEmpty(); PackageManifest.Tool tool = new PackageManifest.Tool(toolCode, id, filePath, - targetModule, optionsToml, optionsNode); + targetModule, optionsToml, optionsNode, hasErrorDiagnostic); tools.add(tool); - addIdTargetModuleToLists(id, targetModule, toolIds, toolTargetModules); } } - validateUniqueIdAndTargetModule(toolIds, toolTargetModules, toolTable); return tools; } - private void validateEmptyOptionsToml(TomlTableNode toolNode, String toolName) throws IOException { - Schema schema = Schema.from(FileUtils.readFileAsString(toolName + "-options-schema.json")); - List requiredFields = schema.required(); - if (!requiredFields.isEmpty()) { - for (String field: requiredFields) { - reportDiagnostic(toolNode, "missing required field '" + field + "'", - ProjectDiagnosticErrorCode.EMPTY_TOOL_PROPERTY.diagnosticId(), DiagnosticSeverity.ERROR); - } - } - } - private PackageDescriptor getPackageDescriptor(TomlTableNode tomlTableNode) { // set defaults String org; @@ -316,7 +313,7 @@ private PackageDescriptor getPackageDescriptor(TomlTableNode tomlTableNode) { if (tomlTableNode.entries().isEmpty()) { reportDiagnostic(tomlTableNode, errorMessage, - ProjectDiagnosticErrorCode.MISSING_PKG_INFO_IN_BALLERINA_TOML.diagnosticId(), + ProjectDiagnosticErrorCode.MISSING_PKG_INFO_IN_BALLERINA_TOML, DiagnosticSeverity.WARNING); return PackageDescriptor.from(defaultOrg(), defaultName(this.projectPath), defaultVersion()); } @@ -324,35 +321,35 @@ private PackageDescriptor getPackageDescriptor(TomlTableNode tomlTableNode) { TopLevelNode topLevelPkgNode = tomlTableNode.entries().get(PACKAGE); if (topLevelPkgNode == null || topLevelPkgNode.kind() != TomlType.TABLE) { reportDiagnostic(tomlTableNode, errorMessage, - ProjectDiagnosticErrorCode.MISSING_PKG_INFO_IN_BALLERINA_TOML.diagnosticId(), + ProjectDiagnosticErrorCode.MISSING_PKG_INFO_IN_BALLERINA_TOML, DiagnosticSeverity.WARNING); return PackageDescriptor.from(defaultOrg(), defaultName(this.projectPath), defaultVersion()); } TomlTableNode pkgNode = (TomlTableNode) topLevelPkgNode; - org = getStringValueFromTomlTableNode(pkgNode, "org"); - if (org == null) { + org = getStringValueFromTomlTableNode(pkgNode, ORG, ""); + if (pkgNode.entries().get(ORG) == null) { org = defaultOrg().value(); reportDiagnostic(pkgNode, "missing key 'org' in table '[package]' in 'Ballerina.toml'. " + "Defaulting to 'org = \"" + org + "\"'", - ProjectDiagnosticErrorCode.MISSING_PKG_INFO_IN_BALLERINA_TOML.diagnosticId(), + ProjectDiagnosticErrorCode.MISSING_PKG_INFO_IN_BALLERINA_TOML, DiagnosticSeverity.WARNING); } - name = getStringValueFromTomlTableNode(pkgNode, "name"); - if (name == null) { + name = getStringValueFromTomlTableNode(pkgNode, NAME, ""); + if (pkgNode.entries().get(NAME) == null) { name = defaultName(this.projectPath).value(); reportDiagnostic(pkgNode, "missing key 'name' in table '[package]' in 'Ballerina.toml'. " + "Defaulting to 'name = \"" + name + "\"'", - ProjectDiagnosticErrorCode.MISSING_PKG_INFO_IN_BALLERINA_TOML.diagnosticId(), + ProjectDiagnosticErrorCode.MISSING_PKG_INFO_IN_BALLERINA_TOML, DiagnosticSeverity.WARNING); } - version = getStringValueFromTomlTableNode(pkgNode, VERSION); - if (version == null) { + version = getStringValueFromTomlTableNode(pkgNode, VERSION, ""); + if (pkgNode.entries().get(VERSION) == null) { version = defaultVersion().value().toString(); reportDiagnostic(pkgNode, "missing key 'version' in table '[package]' in 'Ballerina.toml'. " + "Defaulting to 'version = \"" + version + "\"'", - ProjectDiagnosticErrorCode.MISSING_PKG_INFO_IN_BALLERINA_TOML.diagnosticId(), + ProjectDiagnosticErrorCode.MISSING_PKG_INFO_IN_BALLERINA_TOML, DiagnosticSeverity.WARNING); } @@ -390,7 +387,7 @@ private void validateIconPathForPng(String icon, TomlTableNode pkgNode) { // if file path does not exist, throw this error reportDiagnostic(pkgNode.entries().get(ICON), "could not locate icon path '" + icon + "'", - "error.invalid.path", DiagnosticSeverity.ERROR); + ProjectDiagnosticErrorCode.INVALID_PATH, DiagnosticSeverity.ERROR); } else { // validate file content // if other file types renamed as png, throw this error @@ -398,7 +395,7 @@ private void validateIconPathForPng(String icon, TomlTableNode pkgNode) { if (!FileUtils.isValidPng(iconPath)) { reportDiagnostic(pkgNode.entries().get("icon"), "invalid 'icon' under [package]: 'icon' can only have 'png' images", - "error.invalid.icon", DiagnosticSeverity.ERROR); + ProjectDiagnosticErrorCode.INVALID_ICON, DiagnosticSeverity.ERROR); } } catch (IOException e) { // should not reach to this line @@ -519,7 +516,7 @@ private PackageManifest.Platform getDependencyPlatform(TopLevelNode dependencyNo if (Files.notExists(path)) { reportDiagnostic(platformEntryTable.entries().get("path"), "could not locate dependency path '" + pathValue + "'", - "error.invalid.path", DiagnosticSeverity.ERROR); + ProjectDiagnosticErrorCode.INVALID_PATH, DiagnosticSeverity.ERROR); } } platformEntryMap.put("path", @@ -570,8 +567,8 @@ private List getLocalRepoDependencies() { TomlTableArrayNode dependencyTableArray = (TomlTableArrayNode) dependencyEntries; for (TomlTableNode dependencyNode : dependencyTableArray.children()) { - String name = getStringValueFromDependencyNode(dependencyNode, "name"); - String org = getStringValueFromDependencyNode(dependencyNode, "org"); + String name = getStringValueFromDependencyNode(dependencyNode, NAME); + String org = getStringValueFromDependencyNode(dependencyNode, ORG); String version = getStringValueFromDependencyNode(dependencyNode, VERSION); String repository = getStringValueFromDependencyNode(dependencyNode, REPOSITORY); @@ -593,12 +590,13 @@ private List getLocalRepoDependencies() { return dependencies; } + // TODO: Fix code and messageFormat parameters in usages. private void reportDiagnostic(TopLevelNode tomlTableNode, String message, - String messageFormat, + ProjectDiagnosticErrorCode errorCode, DiagnosticSeverity severity) { DiagnosticInfo diagnosticInfo = - new DiagnosticInfo(null, messageFormat, severity); + new DiagnosticInfo(errorCode.diagnosticId(), errorCode.messageKey(), severity); TomlDiagnostic tomlDiagnostic = new TomlDiagnostic( tomlTableNode.location(), diagnosticInfo, @@ -797,36 +795,27 @@ private String getStringValueFromPreBuildToolNode(TomlTableNode toolNode, String TopLevelNode topLevelNode = toolNode.entries().get(key); String errorMessage = "missing key '[" + key + "]' in table '[tool." + toolCode + "]'."; if (topLevelNode == null) { - if (!key.equals("targetModule")) { + if (!key.equals(TARGETMODULE)) { reportDiagnostic(toolNode, errorMessage, - ProjectDiagnosticErrorCode.MISSING_TOOL_PROPERTIES_IN_BALLERINA_TOML.diagnosticId(), + ProjectDiagnosticErrorCode.MISSING_TOOL_PROPERTIES_IN_BALLERINA_TOML, DiagnosticSeverity.ERROR); - return null; } - reportDiagnostic(toolNode, errorMessage + " Default module will be taken as target module.", - ProjectDiagnosticErrorCode.MISSING_TOOL_PROPERTIES_IN_BALLERINA_TOML.diagnosticId(), - DiagnosticSeverity.WARNING); return null; } ToolNodeValueType toolNodeValueType = getBuildToolTomlValueType(topLevelNode); if (ToolNodeValueType.STRING.equals(toolNodeValueType)) { return getStringFromTomlTableNode(topLevelNode); } else if (ToolNodeValueType.EMPTY.equals(toolNodeValueType)) { - if (!key.equals("targetModule")) { + if (!key.equals(TARGETMODULE)) { reportDiagnostic(toolNode, "empty string found for key '[" + key + "]' in table '[tool." + toolCode + "]'.", - ProjectDiagnosticErrorCode.EMPTY_TOOL_PROPERTY.diagnosticId(), + ProjectDiagnosticErrorCode.EMPTY_TOOL_PROPERTY, DiagnosticSeverity.ERROR); - return null; } - reportDiagnostic(toolNode, "empty string found for key '[" + key + "]' in table '[tool." - + toolCode + "]'. " + "Default module will be taken as the target module", - ProjectDiagnosticErrorCode.EMPTY_TOOL_PROPERTY.diagnosticId(), - DiagnosticSeverity.WARNING); - return getStringFromTomlTableNode(topLevelNode); + return null; } else if (ToolNodeValueType.NON_STRING.equals(toolNodeValueType)) { reportDiagnostic(toolNode, "incompatible type found for key '[" + key + "]': expected 'STRING'", - ProjectDiagnosticErrorCode.INCOMPATIBLE_TYPE_FOR_TOOL_PROPERTY.diagnosticId(), + ProjectDiagnosticErrorCode.INCOMPATIBLE_TYPE_FOR_TOOL_PROPERTY, DiagnosticSeverity.ERROR); return null; } @@ -845,42 +834,6 @@ private Toml getToml(TomlTableNode toolNode, String key) { return new Toml(optionsNode); } - private void addIdTargetModuleToLists(String id, String targetModule, List toolIds, - List targetModules) { - if (id != null) { - toolIds.add(id); - } - if (targetModule == null || targetModule.isEmpty()) { - targetModules.add("default"); - return; - } - targetModules.add(targetModule); - } - - private void validateUniqueIdAndTargetModule(List toolIds, List targetModules, - TomlTableNode tomlTableNode) { - Set toolIdsSet = new HashSet<>(); - Set targetModuleSet = new HashSet<>(); - for (String toolId: toolIds) { - if (!toolIdsSet.add(toolId)) { - reportDiagnostic(tomlTableNode, "recurring tool id '" + toolId + "' found in Ballerina.toml. " + - "Tool id must be unique for each tool", - ProjectDiagnosticErrorCode.RECURRING_TOOL_PROPERTIES.diagnosticId(), - DiagnosticSeverity.ERROR); - break; - } - } - for (String targetModule: targetModules) { - if (!targetModuleSet.add(targetModule)) { - reportDiagnostic(tomlTableNode, "recurring target module '" + targetModule + "' found in " + - "Ballerina.toml. Target module must be unique for each tool", - ProjectDiagnosticErrorCode.RECURRING_TOOL_PROPERTIES.diagnosticId(), - DiagnosticSeverity.ERROR); - break; - } - } - } - /** * Check file name has {@code .png} extension. * diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/PackageDiagnostic.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/PackageDiagnostic.java index a8fd54d1a7b3..ec1502a9bb37 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/PackageDiagnostic.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/PackageDiagnostic.java @@ -52,7 +52,7 @@ public class PackageDiagnostic extends Diagnostic { protected Project project; protected ModuleDescriptor moduleDescriptor; - protected PackageDiagnostic(DiagnosticInfo diagnosticInfo, Location location) { + public PackageDiagnostic(DiagnosticInfo diagnosticInfo, Location location) { this.diagnostic = DiagnosticFactory.createDiagnostic(diagnosticInfo, location); this.location = location; } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/ProjectDiagnosticErrorCode.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/ProjectDiagnosticErrorCode.java index 15e97c34b9fa..5981655136e8 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/ProjectDiagnosticErrorCode.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/ProjectDiagnosticErrorCode.java @@ -27,24 +27,37 @@ */ public enum ProjectDiagnosticErrorCode implements DiagnosticCode { - INVALID_BALA_FILE("BCE5000", "invalid.bala.file"), - OLD_DEPENDENCIES_TOML("BCE5001", "old.dependencies.toml"), - LOCAL_PACKAGES_IN_DEPENDENCIES_TOML("BCE5002", "local.packages.in.dependencies.toml"), - CORRUPTED_DEPENDENCIES_TOML("BCE5003", "corrupted.dependencies.toml"), - INCOMPATIBLE_DEPENDENCY_VERSIONS("BCE5004", "incompatible.dependency.versions"), - PACKAGE_NOT_FOUND("BCE5005", "package.not.found"), - MISSING_PKG_INFO_IN_BALLERINA_TOML("BCE5006", "missing.package.info"), - DEPRECATED_PACKAGE("BCE5007", "deprecated.package"), - BUILT_WITH_OLDER_SL_UPDATE_DISTRIBUTION("BCE5008", "built.with.older.sl.update.distribution"), - CUSTOM_REPOSITORY_NOT_FOUND("BCE5009", "custom.repository.not.found"), - MISSING_TOOL_PROPERTIES_IN_BALLERINA_TOML("BCE5400", "missing.tool.properties"), - INCOMPATIBLE_TYPE_FOR_TOOL_PROPERTY("BCE5401", "incompatible.tool.properties"), - EMPTY_TOOL_PROPERTY("BCE5402", "empty.tool.properties"), - TOOL_OPTIONS_VALIDATION_SKIPPED("BCE5403", "tool.options.validation.skipped"), - RECURRING_TOOL_PROPERTIES("BCE5404", "recurring.tool.properties"), - MODULE_NOT_FOUND("BCE5100", "module.not.found"), - UNSUPPORTED_COMPILER_PLUGIN_TYPE("BCE5200", "unsupported.compiler.plugin.type"), - CONFLICTING_PLATFORM_JAR_FILES("BCE5300", "conflicting.platform.jars.type"), + // Error codes used in the ManifestBuilder + MISSING_PKG_INFO_IN_BALLERINA_TOML("BCE5001", "missing.package.info"), + INVALID_PATH("BCE5002", "error.invalid.path"), + INVALID_ICON("BCE5003", "error.invalid.icon"), + + // Error codes used in DependencyManifestBuilder. + OLD_DEPENDENCIES_TOML("BCE5101", "old.dependencies.toml"), + LOCAL_PACKAGES_IN_DEPENDENCIES_TOML("BCE5102", "local.packages.in.dependencies.toml"), + CORRUPTED_DEPENDENCIES_TOML("BCE5103", "corrupted.dependencies.toml"), + + // Error codes used during dependency resolution. + INCOMPATIBLE_DEPENDENCY_VERSIONS("BCE5201", "incompatible.dependency.versions"), + PACKAGE_NOT_FOUND("BCE5202", "package.not.found"), + DEPRECATED_PACKAGE("BCE5203", "deprecated.package"), + BUILT_WITH_OLDER_SL_UPDATE_DISTRIBUTION("BCE5204", "built.with.older.sl.update.distribution"), + CUSTOM_REPOSITORY_NOT_FOUND("BCE5205", "custom.repository.not.found"), + + // Error codes related to build tools. + MISSING_TOOL_PROPERTIES_IN_BALLERINA_TOML("BCE5301", "missing.tool.properties"), + INCOMPATIBLE_TYPE_FOR_TOOL_PROPERTY("BCE5302", "incompatible.tool.properties"), + EMPTY_TOOL_PROPERTY("BCE5303", "empty.tool.properties"), + TOOL_OPTIONS_VALIDATION_SKIPPED("BCE5304", "tool.options.validation.skipped"), + RECURRING_TOOL_PROPERTIES("BCE5305", "recurring.tool.properties"), + BUILD_TOOL_NOT_FOUND("BCE5306", "build.tool.not.found"), + TOOL_OPTIONS_VALIDATION_FAILED("BCE5307", "tool.options.validation.failed"), + + // Error codes used for compiler plugins. + UNSUPPORTED_COMPILER_PLUGIN_TYPE("BCE5401", "unsupported.compiler.plugin.type"), + + // Error codes used for Jar resolving. + CONFLICTING_PLATFORM_JAR_FILES("BCE5501", "conflicting.platform.jars.type"), ; private final String diagnosticId; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/TypeConverter.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/TypeConverter.java index e4286cdddc38..ad128e640726 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/TypeConverter.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/TypeConverter.java @@ -20,6 +20,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; +import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; @@ -33,6 +34,7 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; +import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; import java.util.HashMap; @@ -181,14 +183,20 @@ private JsonObject generateRecordType(BRecordType effectiveType, BIntersectionTy if (!requiredFields.isEmpty()) { typeNode.add("required", requiredFields); } - // Get record type and set the type name as a property - for (BType bType : intersectionType.getConstituentTypes()) { - // Does not consider anonymous records - if (bType.tag == TypeTags.TYPEREFDESC) { - typeNode.addProperty("name", bType.toString().trim()); + BTypeSymbol intersectionSymbol = intersectionType.tsymbol; + // The tsymbol name is implicitly empty + if (intersectionSymbol.name != Names.EMPTY) { + typeNode.addProperty("name", intersectionSymbol.toString().trim()); + } else { + // Get record type and set the type name as a property + for (BType bType : intersectionType.getConstituentTypes()) { + // Does not consider anonymous records + if (bType.tag == TypeTags.TYPEREFDESC) { + // Revisit with https://github.com/ballerina-platform/ballerina-lang/issues/24078 + typeNode.addProperty("name", bType.toString().trim()); + } } } - return typeNode; } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/environment/EnvironmentPackageCache.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/environment/EnvironmentPackageCache.java index 14634773018a..819dbc4c9912 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/environment/EnvironmentPackageCache.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/environment/EnvironmentPackageCache.java @@ -22,15 +22,20 @@ */ public class EnvironmentPackageCache implements WritablePackageCache { - private final Map projects = new HashMap<>(); + private final Map projectsById = new HashMap<>(); + private final Map>> + projectsByOrgNameVersion = new HashMap<>(); public void cache(Package pkg) { - projects.put(pkg.packageId(), pkg.project()); + projectsById.put(pkg.packageId(), pkg.project()); + projectsByOrgNameVersion.computeIfAbsent(pkg.packageOrg(), k -> new HashMap<>()) + .computeIfAbsent(pkg.packageName(), k -> new HashMap<>()) + .put(pkg.packageVersion(), pkg.project()); } @Override public Optional getPackage(PackageId packageId) { - Project project = projects.get(packageId); + Project project = projectsById.get(packageId); if (project == null) { return Optional.empty(); } @@ -40,7 +45,7 @@ public Optional getPackage(PackageId packageId) { @Override public Package getPackageOrThrow(PackageId packageId) { - Project project = projects.get(packageId); + Project project = projectsById.get(packageId); if (project == null) { throw new IllegalStateException("Cannot find a Package for the given PackageId: " + packageId); } @@ -51,23 +56,15 @@ public Package getPackageOrThrow(PackageId packageId) { public Optional getPackage(PackageOrg packageOrg, PackageName packageName, PackageVersion version) { - // Do we have a need to improve this logic? - for (Project project : projects.values()) { - PackageDescriptor pkgDesc = project.currentPackage().descriptor(); - if (pkgDesc.org().equals(packageOrg) && pkgDesc.name().equals(packageName) && - pkgDesc.version().equals(version)) { - return Optional.of(project.currentPackage()); - } - } - return Optional.empty(); + return Optional.ofNullable(projectsByOrgNameVersion.getOrDefault(packageOrg, new HashMap<>()) + .getOrDefault(packageName, new HashMap<>()) + .get(version)).map(Project::currentPackage); } @Override public List getPackages(PackageOrg packageOrg, PackageName packageName) { - // Do we have a need to improve this logic? - // TODO Optimize this logic List foundList = new ArrayList<>(); - for (Project project : projects.values()) { + for (Project project : projectsById.values()) { PackageManifest pkgDesc = project.currentPackage().manifest(); if (pkgDesc.org().equals(packageOrg) && pkgDesc.name().equals(packageName)) { @@ -79,6 +76,14 @@ public List getPackages(PackageOrg packageOrg, PackageName packageName) @Override public void removePackage(PackageId packageId) { - projects.remove(packageId); + Optional project = Optional.ofNullable(projectsById.get(packageId)); + if (project.isEmpty()) { + return; + } + PackageDescriptor pkgDesc = project.get().currentPackage().descriptor(); + projectsByOrgNameVersion.get(pkgDesc.org()) + .get(pkgDesc.name()) + .remove(pkgDesc.version()); + projectsById.remove(packageId); } } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/util/ProjectConstants.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/util/ProjectConstants.java index 1ced0dd81592..149440120bc3 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/util/ProjectConstants.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/util/ProjectConstants.java @@ -134,4 +134,8 @@ private ProjectConstants() {} public static final String CONFIG_DIR = ".config"; public static final String PROFILER_DIR_NAME = "profiler"; public static final String TOOL_CACHE_DIR = "tool-cache"; + public static final String TOOL_DIAGNOSTIC_CODE_PREFIX = "BCE53"; + public static final String ORG = "org"; + public static final String PACKAGE_NAME = "name"; + public static final String LOCAL_TOOLS_JSON = "local-tools.json"; } diff --git a/compiler/ballerina-lang/src/main/java/module-info.java b/compiler/ballerina-lang/src/main/java/module-info.java index 89c5534e5d54..8fb194dc243c 100644 --- a/compiler/ballerina-lang/src/main/java/module-info.java +++ b/compiler/ballerina-lang/src/main/java/module-info.java @@ -82,4 +82,5 @@ exports io.ballerina.projects.internal.configschema to org.ballerinalang.config.schema.generator, io.ballerina.language.server.core; exports io.ballerina.projects.plugins.completion; + exports io.ballerina.projects.buildtools; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java index 8efd06fc65e1..7a4a564c981a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java @@ -521,7 +521,9 @@ public static String generateReturnType(BType bType) { static String cleanupObjectTypeName(String typeName) { int index = typeName.lastIndexOf("."); // Internal type names can contain dots hence use the `lastIndexOf` int typeNameLength = typeName.length(); - if (index > 0 && index != typeNameLength - 1) { // Resource method name can contain . at the end + if (index > 1 && typeName.charAt(index - 1) == '\\') { // Methods can contain escaped characters + return typeName; + } else if (index > 0 && index != typeNameLength - 1) { // Resource method name can contain . at the end return typeName.substring(index + 1); } else if (index > 0) { // We will reach here for resource methods eg: (MyClient8.$get$.) diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmConstants.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmConstants.java index a83d919ed8a4..388089ae40b0 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmConstants.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmConstants.java @@ -344,8 +344,8 @@ public class JvmConstants { public static final String TYPEDESC_CLASS_PREFIX = "$typedesc$"; public static final String FRAME_CLASS_PREFIX = "frames/$frame$"; public static final String BALLERINA = "ballerina"; - public static final String ENCODED_DOT_CHARACTER = "$0046"; - public static final String ENCODED_JAVA_MODULE = "jballerina$0046java"; + public static final String ENCODED_DOT_CHARACTER = "&0046"; + public static final String ENCODED_JAVA_MODULE = "jballerina&0046java"; public static final PackageID DEFAULT = new PackageID(Names.ANON_ORG, new Name(ENCODED_DOT_CHARACTER), DEFAULT_VERSION); public static final String BUILT_IN_PACKAGE_NAME = "lang" + ENCODED_DOT_CHARACTER + "annotations"; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java index aea21af2282a..18e2d95e4743 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java @@ -567,7 +567,7 @@ private void linkModuleFunctions(BIRPackage birPackage, String initClass, boolea // link the bir function for lookup String birFuncName = birFunc.name.value; String balFileName; - if (birFunc.pos == null) { + if (birFunc.pos == null || birFunc.pos == symbolTable.builtinPos) { balFileName = MODULE_INIT_CLASS_NAME; } else { balFileName = birFunc.pos.lineRange().fileName(); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodResolver.java index d41a5e17ad72..94191253b4ae 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodResolver.java @@ -193,13 +193,15 @@ private boolean isAcceptingBundledParams(JMethodRequest jMethodRequest, JMethod return false; } Class[] paramTypes = jMethod.getParamTypes(); - if (count == reducedParamCount && isParamAssignableToBArray(paramTypes[0])) { + if (count == reducedParamCount && paramTypes.length > 0 && isParamAssignableToBArray(paramTypes[0])) { return true; - } else if ((count == (reducedParamCount + 1)) && isParamAssignableToBArray(paramTypes[1])) { + } else if ((count == (reducedParamCount + 1)) && paramTypes.length > 1 && + isParamAssignableToBArray(paramTypes[1])) { // This is for object interop functions when self is passed as a parameter jMethod.setReceiverType(jMethodRequest.receiverType); return jMethodRequest.receiverType != null; - } else if ((count == (reducedParamCount + 2)) && isParamAssignableToBArray(paramTypes[2])) { + } else if ((count == (reducedParamCount + 2)) && paramTypes.length > 2 && + isParamAssignableToBArray(paramTypes[2])) { // This is for object interop functions when both BalEnv and self is passed as parameters. if (jMethodRequest.receiverType != null) { jMethod.setReceiverType(jMethodRequest.receiverType); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MainMethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MainMethodGen.java index 8369450757fd..5a4ec872f1a1 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MainMethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MainMethodGen.java @@ -55,8 +55,10 @@ import static org.objectweb.asm.Opcodes.INVOKESPECIAL; import static org.objectweb.asm.Opcodes.INVOKESTATIC; import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; +import static org.objectweb.asm.Opcodes.LCONST_1; import static org.objectweb.asm.Opcodes.NEW; import static org.objectweb.asm.Opcodes.PUTFIELD; +import static org.objectweb.asm.Opcodes.PUTSTATIC; import static org.objectweb.asm.Opcodes.RETURN; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CLI_SPEC; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.COMPATIBILITY_CHECKER; @@ -91,6 +93,7 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.STRING_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.TEST_ARGUMENTS; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.TEST_CONFIG_ARGS; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.TEST_EXECUTION_STATE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.THROWABLE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.TOML_DETAILS; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.ADD_SHUTDOWN_HOOK; @@ -436,7 +439,7 @@ private void genSubmitToScheduler(String initClass, MethodVisitor mv, boolean is mv.visitFieldInsn(PUTFIELD, STRAND_CLASS, MethodGenUtils.FRAMES, STACK_FRAMES); startScheduler(indexMap.get(SCHEDULER_VAR), mv); - handleErrorFromFutureValue(mv, isTestFunction); + handleErrorFromFutureValue(mv, initClass, isTestFunction); } private void stopListeners(MethodVisitor mv, boolean isServiceEPAvailable) { @@ -444,19 +447,20 @@ private void stopListeners(MethodVisitor mv, boolean isServiceEPAvailable) { mv.visitMethodInsn(INVOKESTATIC , LAUNCH_UTILS, "stopListeners", "(Z)V", false); } - private void handleErrorFromFutureValue(MethodVisitor mv, boolean isTestFunction) { + private void handleErrorFromFutureValue(MethodVisitor mv, String initClass, boolean isTestFunction) { mv.visitVarInsn(ALOAD, indexMap.get(INIT_FUTURE_VAR)); mv.visitInsn(DUP); - mv.visitFieldInsn(GETFIELD , FUTURE_VALUE , PANIC_FIELD, GET_THROWABLE); + mv.visitFieldInsn(GETFIELD, FUTURE_VALUE, PANIC_FIELD, GET_THROWABLE); // handle any runtime errors Label labelIf = new Label(); mv.visitJumpInsn(IFNULL, labelIf); + mv.visitFieldInsn(GETFIELD, FUTURE_VALUE, PANIC_FIELD, GET_THROWABLE); if (isTestFunction) { - mv.visitFieldInsn(GETFIELD , FUTURE_VALUE , PANIC_FIELD, GET_THROWABLE); mv.visitMethodInsn(INVOKESTATIC, RUNTIME_UTILS, HANDLE_STOP_PANIC_METHOD, HANDLE_THROWABLE, false); + mv.visitInsn(LCONST_1); + mv.visitFieldInsn(PUTSTATIC, initClass, TEST_EXECUTION_STATE, "J"); } else { - mv.visitFieldInsn(GETFIELD, FUTURE_VALUE, PANIC_FIELD, GET_THROWABLE); mv.visitMethodInsn(INVOKESTATIC, RUNTIME_UTILS, HANDLE_THROWABLE_METHOD, HANDLE_THROWABLE, false); } mv.visitInsn(RETURN); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java index 8a2f42b8e1b0..082222b9cee2 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java @@ -9758,9 +9758,9 @@ private BLangAssignment createStructFieldUpdate(BLangFunction function, BLangExp BLangAssignment assignmentStmt = (BLangAssignment) TreeBuilder.createAssignmentNode(); // position information is not passed to remove code coverage for record/class definition - expr.pos = null; - fieldName.pos = null; - fieldSymbol.pos = null; + expr.pos = this.symTable.builtinPos; + fieldName.pos = this.symTable.builtinPos; + fieldSymbol.pos = this.symTable.builtinPos; assignmentStmt.expr = expr; assignmentStmt.pos = function.pos; assignmentStmt.setVariable(fieldAccess); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/MockDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/MockDesugar.java index 5bd74cd596cd..d9ce5313f26e 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/MockDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/MockDesugar.java @@ -115,35 +115,13 @@ public void generateMockFunctions(BLangPackage pkgNode) { // Get the Mock Function map from the pkgNode Map mockFunctionMap = pkgNode.getTestablePkg().getMockFunctionNamesMap(); - // Get all the imports symbols from the testable package - Set mockFunctionSet = mockFunctionMap.keySet(); - ArrayList importsList = new ArrayList<>(); - for (BLangImportPackage importPkg : pkgNode.getTestablePkg().getImports()) { - if (importPkg.symbol == null) { - continue; - } - if (!importPkg.symbol.toString().contains(testPackageSymbol)) { - importsList.add(importPkg.symbol.toString()); - } - } - - // Get all the imports from the current package - for (BLangImportPackage importPkg : pkgNode.getImports()) { - if (importPkg.symbol == null) { - continue; - } - if (importsList.contains(importPkg.symbol.toString())) { - continue; - } - if (!importPkg.symbol.toString().contains(testPackageSymbol)) { - importsList.add(importPkg.symbol.toString()); - } - } + // Get the mock function type map from the pkgNode + Map isLegacyMockingMap = pkgNode.getTestablePkg().getIsLegacyMockingMap(); + // Get the set of functions to generate + Set mockFunctionSet = mockFunctionMap.keySet(); for (String function : mockFunctionSet) { - if (function.contains(pkgNode.packageID.toString()) ? !function.split(pkgNode.packageID.toString())[1]. - startsWith(MOCK_LEGACY_DELIMITER) : - !startsWithMockLegacyDelimiterForImportedMockFunctions(function, importsList)) { + if (!isLegacyMockingMap.get(function)) { pkgNode.getTestablePkg().functions.add(generateMockFunction(function)); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java index 3d256e83b017..707d664861a6 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java @@ -2103,8 +2103,8 @@ public void visit(BLangGroupExpr groupExpr) { @Override public void visit(BLangLetExpression letExpr) { - letExpr.expr = rewrite(letExpr.expr); letExpr.letVarDeclarations.forEach(var -> this.acceptNode((BLangNode) var.definitionNode)); + letExpr.expr = rewrite(letExpr.expr); result = letExpr; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/TransactionDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/TransactionDesugar.java index 886b44eefb74..d89ce5c31b7d 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/TransactionDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/TransactionDesugar.java @@ -441,6 +441,7 @@ void createRollbackIfFailed(Location pos, BLangBlockStmt onFailBodyBlock, // transactional BLangTransactionalExpr isTransactionalCheck = TreeBuilder.createTransactionalExpressionNode(); + isTransactionalCheck.setBType(symTable.booleanType); isTransactionalCheck.pos = pos; // if(($trxError$ is error) && !($trxError$ is TransactionError) && transactional) diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java index fd187117256d..17bd8441aff5 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java @@ -696,7 +696,7 @@ public void visit(BLangMatchStatement source) { source.cloneRef = clone; clone.setExpression(clone(source.getExpression())); clone.matchClauses = cloneList(source.matchClauses); - clone.onFailClause = source.onFailClause; + clone.onFailClause = clone(source.onFailClause); } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java index 4aa6aa675130..7ec274881c0a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java @@ -183,6 +183,7 @@ public void checkQueryType(BLangQueryExpr queryExpr, TypeChecker.AnalyzerData da if (finalClause.getKind() == NodeKind.SELECT) { actualType = resolveQueryType(commonAnalyzerData.queryEnvs.peek(), ((BLangSelectClause) finalClause).expression, data.expType, queryExpr, clauses, data); + queryExpr.setDeterminedType(actualType); actualType = (actualType == symTable.semanticError) ? actualType : types.checkType(queryExpr.pos, actualType, data.expType, DiagnosticErrorCode.INCOMPATIBLE_TYPES); } else { @@ -196,6 +197,7 @@ public void checkQueryType(BLangQueryExpr queryExpr, TypeChecker.AnalyzerData da if (completionType != null) { queryType = BUnionType.create(null, queryType, completionType); } + queryExpr.setDeterminedType(queryType); actualType = types.checkType(finalClauseExpr.pos, queryType, data.expType, DiagnosticErrorCode.INCOMPATIBLE_TYPES); } 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 1398f14a277d..a94d988120a9 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 @@ -498,12 +498,18 @@ public void visit(BLangFunction funcNode, AnalyzerData data) { //set function param flag to final funcNode.symbol.params.forEach(param -> param.flags |= Flags.FUNCTION_FINAL); + BVarSymbol restParamSym = funcNode.symbol.restParam; + if (restParamSym != null) { + restParamSym.flags |= Flags.FUNCTION_FINAL; + } + if (!funcNode.flagSet.contains(Flag.WORKER)) { // annotation validation for workers is done for the invocation. funcNode.annAttachments.forEach(annotationAttachment -> { if (Symbols.isFlagOn(funcNode.symbol.flags, Flags.REMOTE) && funcNode.receiver != null - && Symbols.isService(funcNode.receiver.symbol)) { + && Symbols.isService(funcNode.receiver.getBType().tsymbol)) { annotationAttachment.attachPoints.add(AttachPoint.Point.SERVICE_REMOTE); + annotationAttachment.attachPoints.add(AttachPoint.Point.OBJECT_METHOD); } else if (funcNode.attachedFunction) { annotationAttachment.attachPoints.add(AttachPoint.Point.OBJECT_METHOD); } @@ -1530,6 +1536,7 @@ public void visit(BLangRecordVariable varNode, AnalyzerData data) { varNode.setBType(symResolver.resolveTypeNode(varNode.typeNode, currentEnv)); } + analyzeNode(varNode.typeNode, data); long ownerSymTag = currentEnv.scope.owner.tag; // If this is a module record variable, checkTypeAndVarCountConsistency already done at symbolEnter. if ((ownerSymTag & SymTag.PACKAGE) != SymTag.PACKAGE && @@ -1584,6 +1591,7 @@ public void visit(BLangTupleVariable varNode, AnalyzerData data) { varNode.setBType(symResolver.resolveTypeNode(varNode.typeNode, currentEnv)); } + analyzeNode(varNode.typeNode, data); long ownerSymTag = currentEnv.scope.owner.tag; // If this is a module tuple variable, checkTypeAndVarCountConsistency already done at symbolEnter. if ((ownerSymTag & SymTag.PACKAGE) != SymTag.PACKAGE && @@ -1733,6 +1741,7 @@ public void visit(BLangErrorVariable varNode, AnalyzerData data) { if (varNode.getBType() == null) { varNode.setBType(symResolver.resolveTypeNode(varNode.typeNode, currentEnv)); } + analyzeNode(varNode.typeNode, data); // match err1 { error(reason,....) => ... } // reason must be a const of subtype of string. 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 819367516b7f..1daf42ab31c3 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 @@ -5013,30 +5013,37 @@ private boolean isReferencingNonWorker(BLangExpression expression, AnalyzerData return true; } - public void visit(BLangTernaryExpr ternaryExpr, AnalyzerData data) { BType condExprType = checkExpr(ternaryExpr.expr, this.symTable.booleanType, data); SymbolEnv thenEnv = typeNarrower.evaluateTruth(ternaryExpr.expr, ternaryExpr.thenExpr, data.env); + BType thenActualType = silentTypeCheckExpr(ternaryExpr.thenExpr, symTable.noType, data); BType thenType = checkExpr(ternaryExpr.thenExpr, thenEnv, data.expType, data); SymbolEnv elseEnv = typeNarrower.evaluateFalsity(ternaryExpr.expr, ternaryExpr.elseExpr, data.env, false); + BType elseActualType = silentTypeCheckExpr(ternaryExpr.elseExpr, symTable.noType, data); BType elseType = checkExpr(ternaryExpr.elseExpr, elseEnv, data.expType, data); if (condExprType == symTable.semanticError || thenType == symTable.semanticError || elseType == symTable.semanticError) { data.resultType = symTable.semanticError; } else if (data.expType == symTable.noType) { - if (types.isAssignable(elseType, thenType)) { - data.resultType = thenType; - } else if (types.isAssignable(thenType, elseType)) { - data.resultType = elseType; - } else { - data.resultType = BUnionType.create(null, thenType, elseType); - } + data.resultType = getConditionalExprType(thenType, elseType); } else { data.resultType = data.expType; } + + ternaryExpr.setDeterminedType(getConditionalExprType(thenActualType, elseActualType)); + } + + private BType getConditionalExprType(BType lhsType, BType rhsType) { + if (types.isAssignable(rhsType, lhsType)) { + return lhsType; + } + if (types.isAssignable(lhsType, rhsType)) { + return rhsType; + } + return BUnionType.create(null, lhsType, rhsType); } public void visit(BLangWaitExpr waitExpr, AnalyzerData data) { @@ -5286,24 +5293,21 @@ private BType getXMLConstituents(BType bType) { public void visit(BLangElvisExpr elvisExpr, AnalyzerData data) { BType lhsType = checkExpr(elvisExpr.lhsExpr, data); - BType actualType = lhsType == symTable.semanticError ? + BType lhsActualType = lhsType == symTable.semanticError ? symTable.semanticError : validateElvisExprLhsExpr(elvisExpr, lhsType); + BType rhsActualType = silentTypeCheckExpr(elvisExpr.rhsExpr, symTable.noType, data); BType rhsReturnType = checkExpr(elvisExpr.rhsExpr, data.expType, data); - BType lhsReturnType = types.checkType(elvisExpr.lhsExpr.pos, actualType, data.expType, + BType lhsReturnType = types.checkType(elvisExpr.lhsExpr.pos, lhsActualType, data.expType, DiagnosticErrorCode.INCOMPATIBLE_TYPES); if (rhsReturnType == symTable.semanticError || lhsReturnType == symTable.semanticError) { data.resultType = symTable.semanticError; } else if (data.expType == symTable.noType) { - if (types.isAssignable(rhsReturnType, lhsReturnType)) { - data.resultType = lhsReturnType; - } else if (types.isAssignable(lhsReturnType, rhsReturnType)) { - data.resultType = rhsReturnType; - } else { - data.resultType = BUnionType.create(null, lhsReturnType, rhsReturnType); - } + data.resultType = getConditionalExprType(lhsReturnType, rhsReturnType); } else { data.resultType = data.expType; } + + elvisExpr.setDeterminedType(getConditionalExprType(lhsActualType, rhsActualType)); } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeNarrower.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeNarrower.java index a2b2ff452d9e..6f51124f5a78 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeNarrower.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeNarrower.java @@ -345,8 +345,12 @@ private NarrowedTypes getNarrowedTypesForBinaryOp(Map var nonLoggingContext = Types.IntersectionContext.typeTestIntersectionCalculationContext(); if (operator == OperatorKind.AND) { trueType = types.getTypeIntersection(nonLoggingContext, lhsTrueType, rhsTrueType, this.env); - BType tmpType = types.getTypeIntersection(nonLoggingContext, lhsTrueType, rhsFalseType, this.env); - falseType = getTypeUnion(lhsFalseType, tmpType); + BType tmpType1 = types.getTypeIntersection(nonLoggingContext, lhsTrueType, rhsFalseType, this.env); + BType tmpType2 = types.getTypeIntersection(nonLoggingContext, lhsFalseType, rhsTrueType, this.env); + if (tmpType1.tag == TypeTags.SEMANTIC_ERROR) { + tmpType1 = tmpType2; + } + falseType = getTypeUnion(lhsFalseType, tmpType1); } else { BType tmpType = types.getTypeIntersection(nonLoggingContext, lhsFalseType, rhsTrueType, this.env); trueType = lhsTypes.containsKey(symbol) ? getTypeUnion(lhsTrueType, tmpType) : diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java index e21f15a19e54..3f3639bb13ae 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java @@ -1498,6 +1498,7 @@ public boolean isInherentlyImmutableType(BType type) { case TypeTags.INVOKABLE: case TypeTags.TYPEDESC: case TypeTags.HANDLE: + case TypeTags.REGEXP: return true; case TypeTags.XML: return getImpliedType(((BXMLType) type).constraint).tag == TypeTags.NEVER; @@ -5615,10 +5616,10 @@ private boolean populateFields(IntersectionContext intersectionContext, BRecordT invokableSymbol.flags = tsymbol.flags; } else { recordFieldSymbol = new BVarSymbol(intersectionFlags, name, env.enclPkg.packageID, - intersectionFieldType, newTypeSymbol, lhsRecordField.pos, SOURCE); + intersectionFieldType, newTypeSymbol, lhsRecordField.symbol.pos, SOURCE); } - newTypeFields.put(key, new BField(name, null, recordFieldSymbol)); + newTypeFields.put(key, new BField(name, recordFieldSymbol.pos, recordFieldSymbol)); newTypeSymbol.scope.define(name, recordFieldSymbol); } return true; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTestablePackage.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTestablePackage.java index 412743a7b36c..87c9d4a9c3fa 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTestablePackage.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTestablePackage.java @@ -29,6 +29,8 @@ public class BLangTestablePackage extends BLangPackage { // Semantic Data //Map to maintain all the mock functions private Map mockFunctionNamesMap = new HashMap<>(); + + private final Map isLegacyMockingMap = new HashMap<>(); public Map getMockFunctionNamesMap() { return mockFunctionNamesMap; } @@ -46,4 +48,12 @@ public void accept(BLangNodeAnalyzer analyzer, T props) { public R apply(BLangNodeTransformer modifier, T props) { return modifier.transform(this, props); } + + public Map getIsLegacyMockingMap() { + return isLegacyMockingMap; + } + + public void addIsLegacyMockingMap(String id, Boolean isLegacy) { + this.isLegacyMockingMap.put(id, isLegacy); + } } diff --git a/compiler/ballerina-lang/src/test/java/io/ballerina/projects/BallerinaTomlTests.java b/compiler/ballerina-lang/src/test/java/io/ballerina/projects/BallerinaTomlTests.java index 46c017cee78b..cfdcbd7d1064 100644 --- a/compiler/ballerina-lang/src/test/java/io/ballerina/projects/BallerinaTomlTests.java +++ b/compiler/ballerina-lang/src/test/java/io/ballerina/projects/BallerinaTomlTests.java @@ -145,10 +145,10 @@ public void testBallerinaTomlWithTool() throws IOException { Assert.assertFalse(packageManifest.diagnostics().hasErrors()); PackageManifest.Tool tool = packageManifest.tools().get(0); - Assert.assertEquals(tool.getType(), "openapi"); - Assert.assertEquals(tool.getId(), "generate-delivery-client"); - Assert.assertEquals(tool.getFilePath(), "delivery.json"); - Assert.assertEquals(tool.getTargetModule(), "delivery"); + Assert.assertEquals(tool.type(), "openapi"); + Assert.assertEquals(tool.id(), "generate-delivery-client"); + Assert.assertEquals(tool.filePath(), "delivery.json"); + Assert.assertEquals(tool.targetModule(), "delivery"); } // Negative tests diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/diagnostics/DiagnosticErrorCode.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/diagnostics/DiagnosticErrorCode.java index c4ccdc215f19..b19909575930 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/diagnostics/DiagnosticErrorCode.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/diagnostics/DiagnosticErrorCode.java @@ -359,7 +359,8 @@ public enum DiagnosticErrorCode implements DiagnosticCode { ERROR_ANNOTATIONS_NOT_ALLOWED_FOR_TUPLE_REST_DESCRIPTOR("BCE0684", "error.annotations.not.allowed.for.tuple.rest.descriptor"), ERROR_INVALID_RE_SYNTAX_CHAR("BCE0685", "error.invalid.syntax.char"), - ERROR_MORE_CLAUSES_AFTER_COLLECT_CLAUSE("BCE0686", "error.more.clauses.after.collect.clause") + ERROR_MORE_CLAUSES_AFTER_COLLECT_CLAUSE("BCE0686", "error.more.clauses.after.collect.clause"), + ERROR_COLLECT_CLAUSE_IN_QUERY_ACTION("BCE0687", "error.collect.clause.in.query.action") ; String diagnosticId; diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/BallerinaParser.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/BallerinaParser.java index 65affc31a52a..fe2e35187276 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/BallerinaParser.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/BallerinaParser.java @@ -11949,10 +11949,11 @@ private STNode parseQueryExprRhs(STNode queryConstructType, boolean isRhsExpr, b } } - if (peek().kind == SyntaxKind.DO_KEYWORD && (!isNestedQueryExpr() || selectClause == null)) { + if (peek().kind == SyntaxKind.DO_KEYWORD && + (!isNestedQueryExpr() || (selectClause == null && collectClause == null))) { STNode intermediateClauses = STNodeFactory.createNodeList(clauses); STNode queryPipeline = STNodeFactory.createQueryPipelineNode(fromClause, intermediateClauses); - return parseQueryAction(queryConstructType, queryPipeline, selectClause); + return parseQueryAction(queryConstructType, queryPipeline, selectClause, collectClause); } if (selectClause == null && collectClause == null) { @@ -13553,7 +13554,8 @@ private STNode parseFieldAccessIdentifier(boolean isInConditionalExpr) { * @param selectClause Select clause if any This is only for validation. * @return Query action node */ - private STNode parseQueryAction(STNode queryConstructType, STNode queryPipeline, STNode selectClause) { + private STNode parseQueryAction(STNode queryConstructType, STNode queryPipeline, STNode selectClause, + STNode collectClause) { if (queryConstructType != null) { queryPipeline = SyntaxErrors.cloneWithLeadingInvalidNodeMinutiae(queryPipeline, queryConstructType, DiagnosticErrorCode.ERROR_QUERY_CONSTRUCT_TYPE_IN_QUERY_ACTION); @@ -13562,6 +13564,10 @@ private STNode parseQueryAction(STNode queryConstructType, STNode queryPipeline, queryPipeline = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(queryPipeline, selectClause, DiagnosticErrorCode.ERROR_SELECT_CLAUSE_IN_QUERY_ACTION); } + if (collectClause != null) { + queryPipeline = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(queryPipeline, collectClause, + DiagnosticErrorCode.ERROR_COLLECT_CLAUSE_IN_QUERY_ACTION); + } startContext(ParserRuleContext.DO_CLAUSE); STNode doKeyword = parseDoKeyword(); diff --git a/compiler/ballerina-parser/src/main/resources/syntax_diagnostic_message.properties b/compiler/ballerina-parser/src/main/resources/syntax_diagnostic_message.properties index 45c38e5854ab..8845c0b8f47c 100644 --- a/compiler/ballerina-parser/src/main/resources/syntax_diagnostic_message.properties +++ b/compiler/ballerina-parser/src/main/resources/syntax_diagnostic_message.properties @@ -275,6 +275,7 @@ error.invalid.token=invalid token ''{0}'' error.invalid.expression.statement=invalid expression statement error.invalid.array.length=invalid array length: array length should be a non-negative integer error.select.clause.in.query.action=select clause in query action +error.collect.clause.in.query.action=collect clause in query action error.more.clauses.after.select.clause=more clauses after select clause error.more.clauses.after.collect.clause=more clauses after collect clause error.query.construct.type.in.query.action= query construct type in query action diff --git a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/syntax/actions/QueryActionTest.java b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/syntax/actions/QueryActionTest.java index 46ec91b6256c..1f57345ad634 100644 --- a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/syntax/actions/QueryActionTest.java +++ b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/syntax/actions/QueryActionTest.java @@ -48,6 +48,16 @@ public void testQueryActionWithLimit() { testFile("query-action/query_action_source_05.bal", "query-action/query_action_assert_05.json"); } + @Test + public void testQueryActionWithGroupBy() { + testFile("query-action/query_action_source_07.bal", "query-action/query_action_assert_07.json"); + } + + @Test + public void testQueryActionWithCollect() { + testFile("query-action/query_action_source_08.bal", "query-action/query_action_assert_08.json"); + } + // Recovery tests @Test diff --git a/compiler/ballerina-parser/src/test/resources/actions/query-action/query_action_assert_07.json b/compiler/ballerina-parser/src/test/resources/actions/query-action/query_action_assert_07.json new file mode 100644 index 000000000000..eb57fce67243 --- /dev/null +++ b/compiler/ballerina-parser/src/test/resources/actions/query-action/query_action_assert_07.json @@ -0,0 +1,1408 @@ +{ + "kind": "FUNCTION_DEFINITION", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "foo" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_SIGNATURE", + "children": [ + { + "kind": "OPEN_PAREN_TOKEN" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_PAREN_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "FUNCTION_BODY_BLOCK", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "VAR_TYPE_DESC", + "children": [ + { + "kind": "VAR_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "orders", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACKET_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "MAPPING_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "orderId" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "1" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "itemName" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "STRING_LITERAL", + "children": [ + { + "kind": "STRING_LITERAL_TOKEN", + "value": "A" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "price" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_FLOATING_POINT_LITERAL_TOKEN", + "value": "23.4" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantity" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "2" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN" + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "MAPPING_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "orderId" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "1" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "itemName" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "STRING_LITERAL", + "children": [ + { + "kind": "STRING_LITERAL_TOKEN", + "value": "A" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "price" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_FLOATING_POINT_LITERAL_TOKEN", + "value": "20.4" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantity" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "1" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN" + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "MAPPING_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "orderId" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "2" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "itemName" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "STRING_LITERAL", + "children": [ + { + "kind": "STRING_LITERAL_TOKEN", + "value": "B" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "price" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_FLOATING_POINT_LITERAL_TOKEN", + "value": "21.5" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantity" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "3" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN" + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "MAPPING_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "orderId" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "1" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "itemName" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "STRING_LITERAL", + "children": [ + { + "kind": "STRING_LITERAL_TOKEN", + "value": "B" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "price" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_FLOATING_POINT_LITERAL_TOKEN", + "value": "21.5" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantity" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "3" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACKET_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + }, + { + "kind": "ACTION_STATEMENT", + "children": [ + { + "kind": "QUERY_ACTION", + "children": [ + { + "kind": "QUERY_PIPELINE", + "children": [ + { + "kind": "FROM_CLAUSE", + "children": [ + { + "kind": "FROM_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "VAR_TYPE_DESC", + "children": [ + { + "kind": "VAR_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "i", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "IN_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACKET_TOKEN" + }, + { + "kind": "LIST", + "children": [ + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "1" + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "2" + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "3" + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "4" + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACKET_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "LIST", + "children": [] + } + ] + }, + { + "kind": "DO_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "VAR_TYPE_DESC", + "children": [ + { + "kind": "VAR_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantities", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "QUERY_EXPRESSION", + "children": [ + { + "kind": "QUERY_PIPELINE", + "children": [ + { + "kind": "FROM_CLAUSE", + "children": [ + { + "kind": "FROM_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "VAR_TYPE_DESC", + "children": [ + { + "kind": "VAR_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "MAPPING_BINDING_PATTERN", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN" + }, + { + "kind": "LIST", + "children": [ + { + "kind": "FIELD_BINDING_PATTERN", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "itemName" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "FIELD_BINDING_PATTERN", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantity" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "IN_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "orders", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "GROUP_BY_CLAUSE", + "children": [ + { + "kind": "GROUP_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BY_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "itemName", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "SELECT_CLAUSE", + "children": [ + { + "kind": "SELECT_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "MAPPING_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN" + }, + { + "kind": "LIST", + "children": [ + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "itemName" + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantity" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "FUNCTION_CALL", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "sum" + } + ] + }, + { + "kind": "OPEN_PAREN_TOKEN" + }, + { + "kind": "LIST", + "children": [ + { + "kind": "POSITIONAL_ARG", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantity" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_PAREN_TOKEN" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN" + } + ] + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/compiler/ballerina-parser/src/test/resources/actions/query-action/query_action_assert_08.json b/compiler/ballerina-parser/src/test/resources/actions/query-action/query_action_assert_08.json new file mode 100644 index 000000000000..e08c46727cca --- /dev/null +++ b/compiler/ballerina-parser/src/test/resources/actions/query-action/query_action_assert_08.json @@ -0,0 +1,1428 @@ +{ + "kind": "FUNCTION_DEFINITION", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "foo" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_SIGNATURE", + "children": [ + { + "kind": "OPEN_PAREN_TOKEN" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_PAREN_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "FUNCTION_BODY_BLOCK", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "VAR_TYPE_DESC", + "children": [ + { + "kind": "VAR_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "orders", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACKET_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "MAPPING_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "orderId" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "1" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "itemName" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "STRING_LITERAL", + "children": [ + { + "kind": "STRING_LITERAL_TOKEN", + "value": "A" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "price" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_FLOATING_POINT_LITERAL_TOKEN", + "value": "23.4" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantity" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "2" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN" + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "MAPPING_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "orderId" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "1" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "itemName" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "STRING_LITERAL", + "children": [ + { + "kind": "STRING_LITERAL_TOKEN", + "value": "A" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "price" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_FLOATING_POINT_LITERAL_TOKEN", + "value": "20.4" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantity" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "1" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN" + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "MAPPING_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "orderId" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "2" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "itemName" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "STRING_LITERAL", + "children": [ + { + "kind": "STRING_LITERAL_TOKEN", + "value": "B" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "price" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_FLOATING_POINT_LITERAL_TOKEN", + "value": "21.5" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantity" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "3" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN" + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "MAPPING_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "orderId" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "1" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "itemName" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "STRING_LITERAL", + "children": [ + { + "kind": "STRING_LITERAL_TOKEN", + "value": "B" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "price" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_FLOATING_POINT_LITERAL_TOKEN", + "value": "21.5" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SPECIFIC_FIELD", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantity" + }, + { + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "3" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACKET_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + }, + { + "kind": "ACTION_STATEMENT", + "children": [ + { + "kind": "QUERY_ACTION", + "children": [ + { + "kind": "QUERY_PIPELINE", + "children": [ + { + "kind": "FROM_CLAUSE", + "children": [ + { + "kind": "FROM_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "VAR_TYPE_DESC", + "children": [ + { + "kind": "VAR_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "i", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "IN_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACKET_TOKEN" + }, + { + "kind": "LIST", + "children": [ + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "1" + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "2" + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "3" + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "4" + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACKET_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "LIST", + "children": [] + } + ] + }, + { + "kind": "DO_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "VAR_TYPE_DESC", + "children": [ + { + "kind": "VAR_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "income", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "QUERY_EXPRESSION", + "children": [ + { + "kind": "QUERY_PIPELINE", + "children": [ + { + "kind": "FROM_CLAUSE", + "children": [ + { + "kind": "FROM_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "VAR_TYPE_DESC", + "children": [ + { + "kind": "VAR_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "MAPPING_BINDING_PATTERN", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN" + }, + { + "kind": "LIST", + "children": [ + { + "kind": "FIELD_BINDING_PATTERN", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "price" + } + ] + } + ] + }, + { + "kind": "COMMA_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "FIELD_BINDING_PATTERN", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantity" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "IN_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "orders", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LET_CLAUSE", + "children": [ + { + "kind": "LET_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LET_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "VAR_TYPE_DESC", + "children": [ + { + "kind": "VAR_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "totPrice", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BINARY_EXPRESSION", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "price", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "ASTERISK_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "quantity", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "COLLECT_CLAUSE", + "children": [ + { + "kind": "COLLECT_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "FUNCTION_CALL", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "sum" + } + ] + }, + { + "kind": "OPEN_PAREN_TOKEN" + }, + { + "kind": "LIST", + "children": [ + { + "kind": "POSITIONAL_ARG", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "totPrice" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_PAREN_TOKEN" + } + ] + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/compiler/ballerina-parser/src/test/resources/actions/query-action/query_action_source_07.bal b/compiler/ballerina-parser/src/test/resources/actions/query-action/query_action_source_07.bal new file mode 100644 index 000000000000..7d0e371ad030 --- /dev/null +++ b/compiler/ballerina-parser/src/test/resources/actions/query-action/query_action_source_07.bal @@ -0,0 +1,14 @@ +function foo() { + var orders = [ + {orderId: 1, itemName: "A", price: 23.4, quantity: 2}, + {orderId: 1, itemName: "A", price: 20.4, quantity: 1}, + {orderId: 2, itemName: "B", price: 21.5, quantity: 3}, + {orderId: 1, itemName: "B", price: 21.5, quantity: 3} + ]; + from var i in [1, 2, 3, 4] + do { + var quantities = from var {itemName, quantity} in orders + group by itemName + select {itemName, quantity: sum(quantity)}; + }; +} diff --git a/compiler/ballerina-parser/src/test/resources/actions/query-action/query_action_source_08.bal b/compiler/ballerina-parser/src/test/resources/actions/query-action/query_action_source_08.bal new file mode 100644 index 000000000000..82face7c525d --- /dev/null +++ b/compiler/ballerina-parser/src/test/resources/actions/query-action/query_action_source_08.bal @@ -0,0 +1,14 @@ +function foo() { + var orders = [ + {orderId: 1, itemName: "A", price: 23.4, quantity: 2}, + {orderId: 1, itemName: "A", price: 20.4, quantity: 1}, + {orderId: 2, itemName: "B", price: 21.5, quantity: 3}, + {orderId: 1, itemName: "B", price: 21.5, quantity: 3} + ]; + from var i in [1, 2, 3, 4] + do { + var income = from var {price, quantity} in orders + let var totPrice = price * quantity + collect sum(totPrice); + }; +} diff --git a/compiler/ballerina-parser/src/test/resources/test_parser.bal b/compiler/ballerina-parser/src/test/resources/test_parser.bal index 6b71ef415c69..7d0e371ad030 100644 --- a/compiler/ballerina-parser/src/test/resources/test_parser.bal +++ b/compiler/ballerina-parser/src/test/resources/test_parser.bal @@ -1,2 +1,14 @@ -public function main() { +function foo() { + var orders = [ + {orderId: 1, itemName: "A", price: 23.4, quantity: 2}, + {orderId: 1, itemName: "A", price: 20.4, quantity: 1}, + {orderId: 2, itemName: "B", price: 21.5, quantity: 3}, + {orderId: 1, itemName: "B", price: 21.5, quantity: 3} + ]; + from var i in [1, 2, 3, 4] + do { + var quantities = from var {itemName, quantity} in orders + group by itemName + select {itemName, quantity: sum(quantity)}; + }; } diff --git a/langlib/lang.annotations/src/main/ballerina/annotations.bal b/langlib/lang.annotations/src/main/ballerina/annotations.bal index d697461e45e3..68219be653ff 100644 --- a/langlib/lang.annotations/src/main/ballerina/annotations.bal +++ b/langlib/lang.annotations/src/main/ballerina/annotations.bal @@ -103,4 +103,4 @@ public const annotation record { "text"|"password"|"file" kind?; } display on source type, source class, source function, source return, source parameter, source field, source listener, - source var, source const, source annotation, source service; + source var, source const, source annotation, source service, source external, source worker; diff --git a/langlib/lang.array/src/main/java/org/ballerinalang/langlib/array/utils/ArrayUtils.java b/langlib/lang.array/src/main/java/org/ballerinalang/langlib/array/utils/ArrayUtils.java index 6f5c627b64b0..1f30f1cd2706 100644 --- a/langlib/lang.array/src/main/java/org/ballerinalang/langlib/array/utils/ArrayUtils.java +++ b/langlib/lang.array/src/main/java/org/ballerinalang/langlib/array/utils/ArrayUtils.java @@ -18,6 +18,7 @@ package org.ballerinalang.langlib.array.utils; +import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.TypeTags; import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.creators.TypeCreator; @@ -33,7 +34,9 @@ import io.ballerina.runtime.internal.errors.ErrorHelper; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import static io.ballerina.runtime.api.constants.RuntimeConstants.ARRAY_LANG_LIB; import static io.ballerina.runtime.internal.errors.ErrorReasons.OPERATION_NOT_SUPPORTED_IDENTIFIER; @@ -101,30 +104,23 @@ public static BError createOpNotSupportedError(Type type, String op) { public static BArray createEmptyArrayFromTuple(BArray arr) { Type arrType = TypeUtils.getImpliedType(arr.getType()); TupleType tupleType = (TupleType) arrType; - List memTypes = new ArrayList<>(); List tupleTypes = tupleType.getTupleTypes(); - boolean isSameType = true; - Type sameType = null; - if (!tupleTypes.isEmpty()) { - sameType = tupleTypes.get(0); - memTypes.add(sameType); - } - for (int i = 1; i < tupleTypes.size(); i++) { - isSameType &= sameType == tupleTypes.get(i); - memTypes.add(tupleTypes.get(i)); - } Type restType = tupleType.getRestType(); - // If there's a tuple-rest-descriptor the array will not be of the same type even if other types are the same + Set uniqueTypes = new HashSet<>(tupleTypes); if (restType != null) { - isSameType = false; - memTypes.add(restType); + uniqueTypes.add(restType); } - // Create an array of one type if the member-type-descriptors are the same - if (isSameType) { - ArrayType type = TypeCreator.createArrayType(sameType); - return ValueCreator.createArrayValue(type); + if (uniqueTypes.isEmpty()) { + // Return an array with never type + return ValueCreator.createArrayValue(TypeCreator.createArrayType(PredefinedTypes.TYPE_NEVER)); + } else if (uniqueTypes.size() == 1) { + // Return an array with the member type + Type type = uniqueTypes.iterator().next(); + ArrayType arrayType = TypeCreator.createArrayType(type); + return ValueCreator.createArrayValue(arrayType); } - UnionType unionType = TypeCreator.createUnionType(memTypes); + // Return an array with the union of member types + UnionType unionType = TypeCreator.createUnionType(new ArrayList<>(uniqueTypes)); ArrayType slicedArrType = TypeCreator.createArrayType(unionType); return ValueCreator.createArrayValue(slicedArrType); } diff --git a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibArrayTest.java b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibArrayTest.java index f07bd8627f67..8b7a128b0f10 100644 --- a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibArrayTest.java +++ b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibArrayTest.java @@ -602,7 +602,9 @@ public Object[] testFunctions() { "testArrSortWithNamedArgs2", "testArrSortWithNamedArgs3", "testArrayPop", - "testSetLengthNegative" + "testSetLengthNegative", + "testArrayFilterWithEmptyArrayAndTypeBinding", + "testArrayReverseWithEmptyArrayAndTypeBinding" }; } } diff --git a/langlib/langlib-test/src/test/resources/test-src/arraylib_test.bal b/langlib/langlib-test/src/test/resources/test-src/arraylib_test.bal index e1810ae806b5..00e2d51077f1 100644 --- a/langlib/langlib-test/src/test/resources/test-src/arraylib_test.bal +++ b/langlib/langlib-test/src/test/resources/test-src/arraylib_test.bal @@ -1832,3 +1832,19 @@ function testSetLengthNegative() { checkpanic result.detail()["message"]); } } + +function testArrayFilterWithEmptyArrayAndTypeBinding() { + var x = []; + anydata[] y = x; + anydata[] z = y.filter(v => v is int); + assertValueEquality(z, []); + assertTrue(z is never[]); +} + +function testArrayReverseWithEmptyArrayAndTypeBinding() { + var x = []; + anydata[] y = x; + anydata[] z = y.reverse(); + assertValueEquality(z, []); + assertTrue(z is never[]); +} diff --git a/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal b/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal index 5be34862ca6a..541815e4a6a2 100644 --- a/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal +++ b/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal @@ -2432,6 +2432,89 @@ function testCloneWithTypeWithAmbiguousUnion() { checkpanic err.detail()["message"]); } +public type CodingExtension record {| + *Element; + + uri url; + Coding valueCoding; +|}; + +public type Coding record {| + *Element; + + string id?; + Extension[] extension?; + uri system?; + string 'version?; + code code?; + string display?; + boolean userSelected?; +|}; + +public type code string; + +public type uri string; + +public type ElementReadOnlyCyclic record {| + string id?; + Extension[] extension?; + ((ElementReadOnlyCyclic & readonly)|())...; +|}; + +public type Element record {| + string id?; + Extension[] extension?; + Element...; +|}; + +public type ExtensionExtension record {| + *Element; + + uri url; + Extension[] extension?; +|}; + +public type CodeableConceptExtension record {| + *Element; + + uri url; + CodeableConcept valueCodeableConcept; +|}; + +public type CodeableConcept record {| + *Element; + + string id?; + Extension[] extension?; + Coding[] coding?; + string text?; +|}; + +public type ExtensionExtension2 record {| + *ElementReadOnlyCyclic; + + uri url; + Extension[] extension?; +|}; + +public type CodeableConceptExtension2 record {| + *ElementReadOnlyCyclic; + + uri url; + CodeableConcept valueCodeableConcept; +|}; + +public type CodingExtension2 record {| + *ElementReadOnlyCyclic; + + uri url; + Coding valueCoding; +|}; + +public type Extension CodeableConceptExtension|ExtensionExtension|CodingExtension; + +public type Extension2 CodeableConceptExtension2|ExtensionExtension2|CodingExtension2; + function testCloneWithTypeToUnion() { int|float|[string, string] unionVar = 2; float|decimal|[string, int]|error tupleValue = unionVar.cloneWithType(UnionTypedesc); @@ -2440,6 +2523,31 @@ function testCloneWithTypeToUnion() { assertFalse(tupleValue is decimal); assertFalse(tupleValue is [string, int]); assertFalse(tupleValue is error); + + json extCoding = { + "valueCoding": { + "system": "http://loinc.org", + "code": "LA29518-0", + "display": "he/him/his/himself" + }, + "url": "http://open.epic.com/FHIR/StructureDefinition/extension/calculated-pronouns-to-use-for-text" + }; + + Extension ext = checkpanic extCoding.cloneWithType(); + assertEquality(ext, { + "url": + "http://open.epic.com/FHIR/StructureDefinition/extension/calculated-pronouns-to-use-for-text", + "valueCoding": {"system": "http://loinc.org", "code": "LA29518-0", "display": "he/him/his/himself"} + }); + assertEquality((typeof ext).toString(), "typedesc CodingExtension"); + + Extension2 ext2 = checkpanic extCoding.cloneWithType(); + assertEquality(ext2, { + "url": + "http://open.epic.com/FHIR/StructureDefinition/extension/calculated-pronouns-to-use-for-text", + "valueCoding": {"system": "http://loinc.org", "code": "LA29518-0", "display": "he/him/his/himself"} + }); + assertEquality((typeof ext2).toString(), "typedesc CodingExtension2"); } type UnionTypedesc typedesc; diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/ExtractToConstantCodeAction.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/ExtractToConstantCodeAction.java index deda1261b1d4..e0b8080d23f7 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/ExtractToConstantCodeAction.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/ExtractToConstantCodeAction.java @@ -117,7 +117,7 @@ public List getCodeActions(CodeActionContext context, CodeAction codeAction = CodeActionUtil.createCodeAction(CommandConstants.EXTRACT_TO_CONSTANT, textEdits, context.fileUri(), CodeActionKind.RefactorExtract); CodeActionUtil.addRenamePopup(context, codeAction, CommandConstants.RENAME_COMMAND_TITLE_FOR_CONSTANT, - getRenamePosition(textEdits.get(1).getRange().getStart(), addNewLineAtStart)); + getRenamePosition(textEdits.get(1).getRange(), addNewLineAtStart)); return Collections.singletonList(codeAction); } @@ -127,7 +127,7 @@ public List getCodeActions(CodeActionContext context, CodeAction codeAction = CodeActionUtil.createCodeAction(CommandConstants.EXTRACT_TO_CONSTANT, textEdits, context.fileUri(), CodeActionKind.RefactorExtract); CodeActionUtil.addRenamePopup(context, codeAction, CommandConstants.RENAME_COMMAND_TITLE_FOR_CONSTANT, - getRenamePosition(textEdits.get(1).getRange().getStart(), addNewLineAtStart)); + getRenamePosition(textEdits.get(1).getRange(), addNewLineAtStart)); return Collections.singletonList(codeAction); } @@ -141,7 +141,7 @@ public List getCodeActions(CodeActionContext context, LinkedHashMap renamePositionMap = new LinkedHashMap<>(); nodeList.forEach(extractableNode -> renamePositionMap.put(extractableNode.toSourceCode().strip(), - getRenamePosition(PositionUtil.toRange(extractableNode.lineRange()).getStart(), + getRenamePosition(PositionUtil.toRange(extractableNode.lineRange()), addNewLineAtStart))); return Collections.singletonList( CodeActionUtil.createCodeAction(CommandConstants.EXTRACT_TO_CONSTANT, @@ -171,13 +171,13 @@ private boolean isExpressionNode(Node node) { return node instanceof StatementNode || node instanceof ModuleMemberDeclarationNode; } - private Position getRenamePosition(Position replacePosition, boolean addNewLineAtStart) { + private Position getRenamePosition(Range range, boolean addNewLineAtStart) { // line position will increment by one due to const declaration statement - int line = replacePosition.getLine() + 1; + int line = range.getEnd().getLine() + 1; if (addNewLineAtStart) { line += 1; } - return new Position(line, replacePosition.getCharacter()); + return new Position(line, range.getStart().getCharacter()); } @Override diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/ExtractToLocalVarCodeAction.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/ExtractToLocalVarCodeAction.java index 6620d250667d..732500a47293 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/ExtractToLocalVarCodeAction.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/ExtractToLocalVarCodeAction.java @@ -139,7 +139,7 @@ public List getCodeActions(CodeActionContext context, CodeAction codeAction = CodeActionUtil.createCodeAction(CommandConstants.EXTRACT_TO_VARIABLE, textEdits, context.fileUri(), CodeActionKind.RefactorExtract); CodeActionUtil.addRenamePopup(context, codeAction, CommandConstants.RENAME_COMMAND_TITLE_FOR_VARIABLE, - getRenamePosition(textEdits.get(1).getRange().getStart())); + getRenamePosition(textEdits.get(1).getRange())); return Collections.singletonList(codeAction); } @@ -165,7 +165,7 @@ public List getCodeActions(CodeActionContext context, String key = extractableNode.toSourceCode().strip(); textEditMap.put(key, getTextEdits(context, tSymbol.get(), extractableNode, statementNode)); - renamePositionMap.put(key, getRenamePosition(PositionUtil.toRange(extractableNode.lineRange()).getStart())); + renamePositionMap.put(key, getRenamePosition(PositionUtil.toRange(extractableNode.lineRange()))); }); if (lsClientCapabilities.getInitializationOptions().isPositionalRefactorRenameSupported()) { @@ -198,8 +198,8 @@ private List getTextEdits(CodeActionContext context, TypeSymbol typeSy return List.of(varDeclEdit, replaceEdit); } - private Position getRenamePosition(Position position) { - return new Position(position.getLine() + 1, position.getCharacter()); + private Position getRenamePosition(Range range) { + return new Position(range.getEnd().getLine() + 1, range.getStart().getCharacter()); } private static List getPossibleExpressionNodes(Node node) { diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/changetype/FixReturnTypeCodeAction.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/changetype/FixReturnTypeCodeAction.java index 37cf178ef4c3..c687ae6e2c3f 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/changetype/FixReturnTypeCodeAction.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/changetype/FixReturnTypeCodeAction.java @@ -69,14 +69,37 @@ public boolean validate(Diagnostic diagnostic, DiagBasedPositionDetails position return false; } - //Suggest the code action only if the immediate parent of the matched node is either of return statement, - //check expression, check action. + // Suggest code action if the node is aligned with a check keyword. NonTerminalNode matchedNode = positionDetails.matchedNode(); - if (matchedNode.parent() != null && matchedNode.parent().kind() != SyntaxKind.RETURN_STATEMENT && - matchedNode.kind() != SyntaxKind.CHECK_EXPRESSION && - matchedNode.kind() != SyntaxKind.CHECK_ACTION) { + if (matchedNode.kind() == SyntaxKind.CHECK_ACTION || matchedNode.kind() == SyntaxKind.CHECK_EXPRESSION) { + return CodeActionNodeValidator.validate(context.nodeAtRange()); + } + + // Suggest code action if the node is an expression inside a return statement. + NonTerminalNode parentNode = matchedNode.parent(); + if (parentNode == null) { + return false; + } + if (parentNode.kind() == SyntaxKind.RETURN_KEYWORD) { + return CodeActionNodeValidator.validate(context.nodeAtRange()); + } + + // Suggest code action if the node is an expression nested into a return statement. + NonTerminalNode grandParentNode = parentNode.parent(); + if (grandParentNode == null) { return false; } + if (grandParentNode.kind() == SyntaxKind.RETURN_STATEMENT) { + return CodeActionNodeValidator.validate(context.nodeAtRange()); + } + + // Suggest code action if the node is an expression inside a collect clause. + if (matchedNode.kind() == SyntaxKind.COLLECT_CLAUSE) { + NonTerminalNode ancestorNode = grandParentNode.parent(); + if (ancestorNode == null || ancestorNode.kind() != SyntaxKind.RETURN_STATEMENT) { + return false; + } + } return CodeActionNodeValidator.validate(context.nodeAtRange()); } diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/imports/ImportModuleCodeAction.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/imports/ImportModuleCodeAction.java index fa10cf60b3c4..9d0392dafa5f 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/imports/ImportModuleCodeAction.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/codeaction/providers/imports/ImportModuleCodeAction.java @@ -36,12 +36,14 @@ import org.ballerinalang.langserver.commons.codeaction.spi.DiagBasedPositionDetails; import org.ballerinalang.langserver.commons.codeaction.spi.DiagnosticBasedCodeActionProvider; import org.ballerinalang.langserver.completions.util.ItemResolverConstants; +import org.ballerinalang.model.Name; import org.ballerinalang.util.diagnostic.DiagnosticErrorCode; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionKind; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextEdit; +import org.wso2.ballerinalang.compiler.tree.BLangIdentifier; import java.util.ArrayList; import java.util.Collections; @@ -79,7 +81,7 @@ public List getCodeActions(Diagnostic diagnostic, // Find the qualified name reference node within the diagnostic location Range diagRange = PositionUtil.toRange(diagnostic.location().lineRange()); NonTerminalNode node = CommonUtil.findNode(diagRange, context.currentSyntaxTree().get()); - QNameRefFinder finder = new QNameRefFinder(); + QNameRefFinder finder = new QNameRefFinder(diagnostic.properties().get(0).value()); node.accept(finder); Optional qNameReferenceNode = finder.getQNameReferenceNode(); if (qNameReferenceNode.isEmpty()) { @@ -169,12 +171,25 @@ public String getName() { * A visitor to find the qualified name reference node within an expression. */ static class QNameRefFinder extends NodeVisitor { + private final String moduleName; + + public QNameRefFinder(Object nameObj) { + if (nameObj instanceof Name name) { + this.moduleName = name.getValue(); + } else if (nameObj instanceof BLangIdentifier identifier) { + this.moduleName = identifier.getValue(); + } else { + this.moduleName = nameObj.toString(); + } + } private QualifiedNameReferenceNode qualifiedNameReferenceNode; @Override public void visit(QualifiedNameReferenceNode qualifiedNameReferenceNode) { - this.qualifiedNameReferenceNode = qualifiedNameReferenceNode; + if (qualifiedNameReferenceNode.modulePrefix().text().equals(moduleName)) { + this.qualifiedNameReferenceNode = qualifiedNameReferenceNode; + } } Optional getQNameReferenceNode() { diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/visitors/FunctionCallExpressionTypeFinder.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/visitors/FunctionCallExpressionTypeFinder.java index f846bba1b8f2..18d52939201d 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/visitors/FunctionCallExpressionTypeFinder.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/visitors/FunctionCallExpressionTypeFinder.java @@ -36,12 +36,14 @@ import io.ballerina.compiler.api.symbols.TypeSymbol; import io.ballerina.compiler.api.symbols.UnionTypeSymbol; import io.ballerina.compiler.api.symbols.VariableSymbol; +import io.ballerina.compiler.api.symbols.WorkerSymbol; import io.ballerina.compiler.syntax.tree.AssignmentStatementNode; import io.ballerina.compiler.syntax.tree.BinaryExpressionNode; import io.ballerina.compiler.syntax.tree.BlockStatementNode; import io.ballerina.compiler.syntax.tree.CheckExpressionNode; import io.ballerina.compiler.syntax.tree.ConditionalExpressionNode; import io.ballerina.compiler.syntax.tree.ErrorConstructorExpressionNode; +import io.ballerina.compiler.syntax.tree.ExplicitAnonymousFunctionExpressionNode; import io.ballerina.compiler.syntax.tree.ExplicitNewExpressionNode; import io.ballerina.compiler.syntax.tree.FailStatementNode; import io.ballerina.compiler.syntax.tree.FunctionArgumentNode; @@ -57,6 +59,7 @@ import io.ballerina.compiler.syntax.tree.MethodCallExpressionNode; import io.ballerina.compiler.syntax.tree.ModuleVariableDeclarationNode; import io.ballerina.compiler.syntax.tree.NamedArgumentNode; +import io.ballerina.compiler.syntax.tree.NamedWorkerDeclarationNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NodeVisitor; import io.ballerina.compiler.syntax.tree.ObjectFieldNode; @@ -402,6 +405,12 @@ public void visit(BlockStatementNode node) { node.parent().accept(this); } + @Override + public void visit(NamedWorkerDeclarationNode namedWorkerDeclarationNode) { + semanticModel.symbol(namedWorkerDeclarationNode) + .ifPresent(value -> checkAndSetTypeResult(((WorkerSymbol) value).returnType())); + } + @Override public void visit(ReturnStatementNode returnStatementNode) { this.semanticModel.typeOf(returnStatementNode).ifPresent(this::checkAndSetTypeResult); @@ -411,11 +420,14 @@ public void visit(ReturnStatementNode returnStatementNode) { // Get function type symbol and get return type descriptor from it returnStatementNode.parent().accept(this); - if (resultFound && returnTypeSymbol.typeKind() == TypeDescKind.FUNCTION) { + + if (!resultFound) { + resetResult(); + return; + } + if (returnTypeSymbol.typeKind() == TypeDescKind.FUNCTION) { FunctionTypeSymbol functionTypeSymbol = (FunctionTypeSymbol) returnTypeSymbol; functionTypeSymbol.returnTypeDescriptor().ifPresentOrElse(this::checkAndSetTypeResult, this::resetResult); - } else { - resetResult(); } } @@ -512,6 +524,11 @@ public void visit(ImplicitAnonymousFunctionExpressionNode expr) { } } + @Override + public void visit(ExplicitAnonymousFunctionExpressionNode explicitAnonymousFunctionExpressionNode) { + semanticModel.typeOf(explicitAnonymousFunctionExpressionNode).ifPresent(this::checkAndSetTypeResult); + } + @Override public void visit(PanicStatementNode panicStatementNode) { checkAndSetTypeResult(semanticModel.types().ERROR); diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/BallerinaPackageService.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/BallerinaPackageService.java index 3a00f737896a..aaa2a4211f50 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/BallerinaPackageService.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/BallerinaPackageService.java @@ -98,15 +98,21 @@ public CompletableFuture components(PackageComponents try { Arrays.stream(documentIdentifiers).iterator().forEachRemaining(documentIdentifier -> { PathUtil.getPathFromURI(documentIdentifier.getUri()).ifPresent(path -> { - Optional project = this.workspaceManager.project(path); - project.ifPresent(value -> jsonPackages.add(getPackageComponents(value))); + Project project = null; + try { + project = this.workspaceManager.loadProject(path); + jsonPackages.add(getPackageComponents(project)); + } catch (Throwable e) { + String msg = "Operation 'ballerinaPackage/components' load project failed!"; + this.clientLogger.logError(PackageContext.PACKAGE_COMPONENTS, msg, e, null); + } }); }); - response.setProjectPackages(jsonPackages); } catch (Throwable e) { String msg = "Operation 'ballerinaPackage/components' failed!"; this.clientLogger.logError(PackageContext.PACKAGE_COMPONENTS, msg, e, null, (Position) null); } + response.setProjectPackages(jsonPackages); return response; }); } diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/hover/HoverObjectResolver.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/hover/HoverObjectResolver.java index 4b549da7301a..9ae8fc280d9e 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/hover/HoverObjectResolver.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/hover/HoverObjectResolver.java @@ -15,6 +15,7 @@ */ package org.ballerinalang.langserver.hover; +import io.ballerina.compiler.api.symbols.ArrayTypeSymbol; import io.ballerina.compiler.api.symbols.ClassSymbol; import io.ballerina.compiler.api.symbols.Documentation; import io.ballerina.compiler.api.symbols.FunctionSymbol; @@ -22,15 +23,20 @@ import io.ballerina.compiler.api.symbols.ObjectTypeSymbol; import io.ballerina.compiler.api.symbols.ParameterKind; import io.ballerina.compiler.api.symbols.ParameterSymbol; +import io.ballerina.compiler.api.symbols.PathParameterSymbol; import io.ballerina.compiler.api.symbols.RecordTypeSymbol; import io.ballerina.compiler.api.symbols.ResourceMethodSymbol; import io.ballerina.compiler.api.symbols.Symbol; +import io.ballerina.compiler.api.symbols.SymbolKind; import io.ballerina.compiler.api.symbols.TypeDefinitionSymbol; import io.ballerina.compiler.api.symbols.TypeDescKind; import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; import io.ballerina.compiler.api.symbols.TypeSymbol; import io.ballerina.compiler.api.symbols.UnionTypeSymbol; import io.ballerina.compiler.api.symbols.VariableSymbol; +import io.ballerina.compiler.api.symbols.resourcepath.PathRestParam; +import io.ballerina.compiler.api.symbols.resourcepath.PathSegmentList; +import io.ballerina.compiler.api.symbols.resourcepath.ResourcePath; import io.ballerina.compiler.syntax.tree.DefaultableParameterNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.NonTerminalNode; @@ -159,11 +165,39 @@ private Hover getHoverObjectForSymbol(FunctionSymbol functionSymbol) { } List hoverContent = new ArrayList<>(); documentation.get().description().ifPresent(hoverContent::add); - + List parameterSymbols = new ArrayList<>(); + boolean isResourceMethod = functionSymbol.kind() == SymbolKind.RESOURCE_METHOD; + + if (isResourceMethod) { + ResourcePath resourcePath = ((ResourceMethodSymbol) functionSymbol).resourcePath(); + switch (resourcePath.kind()) { + case PATH_SEGMENT_LIST -> { + PathSegmentList pathSegmentList = (PathSegmentList) resourcePath; + List pathParameterSymbols = pathSegmentList.pathParameters(); + parameterSymbols.addAll(pathParameterSymbols); + pathSegmentList.pathRestParameter().ifPresent(parameterSymbols::add); + } + case PATH_REST_PARAM -> parameterSymbols.add(((PathRestParam) resourcePath).parameter()); + default -> { + // ignore + } + } + } Map paramsMap = documentation.get().parameterMap(); - if (!paramsMap.isEmpty()) { - List params = new ArrayList<>(); + List params = new ArrayList<>(); + + if (!paramsMap.isEmpty() || !parameterSymbols.isEmpty()) { params.add(MarkupUtils.header(3, ContextConstants.PARAM_TITLE) + CommonUtil.MD_LINE_SEPARATOR); + params.addAll(parameterSymbols.stream().map(param -> { + if (param.getName().isEmpty()) { + return MarkupUtils.quotedString(NameUtil + .getModifiedTypeName(context, param.typeDescriptor())); + } + String paramName = param.getName().get(); + String desc = paramsMap.getOrDefault(paramName, ""); + return MarkupUtils.quotedString(NameUtil.getModifiedTypeName(context, param.typeDescriptor())) + " " + + MarkupUtils.italicString(MarkupUtils.boldString(paramName)) + " : " + desc; + }).collect(Collectors.toList())); params.addAll(functionSymbol.typeDescriptor().params().get().stream().map(param -> { if (param.getName().isEmpty()) { return MarkupUtils.quotedString(NameUtil @@ -196,12 +230,20 @@ private Hover getHoverObjectForSymbol(FunctionSymbol functionSymbol) { Optional restParam = functionSymbol.typeDescriptor().restParam(); if (restParam.isPresent()) { - String modifiedTypeName = NameUtil.getModifiedTypeName(context, restParam.get().typeDescriptor()); + TypeSymbol typeSymbol = restParam.get().typeDescriptor(); + String modifiedTypeName = typeSymbol.typeKind() == TypeDescKind.ARRAY ? NameUtil + .getModifiedTypeName(context, ((ArrayTypeSymbol) typeSymbol).memberTypeDescriptor()) + : NameUtil.getModifiedTypeName(context, typeSymbol); + StringBuilder restParamBuilder = new StringBuilder(MarkupUtils.quotedString(modifiedTypeName + "...")); if (restParam.get().getName().isPresent()) { + String paramName = paramsMap.get(restParam.get().getName().get()); + if (paramName == null) { + paramName = ""; + } restParamBuilder.append(" ") .append(MarkupUtils.italicString(MarkupUtils.boldString(restParam.get().getName().get()))) - .append(" : ").append(paramsMap.get(restParam.get().getName().get())); + .append(" : ").append(paramName); } params.add(restParamBuilder.toString()); } diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/hover/HoverUtil.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/hover/HoverUtil.java index 6fde38a0f2ba..229e418b1b7f 100755 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/hover/HoverUtil.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/hover/HoverUtil.java @@ -26,6 +26,7 @@ import io.ballerina.compiler.syntax.tree.ExpressionNode; import io.ballerina.compiler.syntax.tree.ModulePartNode; import io.ballerina.compiler.syntax.tree.NonTerminalNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.compiler.syntax.tree.Token; import io.ballerina.projects.Document; import io.ballerina.projects.ModuleId; @@ -70,7 +71,7 @@ public static Hover getHover(HoverContext context) { LinePosition linePosition = LinePosition.from(cursorPosition.getLine(), cursorPosition.getCharacter()); // Check for the cancellation before the time-consuming operation context.checkCancelled(); - Optional symbolAtCursor = semanticModel.get().symbol(srcFile.get(), linePosition); + Optional symbolAtCursor = getSymbolAtCursor(context, semanticModel.get(), srcFile.get(), linePosition); // Check for the cancellation after the time-consuming operation context.checkCancelled(); @@ -117,6 +118,18 @@ public static Hover getHover(HoverContext context) { return hoverObj; } + private static Optional getSymbolAtCursor(HoverContext context, SemanticModel semanticModel, + Document srcFile, LinePosition linePosition) { + NonTerminalNode cursor = context.getNodeAtCursor(); + SyntaxKind kind = cursor.kind(); + if (kind == SyntaxKind.LIST || kind == SyntaxKind.PARENTHESIZED_ARG_LIST + || kind == SyntaxKind.SIMPLE_NAME_REFERENCE + && cursor.parent().kind() == SyntaxKind.CLIENT_RESOURCE_ACCESS_ACTION) { + return semanticModel.symbol(cursor.parent()); + } + return semanticModel.symbol(srcFile, linePosition); + } + /** * returns the default hover object. * diff --git a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/CreateFunctionTest.java b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/CreateFunctionTest.java index a451910e5282..3f80af11e19d 100644 --- a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/CreateFunctionTest.java +++ b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/CreateFunctionTest.java @@ -186,6 +186,18 @@ public Object[][] dataProvider() { {"create_function_in_anonymous_function1.json"}, {"create_function_in_anonymous_function2.json"}, + + {"create_function_in_worker1.json"}, + {"create_function_in_worker2.json"}, + {"create_function_in_worker3.json"}, + {"create_function_in_worker4.json"}, + {"create_function_in_worker5.json"}, + + {"create_function_in_explicit_anonymous_function1.json"}, + {"create_function_in_explicit_anonymous_function2.json"}, + {"create_function_in_explicit_anonymous_function3.json"}, + {"create_function_in_explicit_anonymous_function4.json"}, + {"create_function_in_explicit_anonymous_function5.json"} }; } diff --git a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/ExtractToConstantTest.java b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/ExtractToConstantTest.java index c76a79ebd2d5..cefff718e58c 100644 --- a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/ExtractToConstantTest.java +++ b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/ExtractToConstantTest.java @@ -71,7 +71,8 @@ public Object[][] dataProvider() { {"extractNumericLiteralInUnaryExprToConstant.json"}, {"extractNumericLiteralInUnaryExprToConstant2.json"}, {"extractExprToConstant1.json"}, - {"extractExprToConstant2.json"} + {"extractExprToConstant2.json"}, + {"extractTwoLinesToConstant.json"} }; } diff --git a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/FixReturnTypeTest.java b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/FixReturnTypeTest.java index 7f937a9541e4..c3389c5ca4b9 100644 --- a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/FixReturnTypeTest.java +++ b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/FixReturnTypeTest.java @@ -67,7 +67,19 @@ public Object[][] dataProvider() { {"fixReturnTypeInUnionContext3.json"}, {"fixReturnTypeInUnionContext4.json"}, {"fixReturnTypeInCommitAction.json"}, - {"fixReturnTypeInMain1.json"} + {"fixReturnTypeInMain1.json"}, + {"fixReturnTypeForConditionalExpr1.json"}, + {"fixReturnTypeForConditionalExpr2.json"}, + {"fixReturnTypeForConditionalExpr3.json"}, + {"fixReturnTypeForConditionalExpr4.json"}, + {"fixReturnTypeForConditionalExpr5.json"}, + {"fixReturnTypeForConditionalExpr6.json"}, + {"fixReturnTypeForConditionalExpr7.json"}, + {"fixReturnTypeForConditionalExpr8.json"}, + {"fixReturnTypeForQueryExpr1.json"}, + {"fixReturnTypeForQueryExpr2.json"}, + {"fixReturnTypeForQueryExpr3.json"}, + {"fixReturnTypeForQueryExpr4.json"} }; } diff --git a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/ImportModuleCodeActionTest.java b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/ImportModuleCodeActionTest.java index 766cece200a1..6a0a6dcabe60 100644 --- a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/ImportModuleCodeActionTest.java +++ b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/codeaction/ImportModuleCodeActionTest.java @@ -67,7 +67,13 @@ public Object[][] dataProvider() { {"importModuleWithLicenceHeader1.json"}, {"importModuleWithLicenceHeader2.json"}, {"importModuleWithIgnoredImport.json"}, - {"importModuleWithTopLevelComment.json"} + {"importModuleWithTopLevelComment.json"}, + {"importMultipleModules1.json"}, + {"importMultipleModules2.json"}, + {"importMultipleModules3.json"}, + {"importMultipleModules4.json"}, + {"importMultipleModules5.json"}, + {"importMultipleModules6.json"}, }; } diff --git a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/packages/ComponentsTest.java b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/packages/ComponentsTest.java index bde559486cd7..eeaf2c24a8f8 100644 --- a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/packages/ComponentsTest.java +++ b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/packages/ComponentsTest.java @@ -61,7 +61,6 @@ public void packageComponentsTestCase(String[] projects, String expected) throws List filePaths = new ArrayList<>(); for (String project : projects) { Path path = configsPath.resolve(project).resolve("main.bal").toAbsolutePath(); - TestUtil.openDocument(serviceEndpoint, path); filePaths.add(path.toString()); } @@ -133,7 +132,8 @@ public Object[][] getDataProvider() { {new String[]{"project", "project-functions", "project-services", "single-file"}, "multiple-packages_expected.json"}, {new String[]{"single-file"}, "single-file-package_expected.json"}, - {new String[]{"project-other"}, "project-other_expected.json"} + {new String[]{"project-other"}, "project-other_expected.json"}, + {new String[]{"non-exist"}, "project-exception_expected.json"} }; } } diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function1.json b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function1.json new file mode 100644 index 000000000000..8a5bd5ce911e --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function1.json @@ -0,0 +1,57 @@ +{ + "position": { + "line": 4, + "character": 16 + }, + "source": "create_function_in_explicit_anonymous_function.bal", + "expected": [ + { + "title": "Create function 'foo(...)'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 25, + "character": 1 + }, + "end": { + "line": 25, + "character": 1 + } + }, + "newText": "\n\nfunction foo() returns int {\n return 0;\n}" + } + ], + "resolvable": true, + "data": { + "extName": "org.ballerinalang.langserver.codeaction.BallerinaCodeActionExtension", + "codeActionName": "Create Function", + "fileUri": "create_function_in_explicit_anonymous_function.bal", + "range": { + "start": { + "line": 4, + "character": 15 + }, + "end": { + "line": 4, + "character": 20 + } + }, + "actionData": { + "key": "node.range", + "value": { + "start": { + "line": 4.0, + "character": 15.0 + }, + "end": { + "line": 4.0, + "character": 20.0 + } + } + } + } + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function2.json b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function2.json new file mode 100644 index 000000000000..cb29a4c1db21 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function2.json @@ -0,0 +1,57 @@ +{ + "position": { + "line": 8, + "character": 10 + }, + "source": "create_function_in_explicit_anonymous_function.bal", + "expected": [ + { + "title": "Create function 'foo(...)'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 25, + "character": 1 + }, + "end": { + "line": 25, + "character": 1 + } + }, + "newText": "\n\nfunction foo(int i) {\n \n}" + } + ], + "resolvable": true, + "data": { + "extName": "org.ballerinalang.langserver.codeaction.BallerinaCodeActionExtension", + "codeActionName": "Create Function", + "fileUri": "create_function_in_explicit_anonymous_function.bal", + "range": { + "start": { + "line": 8, + "character": 8 + }, + "end": { + "line": 8, + "character": 15 + } + }, + "actionData": { + "key": "node.range", + "value": { + "start": { + "line": 8.0, + "character": 8.0 + }, + "end": { + "line": 8.0, + "character": 15.0 + } + } + } + } + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function3.json b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function3.json new file mode 100644 index 000000000000..578f6dfa919f --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function3.json @@ -0,0 +1,57 @@ +{ + "position": { + "line": 13, + "character": 17 + }, + "source": "create_function_in_explicit_anonymous_function.bal", + "expected": [ + { + "title": "Create function 'foo(...)'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 25, + "character": 1 + }, + "end": { + "line": 25, + "character": 1 + } + }, + "newText": "\n\nfunction foo() {\n \n}" + } + ], + "resolvable": true, + "data": { + "extName": "org.ballerinalang.langserver.codeaction.BallerinaCodeActionExtension", + "codeActionName": "Create Function", + "fileUri": "create_function_in_explicit_anonymous_function.bal", + "range": { + "start": { + "line": 13, + "character": 15 + }, + "end": { + "line": 13, + "character": 20 + } + }, + "actionData": { + "key": "node.range", + "value": { + "start": { + "line": 13.0, + "character": 15.0 + }, + "end": { + "line": 13.0, + "character": 20.0 + } + } + } + } + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function4.json b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function4.json new file mode 100644 index 000000000000..4d499c7f50d1 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function4.json @@ -0,0 +1,57 @@ +{ + "position": { + "line": 17, + "character": 17 + }, + "source": "create_function_in_explicit_anonymous_function.bal", + "expected": [ + { + "title": "Create function 'foo(...)'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 25, + "character": 1 + }, + "end": { + "line": 25, + "character": 1 + } + }, + "newText": "\n\nfunction foo() returns module1:TestRecord2 {\n return {};\n}" + } + ], + "resolvable": true, + "data": { + "extName": "org.ballerinalang.langserver.codeaction.BallerinaCodeActionExtension", + "codeActionName": "Create Function", + "fileUri": "create_function_in_explicit_anonymous_function.bal", + "range": { + "start": { + "line": 17, + "character": 15 + }, + "end": { + "line": 17, + "character": 20 + } + }, + "actionData": { + "key": "node.range", + "value": { + "start": { + "line": 17.0, + "character": 15.0 + }, + "end": { + "line": 17.0, + "character": 20.0 + } + } + } + } + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function5.json b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function5.json new file mode 100644 index 000000000000..008a88e1e092 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_explicit_anonymous_function5.json @@ -0,0 +1,57 @@ +{ + "position": { + "line": 22, + "character": 22 + }, + "source": "create_function_in_explicit_anonymous_function.bal", + "expected": [ + { + "title": "Create function 'foo(...)'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 25, + "character": 1 + }, + "end": { + "line": 25, + "character": 1 + } + }, + "newText": "\n\nfunction foo() returns int {\n return 0;\n}" + } + ], + "resolvable": true, + "data": { + "extName": "org.ballerinalang.langserver.codeaction.BallerinaCodeActionExtension", + "codeActionName": "Create Function", + "fileUri": "create_function_in_explicit_anonymous_function.bal", + "range": { + "start": { + "line": 22, + "character": 19 + }, + "end": { + "line": 22, + "character": 24 + } + }, + "actionData": { + "key": "node.range", + "value": { + "start": { + "line": 22.0, + "character": 19.0 + }, + "end": { + "line": 22.0, + "character": 24.0 + } + } + } + } + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker1.json b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker1.json new file mode 100644 index 000000000000..d7dd1dd1e95b --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker1.json @@ -0,0 +1,57 @@ +{ + "position": { + "line": 4, + "character": 16 + }, + "source": "create_function_in_worker.bal", + "expected": [ + { + "title": "Create function 'foo(...)'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 25, + "character": 1 + }, + "end": { + "line": 25, + "character": 1 + } + }, + "newText": "\n\nfunction foo() returns int {\n return 0;\n}" + } + ], + "resolvable": true, + "data": { + "extName": "org.ballerinalang.langserver.codeaction.BallerinaCodeActionExtension", + "codeActionName": "Create Function", + "fileUri": "create_function_in_worker.bal", + "range": { + "start": { + "line": 4, + "character": 15 + }, + "end": { + "line": 4, + "character": 20 + } + }, + "actionData": { + "key": "node.range", + "value": { + "start": { + "line": 4.0, + "character": 15.0 + }, + "end": { + "line": 4.0, + "character": 20.0 + } + } + } + } + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker2.json b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker2.json new file mode 100644 index 000000000000..7374fb76cf78 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker2.json @@ -0,0 +1,57 @@ +{ + "position": { + "line": 8, + "character": 10 + }, + "source": "create_function_in_worker.bal", + "expected": [ + { + "title": "Create function 'foo(...)'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 25, + "character": 1 + }, + "end": { + "line": 25, + "character": 1 + } + }, + "newText": "\n\nfunction foo(int i) {\n \n}" + } + ], + "resolvable": true, + "data": { + "extName": "org.ballerinalang.langserver.codeaction.BallerinaCodeActionExtension", + "codeActionName": "Create Function", + "fileUri": "create_function_in_worker.bal", + "range": { + "start": { + "line": 8, + "character": 8 + }, + "end": { + "line": 8, + "character": 15 + } + }, + "actionData": { + "key": "node.range", + "value": { + "start": { + "line": 8.0, + "character": 8.0 + }, + "end": { + "line": 8.0, + "character": 15.0 + } + } + } + } + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker3.json b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker3.json new file mode 100644 index 000000000000..641f38fc3fb9 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker3.json @@ -0,0 +1,57 @@ +{ + "position": { + "line": 13, + "character": 17 + }, + "source": "create_function_in_worker.bal", + "expected": [ + { + "title": "Create function 'foo(...)'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 25, + "character": 1 + }, + "end": { + "line": 25, + "character": 1 + } + }, + "newText": "\n\nfunction foo() {\n \n}" + } + ], + "resolvable": true, + "data": { + "extName": "org.ballerinalang.langserver.codeaction.BallerinaCodeActionExtension", + "codeActionName": "Create Function", + "fileUri": "create_function_in_worker.bal", + "range": { + "start": { + "line": 13, + "character": 15 + }, + "end": { + "line": 13, + "character": 20 + } + }, + "actionData": { + "key": "node.range", + "value": { + "start": { + "line": 13.0, + "character": 15.0 + }, + "end": { + "line": 13.0, + "character": 20.0 + } + } + } + } + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker4.json b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker4.json new file mode 100644 index 000000000000..55276910a9b4 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker4.json @@ -0,0 +1,57 @@ +{ + "position": { + "line": 17, + "character": 17 + }, + "source": "create_function_in_worker.bal", + "expected": [ + { + "title": "Create function 'foo(...)'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 25, + "character": 1 + }, + "end": { + "line": 25, + "character": 1 + } + }, + "newText": "\n\nfunction foo() returns module1:TestRecord2 {\n return {};\n}" + } + ], + "resolvable": true, + "data": { + "extName": "org.ballerinalang.langserver.codeaction.BallerinaCodeActionExtension", + "codeActionName": "Create Function", + "fileUri": "create_function_in_worker.bal", + "range": { + "start": { + "line": 17, + "character": 15 + }, + "end": { + "line": 17, + "character": 20 + } + }, + "actionData": { + "key": "node.range", + "value": { + "start": { + "line": 17.0, + "character": 15.0 + }, + "end": { + "line": 17.0, + "character": 20.0 + } + } + } + } + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker5.json b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker5.json new file mode 100644 index 000000000000..fd4ff61bc968 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/config/create_function_in_worker5.json @@ -0,0 +1,57 @@ +{ + "position": { + "line": 22, + "character": 22 + }, + "source": "create_function_in_worker.bal", + "expected": [ + { + "title": "Create function 'foo(...)'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 25, + "character": 1 + }, + "end": { + "line": 25, + "character": 1 + } + }, + "newText": "\n\nfunction foo() returns int {\n return 0;\n}" + } + ], + "resolvable": true, + "data": { + "extName": "org.ballerinalang.langserver.codeaction.BallerinaCodeActionExtension", + "codeActionName": "Create Function", + "fileUri": "create_function_in_worker.bal", + "range": { + "start": { + "line": 22, + "character": 19 + }, + "end": { + "line": 22, + "character": 24 + } + }, + "actionData": { + "key": "node.range", + "value": { + "start": { + "line": 22.0, + "character": 19.0 + }, + "end": { + "line": 22.0, + "character": 24.0 + } + } + } + } + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/source/create_function_in_explicit_anonymous_function.bal b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/source/create_function_in_explicit_anonymous_function.bal new file mode 100644 index 000000000000..19bee2900b25 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/source/create_function_in_explicit_anonymous_function.bal @@ -0,0 +1,26 @@ +import ballerina/module1; + +function createFunctionInWorker() { + var fn1 = function() returns int { + return foo(); + }; + + var fn2 = function() returns string { + foo(32); + return "bar"; + }; + + var fn3 = function() { + return foo(); + }; + + var fn4 = function() returns module1:TestRecord2 { + return foo(); + }; + + var fn5 = function() returns function () returns int { + return function () returns int { + return foo(); + }; + }; +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/source/create_function_in_worker.bal b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/source/create_function_in_worker.bal new file mode 100644 index 000000000000..d889e6371fc8 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/create-function/source/create_function_in_worker.bal @@ -0,0 +1,26 @@ +import ballerina/module1; + +function createFunctionInWorker() { + worker A returns int { + return foo(); + } + + worker B returns string { + foo(32); + return "bar"; + } + + worker C { + return foo(); + } + + worker D returns module1:TestRecord2 { + return foo(); + } + + fork { + worker E returns int { + return foo(); + } + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-constant/config/extractTwoLinesToConstant.json b/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-constant/config/extractTwoLinesToConstant.json new file mode 100644 index 000000000000..a8e7bd7b2599 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-constant/config/extractTwoLinesToConstant.json @@ -0,0 +1,59 @@ +{ + "range": { + "start": { + "line": 26, + "character": 13 + }, + "end": { + "line": 27, + "character": 18 + } + }, + "source": "extractToConstant.bal", + "expected": [ + { + "title": "Extract to constant", + "kind": "refactor.extract", + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + }, + "newText": "const string CONST = \"abc\"\n + \"def\";\n" + }, + { + "range": { + "start": { + "line": 26, + "character": 13 + }, + "end": { + "line": 27, + "character": 18 + } + }, + "newText": "CONST" + } + ], + "command": { + "title": "Rename constant", + "command": "ballerina.action.positional.rename", + "arguments": [ + "extractToConstant.bal", + { + "line": 28, + "character": 13 + } + ] + }, + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-constant/source/extractToConstant.bal b/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-constant/source/extractToConstant.bal index c17dffb96115..d66d4c56ccf7 100644 --- a/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-constant/source/extractToConstant.bal +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-constant/source/extractToConstant.bal @@ -23,3 +23,6 @@ const int CONST1 = 10 + 20; int unaryNumericExpr = -100; boolean unaryLogicalExpr = !true; + +string str = "abc" + + "def"; diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-local-variable/config/extractToVariableInQueryExpression.json b/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-local-variable/config/extractToVariableInQueryExpression.json index f2be908fbbca..c5a1e8d06e4b 100644 --- a/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-local-variable/config/extractToVariableInQueryExpression.json +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-local-variable/config/extractToVariableInQueryExpression.json @@ -48,7 +48,7 @@ "arguments": [ "extractToVariableInQueryExpression.bal", { - "line": 3, + "line": 4, "character": 24 } ] diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-local-variable/config/extractToVariableInTableConstructor.json b/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-local-variable/config/extractToVariableInTableConstructor.json index 3b7b1bb47622..975f9b02f3bf 100644 --- a/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-local-variable/config/extractToVariableInTableConstructor.json +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-local-variable/config/extractToVariableInTableConstructor.json @@ -48,7 +48,7 @@ "arguments": [ "extractToVariableInTableConstructor.bal", { - "line": 6, + "line": 9, "character": 30 } ] diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-local-variable/config/extractToVariableInTableConstructor2.json b/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-local-variable/config/extractToVariableInTableConstructor2.json index 59c6e0b0eb49..a16420363e2a 100644 --- a/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-local-variable/config/extractToVariableInTableConstructor2.json +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/extract-to-local-variable/config/extractToVariableInTableConstructor2.json @@ -48,7 +48,7 @@ "arguments": [ "extractToVariableInTableConstructor.bal", { - "line": 13, + "line": 16, "character": 21 } ] diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr1.json b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr1.json new file mode 100644 index 000000000000..fc6b045f0a45 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr1.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 6, + "character": 39 + }, + "source": "fixReturnTypeForConditionalExpr.bal", + "expected": [ + { + "title": "Change return type to 'boolean|int'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 5, + "character": 76 + }, + "end": { + "line": 5, + "character": 76 + } + }, + "newText": " returns boolean|int" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr2.json b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr2.json new file mode 100644 index 000000000000..036ff255608e --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr2.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 6, + "character": 51 + }, + "source": "fixReturnTypeForConditionalExpr.bal", + "expected": [ + { + "title": "Change return type to 'boolean|int'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 5, + "character": 76 + }, + "end": { + "line": 5, + "character": 76 + } + }, + "newText": " returns boolean|int" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr3.json b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr3.json new file mode 100644 index 000000000000..b50259462922 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr3.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 11, + "character": 72 + }, + "source": "fixReturnTypeForConditionalExpr.bal", + "expected": [ + { + "title": "Change return type to 'module1:TestRecord1|int'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 9, + "character": 76 + }, + "end": { + "line": 9, + "character": 76 + } + }, + "newText": " returns module1:TestRecord1|int" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr4.json b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr4.json new file mode 100644 index 000000000000..1e4a2b4bc402 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr4.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 11, + "character": 81 + }, + "source": "fixReturnTypeForConditionalExpr.bal", + "expected": [ + { + "title": "Change return type to 'module1:TestRecord1|int'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 9, + "character": 76 + }, + "end": { + "line": 9, + "character": 76 + } + }, + "newText": " returns module1:TestRecord1|int" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr5.json b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr5.json new file mode 100644 index 000000000000..a8151ae1e05d --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr5.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 18, + "character": 18 + }, + "source": "fixReturnTypeForConditionalExpr.bal", + "expected": [ + { + "title": "Change return type to 'string|boolean'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 17, + "character": 50 + }, + "end": { + "line": 17, + "character": 50 + } + }, + "newText": " returns string|boolean" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr6.json b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr6.json new file mode 100644 index 000000000000..00cbb777ecf6 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr6.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 18, + "character": 28 + }, + "source": "fixReturnTypeForConditionalExpr.bal", + "expected": [ + { + "title": "Change return type to 'string|boolean'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 17, + "character": 50 + }, + "end": { + "line": 17, + "character": 50 + } + }, + "newText": " returns string|boolean" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr7.json b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr7.json new file mode 100644 index 000000000000..ea5c8625404e --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr7.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 23, + "character": 74 + }, + "source": "fixReturnTypeForConditionalExpr.bal", + "expected": [ + { + "title": "Change return type to 'string|int|boolean|module1:TestRecord1|float'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 21, + "character": 91 + }, + "end": { + "line": 21, + "character": 91 + } + }, + "newText": " returns string|int|boolean|module1:TestRecord1|float" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr8.json b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr8.json new file mode 100644 index 000000000000..5916087bcef2 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForConditionalExpr8.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 23, + "character": 86 + }, + "source": "fixReturnTypeForConditionalExpr.bal", + "expected": [ + { + "title": "Change return type to 'string|int|boolean|module1:TestRecord1|float'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 21, + "character": 91 + }, + "end": { + "line": 21, + "character": 91 + } + }, + "newText": " returns string|int|boolean|module1:TestRecord1|float" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForQueryExpr1.json b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForQueryExpr1.json new file mode 100644 index 000000000000..317f8dfc1279 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForQueryExpr1.json @@ -0,0 +1,69 @@ +{ + "position": { + "line": 29, + "character": 11 + }, + "source": "fixReturnTypeForQueryExpr.bal", + "expected": [ + { + "title": "Change return type to 'map[]'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 28, + "character": 32 + }, + "end": { + "line": 28, + "character": 32 + } + }, + "newText": " returns map[]" + } + ], + "resolvable": false + }, + { + "title": "Change return type to 'record {|string name; float gpa;|}[]'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 28, + "character": 32 + }, + "end": { + "line": 28, + "character": 32 + } + }, + "newText": " returns record {|string name; float gpa;|}[]" + } + ], + "resolvable": false + }, + { + "title": "Change return type to 'json[]'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 28, + "character": 32 + }, + "end": { + "line": 28, + "character": 32 + } + }, + "newText": " returns json[]" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForQueryExpr2.json b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForQueryExpr2.json new file mode 100644 index 000000000000..ebc954fefb84 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForQueryExpr2.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 36, + "character": 24 + }, + "source": "fixReturnTypeForQueryExpr.bal", + "expected": [ + { + "title": "Change return type to 'stream'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 34, + "character": 38 + }, + "end": { + "line": 34, + "character": 38 + } + }, + "newText": " returns stream" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForQueryExpr3.json b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForQueryExpr3.json new file mode 100644 index 000000000000..1a967ad049da --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForQueryExpr3.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 42, + "character": 12 + }, + "source": "fixReturnTypeForQueryExpr.bal", + "expected": [ + { + "title": "Change return type to '(int?)[]'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 40, + "character": 35 + }, + "end": { + "line": 40, + "character": 35 + } + }, + "newText": " returns (int?)[]" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForQueryExpr4.json b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForQueryExpr4.json new file mode 100644 index 000000000000..094ffcd0fa60 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/config/fixReturnTypeForQueryExpr4.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 49, + "character": 18 + }, + "source": "fixReturnTypeForQueryExpr.bal", + "expected": [ + { + "title": "Change return type to 'float'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 46, + "character": 35 + }, + "end": { + "line": 46, + "character": 35 + } + }, + "newText": " returns float" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/source/fixReturnTypeForConditionalExpr.bal b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/source/fixReturnTypeForConditionalExpr.bal new file mode 100644 index 000000000000..0e3ef9079b05 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/source/fixReturnTypeForConditionalExpr.bal @@ -0,0 +1,25 @@ +import ballerina/module1; + +boolean moduleFlag1 = false; +boolean moduleFlag2 = true; + +function testNestedTernaryExpr2(boolean flag1, boolean flag2, boolean flag3) { + return flag1 == flag3 ? flag1 && moduleFlag2 : 0; +} + +function testNestedTernaryExpr3(boolean flag1, boolean flag2, boolean flag3) { + module1:TestRecord1 rec1 = {}; + return (flag1 == flag3 ? moduleFlag1 && flag2 : flag1 || flag3) ? rec1 : 0 + 32; +} + +string? moduleNullableStr = (); +int? moduleNullableInt = 1; + +function testNestedElvisExpr1(string? nullableStr) { + return nullableStr ?: false; +} + +function testNestedElvisExpr2(string? nullableStr, int? nullableInt, boolean? nullableBool) { + module1:TestRecord1? nullableRec = {}; + return moduleNullableStr ?: moduleNullableInt ?: nullableBool ?: nullableRec ?: 0.0; +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/source/fixReturnTypeForQueryExpr.bal b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/source/fixReturnTypeForQueryExpr.bal new file mode 100644 index 000000000000..5f862e6b5f0e --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/fix-return-type/source/fixReturnTypeForQueryExpr.bal @@ -0,0 +1,51 @@ +type Person record {| + readonly int id; + string name; + int age?; + Person parent?; +|}; + +type Student record { + int id; + string fname; + string lname; + int age; + float gpa; +}; + +function getStudents() returns Student[] { + Student s3 = {id: 3, fname: "Amy", lname: "Melina", age: 30, gpa: 1.3}; + Student s1 = {id: 1, fname: "Jon", lname: "Doe", age: 21, gpa: 2.1}; + Student s2 = {id: 2, fname: "Jane", lname: "Doe", age: 25, gpa: 3.2}; + return [s1, s2, s3]; +} + +function getPeople() returns Person[] { + Person p1 = {id: 1, name: "Jon Doe"}; + Person p2 = {id: 2, name: "Jane Doe"}; + return [p1, p2]; +} + +function testQueryExprWithJoin() { + return from var st in getStudents() + join var {id, name} in getPeople() on st.id equals id + select {name: name, gpa: st.gpa}; +} + +function testQueryExprWithOnConflict() { + return table key(id) from var st in getStudents().toStream() + select {id: st.id, name: st.fname, age: st.age} + on conflict error("Conflicted Key", cKey = st.id); +} + +function testQueryExprWithGroupBy() { + return from var {name, age} in getPeople() + group by age + select age; +} + +function testQueryExprWithCollect() { + return from var {age, gpa} in getStudents() + let var ageScore = (50 - age) * gpa + collect sum(ageScore); +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules1.json b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules1.json new file mode 100644 index 000000000000..2972d2f259ef --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules1.json @@ -0,0 +1,69 @@ +{ + "position": { + "line": 2, + "character": 30 + }, + "source": "project/importMultipleModules1.bal", + "expected": [ + { + "title": "Import module 'ballerina/lang.array'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + }, + "newText": "import ballerina/lang.array;\n" + } + ], + "resolvable": false + }, + { + "title": "Import module 'project.array'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + }, + "newText": "import project.array;\n" + } + ], + "resolvable": false + }, + { + "title": "Import module 'project.module2'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + }, + "newText": "import project.module2;\n" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules2.json b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules2.json new file mode 100644 index 000000000000..0907878ba866 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules2.json @@ -0,0 +1,69 @@ +{ + "position": { + "line": 6, + "character": 32 + }, + "source": "project/importMultipleModules1.bal", + "expected": [ + { + "title": "Import module 'ballerina/module1'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + }, + "newText": "import ballerina/module1;\n" + } + ], + "resolvable": false + }, + { + "title": "Import module 'project.module1'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + }, + "newText": "import project.module1;\n" + } + ], + "resolvable": false + }, + { + "title": "Import module 'project.module2'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + }, + "newText": "import project.module2;\n" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules3.json b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules3.json new file mode 100644 index 000000000000..01b03b10a716 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules3.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 3, + "character": 30 + }, + "source": "project/importMultipleModules2.bal", + "expected": [ + { + "title": "Import module 'project.module2'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 1, + "character": 0 + }, + "end": { + "line": 1, + "character": 0 + } + }, + "newText": "import project.module2;\n" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules4.json b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules4.json new file mode 100644 index 000000000000..645b5665888f --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules4.json @@ -0,0 +1,49 @@ +{ + "position": { + "line": 3, + "character": 30 + }, + "source": "project/importMultipleModules3.bal", + "expected": [ + { + "title": "Import module 'ballerina/module1'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 1, + "character": 0 + }, + "end": { + "line": 1, + "character": 0 + } + }, + "newText": "import ballerina/module1;\n" + } + ], + "resolvable": false + }, + { + "title": "Import module 'project.module1'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 1, + "character": 0 + }, + "end": { + "line": 1, + "character": 0 + } + }, + "newText": "import project.module1;\n" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules5.json b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules5.json new file mode 100644 index 000000000000..edbb6dd659a5 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules5.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 2, + "character": 25 + }, + "source": "project/importMultipleModules4.bal", + "expected": [ + { + "title": "Import module 'project.module2'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + }, + "newText": "import project.module2;\n" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules6.json b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules6.json new file mode 100644 index 000000000000..bfb5c68b65a3 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/config/importMultipleModules6.json @@ -0,0 +1,29 @@ +{ + "position": { + "line": 3, + "character": 25 + }, + "source": "project/importMultipleModules4.bal", + "expected": [ + { + "title": "Import module 'project.module2'", + "kind": "quickfix", + "edits": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + }, + "newText": "import project.module2;\n" + } + ], + "resolvable": false + } + ] +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/importMultipleModules1.bal b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/importMultipleModules1.bal new file mode 100644 index 000000000000..de63db1f4dfb --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/importMultipleModules1.bal @@ -0,0 +1,8 @@ + +function testSubModuleWithNoImports() { + _ = module2:isIntArray(array:size()); +} + +function testExternalModuleWithNoImports() { + _ = module2:isIntArray(module1:function1()); +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/importMultipleModules2.bal b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/importMultipleModules2.bal new file mode 100644 index 000000000000..2d18a6f51360 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/importMultipleModules2.bal @@ -0,0 +1,5 @@ +import project.array; + +function testSubModuleWithOneImport() { + _ = module2:isIntArray(array:size()); +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/importMultipleModules3.bal b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/importMultipleModules3.bal new file mode 100644 index 000000000000..1cf190cd79ef --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/importMultipleModules3.bal @@ -0,0 +1,5 @@ +import project.module2; + +function testExternalModuleWithOneImport() { + _ = module2:isIntArray(module1:function1()); +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/importMultipleModules4.bal b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/importMultipleModules4.bal new file mode 100644 index 000000000000..02391141ddc8 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/importMultipleModules4.bal @@ -0,0 +1,5 @@ + +public function testLangLib() { + module2:assertEquals(3, 3); + module2:assertEquals("x", "x"); +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/modules/module2/mod.bal b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/modules/module2/mod.bal new file mode 100644 index 000000000000..33c61412d5c8 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/import-module/source/project/modules/module2/mod.bal @@ -0,0 +1,5 @@ +public function first(int[] arr) returns int => arr[0]; + +public function assertEquals(anydata expected, anydata actual) {} + +public function isIntArray(any value) returns boolean => value is int[]; diff --git a/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/externalFunctionAnnotation1.json b/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/externalFunctionAnnotation1.json index 611cad7857fa..0140641c0aa5 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/externalFunctionAnnotation1.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/externalFunctionAnnotation1.json @@ -392,6 +392,15 @@ "sortText": "P", "insertText": "typedesc", "insertTextFormat": "Snippet" + }, + { + "label": "display", + "kind": "Property", + "detail": "Annotation", + "sortText": "B", + "insertText": "display {\n\tlabel: ${1:\"\"}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [] } ] } diff --git a/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/externalFunctionAnnotation2.json b/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/externalFunctionAnnotation2.json index e23f771bff30..83d291b37c27 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/externalFunctionAnnotation2.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/externalFunctionAnnotation2.json @@ -392,6 +392,15 @@ "sortText": "P", "insertText": "typedesc", "insertTextFormat": "Snippet" + }, + { + "label": "display", + "kind": "Property", + "detail": "Annotation", + "sortText": "B", + "insertText": "display {\n\tlabel: ${1:\"\"}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [] } ] } diff --git a/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/startActionAnnotation1.json b/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/startActionAnnotation1.json index eaf621f46a1f..5e1daca3dc0e 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/startActionAnnotation1.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/startActionAnnotation1.json @@ -401,6 +401,15 @@ "sortText": "P", "insertText": "typedesc", "insertTextFormat": "Snippet" + }, + { + "label": "display", + "kind": "Property", + "detail": "Annotation", + "sortText": "B", + "insertText": "display {\n\tlabel: ${1:\"\"}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [] } ] } diff --git a/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/startActionAnnotation2.json b/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/startActionAnnotation2.json index b71fc0874085..4fd4102cc768 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/startActionAnnotation2.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/startActionAnnotation2.json @@ -401,6 +401,15 @@ "sortText": "P", "insertText": "typedesc", "insertTextFormat": "Snippet" + }, + { + "label": "display", + "kind": "Property", + "detail": "Annotation", + "sortText": "B", + "insertText": "display {\n\tlabel: ${1:\"\"}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [] } ] } diff --git a/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/workerDeclAnnotation1.json b/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/workerDeclAnnotation1.json index ed4c5d941759..220c7227eda9 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/workerDeclAnnotation1.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/workerDeclAnnotation1.json @@ -401,6 +401,15 @@ "sortText": "P", "insertText": "typedesc", "insertTextFormat": "Snippet" + }, + { + "label": "display", + "kind": "Property", + "detail": "Annotation", + "sortText": "B", + "insertText": "display {\n\tlabel: ${1:\"\"}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [] } ] } diff --git a/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/workerDeclAnnotation2.json b/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/workerDeclAnnotation2.json index bac02a34004a..71bc7bc43d63 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/workerDeclAnnotation2.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/annotation_ctx/config/workerDeclAnnotation2.json @@ -401,6 +401,15 @@ "sortText": "P", "insertText": "typedesc", "insertTextFormat": "Snippet" + }, + { + "label": "display", + "kind": "Property", + "detail": "Annotation", + "sortText": "B", + "insertText": "display {\n\tlabel: ${1:\"\"}\n}", + "insertTextFormat": "Snippet", + "additionalTextEdits": [] } ] } diff --git a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_api_docs_function1.json b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_api_docs_function1.json index 6710c8b7f3ea..a531799090ac 100644 --- a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_api_docs_function1.json +++ b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_api_docs_function1.json @@ -10,7 +10,7 @@ "result": { "contents": { "kind": "markdown", - "value": "Returns the maximum of one or more int values.\n\n```ballerina\nint:max(50, 20, 30, 70, 65) ⇒ 70\n[int, int, int] scores = [52, 95, 76];\nint:max(...scores) ⇒ 95\nint n = 18;\nn.max(25, 30, 4, 15) ⇒ 30\n```\n \n \n--- \n \n### Parameters \n \n`int` ***n*** : first int value \n`int[]...` ***ns*** : other int values \n \n--- \n \n### Return \n`int` : maximum value of value of parameter `n` and all of parameter `ns` \n \n--- \n \n[View API Docs](https://lib.ballerina.io/ballerina/lang.int/0.0.0#max)" + "value": "Returns the maximum of one or more int values.\n\n```ballerina\nint:max(50, 20, 30, 70, 65) ⇒ 70\n[int, int, int] scores = [52, 95, 76];\nint:max(...scores) ⇒ 95\nint n = 18;\nn.max(25, 30, 4, 15) ⇒ 30\n```\n \n \n--- \n \n### Parameters \n \n`int` ***n*** : first int value \n`int...` ***ns*** : other int values \n \n--- \n \n### Return \n`int` : maximum value of value of parameter `n` and all of parameter `ns` \n \n--- \n \n[View API Docs](https://lib.ballerina.io/ballerina/lang.int/0.0.0#max)" } }, "id": { diff --git a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_api_docs_negative.json b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_api_docs_negative.json deleted file mode 100644 index a2b638e8b888..000000000000 --- a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_api_docs_negative.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "position": { - "line": 16, - "character": 29 - }, - "source": { - "file": "hover_for_api_docs_negative.bal" - }, - "expected": { - "result": { - "contents": { - "kind": "markdown", - "value": "" - } - }, - "id": { - "left": "324" - }, - "jsonrpc": "2.0" - } -} diff --git a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path1.json b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path1.json new file mode 100644 index 000000000000..ef8a552b08b7 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path1.json @@ -0,0 +1,21 @@ +{ + "position": { + "line": 28, + "character": 34 + }, + "source": { + "file": "hover_for_resource_path.bal" + }, + "expected": { + "result": { + "contents": { + "kind": "markdown", + "value": "Test Description.\n \n \n--- \n \n### Parameters \n \n`string` ***param1*** : parameter description \n`string` ***param2*** : parameter description \n`string` ***param3*** : parameter description \n`int...` ***param4*** : \n \n--- \n \n### Return \n`string` : return value description" + } + }, + "id": { + "left": "324" + }, + "jsonrpc": "2.0" + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path2.json b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path2.json new file mode 100644 index 000000000000..a04d5db61f5a --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path2.json @@ -0,0 +1,21 @@ +{ + "position": { + "line": 28, + "character": 39 + }, + "source": { + "file": "hover_for_resource_path.bal" + }, + "expected": { + "result": { + "contents": { + "kind": "markdown", + "value": "Test Description.\n \n \n--- \n \n### Parameters \n \n`string` ***param1*** : parameter description \n`string` ***param2*** : parameter description \n`string` ***param3*** : parameter description \n`int...` ***param4*** : \n \n--- \n \n### Return \n`string` : return value description" + } + }, + "id": { + "left": "324" + }, + "jsonrpc": "2.0" + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path3.json b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path3.json new file mode 100644 index 000000000000..9d194b82f512 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path3.json @@ -0,0 +1,21 @@ +{ + "position": { + "line": 28, + "character": 55 + }, + "source": { + "file": "hover_for_resource_path.bal" + }, + "expected": { + "result": { + "contents": { + "kind": "markdown", + "value": "Test Description.\n \n \n--- \n \n### Parameters \n \n`string` ***param1*** : parameter description \n`string` ***param2*** : parameter description \n`string` ***param3*** : parameter description \n`int...` ***param4*** : \n \n--- \n \n### Return \n`string` : return value description" + } + }, + "id": { + "left": "324" + }, + "jsonrpc": "2.0" + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path4.json b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path4.json new file mode 100644 index 000000000000..1d675d02ece9 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path4.json @@ -0,0 +1,21 @@ +{ + "position": { + "line": 29, + "character": 34 + }, + "source": { + "file": "hover_for_resource_path.bal" + }, + "expected": { + "result": { + "contents": { + "kind": "markdown", + "value": "Description.\n \n \n--- \n \n### Parameters \n \n`int` ***ids*** : parameter description \n \n--- \n \n### Return \n`string` : return value description" + } + }, + "id": { + "left": "324" + }, + "jsonrpc": "2.0" + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path5.json b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path5.json new file mode 100644 index 000000000000..6482412964c5 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path5.json @@ -0,0 +1,21 @@ +{ + "position": { + "line": 30, + "character": 35 + }, + "source": { + "file": "hover_for_resource_path.bal" + }, + "expected": { + "result": { + "contents": { + "kind": "markdown", + "value": "Description.\n \n \n--- \n \n### Parameters \n \n`int` ***ids*** : parameter description \n \n--- \n \n### Return \n`string` : return value description" + } + }, + "id": { + "left": "324" + }, + "jsonrpc": "2.0" + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path6.json b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path6.json new file mode 100644 index 000000000000..ef8a552b08b7 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_for_resource_path6.json @@ -0,0 +1,21 @@ +{ + "position": { + "line": 28, + "character": 34 + }, + "source": { + "file": "hover_for_resource_path.bal" + }, + "expected": { + "result": { + "contents": { + "kind": "markdown", + "value": "Test Description.\n \n \n--- \n \n### Parameters \n \n`string` ***param1*** : parameter description \n`string` ***param2*** : parameter description \n`string` ***param3*** : parameter description \n`int...` ***param4*** : \n \n--- \n \n### Return \n`string` : return value description" + } + }, + "id": { + "left": "324" + }, + "jsonrpc": "2.0" + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_function_call_with_defaultable_param2.json b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_function_call_with_defaultable_param2.json index 61ca5cf37bb8..cfd36ebb1661 100644 --- a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_function_call_with_defaultable_param2.json +++ b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_function_call_with_defaultable_param2.json @@ -10,7 +10,7 @@ "result": { "contents": { "kind": "markdown", - "value": "This is function4 with input parameters\n \n \n--- \n \n### Parameters \n \n`int` ***param1*** : param1 Parameter Description \n`int` ***param2*** : param2 Parameter Description \n`string` ***param3*** : param3 Parameter Description \n`float[]...` ***param4*** : param4 Parameter Description \n \n--- \n \n[View API Docs](https://lib.ballerina.io/ballerina/module1/0.1.0#function4)" + "value": "This is function4 with input parameters\n \n \n--- \n \n### Parameters \n \n`int` ***param1*** : param1 Parameter Description \n`int` ***param2*** : param2 Parameter Description \n`string` ***param3*** : param3 Parameter Description \n`float...` ***param4*** : param4 Parameter Description \n \n--- \n \n[View API Docs](https://lib.ballerina.io/ballerina/module1/0.1.0#function4)" } }, "id": { diff --git a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_function_def1.json b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_function_def1.json index 31faac721b6d..e501c6a9aaea 100644 --- a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_function_def1.json +++ b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_function_def1.json @@ -10,7 +10,7 @@ "result": { "contents": { "kind": "markdown", - "value": "Description\n \n \n--- \n \n### Parameters \n \n`int` ***param1*** : param1 Parameter Description \n`int` ***param2*** : param2 Parameter Description \n`string` ***param3*** : param3 Parameter Description`(default: \"\")` \n`int[]...` ***restparam*** : restparam Parameter Description \n \n--- \n \n### Return \n`int` : Return Value Description" + "value": "Description\n \n \n--- \n \n### Parameters \n \n`int` ***param1*** : param1 Parameter Description \n`int` ***param2*** : param2 Parameter Description \n`string` ***param3*** : param3 Parameter Description`(default: \"\")` \n`int...` ***restparam*** : restparam Parameter Description \n \n--- \n \n### Return \n`int` : Return Value Description" } }, "id": { diff --git a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_function_def2.json b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_function_def2.json index c3c6d739f136..5b7b3bbf1cb4 100644 --- a/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_function_def2.json +++ b/language-server/modules/langserver-core/src/test/resources/hover/configs/hover_function_def2.json @@ -10,7 +10,7 @@ "result": { "contents": { "kind": "markdown", - "value": "Description\n \n \n--- \n \n### Parameters \n \n`int` ***param1*** : param1 Parameter Description \n`int` ***param2*** : param2 Parameter Description \n`string` ***param3*** : param3 Parameter Description`(default: \"\")` \n`int[]...` ***restparam*** : restparam Parameter Description \n \n--- \n \n### Return \n`int` : Return Value Description" + "value": "Description\n \n \n--- \n \n### Parameters \n \n`int` ***param1*** : param1 Parameter Description \n`int` ***param2*** : param2 Parameter Description \n`string` ***param3*** : param3 Parameter Description`(default: \"\")` \n`int...` ***restparam*** : restparam Parameter Description \n \n--- \n \n### Return \n`int` : Return Value Description" } }, "id": { diff --git a/language-server/modules/langserver-core/src/test/resources/hover/source/hover_for_api_docs_negative.bal b/language-server/modules/langserver-core/src/test/resources/hover/source/hover_for_api_docs_negative.bal deleted file mode 100644 index 5ce0cfe1b655..000000000000 --- a/language-server/modules/langserver-core/src/test/resources/hover/source/hover_for_api_docs_negative.bal +++ /dev/null @@ -1,18 +0,0 @@ -public client class Client { - public string url; - - public function init(string url) { - self.url = url; - } - - resource function post path2/[string id]/path4(string str, int a) returns string { - return ""; - } - -} - - -public function test() { - Client cl = new (""); - string a = cl-> /path2/[""]/path4.post() -} diff --git a/language-server/modules/langserver-core/src/test/resources/hover/source/hover_for_resource_path.bal b/language-server/modules/langserver-core/src/test/resources/hover/source/hover_for_resource_path.bal new file mode 100644 index 000000000000..a582367ae35c --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/hover/source/hover_for_resource_path.bal @@ -0,0 +1,32 @@ +client class Client { + # Test Description. + # + # + param1 - parameter description + # + param2 - parameter description + # + param3 - parameter description + # + return - return value description + resource function post path/[string param1]/path2/[string ...param2](string param3, int ...param4) returns string { + return "post"; + } + + # Description. + # + # + ids - parameter description + # + return - return value description + resource function get [int... ids]() returns string { + return "get"; + } + + # Description. + # + return - return value description + resource function get path2/[int... ]() returns string { + return "get"; + } +} + +public function test() { + Client cli = new Client(); + string stringResult = cli->/path/[""]/path2/[""].post("", 1); + string stringResult2 = cli->/[0]; + string stringResult3 = cli->/[0]; +} diff --git a/language-server/modules/langserver-core/src/test/resources/packages/components/project-exception_expected.json b/language-server/modules/langserver-core/src/test/resources/packages/components/project-exception_expected.json new file mode 100644 index 000000000000..4677642a3ac7 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/packages/components/project-exception_expected.json @@ -0,0 +1,3 @@ +{ + "result": [] +} diff --git a/misc/compiler-plugins/modules/configurable-schema-generator/src/test/resources/DefaultModuleProjects/ComplexTypeConfigs2/expected-schema.json b/misc/compiler-plugins/modules/configurable-schema-generator/src/test/resources/DefaultModuleProjects/ComplexTypeConfigs2/expected-schema.json index 4ecaf0f1979d..38666331f1d7 100644 --- a/misc/compiler-plugins/modules/configurable-schema-generator/src/test/resources/DefaultModuleProjects/ComplexTypeConfigs2/expected-schema.json +++ b/misc/compiler-plugins/modules/configurable-schema-generator/src/test/resources/DefaultModuleProjects/ComplexTypeConfigs2/expected-schema.json @@ -140,12 +140,31 @@ } }, "description": "" + }, + "employeeReadOnly": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "salary": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "name", + "salary" + ], + "name": "dilhashanazeer/complexconfigs2:0.1.0:EmployeeReadOnly", + "description": "" } }, "additionalProperties": false, "required": [ "employeeArray", - "empMapArray" + "empMapArray", + "employeeReadOnly" ] } }, diff --git a/misc/compiler-plugins/modules/configurable-schema-generator/src/test/resources/DefaultModuleProjects/ComplexTypeConfigs2/main.bal b/misc/compiler-plugins/modules/configurable-schema-generator/src/test/resources/DefaultModuleProjects/ComplexTypeConfigs2/main.bal index b853b3a28d88..13cc853ef4f8 100644 --- a/misc/compiler-plugins/modules/configurable-schema-generator/src/test/resources/DefaultModuleProjects/ComplexTypeConfigs2/main.bal +++ b/misc/compiler-plugins/modules/configurable-schema-generator/src/test/resources/DefaultModuleProjects/ComplexTypeConfigs2/main.bal @@ -10,6 +10,11 @@ type Employee record { int salary; }; +type EmployeeReadOnly readonly & record { + string name; + int salary; +}; + configurable table key(name) t = table [ { name: "John", salary: 100 }, { name: "Jane", salary: 200 } @@ -22,6 +27,7 @@ configurable map[] myArray = [{"val1" : 22}, {"val2" : 32}]; configurable Employee[] employeeArray = ?; configurable map employeeMap = {"emp1": {name: "user1", salary: 1200}}; configurable map[] empMapArray = ?; +configurable EmployeeReadOnly employeeReadOnly = ?; public function main() { } diff --git a/misc/docerina/src/main/java/org/ballerinalang/docgen/Generator.java b/misc/docerina/src/main/java/org/ballerinalang/docgen/Generator.java index eb3a3f1182a9..ef1ef58691d7 100644 --- a/misc/docerina/src/main/java/org/ballerinalang/docgen/Generator.java +++ b/misc/docerina/src/main/java/org/ballerinalang/docgen/Generator.java @@ -167,8 +167,14 @@ public static boolean setModuleFromSyntaxTree(Module module, SyntaxTree syntaxTr ((ModuleVariableDeclarationNode) node).visibilityQualifier().isPresent() && ((ModuleVariableDeclarationNode) node).visibilityQualifier().get().kind() .equals(SyntaxKind.PUBLIC_KEYWORD)) { - module.variables.add(getModuleVariable((ModuleVariableDeclarationNode) node, semanticModel, - module)); + DefaultableVariable defaultableVariable = getModuleVariable((ModuleVariableDeclarationNode) node, + semanticModel, module); + if (containsToken(((ModuleVariableDeclarationNode) node).qualifiers(), + SyntaxKind.CONFIGURABLE_KEYWORD)) { + module.configurables.add(defaultableVariable); + } else { + module.variables.add(defaultableVariable); + } } } } diff --git a/misc/docerina/src/main/java/org/ballerinalang/docgen/generator/model/Module.java b/misc/docerina/src/main/java/org/ballerinalang/docgen/generator/model/Module.java index 1f6d4a57619e..0a626c879580 100644 --- a/misc/docerina/src/main/java/org/ballerinalang/docgen/generator/model/Module.java +++ b/misc/docerina/src/main/java/org/ballerinalang/docgen/generator/model/Module.java @@ -76,7 +76,7 @@ public class Module extends ModuleMetaData { public List anyDataTypes = new ArrayList<>(); @Expose public List anyTypes = new ArrayList<>(); - + @Expose public List stringTypes = new ArrayList<>(); @Expose public List integerTypes = new ArrayList<>(); @@ -88,6 +88,8 @@ public class Module extends ModuleMetaData { public List enums = new ArrayList<>(); @Expose public List variables = new ArrayList<>(); - + @Expose + public List configurables = new ArrayList<>(); + @Expose public List resources = new ArrayList<>(); } diff --git a/misc/formatter/modules/formatter-core/src/main/java/org/ballerinalang/formatter/core/FormattingTreeModifier.java b/misc/formatter/modules/formatter-core/src/main/java/org/ballerinalang/formatter/core/FormattingTreeModifier.java index f37792e9df3b..daf03770d9c9 100644 --- a/misc/formatter/modules/formatter-core/src/main/java/org/ballerinalang/formatter/core/FormattingTreeModifier.java +++ b/misc/formatter/modules/formatter-core/src/main/java/org/ballerinalang/formatter/core/FormattingTreeModifier.java @@ -245,6 +245,7 @@ import io.ballerina.compiler.syntax.tree.XMLStartTagNode; import io.ballerina.compiler.syntax.tree.XMLStepExpressionNode; import io.ballerina.compiler.syntax.tree.XMLTextNode; +import io.ballerina.tools.text.LinePosition; import io.ballerina.tools.text.LineRange; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -4234,10 +4235,12 @@ private MinutiaeList getLeadingMinutiae(Token token) { // Therefore, increase the 'consecutiveNewlines' count consecutiveNewlines++; - for (int i = 0; i < env.leadingNL; i++) { - prevMinutiae = getNewline(); - leadingMinutiae.add(prevMinutiae); - consecutiveNewlines++; + if (!token.isMissing()) { + for (int i = 0; i < env.leadingNL; i++) { + prevMinutiae = getNewline(); + leadingMinutiae.add(prevMinutiae); + consecutiveNewlines++; + } } } @@ -4257,7 +4260,8 @@ private MinutiaeList getLeadingMinutiae(Token token) { // Shouldn't update the prevMinutiae continue; } - if (env.preserveIndentation) { + if (env.preserveIndentation && + (prevMinutiae == null || prevMinutiae.kind() == SyntaxKind.END_OF_LINE_MINUTIAE)) { addWhitespace(getPreservedIndentation(token), leadingMinutiae); } else { addWhitespace(1, leadingMinutiae); @@ -4355,7 +4359,9 @@ private MinutiaeList getTrailingMinutiae(Token token) { // Preserve the necessary trailing minutiae coming from the original token int consecutiveNewlines = 0; - for (Minutiae minutiae : token.trailingMinutiae()) { + int size = token.trailingMinutiae().size(); + for (int i = 0; i < size; i++) { + Minutiae minutiae = token.trailingMinutiae().get(i); switch (minutiae.kind()) { case END_OF_LINE_MINUTIAE: preserveIndentation(true); @@ -4369,16 +4375,26 @@ private MinutiaeList getTrailingMinutiae(Token token) { continue; } - addWhitespace(env.trailingWS, trailingMinutiae); + // We reach here when the prevMinutiae is an invalid node/token + if (i == size - 1) { + addWhitespace(env.trailingWS, trailingMinutiae); + } else { + addWhitespace(1, trailingMinutiae); + } break; case COMMENT_MINUTIAE: - addWhitespace(1, trailingMinutiae); + if (!matchesMinutiaeKind(prevMinutiae, SyntaxKind.WHITESPACE_MINUTIAE)) { + addWhitespace(1, trailingMinutiae); + } trailingMinutiae.add(minutiae); consecutiveNewlines = 0; break; case INVALID_TOKEN_MINUTIAE_NODE: case INVALID_NODE_MINUTIAE: default: + if (matchesMinutiaeKind(prevMinutiae, SyntaxKind.END_OF_LINE_MINUTIAE)) { + addWhitespace(env.currentIndentation, trailingMinutiae); + } trailingMinutiae.add(minutiae); consecutiveNewlines = 0; break; @@ -4480,18 +4496,23 @@ private void preserveIndentation(boolean value) { * @param token token of which the indentation is required. */ private int getPreservedIndentation(Token token) { - int position = token.lineRange().startLine().offset(); + LinePosition startLinePos = token.lineRange().startLine(); + int position = startLinePos.offset(); + int startLine = startLinePos.line(); + for (Token invalidToken : token.leadingInvalidTokens()) { + LinePosition invalidTokenStartLinePos = invalidToken.lineRange().startLine(); + if (invalidTokenStartLinePos.line() == startLine) { + position = invalidTokenStartLinePos.offset(); + break; + } + } int tabSize = options.getTabSize(); - int offset = position % tabSize; if (env.currentIndentation % tabSize == 0 && env.currentIndentation > position) { return env.currentIndentation; } + int offset = position % tabSize; if (offset != 0) { - if (offset > 2) { - position = position + tabSize - offset; - } else { - position = position - offset; - } + return offset > 2 ? position + tabSize - offset : position - offset; } return position; } @@ -4687,6 +4708,10 @@ private boolean hasTrailingNL(Token token) { return false; } + private boolean matchesMinutiaeKind(Minutiae minutiae, SyntaxKind kind) { + return minutiae != null && minutiae.kind() == kind; + } + private NodeList sortAndGroupImportDeclarationNodes( NodeList importDeclarationNodes) { // moduleImports would collect only module level imports if grouping is enabled, diff --git a/misc/formatter/modules/formatter-core/src/test/java/org/ballerinalang/formatter/core/ParserTestFormatter.java b/misc/formatter/modules/formatter-core/src/test/java/org/ballerinalang/formatter/core/ParserTestFormatter.java index 449b6ded79c9..54d2f79ccaea 100644 --- a/misc/formatter/modules/formatter-core/src/test/java/org/ballerinalang/formatter/core/ParserTestFormatter.java +++ b/misc/formatter/modules/formatter-core/src/test/java/org/ballerinalang/formatter/core/ParserTestFormatter.java @@ -89,11 +89,6 @@ public List skipList() { "service_decl_source_02.bal", "service_decl_source_05.bal", "service_decl_source_17.bal", "service_decl_source_20.bal", - // The following tests are disabled due to tokens merging together after formatting. issue #35240 - "query_expr_source_121.bal", "query_expr_source_123.bal", "query_expr_source_124.bal", - "query_expr_source_126.bal", "match_stmt_source_21.bal", - "func_params_source_27.bal", - "separated_node_list_import_decl.bal", "node_location_test_03.bal", // parser tests with syntax errors that cannot be handled by the formatter diff --git a/misc/formatter/modules/formatter-core/src/test/resources/actions/query/assert/query_action_4.bal b/misc/formatter/modules/formatter-core/src/test/resources/actions/query/assert/query_action_4.bal new file mode 100644 index 000000000000..f12c66c254d3 --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/actions/query/assert/query_action_4.bal @@ -0,0 +1,5 @@ +public function main() { + var c = from var i in 0 ... 5 + select i + where i %2 != 0; +} diff --git a/misc/formatter/modules/formatter-core/src/test/resources/actions/query/source/query_action_4.bal b/misc/formatter/modules/formatter-core/src/test/resources/actions/query/source/query_action_4.bal new file mode 100644 index 000000000000..8a3c6a2286e4 --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/actions/query/source/query_action_4.bal @@ -0,0 +1,5 @@ +public function main() { + var c = from var i in 0 ... 5 + select i + where i %2 != 0 ; +} diff --git a/misc/formatter/modules/formatter-core/src/test/resources/declarations/function-definition/assert/function_definition_declaration_3.bal b/misc/formatter/modules/formatter-core/src/test/resources/declarations/function-definition/assert/function_definition_declaration_3.bal new file mode 100644 index 000000000000..0a1ef5d2fc9e --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/declarations/function-definition/assert/function_definition_declaration_3.bal @@ -0,0 +1,14 @@ +function f { + if + worker A { + + } +} + +function g { + if + worker + B { + + } +} diff --git a/misc/formatter/modules/formatter-core/src/test/resources/declarations/function-definition/source/function_definition_declaration_3.bal b/misc/formatter/modules/formatter-core/src/test/resources/declarations/function-definition/source/function_definition_declaration_3.bal new file mode 100644 index 000000000000..e7231188cebd --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/declarations/function-definition/source/function_definition_declaration_3.bal @@ -0,0 +1,14 @@ +function f { + if + worker A{ + + } +} + +function g { + if + worker + B { + + } +} diff --git a/misc/formatter/modules/formatter-core/src/test/resources/statements/do/assert/do_statement_3.bal b/misc/formatter/modules/formatter-core/src/test/resources/statements/do/assert/do_statement_3.bal new file mode 100644 index 000000000000..305ea80f9fbe --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/statements/do/assert/do_statement_3.bal @@ -0,0 +1,11 @@ +function baz() { + int x = 1; + do { + int b = 2; + } on f + + if + while x < 5 { + x += 1; + } + } diff --git a/misc/formatter/modules/formatter-core/src/test/resources/statements/do/source/do_statement_3.bal b/misc/formatter/modules/formatter-core/src/test/resources/statements/do/source/do_statement_3.bal new file mode 100644 index 000000000000..1faa51134160 --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/statements/do/source/do_statement_3.bal @@ -0,0 +1,11 @@ +function baz() { + int x = 1; + do { + int b = 2; + } on f + + if + while x < 5 { + x += 1; + } +} diff --git a/misc/identifier-util/src/main/java/io/ballerina/identifier/Utils.java b/misc/identifier-util/src/main/java/io/ballerina/identifier/Utils.java index d0a5916552bd..1457dc1248e7 100644 --- a/misc/identifier-util/src/main/java/io/ballerina/identifier/Utils.java +++ b/misc/identifier-util/src/main/java/io/ballerina/identifier/Utils.java @@ -33,7 +33,7 @@ public class Utils { private static final String UNICODE_REGEX = "\\\\(\\\\*)u\\{([a-fA-F0-9]+)\\}"; public static final Pattern UNICODE_PATTERN = Pattern.compile(UNICODE_REGEX); - private static final String CHAR_PREFIX = "$"; + private static final String CHAR_PREFIX = "&"; private static final String ESCAPE_PREFIX = "\\"; private static final Pattern UNESCAPED_SPECIAL_CHAR_SET = Pattern.compile("([$&+,:;=\\?@#\\\\|/'\\ \\[\\}\\]<\\>" + ".\"^*{}~`()%!-])"); @@ -154,7 +154,7 @@ public static String decodeIdentifier(String encodedIdentifier) { StringBuilder sb = new StringBuilder(); int index = 0; while (index < encodedIdentifier.length()) { - if (encodedIdentifier.charAt(index) == '$' && index + 4 < encodedIdentifier.length()) { + if (encodedIdentifier.charAt(index) == '&' && index + 4 < encodedIdentifier.length()) { if (isUnicodePoint(encodedIdentifier, index)) { sb.append((char) Integer.parseInt(encodedIdentifier.substring(index + 1, index + 5))); index += 5; @@ -259,13 +259,13 @@ public static String encodeFunctionIdentifier(String functionName) { functionName = encodeIdentifier(functionName); switch (functionName) { case ".": - return "$gen$$0046$0060init$0062"; + return "$gen$&0046&0060init&0062"; case ".": - return "$gen$$0046$0060start$0062"; + return "$gen$&0046&0060start&0062"; case ".": - return "$gen$$0046$0060stop$0062"; + return "$gen$&0046&0060stop&0062"; case ".": - return "$gen$$0046$0060testinit$0062"; + return "$gen$&0046&0060testinit&0062"; } Identifier encodedName = encodeGeneratedName(functionName); return encodedName.isEncoded ? GENERATED_METHOD_PREFIX + encodedName.name : functionName; diff --git a/misc/testerina/modules/testerina-core/src/main/ballerina/execute.bal b/misc/testerina/modules/testerina-core/src/main/ballerina/execute.bal index 4b1d86a679f1..adda3d0293a8 100644 --- a/misc/testerina/modules/testerina-core/src/main/ballerina/execute.bal +++ b/misc/testerina/modules/testerina-core/src/main/ballerina/execute.bal @@ -293,13 +293,10 @@ function skipDataDrivenTest(TestFunction testFunction, string suffix, TestType t if (!suffixMatch) { string[] subTests = filterSubTests.get(functionKey); foreach string subFilter in subTests { - string updatedSubFilter = subFilter; if (testType == DATA_DRIVEN_MAP_OF_TUPLE) { if (subFilter.startsWith(SINGLE_QUOTE) && subFilter.endsWith(SINGLE_QUOTE)) { updatedSubFilter = subFilter.substring(1, subFilter.length() - 1); - } else { - continue; } } string|error decodedSubFilter = escapeSpecialCharacters(updatedSubFilter); diff --git a/misc/testerina/modules/testerina-core/src/main/java/org/ballerinalang/testerina/core/MockAnnotationProcessor.java b/misc/testerina/modules/testerina-core/src/main/java/org/ballerinalang/testerina/core/MockAnnotationProcessor.java index 4535ce9d2173..1a60b64447c9 100644 --- a/misc/testerina/modules/testerina-core/src/main/java/org/ballerinalang/testerina/core/MockAnnotationProcessor.java +++ b/misc/testerina/modules/testerina-core/src/main/java/org/ballerinalang/testerina/core/MockAnnotationProcessor.java @@ -149,6 +149,8 @@ public void process(SimpleVariableNode simpleVariableNode, List # --> ` to registry @@ -257,6 +259,8 @@ public void process(FunctionNode functionNode, List an (BLangTestablePackage) ((BLangFunction) functionNode).parent; // parent -> BLangPackage bLangTestablePackage.addMockFunction(functionToMockID + MOCK_LEGACY_DELIMITER + vals[1], functionName); + bLangTestablePackage.addIsLegacyMockingMap(functionToMockID + MOCK_LEGACY_DELIMITER + vals[1], + true); // Adding ` # --> ` to registry String className = getQualifiedClassName(bLangTestablePackage, diff --git a/misc/testerina/modules/testerina-runtime/src/main/java/org/ballerinalang/test/runtime/entity/CoverageReport.java b/misc/testerina/modules/testerina-runtime/src/main/java/org/ballerinalang/test/runtime/entity/CoverageReport.java index b9f6ad5b7527..5fa822659155 100644 --- a/misc/testerina/modules/testerina-runtime/src/main/java/org/ballerinalang/test/runtime/entity/CoverageReport.java +++ b/misc/testerina/modules/testerina-runtime/src/main/java/org/ballerinalang/test/runtime/entity/CoverageReport.java @@ -77,17 +77,16 @@ public class CoverageReport { private final String title; private final Path coverageDir; - private Path executionDataFile; - private Path classesDirectory; - private ExecFileLoader execFileLoader; - private Module module; - private Target target; - private Map moduleCoverageMap; - private List packageNativeClassCoverageList; - private List packageBalClassCoverageList; - private List packageSourceCoverageList; - private List packageExecData; - private List sessionInfoList; + private final Path executionDataFile; + private final Path classesDirectory; + private final ExecFileLoader execFileLoader; + private final Module module; + private final Map moduleCoverageMap; + private final List packageNativeClassCoverageList; + private final List packageBalClassCoverageList; + private final List packageSourceCoverageList; + private final List packageExecData; + private final List sessionInfoList; public CoverageReport(Module module, Map moduleCoverageMap, List packageNativeClassCoverageList, @@ -95,7 +94,7 @@ public CoverageReport(Module module, Map moduleCoverageM List packageSourceCoverageList, List packageExecData, List sessionInfoList) throws IOException { this.module = module; - this.target = new Target(module.project().targetDir()); + Target target = new Target(module.project().targetDir()); this.coverageDir = target.getTestsCachePath().resolve(TesterinaConstants.COVERAGE_DIR); this.title = coverageDir.toFile().getName(); this.classesDirectory = coverageDir.resolve(TesterinaConstants.BIN_DIR); @@ -115,7 +114,7 @@ public CoverageReport(Module module, Map moduleCoverageM * @param jBallerinaBackend JBallerinaBackend * @param includesInCoverage boolean * @param exclusionClassList list of classes to be excluded - * @throws IOException + * @throws IOException if an error occurs while generating the report */ public void generateReport(JBallerinaBackend jBallerinaBackend, String includesInCoverage, String reportFormat, Module originalModule, Set exclusionClassList) @@ -182,7 +181,7 @@ private List getDependenciesForJacocoXML(JBallerinaBackend jBallerinaBacke * @param originalModule Module * @param exclusionClassList list of classes to be excluded * @return CoverageBuilder - * @throws IOException + * @throws IOException if an error occurs while generating the report */ private CoverageBuilder generateTesterinaCoverageReport(String orgName, String packageName, List filteredPathList, @@ -200,12 +199,8 @@ private CoverageBuilder generateTesterinaCoverageReport(String orgName, String p private List getGeneratedFilesToExclude(Module originalModules) { List excludedDocumentIds = new ArrayList<>(this.module.documentIds()); - List oldDocuementIds = new ArrayList<>(); - // Add all old document ids - for (DocumentId documentId : originalModules.documentIds()) { - oldDocuementIds.add(documentId); - } - excludedDocumentIds.removeAll(oldDocuementIds); + List oldDocumentIds = new ArrayList<>(originalModules.documentIds()); + excludedDocumentIds.removeAll(oldDocumentIds); return excludedDocumentIds; } @@ -247,9 +242,7 @@ private void updatePackageLevelCoverage(CoverageBuilder coverageBuilder) { } // Jacoco is capable of handling duplicated execution data, // so it is not needed to remove duplicates. - for (ExecutionData executionData : execFileLoader.getExecutionDataStore().getContents()) { - packageExecData.add(executionData); - } + packageExecData.addAll(execFileLoader.getExecutionDataStore().getContents()); } private boolean isExistingSessionInfo(SessionInfo sessionInfo) { @@ -276,7 +269,7 @@ private void removeFromCoverageList(IClassCoverage classCoverage) { coverageToRemove = coverage; } } - if (isExists && coverageToRemove != null) { + if (isExists) { packageNativeClassCoverageList.remove(coverageToRemove); } } @@ -336,7 +329,7 @@ private void createReport(final IBundleCoverage bundleCoverage, Map> coveredLinesList = moduleCoverage.getCoveredLinesList( sourceFileCoverage.getName()); - if (!missedLinesList.isEmpty() && !coveredLinesList.isEmpty()) { + if (missedLinesList.isPresent() && coveredLinesList.isPresent()) { List missedLines = missedLinesList.get(); List coveredLines = coveredLinesList.get(); List existingMissedLines = new ArrayList<>(missedLines); @@ -388,8 +381,8 @@ private void createReport(final IBundleCoverage bundleCoverage, Map moduleCoverageM modifiedDocument.textDocument().textLines()); if (!stringPatch.getDeltas().isEmpty()) { List> patchDeltas = stringPatch.getDeltas(); - List insertedLines = new ArrayList(); + List insertedLines = new ArrayList<>(); List modifiedLines = new ArrayList<>(); List deletedLines = new ArrayList<>(); for (AbstractDelta delta : patchDeltas) { @@ -463,7 +456,10 @@ private void filterGeneratedCoverage(Map moduleCoverageM } } else if (delta.getType().equals(DeltaType.DELETE)) { int lineNumber = delta.getSource().getPosition(); - deletedLines.add(lineNumber); + int size = delta.getSource().size(); + for (int i = lineNumber; i < lineNumber + size; i++) { + deletedLines.add(i); + } } } @@ -471,47 +467,43 @@ private void filterGeneratedCoverage(Map moduleCoverageM List coveredLinesList = moduleCoverage.getCoveredLinesList(modifiedDocument.name()).get(); List missedLinesList = moduleCoverage.getMissedLinesList(modifiedDocument.name()).get(); - // Populate lineStatus with zeroes - List lineStatus = new ArrayList<>(); + // create the modified document coverage list + List modifiedDocLineStatus = new ArrayList<>(); for (int i = 0; i < modifiedDocument.textDocument().textLines().size(); i++) { - lineStatus.add(0); - } - - // Replace line status with respective covered and missed statuses - for (Integer coveredLine : coveredLinesList) { - lineStatus.remove(coveredLine); - lineStatus.add(coveredLine, FULLY_COVERED); - } - for (Integer missedLine : missedLinesList) { - lineStatus.remove(missedLine); - lineStatus.add(missedLine, NOT_COVERED); + if (coveredLinesList.contains(i + 1)) { + modifiedDocLineStatus.add(FULLY_COVERED); + } else if (missedLinesList.contains(i + 1)) { + modifiedDocLineStatus.add(NOT_COVERED); + } else { + modifiedDocLineStatus.add(EMPTY); + } } - List newLineStatus = new ArrayList<>(); - for (int i = 0; i < lineStatus.size(); i++) { - if (insertedLines.contains(i)) { - continue; + // iterate the modified coverage list and map to the original document + List originalDocLineStatus = new ArrayList<>(); + for (int i = 0; i < modifiedDocument.textDocument().textLines().size(); i++) { + while (deletedLines.contains(originalDocLineStatus.size() + 1)) { + // if the next line is deleted in the source, we add the line status as empty + originalDocLineStatus.add(EMPTY); } - // If an empty line was modified or deleted, add the line status as empty if (modifiedLines.contains(i)) { - newLineStatus.add(EMPTY); - } else { - newLineStatus.add(lineStatus.get(i)); - } - // If a deleted line exists - if (deletedLines.contains(i)) { - newLineStatus.add(EMPTY); + // if the line is modified, we add the line status as empty + originalDocLineStatus.add(EMPTY); + } else if (!insertedLines.contains(i)) { + // if the line is not modified, nor inserted, we add the line status as it is + originalDocLineStatus.add(modifiedDocLineStatus.get(i)); } + // if the line is inserted, we ignore it } List newCoveredLines = new ArrayList<>(); List newMissedLines = new ArrayList<>(); // Go through line status and get the new covered and missed lines - for (int i = 0; i < newLineStatus.size(); i++) { - if (newLineStatus.get(i).equals(FULLY_COVERED)) { + for (int i = 0; i < originalDocLineStatus.size(); i++) { + if (originalDocLineStatus.get(i).equals(FULLY_COVERED)) { newCoveredLines.add(i + 1); - } else if (newLineStatus.get(i).equals(NOT_COVERED)) { + } else if (originalDocLineStatus.get(i).equals(NOT_COVERED)) { newMissedLines.add(i + 1); } } @@ -526,7 +518,6 @@ private void filterGeneratedCoverage(Map moduleCoverageM // Diff exception caught when diff cannot be calculated properly // NullPointer caught when a Generated Source File is passed or if its an empty file // continue to consider other files in the coverage - continue; } } } @@ -604,7 +595,6 @@ private List getPlatformLibsList(JBallerinaBackend jBallerinaBackend, Pack return platformLibsList; } - private List getDependencyJarList(JBallerinaBackend jBallerinaBackend) { List dependencyPathList = new ArrayList<>(); module.packageInstance().getResolution().allDependencies() @@ -630,5 +620,4 @@ private List getDependencyJarList(JBallerinaBackend jBallerinaBackend) { }); return dependencyPathList; } - } diff --git a/misc/testerina/modules/testerina-runtime/src/main/java/org/ballerinalang/test/runtime/util/TesterinaConstants.java b/misc/testerina/modules/testerina-runtime/src/main/java/org/ballerinalang/test/runtime/util/TesterinaConstants.java index bd2619450817..5e6084bd5391 100644 --- a/misc/testerina/modules/testerina-runtime/src/main/java/org/ballerinalang/test/runtime/util/TesterinaConstants.java +++ b/misc/testerina/modules/testerina-runtime/src/main/java/org/ballerinalang/test/runtime/util/TesterinaConstants.java @@ -25,7 +25,7 @@ */ public class TesterinaConstants { - public static final String DOT_REPLACER = "$0046"; + public static final String DOT_REPLACER = "&0046"; public static final String BALLERINA_SOURCE_ROOT = "ballerina.source.root"; public static final String TESTERINA_TEMP_DIR = ".testerina"; public static final String TESTERINA_TEST_SUITE = "test_suit.json"; diff --git a/project-api/project-api-test/build.gradle b/project-api/project-api-test/build.gradle index 2223fb792c02..9f3519c92800 100644 --- a/project-api/project-api-test/build.gradle +++ b/project-api/project-api-test/build.gradle @@ -65,6 +65,8 @@ dependencies { compilerPluginJar project(':project-api-test-artifact:bad-sad-compiler-plugin') compilerPluginJar project(':project-api-test-artifact:init-function-codegen-compiler-plugin') compilerPluginJar project(':project-api-test-artifact:init-function-code-modify-compiler-plugin') + compilerPluginJar project(':project-api-test-artifact:remove-function-code-modify-compiler-plugin') + compilerPluginJar project(':project-api-test-artifact:add-remove-function-code-modify-compiler-plugin') compilerPluginJar project(':compiler-plugins:package-semantic-analyzer') compilerPluginJar project(':project-api-test-artifact:init-function-diagnostic-compiler-plugin') compilerPluginJar project(':project-api-test-artifact:compiler-plugin-with-analyzer-generator-modifier') diff --git a/project-api/test-artifacts/add-remove-function-code-modify-compiler-plugin/build.gradle b/project-api/test-artifacts/add-remove-function-code-modify-compiler-plugin/build.gradle new file mode 100644 index 000000000000..34b3532abb7d --- /dev/null +++ b/project-api/test-artifacts/add-remove-function-code-modify-compiler-plugin/build.gradle @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, WSO2 Inc. (http://www.wso2.org) 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. + * + */ + +apply from: "$rootDir/gradle/javaProject.gradle" + +description = 'Compiler Plugin Tests - Code Modify Add And Remove Function' +version = '1.0.0' + +dependencies { + implementation project(':ballerina-lang') + implementation project(':ballerina-parser') + implementation project(':ballerina-tools-api') + implementation project(':project-api-test-artifact:diagnostic-utils-lib') +} + +ext.moduleName = 'compiler.plugin.test.addremove.func.codemodify' + +compileJava { + doFirst { + options.compilerArgs = [ + '--module-path', classpath.asPath, + ] + classpath = files() + } +} diff --git a/project-api/test-artifacts/add-remove-function-code-modify-compiler-plugin/src/main/java/io/samjs/plugins/addremove/codemodify/AddRemoveFunctionsCodeModifier.java b/project-api/test-artifacts/add-remove-function-code-modify-compiler-plugin/src/main/java/io/samjs/plugins/addremove/codemodify/AddRemoveFunctionsCodeModifier.java new file mode 100644 index 000000000000..ea873d9f983b --- /dev/null +++ b/project-api/test-artifacts/add-remove-function-code-modify-compiler-plugin/src/main/java/io/samjs/plugins/addremove/codemodify/AddRemoveFunctionsCodeModifier.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2024, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 io.samjs.plugins.addremove.codemodify; + +import io.ballerina.compiler.syntax.tree.FunctionBodyBlockNode; +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.FunctionSignatureNode; +import io.ballerina.compiler.syntax.tree.MinutiaeList; +import io.ballerina.compiler.syntax.tree.ModuleMemberDeclarationNode; +import io.ballerina.compiler.syntax.tree.ModulePartNode; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.OptionalTypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.RequiredParameterNode; +import io.ballerina.compiler.syntax.tree.ReturnTypeDescriptorNode; +import io.ballerina.compiler.syntax.tree.SimpleNameReferenceNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.compiler.syntax.tree.SyntaxTree; +import io.ballerina.compiler.syntax.tree.Token; +import io.ballerina.projects.Document; +import io.ballerina.projects.DocumentId; +import io.ballerina.projects.ModuleId; +import io.ballerina.projects.plugins.CodeModifier; +import io.ballerina.projects.plugins.CodeModifierContext; + +import java.util.ArrayList; +import java.util.List; + +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyMinutiaeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createMinutiaeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createSeparatedNodeList; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken; +import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createWhitespaceMinutiae; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createFunctionBodyBlockNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createFunctionDefinitionNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createFunctionSignatureNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createOptionalTypeDescriptorNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createParameterizedTypeDescriptorNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createRequiredParameterNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createReturnTypeDescriptorNode; +import static io.ballerina.compiler.syntax.tree.NodeFactory.createSimpleNameReferenceNode; + +/** + * A {@code CodeModifier} implementation that modify each bal file by adding a specific function to the end. + * + * @since 2201.9.0 + */ +public class AddRemoveFunctionsCodeModifier extends CodeModifier { + + @Override + public void init(CodeModifierContext modifierContext) { + modifierContext.addSourceModifierTask(context -> { + for (ModuleId moduleId : context.currentPackage().moduleIds()) { + io.ballerina.projects.Module module = context.currentPackage().module(moduleId); + for (DocumentId documentId : module.documentIds()) { + Document document = module.document(documentId); + ModulePartNode rootNode = document.syntaxTree().rootNode(); + + // Remove empty functions + List emptyFunctionNodes = rootNode.members().stream() + .filter(member -> member.kind() == SyntaxKind.FUNCTION_DEFINITION) + .filter(member -> ((FunctionDefinitionNode) member).functionBody().children().size() < 3) + .toList(); + NodeList updatedMembers = + rootNode.members().removeAll(emptyFunctionNodes); + + // Add new function + updatedMembers = updatedMembers.add(createFunctionDefNode(document)); + ModulePartNode newModulePart = rootNode.modify( + rootNode.imports(), updatedMembers, rootNode.eofToken()); + SyntaxTree updatedSyntaxTree = document.syntaxTree().modifyWith(newModulePart); + context.modifySourceFile(updatedSyntaxTree.textDocument(), documentId); + } + } + }); + } + + private static FunctionDefinitionNode createFunctionDefNode(Document document) { + List qualifierList = new ArrayList<>(); + Token publicToken = createToken(SyntaxKind.PUBLIC_KEYWORD, generateMinutiaeListWithTwoNewline(), + generateMinutiaeListWithWhitespace()); + qualifierList.add(publicToken); + SimpleNameReferenceNode simpleNameRefNode = createSimpleNameReferenceNode( + createIdentifierToken("string", createEmptyMinutiaeList(), + generateMinutiaeListWithWhitespace())); + RequiredParameterNode requiredParameterNode = + createRequiredParameterNode(createEmptyNodeList(), simpleNameRefNode, + createIdentifierToken("params")); + OptionalTypeDescriptorNode optionalErrorTypeDescriptorNode = + createOptionalTypeDescriptorNode( + createParameterizedTypeDescriptorNode(SyntaxKind.ERROR_TYPE_DESC, + createToken(SyntaxKind.ERROR_KEYWORD), null), + createToken(SyntaxKind.QUESTION_MARK_TOKEN, createEmptyMinutiaeList(), + generateMinutiaeListWithWhitespace())); + ReturnTypeDescriptorNode returnTypeDescriptorNode = + createReturnTypeDescriptorNode(createToken(SyntaxKind.RETURNS_KEYWORD, + createEmptyMinutiaeList(), generateMinutiaeListWithWhitespace()), + createEmptyNodeList(), optionalErrorTypeDescriptorNode); + FunctionSignatureNode functionSignatureNode = + createFunctionSignatureNode(createToken(SyntaxKind.OPEN_PAREN_TOKEN), + createSeparatedNodeList(requiredParameterNode), + createToken(SyntaxKind.CLOSE_PAREN_TOKEN, + createEmptyMinutiaeList(), generateMinutiaeListWithWhitespace()), + returnTypeDescriptorNode); + FunctionBodyBlockNode emptyFunctionBodyNode = + createFunctionBodyBlockNode( + createToken(SyntaxKind.OPEN_BRACE_TOKEN, createEmptyMinutiaeList(), + generateMinutiaeListWithNewline()), null, + createEmptyNodeList(), createToken(SyntaxKind.CLOSE_BRACE_TOKEN), null); + return createFunctionDefinitionNode( + SyntaxKind.FUNCTION_DEFINITION, null, createNodeList(qualifierList), + createToken(SyntaxKind.FUNCTION_KEYWORD, createEmptyMinutiaeList(), + generateMinutiaeListWithWhitespace()), + createIdentifierToken("newFunctionByCodeModifier" + + document.name().replace(".bal", "").replace("/", "_") + .replace("-", "_")), + createEmptyNodeList(), functionSignatureNode, emptyFunctionBodyNode); + } + + private static MinutiaeList generateMinutiaeListWithWhitespace() { + return createMinutiaeList(createWhitespaceMinutiae(" ")); + } + + private static MinutiaeList generateMinutiaeListWithNewline() { + return createMinutiaeList(createWhitespaceMinutiae("\n")); + } + + private static MinutiaeList generateMinutiaeListWithTwoNewline() { + return createMinutiaeList(createWhitespaceMinutiae("\n\n")); + } +} diff --git a/project-api/test-artifacts/add-remove-function-code-modify-compiler-plugin/src/main/java/io/samjs/plugins/addremove/codemodify/CodeModifyFunctionPlugin.java b/project-api/test-artifacts/add-remove-function-code-modify-compiler-plugin/src/main/java/io/samjs/plugins/addremove/codemodify/CodeModifyFunctionPlugin.java new file mode 100644 index 000000000000..a28881f24edf --- /dev/null +++ b/project-api/test-artifacts/add-remove-function-code-modify-compiler-plugin/src/main/java/io/samjs/plugins/addremove/codemodify/CodeModifyFunctionPlugin.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 io.samjs.plugins.addremove.codemodify; + +import io.ballerina.projects.plugins.CompilerPlugin; +import io.ballerina.projects.plugins.CompilerPluginContext; + +/** + * A sample {@code CompilerPlugin} that modifies source files by removing empty functions and adding new function. + * + * @since 2201.9.0 + */ +public class CodeModifyFunctionPlugin extends CompilerPlugin { + @Override + public void init(CompilerPluginContext pluginContext) { + pluginContext.addCodeModifier(new AddRemoveFunctionsCodeModifier()); + } +} diff --git a/project-api/test-artifacts/add-remove-function-code-modify-compiler-plugin/src/main/java/module-info.java b/project-api/test-artifacts/add-remove-function-code-modify-compiler-plugin/src/main/java/module-info.java new file mode 100644 index 000000000000..03c5aa00d214 --- /dev/null +++ b/project-api/test-artifacts/add-remove-function-code-modify-compiler-plugin/src/main/java/module-info.java @@ -0,0 +1,8 @@ +module compiler.plugin.test.addremove.func.codemodify { + requires io.ballerina.lang; + requires io.ballerina.parser; + requires io.ballerina.tools.api; + requires compiler.plugin.test.diagnostic.utils.lib; + + exports io.samjs.plugins.addremove.codemodify; +} diff --git a/project-api/test-artifacts/remove-function-code-modify-compiler-plugin/build.gradle b/project-api/test-artifacts/remove-function-code-modify-compiler-plugin/build.gradle new file mode 100644 index 000000000000..86455f3cacb8 --- /dev/null +++ b/project-api/test-artifacts/remove-function-code-modify-compiler-plugin/build.gradle @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, WSO2 Inc. (http://www.wso2.org) 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. + * + */ + +apply from: "$rootDir/gradle/javaProject.gradle" + +description = 'Compiler Plugin Tests - Code Modify Remove Function' +version = '1.0.0' + +dependencies { + implementation project(':ballerina-lang') + implementation project(':ballerina-parser') + implementation project(':ballerina-tools-api') + implementation project(':project-api-test-artifact:diagnostic-utils-lib') +} + +ext.moduleName = 'compiler.plugin.test.remove.func.codemodify' + +compileJava { + doFirst { + options.compilerArgs = [ + '--module-path', classpath.asPath, + ] + classpath = files() + } +} diff --git a/project-api/test-artifacts/remove-function-code-modify-compiler-plugin/src/main/java/io/samjs/plugins/remove/codemodify/CodeModifyFunctionPlugin.java b/project-api/test-artifacts/remove-function-code-modify-compiler-plugin/src/main/java/io/samjs/plugins/remove/codemodify/CodeModifyFunctionPlugin.java new file mode 100644 index 000000000000..fd28a4b25a47 --- /dev/null +++ b/project-api/test-artifacts/remove-function-code-modify-compiler-plugin/src/main/java/io/samjs/plugins/remove/codemodify/CodeModifyFunctionPlugin.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 io.samjs.plugins.remove.codemodify; + +import io.ballerina.projects.plugins.CompilerPlugin; +import io.ballerina.projects.plugins.CompilerPluginContext; + +/** + * A sample {@code CompilerPlugin} that modifies source files. + * + * @since 2201.9.0 + */ +public class CodeModifyFunctionPlugin extends CompilerPlugin { + @Override + public void init(CompilerPluginContext pluginContext) { + pluginContext.addCodeModifier(new RemoveFunctionCodeModifier()); + } +} diff --git a/project-api/test-artifacts/remove-function-code-modify-compiler-plugin/src/main/java/io/samjs/plugins/remove/codemodify/RemoveFunctionCodeModifier.java b/project-api/test-artifacts/remove-function-code-modify-compiler-plugin/src/main/java/io/samjs/plugins/remove/codemodify/RemoveFunctionCodeModifier.java new file mode 100644 index 000000000000..4ec36b235ad0 --- /dev/null +++ b/project-api/test-artifacts/remove-function-code-modify-compiler-plugin/src/main/java/io/samjs/plugins/remove/codemodify/RemoveFunctionCodeModifier.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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 io.samjs.plugins.remove.codemodify; + +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.ModuleMemberDeclarationNode; +import io.ballerina.compiler.syntax.tree.ModulePartNode; +import io.ballerina.compiler.syntax.tree.NodeList; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.compiler.syntax.tree.SyntaxTree; +import io.ballerina.projects.Document; +import io.ballerina.projects.DocumentId; +import io.ballerina.projects.ModuleId; +import io.ballerina.projects.Package; +import io.ballerina.projects.plugins.CodeModifier; +import io.ballerina.projects.plugins.CodeModifierContext; + +import java.util.Optional; + +/** + * A {@code CodeModifier} implementation that modify each bal file by removing empty functions. + * + * @since 2201.9.0 + */ +public class RemoveFunctionCodeModifier extends CodeModifier { + + @Override + public void init(CodeModifierContext modifierContext) { + modifierContext.addSourceModifierTask(sourceModifierContext -> { + // Look for the first function with name "bar" in each bal file and remove if available + Package currentPackage = sourceModifierContext.currentPackage(); + for (ModuleId moduleId : currentPackage.moduleIds()) { + io.ballerina.projects.Module module = currentPackage.module(moduleId); + for (DocumentId documentId : module.documentIds()) { + Document document = module.document(documentId); + ModulePartNode rootNode = document.syntaxTree().rootNode(); + Optional barFunctionNode = rootNode.members().stream() + .filter(member -> member.kind() == SyntaxKind.FUNCTION_DEFINITION + && "bar".equals(((FunctionDefinitionNode) member).functionName().text())) + .findFirst(); + if (barFunctionNode.isEmpty()) { + continue; + } + NodeList updatedMembers = + rootNode.members().remove(barFunctionNode.get()); + ModulePartNode newModulePart = + rootNode.modify(rootNode.imports(), updatedMembers, rootNode.eofToken()); + SyntaxTree updatedSyntaxTree = document.syntaxTree().modifyWith(newModulePart); + sourceModifierContext.modifySourceFile(updatedSyntaxTree.textDocument(), documentId); + } + } + }); + } +} diff --git a/project-api/test-artifacts/remove-function-code-modify-compiler-plugin/src/main/java/module-info.java b/project-api/test-artifacts/remove-function-code-modify-compiler-plugin/src/main/java/module-info.java new file mode 100644 index 000000000000..494df1a7b82f --- /dev/null +++ b/project-api/test-artifacts/remove-function-code-modify-compiler-plugin/src/main/java/module-info.java @@ -0,0 +1,8 @@ +module compiler.plugin.test.remove.func.codemodify { + requires io.ballerina.lang; + requires io.ballerina.parser; + requires io.ballerina.tools.api; + requires compiler.plugin.test.diagnostic.utils.lib; + + exports io.samjs.plugins.remove.codemodify; +} diff --git a/project-api/test-artifacts/sample-openapi-build-tool/src/main/java/build/tool/runner/SampleToolRunner.java b/project-api/test-artifacts/sample-openapi-build-tool/src/main/java/build/tool/runner/SampleToolRunner.java index f5dc918fc1f5..357e76a885f6 100644 --- a/project-api/test-artifacts/sample-openapi-build-tool/src/main/java/build/tool/runner/SampleToolRunner.java +++ b/project-api/test-artifacts/sample-openapi-build-tool/src/main/java/build/tool/runner/SampleToolRunner.java @@ -18,8 +18,8 @@ package build.tool.runner; -import io.ballerina.cli.tool.CodeGeneratorTool; -import io.ballerina.projects.ToolContext; +import io.ballerina.projects.buildtools.CodeGeneratorTool; +import io.ballerina.projects.buildtools.ToolContext; import io.ballerina.tools.diagnostics.DiagnosticFactory; import io.ballerina.tools.diagnostics.DiagnosticInfo; import io.ballerina.tools.diagnostics.DiagnosticSeverity; @@ -44,7 +44,7 @@ public void execute(ToolContext toolContext) { "The provided filePath does not exist", DiagnosticSeverity.ERROR); toolContext.reportDiagnostic(DiagnosticFactory.createDiagnostic(diagnosticInfo, new NullLocation())); } - System.out.println("Running sample build tool: " + toolContext.toolType()); + System.out.println("Running sample build tool: " + toolContext.toolId()); System.out.println("Cache created at: " + toolContext.cachePath()); } diff --git a/project-api/test-artifacts/sample-openapi-build-tool/src/main/resources/META-INF/services/io.ballerina.cli.tool.CodeGeneratorTool b/project-api/test-artifacts/sample-openapi-build-tool/src/main/resources/META-INF/services/io.ballerina.projects.buildtools.CodeGeneratorTool similarity index 100% rename from project-api/test-artifacts/sample-openapi-build-tool/src/main/resources/META-INF/services/io.ballerina.cli.tool.CodeGeneratorTool rename to project-api/test-artifacts/sample-openapi-build-tool/src/main/resources/META-INF/services/io.ballerina.projects.buildtools.CodeGeneratorTool diff --git a/settings.gradle b/settings.gradle index e5d32276bcfc..758a7ef3a5d7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -129,6 +129,8 @@ include(':project-api-test-artifact:bad-sad-compiler-plugin') include(':project-api-test-artifact:logging-file-appender-plugin') include(':project-api-test-artifact:init-function-codegen-compiler-plugin') include(':project-api-test-artifact:init-function-code-modify-compiler-plugin') +include(':project-api-test-artifact:remove-function-code-modify-compiler-plugin') +include(':project-api-test-artifact:add-remove-function-code-modify-compiler-plugin') include(':project-api-test-artifact:log-creator-in-built-code-modifier') include(':project-api-test-artifact:log-creator-in-built-code-generator') include(':project-api-test-artifact:log-creator-in-built-code-analyzer') @@ -163,6 +165,8 @@ project(':project-api-test-artifact:bad-sad-compiler-plugin').projectDir = file( project(':project-api-test-artifact:logging-file-appender-plugin').projectDir = file('project-api/test-artifacts/logging-file-appender-plugin') project(':project-api-test-artifact:init-function-codegen-compiler-plugin').projectDir = file('project-api/test-artifacts/init-function-codegen-compiler-plugin') project(':project-api-test-artifact:init-function-code-modify-compiler-plugin').projectDir = file('project-api/test-artifacts/init-function-code-modify-compiler-plugin') +project(':project-api-test-artifact:remove-function-code-modify-compiler-plugin').projectDir = file('project-api/test-artifacts/remove-function-code-modify-compiler-plugin') +project(':project-api-test-artifact:add-remove-function-code-modify-compiler-plugin').projectDir = file('project-api/test-artifacts/add-remove-function-code-modify-compiler-plugin') project(':project-api-test-artifact:log-creator-in-built-code-modifier').projectDir = file('project-api/test-artifacts/log-creator-in-built-code-modifier') project(':project-api-test-artifact:log-creator-in-built-code-generator').projectDir = file('project-api/test-artifacts/log-creator-in-built-code-generator') project(':project-api-test-artifact:log-creator-in-built-code-analyzer').projectDir = file('project-api/test-artifacts/log-creator-in-built-code-analyzer') diff --git a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/symbols/ErrorTypeSymbolTest.java b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/symbols/ErrorTypeSymbolTest.java index f14143d90a0b..c497f4573ccc 100644 --- a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/symbols/ErrorTypeSymbolTest.java +++ b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/symbols/ErrorTypeSymbolTest.java @@ -19,10 +19,15 @@ package io.ballerina.semantic.api.test.symbols; import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.impl.symbols.BallerinaErrorTypeSymbol; +import io.ballerina.compiler.api.impl.symbols.BallerinaIntersectionTypeSymbol; +import io.ballerina.compiler.api.impl.symbols.BallerinaRecordTypeSymbol; +import io.ballerina.compiler.api.impl.symbols.BallerinaTypeReferenceTypeSymbol; import io.ballerina.compiler.api.symbols.Documentable; import io.ballerina.compiler.api.symbols.Documentation; import io.ballerina.compiler.api.symbols.ErrorTypeSymbol; import io.ballerina.compiler.api.symbols.ModuleSymbol; +import io.ballerina.compiler.api.symbols.RecordFieldSymbol; import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.api.symbols.SymbolKind; import io.ballerina.compiler.api.symbols.TypeDefinitionSymbol; @@ -41,6 +46,7 @@ import org.wso2.ballerinalang.compiler.util.Names; import java.util.List; +import java.util.Map; import java.util.Optional; import static io.ballerina.semantic.api.test.util.SemanticAPITestUtils.getDefaultModulesSemanticModel; @@ -133,6 +139,46 @@ public Object[][] getTypeRefErrorType() { }; } + @Test(dataProvider = "IntersectionErrorTypeProvider") + public void testFieldDescriptorsOfErrorIntersection(int line, int offset, String expectedErrorType, + List expectedFieldNames) { + Optional symbol = model.symbol(srcFile, LinePosition.from(line, offset)); + assertTrue(symbol.isPresent()); + + Symbol ballerinaTypeDefinitionSymbol = symbol.get(); + assertEquals(ballerinaTypeDefinitionSymbol.kind(), SymbolKind.TYPE); + assertTrue(ballerinaTypeDefinitionSymbol.getName().isPresent()); + assertEquals(ballerinaTypeDefinitionSymbol.getName().get(), expectedErrorType); + + TypeSymbol ballerinaIntersectionTypeSymbol = + ((BallerinaTypeReferenceTypeSymbol) ballerinaTypeDefinitionSymbol).typeDescriptor(); + assertEquals(ballerinaIntersectionTypeSymbol.kind(), SymbolKind.TYPE); + + TypeSymbol ballerinaErrorTypeSymbol = + ((BallerinaIntersectionTypeSymbol) ballerinaIntersectionTypeSymbol).effectiveTypeDescriptor(); + assertEquals(ballerinaErrorTypeSymbol.kind(), SymbolKind.TYPE); + + TypeSymbol ballerinaRecordTypeSymbol = + ((BallerinaErrorTypeSymbol) ballerinaErrorTypeSymbol).detailTypeDescriptor(); + assertEquals(ballerinaRecordTypeSymbol.kind(), SymbolKind.TYPE); + + Map stringRecordFieldSymbolMap = + ((BallerinaRecordTypeSymbol) ballerinaRecordTypeSymbol).fieldDescriptors(); + List actualFieldNames = stringRecordFieldSymbolMap.keySet().stream().toList(); + assertEquals(actualFieldNames.size(), expectedFieldNames.size()); + for (int i = 0; i < actualFieldNames.size(); i++) { + assertEquals(actualFieldNames.get(i), expectedFieldNames.get(i)); + } + } + + @DataProvider(name = "IntersectionErrorTypeProvider") + public Object[][] getIntersectionErrorType() { + return new Object[][]{ + {62, 4, "SimpleIntersectionError", List.of("msg", "value")}, + {63, 4, "MultipleIntersectionError", List.of("flag", "msg", "value")} + }; + } + private void assertModuleInfo(ModuleSymbol module) { assertEquals(module.id().orgName(), Names.ANON_ORG.toString()); assertEquals(module.id().moduleName(), PackageID.DEFAULT.toString()); diff --git a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/typeof/TypeOfConditionalExprTest.java b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/typeof/TypeOfConditionalExprTest.java new file mode 100644 index 000000000000..10b596f22de3 --- /dev/null +++ b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/typeof/TypeOfConditionalExprTest.java @@ -0,0 +1,105 @@ +package io.ballerina.semantic.api.test.typeof; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.TypeDescKind; +import io.ballerina.compiler.api.symbols.TypeSymbol; +import io.ballerina.compiler.api.symbols.UnionTypeSymbol; +import io.ballerina.semantic.api.test.util.SemanticAPITestUtils; +import io.ballerina.tools.text.LinePosition; +import io.ballerina.tools.text.LineRange; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static io.ballerina.compiler.api.symbols.TypeDescKind.BOOLEAN; +import static io.ballerina.compiler.api.symbols.TypeDescKind.FLOAT; +import static io.ballerina.compiler.api.symbols.TypeDescKind.INT; +import static io.ballerina.compiler.api.symbols.TypeDescKind.STRING; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +/** + * Tests cases for testing typeOf() with conditional expressions. + * + * @since 2201.9.0 + */ +public class TypeOfConditionalExprTest { + + private SemanticModel model; + + @BeforeClass + public void setup() { + model = SemanticAPITestUtils.getDefaultModulesSemanticModel( + "test-src/symbols/symbols_in_conditional_exprs_test.bal"); + } + + @Test(dataProvider = "TernaryExprsPos") + public void testTernaryExprs(int sLine, int sCol, int eLine, int eCol, List kinds) { + assertType(sLine, sCol, eLine, eCol, kinds); + } + + @DataProvider(name = "TernaryExprsPos") + public Object[][] getTernaryExprPos() { + return new Object[][]{ + {3, 10, 3, 36, List.of(BOOLEAN)}, + {4, 11, 4, 39, List.of(STRING, BOOLEAN)}, + {5, 15, 5, 43, List.of(STRING, BOOLEAN)}, + {6, 14, 6, 60, List.of(STRING, BOOLEAN, INT)}, + {9, 11, 9, 30, List.of(BOOLEAN)}, + {13, 11, 13, 32, List.of(STRING, BOOLEAN)}, + {17, 11, 17, 46, List.of(STRING, BOOLEAN, INT)}, + {21, 11, 21, 45, List.of(STRING, BOOLEAN, INT)}, + {25, 11, 25, 85, List.of(STRING, INT)}, + {29, 11, 29, 79, List.of(BOOLEAN, STRING, INT)}, + }; + } + + @Test(dataProvider = "ElvisExprsPos") + public void testElvisExprs(int sLine, int sCol, int eLine, int eCol, List kinds) { + assertType(sLine, sCol, eLine, eCol, kinds); + } + + @DataProvider(name = "ElvisExprsPos") + public Object[][] getElvisExprPos() { + return new Object[][]{ + {35, 10, 35, 38, List.of(STRING)}, + {36, 10, 36, 36, List.of(STRING, BOOLEAN)}, + {37, 11, 37, 58, List.of(STRING, INT, BOOLEAN)}, + {40, 11, 40, 33, List.of(STRING)}, + {44, 11, 44, 31, List.of(STRING, BOOLEAN)}, + {52, 11, 52, 72, List.of(STRING, INT, BOOLEAN, FLOAT)}, + {56, 11, 57, 33, List.of(BOOLEAN, INT, STRING)}, + }; + } + + private TypeSymbol assertType(int sLine, int sCol, int eLine, int eCol, List kinds) { + Optional type = model.typeOf( + LineRange.from("symbols_in_conditional_exprs_test.bal", LinePosition.from(sLine, sCol), + LinePosition.from(eLine, eCol))); + + if (kinds == null) { + assertTrue(type.isEmpty()); + return null; + } + assertFalse(type.isEmpty()); + + TypeSymbol typeSymbol = type.get(); + if (Objects.requireNonNull(type.get().typeKind()) == TypeDescKind.UNION) { + UnionTypeSymbol unionTypeSymbol = (UnionTypeSymbol) typeSymbol; + assertEquals(unionTypeSymbol.memberTypeDescriptors().size(), kinds.size()); + for (int i = 0; i < kinds.size(); i++) { + assertEquals(unionTypeSymbol.memberTypeDescriptors().get(i).typeKind(), kinds.get(i)); + } + } else { + assertEquals(type.get().typeKind(), kinds.get(0)); + } + + return type.get(); + } + +} diff --git a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/typeof/TypeOfQueryExprTest.java b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/typeof/TypeOfQueryExprTest.java index c8e678abbd0d..d3ec6b0f922b 100644 --- a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/typeof/TypeOfQueryExprTest.java +++ b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/typeof/TypeOfQueryExprTest.java @@ -20,6 +20,7 @@ import io.ballerina.compiler.api.SemanticModel; import io.ballerina.compiler.api.symbols.ArrayTypeSymbol; +import io.ballerina.compiler.api.symbols.StreamTypeSymbol; import io.ballerina.compiler.api.symbols.TypeDescKind; import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; import io.ballerina.compiler.api.symbols.TypeSymbol; @@ -35,13 +36,16 @@ import static io.ballerina.compiler.api.symbols.TypeDescKind.ARRAY; import static io.ballerina.compiler.api.symbols.TypeDescKind.ERROR; +import static io.ballerina.compiler.api.symbols.TypeDescKind.FLOAT; import static io.ballerina.compiler.api.symbols.TypeDescKind.INT; import static io.ballerina.compiler.api.symbols.TypeDescKind.RECORD; +import static io.ballerina.compiler.api.symbols.TypeDescKind.STREAM; import static io.ballerina.compiler.api.symbols.TypeDescKind.STRING; import static io.ballerina.compiler.api.symbols.TypeDescKind.TABLE; import static io.ballerina.compiler.api.symbols.TypeDescKind.TYPE_REFERENCE; import static io.ballerina.compiler.api.symbols.TypeDescKind.UNION; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; /** @@ -123,6 +127,50 @@ public Object[][] getExprPos2() { }; } + @Test(dataProvider = "ExprPos3") + public void testQueryExprWithInvalidExpectedType(int sLine, int sCol, int eLine, int eCol, + TypeDescKind expectedKind, TypeDescKind expectedMemberKind, + String expectedMemberName) { + TypeSymbol type = assertType(sLine, sCol, eLine, eCol, expectedKind); + assertNotNull(type); + + switch (type.typeKind()) { + case ARRAY -> { + ArrayTypeSymbol arrayType = (ArrayTypeSymbol) type; + assertEquals(arrayType.memberTypeDescriptor().typeKind(), expectedMemberKind); + if (expectedMemberName != null) { + assertTrue(arrayType.memberTypeDescriptor().getName().isPresent()); + assertEquals(arrayType.memberTypeDescriptor().getName().get(), expectedMemberName); + } + } + case STREAM -> { + StreamTypeSymbol streamType = (StreamTypeSymbol) type; + assertEquals(streamType.typeParameter().typeKind(), expectedMemberKind); + if (expectedMemberName != null) { + assertTrue(streamType.typeParameter().getName().isPresent()); + assertEquals(streamType.typeParameter().getName().get(), expectedMemberName); + } + } + default -> assertEquals(type.typeKind(), expectedKind); + } + } + + @DataProvider(name = "ExprPos3") + public Object[][] getExprPos3() { + return new Object[][]{ + {98, 9, 99, 51, ARRAY, RECORD, null}, + {100, 9, 101, 59, ARRAY, TYPE_REFERENCE, "Person"}, + {104, 14, 106, 27, ARRAY, RECORD, null}, + {109, 13, 111, 34, ARRAY, RECORD, null}, + {114, 13, 116, 36, ARRAY, RECORD, null}, + {119, 13, 121, 13, ARRAY, TYPE_REFERENCE, "Student"}, + {124, 9, 126, 13, ARRAY, TYPE_REFERENCE, "Student"}, + {129, 9, 131, 53, STREAM, TYPE_REFERENCE, "Person"}, + {134, 14, 136, 14, ARRAY, UNION, null}, + {139, 11, 141, 29, FLOAT, null, null}, + }; + } + @Test public void testTableKeySpecifier() { UnionTypeSymbol type = (UnionTypeSymbol) assertType(50, 37, 52, 80, UNION); diff --git a/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbols/error_type_symbol_test.bal b/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbols/error_type_symbol_test.bal index aa5194d7aa0c..1ca615fd2154 100644 --- a/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbols/error_type_symbol_test.bal +++ b/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbols/error_type_symbol_test.bal @@ -43,3 +43,23 @@ function test() { CancelledError distinctErr; IntersectionError intersectionErr; } + +type ErrorDetail1 record { + string msg; + int value?; +}; + +type ErrorDetail2 record {| + *ErrorDetail1; + boolean|int flag; +|}; + +type SimpleError error; +type InclusionError error; + +type SimpleIntersectionError distinct error & error & error; +type MultipleIntersectionError InclusionError & error & SimpleError & error; +function testMultipleIntersectionError() { + SimpleIntersectionError simpleErr; + MultipleIntersectionError multipleErr; +} diff --git a/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbols/symbols_in_conditional_exprs_test.bal b/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbols/symbols_in_conditional_exprs_test.bal new file mode 100644 index 000000000000..26d927fb74fa --- /dev/null +++ b/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbols/symbols_in_conditional_exprs_test.bal @@ -0,0 +1,59 @@ +boolean moduleFlag1 = false; +boolean moduleFlag2 = true; + +() var1 = moduleFlag1 ? true : false; +int var2 = moduleFlag1 ? "True" : false; +boolean var3 = moduleFlag1 ? "True" : false; +string var4 = moduleFlag1 ? "True" : moduleFlag2 ? false : 0; + +function testTernaryExprWithSameType(boolean flag) { + return flag ? true : false; +} + +function testTernaryExprWithDifferentType(boolean flag) { + return flag ? "True" : false; +} + +function testNestedTernaryExpr1(boolean flag1, boolean flag2) { + return flag1 ? flag2 ? "True " : false : 0; +} + +function testNestedTernaryExpr2(boolean flag1, boolean flag2) { + return flag1 ? "True" : flag2 ? false : 0; +} + +function testNestedTernaryExpr3(boolean flag1, boolean flag2, boolean flag3) { + return (flag1 == flag3 ? moduleFlag1 && flag2 : flag1 || flag3) ? "True" : 0 + 32; +} + +function testNestedTernaryExpr4(boolean flag1, boolean flag2, boolean flag3) { + return flag1 == flag3 ? flag1 && flag2 : moduleFlag2 ? "True" + "False" : 0; +} + +string? moduleNullableStr = (); +int? moduleNullableInt = 1; + +() var5 = moduleNullableStr ?: "False"; +() var6 = moduleNullableStr ?: false; +int var7 = moduleNullableStr ?: moduleNullableInt ?: false; + +function testElvisExprWithSameType(string? nullableStr) { + return nullableStr ?: "False"; +} + +function testElvisExprWithDifferentType(string? nullableStr) { + return nullableStr ?: false; +} + +function testNestedElvisExpr1(string? nullableStr, int? nullableInt) { + return moduleNullableStr ?: moduleNullableInt ?: false; +} + +function testNestedElvisExpr2(string? nullableStr, int? nullableInt, boolean? nullableBool) { + return moduleNullableStr ?: moduleNullableInt ?: nullableBool ?: 0.0; +} + +function testNestedElvisExpr3(string? nullableStr, int? nullableInt, boolean? nullableBool) { + return moduleNullableStr ?: (nullableBool is boolean ? nullableBool || moduleFlag1 + : nullableInt ?: "False"); +} diff --git a/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbols/symbols_in_query_exprs_test.bal b/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbols/symbols_in_query_exprs_test.bal index e4874dff3721..6af6c601503d 100644 --- a/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbols/symbols_in_query_exprs_test.bal +++ b/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbols/symbols_in_query_exprs_test.bal @@ -89,3 +89,55 @@ function getPeople() returns Person[] { Person p2 = {id: 2, name: "Jane Doe"}; return [p1, p2]; } + + + +Student[] moduleStudents = getStudents(); +Person[] modulePeople = getPeople(); + +// basic/minimal +() v11 = from var st in moduleStudents + select {id: st.id, name: st.fname, age: st.age}; +() v12 = from var st in moduleStudents + select {id: st.id, name: st.fname, age: st.age}; + +// where clause +Student v13 = from var st in moduleStudents + where st.fname == "Jon" + select {name: st.fname}; + +// let clause +Person v14 = from var st in moduleStudents + let string name = st.fname + " " + st.lname + select {id: st.id, name: name}; + +// join clause +Person v15 = from var st in moduleStudents + join var {id, name} in modulePeople on st.id equals id + select {name: name, gpa: st.gpa}; + +// order by clause +Person v16 = from var st in moduleStudents + order by st.id + select st; + +// limit clause +() v17 = from var st in moduleStudents + limit LIMIT + select st; + +// on conflict clause +() v18 = table key(id) from var st in moduleStudents.toStream() + select {id: st.id, name: st.fname, age: st.age} + on conflict error("Conflicted Key", cKey = st.id); + +// group by clause +Student v19 = from var {name, age} in modulePeople + group by age + select age; + +function testQueryExprWithCollect() { + return from var {age, gpa} in getStudents() + let var ageScore = (50 - age) * gpa + collect sum(ageScore); +} diff --git a/tests/ballerina-compiler-api-test/src/test/resources/testng.xml b/tests/ballerina-compiler-api-test/src/test/resources/testng.xml index b9b7eef9676f..1e21a251f440 100644 --- a/tests/ballerina-compiler-api-test/src/test/resources/testng.xml +++ b/tests/ballerina-compiler-api-test/src/test/resources/testng.xml @@ -200,6 +200,7 @@ + diff --git a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BRunUtil.java b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BRunUtil.java index 3be624a79cdb..dfc7680ac7e7 100644 --- a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BRunUtil.java +++ b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BRunUtil.java @@ -373,8 +373,8 @@ public static void runInit(CompileResult compileResult) throws ClassNotFoundExce directRun(compileResult.getClassLoader().loadClass(configClassName), "$configureInit", new Class[]{String[].class, Path[].class, String.class}, new Object[]{new String[]{}, configurationDetails.paths, configurationDetails.configContent}); - runOnSchedule(initClazz, ASTBuilderUtil.createIdentifier(null, "$moduleInit"), scheduler); - runOnSchedule(initClazz, ASTBuilderUtil.createIdentifier(null, "$moduleStart"), scheduler); + runOnSchedule(initClazz, "$moduleInit", scheduler); + runOnSchedule(initClazz, "$moduleStart", scheduler); // if (temp) { // scheduler.immortal = true; // new Thread(scheduler::start).start(); @@ -397,6 +397,26 @@ private static void directRun(Class initClazz, String functionName, Class[] p } } + public static void runOnSchedule(CompileResult compileResult, String functionName, Scheduler scheduler) { + BIRNode.BIRFunction function = getInvokedFunction(compileResult, functionName); + PackageManifest packageManifest = compileResult.packageManifest(); + String funcClassName = JarResolver.getQualifiedClassName(packageManifest.org().toString(), + packageManifest.name().toString(), + packageManifest.version().toString(), + getClassName(function.pos.lineRange().fileName())); + Class funcClass = null; + try { + funcClass = compileResult.getClassLoader().loadClass(funcClassName); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Error while invoking function '" + functionName + "'", e); + } + runOnSchedule(funcClass, functionName, scheduler); + } + + private static void runOnSchedule(Class initClazz, String name, Scheduler scheduler) { + runOnSchedule(initClazz, ASTBuilderUtil.createIdentifier(null, name), scheduler); + } + private static void runOnSchedule(Class initClazz, BLangIdentifier name, Scheduler scheduler) { String funcName = JvmCodeGenUtil.cleanupFunctionName(name.value); try { @@ -418,6 +438,7 @@ private static void runOnSchedule(Class initClazz, BLangIdentifier name, Sche }; final FutureValue out = scheduler .schedule(new Object[1], func, null, null, null, PredefinedTypes.TYPE_ANY, null, null); + Scheduler.setDaemonStrand(out.strand); scheduler.start(); final Throwable t = out.panic; if (t != null) { diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/runtime/api/tests/Async.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/runtime/api/tests/Async.java index 9ad21188a523..0082cbddda6c 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/runtime/api/tests/Async.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/runtime/api/tests/Async.java @@ -89,7 +89,7 @@ public static long getA(Environment env, BObject obj) { } public static long getResourceA(Environment env, BObject obj) { - invokeAsync(env, obj, "$gen$$getA$$0046"); + invokeAsync(env, obj, "$gen$$getA$&0046"); return 0; } @@ -117,12 +117,12 @@ public static long nonIsolatedGetA(Environment env, BObject obj) { } public static long getNonIsolatedResourceA(Environment env, BObject obj) { - invokeMethodAsyncConcurrently(env, obj, "$gen$$getA$$0046"); + invokeMethodAsyncConcurrently(env, obj, "$gen$$getA$&0046"); return 0; } public static long getNonIsolatedResourceB(Environment env, BObject obj) { - invokeMethodAsyncConcurrently(env, obj, "$gen$$getB$$0046"); + invokeMethodAsyncConcurrently(env, obj, "$gen$$getB$&0046"); return 0; } @@ -135,7 +135,7 @@ public static boolean nonIsolatedClassIsIsolatedFunction(BObject obj) { } public static long isolatedServiceGetA(Environment env, BObject obj) { - invokeMethodAsyncConcurrently(env, obj, "$gen$$getA$$0046"); + invokeMethodAsyncConcurrently(env, obj, "$gen$$getA$&0046"); return 0; } @@ -148,7 +148,7 @@ public static boolean isolatedServiceIsIsolatedFunction(BObject obj) { } public static long nonIsolatedServiceGetA(Environment env, BObject obj) { - invokeMethodAsyncSequentially(env, obj, "$gen$$getA$$0046"); + invokeMethodAsyncSequentially(env, obj, "$gen$$getA$&0046"); return 0; } @@ -253,7 +253,7 @@ public void notifyFailure(BError error) { private static boolean isResourceMethodIsolated(BObject obj) { MethodType[] methods = ((BServiceType) obj.getType()).getResourceMethods(); for (MethodType method : methods) { - if (method.getName().equals("$gen$$getA$$0046")) { + if (method.getName().equals("$gen$$getA$&0046")) { return method.isIsolated(); } } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/servicetests/ServiceValue.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/servicetests/ServiceValue.java index d1482fbdbe9a..529b5c37935e 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/servicetests/ServiceValue.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/servicetests/ServiceValue.java @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.ObjectType; +import io.ballerina.runtime.api.types.RemoteMethodType; import io.ballerina.runtime.api.types.ResourceMethodType; import io.ballerina.runtime.api.types.ServiceType; import io.ballerina.runtime.api.utils.StringUtils; @@ -191,6 +192,16 @@ public static BValue getResourceMethodAnnotations(BObject service, BString metho return null; } + public static BValue getRemoteMethodAnnotations(BObject service, BString method, BString annotName) { + String methodName = method.getValue(); + for (RemoteMethodType methodType : ((ServiceType) service.getOriginalType()).getRemoteMethods()) { + if (methodType.getName().equals(methodName)) { + return (BValue) methodType.getAnnotation(annotName); + } + } + return null; + } + public static BArray getParamDefaultability(BObject service, BString name) { ServiceType serviceType = (ServiceType) service.getType(); Optional func = Arrays.stream(serviceType.getResourceMethods()) diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/ClientResourceAccessActionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/ClientResourceAccessActionTest.java index dff2707eb0aa..cd9f82d2c15d 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/ClientResourceAccessActionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/ClientResourceAccessActionTest.java @@ -58,7 +58,8 @@ public Object[][] testClientResourceAccessActionData() { {"testResourceAccessOfAnObjectConstructedViaObjectCons"}, {"testResourceAccessContainingSpecialChars"}, {"testClosuresFromPathParams"}, - {"testAccessingResourceWithIncludedRecordParam"} + {"testAccessingResourceWithIncludedRecordParam"}, + {"testAccessingResourceWithEscapedChars"} }; } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationAttachmentNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationAttachmentNegativeTest.java index fd2ccdc03900..cea205c180fa 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationAttachmentNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationAttachmentNegativeTest.java @@ -38,7 +38,7 @@ public class AnnotationAttachmentNegativeTest { @BeforeClass public void setup() { compileResult = BCompileUtil.compile("test-src/annotations/annot_attachments_negative.bal"); - Assert.assertEquals(compileResult.getErrorCount(), 277); + Assert.assertEquals(compileResult.getErrorCount(), 302); } @Test @@ -520,6 +520,60 @@ public void testInvalidConstAnnotElements() { validateError(compileResult, index, "expression is not a constant expression", line + 7, 16); } + public void testInvalidAnnotationAttachmentsOnMembersOfStructuredTypedBindingPatterns() { + int index = 277; + int line = 989; + validateError(compileResult, index++, "undefined annotation 'UndefinedAnnotation'", line, 2); + validateError(compileResult, index++, "undefined annotation 'UndefinedAnnotation'", line += 2, 10); + validateError(compileResult, index++, "undefined annotation 'UndefinedAnnotation'", line += 2, 16); + validateError(compileResult, index++, "undefined annotation 'UndefinedAnnotation'", line += 2, 12); + validateError(compileResult, index++, "undefined annotation 'UndefinedAnnotation'", line += 2, 16); + validateError(compileResult, index++, "undefined annotation 'UndefinedAnnotation'", line += 3, 6); + validateError(compileResult, index++, "undefined annotation 'UndefinedAnnotation'", line += 1, 6); + validateError(compileResult, index++, "undefined annotation 'UndefinedAnnotation'", line += 1, 7); + validateError(compileResult, index++, "undefined annotation 'UndefinedAnnotation'", line += 1, 14); + validateError(compileResult, index++, "undefined annotation 'UndefinedAnnotation'", line += 1, 20); + validateError(compileResult, index++, "undefined annotation 'UndefinedAnnotation'", line += 1, 16); + validateError(compileResult, index, "undefined annotation 'UndefinedAnnotation'", line += 1, 20); + } + + @Test + public void testInvalidAttachmentOnServiceRemoteMethod() { + int index = 289; + int line = 1014; + validateError(compileResult, index++, "annotation 'v1' is not allowed on service_remote, object_method, " + + "function", line, 5); + validateError(compileResult, index++, "annotation 'v2' is not allowed on service_remote, object_method, " + + "function", line += 3, 5); + validateError(compileResult, index++, "annotation 'v6' is not allowed on service_remote, object_method, " + + "function", line += 12, 5); + validateError(compileResult, index++, "annotation 'v7' is not allowed on service_remote, object_method, " + + "function", line += 3, 5); + validateError(compileResult, index++, "annotation 'v8' is not allowed on service_remote, object_method, " + + "function", ++line, 5); + validateError(compileResult, index++, "annotation 'v9' is not allowed on service_remote, object_method, " + + "function", line += 3, 5); + validateError(compileResult, index++, "annotation 'v10' is not allowed on service_remote, object_method, " + + "function", line += 3, 5); + validateError(compileResult, index++, "annotation 'v11' is not allowed on service_remote, object_method, " + + "function", line += 3, 5); + validateError(compileResult, index++, "annotation 'v12' is not allowed on service_remote, object_method, " + + "function", line += 3, 5); + validateError(compileResult, index++, "annotation 'v13' is not allowed on service_remote, object_method, " + + "function", line += 3, 5); + validateError(compileResult, index, "annotation 'v15' is not allowed on service_remote, object_method, " + + "function", line + 3, 5); + } + + @Test + public void testInvalidServiceRemoteMethodAttachmentOnNonRemoteServiceMethods() { + int index = 300; + validateError(compileResult, index++, "annotation 'v26' is not allowed on object_method, function", + 1056, 5); + validateError(compileResult, index, "annotation 'v26' is not allowed on object_method, function", + 1059, 5); + } + @AfterClass public void tearDown() { compileResult = null; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationAttachmentSymbolsTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationAttachmentSymbolsTest.java index e5b8194f7502..68fda5c98431 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationAttachmentSymbolsTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationAttachmentSymbolsTest.java @@ -394,6 +394,16 @@ public void testConstAnnots() { assertAttachmentSymbol(attachmentsF4.get(0), "v29", true, "increment", -2L); } + @Test + public void testAnnotWithServiceRemoteMethodAttachmentPoint() { + BLangFunction function = getFunction("ServiceClass.serviceRemoteFn1"); + List attachments = function.symbol.getAnnotations(); + Assert.assertEquals(attachments.size(), 2); + + assertAttachmentSymbol(attachments.get(0), "v31"); + assertAttachmentSymbol(attachments.get(1), "v32", true, "increment", 1112L); + } + private BLangTypeDefinition getTypeDefinition(List typeDefinitions, String name) { for (TypeDefinition typeDefinition : typeDefinitions) { BLangTypeDefinition bLangTypeDefinition = (BLangTypeDefinition) typeDefinition; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationAttachmentTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationAttachmentTest.java index 1c3eb247ac2f..2f99f36bfd2f 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationAttachmentTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationAttachmentTest.java @@ -18,6 +18,7 @@ import io.ballerina.tools.diagnostics.Location; import org.ballerinalang.model.elements.PackageID; +import org.ballerinalang.model.tree.ClassDefinition; import org.ballerinalang.model.tree.NodeKind; import org.ballerinalang.model.tree.ServiceNode; import org.ballerinalang.model.tree.TopLevelNode; @@ -51,6 +52,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangRecordLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangSimpleVarRef; import org.wso2.ballerinalang.compiler.tree.expressions.BLangTypeConversionExpr; +import org.wso2.ballerinalang.compiler.tree.types.BLangObjectTypeNode; import org.wso2.ballerinalang.compiler.tree.types.BLangTupleTypeNode; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -161,15 +163,7 @@ public void testAnnotOnListener() { @Test public void testAnnotOnServiceOne() { - List attachments = (List) - compileResult.getAST().getServices().stream() - .filter(serviceNode -> - serviceNode.getAbsolutePath().stream().anyMatch(p -> p.getValue().contains("ser"))) - .findFirst() - .get().getServiceClass().getAnnotationAttachments() - .stream() - .filter(ann -> !isServiceIntropAnnot((BLangAnnotationAttachment) ann)) - .collect(Collectors.toList()); + List attachments = getServiceClassAnnotations("ser"); Assert.assertEquals(attachments.size(), 1); assertAnnotationNameAndKeyValuePair(attachments.get(0), "v8", "val", "v8"); } @@ -568,6 +562,43 @@ public void testAnnotOnTupleMember() { Assert.assertEquals(m1.annAttachments.get(0).annotationName.getValue(), "v30"); } + @Test + public void testAnnotOnServiceRemoteMethodOfServiceClass() { + BLangClassDefinition serviceClass = getClassDefinition(((BLangPackage) compileResult.getAST()).topLevelNodes, + "ServiceClass"); + List attachments = + serviceClass.functions.stream() + .filter(f -> f.name.value.equals("serviceRemoteFn1")) + .findFirst() + .get().getAnnotationAttachments(); + Assert.assertEquals(attachments.size(), 2); + assertAnnotationNameAndKeyValuePair(attachments.get(0), "v31", "increment", 1111L); + assertAnnotationNameAndKeyValuePair(attachments.get(1), "v32", "increment", 1112L); + } + + @Test + public void testAnnotOnServiceRemoteMethodOfServiceType() { + BLangTypeDefinition serviceObject = getTypeDefinition(compileResult.getAST().getTypeDefinitions(), + "ServiceObject"); + List attachments = + ((BLangObjectTypeNode) serviceObject.typeNode).functions.stream() + .filter(f -> f.name.value.equals("serviceRemoteFn2")) + .findFirst() + .get().getAnnotationAttachments(); + Assert.assertEquals(attachments.size(), 1); + assertAnnotationNameAndKeyValuePair(attachments.get(0), "v31", "increment", 1113L); + } + + @Test + public void testAnnotOnServiceRemoteMethodOfServiceDecl() { + List attachments = getServiceClassForServiceDecl("ser2").getFunctions().stream() + .filter(f -> f.name.value.equals("serviceRemoteFn3")) + .findFirst() + .get().getAnnotationAttachments(); + Assert.assertEquals(attachments.size(), 1); + assertAnnotationNameAndKeyValuePair(attachments.get(0), "v32", "increment", 1114L); + } + private BLangTypeDefinition getTypeDefinition(List typeDefinitions, String name) { for (TypeDefinition typeDefinition : typeDefinitions) { BLangTypeDefinition bLangTypeDefinition = (BLangTypeDefinition) typeDefinition; @@ -592,6 +623,23 @@ private BLangClassDefinition getClassDefinition(List typ throw new RuntimeException("Class Definition '" + name + "' not found."); } + private List getServiceClassAnnotations(String name) { + return (List) + getServiceClassForServiceDecl(name).getAnnotationAttachments() + .stream() + .filter(ann -> !isServiceIntropAnnot((BLangAnnotationAttachment) ann)) + .collect(Collectors.toList()); + } + + private ClassDefinition getServiceClassForServiceDecl(String name) { + return compileResult.getAST().getServices().stream() + .filter(serviceNode -> + serviceNode.getAbsolutePath().stream() + .anyMatch(p -> p.getValue().contains(name))) + .findFirst() + .get().getServiceClass(); + } + @AfterClass public void tearDown() { compileResult = null; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationRuntimeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationRuntimeTest.java index e83dd7806b40..af8cd3af966a 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationRuntimeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/AnnotationRuntimeTest.java @@ -210,6 +210,12 @@ public void testListExprInConstAnnot() { BRunUtil.invoke(resultOne, "testListExprInConstAnnot"); } + @Test + public void testServiceRemoteMethodAnnotations() { + CompileResult result = BCompileUtil.compile("test-src/annotations/service_remote_method_annotations.bal"); + BRunUtil.invoke(result, "testServiceRemoteMethodAnnotations"); + } + @AfterClass public void tearDown() { resultOne = null; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/DisplayAnnotationTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/DisplayAnnotationTest.java index 3b6c09cba9a3..155c2050b1a3 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/DisplayAnnotationTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/annotations/DisplayAnnotationTest.java @@ -18,6 +18,7 @@ import org.ballerinalang.model.tree.AnnotationAttachmentNode; import org.ballerinalang.model.tree.ClassDefinition; +import org.ballerinalang.model.tree.FunctionNode; import org.ballerinalang.model.tree.NodeKind; import org.ballerinalang.model.tree.SimpleVariableNode; import org.ballerinalang.model.tree.TypeDefinition; @@ -30,13 +31,18 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.wso2.ballerinalang.compiler.tree.BLangAnnotationAttachment; +import org.wso2.ballerinalang.compiler.tree.BLangBlockFunctionBody; import org.wso2.ballerinalang.compiler.tree.BLangClassDefinition; +import org.wso2.ballerinalang.compiler.tree.BLangExternalFunctionBody; import org.wso2.ballerinalang.compiler.tree.BLangFunction; import org.wso2.ballerinalang.compiler.tree.BLangPackage; import org.wso2.ballerinalang.compiler.tree.BLangService; import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangInvocation; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangLambdaFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangTypeConversionExpr; +import org.wso2.ballerinalang.compiler.tree.statements.BLangSimpleVariableDef; +import org.wso2.ballerinalang.compiler.tree.statements.BLangStatement; import java.util.List; @@ -91,7 +97,7 @@ public void testDisplayAnnotOnObjectAndMemberFunction() { @Test public void testDisplayAnnotOnRecord() { - TypeDefinition typeDefinition = result.getAST().getTypeDefinitions().get(13); + TypeDefinition typeDefinition = result.getAST().getTypeDefinitions().get(15); List annot = typeDefinition.getAnnotationAttachments(); Assert.assertEquals(annot.size(), 1); Assert.assertEquals(annot.get(0).getExpression().toString(), @@ -104,7 +110,30 @@ public void testDisplayAnnotOnRecord() { "clientSecret field,kind: <\"text\"|\"password\"|\"file\"?> password}"); } - @Test void testDisplayAnnotationNegative() { + @Test + public void testDisplayAnnotOnWorker() { + BLangBlockFunctionBody bLangBlockFunctionBody = + ((BLangBlockFunctionBody) result.getAST().getFunctions().get(2).getBody()); + BLangStatement bLangStatement = bLangBlockFunctionBody.getStatements().get(1); + FunctionNode workerExpression = + ((BLangLambdaFunction) ((BLangSimpleVariableDef) bLangStatement).getVariable() + .getInitialExpression()).getFunctionNode(); + BLangAnnotationAttachment annot = + (BLangAnnotationAttachment) workerExpression.getAnnotationAttachments().get(0); + Assert.assertEquals(getActualExpressionFromAnnotationAttachmentExpr(annot.expr).toString(), + " {label: worker annotation,type: named,id: hash}"); + } + + @Test + public void testDisplayAnnotOnExternalFunctionBody() { + BLangExternalFunctionBody body = (BLangExternalFunctionBody) result.getAST().getFunctions().get(3).getBody(); + BLangAnnotationAttachment annot = (BLangAnnotationAttachment) ((List) body.annAttachments).get(0); + Assert.assertEquals(getActualExpressionFromAnnotationAttachmentExpr(annot.expr).toString(), + " {label: external,id: hash}"); + } + + @Test + void testDisplayAnnotationNegative() { BAssertUtil.validateError(negative, 0, "cannot specify more than one annotation value " + "for annotation 'ballerina/lang.annotations:0.0.0:display'", 17, 1); BAssertUtil.validateError(negative, 1, "incompatible types: expected '\"text\"|\"password\"|\"file\"?'," + diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/EqualAndNotEqualOperationsTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/EqualAndNotEqualOperationsTest.java index aceeba7cc21e..4f145721dc4e 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/EqualAndNotEqualOperationsTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/EqualAndNotEqualOperationsTest.java @@ -190,7 +190,8 @@ public Object[] getXmlTestFunctions() { "testUnequalXmlIgnoringAttributeOrder", "testEqualXmlWithPI", "testUnequalXmlWithUnequalPI", "testUnequalXmlWithPIInWrongOrder", "testUnequalXmlWithMultiplePIInWrongOrder", "testUnequalXmlWithMissingPI", "testXmlWithNamespacesPositive", "testXmlWithNamespacesNegative", - "testXmlSequenceAndXmlItemEqualityPositive", "testXmlSequenceAndXmlItemEqualityNegative" + "testXmlSequenceAndXmlItemEqualityPositive", "testXmlSequenceAndXmlItemEqualityNegative", + "testXmlSequenceLHSEquals" }; } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/basic/NegativeValidationTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/basic/NegativeValidationTest.java index 32da1137cfa1..41aadfc88701 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/basic/NegativeValidationTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/basic/NegativeValidationTest.java @@ -138,7 +138,7 @@ public void testMethodNotFound6() { public void testMethodNotFound7() { String path = "test-src/javainterop/negative/method_not_found7.bal"; CompileResult compileResult = BCompileUtil.compile(path); - Assert.assertEquals(compileResult.getDiagnostics().length, 3); + Assert.assertEquals(compileResult.getDiagnostics().length, 4); String message = "{ballerina/jballerina.java}METHOD_NOT_FOUND 'No such public static method '%s' with " + "'%s' parameter(s) found in class '%s''"; BAssertUtil.validateError(compileResult, 0, String.format(message, "getPrintableStackTrace", "1", @@ -149,6 +149,10 @@ public void testMethodNotFound7() { "{ballerina/jballerina.java}METHOD_NOT_FOUND 'No such public method 'concat' " + "with '3' parameter(s) found in class 'io.ballerina.runtime.api.values.BString''", "method_not_found7.bal", 27, 1); + BAssertUtil.validateError(compileResult, 3, + "{ballerina/jballerina.java}METHOD_NOT_FOUND 'No such public static method 'indexOf' " + + "with '0' parameter(s) found in class 'java.lang.String''", + "method_not_found7.bal", 32, 1); } @Test diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java index ee5cb48fc591..078b4267e901 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java @@ -80,7 +80,8 @@ public Object[] dataToTestQueryActionOrExpr() { "testQueryActionOrExpressionWithUnionRecordResultType", "testQueryActionOrExprWithAnyOrErrResultType", "testNestedQueryActionOrExprWithClientResourceAccessAction", - "testQueryActionWithQueryExpression" + "testQueryActionWithQueryExpression", + "testQueryActionWithLetExpression" }; } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionTest.java index a717549b4f41..2ab2c48a7c4c 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionTest.java @@ -174,6 +174,16 @@ public void testErrorHandlingWithinQueryAction() { public void testReturnStmtWithinQueryAction() { BRunUtil.invoke(result, "testReturnStmtWithinQueryAction"); } + + @Test + public void testQueryActionWithCollectClauseInsideLeClause() { + BRunUtil.invoke(result, "testQueryActionWithCollectClauseInsideLeClause1"); + } + + @Test + public void testQueryActionWithCollectClauseInsideLeClauseWithEmptyDoBody() { + BRunUtil.invoke(result, "testQueryActionWithCollectClauseInsideLeClause2"); + } @Test public void testQueryActionWithDoClauseContainsCheck() { diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java index 30d72d802dc4..29c4fcdc886b 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java @@ -189,4 +189,15 @@ public void testQueryExpressionWithMismatchedReturnType() { validateError(compileResult, index++, "missing non-defaultable required record field 'x'", 47, 24); Assert.assertEquals(compileResult.getDiagnostics().length, index); } + + @Test(description = "Test the query actions having select or collect clauses") + public void testQueryActionsWithInvalidFinalClause() { + CompileResult compileResult = BCompileUtil.compile( + "test-src/query/query_action_with_final_clause_negative.bal"); + int index = 0; + validateError(compileResult, index++, "select clause in query action", 3, 5); + validateError(compileResult, index++, "collect clause in query action", 11, 5); + validateError(compileResult, index++, "collect clause in query action", 19, 5); + Assert.assertEquals(compileResult.getDiagnostics().length, index); + } } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java index 7ca9edaca1d8..83e50996f2a2 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java @@ -17,12 +17,21 @@ */ package org.ballerinalang.test.runtime.api; +import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.creators.ValueCreator; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.internal.scheduling.Scheduler; import org.ballerinalang.test.BCompileUtil; import org.ballerinalang.test.BRunUtil; import org.ballerinalang.test.CompileResult; +import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import java.util.concurrent.atomic.AtomicReference; + /** * Test cases for runtime api. * @@ -49,4 +58,41 @@ public Object[] packageNameProvider() { "environment" }; } + + @Test + public void testRecordNoStrandDefaultValue() { + CompileResult strandResult = BCompileUtil.compile("test-src/runtime/api/no_strand"); + final Scheduler scheduler = new Scheduler(false); + AtomicReference exceptionRef = new AtomicReference<>(); + Thread thread1 = new Thread(() -> { + BRunUtil.runOnSchedule(strandResult, "main", scheduler); + }); + Thread thread2 = new Thread(() -> { + try { + Thread.sleep(1000); + BMap recordValue = ValueCreator.createRecordValue(new Module("testorg", + "no_strand", "1"), "MutualSslHandshake"); + Assert.assertEquals(recordValue.getType().getName(), "MutualSslHandshake"); + Assert.assertEquals(recordValue.get(StringUtils.fromString("status")), + StringUtils.fromString("passed")); + Assert.assertNull(recordValue.get(StringUtils.fromString("base64EncodedCert"))); + } catch (Throwable e) { + exceptionRef.set(e); + } finally { + scheduler.poison(); + } + }); + try { + thread1.start(); + thread2.start(); + thread1.join(); + thread2.join(); + Throwable storedException = exceptionRef.get(); + if (storedException != null) { + throw new AssertionError("Test failed due to an exception in a thread", storedException); + } + } catch (InterruptedException e) { + throw new RuntimeException("Error while invoking function 'main'", e); + } + } } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/ifelse/TypeGuardTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/ifelse/TypeGuardTest.java index 6fc16c0b29b0..cdfc29dd5868 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/ifelse/TypeGuardTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/ifelse/TypeGuardTest.java @@ -142,6 +142,8 @@ public void testTypeGuardSemanticsNegative() { // 190, 17); // BAssertUtil.validateError(negativeResult, i++, // "incompatible types: expected 'string', found '(boolean|int|string)'", 192, 20); + BAssertUtil.validateError(negativeResult, i++, + "incompatible types: expected 'string', found 'boolean'", 192, 20); BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(int|boolean)'", 199, 17); BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'string', found '(float|string)'", @@ -751,6 +753,11 @@ public void testCustomCircularTupleTypeWithIsCheck() { BRunUtil.invoke(result, "testCustomCircularTupleTypeWithIsCheck"); } + @Test + public void testSingletonTypeNarrowedTypeDesc() { + BRunUtil.invoke(result, "testSingletonTypeNarrowedTypeDesc"); + } + @Test public void testTypeGuardsAccountingForSemTypes1() { CompileResult result = BCompileUtil.compile("test-src/statements/ifelse/test_type_guard_sem_types_1.bal"); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java index db77c3b08be6..6e2c009e072f 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finaltypes/FinalAccessTest.java @@ -47,16 +47,21 @@ public void setup() { public void testFinalFailCase() { CompileResult compileResultNegative = BCompileUtil.compile( "test-src/types/finaltypes/test_implicitly_final_negative.bal"); - Assert.assertEquals(compileResultNegative.getErrorCount(), 8); - BAssertUtil.validateError(compileResultNegative, 0, "cannot assign a value to function argument 'a'", 11, 5); - BAssertUtil.validateError(compileResultNegative, 1, "cannot assign a value to function argument 'a'", 17, 5); - BAssertUtil.validateError(compileResultNegative, 2, "cannot assign a value to function argument 'f'", 22, 5); - BAssertUtil.validateError(compileResultNegative, 3, "cannot assign a value to function argument 's'", 23, 5); - BAssertUtil.validateError(compileResultNegative, 4, "cannot assign a value to function argument 'b'", 24, 5); - BAssertUtil.validateError(compileResultNegative, 5, "cannot assign a value to function argument 'j'", 25, 5); - BAssertUtil.validateError(compileResultNegative, 6, "cannot assign a value to function argument 'a'", 38, 5); - BAssertUtil.validateError(compileResultNegative, 7, "invalid assignment: 'listener' declaration is final", + int i = 0; + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'a'", 11, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'a'", 17, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'f'", 22, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 's'", 23, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'b'", 24, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'j'", 25, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'a'", 38, 5); + BAssertUtil.validateError(compileResultNegative, i++, "invalid assignment: 'listener' declaration is final", 45, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'p2'", 49, 5); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'b'", 53, 9); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'b'", 59, 9); + BAssertUtil.validateError(compileResultNegative, i++, "cannot assign a value to function argument 'b'", 66, 9); + Assert.assertEquals(compileResultNegative.getErrorCount(), i); } @Test diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/readonly/SelectivelyImmutableTypeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/readonly/SelectivelyImmutableTypeTest.java index d0d075190cbc..6f44bf47867b 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/readonly/SelectivelyImmutableTypeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/readonly/SelectivelyImmutableTypeTest.java @@ -193,6 +193,8 @@ public void testImmutableTypesNegative() { "'(json & readonly)[]'", 407, 28); validateError(result, index++, "incompatible types: expected '(json & readonly)[]', found " + "'(xml & readonly)[]'", 408, 29); + validateError(result, index++, "incompatible types: expected " + + "'((stream|ballerina/lang.string:0.0.0:RegExp) & readonly)', found 'stream'", 412, 48); assertEquals(result.getErrorCount(), index); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/regexp/RegExpTypeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/regexp/RegExpTypeTest.java index 432b9fd55425..fcda49704652 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/regexp/RegExpTypeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/regexp/RegExpTypeTest.java @@ -49,7 +49,8 @@ public Object[] dataToTestRegExp() { "testAssignabilityWithRegExp", "testSubtypingWithRegExp", "testRegExpWithVar", - "testRegExpWithUserDefinedType" + "testRegExpWithUserDefinedType", + "testRegExpReadonlyLocalVars" }; } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/action/client_resource_access_action.bal b/tests/jballerina-unit-test/src/test/resources/test-src/action/client_resource_access_action.bal index b81c79194e13..2fbad10a56b6 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/action/client_resource_access_action.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/action/client_resource_access_action.bal @@ -697,6 +697,55 @@ function testAccessingResourceWithIncludedRecordParam() { assertEquality(d, 3); } +client class Client11 { + resource function get v1\.2/greeting1() returns string { + return "Path1"; + } + + resource function get ["v1.2"]/greeting2() returns string { + return "Path2"; + } + + resource function get v1\.2/ab\.c/greeting3() returns string { + return "Path3"; + } + + resource function get v1\.\.2() returns string { + return "Path4"; + } + + function abc\.abc() returns string { + return "abc.abc"; + } +} + +function testAccessingResourceWithEscapedChars() { + Client11 cl = new; + string a1 = cl->/v1\.2/greeting1; + assertEquality(a1, "Path1"); + + string a2 = cl->/["v1.2"]/greeting1; + assertEquality(a2, "Path1"); + + string b1 = cl->/["v1.2"]/greeting2; + assertEquality(b1, "Path2"); + + string b2 = cl->/v1\.2/greeting2; + assertEquality(b2, "Path2"); + + string c1 = cl->/v1\.2/ab\.c/greeting3; + assertEquality(c1, "Path3"); + + string c2 = cl->/["v1.2"]/["ab.c"]/greeting3; + assertEquality(c2, "Path3"); + + string d1 = cl->/v1\.\.2; + assertEquality(d1, "Path4"); + + string e1 = cl.abc\.abc(); + assertEquality(e1, "abc.abc"); +} + function assertEquality(any|error actual, any|error expected) { if expected is anydata && actual is anydata && expected == actual { return; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/annotations/annot_attachments.bal b/tests/jballerina-unit-test/src/test/resources/test-src/annotations/annot_attachments.bal index 37ad9aec0a6b..f8ab75fa1ed2 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/annotations/annot_attachments.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/annotations/annot_attachments.bal @@ -337,3 +337,32 @@ type F3 record {| type F4 record {| int x; |}; + +public annotation record {| int increment; |} v31 on service remote function; +public const annotation record {| int increment; |} v32 on source type, source service remote function; + +service class ServiceClass { + @v31 { + increment: 1111 + } + @v32 { + increment: 1112 + } + remote function serviceRemoteFn1() { + } +} + +type ServiceObject service object { + @v31 { + increment: 1113 + } + remote function serviceRemoteFn2(); +}; + +service /ser2 on new Listener() { + @v32 { + increment: 1114 + } + remote function serviceRemoteFn3() { + } +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/annotations/annot_attachments_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/annotations/annot_attachments_negative.bal index 8dd83a0524d2..b9177dbe437c 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/annotations/annot_attachments_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/annotations/annot_attachments_negative.bal @@ -975,3 +975,87 @@ type F4 record {| type F5 record {| int x; |}; + +type Person record {| + string fname; + string lname; +|}; + +function getPerson() returns Person { + Person person = {fname: "Anne", lname: "Frank"}; + return person; +} + +[@UndefinedAnnotation int, int] [f1, s2] = [1, 2]; + +record {|@UndefinedAnnotation string fname; string lname;|} {fname, lname} = getPerson(); + +error err = error("err", i = 33); + +error> error () = error("err"); + +error error () = error("err"); + +function testInvalidAnnotationAttachmentsOnMembersOfStructuredTypedBindingPatterns() { + [@UndefinedAnnotation int, int] [first, second] = [1, 2]; + [@UndefinedAnnotation int, int, int] [a, b, c] = [1, 2, 3]; + [[@UndefinedAnnotation int, int], int] [[a1, b1], c1] = [[1, 2], 3]; + record {|@UndefinedAnnotation string fname; string lname;|} {fname, lname} = getPerson(); + error err = error("err", i = 33); + error> error () = error("err"); + error error () = error("err"); +} + +public annotation v26 on service remote function; + +service class ServiceClass2 { + string name = "ballerina"; + + @v1 { + val: "v1" + } + @v2 { + val: "v2" + } + @v3 { // OK + val: "v3" + } + @v4 { // OK + x: 1 + } + @v5 { // OK + val: "v5" + } + @v6 { + val: "v6" + } + @v7 + @v8 { + val: "v8" + } + @v9 { + val: "v9" + } + @v10 { + val: "v10" + } + @v11 { + val: 11 + } + @v12 { + val: "v12" + } + @v13 { + val: "v13" + } + @v15 { + val: false + } + remote function getName() returns string { return self.name; } + + @v26 + resource function get name() returns string { return self.name; } + + @v26 + function getFirstName() returns string { return self.name; } +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/annotations/display_annot.bal b/tests/jballerina-unit-test/src/test/resources/test-src/annotations/display_annot.bal index e7cff1656913..6cb6f0cd2cd6 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/annotations/display_annot.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/annotations/display_annot.bal @@ -74,3 +74,16 @@ public type RefreshTokenGrantConfig record {| @display {iconPath: "Field.icon", label: "clientSecret field", kind: "password"} string clientSecret; |}; + +function annotationOnWorker() { + @display { + label: "worker annotation", + 'type: "named", + id: "hash" + } + worker testWorker { + + } +} + +function testExternalFunction() = @display { label: "external", id: "hash" } external; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/annotations/service_remote_method_annotations.bal b/tests/jballerina-unit-test/src/test/resources/test-src/annotations/service_remote_method_annotations.bal new file mode 100644 index 000000000000..31e80ce8dd18 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/annotations/service_remote_method_annotations.bal @@ -0,0 +1,56 @@ +// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.org). +// +// 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. + +import ballerina/jballerina.java; + +type Rec record {| + int increment; +|}; + +public annotation Rec srma on service remote function; + +service class ServiceClass { + @srma { + increment: 1111 + } + remote function serviceRemoteFn() { + + } +} + +function testServiceRemoteMethodAnnotations() returns error? { + any annots = getRemoteMethodAnnotations(new ServiceClass(), "serviceRemoteFn", "srma"); + assertTrue(annots is Rec); + Rec rec = annots; + assertEquality({increment: 1111}, rec); +} + +function getRemoteMethodAnnotations(service object {} obj, string methodName, string annotName) returns any = + @java:Method { + 'class: "org/ballerinalang/nativeimpl/jvm/servicetests/ServiceValue" + } external; + +function assertTrue(anydata actual) { + assertEquality(true, actual); +} + +function assertEquality(anydata expected, anydata actual) { + if expected == actual { + return; + } + + panic error(string `expected ${expected.toBalString()}, found ${actual.toBalString()}`); +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation.bal index b98cf9aa172f..ae0be91693b8 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation.bal @@ -1135,6 +1135,17 @@ public function testXmlSequenceAndXmlItemEqualityNegative() { test:assertFalse(x1 == x3 || !(x1 != x3) || x3 == x1 || !(x3 != x1)); } +public function testXmlSequenceLHSEquals() { + string a = "hello"; + xml x1 = xml `AS-${a}`; + xml x2 = xml `AS-${a}`; + test:assertTrue(x1 == x2 && !(x1 != x2)); + + x1 = xml ``; + x2 = xml ``; + test:assertTrue(x1 == x2 && !(x1 != x2)); +} + function testXmlStringNegative() { anydata x1 = xml `The Lost World`; anydata x2 = "The Lost World"; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/negative/method_not_found7.bal b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/negative/method_not_found7.bal index c6cc062a3d30..169a3c17440a 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/negative/method_not_found7.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/negative/method_not_found7.bal @@ -28,3 +28,7 @@ function concatString(handle h, string s1, string s2) returns handle = @java:Met 'class: "io.ballerina.runtime.api.values.BString", name: "concat" } external; + +function indexOf() returns byte = @java:Method { + 'class: "java.lang.String" +} external; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/klass/simple_service_class.bal b/tests/jballerina-unit-test/src/test/resources/test-src/klass/simple_service_class.bal index 9ae5d0989f34..5f2e06709592 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/klass/simple_service_class.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/klass/simple_service_class.bal @@ -79,8 +79,8 @@ function testServiceObjectValue() { var y = wait callMethod(s, "$get$foo$path"); assertEquality(y, s.message + "foo"); - // "$get$." is encorded into "$gen$$get$$0046" - var z = wait callMethod(s, "$gen$$get$$0046"); + // "$get$." is encoded into "$gen$$get$&0046" + var z = wait callMethod(s, "$gen$$get$&0046"); assertEquality(z, s.message + "dot"); var rParamVal0 = wait callMethodWithParams(s, "$get$foo$^", [1]); diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/query-action.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/query-action.bal index 5ba711795070..0efc2aed6906 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/query-action.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/query-action.bal @@ -862,6 +862,26 @@ function testJoinedQueryActionWithRegExp() { "(?:A|B|[ab-fgh]+(?im-x:[cdeg-k]??AAAB*[^abc-efg](?:A|B|[ab-fgh]+(?im-x:[cdeg-k]??A)|)|^|PQ?)|)|^|PQ?"); } +function testQueryActionWithCollectClauseInsideLeClause1() { + int[][] arr = [[1, 2], [3, 4], [5, 6]]; + int total = 0; + from var i in arr + let int tot = from var j in i collect sum(j) + do { + total += tot; + }; + assertEquality(21, total); +} + +function testQueryActionWithCollectClauseInsideLeClause2() { + float[][] items = [[1.1, 2.1], [3.1, 4.1], [5.2, 6.2]]; + from var i in items + let float tot = from var j in i collect sum(j) + do { + + }; +} + function assertEquality(any|error expected, any|error actual) { if expected is anydata && actual is anydata && expected == actual { return; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_or_expr.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_or_expr.bal index d37d6d8c5ac3..f5aa6e3e4461 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_or_expr.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_or_expr.bal @@ -988,11 +988,7 @@ type Student record { function calGraduationYear(int year) returns int => year + 5; function getBestStudents() returns any|error { - Student s1 = {firstName: "Martin", lastName: "Sadler", intakeYear: 1990, gpa: 3.5}; - Student s2 = {firstName: "Ranjan", lastName: "Fonseka", intakeYear: 2001, gpa: 1.9}; - Student s3 = {firstName: "Michelle", lastName: "Guthrie", intakeYear: 2002, gpa: 3.7}; - Student s4 = {firstName: "George", lastName: "Fernando", intakeYear: 2005, gpa: 4.0}; - Student[] studentList = [s1, s2, s3]; + Student[] studentList = getStudents(); return from var student in studentList where student.gpa >= 2.0 @@ -1006,6 +1002,35 @@ function testQueryActionOrExprWithAnyOrErrResultType() { assertTrue(getBestStudents() is record {|string name; string degree; int graduationYear;|}[]); } +function getStudents() returns Student[] { + return [ + {firstName: "Martin", lastName: "Sadler", intakeYear: 1990, gpa: 3.5}, + {firstName: "Ranjan", lastName: "Fonseka", intakeYear: 2001, gpa: 1.9}, + {firstName: "Michelle", lastName: "Guthrie", intakeYear: 2002, gpa: 3.7}, + {firstName: "George", lastName: "Fernando", intakeYear: 2005, gpa: 4.0} + ]; +} + +function testQueryActionWithLetExpression() { + Student[] studentList = getStudents(); + float[] actualGpaList = []; + _ = from var {gpa} in studentList + do { + float gpaVal = let var sGpa = gpa in sGpa; + actualGpaList.push(gpaVal); + }; + assertEquality([3.5, 1.9, 3.7, 4.0], actualGpaList); + + int[] actualValues = []; + int weight = 2; + _ = from var i in [1, 2, 3, 4] + do { + int gpaVal = let var sGpa = i in weight * i; + actualValues.push(gpaVal); + }; + assertEquality([2, 4, 6, 8], actualValues); +} + const ASSERTION_ERROR_REASON = "AssertionError"; function assertEquality(anydata expected, anydata actual) { diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_with_final_clause_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_with_final_clause_negative.bal new file mode 100644 index 000000000000..3f1e885c9be9 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/query_action_with_final_clause_negative.bal @@ -0,0 +1,23 @@ +function queryActionWithSelectClause() { + from var i in [1, 2, 3, 4] + select i + do { + + }; +} + +function queryActionWithCollectClasue1() { + from var i in [1, 2, 3, 4] + collect i + do { + + }; +} + +function queryActionWithCollectClasue2() { + from var i in [1, 2, 3, 4] + collect sum(i) + do { + + }; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/async/main.bal b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/async/main.bal index adaf1a209dfa..011406a86148 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/async/main.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/async/main.bal @@ -337,14 +337,14 @@ public function main() { test:assertEquals(isolatedServiceClass.asyncGetA(), 3); test:assertTrue(isolatedServiceClass.isIsolated()); test:assertTrue(isolatedServiceClass.isIsolatedFunction()); - test:assertTrue(isolatedServiceClass.isIsolatedFunctionWithName("$gen$$getA$$0046")); + test:assertTrue(isolatedServiceClass.isIsolatedFunctionWithName("$gen$$getA$&0046")); NonIsolatedServiceClass nonIsolatedServiceClass = new (); test:assertEquals(nonIsolatedServiceClass.callGetA(), 4); test:assertEquals(nonIsolatedServiceClass.asyncGetA(), 4); test:assertFalse(nonIsolatedServiceClass.isIsolated()); test:assertFalse(nonIsolatedServiceClass.isIsolatedFunction()); - test:assertFalse(nonIsolatedServiceClass.isIsolatedFunctionWithName("$gen$$getA$$0046")); + test:assertFalse(nonIsolatedServiceClass.isIsolatedFunctionWithName("$gen$$getA$&0046")); // invokeAsync api calls negative test cases diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/identifier_utils/main.bal b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/identifier_utils/main.bal index 8b8fa9362fbf..f3970e5cfe0d 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/identifier_utils/main.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/identifier_utils/main.bal @@ -54,7 +54,7 @@ function testFunctionParameters() { function testIdentifierDecoding() { test:assertEquals(decodeIdentifier("üňĩćőđę_ƈȏɳʂʈ_IL"), "üňĩćőđę_ƈȏɳʂʈ_IL"); test:assertEquals(decodeIdentifier("const_IL_123"), "const_IL_123"); - test:assertEquals(decodeIdentifier(" $0047$0058@$0091`{~_IL"), " /:@[`{~_IL"); + test:assertEquals(decodeIdentifier(" &0047&0058@&0091`{~_IL"), " /:@[`{~_IL"); } function testEscapeSpecialCharacters() { diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/no_strand/Ballerina.toml b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/no_strand/Ballerina.toml new file mode 100644 index 000000000000..7c01db43e064 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/no_strand/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org= "testorg" +name="no_strand" +version= "1.0.0" diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/no_strand/main.bal b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/no_strand/main.bal new file mode 100644 index 000000000000..96d3a1c5e4e8 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/no_strand/main.bal @@ -0,0 +1,55 @@ +// Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). +// +// WSO2 Inc. 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. + +import ballerina/lang.runtime; + +public class Listener { + + private string name = ""; + + public function init(string name){ + self.name = name; + } + + public function 'start() returns error? { + } + + public function gracefulStop() returns error? { + } + + public function immediateStop() returns error? { + } + + public function attach(service object {} s, string[]|string? name = ()) returns error? { + } + + public function detach(service object {} s) returns error? { + } +} + +public function main() { + Listener l = new("TestListener"); + runtime:registerListener(l); +} + +public type MutualSslHandshake record {| + MutualSslStatus status = PASSED; + string? base64EncodedCert = (); +|}; + +public type MutualSslStatus PASSED|FAILED|(); +public const PASSED = "passed"; +public const FAILED = "failed"; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/utils/modules/jsons/jsons.bal b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/utils/modules/jsons/jsons.bal index a06820f826a2..9e1e9ed47280 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/utils/modules/jsons/jsons.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/runtime/api/utils/modules/jsons/jsons.bal @@ -118,6 +118,10 @@ function testConvertJSON() { jval = null; jval_result = trap convertJSON(jval, typeof ()); test:assertEquals(jval_result, ()); + + handle val = java:fromString("apple"); + jval_result = trap convertJSON(val, string); + test:assertEquals(jval_result, "apple"); } function convertJSONToRecord(anydata v, typedesc t) returns map = @java:Method { @@ -125,7 +129,7 @@ function convertJSONToRecord(anydata v, typedesc t) returns map t) returns anydata = @java:Method { +function convertJSON(any v, typedesc t) returns anydata = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.runtime.api.tests.JsonValues", name: "testConvertJSON" } external; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/type-guard.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/type-guard.bal index 4c4ecd5d090d..72acd764670c 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/type-guard.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/type-guard.bal @@ -1815,6 +1815,47 @@ function checkIsExpressionWithCircularTuple2(Type t) returns string? { } } +function testSingletonTypeNarrowedTypeDesc() { + assertEquality(testSingletonTypeNarrowedTypeDesc1(1,1), 1); + assertEquality(testSingletonTypeNarrowedTypeDesc2(3,1), 3); + assertEquality(testSingletonTypeNarrowedTypeDesc3("","abc"), "abc"); +} + +function testSingletonTypeNarrowedTypeDesc1(int foo, int bar) returns int { + if foo != 1 { + return foo; + } else if bar != 1 && foo == 1 { + return bar; + } else if foo == 1 && bar == 1 { + return 1; + } + return -1; +} + +function testSingletonTypeNarrowedTypeDesc2(1|2|3 foo, int bar) returns int { + if foo == 1 { + return foo; + } else if bar != 1 && foo == 2 { + return bar; + } else if foo == 3 && bar == 1 { + return 3; + } + return -1; +} + +function testSingletonTypeNarrowedTypeDesc3(string foo, string bar) returns string { + if foo != "" { + return foo; + } + if bar != "" && foo == "" { + return bar; + } + if foo == "" && bar == "" { + return ""; + } + return ""; +} + function assertTrue(anydata actual) { assertEquality(true, actual); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal index bc5e6a8db91b..d7de92a7a443 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/finaltypes/test_implicitly_final_negative.bal @@ -44,3 +44,25 @@ listener test:MockListener ml = new (8080); public function testChangingListenerVariableAfterDefining() { ml = new test:MockListener(8081); } + +function testRestParamFinal(string p1, string... p2) { + p2 = ["a", "b"]; +} + +function (int a, int... b) testModuleLevelRestParamFinal = function (int i, int... b) { + b = []; + }; + +public function testLocalLevelRestParamFinal() { + int[] arr = []; + function (int a, int... b) func = function (int i, int... b) { + b = arr; + }; +} + +public function testLocalLevelRestParamFinalWithVar() { + int[] arr = []; + var func = function (int i, int... b) { + b = arr; + }; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_inherently_immutable_type.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_inherently_immutable_type.bal index faaf6de44071..f5a9529587cf 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_inherently_immutable_type.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_inherently_immutable_type.bal @@ -16,6 +16,7 @@ import ballerina/jballerina.java; import ballerina/lang.'xml; +import ballerina/lang.regexp; // readonly-type-descriptor := readonly // A shape belongs to the type readonly if its read-only bit is on. @@ -163,6 +164,14 @@ function testRuntimeIsTypeForInherentlyImmutableBasicTypes() { 'xml:Text xmlText = xml `xml text`; any n = xmlText; assertTrue(n is readonly); + + any reg1 = re `pattern`; + assertTrue(reg1 is readonly); + + readonly reg2 = re `pattern`; + assertTrue(reg2 is readonly); + + assertTrue(regexp:fromString("pattern") is readonly); } function testRuntimeIsTypeForNeverImmutableBasicTypes() { diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type.bal index ebae240dc5b2..89489482b63d 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type.bal @@ -1197,6 +1197,11 @@ function testReadOnlyIntersectionWithJsonAndAnydata() { assertFalse(l is (string|error)[]); } +function testSelectivelyImmutabilityWithRegexp() { + (stream|string:RegExp) & readonly x = re `pattern`; + assertTrue(x is (string:RegExp & readonly)); +} + const ASSERTION_ERROR_REASON = "AssertionError"; function assertTrue(any|error actual) { diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type_negative.bal index 62657323d06a..65eafecc2bbd 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type_negative.bal @@ -407,3 +407,7 @@ function testReadOnlyIntersectionWithJsonAndAnydataNegative() { (xml & readonly)[] n = k; // error (json & readonly)[] _ = n; // error } + +function testUnsupportedIntersectionWithReadonly(stream strm) { + (stream|string:RegExp) & readonly _ = strm; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/regexp/regexp_type_test.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/regexp/regexp_type_test.bal index edaca3d8ece0..00f785e2f7b0 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/regexp/regexp_type_test.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/regexp/regexp_type_test.bal @@ -13,6 +13,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. +import ballerina/lang.regexp; function testBasicRegExp() { string:RegExp _ = re `AB*|[^c-d]{1,5}`; @@ -105,6 +106,27 @@ function testRegExpWithUserDefinedType() { assertEquality(re `AB*|[^c-d]{1,5}`, x7); } +type T1 string:RegExp & readonly; +type T2 regexp:RegExp & readonly; + +type Foo record {| + int e; + readonly regexp:RegExp f; +|}; + +Foo & readonly rf = {e: 1, f: re `test`}; + +function testRegExpReadonlyLocalVars() { + string:RegExp & readonly r1 = re `test`; + assertEquality(true, r1 is readonly); + + T1 & readonly r2 = re `test`; + assertEquality(true, r2 is readonly); + + (T2 & readonly) & string:RegExp r3 = re `test`; + assertEquality(true, r3 is readonly); +} + const ASSERTION_ERROR_REASON = "AssertionError"; function assertEquality(any|error expected, any|error actual) { diff --git a/tests/testerina-integration-test/build.gradle b/tests/testerina-integration-test/build.gradle index cc39c16409d3..d91c3ecc8800 100644 --- a/tests/testerina-integration-test/build.gradle +++ b/tests/testerina-integration-test/build.gradle @@ -23,6 +23,9 @@ def extractedDist = "$buildDir/extractedDistribution/jballerina-tools-${project. configurations { jballerinaTools + compilerPluginJar { + transitive false + } } dependencies { @@ -48,12 +51,22 @@ dependencies { implementation group: 'org.ow2.asm', name: 'asm-commons', version: "${project.ow2AsmCommonsVersion}" implementation group: 'org.ow2.asm', name: 'asm-tree', version: "${project.ow2AsmTreeVersion}" implementation group: 'io.github.java-diff-utils', name: 'java-diff-utils', version: "${project.javaDiffUtilsVersion}" + + compilerPluginJar project(':project-api-test-artifact:init-function-code-modify-compiler-plugin') + compilerPluginJar project(':project-api-test-artifact:remove-function-code-modify-compiler-plugin') + compilerPluginJar project(':project-api-test-artifact:add-remove-function-code-modify-compiler-plugin') + compilerPluginJar project(':project-api-test-artifact:diagnostic-utils-lib') } jacoco { toolVersion = "${project.jacocoVersion}" } +task copyCompilerPluginJars(type: Copy) { + from configurations.compilerPluginJar + into "$buildDir/compiler-plugin-jars" +} + task extractDistribution(type: Copy) { dependsOn ':jballerina-tools:build' from zipTree(configurations.jballerinaTools.asPath) @@ -72,6 +85,7 @@ test { mustRunAfter ':jballerina-integration-test:test' dependsOn extractDistribution dependsOn testUtilsJar + dependsOn copyCompilerPluginJars systemProperty 'enableTesterinaTests', 'true' maxParallelForks = 1 diff --git a/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/CodeCoverageReportTest.java b/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/CodeCoverageReportTest.java index 82693a89b09d..3e2e37b0c219 100644 --- a/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/CodeCoverageReportTest.java +++ b/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/CodeCoverageReportTest.java @@ -111,42 +111,42 @@ public void multipleModulePkgCoverageTest() throws BallerinaTestException { copyReportDTDFile(reportRoot); ArrayList expectedPackageNames = new ArrayList<>(); Collections.addAll(expectedPackageNames, - "testerina_report/foo$0046math$test/0/constants", + "testerina_report/foo&0046math$test/0/constants", "testerina_report/foo$test/0/types", - "testerina_report/foo$0046bar/0/creators", - "testerina_report/foo$0046bar$0046tests/0", - "testerina_report/foo$0046math$test/0", + "testerina_report/foo&0046bar/0/creators", + "testerina_report/foo&0046bar&0046tests/0", + "testerina_report/foo&0046math$test/0", "testerina_report/foo/0/creators", - "testerina_report/foo$0046bar$0046tests/0/creators", - "testerina_report/foo$0046bar$0046tests/0/types", - "testerina_report/foo$0046math/0/constants", - "testerina_report/foo$0046bar$0046tests$test/0/constants", - "testerina_report/foo$0046math/0/types", - "testerina_report/foo$0046bar/0/annotations", + "testerina_report/foo&0046bar&0046tests/0/creators", + "testerina_report/foo&0046bar&0046tests/0/types", + "testerina_report/foo&0046math/0/constants", + "testerina_report/foo&0046bar&0046tests$test/0/constants", + "testerina_report/foo&0046math/0/types", + "testerina_report/foo&0046bar/0/annotations", "test-report-tests/modules/math", - "testerina_report/foo$0046math$test/0/creators", - "testerina_report/foo$0046math/0/creators", - "testerina_report/foo$0046bar$0046tests$test/0/types", + "testerina_report/foo&0046math$test/0/creators", + "testerina_report/foo&0046math/0/creators", + "testerina_report/foo&0046bar&0046tests$test/0/types", "test-report-tests", "testerina_report/foo/0/types", - "testerina_report/foo$0046bar/0/types", - "testerina_report/foo$0046bar$0046tests/0/constants", + "testerina_report/foo&0046bar/0/types", + "testerina_report/foo&0046bar&0046tests/0/constants", "testerina_report/foo$test/0", "testerina_report/foo/0", - "testerina_report/foo$0046bar/0", + "testerina_report/foo&0046bar/0", "testerina_report/foo/0/annotations", "testerina_report/foo/0/constants", - "testerina_report/foo$0046math$test/0/types", - "testerina_report/foo$0046math/0/annotations", - "testerina_report/foo$0046bar$0046tests$test/0/creators", - "testerina_report/foo$0046bar$0046tests$test/0", - "testerina_report/foo$0046bar$0046tests/0/annotations", - "testerina_report/foo$0046bar$0046tests$test/0/annotations", - "testerina_report/foo$0046math$test/0/annotations", + "testerina_report/foo&0046math$test/0/types", + "testerina_report/foo&0046math/0/annotations", + "testerina_report/foo&0046bar&0046tests$test/0/creators", + "testerina_report/foo&0046bar&0046tests$test/0", + "testerina_report/foo&0046bar&0046tests/0/annotations", + "testerina_report/foo&0046bar&0046tests$test/0/annotations", + "testerina_report/foo&0046math$test/0/annotations", "testerina_report/foo$test/0/annotations", "testerina_report/foo$test/0/constants", - "testerina_report/foo$0046math/0", - "testerina_report/foo$0046bar/0/constants", + "testerina_report/foo&0046math/0", + "testerina_report/foo&0046bar/0/constants", "test-report-tests/modules/bar", "testerina_report/foo$test/0/creators" ); diff --git a/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/CodegenCodeCoverageTest.java b/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/CodegenCodeCoverageTest.java index 97c145ff8974..4e321b2e1171 100644 --- a/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/CodegenCodeCoverageTest.java +++ b/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/CodegenCodeCoverageTest.java @@ -25,8 +25,10 @@ import org.ballerinalang.test.context.BMainInstance; import org.ballerinalang.test.context.BallerinaTestException; import org.ballerinalang.test.context.LogLeecher; +import org.ballerinalang.testerina.test.utils.FileUtils; import org.testng.Assert; import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.BufferedReader; @@ -36,57 +38,118 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; +import java.util.Map; /** * Test class to test report for Codegen ballerina projects. */ public class CodegenCodeCoverageTest extends BaseTestCase { + private static final String TOTAL_TESTS = "totalTests"; + private static final String PASSED_TESTS = "passed"; + private static final String FAILED_TESTS = "failed"; + private static final String SKIPPED_TESTS = "skipped"; + private static final String FOO_COVERED = "fooCovered"; + private static final String FOO_MISSED = "fooMissed"; + private static final String MAIN_COVERED = "mainCovered"; + private static final String MAIN_MISSED = "mainMissed"; + Path repoBalaPath; private BMainInstance balClient; - private String projectPath; - private Path resultsJsonPath; private JsonObject resultObj; - private static final int TOTAL_TESTS = 0; - private static final int PASSED = 1; - private static final int SKIPPED = 2; - private static final int FAILED = 3; @BeforeClass public void setup() throws BallerinaTestException, IOException { balClient = new BMainInstance(balServer); - projectPath = projectBasedTestsPath.resolve("codegen-coverage-test").toString(); - resultsJsonPath = projectBasedTestsPath.resolve("codegen-coverage-test").resolve("target").resolve("report") - .resolve("test_results.json"); - Path repoBalaPath = Paths.get(balServer.getServerHome()).resolve("repo"); - Path balaPath = projectBasedTestsPath.resolve( - "codegen-coverage-test/balas/package_comp_plugin_code_modify_add_function.bala"); - BCompileUtil.copyBalaToExtractedDist(balaPath, - "samjs", - "package_comp_plugin_code_modify_add_function", - "0.1.0", - repoBalaPath); + FileUtils.copyFolder(Paths.get("build").resolve("compiler-plugin-jars"), + projectBasedTestsPath.resolve("compiler-plugin-jars")); + repoBalaPath = Paths.get(balServer.getServerHome()).resolve("repo"); } - @Test(enabled = false) - public void codegenCoverageTest() throws BallerinaTestException { - String[] args = new String[] {"--code-coverage"}; - runCommand(args); - int total = 2, passed = 2, failed = 0, skipped = 0; - int[] status = {total, passed, failed, skipped}; - validateStatus(status, "codegen_coverage_test"); - validateCoverage(); + @DataProvider(name = "provideCoverageData") + public Object[][] provideCoverageData() { + return new Object[][]{ + { + // adds a new function to all files + "line-insert-test", + "package_comp_plugin_code_modify_add_function", + Map.of(TOTAL_TESTS, 2, PASSED_TESTS, 2, FAILED_TESTS, 0, SKIPPED_TESTS, 0), + Map.of( + FOO_COVERED, new int[]{3, 4}, + FOO_MISSED, new int[]{8, 11, 12}, + MAIN_COVERED, new int[]{8, 9}, + MAIN_MISSED, new int[]{3, 4, 5} + ) + }, + { + // remove "bar" function if available from all files + "line-remove-test", + "package_comp_plugin_code_modify_remove_function", + Map.of(TOTAL_TESTS, 2, PASSED_TESTS, 2, FAILED_TESTS, 0, SKIPPED_TESTS, 0), + Map.of( + FOO_COVERED, new int[]{3, 4}, + FOO_MISSED, new int[]{11, 12}, + MAIN_COVERED, new int[]{8, 9}, + MAIN_MISSED, new int[]{3, 4, 5} + ) + }, + { + // remove empty functions and add a new function. + "line-insert-and-remove-test", + "package_comp_plugin_code_modify_add_remove_function", + Map.of(TOTAL_TESTS, 2, PASSED_TESTS, 2, FAILED_TESTS, 0, SKIPPED_TESTS, 0), + Map.of( + FOO_COVERED, new int[]{3, 4}, + FOO_MISSED, new int[]{9, 10}, + MAIN_COVERED, new int[]{17, 18}, + MAIN_MISSED, new int[]{3, 4, 5} + ) + } + }; + } + + @Test(description = "Test code coverage report generation for a codegen project", + dataProvider = "provideCoverageData") + public void codegenCoverageTest(String projectName, String compilerPluginName, Map status, Map coverage) throws BallerinaTestException, IOException { + publishCompilerPlugin(compilerPluginName); + String[] args = new String[]{"--code-coverage"}; + runCommand(projectName, args); + validateStatus(status); + validateCoverage(coverage); + } + + private void publishCompilerPlugin(String compilerPluginName) throws BallerinaTestException, IOException { + String compilerPluginBalaPath = projectBasedTestsPath.resolve("compiler-plugins") + .resolve(compilerPluginName).toString(); + balClient.runMain("pack", new String[]{}, null, null, new LogLeecher[]{}, compilerPluginBalaPath); + Path balaPath = projectBasedTestsPath.resolve(compilerPluginBalaPath).resolve("target").resolve("bala") + .resolve("samjs-" + compilerPluginName + "-java17-0.1.0.bala"); + BCompileUtil.copyBalaToExtractedDist(balaPath, "samjs", compilerPluginName, "0.1.0", repoBalaPath); } - private void validateCoverage() { - JsonParser parser = new JsonParser(); + + private void validateStatus(Map status) { + String moduleName = "codegen_coverage_test"; + for (JsonElement obj : resultObj.get("moduleStatus").getAsJsonArray()) { + JsonObject moduleObj = ((JsonObject) obj); + if (moduleName.equals(moduleObj.get("name").getAsString())) { + String msg = "Status check failed for module : " + moduleName + "."; + validateStatus(status, moduleObj, msg); + return; + } + } + Assert.fail("Module status for " + moduleName + " could not be found"); + } + + private void validateCoverage(Map coverage) { // foo file - int[] fooCovered = new int[] {3, 4}, fooMissed = new int[] {8, 11, 12}; + int[] fooCovered = coverage.get(FOO_COVERED), fooMissed = coverage.get(FOO_MISSED); float fooPercentageVal = (float) (fooCovered.length) / (fooCovered.length + fooMissed.length) * 100; float fooPercentage = (float) (Math.round(fooPercentageVal * 100.0) / 100.0); int fooCoveredVal = fooCovered.length, fooMissedVal = fooMissed.length; // main file - int[] mainCovered = new int[] {8, 9}, mainMissed = new int[] {3, 4, 5}; + int[] mainCovered = coverage.get(MAIN_COVERED), mainMissed = coverage.get(MAIN_MISSED); float mainPercentageVal = (float) (mainCovered.length) / (mainCovered.length + mainMissed.length) * 100; float mainPercentage = @@ -105,16 +168,16 @@ private void validateCoverage() { for (JsonElement sourceFiles : moduleObj.get("sourceFiles").getAsJsonArray()) { JsonObject fileObj = (JsonObject) sourceFiles; if ("foo.bal".equals(fileObj.get("name").getAsString())) { - Assert.assertEquals(parser.parse(Arrays.toString(fooCovered)), - parser.parse(fileObj.get("coveredLines").getAsJsonArray().toString())); - Assert.assertEquals(parser.parse(Arrays.toString(fooMissed)), - parser.parse(fileObj.get("missedLines").getAsJsonArray().toString())); + Assert.assertEquals(JsonParser.parseString(Arrays.toString(fooCovered)), + JsonParser.parseString(fileObj.get("coveredLines").getAsJsonArray().toString())); + Assert.assertEquals(JsonParser.parseString(Arrays.toString(fooMissed)), + JsonParser.parseString(fileObj.get("missedLines").getAsJsonArray().toString())); Assert.assertEquals(fooPercentage, fileObj.get("coveragePercentage").getAsFloat()); } else if ("main.bal".equals(fileObj.get("name").getAsString())) { - Assert.assertEquals(parser.parse(Arrays.toString(mainCovered)), - parser.parse(fileObj.get("coveredLines").getAsJsonArray().toString())); - Assert.assertEquals(parser.parse(Arrays.toString(mainMissed)), - parser.parse(fileObj.get("missedLines").getAsJsonArray().toString())); + Assert.assertEquals(JsonParser.parseString(Arrays.toString(mainCovered)), + JsonParser.parseString(fileObj.get("coveredLines").getAsJsonArray().toString())); + Assert.assertEquals(JsonParser.parseString(Arrays.toString(mainMissed)), + JsonParser.parseString(fileObj.get("missedLines").getAsJsonArray().toString())); Assert.assertEquals(mainPercentage, fileObj.get("coveragePercentage").getAsFloat()); } } @@ -125,28 +188,17 @@ private void validateCoverage() { } - - private void validateStatus(int[] status, String moduleName) { - for (JsonElement obj : resultObj.get("moduleStatus").getAsJsonArray()) { - JsonObject moduleObj = ((JsonObject) obj); - if (moduleName.equals(moduleObj.get("name").getAsString())) { - String msg = "Status check failed for module : " + moduleName + "."; - validateStatus(status, moduleObj, msg); - return; - } - } - Assert.fail("Module status for " + moduleName + " could not be found"); - } - - private void validateStatus(int[] status, JsonObject obj, String msg) { - Assert.assertEquals(obj.get("totalTests").getAsInt(), status[TOTAL_TESTS], msg); - Assert.assertEquals(obj.get("passed").getAsInt(), status[PASSED], msg); - Assert.assertEquals(obj.get("failed").getAsInt(), status[FAILED], msg); - Assert.assertEquals(obj.get("skipped").getAsInt(), status[SKIPPED], msg); + private void validateStatus(Map status, JsonObject obj, String msg) { + Assert.assertEquals(obj.get(TOTAL_TESTS).getAsInt(), status.get(TOTAL_TESTS), msg); + Assert.assertEquals(obj.get(PASSED_TESTS).getAsInt(), status.get(PASSED_TESTS), msg); + Assert.assertEquals(obj.get(FAILED_TESTS).getAsInt(), status.get(FAILED_TESTS), msg); + Assert.assertEquals(obj.get(SKIPPED_TESTS).getAsInt(), status.get(SKIPPED_TESTS), msg); } - private void runCommand(String[] args) throws BallerinaTestException { - balClient.runMain("test", args, null, new String[]{}, new LogLeecher[]{}, projectPath); + private void runCommand(String projectName, String[] args) throws BallerinaTestException { + Path projectPath = projectBasedTestsPath.resolve("code-coverage-report-test").resolve(projectName); + Path resultsJsonPath = projectPath.resolve("target").resolve("report").resolve("test_results.json"); + balClient.runMain("test", args, null, new String[]{}, new LogLeecher[]{}, projectPath.toString()); Gson gson = new Gson(); try (BufferedReader bufferedReader = Files.newBufferedReader(resultsJsonPath, StandardCharsets.UTF_8)) { resultObj = gson.fromJson(bufferedReader, JsonObject.class); diff --git a/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/DataProviderTest.java b/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/DataProviderTest.java index 606ffb07c369..b2497259b6ec 100644 --- a/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/DataProviderTest.java +++ b/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/DataProviderTest.java @@ -83,6 +83,15 @@ public void testValidDataProviderCase() throws BallerinaTestException, IOExcepti AssertionUtils.assertOutput("DataProviderTest-testValidDataProviderCase.txt", output); } + @Test (dependsOnMethods = "testValidDataProviderWithFail") + public void testValidDataProviderCaseWithoutQuotes() throws BallerinaTestException, IOException { + String[] args = mergeCoverageArgs(new String[]{"--tests", "dataproviders:jsonDataProviderTest#json1", + "data-providers"}); + String output = balClient.runMainAndReadStdOut("test", args, + new HashMap<>(), projectPath, false); + AssertionUtils.assertOutput("DataProviderTest-testValidDataProviderCase.txt", output); + } + @Test (dependsOnMethods = "testValidDataProviderCase") public void testDataProviderWithMixedType() throws BallerinaTestException, IOException { String[] args = mergeCoverageArgs(new String[]{"--tests", "testFunction1#'CaseNew*'", @@ -92,6 +101,15 @@ public void testDataProviderWithMixedType() throws BallerinaTestException, IOExc AssertionUtils.assertOutput("DataProviderTest-testDataProviderWithMixedType.txt", output); } + @Test (dependsOnMethods = "testValidDataProviderCase") + public void testDataProviderWithMixedTypeWithoutQuotes() throws BallerinaTestException, IOException { + String[] args = mergeCoverageArgs(new String[]{"--tests", "testFunction1#CaseNew*", + "data-providers"}); + String output = balClient.runMainAndReadStdOut("test", args, + new HashMap<>(), projectPath, false); + AssertionUtils.assertOutput("DataProviderTest-testDataProviderWithMixedType.txt", output); + } + @Test (dependsOnMethods = "testDataProviderWithMixedType") public void testWithSpecialKeys() throws BallerinaTestException, IOException { String[] args = mergeCoverageArgs(new String[]{"--tests", "testFunction2", diff --git a/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/SingleTestExecutionWithInitFailuresTest.java b/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/SingleTestExecutionWithInitFailuresTest.java new file mode 100644 index 000000000000..2556200fc113 --- /dev/null +++ b/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/SingleTestExecutionWithInitFailuresTest.java @@ -0,0 +1,47 @@ +/* + * 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.testerina.test; + +import org.ballerinalang.test.context.BMainInstance; +import org.ballerinalang.test.context.BallerinaTestException; +import org.ballerinalang.testerina.test.utils.AssertionUtils; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.util.HashMap; + +public class SingleTestExecutionWithInitFailuresTest extends BaseTestCase { + + private BMainInstance balClient; + private String projectPath; + + @BeforeClass + public void setup() throws BallerinaTestException { + balClient = new BMainInstance(balServer); + projectPath = singleFileTestsPath.resolve("single-test-execution").toString(); + } + + @Test + public void testSingleBalTestExecutionWithInitFailure() throws BallerinaTestException, IOException { + String[] args = mergeCoverageArgs(new String[]{"--tests", "testFunc", "bal-test-with-init-failure.bal"}); + String output = balClient.runMainAndReadStdOut("test", args, new HashMap<>(), projectPath, false); + AssertionUtils.assertOutput("SingleFileTestExecutionWithInitFailure.txt", output); + } +} diff --git a/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/TestExecutionWithInitFailuresTest.java b/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/TestExecutionWithInitFailuresTest.java new file mode 100644 index 000000000000..906a1aa637e0 --- /dev/null +++ b/tests/testerina-integration-test/src/test/java/org/ballerinalang/testerina/test/TestExecutionWithInitFailuresTest.java @@ -0,0 +1,67 @@ +/* + * 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.testerina.test; + +import org.ballerinalang.test.context.BMainInstance; +import org.ballerinalang.test.context.BallerinaTestException; +import org.ballerinalang.testerina.test.utils.AssertionUtils; +import org.ballerinalang.testerina.test.utils.CommonUtils; +import org.ballerinalang.testerina.test.utils.FileUtils; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.HashMap; + +import static org.ballerinalang.testerina.test.BaseTestCase.balServer; +import static org.ballerinalang.testerina.test.BaseTestCase.projectBasedTestsPath; + +public class TestExecutionWithInitFailuresTest { + + private BMainInstance balClient; + private String projectPath; + + @BeforeClass + public void setup() throws BallerinaTestException { + balClient = new BMainInstance(balServer); + projectPath = projectBasedTestsPath.resolve("test-execution-with-init-failure").toString(); + } + + @Test() + public void testModuleExecutionFlow() throws BallerinaTestException, IOException { + String[] args = new String[]{}; + String output = balClient.runMainAndReadStdOut("test", args, + new HashMap<>(), projectPath, false); + String firstString = "tests.test_execute-generated_"; + String endString = "lineNumber"; + output = CommonUtils.replaceVaryingString(firstString, endString, output); + AssertionUtils.assertOutput("TestExecutionWithInitFailures.txt", output); + } + + @AfterMethod + public void copyExec() { + try { + FileUtils.copyBallerinaExec(Paths.get(projectPath), String.valueOf(System.currentTimeMillis())); + } catch (IOException e) { + // ignore exception + } + } +} diff --git a/tests/testerina-integration-test/src/test/resources/command-outputs/unix/DataProviderTest-testDataProviderSingleFailure.txt b/tests/testerina-integration-test/src/test/resources/command-outputs/unix/DataProviderTest-testDataProviderSingleFailure.txt index 48139cc61cf4..67999415aec0 100644 --- a/tests/testerina-integration-test/src/test/resources/command-outputs/unix/DataProviderTest-testDataProviderSingleFailure.txt +++ b/tests/testerina-integration-test/src/test/resources/command-outputs/unix/DataProviderTest-testDataProviderSingleFailure.txt @@ -19,7 +19,7 @@ Running Tests with Coverage callableName: testDividingValuesNegative moduleName: intg_tests.dataproviders$test.0.tests.new-data-provider-tests fileName: tests/new-data-provider-tests.bal lineNumber: 191 callableName: testDividingValuesNegative$lambda14$ moduleName: intg_tests.dataproviders$test.0.tests.test_execute-generated_*****lineNumber: 18 ",functionName="testDividingValuesNegative") - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 349 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 346 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 diff --git a/tests/testerina-integration-test/src/test/resources/command-outputs/unix/InvalidDataProviderTestCase-testInvalidDataProvider.txt b/tests/testerina-integration-test/src/test/resources/command-outputs/unix/InvalidDataProviderTestCase-testInvalidDataProvider.txt index a27ffae1b440..8714477b5792 100644 --- a/tests/testerina-integration-test/src/test/resources/command-outputs/unix/InvalidDataProviderTestCase-testInvalidDataProvider.txt +++ b/tests/testerina-integration-test/src/test/resources/command-outputs/unix/InvalidDataProviderTestCase-testInvalidDataProvider.txt @@ -13,7 +13,7 @@ Running Tests [fail data provider for the function testInvalidDataProvider] error {ballerina/test:0}ExecutionError ("error("{ballerina/lang.function}IncompatibleArguments",message="arguments of incompatible types: argument list '(int)' cannot be passed to function expecting parameter list '(string)'") callableName: call moduleName: ballerina.lang.function.0 fileName: function.bal lineNumber: 37 - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 339 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 336 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 @@ -21,7 +21,7 @@ Running Tests callableName: startSuite moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 48 callableName: __execute__ fileName: invalid-data-provider-test.bal lineNumber: 37 ",functionName="testInvalidDataProvider") - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 349 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 346 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 diff --git a/tests/testerina-integration-test/src/test/resources/command-outputs/unix/InvalidDataProviderTestCase-testInvalidDataProvider2.txt b/tests/testerina-integration-test/src/test/resources/command-outputs/unix/InvalidDataProviderTestCase-testInvalidDataProvider2.txt index 921212fb9f70..4138232d0ce0 100644 --- a/tests/testerina-integration-test/src/test/resources/command-outputs/unix/InvalidDataProviderTestCase-testInvalidDataProvider2.txt +++ b/tests/testerina-integration-test/src/test/resources/command-outputs/unix/InvalidDataProviderTestCase-testInvalidDataProvider2.txt @@ -15,7 +15,7 @@ Running Tests [fail data provider for the function testInvalidDataProvider2] error {ballerina/test:0}ExecutionError ("error("{ballerina/lang.function}IncompatibleArguments",message="arguments of incompatible types: argument list '(int,int,int)' cannot be passed to function expecting parameter list '(string,string,string)'") callableName: call moduleName: ballerina.lang.function.0 fileName: function.bal lineNumber: 37 - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 339 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 336 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 @@ -23,7 +23,7 @@ Running Tests callableName: startSuite moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 48 callableName: __execute__ fileName: invalid-data-provider-test2.bal lineNumber: 39 ",functionName="testInvalidDataProvider2") - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 349 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 346 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 diff --git a/tests/testerina-integration-test/src/test/resources/command-outputs/unix/InvalidDataProviderTestCase-testInvalidTupleDataProvider.txt b/tests/testerina-integration-test/src/test/resources/command-outputs/unix/InvalidDataProviderTestCase-testInvalidTupleDataProvider.txt index 068ebba3b51e..5811136fd5b6 100644 --- a/tests/testerina-integration-test/src/test/resources/command-outputs/unix/InvalidDataProviderTestCase-testInvalidTupleDataProvider.txt +++ b/tests/testerina-integration-test/src/test/resources/command-outputs/unix/InvalidDataProviderTestCase-testInvalidTupleDataProvider.txt @@ -12,7 +12,7 @@ Running Tests [fail data provider for the function testInvalidTupleDataProvider] error {ballerina/test:0}ExecutionError ("error("{ballerina/lang.function}IncompatibleArguments",message="arguments of incompatible types: argument list '(string,int)' cannot be passed to function expecting parameter list '(string,string)'") callableName: call moduleName: ballerina.lang.function.0 fileName: function.bal lineNumber: 37 - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 339 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 336 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 @@ -20,7 +20,7 @@ Running Tests callableName: startSuite moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 48 callableName: __execute__ fileName: invalid-data-provider-test3.bal lineNumber: 36 ",functionName="testInvalidTupleDataProvider") - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 349 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 346 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 diff --git a/tests/testerina-integration-test/src/test/resources/command-outputs/unix/SingleFileTestExecutionWithInitFailure.txt b/tests/testerina-integration-test/src/test/resources/command-outputs/unix/SingleFileTestExecutionWithInitFailure.txt new file mode 100644 index 000000000000..f7fda808fadd --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/command-outputs/unix/SingleFileTestExecutionWithInitFailure.txt @@ -0,0 +1,10 @@ +Code coverage is not yet supported with single bal files. Ignoring the flag and continuing the test run... +warning: ignoring --includes flag since code coverage is not enabled +Compiling source + bal-test-with-init-failure.bal + +Running Tests + + bal-test-with-init-failure.bal +error: {ballerina}DivisionByZero {"message":" / by zero"} +error: there are test failures diff --git a/tests/testerina-integration-test/src/test/resources/command-outputs/unix/TestExecutionWithInitFailures.txt b/tests/testerina-integration-test/src/test/resources/command-outputs/unix/TestExecutionWithInitFailures.txt new file mode 100644 index 000000000000..d17dbb2fdf17 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/command-outputs/unix/TestExecutionWithInitFailures.txt @@ -0,0 +1,30 @@ +Compiling source + wso2/testExecutionWithModuleInitFailure:0.0.0 + +Running Tests + + testExecutionWithModuleInitFailure.moduleD + [pass] testFunc + + + 1 passing + 0 failing + 0 skipped + + testExecutionWithModuleInitFailure +error: {ballerina}DivisionByZero {"message":" / by zero"} + + testExecutionWithModuleInitFailure.moduleA +error: {ballerina}DivisionByZero {"message":" / by zero"} + + testExecutionWithModuleInitFailure.moduleC + [pass] testFunc + + + 1 passing + 0 failing + 0 skipped + + testExecutionWithModuleInitFailure.moduleB +error: {ballerina}DivisionByZero {"message":" / by zero"} +error: there are test failures diff --git a/tests/testerina-integration-test/src/test/resources/command-outputs/windows/DataProviderTest-testDataProviderSingleFailure.txt b/tests/testerina-integration-test/src/test/resources/command-outputs/windows/DataProviderTest-testDataProviderSingleFailure.txt index c63c5bc8af00..08db5f4bd30c 100644 --- a/tests/testerina-integration-test/src/test/resources/command-outputs/windows/DataProviderTest-testDataProviderSingleFailure.txt +++ b/tests/testerina-integration-test/src/test/resources/command-outputs/windows/DataProviderTest-testDataProviderSingleFailure.txt @@ -19,7 +19,7 @@ Running Tests with Coverage callableName: testDividingValuesNegative moduleName: intg_tests.dataproviders$test.0.tests.new-data-provider-tests fileName: tests/new-data-provider-tests.bal lineNumber: 191 callableName: testDividingValuesNegative$lambda14$ moduleName: intg_tests.dataproviders$test.0.tests.test_execute-generated_*****lineNumber: 18 ",functionName="testDividingValuesNegative") - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 349 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 346 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 diff --git a/tests/testerina-integration-test/src/test/resources/command-outputs/windows/InvalidDataProviderTestCase-testInvalidDataProvider.txt b/tests/testerina-integration-test/src/test/resources/command-outputs/windows/InvalidDataProviderTestCase-testInvalidDataProvider.txt index ba73eb492805..b843eb1c0939 100644 --- a/tests/testerina-integration-test/src/test/resources/command-outputs/windows/InvalidDataProviderTestCase-testInvalidDataProvider.txt +++ b/tests/testerina-integration-test/src/test/resources/command-outputs/windows/InvalidDataProviderTestCase-testInvalidDataProvider.txt @@ -13,7 +13,7 @@ Running Tests [fail data provider for the function testInvalidDataProvider] error {ballerina/test:0}ExecutionError ("error("{ballerina/lang.function}IncompatibleArguments",message="arguments of incompatible types: argument list '(int)' cannot be passed to function expecting parameter list '(string)'") callableName: call moduleName: ballerina.lang.function.0 fileName: function.bal lineNumber: 37 - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 339 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 336 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 @@ -21,7 +21,7 @@ Running Tests callableName: startSuite moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 48 callableName: __execute__ fileName: invalid-data-provider-test.bal lineNumber: 37 ",functionName="testInvalidDataProvider") - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 349 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 346 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 diff --git a/tests/testerina-integration-test/src/test/resources/command-outputs/windows/InvalidDataProviderTestCase-testInvalidDataProvider2.txt b/tests/testerina-integration-test/src/test/resources/command-outputs/windows/InvalidDataProviderTestCase-testInvalidDataProvider2.txt index 95f28ab15b5a..76b7ee13ddf5 100644 --- a/tests/testerina-integration-test/src/test/resources/command-outputs/windows/InvalidDataProviderTestCase-testInvalidDataProvider2.txt +++ b/tests/testerina-integration-test/src/test/resources/command-outputs/windows/InvalidDataProviderTestCase-testInvalidDataProvider2.txt @@ -15,7 +15,7 @@ Running Tests [fail data provider for the function testInvalidDataProvider2] error {ballerina/test:0}ExecutionError ("error("{ballerina/lang.function}IncompatibleArguments",message="arguments of incompatible types: argument list '(int,int,int)' cannot be passed to function expecting parameter list '(string,string,string)'") callableName: call moduleName: ballerina.lang.function.0 fileName: function.bal lineNumber: 37 - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 339 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 336 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 @@ -23,7 +23,7 @@ Running Tests callableName: startSuite moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 48 callableName: __execute__ fileName: invalid-data-provider-test2.bal lineNumber: 39 ",functionName="testInvalidDataProvider2") - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 349 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 346 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 diff --git a/tests/testerina-integration-test/src/test/resources/command-outputs/windows/InvalidDataProviderTestCase-testInvalidTupleDataProvider.txt b/tests/testerina-integration-test/src/test/resources/command-outputs/windows/InvalidDataProviderTestCase-testInvalidTupleDataProvider.txt index 43fb04a46c97..e1f62e2ac8ad 100644 --- a/tests/testerina-integration-test/src/test/resources/command-outputs/windows/InvalidDataProviderTestCase-testInvalidTupleDataProvider.txt +++ b/tests/testerina-integration-test/src/test/resources/command-outputs/windows/InvalidDataProviderTestCase-testInvalidTupleDataProvider.txt @@ -12,7 +12,7 @@ Running Tests [fail data provider for the function testInvalidTupleDataProvider] error {ballerina/test:0}ExecutionError ("error("{ballerina/lang.function}IncompatibleArguments",message="arguments of incompatible types: argument list '(string,int)' cannot be passed to function expecting parameter list '(string,string)'") callableName: call moduleName: ballerina.lang.function.0 fileName: function.bal lineNumber: 37 - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 339 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 336 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 @@ -20,7 +20,7 @@ Running Tests callableName: startSuite moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 48 callableName: __execute__ fileName: invalid-data-provider-test3.bal lineNumber: 36 ",functionName="testInvalidTupleDataProvider") - callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 349 + callableName: executeTestFunction moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 346 callableName: executeDataDrivenTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 136 callableName: executeDataDrivenTestSet moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 123 callableName: executeTest moduleName: ballerina.test.0 fileName: execute.bal lineNumber: 83 diff --git a/tests/testerina-integration-test/src/test/resources/command-outputs/windows/SingleFileTestExecutionWithInitFailure.txt b/tests/testerina-integration-test/src/test/resources/command-outputs/windows/SingleFileTestExecutionWithInitFailure.txt new file mode 100644 index 000000000000..d3adad116c12 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/command-outputs/windows/SingleFileTestExecutionWithInitFailure.txt @@ -0,0 +1,10 @@ +Code coverage is not yet supported with single bal files. Ignoring the flag and continuing the test run... +warning: ignoring --includes flag since code coverage is not enabled +Compiling source + bal-test-with-init-failure.bal + +Running Tests + + bal-test-with-init-failure.bal +error: {ballerina}DivisionByZero {"message":" / by zero"} +error: there are test failures diff --git a/tests/testerina-integration-test/src/test/resources/command-outputs/windows/TestExecutionWithInitFailures.txt b/tests/testerina-integration-test/src/test/resources/command-outputs/windows/TestExecutionWithInitFailures.txt new file mode 100644 index 000000000000..c235bb1dec8d --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/command-outputs/windows/TestExecutionWithInitFailures.txt @@ -0,0 +1,30 @@ +Compiling source + wso2/testExecutionWithModuleInitFailure:0.0.0 + +Running Tests + + testExecutionWithModuleInitFailure.moduleD + [pass] testFunc + + + 1 passing + 0 failing + 0 skipped + + testExecutionWithModuleInitFailure +error: {ballerina}DivisionByZero {"message":" / by zero"} + + testExecutionWithModuleInitFailure.moduleA +error: {ballerina}DivisionByZero {"message":" / by zero"} + + testExecutionWithModuleInitFailure.moduleC + [pass] testFunc + + + 1 passing + 0 failing + 0 skipped + + testExecutionWithModuleInitFailure.moduleB +error: {ballerina}DivisionByZero {"message":" / by zero"} +error: there are test failures diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/codegen-coverage-test/Ballerina.toml b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-and-remove-test/Ballerina.toml similarity index 100% rename from tests/testerina-integration-test/src/test/resources/project-based-tests/codegen-coverage-test/Ballerina.toml rename to tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-and-remove-test/Ballerina.toml diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-and-remove-test/foo.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-and-remove-test/foo.bal new file mode 100644 index 000000000000..61ac58edf3d0 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-and-remove-test/foo.bal @@ -0,0 +1,10 @@ + +function foo(int a) returns (int) { + return a; +} + +function bar() {} + +function foobar(int a) returns (int) { + return a + 2; +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-and-remove-test/main.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-and-remove-test/main.bal new file mode 100644 index 000000000000..f2c9aea8d584 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-and-remove-test/main.bal @@ -0,0 +1,18 @@ +import samjs/package_comp_plugin_code_modify_add_remove_function as _; + +public function main() { + _ = intAdd(1,2); +} + +function baz () { + +} + +function qux () { + + +} + +function intAdd(int a, int b) returns (int) { + return a + b; +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/codegen-coverage-test/tests/main_test.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-and-remove-test/tests/main_test.bal similarity index 100% rename from tests/testerina-integration-test/src/test/resources/project-based-tests/codegen-coverage-test/tests/main_test.bal rename to tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-and-remove-test/tests/main_test.bal diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-test/Ballerina.toml b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-test/Ballerina.toml new file mode 100644 index 000000000000..fea366370c8f --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-test/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "intg_tests" +name = "codegen_coverage_test" +version = "0.0.0" diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/codegen-coverage-test/foo.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-test/foo.bal similarity index 100% rename from tests/testerina-integration-test/src/test/resources/project-based-tests/codegen-coverage-test/foo.bal rename to tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-test/foo.bal diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/codegen-coverage-test/main.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-test/main.bal similarity index 100% rename from tests/testerina-integration-test/src/test/resources/project-based-tests/codegen-coverage-test/main.bal rename to tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-test/main.bal diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-test/tests/main_test.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-test/tests/main_test.bal new file mode 100644 index 000000000000..04e384917881 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-insert-test/tests/main_test.bal @@ -0,0 +1,11 @@ +import ballerina/test; + +@test:Config {} +public function test1() { + test:assertEquals(intAdd(4, 5), 9); +} + +@test:Config {} +public function test2() { + test:assertEquals(foo(2), 2); +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-remove-test/Ballerina.toml b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-remove-test/Ballerina.toml new file mode 100644 index 000000000000..fea366370c8f --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-remove-test/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "intg_tests" +name = "codegen_coverage_test" +version = "0.0.0" diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-remove-test/foo.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-remove-test/foo.bal new file mode 100644 index 000000000000..aecd08b517a1 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-remove-test/foo.bal @@ -0,0 +1,12 @@ + +function foo(int a) returns (int) { + return a; +} + +function bar() { + +} + +function foobar(int a) returns (int) { + return a + 2; +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-remove-test/main.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-remove-test/main.bal new file mode 100644 index 000000000000..8abd872f04da --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-remove-test/main.bal @@ -0,0 +1,9 @@ +import samjs/package_comp_plugin_code_modify_remove_function as _; + +public function main() { + _ = intAdd(1,2); +} + +function intAdd(int a, int b) returns (int) { + return a + b; +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-remove-test/tests/main_test.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-remove-test/tests/main_test.bal new file mode 100644 index 000000000000..04e384917881 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/code-coverage-report-test/line-remove-test/tests/main_test.bal @@ -0,0 +1,11 @@ +import ballerina/test; + +@test:Config {} +public function test1() { + test:assertEquals(intAdd(4, 5), 9); +} + +@test:Config {} +public function test2() { + test:assertEquals(foo(2), 2); +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/codegen-coverage-test/balas/package_comp_plugin_code_modify_add_function.bala b/tests/testerina-integration-test/src/test/resources/project-based-tests/codegen-coverage-test/balas/package_comp_plugin_code_modify_add_function.bala deleted file mode 100644 index 9946074ea5b0..000000000000 Binary files a/tests/testerina-integration-test/src/test/resources/project-based-tests/codegen-coverage-test/balas/package_comp_plugin_code_modify_add_function.bala and /dev/null differ diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_function/Ballerina.toml b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_function/Ballerina.toml new file mode 100644 index 000000000000..f0f026dc457b --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_function/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "samjs" +name = "package_comp_plugin_code_modify_add_function" +version = "0.1.0" diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_function/CompilerPlugin.toml b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_function/CompilerPlugin.toml new file mode 100644 index 000000000000..3ac66eaa133b --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_function/CompilerPlugin.toml @@ -0,0 +1,8 @@ +[plugin] +class = "io.samjs.plugins.init.codemodify.CodeModifyFunctionPlugin" + +[[dependency]] +path = "../../compiler-plugin-jars/init-function-code-modify-compiler-plugin-1.0.0.jar" + +[[dependency]] +path = "../../compiler-plugin-jars/diagnostic-utils-lib-1.0.0.jar" diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_function/main.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_function/main.bal new file mode 100644 index 000000000000..41a101b1c439 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_function/main.bal @@ -0,0 +1,3 @@ +public function doSomething() { + // Do nothing +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_remove_function/Ballerina.toml b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_remove_function/Ballerina.toml new file mode 100644 index 000000000000..0c831a1d9817 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_remove_function/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "samjs" +name = "package_comp_plugin_code_modify_add_remove_function" +version = "0.1.0" diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_remove_function/CompilerPlugin.toml b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_remove_function/CompilerPlugin.toml new file mode 100644 index 000000000000..4d3b6c8c3be4 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_remove_function/CompilerPlugin.toml @@ -0,0 +1,8 @@ +[plugin] +class = "io.samjs.plugins.addremove.codemodify.CodeModifyFunctionPlugin" + +[[dependency]] +path = "../../compiler-plugin-jars/add-remove-function-code-modify-compiler-plugin-1.0.0.jar" + +[[dependency]] +path = "../../compiler-plugin-jars/diagnostic-utils-lib-1.0.0.jar" diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_remove_function/main.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_remove_function/main.bal new file mode 100644 index 000000000000..41a101b1c439 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_add_remove_function/main.bal @@ -0,0 +1,3 @@ +public function doSomething() { + // Do nothing +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_remove_function/Ballerina.toml b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_remove_function/Ballerina.toml new file mode 100644 index 000000000000..c220c0f5d8db --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_remove_function/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "samjs" +name = "package_comp_plugin_code_modify_remove_function" +version = "0.1.0" diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_remove_function/CompilerPlugin.toml b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_remove_function/CompilerPlugin.toml new file mode 100644 index 000000000000..e96b55fd8c4b --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_remove_function/CompilerPlugin.toml @@ -0,0 +1,8 @@ +[plugin] +class = "io.samjs.plugins.remove.codemodify.CodeModifyFunctionPlugin" + +[[dependency]] +path = "../../compiler-plugin-jars/remove-function-code-modify-compiler-plugin-1.0.0.jar" + +[[dependency]] +path = "../../compiler-plugin-jars/diagnostic-utils-lib-1.0.0.jar" diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_remove_function/main.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_remove_function/main.bal new file mode 100644 index 000000000000..41a101b1c439 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/compiler-plugins/package_comp_plugin_code_modify_remove_function/main.bal @@ -0,0 +1,3 @@ +public function doSomething() { + // Do nothing +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/Ballerina.toml b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/Ballerina.toml new file mode 100644 index 000000000000..c061a3482eee --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/Ballerina.toml @@ -0,0 +1,7 @@ +[package] +org = "wso2" +name = "testExecutionWithModuleInitFailure" +version = "0.0.0" + +[build-options] +observabilityIncluded = false diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/main.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/main.bal new file mode 100644 index 000000000000..d1948f463ce6 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/main.bal @@ -0,0 +1,29 @@ +// Copyright (c) 2024, 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. + +import testExecutionWithModuleInitFailure.moduleA; +import testExecutionWithModuleInitFailure.moduleD; + +public function main() { +} + +public function getGreeting(string s1, string s2) returns string { + return s1 + moduleA:addSpaceAndGetString(s2); +} + +public function getKey(int i) returns int { + return i * moduleD:add(i, i*i); +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleA/main.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleA/main.bal new file mode 100644 index 000000000000..f69e9257e019 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleA/main.bal @@ -0,0 +1,24 @@ +// Copyright (c) 2024, 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. + +import testExecutionWithModuleInitFailure.moduleB; + +public function main() { +} + +public function addSpaceAndGetString(string s) returns string { + return " " + moduleB:add(s); +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleA/tests/test.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleA/tests/test.bal new file mode 100644 index 000000000000..0a39e396dbe8 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleA/tests/test.bal @@ -0,0 +1,22 @@ +// Copyright (c) 2024, 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. + +import ballerina/test; + +@test:Config {} +public function testFunc() { + test:assertEquals(addSpaceAndGetString("World"), " World!"); +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleB/main.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleB/main.bal new file mode 100644 index 000000000000..8f95342e2231 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleB/main.bal @@ -0,0 +1,26 @@ +// Copyright (c) 2024, 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. + +import testExecutionWithModuleInitFailure.moduleC; + +int a = 1/0; + +public function main() { +} + +public function add(string s) returns string { + return s + moduleC:foo(); +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleB/tests/test.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleB/tests/test.bal new file mode 100644 index 000000000000..d656e332ff54 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleB/tests/test.bal @@ -0,0 +1,22 @@ +// Copyright (c) 2024, 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. + +import ballerina/test; + +@test:Config {} +function testFunc() { + test:assertEquals(add("World"), "World!"); +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleC/main.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleC/main.bal new file mode 100644 index 000000000000..3a3af34ef109 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleC/main.bal @@ -0,0 +1,19 @@ +// Copyright (c) 2024, 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. + +public function foo() returns string { + return "!"; +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleC/tests/test.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleC/tests/test.bal new file mode 100644 index 000000000000..9574618fa2c0 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleC/tests/test.bal @@ -0,0 +1,22 @@ +// Copyright (c) 2024, 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. + +import ballerina/test; + +@test:Config {} +public function testFunc() { + test:assertEquals(foo(), "!"); +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleD/main.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleD/main.bal new file mode 100644 index 000000000000..9ee2f5fadc94 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleD/main.bal @@ -0,0 +1,19 @@ +// Copyright (c) 2024, 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. + +public function add(int a, int b) returns int { + return a + b; +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleD/tests/test.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleD/tests/test.bal new file mode 100644 index 000000000000..906f74278852 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/modules/moduleD/tests/test.bal @@ -0,0 +1,22 @@ +// Copyright (c) 2024, 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. + +import ballerina/test; + +@test:Config {} +function testFunc() { + test:assertEquals(add(3, 2), 5); +} diff --git a/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/tests/test_main.bal b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/tests/test_main.bal new file mode 100644 index 000000000000..5c6fb0aee224 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/project-based-tests/test-execution-with-init-failure/tests/test_main.bal @@ -0,0 +1,27 @@ +// Copyright (c) 2024, 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. + +import ballerina/test; + +@test:Config {} +function testFunc() { + test:assertEquals(getGreeting("Hello", "World"), "Hello World!"); +} + +@test:Config {} +function testFunc2() { + test:assertEquals(getKey(2), 12); +} diff --git a/tests/testerina-integration-test/src/test/resources/single-file-tests/single-test-execution/bal-test-with-init-failure.bal b/tests/testerina-integration-test/src/test/resources/single-file-tests/single-test-execution/bal-test-with-init-failure.bal new file mode 100644 index 000000000000..d40fc002ccb1 --- /dev/null +++ b/tests/testerina-integration-test/src/test/resources/single-file-tests/single-test-execution/bal-test-with-init-failure.bal @@ -0,0 +1,23 @@ +// 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. + +import ballerina/test; + +int a = 1/0; + +@test:Config {} +public function testFunc() { +} diff --git a/tests/testerina-integration-test/src/test/resources/testng.xml b/tests/testerina-integration-test/src/test/resources/testng.xml index deb6726227db..db46e1c50239 100644 --- a/tests/testerina-integration-test/src/test/resources/testng.xml +++ b/tests/testerina-integration-test/src/test/resources/testng.xml @@ -57,6 +57,8 @@ under the License. + +