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 interactiveWidgets = [
      'IconButton',
      'GestureDetector',
      'InkWell',
      'InkResponse',
    ];

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

    // For GestureDetector, InkWell, InkResponse: only check if they have interactive callbacks
    // IconButton is always considered interactive
    if (typeName != 'IconButton' && !hasInteractiveCallbacks(node)) {
      return;
    }

    // Check for tooltip parameter (directly on the widget)
    bool hasTooltip = false;
    for (final arg in node.argumentList.arguments) {
      if (arg is NamedExpression && arg.name.label.name == 'tooltip') {
        hasTooltip = hasNonEmptyStringValue(arg.expression);
        if (hasTooltip) break;
      }
    }

    // For IconButton, also check if the icon has a semanticLabel
    if (!hasTooltip && typeName == 'IconButton') {
      for (final arg in node.argumentList.arguments) {
        if (arg is NamedExpression && arg.name.label.name == 'icon') {
          final iconExpression = arg.expression;
          if (iconExpression is InstanceCreationExpression) {
            // Check if Icon has semanticLabel parameter
            for (final iconArg in iconExpression.argumentList.arguments) {
              if (iconArg is NamedExpression &&
                  iconArg.name.label.name == 'semanticLabel') {
                hasTooltip = hasNonEmptyStringValue(iconArg.expression);
                if (hasTooltip) break;
              }
            }
          }
          if (hasTooltip) break;
        }
      }
    }

    // Check for parent tooltip or Semantics wrapper
    if (!hasTooltip) {
      hasTooltip = hasParentTooltip(node);
    }

    final semanticInfo = getSemanticWrappingInfo(node);

    // Report if no tooltip and not wrapped in Semantics with label
    if (!hasTooltip && !semanticInfo.hasSemantics) {
      reporter.atNode(node, code);
    }
  });
}