encapsulate scanner configuration in ScannerConfiguration

... and update TypeInfoTest to use ScannerConfiguration

Change-Id: I637d6fbeecd0b6d16c37e57cfd8ba9fc1fba494a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100791
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/front_end/lib/src/fasta/scanner.dart b/pkg/front_end/lib/src/fasta/scanner.dart
index e3947f1..ae96681 100644
--- a/pkg/front_end/lib/src/fasta/scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner.dart
@@ -8,12 +8,16 @@
 
 import '../scanner/token.dart' show Token;
 
+import 'scanner/abstract_scanner.dart' show ScannerConfiguration;
+
 import 'scanner/string_scanner.dart' show StringScanner;
 
 import 'scanner/utf8_bytes_scanner.dart' show Utf8BytesScanner;
 
 import 'scanner/recover.dart' show defaultRecoveryStrategy;
 
+export 'scanner/abstract_scanner.dart' show ScannerConfiguration;
+
 export 'scanner/token.dart'
     show
         StringToken,
@@ -58,32 +62,42 @@
 /// Scan/tokenize the given UTF8 [bytes].
 /// If [recover] is null, then the [defaultRecoveryStrategy] is used.
 ScannerResult scan(List<int> bytes,
-    {bool includeComments: false, Recover recover}) {
+    {bool includeComments: false,
+    ScannerConfiguration configuration,
+    Recover recover}) {
   if (bytes.last != 0) {
     throw new ArgumentError("[bytes]: the last byte must be null.");
   }
-  Scanner scanner =
-      new Utf8BytesScanner(bytes, includeComments: includeComments);
+  Scanner scanner = new Utf8BytesScanner(bytes,
+      configuration: configuration, includeComments: includeComments);
   return _tokenizeAndRecover(scanner, recover, bytes: bytes);
 }
 
 /// Scan/tokenize the given [source].
 /// If [recover] is null, then the [defaultRecoveryStrategy] is used.
 ScannerResult scanString(String source,
-    {bool enableGtGtGt: false,
-    bool enableGtGtGtEq: false,
-    bool enableNonNullable: false,
+    {bool enableGtGtGt,
+    bool enableGtGtGtEq,
+    bool enableNonNullable,
+    ScannerConfiguration configuration,
     bool includeComments: false,
     bool scanLazyAssignmentOperators: false,
     Recover recover}) {
   // TODO(brianwilkerson): Remove the parameter `enableGtGtGt` after the feature
   // has been anabled by default.
   assert(source != null, 'source must not be null');
-  StringScanner scanner =
-      new StringScanner(source, includeComments: includeComments);
-  scanner.enableGtGtGt = enableGtGtGt;
-  scanner.enableGtGtGtEq = enableGtGtGtEq;
-  scanner.enableNonNullable = enableNonNullable;
+  StringScanner scanner = new StringScanner(source,
+      configuration: configuration, includeComments: includeComments);
+  // TODO(danrubel): remove these flags and use configuration instead.
+  if (enableGtGtGt != null) {
+    scanner.enableGtGtGt = enableGtGtGt;
+  }
+  if (enableGtGtGtEq != null) {
+    scanner.enableGtGtGtEq = enableGtGtGtEq;
+  }
+  if (enableNonNullable != null) {
+    scanner.enableNonNullable = enableNonNullable;
+  }
   return _tokenizeAndRecover(scanner, recover, source: source);
 }
 
diff --git a/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart b/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
index 65f9f6e..19f1dee 100644
--- a/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
@@ -94,9 +94,22 @@
 
   final List<int> lineStarts;
 
-  AbstractScanner(this.includeComments, {int numberOfBytesHint})
+  AbstractScanner(ScannerConfiguration config, this.includeComments,
+      {int numberOfBytesHint})
       : lineStarts = new LineStarts(numberOfBytesHint) {
     this.tail = this.tokens;
+    this.configuration = config;
+  }
+
+  /**
+   * Configure which tokens are produced based upon the specified configuration.
+   */
+  set configuration(ScannerConfiguration config) {
+    if (config != null) {
+      enableNonNullable = config.enableNonNullable;
+      enableGtGtGt = config.enableGtGtGt;
+      enableGtGtGtEq = config.enableGtGtGtEq;
+    }
   }
 
   /**
@@ -1378,3 +1391,31 @@
     array = newArray;
   }
 }
+
+/// [ScannerConfiguration] contains information for configuring which tokens
+/// the scanner produces based upon the Dart language level.
+class ScannerConfiguration {
+  static const classic = ScannerConfiguration();
+
+  /// Experimental flag for enabling scanning of NNBD tokens
+  /// such as 'required' and 'late'
+  final bool enableNonNullable;
+
+  /// Experimental flag for enabling scanning of `>>>`.
+  /// See https://github.com/dart-lang/language/issues/61
+  /// and https://github.com/dart-lang/language/issues/60
+  final bool enableGtGtGt;
+
+  /// Experimental flag for enabling scanning of `>>>=`.
+  /// See https://github.com/dart-lang/language/issues/61
+  /// and https://github.com/dart-lang/language/issues/60
+  final bool enableGtGtGtEq;
+
+  const ScannerConfiguration({
+    bool enableGtGtGt,
+    bool enableGtGtGtEq,
+    bool enableNonNullable,
+  })  : this.enableGtGtGt = enableGtGtGt ?? false,
+        this.enableGtGtGtEq = enableGtGtGtEq ?? false,
+        this.enableNonNullable = enableNonNullable ?? false;
+}
diff --git a/pkg/front_end/lib/src/fasta/scanner/array_based_scanner.dart b/pkg/front_end/lib/src/fasta/scanner/array_based_scanner.dart
index b6e0f09..66e7e39 100644
--- a/pkg/front_end/lib/src/fasta/scanner/array_based_scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/array_based_scanner.dart
@@ -20,15 +20,17 @@
 
 import 'characters.dart' show $LF, $STX;
 
-import 'abstract_scanner.dart' show AbstractScanner, closeBraceInfoFor;
+import 'abstract_scanner.dart'
+    show AbstractScanner, ScannerConfiguration, closeBraceInfoFor;
 
 import '../util/link.dart' show Link;
 
 abstract class ArrayBasedScanner extends AbstractScanner {
   bool hasErrors = false;
 
-  ArrayBasedScanner(bool includeComments, {int numberOfBytesHint})
-      : super(includeComments, numberOfBytesHint: numberOfBytesHint);
+  ArrayBasedScanner(ScannerConfiguration config, bool includeComments,
+      {int numberOfBytesHint})
+      : super(config, includeComments, numberOfBytesHint: numberOfBytesHint);
 
   /**
    * The stack of open groups, e.g [: { ... ( .. :]
diff --git a/pkg/front_end/lib/src/fasta/scanner/string_scanner.dart b/pkg/front_end/lib/src/fasta/scanner/string_scanner.dart
index 952099d..93fe008 100644
--- a/pkg/front_end/lib/src/fasta/scanner/string_scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/string_scanner.dart
@@ -8,6 +8,8 @@
 
 import '../../scanner/token.dart' as analyzer show StringToken;
 
+import 'abstract_scanner.dart' show ScannerConfiguration;
+
 import 'array_based_scanner.dart' show ArrayBasedScanner;
 
 import 'token.dart' show CommentToken, DartDocToken, StringToken;
@@ -25,9 +27,10 @@
   /** The current offset in [string]. */
   int scanOffset = -1;
 
-  StringScanner(String string, {bool includeComments: false})
+  StringScanner(String string,
+      {ScannerConfiguration configuration, bool includeComments: false})
       : string = ensureZeroTermination(string),
-        super(includeComments);
+        super(configuration, includeComments);
 
   static String ensureZeroTermination(String string) {
     return (string.isEmpty || string.codeUnitAt(string.length - 1) != 0)
diff --git a/pkg/front_end/lib/src/fasta/scanner/utf8_bytes_scanner.dart b/pkg/front_end/lib/src/fasta/scanner/utf8_bytes_scanner.dart
index eec14d4..70f61c8 100644
--- a/pkg/front_end/lib/src/fasta/scanner/utf8_bytes_scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/utf8_bytes_scanner.dart
@@ -12,6 +12,8 @@
 
 import '../scanner.dart' show unicodeReplacementCharacter;
 
+import 'abstract_scanner.dart' show ScannerConfiguration;
+
 import 'token.dart' show CommentToken, DartDocToken, StringToken;
 
 import 'array_based_scanner.dart' show ArrayBasedScanner;
@@ -81,8 +83,9 @@
    * array whose last element is '0' to signal the end of the file. If this
    * is not the case, the entire array is copied before scanning.
    */
-  Utf8BytesScanner(this.bytes, {bool includeComments: false})
-      : super(includeComments, numberOfBytesHint: bytes.length) {
+  Utf8BytesScanner(this.bytes,
+      {ScannerConfiguration configuration, bool includeComments: false})
+      : super(configuration, includeComments, numberOfBytesHint: bytes.length) {
     assert(bytes.last == 0);
     // Skip a leading BOM.
     if (containsBomAt(0)) byteOffset += 3;
diff --git a/pkg/front_end/test/fasta/parser/type_info_test.dart b/pkg/front_end/test/fasta/parser/type_info_test.dart
index d095d9a..6adff9e 100644
--- a/pkg/front_end/test/fasta/parser/type_info_test.dart
+++ b/pkg/front_end/test/fasta/parser/type_info_test.dart
@@ -2,15 +2,12 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import 'dart:convert';
-
 import 'package:front_end/src/fasta/messages.dart';
 import 'package:front_end/src/fasta/parser.dart';
 import 'package:front_end/src/fasta/parser/type_info.dart';
 import 'package:front_end/src/fasta/parser/type_info_impl.dart';
 import 'package:front_end/src/fasta/scanner.dart' hide scanString;
-import 'package:front_end/src/fasta/scanner/recover.dart'
-    show defaultRecoveryStrategy;
+import 'package:front_end/src/fasta/scanner.dart' as scanner;
 import 'package:front_end/src/scanner/token.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -33,34 +30,13 @@
   });
 }
 
-/// TODO(danrubel): Remove this and use scanner.dart scanString
-/// once support for `>>>` is permanently enabled.
-///
-/// Scan/tokenize the given [source].
-/// If [recover] is null, then the [defaultRecoveryStrategy] is used.
 ScannerResult scanString(String source,
-    {bool includeComments: false,
-    bool scanLazyAssignmentOperators: false,
-    Recover recover}) {
-  assert(source != null, 'source must not be null');
-  StringScanner scanner =
-      new StringScanner(source, includeComments: includeComments)
-        ..enableGtGtGt = true
-        ..enableGtGtGtEq = true;
-  return _tokenizeAndRecover(scanner, recover, source: source);
-}
-
-/// TODO(danrubel): Remove this once support for `>>>` is permanently enabled.
-ScannerResult _tokenizeAndRecover(Scanner scanner, Recover recover,
-    {List<int> bytes, String source}) {
-  Token tokens = scanner.tokenize();
-  if (scanner.hasErrors) {
-    if (bytes == null) bytes = utf8.encode(source);
-    recover ??= defaultRecoveryStrategy;
-    tokens = recover(bytes, tokens, scanner.lineStarts);
-  }
-  return new ScannerResult(tokens, scanner.lineStarts, scanner.hasErrors);
-}
+        {bool includeComments: false, Recover recover}) =>
+    scanner.scanString(source,
+        configuration: const ScannerConfiguration(
+            enableGtGtGt: true, enableGtGtGtEq: true),
+        includeComments: includeComments,
+        recover: recover);
 
 @reflectiveTest
 class NoTypeInfoTest {