visitFunctionDeclaration method

  1. @override
void visitFunctionDeclaration(
  1. SFunctionDeclaration node
)
override

Implementation

@override
void visitFunctionDeclaration(SFunctionDeclaration node) {
  final functionName = node.name?.name ?? '';
  Logger.debug(
    "[DeclarationVisitor.visitFunctionDeclaration] Processing function: $functionName",
  );

  if (environment.isDefinedLocally(functionName)) {
    return;
  }

  // Handle type parameters for generic functions
  Environment? tempEnvironment;
  final typeParameters = node.functionExpression?.typeParameters;

  if (typeParameters != null) {
    Logger.debug(
      "[DeclarationVisitor.visitFunctionDeclaration] Function '$functionName' 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 TypeParameter placeholder in the temp environment
      final typeParamPlaceholder = TypeParameter(paramName);
      tempEnvironment.define(paramName, typeParamPlaceholder);

      Logger.debug(
        "[DeclarationVisitor.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;

  // Now resolve the return type (which might reference type parameters)
  final returnTypeNode = node.returnType;
  RuntimeType declaredReturnType;

  if (returnTypeNode is SNamedType) {
    final typeName = returnTypeNode.name?.name ?? '';
    Logger.debug(
      "[DeclarationVisitor.visitFunctionDeclaration]   Return type node name: $typeName",
    );

    try {
      final resolvedType = resolveEnvironment.get(typeName);
      Logger.debug(
        "[DeclarationVisitor.visitFunctionDeclaration]     environment.get('$typeName') resolved to: ${resolvedType?.runtimeType} with name: ${(resolvedType is RuntimeType ? resolvedType.name : 'N/A')}",
      );

      if (resolvedType is RuntimeType) {
        declaredReturnType = resolvedType;
      } else {
        Logger.warn(
          "[DeclarationVisitor.visitFunctionDeclaration]     Type '$typeName' resolved to non-RuntimeType: $resolvedType. Using placeholder.",
        );
        declaredReturnType = BridgedClass(nativeType: Object, name: typeName);
      }
    } on RuntimeD4rtException catch (e) {
      Logger.warn(
        "[DeclarationVisitor.visitFunctionDeclaration]     Type '$typeName' not found in environment (RuntimeError: ${e.message}). Using placeholder.",
      );
      declaredReturnType = BridgedClass(nativeType: Object, name: typeName);
    }
  } else if (returnTypeNode == null) {
    declaredReturnType = BridgedClass(nativeType: Object, name: 'dynamic');
  } else {
    // For other TypeAnnotation types, use a generic placeholder
    declaredReturnType = BridgedClass(
      nativeType: Object,
      name: 'unknown_type_placeholder',
    );
  }

  bool isNullable =
      (returnTypeNode is SNamedType) && returnTypeNode.isNullable;

  // For async functions with Future<T?> return type, extract isNullable from inner type T
  final body0 = node.functionExpression?.body;
  bool isAsync = (body0 is SBlockFunctionBody)
      ? body0.isAsync
      : (body0 is SExpressionFunctionBody)
      ? body0.isAsync
      : false;
  if (isAsync &&
      !isNullable &&
      returnTypeNode is SNamedType &&
      returnTypeNode.name?.name == 'Future') {
    final typeArgs = returnTypeNode.typeArguments;
    if (typeArgs != null && typeArgs.arguments.isNotEmpty) {
      final innerType = typeArgs.arguments.first;
      if (innerType is SNamedType) {
        isNullable = innerType.isNullable;
      }
    }
  }

  final function = InterpretedFunction.declaration(
    node,
    environment, // The function captures the environment it's declared in
    declaredReturnType,
    isNullable,
  );
  Logger.debug(
    "[DeclarationVisitor.visitFunctionDeclaration]   Defining function '$functionName' with declaredReturnType: ${declaredReturnType.name} (Hash: ${declaredReturnType.hashCode})",
  );
  environment.define(functionName, function);
}