visitFunctionDeclaration method
Visit a SFunctionDeclaration.
Implementation
@override
Object? visitFunctionDeclaration(SFunctionDeclaration node) {
// Create a function object that captures the current environment (closure)
// Use the .declaration constructor
final returnType = node.returnType;
final body0 = node.functionExpression!.body;
bool isAsync = (body0 is SBlockFunctionBody)
? body0.isAsync
: (body0 is SExpressionFunctionBody)
? body0.isAsync
: false;
bool isNullable = false;
if (returnType is SNamedType) {
isNullable = returnType.isNullable;
// For async functions: Future<T?> → extract nullability from inner type T
if (isAsync && !isNullable && returnType.name?.name == 'Future') {
final typeArgs = returnType.typeArguments;
if (typeArgs != null && typeArgs.arguments.isNotEmpty) {
final innerType = typeArgs.arguments.first;
if (innerType is SNamedType) {
isNullable = innerType.isNullable;
}
}
}
}
// Handle type parameters for generic functions
Environment? tempEnvironment;
final typeParameters = node.functionExpression!.typeParameters;
if (typeParameters != null) {
Logger.debug(
"[InterpreterVisitor.visitFunctionDeclaration] Function '${node.name!.name}' has ${typeParameters.typeParameters.length} type parameters",
);
// Create a temporary environment for type resolution
tempEnvironment = Environment(enclosing: environment);
// Create temporary type parameter placeholders
for (final typeParam in typeParameters.typeParameters) {
final paramName = typeParam.name!.name;
// Create a simple STypeParameter placeholder in the temp environment
final typeParamPlaceholder = TypeParameter(paramName);
tempEnvironment.define(paramName, typeParamPlaceholder);
Logger.debug(
"[InterpreterVisitor.visitFunctionDeclaration] Defined type parameter '$paramName' in temp environment",
);
}
}
// Use the temp environment (if any) for type resolution, otherwise use the normal environment
final resolveEnvironment = tempEnvironment ?? environment;
final declaredReturnType = tempEnvironment != null
? _resolveTypeAnnotationWithEnvironment(
node.returnType,
resolveEnvironment,
isAsync: isAsync,
)
: _resolveTypeAnnotation(node.returnType, isAsync: isAsync);
final function = InterpretedFunction.declaration(
node,
environment,
declaredReturnType,
isNullable,
);
// Define the function in the current environment
environment.define(node.name!.name, function);
return null; // Declaration itself doesn't return a value
}