Skip to content

Commit 2fcea93

Browse files
committed
Refactor return type validation in WorkflowValidatorTask to use WorkflowPluginUtils for subtype checking
1 parent 5d3dfc5 commit 2fcea93

2 files changed

Lines changed: 21 additions & 83 deletions

File tree

compiler-plugin/src/main/java/io/ballerina/stdlib/workflow/compiler/WorkflowPluginUtils.java

Lines changed: 20 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
* specific language governing permissions and limitations
1616
* under the License.
1717
*/
18-
1918
package io.ballerina.stdlib.workflow.compiler;
2019

2120
import io.ballerina.compiler.api.SemanticModel;
@@ -27,7 +26,6 @@
2726
import io.ballerina.compiler.api.symbols.TypeDescKind;
2827
import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol;
2928
import io.ballerina.compiler.api.symbols.TypeSymbol;
30-
import io.ballerina.compiler.api.symbols.UnionTypeSymbol;
3129
import io.ballerina.compiler.syntax.tree.AnnotationNode;
3230
import io.ballerina.compiler.syntax.tree.ExpressionNode;
3331
import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode;
@@ -198,93 +196,41 @@ public static TypeSymbol resolveTypeReference(TypeSymbol typeSymbol) {
198196
}
199197

200198
/**
201-
* Checks if a type is a subtype of anydata.
202-
* Uses the semantic model's type checking when available, otherwise falls back to kind checking.
199+
* Checks if a type is a subtype of anydata using the compiler's built-in type checking.
203200
*
204201
* @param typeSymbol the type symbol to check
205-
* @param semanticModel the semantic model (may be null for fallback behavior)
202+
* @param semanticModel the semantic model
206203
* @return true if the type is a subtype of anydata
207204
*/
208205
public static boolean isSubtypeOfAnydata(TypeSymbol typeSymbol, SemanticModel semanticModel) {
209-
if (semanticModel != null) {
210-
return typeSymbol.subtypeOf(semanticModel.types().ANYDATA);
211-
}
212-
// Fallback to kind-based checking
213-
return isSubtypeOfAnydataByKind(typeSymbol);
206+
return typeSymbol.subtypeOf(semanticModel.types().ANYDATA);
214207
}
215208

