run method

  1. @override
void run(
  1. CustomLintResolver resolver,
  2. DiagnosticReporter reporter,
  3. CustomLintContext context
)

Emits lints for a given file.

run will only be invoked with files respecting filesToAnalyze

Implementation

@override
void run(
  CustomLintResolver resolver,
  DiagnosticReporter reporter,
  CustomLintContext context,
) {
  context.registry.addInstanceCreationExpression((node) {
    final type = node.staticType;
    if (type == null) {
      return;
    }

    final typeName = type.element?.name;

    // Check for interactive widgets
    final interactiveTypes = [
      'IconButton',
      'InkWell',
      'GestureDetector',
      'TextButton',
      'ElevatedButton',
      'OutlinedButton',
    ];

    if (!interactiveTypes.contains(typeName)) {
      return;
    }

    // For IconButton, check if iconSize or constraints are specified
    if (typeName == 'IconButton') {
      for (final arg in node.argumentList.arguments) {
        if (arg is NamedExpression) {
          final paramName = arg.name.label.name;

          // Check iconSize parameter
          if (paramName == 'iconSize') {
            final value = extractNumericValue(arg.expression);
            if (value != null && value < minimumSize) {
              reporter.atNode(node, code);
              return;
            }
          }
        }
      }

      // IconButton defaults to 48x48, so only warn if explicitly set smaller
      // We'll skip reporting if no size is specified (uses default)
    }

    // For Material buttons, check if style overrides minimum size
    if (typeName == 'TextButton' ||
        typeName == 'ElevatedButton' ||
        typeName == 'OutlinedButton') {
      for (final arg in node.argumentList.arguments) {
        if (arg is NamedExpression && arg.name.label.name == 'style') {
          final dimensions = extractButtonStyleSize(arg.expression);
          if (dimensions != null &&
              (dimensions.width < minimumSize ||
                  dimensions.height < minimumSize)) {
            reporter.atNode(node, code);
            return;
          }
        }
      }
    }

    // For InkWell/GestureDetector, check if they wrap a SizedBox or Container with size
    // OR if they are wrapped by a SizedBox/Container with small size
    if (typeName == 'InkWell' || typeName == 'GestureDetector') {
      bool foundSmallSize = false;

      // First check if the widget itself wraps a sized child
      for (final arg in node.argumentList.arguments) {
        if (arg is NamedExpression && arg.name.label.name == 'child') {
          final child = arg.expression;
          if (child is InstanceCreationExpression) {
            final childType = child.staticType?.element?.name;

            // Check if child is SizedBox or Container with explicit size
            if (childType == 'SizedBox' || childType == 'Container') {
              final dimensions = extractDimensions(child);
              if (dimensions != null) {
                if (dimensions.width < minimumSize ||
                    dimensions.height < minimumSize) {
                  foundSmallSize = true;
                  break;
                }
              }
            }
          }
        }
      }

      // Also check if the widget is wrapped by a SizedBox/Container with small size
      if (!foundSmallSize) {
        AstNode? parent = node.parent;
        while (parent != null) {
          if (parent is NamedExpression &&
              parent.name.label.name == 'child') {
            final grandParent = parent.parent?.parent;
            if (grandParent is InstanceCreationExpression) {
              final parentType = grandParent.staticType?.element?.name;
              if (parentType == 'SizedBox' || parentType == 'Container') {
                final dimensions = extractDimensions(grandParent);
                if (dimensions != null) {
                  if (dimensions.width < minimumSize ||
                      dimensions.height < minimumSize) {
                    foundSmallSize = true;
                    break;
                  }
                }
              }
            }
          }
          parent = parent.parent;
        }
      }

      if (foundSmallSize) {
        reporter.atNode(node, code);
      }
    }
  });
}