visitClassDeclaration method

  1. @override
void visitClassDeclaration(
  1. ClassDeclaration node
)
override

Implementation

@override
void visitClassDeclaration(ClassDeclaration node) {
  final classBody = node.body;
  if (classBody is! BlockClassBody) {
    super.visitClassDeclaration(node);
    return;
  }

  final extendsClause = node.extendsClause;
  final superClassName = extendsClause?.superclass.name.lexeme;
  final mixins =
      node.withClause?.mixinTypes.map((t) => t.name.lexeme).toList() ?? [];

  // Detect ValueNotifier subclasses
  if (extendsClause != null && superClassName == 'ValueNotifier') {
    final className = node.namePart.typeName.lexeme;
    final typeArgs = extendsClause.superclass.typeArguments;
    final valueType = (typeArgs != null && typeArgs.arguments.isNotEmpty)
        ? typeArgs.arguments.first.toSource()
        : 'dynamic';
    final methods = <MethodInfo>[];
    for (final member in classBody.members) {
      if (member is MethodDeclaration) {
        if (member.name.lexeme == 'dispose') continue;
        methods.add(buildMethodInfo(member, callsNotifyListeners: false));
      }
    }
    nodes.add(
      LogicUnitNode(
        name: className,
        stateFields: [FieldInfo(rawName: 'value', type: valueType)],
        methods: methods,
        isNotifier: true,
        notifierType: NotifierType.stateNotifier,
        role: 'provider',
        superClassName: superClassName,
        mixins: mixins,
        filePath: filePath,
        offset: node.offset,
        length: node.length,
      ),
    );
    super.visitClassDeclaration(node);
    return;
  }

  // Detect ChangeNotifier classes (Standard Provider pattern)
  if (extendsClause != null && superClassName == 'ChangeNotifier') {
    final className = node.namePart.typeName.lexeme;
    final stateFields = <FieldInfo>[];
    final methods = <MethodInfo>[];

    bool isFamilyCandidate = false;
    for (final member in classBody.members) {
      if (member is FieldDeclaration) {
        for (final variable in member.fields.variables) {
          final typeSource = member.fields.type?.toSource() ?? 'dynamic';
          final initializer = variable.initializer?.toSource();
          stateFields.add(
            FieldInfo(
              rawName: variable.name.lexeme,
              type: typeSource,
              initializer: initializer,
            ),
          );
        }
      } else if (member is ConstructorDeclaration) {
        for (final param in member.parameters.parameters) {
          final paramName = param.name?.lexeme ?? '';
          if (paramName != 'key') {
            isFamilyCandidate = true;
            break;
          }
        }
      } else if (member is MethodDeclaration) {
        if (member.name.lexeme == 'dispose') continue;
        methods.add(
          buildMethodInfo(
            member,
            callsNotifyListeners: member.body.toSource().contains(
              'notifyListeners()',
            ),
          ),
        );
      }
    }

    nodes.add(
      LogicUnitNode(
        name: className,
        stateFields: stateFields,
        methods: methods,
        isNotifier: true,
        notifierType: detectNotifierType(methods),
        isFamilyCandidate: isFamilyCandidate,
        role: _inferRole(className, superClassName),
        superClassName: superClassName,
        mixins: mixins,
        filePath: filePath,
        offset: node.offset,
        length: node.length,
      ),
    );
  } else if (extendsClause != null && superClassName == 'StatelessWidget') {
    final className = node.namePart.typeName.lexeme;
    int buildMethodOffset = -1;

    for (final member in classBody.members) {
      if (member is MethodDeclaration && member.name.lexeme == 'build') {
        buildMethodOffset = member.offset;
      }
    }

    nodes.add(
      WidgetNode(
        widgetName: className,
        widgetType: 'StatelessWidget',
        buildMethodOffset: buildMethodOffset,
        filePath: filePath,
        offset: node.offset,
        length: node.length,
      ),
    );
  } else if (extendsClause != null && superClassName == 'StatefulWidget') {
    final className = node.namePart.typeName.lexeme;
    int createStateOffset = -1;
    for (final member in classBody.members) {
      if (member is MethodDeclaration &&
          member.name.lexeme == 'createState') {
        createStateOffset = member.offset;
      }
    }
    nodes.add(
      WidgetNode(
        widgetName: className,
        widgetType: 'StatefulWidget',
        buildMethodOffset: createStateOffset,
        filePath: filePath,
        offset: node.offset,
        length: node.length,
      ),
    );
  } else if (extendsClause != null && superClassName == 'State') {
    final className = node.namePart.typeName.lexeme;
    final typeArguments = extendsClause.superclass.typeArguments;
    if (typeArguments != null && typeArguments.arguments.isNotEmpty) {
      final widgetName = typeArguments.arguments.first.toSource();
      nodes.add(
        StateNode(
          stateClassName: className,
          widgetName: widgetName,
          filePath: filePath,
          offset: node.offset,
          length: node.length,
        ),
      );
    }
  } else if (extendsClause != null && superClassName == 'HookWidget') {
    final className = node.namePart.typeName.lexeme;
    int buildMethodOffset = -1;
    for (final member in classBody.members) {
      if (member is MethodDeclaration && member.name.lexeme == 'build') {
        buildMethodOffset = member.offset;
      }
    }
    nodes.add(
      HookWidgetNode(
        widgetName: className,
        buildMethodOffset: buildMethodOffset,
        filePath: filePath,
        offset: node.offset,
        length: node.length,
      ),
    );
  } else if (_isLogicClass(
    className: node.namePart.typeName.lexeme,
    superClassName: superClassName,
  )) {
    // Catch-all for other logic classes (repositories, services)
    final className = node.namePart.typeName.lexeme;
    nodes.add(
      LogicUnitNode(
        name: className,
        stateFields: [], // Not a framework-managed state
        methods: [], // Could be expanded to scan methods
        isNotifier: false,
        role: _inferRole(className, superClassName),
        superClassName: superClassName,
        mixins: mixins,
        filePath: filePath,
        offset: node.offset,
        length: node.length,
      ),
    );
  }

  super.visitClassDeclaration(node);
}