Skip to content

RangeError when input string contains \r\n and a pattern matches an empty string #1797

Closed
dart-archive/string_scanner
#81
@DanTup

Description

@DanTup

I hit this while trying to update the Dart textmate grammar (which uses this class for testing) to use negative look-aheads. If the input string contains \r\n and a pattern matches a zero-length string when the scanner is between \r and \n, this exception is thrown:

RangeError (length): Invalid value: Valid value range is empty: -1
#0      List.[] (dart:core-patch/growable_array.dart)
dart-lang/tools#1787      List.removeLast (dart:core-patch/growable_array.dart:337:20)
dart-lang/tools#1788      LineScanner._newlinesIn (package:string_scanner/src/line_scanner.dart:122:16)
dart-lang/string_scanner#3      LineScanner.scan (package:string_scanner/src/line_scanner.dart:106:22)
dart-lang/tools#1789      main (file:///D:/Dev/Test%20Projects/string_scanner_cr_lf/bin/string_scanner_cr_lf.dart:15:44)
dart-lang/string_scanner#5      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:295:33)
dart-lang/tools#1790      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

scan() calls _newlinesIn() which seems to assume that if the current position is between \r and \n then the matched text will always contain at least one newline.

Here's a repro that matches a line starting /// and then tries to scan for a line that does not start ///. With the \n line ending, it works fine, but with \r\n it throws the error above.

import 'dart:convert';

import 'package:string_scanner/string_scanner.dart';

void main(List<String> arguments) {
  for (var lineEnding in ['\n', '\r\n']) {
    var scanner = LineScanner('/// test${lineEnding}test');
    var startPattern = RegExp(r'^///', multiLine: true);
    var endPattern = RegExp(r'^(?!///)', multiLine: true);

    print('');
    print('Testing with ${jsonEncode(lineEnding)}');
    while (!scanner.isDone) {
      if (scanner.scan(startPattern)) {
        print('');
        print('Line ${scanner.line} starts comment');
        while (!scanner.isDone && !scanner.scan(endPattern)) {
          scanner.readChar();
        }
        print('Line ${scanner.line} ends comment');
      }
      if (scanner.isDone) break;
      scanner.readChar();
    }
  }
}

The output looks like:

Testing with "\n"

Line 0 starts comment
Line 1 ends comment

Testing with "\r\n"

Line 0 starts comment
Unhandled exception:
RangeError (length): Invalid value: Valid value range is empty: -1
#0      List.[] (dart:core-patch/growable_array.dart)
dart-lang/tools#1787      List.removeLast (dart:core-patch/growable_array.dart:337:20)
dart-lang/tools#1788      LineScanner._newlinesIn (package:string_scanner/src/line_scanner.dart:122:16)
dart-lang/string_scanner#3      LineScanner.scan (package:string_scanner/src/line_scanner.dart:106:22)
dart-lang/tools#1789      main (file:///D:/Dev/Test%20Projects/string_scanner_cr_lf/bin/string_scanner_cr_lf.dart:17:44)
dart-lang/string_scanner#5      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:295:33)
dart-lang/tools#1790      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions