check method

  1. @override
void check(
  1. DcqRegistry registry
)

Implementation

@override
void check(DcqRegistry registry) {
  final eventBaseNames = <String>{};
  // Map of class name -> superclass name for all classes with extends.
  final classSuperMap = <String, String>{};
  final handledEvents = <String>{};
  ClassDeclaration? blocNode;

  registry.addClassDeclaration((node) {
    final superclass = node.extendsClause?.superclass.type;
    if (superclass is InterfaceType && isBloc(superclass)) {
      blocNode = node;
      // Collect event base class name from Bloc type parameters.
      final typeArgs =
          node.extendsClause?.superclass.typeArguments?.arguments;
      if (typeArgs != null && typeArgs.isNotEmpty) {
        final eventType = typeArgs.first;
        if (eventType is NamedType) {
          eventBaseNames.add(eventType.name.lexeme);
        }
      }
    }

    // Record class -> superclass mapping for later resolution.
    final superName = node.extendsClause?.superclass.name.lexeme;
    if (superName != null) {
      classSuperMap[node.namePart.typeName.lexeme] = superName;
    }
  });

  registry.addMethodInvocation((node) {
    if (node.methodName.name != 'on') return;
    final typeArgs = node.typeArguments?.arguments;
    if (typeArgs == null || typeArgs.isEmpty) return;
    final firstArg = typeArgs.first;
    if (firstArg is NamedType) {
      handledEvents.add(firstArg.name.lexeme);
    }
  });

  registry.afterLibrary(() {
    if (blocNode == null) return;
    // Resolve event subclasses transitively: walk the inheritance chain
    // so multi-level hierarchies (e.g. AppEvent > DataEvent > LoadDataEvent)
    // are fully discovered.
    final eventClasses = <String>{};
    final queue = eventBaseNames.toList();
    while (queue.isNotEmpty) {
      final parent = queue.removeLast();
      for (final entry in classSuperMap.entries) {
        if (entry.value == parent && eventClasses.add(entry.key)) {
          queue.add(entry.key);
        }
      }
    }
    // Only leaf event classes (those with no subclasses) need handlers.
    final nonLeaf = classSuperMap.values.toSet();
    final leafEvents = eventClasses.difference(nonLeaf);
    final unhandled = leafEvents.difference(handledEvents);
    if (unhandled.isNotEmpty) {
      reportAtToken(blocNode!.namePart.typeName);
    }
  });
}