From 19a9281070f7859d175b2f77527906fdd726f33b Mon Sep 17 00:00:00 2001 From: rdulmina Date: Mon, 10 Feb 2025 16:46:18 +0530 Subject: [PATCH 1/8] Add support for string template expression as constant expression --- .../semantics/analyzer/ConstantAnalyzer.java | 7 ++ .../analyzer/ConstantTypeChecker.java | 32 +++++++++ .../semantics/analyzer/TypeChecker.java | 5 +- .../compiler/semantics/model/SymbolTable.java | 2 + .../constant/StringTemplateConstantTest.java | 61 +++++++++++++++++ .../string-template-constant-negative.bal | 25 +++++++ .../constant/string-template-constant.bal | 67 +++++++++++++++++++ 7 files changed, 195 insertions(+), 4 deletions(-) create mode 100644 tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java create mode 100644 tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant-negative.bal create mode 100644 tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant.bal diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantAnalyzer.java index 7862a83d301c..c8f29de79bfd 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantAnalyzer.java @@ -38,6 +38,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangRecordLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangSimpleVarRef; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangStringTemplateLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangUnaryExpr; import org.wso2.ballerinalang.compiler.util.CompilerContext; import org.wso2.ballerinalang.compiler.util.Names; @@ -172,10 +173,16 @@ public void visit(BLangListConstructorExpr.BLangListConstructorSpreadOpExpr spre analyzeExpr(spreadOpExpr.expr); } + @Override + public void visit(BLangStringTemplateLiteral stringTemplateLiteral) { + stringTemplateLiteral.exprs.forEach(this::analyzeExpr); + } + void analyzeExpr(BLangExpression expr) { switch (expr.getKind()) { case LITERAL: case NUMERIC_LITERAL: + case STRING_TEMPLATE_LITERAL: case RECORD_LITERAL_EXPR: case LIST_CONSTRUCTOR_EXPR: case LIST_CONSTRUCTOR_SPREAD_OP: diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java index 19b37c0e15c3..02b20aa304d5 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java @@ -86,6 +86,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangRecordLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangSimpleVarRef; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangStringTemplateLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangUnaryExpr; import org.wso2.ballerinalang.compiler.util.BArrayState; import org.wso2.ballerinalang.compiler.util.CompilerContext; @@ -193,6 +194,7 @@ public BType checkConstExpr(BLangExpression expr, SymbolEnv env, BType expType, switch (expr.getKind()) { case LITERAL: case NUMERIC_LITERAL: + case STRING_TEMPLATE_LITERAL: case RECORD_LITERAL_EXPR: case LIST_CONSTRUCTOR_EXPR: case SIMPLE_VARIABLE_REF: @@ -511,6 +513,36 @@ public void visit(BLangUnaryExpr unaryExpr, AnalyzerData data) { data.resultType = finiteType; } + @Override + public void visit(BLangStringTemplateLiteral stringTemplateLiteral, AnalyzerData data) { + StringBuilder resultString = new StringBuilder(); + stringTemplateLiteral.exprs.forEach(expr -> { + BType exprType = checkConstExpr(expr, data); + if (exprType == symTable.semanticError) { + data.resultType = symTable.semanticError; + return; + } + if (!types.isNonNilSimpleBasicTypeOrString(exprType)) { + dlog.error(expr.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, symTable.interpolationAllowedType, + exprType); + data.resultType = symTable.semanticError; + return; + } + + + BLangLiteral exprLiteral = (BLangLiteral) ((BFiniteType) exprType).getValueSpace().iterator().next(); + resultString.append(getValue(exprLiteral)); + }); + + Location pos = stringTemplateLiteral.pos; + BType finiteType = getFiniteType(resultString.toString(), data.constantSymbol, pos, symTable.stringType); + if (data.compoundExprCount == 0 && types.typeIncompatible(pos, finiteType, data.expType)) { + data.resultType = symTable.semanticError; + return; + } + data.resultType = finiteType; + } + private BRecordType createNewRecordType(BRecordTypeSymbol symbol, LinkedHashMap inferredFields, AnalyzerData data) { BRecordType recordType = new BRecordType(symbol); 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 cef5cdbdf1a3..2729d6039c97 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 @@ -8220,10 +8220,7 @@ private void checkStringTemplateExprs(List exprs, Ana } if (!types.isNonNilSimpleBasicTypeOrString(type)) { - dlog.error(expr.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, - BUnionType.create(null, symTable.intType, symTable.floatType, - symTable.decimalType, symTable.stringType, - symTable.booleanType), type); + dlog.error(expr.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, symTable.interpolationAllowedType, type); } } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java index 5d3bfba21165..2a0ba4f3fc3c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java @@ -141,6 +141,8 @@ public class SymbolTable { public final BType readonlyType = new BReadonlyType(TypeTags.READONLY, null); public final BType pathParamAllowedType = BUnionType.create(null, intType, stringType, floatType, booleanType, decimalType); + public final BType interpolationAllowedType = BUnionType.create(null, + intType, stringType, floatType, booleanType, decimalType); public final BIntersectionType anyAndReadonly; public BUnionType anyAndReadonlyOrError; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java new file mode 100644 index 000000000000..d04462ae182a --- /dev/null +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2025, 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. + */ + +package org.ballerinalang.test.types.constant; + +import org.ballerinalang.test.BAssertUtil; +import org.ballerinalang.test.BCompileUtil; +import org.ballerinalang.test.BRunUtil; +import org.ballerinalang.test.CompileResult; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * This class contains a set of test cases to test the string template expression as a constant expression. + */ +public class StringTemplateConstantTest { + + private CompileResult compileResult; + + @BeforeClass + public void setup() { + compileResult = BCompileUtil.compile("test-src/types/constant/string-template-constant.bal"); + } + + @Test + public void testStringTemplateConstantExpr() { + Object returns = BRunUtil.invoke(compileResult, "testStringTemplateConstantExpr"); + Assert.assertNull(returns); + } + + @Test + public void testStringTemplateConstantExprNegative() { + CompileResult negativeResult = BCompileUtil.compile( + "test-src/types/constant/string-template-constant-negative.bal"); + int index = 0; + BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected '(int|string|" + + "float|boolean|decimal)', found '(record {| 4 a; |} & readonly)'", 17, 27); + BAssertUtil.validateError(negativeResult, index++, "expression is not a constant expression", + 20, 30); + BAssertUtil.validateError(negativeResult, index++, "expression is not a constant expression", + 21, 30); + + Assert.assertEquals(negativeResult.getErrorCount(), index); + } +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant-negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant-negative.bal new file mode 100644 index 000000000000..c38ba92ef313 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant-negative.bal @@ -0,0 +1,25 @@ +// Copyright (c) 2025 WSO2 LLC. (http://www.wso2.org) All Rights Reserved. +// +// 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. +const v1 = {a:4}; +const v2 = string `hello${v1}`; + +boolean bool = true; +const v3 = string `This is ${bool}`; +const v4 = string `This is ${foo()}`; + +function foo() returns boolean { + return false; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant.bal new file mode 100644 index 000000000000..9b54dc590b22 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant.bal @@ -0,0 +1,67 @@ +// Copyright (c) 2025 WSO2 LLC. (http://www.wso2.org) All Rights Reserved. +// +// 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. +const v1 = string `hello ${"world"}`; +const v2 = string `hello${" world"}`; + +const boolean bool = true; +const v3 = string `This is ${bool}`; +const v4 = string `This is ${!bool}`; + +const int intVal = 444; +string v5 = string `${intVal} is greater than ${100}.`; + +const float floatVal = 5.0090; +string v6 = string `this is a float value ${floatVal}. ${v2}. Have a nice day.`; + +const decimal b = 5.7888; +const decimal c = 5.7888; +const v7 = string `hello ${b + c}. This is ${b - c}.`; + +public function testStringTemplateConstantExpr() { + assertEquality("hello world", v1); + assertEquality("hello world", v2); + assertEquality("This is true", v3); + assertEquality("This is false", v4); + assertEquality("444 is greater than 100.", v5); + assertEquality("this is a float value 5.009. hello world. Have a nice day.", v6); + assertEquality("hello 11.5776. This is 0.0000.", v7); + + // define a new non constant variables for the above v1 to v7 locally + string v11 = string `hello ${"world"}`; + string v12 = string `hello${" world"}`; + string v13 = string `This is ${bool}`; + string v14 = string `This is ${!bool}`; + string v15 = string `${intVal} is greater than ${100}.`; + string v16 = string `this is a float value ${floatVal}. ${v12}. Have a nice day.`; + string v17 = string `hello ${b + c}. This is ${b - c}.`; + + // assert the equality of the above local variables with the constant variables + assertEquality(v1, v11); + assertEquality(v2, v12); + assertEquality(v3, v13); + assertEquality(v4, v14); + assertEquality(v5, v15); + assertEquality(v6, v16); + assertEquality(v7, v17); // Todo: This needs to be fixed +} + +function assertEquality(anydata expected, anydata actual) { + if expected == actual { + return; + } + + panic error(string `expected '${expected.toBalString()}', found '${actual.toBalString()}'`); +} From bbf844a561c823119e1e7e24dc6cad93b88129fa Mon Sep 17 00:00:00 2001 From: gimantha Date: Mon, 17 Feb 2025 20:33:28 +0530 Subject: [PATCH 2/8] Fix unit test failures --- .../compiler/semantics/analyzer/ConstantTypeChecker.java | 3 ++- .../ballerinalang/compiler/semantics/model/SymbolTable.java | 4 ++-- .../test/types/constant/StringTemplateConstantTest.java | 4 ++-- .../test-src/types/constant/string-template-constant.bal | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java index 02b20aa304d5..d3475688540e 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java @@ -1609,7 +1609,8 @@ private Object calculateSubtract(Object lhs, Object rhs, BType type, AnalyzerDat case TypeTags.DECIMAL: BigDecimal lhsDecimal = new BigDecimal(String.valueOf(lhs), MathContext.DECIMAL128); BigDecimal rhsDecimal = new BigDecimal(String.valueOf(rhs), MathContext.DECIMAL128); - BigDecimal resultDecimal = lhsDecimal.subtract(rhsDecimal, MathContext.DECIMAL128); + BigDecimal resultDecimal = lhsDecimal.compareTo(rhsDecimal) == 0 ? BigDecimal.ZERO : + lhsDecimal.subtract(rhsDecimal, MathContext.DECIMAL128); resultDecimal = types.getValidDecimalNumber(data.pos, resultDecimal); return resultDecimal != null ? resultDecimal.toPlainString() : null; default: diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java index 2a0ba4f3fc3c..ceea060b0110 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java @@ -141,8 +141,8 @@ public class SymbolTable { public final BType readonlyType = new BReadonlyType(TypeTags.READONLY, null); public final BType pathParamAllowedType = BUnionType.create(null, intType, stringType, floatType, booleanType, decimalType); - public final BType interpolationAllowedType = BUnionType.create(null, - intType, stringType, floatType, booleanType, decimalType); + public final BType interpolationAllowedType = BUnionType.create(null, intType, floatType, decimalType, + stringType, booleanType); public final BIntersectionType anyAndReadonly; public BUnionType anyAndReadonlyOrError; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java index d04462ae182a..3b97a7786306 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java @@ -49,8 +49,8 @@ public void testStringTemplateConstantExprNegative() { CompileResult negativeResult = BCompileUtil.compile( "test-src/types/constant/string-template-constant-negative.bal"); int index = 0; - BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected '(int|string|" + - "float|boolean|decimal)', found '(record {| 4 a; |} & readonly)'", 17, 27); + BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected '(int|float|" + + "decimal|string|boolean)', found '(record {| 4 a; |} & readonly)'", 17, 27); BAssertUtil.validateError(negativeResult, index++, "expression is not a constant expression", 20, 30); BAssertUtil.validateError(negativeResult, index++, "expression is not a constant expression", diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant.bal index 9b54dc590b22..113fb34b5b3b 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant.bal @@ -37,7 +37,7 @@ public function testStringTemplateConstantExpr() { assertEquality("This is false", v4); assertEquality("444 is greater than 100.", v5); assertEquality("this is a float value 5.009. hello world. Have a nice day.", v6); - assertEquality("hello 11.5776. This is 0.0000.", v7); + assertEquality("hello 11.5776. This is 0.", v7); // define a new non constant variables for the above v1 to v7 locally string v11 = string `hello ${"world"}`; From 68f460e6620c9196e1c649784a672d8723afbe9f Mon Sep 17 00:00:00 2001 From: Gimantha Bandara Date: Tue, 18 Feb 2025 10:34:21 +0530 Subject: [PATCH 3/8] Update tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java Co-authored-by: Maryam Ziyad --- .../test/types/constant/StringTemplateConstantTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java index 3b97a7786306..82ebb800da82 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java @@ -40,8 +40,7 @@ public void setup() { @Test public void testStringTemplateConstantExpr() { - Object returns = BRunUtil.invoke(compileResult, "testStringTemplateConstantExpr"); - Assert.assertNull(returns); + BRunUtil.invoke(compileResult, "testStringTemplateConstantExpr"); } @Test From f54d372825273d456ce4ab61aa94cf57fef9cc02 Mon Sep 17 00:00:00 2001 From: gimantha Date: Tue, 18 Feb 2025 17:47:27 +0530 Subject: [PATCH 4/8] Address review suggestions --- .../compiler/semantics/analyzer/ConstantTypeChecker.java | 5 ++--- .../compiler/semantics/analyzer/SemanticAnalyzer.java | 8 ++++++++ .../test/types/constant/StringTemplateConstantTest.java | 6 ++++-- ...template-constant.bal => string_template_constant.bal} | 0 ...negative.bal => string_template_constant_negative.bal} | 0 5 files changed, 14 insertions(+), 5 deletions(-) rename tests/jballerina-unit-test/src/test/resources/test-src/types/constant/{string-template-constant.bal => string_template_constant.bal} (100%) rename tests/jballerina-unit-test/src/test/resources/test-src/types/constant/{string-template-constant-negative.bal => string_template_constant_negative.bal} (100%) diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java index d3475688540e..aaebc0bfdba4 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java @@ -516,7 +516,7 @@ public void visit(BLangUnaryExpr unaryExpr, AnalyzerData data) { @Override public void visit(BLangStringTemplateLiteral stringTemplateLiteral, AnalyzerData data) { StringBuilder resultString = new StringBuilder(); - stringTemplateLiteral.exprs.forEach(expr -> { + for (BLangExpression expr : stringTemplateLiteral.exprs) { BType exprType = checkConstExpr(expr, data); if (exprType == symTable.semanticError) { data.resultType = symTable.semanticError; @@ -529,10 +529,9 @@ public void visit(BLangStringTemplateLiteral stringTemplateLiteral, AnalyzerData return; } - BLangLiteral exprLiteral = (BLangLiteral) ((BFiniteType) exprType).getValueSpace().iterator().next(); resultString.append(getValue(exprLiteral)); - }); + } Location pos = stringTemplateLiteral.pos; BType finiteType = getFiniteType(resultString.toString(), data.constantSymbol, pos, symTable.stringType); 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 5b8566b99307..f12eee86578d 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 @@ -143,6 +143,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangRecordVarRef.BLangRecordVarRefKeyValue; import org.wso2.ballerinalang.compiler.tree.expressions.BLangRegExpTemplateLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangSimpleVarRef; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangStringTemplateLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangTupleVarRef; import org.wso2.ballerinalang.compiler.tree.expressions.BLangTypeConversionExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangTypeInit; @@ -4429,6 +4430,10 @@ private void checkAnnotConstantExpression(BLangExpression expression) { case UNARY_EXPR: checkAnnotConstantExpression(((BLangUnaryExpr) expression).expr); break; + case BINARY_EXPR: + checkAnnotConstantExpression(((BLangBinaryExpr)expression).lhsExpr); + checkAnnotConstantExpression(((BLangBinaryExpr)expression).rhsExpr); + break; case SIMPLE_VARIABLE_REF: BSymbol symbol = ((BLangSimpleVarRef) expression).symbol; // Symbol can be null in some invalid scenarios. Eg - const string m = { name: "Ballerina" }; @@ -4451,6 +4456,9 @@ private void checkAnnotConstantExpression(BLangExpression expression) { case LIST_CONSTRUCTOR_EXPR: ((BLangListConstructorExpr) expression).exprs.forEach(this::checkAnnotConstantExpression); break; + case STRING_TEMPLATE_LITERAL: + ((BLangStringTemplateLiteral) expression).exprs.forEach(this::checkAnnotConstantExpression); + break; default: dlog.error(expression.pos, DiagnosticErrorCode.EXPRESSION_IS_NOT_A_CONSTANT_EXPRESSION); break; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java index 3b97a7786306..5fbc45c3f99f 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java @@ -27,6 +27,8 @@ import org.testng.annotations.Test; /** + * @since 2201.12.0 + * * This class contains a set of test cases to test the string template expression as a constant expression. */ public class StringTemplateConstantTest { @@ -35,7 +37,7 @@ public class StringTemplateConstantTest { @BeforeClass public void setup() { - compileResult = BCompileUtil.compile("test-src/types/constant/string-template-constant.bal"); + compileResult = BCompileUtil.compile("test-src/types/constant/string_template_constant.bal"); } @Test @@ -47,7 +49,7 @@ public void testStringTemplateConstantExpr() { @Test public void testStringTemplateConstantExprNegative() { CompileResult negativeResult = BCompileUtil.compile( - "test-src/types/constant/string-template-constant-negative.bal"); + "test-src/types/constant/string_template_constant_negative.bal"); int index = 0; BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected '(int|float|" + "decimal|string|boolean)', found '(record {| 4 a; |} & readonly)'", 17, 27); diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string_template_constant.bal similarity index 100% rename from tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant.bal rename to tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string_template_constant.bal diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant-negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string_template_constant_negative.bal similarity index 100% rename from tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string-template-constant-negative.bal rename to tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string_template_constant_negative.bal From 6a454a723fa8508500f81847cfea8f48dfe003b3 Mon Sep 17 00:00:00 2001 From: gimantha Date: Wed, 19 Feb 2025 11:48:27 +0530 Subject: [PATCH 5/8] Support simplevarrefs in const string templates in const annotations --- .../semantics/analyzer/ConstantValueResolver.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantValueResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantValueResolver.java index b929c95ad4fe..585a52d8fe20 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantValueResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantValueResolver.java @@ -63,6 +63,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangRecordLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangSimpleVarRef; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangStringTemplateLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangUnaryExpr; import org.wso2.ballerinalang.compiler.tree.types.BLangRecordTypeNode; import org.wso2.ballerinalang.compiler.util.CompilerContext; @@ -298,6 +299,15 @@ public void visit(BLangBinaryExpr binaryExpr) { this.result = calculateConstValue(lhs, rhs, binaryExpr.opKind); } + public void visit(BLangStringTemplateLiteral stringTemplateLiteral) { + StringBuilder resultString = new StringBuilder(); + stringTemplateLiteral.exprs.forEach(expr -> { + expr.accept(this); + resultString.append(this.result); + }); + this.result = new BLangConstantValue(resultString.toString(), symTable.stringType); + } + @Override public void visit(BLangGroupExpr groupExpr) { this.result = constructBLangConstantValue(groupExpr.expression); @@ -610,6 +620,7 @@ private BLangConstantValue constructBLangConstantValue(BLangExpression node) { case BINARY_EXPR: case GROUP_EXPR: case UNARY_EXPR: + case STRING_TEMPLATE_LITERAL: BLangConstantValue prevResult = this.result; Location prevPos = this.currentPos; this.currentPos = node.pos; From 432fd1796c51c9713090283962a772ddc6d47ec8 Mon Sep 17 00:00:00 2001 From: gimantha Date: Wed, 19 Feb 2025 12:05:58 +0530 Subject: [PATCH 6/8] Revert visiting binary expr --- .../compiler/semantics/analyzer/SemanticAnalyzer.java | 4 ---- 1 file changed, 4 deletions(-) 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 f12eee86578d..4df42cb6e079 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 @@ -4430,10 +4430,6 @@ private void checkAnnotConstantExpression(BLangExpression expression) { case UNARY_EXPR: checkAnnotConstantExpression(((BLangUnaryExpr) expression).expr); break; - case BINARY_EXPR: - checkAnnotConstantExpression(((BLangBinaryExpr)expression).lhsExpr); - checkAnnotConstantExpression(((BLangBinaryExpr)expression).rhsExpr); - break; case SIMPLE_VARIABLE_REF: BSymbol symbol = ((BLangSimpleVarRef) expression).symbol; // Symbol can be null in some invalid scenarios. Eg - const string m = { name: "Ballerina" }; From 2150f2009ddc0614ba1a0e1e6a7efdaa543ca049 Mon Sep 17 00:00:00 2001 From: Gimantha Bandara Date: Wed, 19 Feb 2025 12:57:23 +0530 Subject: [PATCH 7/8] Apply suggestions from code review Co-authored-by: Sasindu Alahakoon <47134066+SasinduDilshara@users.noreply.github.com> --- .../compiler/semantics/analyzer/ConstantTypeChecker.java | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java index aaebc0bfdba4..98b5336af49d 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java @@ -528,7 +528,6 @@ public void visit(BLangStringTemplateLiteral stringTemplateLiteral, AnalyzerData data.resultType = symTable.semanticError; return; } - BLangLiteral exprLiteral = (BLangLiteral) ((BFiniteType) exprType).getValueSpace().iterator().next(); resultString.append(getValue(exprLiteral)); } From 73868ecf297250f92b4d3b5aaf97777272d29dae Mon Sep 17 00:00:00 2001 From: gimantha Date: Wed, 19 Feb 2025 13:49:32 +0530 Subject: [PATCH 8/8] Add more tests --- .../test/types/constant/StringTemplateConstantTest.java | 2 ++ .../types/constant/string_template_constant_negative.bal | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java index 7be8ba9c3f37..a9e87b4e98c0 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/StringTemplateConstantTest.java @@ -56,6 +56,8 @@ public void testStringTemplateConstantExprNegative() { 20, 30); BAssertUtil.validateError(negativeResult, index++, "expression is not a constant expression", 21, 30); + BAssertUtil.validateError(negativeResult, index++, "expression is not a constant expression", + 29, 21); Assert.assertEquals(negativeResult.getErrorCount(), index); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string_template_constant_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string_template_constant_negative.bal index c38ba92ef313..5b62c633c9bc 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string_template_constant_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/constant/string_template_constant_negative.bal @@ -23,3 +23,7 @@ const v4 = string `This is ${foo()}`; function foo() returns boolean { return false; } + +final string baz = "FOO"; +string gim = "BAR"; +const A = string `${baz}:${gim}`;