locateParseError function

ParseErrorLocation locateParseError(
  1. String text, {
  2. int? parserPosition,
  3. String? parserMessage,
  4. String? language,
})

Picks the best error location for text, given the parser's own parserPosition/parserMessage (which may be unreliable). language enables language-specific recovery (e.g. a missing ;).

Implementation

ParseErrorLocation locateParseError(
  String text, {
  int? parserPosition,
  String? parserMessage,
  String? language,
}) {
  final structural = _bracketImbalance(text);
  final msg = (parserMessage ?? '').toLowerCase();

  // The parser position is trustworthy only when it is concrete: non-zero and
  // not the catch-all "end of input expected" that PEG parsers emit at 0.
  final parserConcrete =
      parserPosition != null &&
      parserPosition > 0 &&
      parserPosition <= text.length &&
      !msg.contains('end of input');

  int offset;
  String? hint;
  if (structural != null) {
    // A bracket imbalance is the most fundamental structural fault — trust it
    // over any parser position, and describe it (e.g. "'(' is never closed").
    offset = structural.offset;
    hint = structural.hint;
  } else {
    // Brackets balance. Prefer a confidently-located missing statement
    // terminator (reported at the end of the offending value — the editor
    // convention) before trusting the parser's own position.
    final missing = (language != null && _semicolonLanguages.contains(language))
        ? _missingTerminator(text)
        : null;
    if (missing != null) {
      offset = missing.offset;
      hint = missing.hint;
    } else if (parserConcrete) {
      // A concrete parser position; with farthest-failure tracking (Dart) this
      // now lands on or next to the real error (e.g. a bad token mid-line).
      offset = parserPosition;
    } else if (parserPosition != null &&
        parserPosition > 0 &&
        parserPosition <= text.length) {
      offset = parserPosition;
    } else {
      offset = _firstMeaningfulOffset(text);
    }
  }

  final (start, end) = _tokenRange(text, offset);
  return ParseErrorLocation(
    offset: offset,
    rangeStart: start,
    rangeEnd: end,
    hint: hint,
  );
}