update scanner config based on language comment

... and add ScannerConfiguration.nonNullable const

Change-Id: I6917c535e40e94cb591f2c4f5582dc0127be17f3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100884
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/analyzer/test/generated/parser_fasta_test.dart b/pkg/analyzer/test/generated/parser_fasta_test.dart
index b4674f0..753fac2 100644
--- a/pkg/analyzer/test/generated/parser_fasta_test.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_test.dart
@@ -20,7 +20,7 @@
 import 'package:front_end/src/fasta/parser/forwarding_listener.dart' as fasta;
 import 'package:front_end/src/fasta/parser/parser.dart' as fasta;
 import 'package:front_end/src/fasta/scanner.dart'
-    show ScannerResult, scanString;
+    show ScannerConfiguration, ScannerResult, scanString;
 import 'package:front_end/src/fasta/scanner/error_token.dart' show ErrorToken;
 import 'package:front_end/src/fasta/scanner/string_scanner.dart';
 import 'package:front_end/src/scanner/errors.dart' show translateErrorToken;
@@ -1515,10 +1515,8 @@
   void createParser(String content,
       {int expectedEndOffset, FeatureSet featureSet}) {
     featureSet ??= FeatureSet.forTesting();
-    var scanner = new StringScanner(content, includeComments: true);
-    if (featureSet != null) {
-      scanner.enableNonNullable = featureSet.isEnabled(Feature.non_nullable);
-    }
+    var scanner = new StringScanner(content,
+        configuration: ScannerConfiguration.nonNullable, includeComments: true);
     _fastaTokens = scanner.tokenize();
     _parserProxy = new ParserProxy(_fastaTokens, featureSet,
         allowNativeClause: allowNativeClause,
@@ -2297,6 +2295,23 @@
 ''', featureSet: preNonNullable);
   }
 
+  void test_late_as_identifier_opt_out() {
+    parseCompilationUnit('''
+// @dart = 2.2
+class C {
+  int late;
+}
+
+void f(C c) {
+  print(c.late);
+}
+
+main() {
+  f(new C());
+}
+''', featureSet: nonNullable);
+  }
+
   void test_nullCheck() {
     var unit = parseNNBDCompilationUnit('f(int? y) { var x = y!; }');
     FunctionDeclaration function = unit.declarations[0];
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 5d6c139..337a7b7 100644
--- a/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
@@ -976,6 +976,7 @@
     }
 
     languageVersion = createLanguageVersionToken(start, major, minor);
+    configuration = ScannerConfiguration.from(languageVersion);
     if (includeComments) {
       _appendToCommentStream(languageVersion);
     }
@@ -1531,6 +1532,13 @@
 /// the scanner produces based upon the Dart language level.
 class ScannerConfiguration {
   static const classic = ScannerConfiguration();
+  static const nonNullable = ScannerConfiguration(enableNonNullable: true);
+
+  /// Return the scanner configuration for the given language version.
+  static ScannerConfiguration from(LanguageVersionToken languageVersion) {
+    // TODO(danrubel): update this to return config for new releases
+    return classic;
+  }
 
   /// Experimental flag for enabling scanning of NNBD tokens
   /// such as 'required' and 'late'
diff --git a/pkg/front_end/test/scanner_fasta_test.dart b/pkg/front_end/test/scanner_fasta_test.dart
index 8c524fc..d4f785b 100644
--- a/pkg/front_end/test/scanner_fasta_test.dart
+++ b/pkg/front_end/test/scanner_fasta_test.dart
@@ -837,6 +837,17 @@
     expectComments(result.tokens, ['// @dart = 2.3'], 0);
   }
 
+  void test_languageVersion_beforeLibrary_noSpaces() {
+    var result = scanSource('''
+// @dart=2.3
+library foo;
+main() {}
+''');
+    expect(result.languageVersion.major, 2);
+    expect(result.languageVersion.minor, 3);
+    expectComments(result.tokens, ['// @dart=2.3'], 0);
+  }
+
   void test_languageVersion_incomplete_version() {
     var result = scanSource('''
 // @dart = 2.
diff --git a/pkg/front_end/test/token_test.dart b/pkg/front_end/test/token_test.dart
index ec1becf..fb9f1ae 100644
--- a/pkg/front_end/test/token_test.dart
+++ b/pkg/front_end/test/token_test.dart
@@ -2,6 +2,7 @@
 // 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 'package:front_end/src/fasta/scanner.dart' show ScannerConfiguration;
 import 'package:front_end/src/fasta/scanner/string_scanner.dart';
 import 'package:front_end/src/scanner/token.dart';
 import 'package:test/test.dart';
@@ -125,8 +126,9 @@
     ]);
     for (Keyword keyword in Keyword.values) {
       var isModifier = modifierKeywords.contains(keyword);
-      var scanner = new StringScanner(keyword.lexeme, includeComments: true)
-        ..enableNonNullable = true;
+      var scanner = new StringScanner(keyword.lexeme,
+          configuration: ScannerConfiguration.nonNullable,
+          includeComments: true);
       Token token = scanner.tokenize();
       expect(token.isModifier, isModifier, reason: keyword.name);
       if (isModifier) {