checkSource function

CheckResult checkSource(
  1. String filePath,
  2. String source
)

Run the full check pipeline (skip-directive scan + AST visitor) against a source string.

This is the library entry point: the check_js_invariants executable calls it over file contents, and consumer test suites can call it against synthetic inputs to pin the rules.

Implementation

CheckResult checkSource(String filePath, String source) {
  // Skip-directive check: scan the first 10 lines for a valid marker. A
  // malformed marker (present but no issue ref, empty reason, or
  // whitespace-only reason after trim) fails the check.
  final header = source.split('\n').take(10).toList();
  String? skipReason;
  int? skipIssue;
  int? malformedMarkerLine;
  for (var i = 0; i < header.length; i++) {
    final line = header[i];
    final m = _skipDirective.firstMatch(line);
    if (m != null) {
      final reason = m.group(1)!.trim();
      if (reason.isNotEmpty) {
        skipReason = reason;
        skipIssue = int.parse(m.group(2)!);
        break;
      }
      malformedMarkerLine = i + 1;
      continue;
    }
    if (_skipDirectiveMarker.hasMatch(line)) {
      malformedMarkerLine = i + 1;
    }
  }

  if (skipReason == null && malformedMarkerLine != null) {
    return CheckResult(
      filePath: filePath,
      violations: [
        '❌ $filePath:$malformedMarkerLine — malformed skip directive\n'
            '   Required: // CHECK_JS_INVARIANTS_SKIP_FILE: <reason> #<issue>\n'
            '   Both <reason> (non-empty) and #<issue> are required.',
      ],
      skipDirective: null,
    );
  }

  if (skipReason != null) {
    return CheckResult(
      filePath: filePath,
      violations: const [],
      skipDirective: (reason: skipReason, issue: skipIssue!),
    );
  }

  final parsed = parseString(content: source, throwIfDiagnostics: false);
  final lineStarts = _computeLineStarts(source);
  final visitor = _InvariantChecker(filePath, lineStarts);
  parsed.unit.visitChildren(visitor);
  return CheckResult(
    filePath: filePath,
    violations: List.unmodifiable(visitor.violations),
    skipDirective: null,
  );
}