add "extension" built-in keyword for extension methods
Change-Id: I6529fff31f681fd24c22f8c7579d2a35097f4d07
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/102940
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/analyzer/lib/src/dart/scanner/scanner.dart b/pkg/analyzer/lib/src/dart/scanner/scanner.dart
index 12599db..b446a32 100644
--- a/pkg/analyzer/lib/src/dart/scanner/scanner.dart
+++ b/pkg/analyzer/lib/src/dart/scanner/scanner.dart
@@ -199,6 +199,8 @@
featureSet == null
? fasta.ScannerConfiguration()
: fasta.ScannerConfiguration(
+ enableExtensionMethods:
+ featureSet.isEnabled(Feature.extension_methods),
enableTripleShift: featureSet.isEnabled(Feature.triple_shift),
enableNonNullable: featureSet.isEnabled(Feature.non_nullable));
}
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 637bbc1..e59d613 100644
--- a/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
+++ b/pkg/front_end/lib/src/fasta/scanner/abstract_scanner.dart
@@ -51,15 +51,18 @@
/// based upon the specified language version.
final LanguageVersionChanged languageVersionChanged;
- /// Experimental flag for enabling scanning of `>>>`.
- /// See https://github.com/dart-lang/language/issues/61
- /// and https://github.com/dart-lang/language/issues/60
- bool _enableTripleShift = false;
+ /// Experimental flag for enabling scanning of the `extension` token.
+ bool _enableExtensionMethods = false;
/// Experimental flag for enabling scanning of NNBD tokens
/// such as 'required' and 'late'.
bool _enableNonNullable = false;
+ /// Experimental flag for enabling scanning of `>>>`.
+ /// See https://github.com/dart-lang/language/issues/61
+ /// and https://github.com/dart-lang/language/issues/60
+ bool _enableTripleShift = false;
+
/**
* The string offset for the next token that will be created.
*
@@ -108,6 +111,7 @@
@override
set configuration(ScannerConfiguration config) {
if (config != null) {
+ _enableExtensionMethods = config.enableExtensionMethods;
_enableNonNullable = config.enableNonNullable;
_enableTripleShift = config.enableTripleShift;
}
@@ -1135,6 +1139,9 @@
if (state == null || state.keyword == null) {
return tokenizeIdentifier(next, start, allowDollar);
}
+ if (!_enableExtensionMethods && state.keyword == Keyword.EXTENSION) {
+ return tokenizeIdentifier(next, start, allowDollar);
+ }
if (!_enableNonNullable &&
(state.keyword == Keyword.LATE || state.keyword == Keyword.REQUIRED)) {
return tokenizeIdentifier(next, start, allowDollar);
@@ -1533,6 +1540,9 @@
static const classic = ScannerConfiguration();
static const nonNullable = ScannerConfiguration(enableNonNullable: true);
+ /// Experimental flag for enabling scanning of the `extension` keyword.
+ final bool enableExtensionMethods;
+
/// Experimental flag for enabling scanning of NNBD tokens
/// such as 'required' and 'late'
final bool enableNonNullable;
@@ -1543,8 +1553,10 @@
final bool enableTripleShift;
const ScannerConfiguration({
- bool enableTripleShift,
+ bool enableExtensionMethods,
bool enableNonNullable,
- }) : this.enableTripleShift = enableTripleShift ?? false,
- this.enableNonNullable = enableNonNullable ?? false;
+ bool enableTripleShift,
+ }) : this.enableExtensionMethods = enableExtensionMethods ?? false,
+ this.enableNonNullable = enableNonNullable ?? false,
+ this.enableTripleShift = enableTripleShift ?? false;
}
diff --git a/pkg/front_end/lib/src/scanner/token.dart b/pkg/front_end/lib/src/scanner/token.dart
index 9f1ba8a..5947f80 100644
--- a/pkg/front_end/lib/src/scanner/token.dart
+++ b/pkg/front_end/lib/src/scanner/token.dart
@@ -173,6 +173,9 @@
static const Keyword EXTENDS = const Keyword("extends", "EXTENDS");
+ static const Keyword EXTENSION = const Keyword("extension", "EXTENSION",
+ isBuiltIn: true, isTopLevelKeyword: true);
+
static const Keyword EXTERNAL =
const Keyword("external", "EXTERNAL", isBuiltIn: true, isModifier: true);
@@ -303,6 +306,7 @@
ENUM,
EXPORT,
EXTENDS,
+ EXTENSION,
EXTERNAL,
FACTORY,
FALSE,
diff --git a/pkg/front_end/test/scanner_fasta_test.dart b/pkg/front_end/test/scanner_fasta_test.dart
index abe0879..210e851 100644
--- a/pkg/front_end/test/scanner_fasta_test.dart
+++ b/pkg/front_end/test/scanner_fasta_test.dart
@@ -52,9 +52,11 @@
@reflectiveTest
class ScannerTest_Fasta_UTF8 extends ScannerTest_Fasta {
@override
- Token scanWithListener(String source, ErrorListener listener) {
+ Token scanWithListener(String source, ErrorListener listener,
+ {ScannerConfiguration configuration}) {
var bytes = utf8.encode(source).toList()..add(0);
- var result = scan(bytes, includeComments: true);
+ var result =
+ scan(bytes, configuration: configuration, includeComments: true);
var token = result.tokens;
// Translate error tokens
@@ -106,8 +108,10 @@
@reflectiveTest
class ScannerTest_Fasta extends ScannerTestBase {
@override
- Token scanWithListener(String source, ErrorListener listener) {
- var result = scanString(source, includeComments: true);
+ Token scanWithListener(String source, ErrorListener listener,
+ {ScannerConfiguration configuration}) {
+ var result =
+ scanString(source, configuration: configuration, includeComments: true);
var token = result.tokens;
// Translate error tokens
diff --git a/pkg/front_end/test/scanner_replacement_test.dart b/pkg/front_end/test/scanner_replacement_test.dart
index 041821a..b0a979a 100644
--- a/pkg/front_end/test/scanner_replacement_test.dart
+++ b/pkg/front_end/test/scanner_replacement_test.dart
@@ -28,13 +28,14 @@
@reflectiveTest
class ScannerTest_Replacement extends ScannerTestBase {
@override
- analyzer.Token scanWithListener(String source, ErrorListener listener) {
+ analyzer.Token scanWithListener(String source, ErrorListener listener,
+ {fasta.ScannerConfiguration configuration}) {
// Process the source similar to
// pkg/analyzer/lib/src/dart/scanner/scanner.dart
// to simulate replacing the analyzer scanner
- fasta.ScannerResult result =
- fasta.scanString(source, includeComments: true);
+ fasta.ScannerResult result = fasta.scanString(source,
+ configuration: configuration, includeComments: true);
fasta.Token tokens = result.tokens;
assertValidTokenStream(tokens, errorsFirst: true);
diff --git a/pkg/front_end/test/scanner_test.dart b/pkg/front_end/test/scanner_test.dart
index 36b1477..932d67a 100644
--- a/pkg/front_end/test/scanner_test.dart
+++ b/pkg/front_end/test/scanner_test.dart
@@ -4,7 +4,7 @@
import 'package:front_end/src/base/errors.dart';
import 'package:front_end/src/fasta/scanner/abstract_scanner.dart'
- show AbstractScanner;
+ show AbstractScanner, ScannerConfiguration;
import 'package:front_end/src/scanner/errors.dart';
import 'package:front_end/src/scanner/reader.dart';
import 'package:front_end/src/scanner/token.dart';
@@ -79,7 +79,8 @@
}
abstract class ScannerTestBase {
- Token scanWithListener(String source, ErrorListener listener);
+ Token scanWithListener(String source, ErrorListener listener,
+ {ScannerConfiguration configuration});
void test_ampersand() {
_assertToken(TokenType.AMPERSAND, "&");
@@ -442,6 +443,16 @@
_assertKeywordToken("extends");
}
+ void test_keyword_extension() {
+ _assertKeywordToken("extension",
+ configuration: ScannerConfiguration(enableExtensionMethods: true));
+ }
+
+ void test_keyword_extension_old() {
+ _assertNotKeywordToken("extension",
+ configuration: ScannerConfiguration(enableExtensionMethods: false));
+ }
+
void test_keyword_factory() {
_assertKeywordToken("factory");
}
@@ -494,6 +505,16 @@
_assertKeywordToken("is");
}
+ void test_keyword_late() {
+ _assertKeywordToken("late",
+ configuration: ScannerConfiguration(enableNonNullable: true));
+ }
+
+ void test_keyword_late_old() {
+ _assertNotKeywordToken("late",
+ configuration: ScannerConfiguration(enableNonNullable: false));
+ }
+
void test_keyword_library() {
_assertKeywordToken("library");
}
@@ -534,6 +555,16 @@
_assertKeywordToken("patch");
}
+ void test_keyword_required() {
+ _assertKeywordToken("required",
+ configuration: ScannerConfiguration(enableNonNullable: true));
+ }
+
+ void test_keyword_required_disabled() {
+ _assertNotKeywordToken("required",
+ configuration: ScannerConfiguration(enableNonNullable: false));
+ }
+
void test_keyword_rethrow() {
_assertKeywordToken("rethrow");
}
@@ -1298,8 +1329,9 @@
* Assert that when scanned the given [source] contains a single keyword token
* with the same lexeme as the original source.
*/
- void _assertKeywordToken(String source) {
- Token token = _scan(source);
+ void _assertKeywordToken(String source,
+ {ScannerConfiguration configuration}) {
+ Token token = _scan(source, configuration: configuration);
expect(token, isNotNull);
expect(token.type.isKeyword, true);
expect(token.offset, 0);
@@ -1308,7 +1340,7 @@
Object value = token.value();
expect(value is Keyword, isTrue);
expect((value as Keyword).lexeme, source);
- token = _scan(" $source ");
+ token = _scan(" $source ", configuration: configuration);
expect(token, isNotNull);
expect(token.type.isKeyword, true);
expect(token.offset, 1);
@@ -1321,6 +1353,27 @@
}
/**
+ * Assert that when scanned the given [source] contains a single identifier token
+ * with the same lexeme as the original source.
+ */
+ void _assertNotKeywordToken(String source,
+ {ScannerConfiguration configuration}) {
+ Token token = _scan(source, configuration: configuration);
+ expect(token, isNotNull);
+ expect(token.type.isKeyword, false);
+ expect(token.offset, 0);
+ expect(token.length, source.length);
+ expect(token.lexeme, source);
+ token = _scan(" $source ", configuration: configuration);
+ expect(token, isNotNull);
+ expect(token.type.isKeyword, false);
+ expect(token.offset, 1);
+ expect(token.length, source.length);
+ expect(token.lexeme, source);
+ expect(token.next.type, TokenType.EOF);
+ }
+
+ /**
* Assert that the token scanned from the given [source] has the
* [expectedType].
*/
@@ -1399,9 +1452,11 @@
expect(token.type, TokenType.EOF);
}
- Token _scan(String source, {bool ignoreErrors: false}) {
+ Token _scan(String source,
+ {ScannerConfiguration configuration, bool ignoreErrors: false}) {
ErrorListener listener = new ErrorListener();
- Token token = scanWithListener(source, listener);
+ Token token =
+ scanWithListener(source, listener, configuration: configuration);
if (!ignoreErrors) {
listener.assertNoErrors();
}
diff --git a/pkg/front_end/test/token_test.dart b/pkg/front_end/test/token_test.dart
index fb9f1ae..fa62c06 100644
--- a/pkg/front_end/test/token_test.dart
+++ b/pkg/front_end/test/token_test.dart
@@ -91,6 +91,7 @@
Keyword.DEFERRED,
Keyword.DYNAMIC,
Keyword.EXPORT,
+ Keyword.EXTENSION,
Keyword.EXTERNAL,
Keyword.FACTORY,
Keyword.GET,
@@ -142,6 +143,7 @@
Keyword.CLASS,
Keyword.ENUM,
Keyword.EXPORT,
+ //Keyword.EXTENSION, <-- when "extension methods" is enabled by default
Keyword.IMPORT,
Keyword.LIBRARY,
Keyword.MIXIN,