visitFunctionDeclaration method

  1. @override
Object? visitFunctionDeclaration(
  1. SFunctionDeclaration node
)
override

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
}