216209
/**
217-
* Checks if a type is a subtype of anydata based on type kind.
218-
* This is a fallback method when semantic model is not available.
219-
*
220-
* @param typeSymbol the type symbol to check
221-
* @return true if the type is a subtype of anydata
222-
*/
223-
public static boolean isSubtypeOfAnydataByKind(TypeSymbol typeSymbol) {
224-
TypeDescKind kind = typeSymbol.typeKind();
225-
226-
// Handle type references
227-
if (kind == TypeDescKind.TYPE_REFERENCE) {
228-
TypeReferenceTypeSymbol typeRef = (TypeReferenceTypeSymbol) typeSymbol;
229-
return isSubtypeOfAnydataByKind(typeRef.typeDescriptor());
230-
}
231-
232-
// anydata includes: (), boolean, int, float, decimal, string, xml,
233-
// anydata[], map<anydata>, table<map<anydata>>, record types
234-
switch (kind) {
235-
case NIL:
236-
case BOOLEAN:
237-
case INT:
238-
case FLOAT:
239-
case DECIMAL:
240-
case STRING:
241-
case XML:
242-
case ANYDATA:
243-
case JSON:
244-
case BYTE:
245-
case ARRAY:
246-
case MAP:
247-
case RECORD:
248-
case TABLE:
249-
case TUPLE:
250-
return true;
251-
case UNION:
252-
// Check if all members are subtypes of anydata
253-
UnionTypeSymbol unionType = (UnionTypeSymbol) typeSymbol;
254-
return unionType.memberTypeDescriptors().stream()
255-
.allMatch(WorkflowPluginUtils::isSubtypeOfAnydataByKind);
256-
default:
257-
return false;
258-
}
259-
}
260-
261-
/**
262-
* Checks if a type is a subtype of anydata or error.
210+
* Checks if a type is a subtype of anydata or error using the compiler's built-in type checking.
211+
* This handles union types like `string|error` where each member must be either anydata or error.
263212
*
264213
* @param typeSymbol the type symbol to check
265-
* @param semanticModel the semantic model (may be null for fallback behavior)
214+
* @param semanticModel the semantic model
266215
* @return true if the type is a subtype of anydata or error
267216
*/
268217
public static boolean isSubtypeOfAnydataOrError(TypeSymbol typeSymbol, SemanticModel semanticModel) {
269-
TypeDescKind kind = typeSymbol.typeKind();
270-
271-
// Handle type references
272-
if (kind == TypeDescKind.TYPE_REFERENCE) {
273-
TypeReferenceTypeSymbol typeRef = (TypeReferenceTypeSymbol) typeSymbol;
274-
return isSubtypeOfAnydataOrError(typeRef.typeDescriptor(), semanticModel);
275-
}
276-
277-
if (kind == TypeDescKind.ERROR) {
278-
return true;
279-
}
280-
281-
// Handle union types like `string|error`
282-
if (kind == TypeDescKind.UNION) {
283-
UnionTypeSymbol unionType = (UnionTypeSymbol) typeSymbol;
218+
// For union types like `string|error`, check each member
219+
if (typeSymbol.typeKind() == TypeDescKind.UNION) {
220+
io.ballerina.compiler.api.symbols.UnionTypeSymbol unionType =
221+
(io.ballerina.compiler.api.symbols.UnionTypeSymbol) typeSymbol;
284222
return unionType.memberTypeDescriptors().stream()
285223
.allMatch(member -> isSubtypeOfAnydataOrError(member, semanticModel));
286224
}
287-
288-
return isSubtypeOfAnydata(typeSymbol, semanticModel);
225+
226+
// Handle type references
227+
if (typeSymbol.typeKind() == TypeDescKind.TYPE_REFERENCE) {
228+
TypeSymbol resolved = resolveTypeReference(typeSymbol);
229+
return isSubtypeOfAnydataOrError(resolved, semanticModel);
230+
}
231+
232+
// Check if it's a subtype of anydata or error
233+
return typeSymbol.subtypeOf(semanticModel.types().ANYDATA)
234+
|| typeSymbol.subtypeOf(semanticModel.types().ERROR);
289235
}
290236
}

compiler-plugin/src/main/java/io/ballerina/stdlib/workflow/compiler/WorkflowValidatorTask.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ private void validateReturnType(FunctionDefinitionNode functionNode, SyntaxNodeA
224224
Optional<TypeSymbol> returnTypeOpt = typeSymbol.returnTypeDescriptor();
225225
if (returnTypeOpt.isPresent()) {
226226
TypeSymbol returnType = returnTypeOpt.get();
227-
if (!isValidReturnType(returnType)) {
227+
if (!WorkflowPluginUtils.isSubtypeOfAnydataOrError(returnType, context.semanticModel())) {
228228
reportDiagnostic(context, functionNode, WorkflowConstants.WORKFLOW_105,
229229
WorkflowConstants.PROCESS_INVALID_RETURN_TYPE);
230230
}
@@ -590,14 +590,6 @@ private boolean isValidEventsType(TypeSymbol typeSymbol) {
590590
return true;
591591
}
592592

593-
/**
594-
* Validates return type is subtype of anydata|error.
595-
*/
596-
private boolean isValidReturnType(TypeSymbol typeSymbol) {
597-
// Use kind-based checking as fallback since we don't have SemanticModel here
598-
return WorkflowPluginUtils.isSubtypeOfAnydataOrError(typeSymbol, null);
599-
}
600-
601593
private void reportDiagnostic(SyntaxNodeAnalysisContext context, FunctionDefinitionNode functionNode,
602594
String code, String message) {
603595
DiagnosticInfo diagnosticInfo = new DiagnosticInfo(code, message, DiagnosticSeverity.ERROR);

0 commit comments

Comments
 (0)