Version 2.16.0-36.0.dev

Merge commit '6f59892b82f31c73fd866338e8c5dad62183e224' into 'dev'
diff --git a/pkg/front_end/analysis_options_no_lints.yaml b/pkg/front_end/analysis_options_no_lints.yaml
index d699e16..aca6b61 100644
--- a/pkg/front_end/analysis_options_no_lints.yaml
+++ b/pkg/front_end/analysis_options_no_lints.yaml
@@ -11,6 +11,7 @@
     - test/extensions/data/**
     - test/id_testing/data/**
     - test/language_versioning/data/**
+    - test/macros/data/**
     - test/patching/data/**
     - test/predicates/data/**
     - test/static_types/data/**
diff --git a/pkg/front_end/lib/src/testing/id_testing_helper.dart b/pkg/front_end/lib/src/testing/id_testing_helper.dart
index 2a8c8c8..77a6a44 100644
--- a/pkg/front_end/lib/src/testing/id_testing_helper.dart
+++ b/pkg/front_end/lib/src/testing/id_testing_helper.dart
@@ -52,6 +52,7 @@
   final Map<ExperimentalFlag, bool> explicitExperimentalFlags;
   final AllowedExperimentalFlags? allowedExperimentalFlags;
   final Uri? librariesSpecificationUri;
+  final Uri? packageConfigUri;
   // TODO(johnniwinther): Tailor support to redefine selected platform
   // classes/members only.
   final bool compileSdk;
@@ -62,6 +63,7 @@
       {this.explicitExperimentalFlags = const {},
       this.allowedExperimentalFlags,
       this.librariesSpecificationUri,
+      this.packageConfigUri,
       this.compileSdk: false,
       this.targetFlags: const TestTargetFlags(),
       this.nnbdMode: NnbdMode.Weak});
@@ -322,6 +324,7 @@
       options.compileSdk = config.compileSdk;
     }
   }
+  options.packagesFileUri = config.packageConfigUri;
   config.customizeCompilerOptions(options, testData);
   InternalCompilerResult compilerResult = await compileScript(
       testData.memorySourceFiles,
diff --git a/pkg/front_end/test/fasta/parser/literal_entry_info_test.dart b/pkg/front_end/test/fasta/parser/literal_entry_info_test.dart
index c282670..7e5d448 100644
--- a/pkg/front_end/test/fasta/parser/literal_entry_info_test.dart
+++ b/pkg/front_end/test/fasta/parser/literal_entry_info_test.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.9
-
 import 'package:_fe_analyzer_shared/src/parser/parser.dart';
 import 'package:_fe_analyzer_shared/src/parser/async_modifier.dart';
 import 'package:_fe_analyzer_shared/src/scanner/scanner.dart';
@@ -540,7 +538,7 @@
   }
 
   void parseEntry(String source, List<String> expectedCalls,
-      {bool inAsync, List<ExpectedError> errors, String expectAfter}) {
+      {bool? inAsync, List<ExpectedError>? errors, String? expectAfter}) {
     final start = scanString(source).tokens;
     final listener = new TestInfoListener();
     final parser = new Parser(listener,
@@ -556,9 +554,9 @@
       throw e;
     }
     if (expectAfter != null) {
-      expect(lastConsumed.next.lexeme, expectAfter);
+      expect(lastConsumed.next!.lexeme, expectAfter);
     } else {
-      expect(lastConsumed.next.isEof, isTrue, reason: lastConsumed.lexeme);
+      expect(lastConsumed.next!.isEof, isTrue, reason: lastConsumed.lexeme);
     }
   }
 }
@@ -838,7 +836,7 @@
   }
 
   void parseEntry(String source, List<String> expectedCalls,
-      {bool inAsync, List<ExpectedError> errors, String expectAfter}) {
+      {bool? inAsync, List<ExpectedError>? errors, String? expectAfter}) {
     final start = scanString(source).tokens;
     final listener = new TestInfoListener();
     final parser = new Parser(listener,
@@ -854,16 +852,16 @@
       throw e;
     }
     if (expectAfter != null) {
-      expect(lastConsumed.next.lexeme, expectAfter);
+      expect(lastConsumed.next!.lexeme, expectAfter);
     } else {
-      expect(lastConsumed.next.isEof, isTrue, reason: lastConsumed.lexeme);
+      expect(lastConsumed.next!.isEof, isTrue, reason: lastConsumed.lexeme);
     }
   }
 }
 
 class TestInfoListener implements Listener {
   List<String> calls = <String>[];
-  List<ExpectedError> errors;
+  List<ExpectedError>? errors;
 
   @override
   void beginBinaryExpression(Token token) {
@@ -876,7 +874,7 @@
   }
 
   @override
-  void beginForControlFlow(Token awaitToken, Token forToken) {
+  void beginForControlFlow(Token? awaitToken, Token forToken) {
     calls.add('beginForControlFlow $awaitToken $forToken');
   }
 
@@ -907,7 +905,7 @@
 
   @override
   void beginVariablesDeclaration(
-      Token token, Token lateToken, Token varFinalOrConst) {
+      Token token, Token? lateToken, Token? varFinalOrConst) {
     // TODO(danrubel): update to include lateToken
     calls.add('beginVariablesDeclaration $token $varFinalOrConst');
   }
@@ -963,7 +961,7 @@
   }
 
   @override
-  void endVariablesDeclaration(int count, Token endToken) {
+  void endVariablesDeclaration(int count, Token? endToken) {
     calls.add('endVariablesDeclaration $count $endToken');
   }
 
@@ -998,7 +996,7 @@
   }
 
   @override
-  void handleForInLoopParts(Token awaitToken, Token forToken,
+  void handleForInLoopParts(Token? awaitToken, Token forToken,
       Token leftParenthesis, Token inKeyword) {
     calls.add('handleForInLoopParts '
         '$awaitToken $forToken $leftParenthesis $inKeyword');
@@ -1028,7 +1026,7 @@
 
   @override
   void handleLiteralList(
-      int count, Token leftBracket, Token constKeyword, Token rightBracket) {
+      int count, Token leftBracket, Token? constKeyword, Token rightBracket) {
     calls.add(
         'handleLiteralList $count, $leftBracket, $constKeyword, $rightBracket');
   }
@@ -1037,7 +1035,7 @@
   void handleLiteralSetOrMap(
     int count,
     Token leftBrace,
-    Token constKeyword,
+    Token? constKeyword,
     Token rightBrace,
     // TODO(danrubel): hasSetEntry parameter exists for replicating existing
     // behavior and will be removed once unified collection has been enabled
@@ -1080,9 +1078,9 @@
   @override
   void handleRecoverableError(
       Message message, Token startToken, Token endToken) {
-    errors ??= <ExpectedError>[];
     int offset = startToken.charOffset;
-    errors.add(error(message.code, offset, endToken.charEnd - offset));
+    (errors ??= <ExpectedError>[])
+        .add(error(message.code, offset, endToken.charEnd - offset));
   }
 
   @override
diff --git a/pkg/front_end/test/fasta/parser/token_stream_rewriter_test.dart b/pkg/front_end/test/fasta/parser/token_stream_rewriter_test.dart
index 24d01ca..6828c746 100644
--- a/pkg/front_end/test/fasta/parser/token_stream_rewriter_test.dart
+++ b/pkg/front_end/test/fasta/parser/token_stream_rewriter_test.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.9
-
 import 'package:_fe_analyzer_shared/src/parser/token_stream_rewriter.dart';
 import 'package:_fe_analyzer_shared/src/scanner/scanner.dart'
     show ScannerResult, scanString;
@@ -39,7 +37,7 @@
 
     TokenStreamRewriter rewriter = getTokenStreamRewriter();
     Token openParen = rewriter.insertParens(a, false);
-    Token closeParen = openParen.next;
+    Token closeParen = openParen.next!;
 
     expect(openParen.lexeme, '(');
     expect(closeParen.lexeme, ')');
@@ -64,8 +62,8 @@
 
     TokenStreamRewriter rewriter = getTokenStreamRewriter();
     Token openParen = rewriter.insertParens(a, true);
-    Token identifier = openParen.next;
-    Token closeParen = identifier.next;
+    Token identifier = openParen.next!;
+    Token closeParen = identifier.next!;
 
     expect(openParen.lexeme, '(');
     expect(identifier.lexeme, '');
@@ -180,7 +178,7 @@
 
     expect(a.next, same(replacement));
     expect(replacement.next, same(c));
-    expect(c.next.isEof, true);
+    expect(c.next!.isEof, true);
 
     normalTestDone(rewriter, a);
   }
@@ -202,7 +200,7 @@
     expect(replacement.replacedToken, same(b));
 
     expect(a.next, same(replacement));
-    expect(replacement.next.isEof, true);
+    expect(replacement.next!.isEof, true);
 
     normalTestDone(rewriter, a);
   }
@@ -226,11 +224,11 @@
     expect(b.precedingComments, same(replacement.precedingComments));
     expect(replacement.replacedToken, same(b));
     expect(replacement.replacedToken.next, same(c));
-    expect(replacement.replacedToken.next.next, same(d));
+    expect(replacement.replacedToken.next!.next, same(d));
 
     expect(a.next, same(replacement));
     expect(replacement.next, same(e));
-    expect(e.next.isEof, true);
+    expect(e.next!.isEof, true);
 
     normalTestDone(rewriter, a);
   }
@@ -254,7 +252,7 @@
     expect(replacement.replacedToken.next, same(c));
 
     expect(a.next, same(replacement));
-    expect(replacement.next.isEof, true);
+    expect(replacement.next!.isEof, true);
 
     normalTestDone(rewriter, a);
   }
@@ -265,18 +263,18 @@
     Token firstToken = scanResult.tokens;
     setupDone(firstToken);
 
-    Token open = scanResult.tokens.next.next;
+    Token open = scanResult.tokens.next!.next!;
     expect(open.lexeme, '(');
-    Token close = open.endGroup;
+    Token close = open.endGroup!;
     expect(close.isSynthetic, isTrue);
-    expect(close.next.isEof, isTrue);
+    expect(close.next!.isEof, isTrue);
     TokenStreamRewriter rewriter = getTokenStreamRewriter();
 
-    Token result = rewriter.moveSynthetic(open.next, close);
+    Token result = rewriter.moveSynthetic(open.next!, close);
     expect(result, close);
     expect(open.endGroup, close);
-    expect(open.next.next, close);
-    expect(close.next.isEof, isFalse);
+    expect(open.next!.next, close);
+    expect(close.next!.isEof, isFalse);
 
     normalTestDone(rewriter, firstToken);
   }
@@ -340,7 +338,7 @@
   }
 
   StringToken _makeToken(int charOffset, String text) {
-    return new StringToken.fromString(null, text, charOffset);
+    return new StringToken.fromString(TokenType.IDENTIFIER, text, charOffset);
   }
 }
 
@@ -384,34 +382,35 @@
   TokenStreamRewriter getTokenStreamRewriter() =>
       new UndoableTokenStreamRewriter();
 
-  List<CachedTokenSetup> setup;
+  List<CachedTokenSetup>? setup;
 
   @override
   void setupDone(Token first) {
     setup = [];
-    Token token = first;
+    Token? token = first;
     while (token != null && !token.isEof) {
-      setup.add(new CachedTokenSetup(token));
+      setup!.add(new CachedTokenSetup(token));
       token = token.next;
     }
   }
 
   @override
   void normalTestDone(TokenStreamRewriter rewriter, Token first) {
-    UndoableTokenStreamRewriter undoableTokenStreamRewriter = rewriter;
+    UndoableTokenStreamRewriter undoableTokenStreamRewriter =
+        rewriter as UndoableTokenStreamRewriter;
     undoableTokenStreamRewriter.undo();
     List<CachedTokenSetup> now = [];
-    Token token = first;
+    Token? token = first;
     while (token != null && !token.isEof) {
       now.add(new CachedTokenSetup(token));
       token = token.next;
     }
-    if (setup.length != now.length) {
-      throw "Different length: ${setup.length} vs ${now.length}";
+    if (setup!.length != now.length) {
+      throw "Different length: ${setup!.length} vs ${now.length}";
     }
-    for (int i = 0; i < setup.length; i++) {
-      if (setup[i] != now[i]) {
-        throw "Different at $i: ${setup[i]} vs ${now[i]}";
+    for (int i = 0; i < setup!.length; i++) {
+      if (setup![i] != now[i]) {
+        throw "Different at $i: ${setup![i]} vs ${now[i]}";
       }
     }
     setup = null;
@@ -420,9 +419,9 @@
 
 class CachedTokenSetup {
   final Token token;
-  final Token prev;
-  final Token next;
-  final Token precedingComments;
+  final Token? prev;
+  final Token? next;
+  final Token? precedingComments;
 
   CachedTokenSetup(this.token)
       : prev = token.previous,
diff --git a/pkg/front_end/test/fasta/tool_git_test.dart b/pkg/front_end/test/fasta/tool_git_test.dart
index 3755f18..6e4b231 100644
--- a/pkg/front_end/test/fasta/tool_git_test.dart
+++ b/pkg/front_end/test/fasta/tool_git_test.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.9
-
 /// Tests the tool `pkg/front_end/tool/fasta`.
 
 import "dart:io";
diff --git a/pkg/front_end/test/fasta/uri_translator_test.dart b/pkg/front_end/test/fasta/uri_translator_test.dart
index 37e3564..806a053 100644
--- a/pkg/front_end/test/fasta/uri_translator_test.dart
+++ b/pkg/front_end/test/fasta/uri_translator_test.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart = 2.9
-
 import 'package:_fe_analyzer_shared/src/util/libraries_specification.dart';
 import 'package:front_end/src/fasta/uri_translator.dart';
 import 'package:package_config/package_config.dart';
diff --git a/pkg/front_end/test/macros/data/package_config.json b/pkg/front_end/test/macros/data/package_config.json
new file mode 100644
index 0000000..97031ce
--- /dev/null
+++ b/pkg/front_end/test/macros/data/package_config.json
@@ -0,0 +1,13 @@
+{
+  "configVersion": 2,
+  "packages": [
+    {
+      "name": "macro",
+      "rootUri": "pkgs/macro/lib/"
+    },
+    {
+      "name": "macro_builder",
+      "rootUri": "pkgs/macro_builder/lib/"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/pkg/front_end/test/macros/data/pkgs/macro/lib/macro.dart b/pkg/front_end/test/macros/data/pkgs/macro/lib/macro.dart
new file mode 100644
index 0000000..bfe0285
--- /dev/null
+++ b/pkg/front_end/test/macros/data/pkgs/macro/lib/macro.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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:macro_builder/macro_builder.dart';
+
+class Macro1 implements Macro {
+  const Macro1();
+}
+
+class Macro2 implements Macro {
+  const Macro2();
+}
+
+class Macro3 implements Macro {
+  const Macro3();
+}
+
+class NonMacro {
+  const NonMacro();
+}
diff --git a/pkg/front_end/test/macros/data/pkgs/macro_builder/lib/macro_builder.dart b/pkg/front_end/test/macros/data/pkgs/macro_builder/lib/macro_builder.dart
new file mode 100644
index 0000000..14354da
--- /dev/null
+++ b/pkg/front_end/test/macros/data/pkgs/macro_builder/lib/macro_builder.dart
@@ -0,0 +1,5 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+export 'src/macro.dart';
diff --git a/pkg/front_end/test/macros/data/pkgs/macro_builder/lib/src/macro.dart b/pkg/front_end/test/macros/data/pkgs/macro_builder/lib/src/macro.dart
new file mode 100644
index 0000000..434331b
--- /dev/null
+++ b/pkg/front_end/test/macros/data/pkgs/macro_builder/lib/src/macro.dart
@@ -0,0 +1,5 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+class Macro {}
diff --git a/pkg/front_end/test/macros/data/tests/declare_macro.dart b/pkg/front_end/test/macros/data/tests/declare_macro.dart
new file mode 100644
index 0000000..58edc63
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/declare_macro.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+/*library: 
+ declaredMacros=[MyMacro],
+ macrosAreAvailable
+*/
+
+import 'package:macro_builder/macro_builder.dart';
+
+class MyMacro implements Macro {}
+
+void main() {}
diff --git a/pkg/front_end/test/macros/data/tests/direct_import.dart b/pkg/front_end/test/macros/data/tests/direct_import.dart
new file mode 100644
index 0000000..b2f6177
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/direct_import.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+/*library: macrosAreAvailable*/
+
+// ignore: unused_import
+import 'package:macro_builder/src/macro.dart';
+
+void main() {}
diff --git a/pkg/front_end/test/macros/data/tests/import_macro_builder.dart b/pkg/front_end/test/macros/data/tests/import_macro_builder.dart
new file mode 100644
index 0000000..14e5966c
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/import_macro_builder.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+/*library: macrosAreAvailable*/
+
+// ignore: unused_import
+import 'package:macro_builder/macro_builder.dart';
+
+void main() {}
diff --git a/pkg/front_end/test/macros/data/tests/import_macro_package.dart b/pkg/front_end/test/macros/data/tests/import_macro_package.dart
new file mode 100644
index 0000000..5e69df3
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/import_macro_package.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+/*library: macrosAreAvailable*/
+
+// ignore: unused_import
+import 'package:macro/macro.dart';
+
+void main() {}
diff --git a/pkg/front_end/test/macros/data/tests/import_macro_source/macro_lib.dart b/pkg/front_end/test/macros/data/tests/import_macro_source/macro_lib.dart
new file mode 100644
index 0000000..a05f387
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/import_macro_source/macro_lib.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+/*library: 
+ declaredMacros=[Macro1],
+ macrosAreAvailable
+*/
+
+import 'package:macro_builder/macro_builder.dart';
+
+class Macro1 implements Macro {
+  const Macro1();
+}
diff --git a/pkg/front_end/test/macros/data/tests/import_macro_source/main.dart b/pkg/front_end/test/macros/data/tests/import_macro_source/main.dart
new file mode 100644
index 0000000..625315b
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/import_macro_source/main.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+/*library: macrosAreAvailable*/
+
+// ignore: unused_import
+import 'macro_lib.dart';
+
+void main() {}
diff --git a/pkg/front_end/test/macros/data/tests/marker.options b/pkg/front_end/test/macros/data/tests/marker.options
new file mode 100644
index 0000000..8416ef5
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/marker.options
@@ -0,0 +1 @@
+cfe=pkg/front_end/test/macros/macro_test.dart
diff --git a/pkg/front_end/test/macros/data/tests/no_import.dart b/pkg/front_end/test/macros/data/tests/no_import.dart
new file mode 100644
index 0000000..e5c2fad
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/no_import.dart
@@ -0,0 +1,5 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+void main() {}
diff --git a/pkg/front_end/test/macros/data/tests/use_macro_package.dart b/pkg/front_end/test/macros/data/tests/use_macro_package.dart
new file mode 100644
index 0000000..09c2b3b
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/use_macro_package.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+/*library: 
+ appliedMacros=[Macro3],
+ macrosAreApplied,
+ macrosAreAvailable
+*/
+@Macro3()
+library use_macro_package;
+
+import 'package:macro/macro.dart';
+
+@Macro1()
+/*member: main:appliedMacros=[Macro1]*/
+void main() {}
+
+@Macro2()
+/*class: Class1:
+ appliedMacros=[Macro2],
+ macrosAreApplied
+*/
+class Class1 {
+  @Macro1()
+  @Macro2()
+  /*member: Class1.method:appliedMacros=[
+    Macro1,
+    Macro2]*/
+  void method() {}
+}
+
+@NonMacro()
+class Class2 {}
+
+/*class: Class3:macrosAreApplied*/
+class Class3 {
+  @Macro3()
+  /*member: Class3.field:appliedMacros=[Macro3]*/
+  var field;
+}
+
+class Class4 {
+  @NonMacro()
+  var field;
+}
diff --git a/pkg/front_end/test/macros/data/tests/use_macro_source/macro_lib.dart b/pkg/front_end/test/macros/data/tests/use_macro_source/macro_lib.dart
new file mode 100644
index 0000000..3f926a8
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/use_macro_source/macro_lib.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+/*library: 
+ declaredMacros=[
+  Macro1,
+  Macro2],
+ macrosAreAvailable
+*/
+
+import 'package:macro_builder/macro_builder.dart';
+
+class Macro1 implements Macro {
+  const Macro1();
+}
+
+class Macro2 implements Macro {
+  const Macro2();
+}
+
+class NonMacro {
+  const NonMacro();
+}
diff --git a/pkg/front_end/test/macros/data/tests/use_macro_source/main.dart b/pkg/front_end/test/macros/data/tests/use_macro_source/main.dart
new file mode 100644
index 0000000..f5a3e60
--- /dev/null
+++ b/pkg/front_end/test/macros/data/tests/use_macro_source/main.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+/*library: 
+ macrosAreApplied,
+ macrosAreAvailable
+*/
+
+import 'macro_lib.dart';
+
+@Macro1()
+/*member: main:appliedMacros=[Macro1]*/
+void main() {}
+
+@Macro2()
+/*class: Class1:
+ appliedMacros=[Macro2],
+ macrosAreApplied
+*/
+class Class1 {
+  @Macro1()
+  @Macro2()
+  /*member: Class1.method:appliedMacros=[
+    Macro1,
+    Macro2]*/
+  void method() {}
+}
+
+@NonMacro()
+class Class2 {}
+
+/*class: Class3:macrosAreApplied*/
+class Class3 {
+  @Macro1()
+  /*member: Class3.field:appliedMacros=[Macro1]*/
+  var field;
+}
+
+class Class4 {
+  @NonMacro()
+  var field;
+}
diff --git a/pkg/front_end/test/macros/macro_test.dart b/pkg/front_end/test/macros/macro_test.dart
new file mode 100644
index 0000000..98f0000
--- /dev/null
+++ b/pkg/front_end/test/macros/macro_test.dart
@@ -0,0 +1,291 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// 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:io' show Directory, Platform;
+
+import 'package:_fe_analyzer_shared/src/testing/id.dart' show ActualData, Id;
+import 'package:_fe_analyzer_shared/src/testing/id_testing.dart';
+
+import 'package:_fe_analyzer_shared/src/testing/features.dart';
+import 'package:front_end/src/testing/id_testing_helper.dart';
+import 'package:kernel/ast.dart';
+import 'package:kernel/class_hierarchy.dart';
+import 'package:kernel/core_types.dart';
+
+Future<void> main(List<String> args) async {
+  Directory dataDir =
+      new Directory.fromUri(Platform.script.resolve('data/tests'));
+  await runTests<Features>(dataDir,
+      args: args,
+      createUriForFileName: createUriForFileName,
+      onFailure: onFailure,
+      runTest: runTestFor(const MacroDataComputer(), [
+        new TestConfig(cfeMarker, 'cfe',
+            packageConfigUri:
+                Platform.script.resolve('data/package_config.json'))
+      ]));
+}
+
+final Uri macroLibraryUri = Uri.parse('package:macro_builder/src/macro.dart');
+const String macroClassName = 'Macro';
+
+class MacroDeclarationData {
+  Class? macroClass;
+  Map<Library, List<Class>> macroDeclarations = {};
+  Set<Class> macroClasses = {};
+}
+
+MacroDeclarationData computeMacroDeclarationData(
+    Component component, ClassHierarchy classHierarchy) {
+  MacroDeclarationData data = new MacroDeclarationData();
+  Class? macroClass;
+  outer:
+  for (Library library in component.libraries) {
+    if (library.importUri == macroLibraryUri) {
+      for (Class cls in library.classes) {
+        if (cls.name == macroClassName) {
+          macroClass = cls;
+          break outer;
+        }
+      }
+    }
+  }
+  if (macroClass != null) {
+    data.macroClass = macroClass;
+    for (Library library in component.libraries) {
+      for (Class cls in library.classes) {
+        if (classHierarchy.isSubtypeOf(cls, macroClass)) {
+          (data.macroDeclarations[library] ??= []).add(cls);
+          data.macroClasses.add(cls);
+        }
+      }
+    }
+  }
+  return data;
+}
+
+class MacroApplications {
+  List<Class> macros = [];
+}
+
+class LibraryMacroApplicationData {
+  MacroApplications? libraryApplications;
+  Map<Class, ClassMacroApplicationData> classData = {};
+  Map<Typedef, MacroApplications> typedefApplications = {};
+  Map<Member, MacroApplications> memberApplications = {};
+}
+
+class ClassMacroApplicationData {
+  MacroApplications? classApplications;
+  Map<Member, MacroApplications> memberApplications = {};
+}
+
+MacroApplications? computeMacroApplications(
+    List<Expression> annotations, MacroDeclarationData macroDeclarationData) {
+  MacroApplications applications = new MacroApplications();
+  bool hasApplications = false;
+  for (Expression annotation in annotations) {
+    if (annotation is ConstantExpression) {
+      Constant constant = annotation.constant;
+      if (constant is InstanceConstant) {
+        if (macroDeclarationData.macroClasses.contains(constant.classNode)) {
+          applications.macros.add(constant.classNode);
+          hasApplications = true;
+        }
+      }
+    }
+  }
+  return hasApplications ? applications : null;
+}
+
+ClassMacroApplicationData? computeClassMacroApplicationData(
+    Class cls, MacroDeclarationData macroDeclarationData) {
+  ClassMacroApplicationData data = new ClassMacroApplicationData();
+  data.classApplications =
+      computeMacroApplications(cls.annotations, macroDeclarationData);
+  for (Member member in cls.members) {
+    MacroApplications? macroApplications =
+        computeMacroApplications(member.annotations, macroDeclarationData);
+    if (macroApplications != null) {
+      data.memberApplications[member] = macroApplications;
+    }
+  }
+  return data.classApplications != null || data.memberApplications.isNotEmpty
+      ? data
+      : null;
+}
+
+LibraryMacroApplicationData? computeLibraryMacroApplicationData(
+    Library library, MacroDeclarationData macroDeclarationData) {
+  LibraryMacroApplicationData data = new LibraryMacroApplicationData();
+  data.libraryApplications =
+      computeMacroApplications(library.annotations, macroDeclarationData);
+  for (Typedef typedef in library.typedefs) {
+    MacroApplications? macroApplications =
+        computeMacroApplications(typedef.annotations, macroDeclarationData);
+    if (macroApplications != null) {
+      data.typedefApplications[typedef] = macroApplications;
+    }
+  }
+  for (Member member in library.members) {
+    MacroApplications? macroApplications =
+        computeMacroApplications(member.annotations, macroDeclarationData);
+    if (macroApplications != null) {
+      data.memberApplications[member] = macroApplications;
+    }
+  }
+  for (Class cls in library.classes) {
+    ClassMacroApplicationData? classMacroApplicationData =
+        computeClassMacroApplicationData(cls, macroDeclarationData);
+    if (classMacroApplicationData != null) {
+      data.classData[cls] = classMacroApplicationData;
+    }
+  }
+  return data.libraryApplications != null ||
+          data.classData.isNotEmpty ||
+          data.memberApplications.isNotEmpty ||
+          data.typedefApplications.isNotEmpty
+      ? data
+      : null;
+}
+
+class MacroDataComputer extends DataComputer<Features> {
+  const MacroDataComputer();
+
+  @override
+  void computeMemberData(
+      TestConfig config,
+      InternalCompilerResult compilerResult,
+      Member member,
+      Map<Id, ActualData<Features>> actualMap,
+      {bool? verbose}) {
+    member.accept(new MacroDataExtractor(compilerResult, actualMap));
+  }
+
+  @override
+  void computeClassData(
+      TestConfig config,
+      InternalCompilerResult compilerResult,
+      Class cls,
+      Map<Id, ActualData<Features>> actualMap,
+      {bool? verbose}) {
+    new MacroDataExtractor(compilerResult, actualMap).computeForClass(cls);
+  }
+
+  @override
+  void computeLibraryData(
+      TestConfig config,
+      InternalCompilerResult compilerResult,
+      Library library,
+      Map<Id, ActualData<Features>> actualMap,
+      {bool? verbose}) {
+    new MacroDataExtractor(compilerResult, actualMap)
+        .computeForLibrary(library);
+  }
+
+  @override
+  DataInterpreter<Features> get dataValidator =>
+      const FeaturesDataInterpreter();
+}
+
+class Tags {
+  static const String macrosAreAvailable = 'macrosAreAvailable';
+  static const String macrosAreApplied = 'macrosAreApplied';
+  static const String declaredMacros = 'declaredMacros';
+  static const String appliedMacros = 'appliedMacros';
+}
+
+class MacroDataExtractor extends CfeDataExtractor<Features> {
+  late final ClassHierarchy classHierarchy;
+  late final MacroDeclarationData macroDeclarationData;
+
+  MacroDataExtractor(InternalCompilerResult compilerResult,
+      Map<Id, ActualData<Features>> actualMap)
+      : super(compilerResult, actualMap) {
+    // TODO(johnniwinther): Why isn't `_UserTag` available in the
+    // [ClassHierarchy] provided by the [compilerResult]?
+    classHierarchy = new ClassHierarchy(
+        compilerResult.component!, new CoreTypes(compilerResult.component!));
+    macroDeclarationData =
+        computeMacroDeclarationData(compilerResult.component!, classHierarchy);
+  }
+
+  LibraryMacroApplicationData? getLibraryMacroApplicationData(Library library) {
+    return computeLibraryMacroApplicationData(library, macroDeclarationData);
+  }
+
+  MacroApplications? getLibraryMacroApplications(Library library) {
+    return getLibraryMacroApplicationData(library)?.libraryApplications;
+  }
+
+  ClassMacroApplicationData? getClassMacroApplicationData(Class cls) {
+    LibraryMacroApplicationData? applicationData =
+        computeLibraryMacroApplicationData(
+            cls.enclosingLibrary, macroDeclarationData);
+    if (applicationData != null) {
+      return applicationData.classData[cls];
+    }
+    return null;
+  }
+
+  MacroApplications? getClassMacroApplications(Class cls) {
+    return getClassMacroApplicationData(cls)?.classApplications;
+  }
+
+  MacroApplications? getMemberMacroApplications(Member member) {
+    Class? enclosingClass = member.enclosingClass;
+    if (enclosingClass != null) {
+      return getClassMacroApplicationData(enclosingClass)
+          ?.memberApplications[member];
+    } else {
+      return getLibraryMacroApplicationData(member.enclosingLibrary)
+          ?.memberApplications[member];
+    }
+  }
+
+  void registerMacroApplications(
+      Features features, MacroApplications? macroApplications) {
+    if (macroApplications != null) {
+      for (Class cls in macroApplications.macros) {
+        features.addElement(Tags.appliedMacros, cls.name);
+      }
+    }
+  }
+
+  @override
+  Features computeLibraryValue(Id id, Library node) {
+    Features features = new Features();
+    if (macroDeclarationData.macroClass != null) {
+      features.add(Tags.macrosAreAvailable);
+    }
+    List<Class>? macroClasses = macroDeclarationData.macroDeclarations[node];
+    if (macroClasses != null) {
+      for (Class cls in macroClasses) {
+        features.addElement(Tags.declaredMacros, cls.name);
+      }
+    }
+    if (getLibraryMacroApplicationData(node) != null) {
+      features.add(Tags.macrosAreApplied);
+    }
+    registerMacroApplications(features, getLibraryMacroApplications(node));
+    return features;
+  }
+
+  @override
+  Features computeClassValue(Id id, Class node) {
+    Features features = new Features();
+    if (getClassMacroApplicationData(node) != null) {
+      features.add(Tags.macrosAreApplied);
+    }
+    registerMacroApplications(features, getClassMacroApplications(node));
+    return features;
+  }
+
+  @override
+  Features computeMemberValue(Id id, Member node) {
+    Features features = new Features();
+    registerMacroApplications(features, getMemberMacroApplications(node));
+    return features;
+  }
+}
diff --git a/pkg/front_end/test/scanner_fasta_test.dart b/pkg/front_end/test/scanner_fasta_test.dart
index b5c55b5..215423e 100644
--- a/pkg/front_end/test/scanner_fasta_test.dart
+++ b/pkg/front_end/test/scanner_fasta_test.dart
@@ -2,8 +2,6 @@
 // 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.
 
-// @dart=2.9
-
 import 'dart:convert';
 
 import 'package:_fe_analyzer_shared/src/scanner/error_token.dart' as fasta;
@@ -38,16 +36,16 @@
 
     usedForFuzzTesting.ScannerResult result =
         usedForFuzzTesting.scanString(source);
-    expect(result?.hasErrors, isFalse);
-    expect(result.tokens?.type, same(Keyword.CLASS));
+    expect(result.hasErrors, isFalse);
+    expect(result.tokens.type, same(Keyword.CLASS));
 
     // UTF8 encode source with trailing zero
     List<int> bytes = utf8.encode(source).toList();
     bytes.add(0);
 
     result = usedForFuzzTesting.scan(bytes);
-    expect(result?.hasErrors, isFalse);
-    expect(result.tokens?.type, same(Keyword.CLASS));
+    expect(result.hasErrors, isFalse);
+    expect(result.tokens.type, same(Keyword.CLASS));
   }
 }
 
@@ -55,7 +53,7 @@
 class ScannerTest_Fasta_UTF8 extends ScannerTest_Fasta {
   @override
   Token scanWithListener(String source, ErrorListener listener,
-      {ScannerConfiguration configuration}) {
+      {ScannerConfiguration? configuration}) {
     var bytes = utf8.encode(source).toList()..add(0);
     var result =
         scan(bytes, configuration: configuration, includeComments: true);
@@ -65,10 +63,10 @@
     if (result.hasErrors) {
       while (token is ErrorToken) {
         translateErrorToken(token,
-            (ScannerErrorCode errorCode, int offset, List<Object> arguments) {
+            (ScannerErrorCode errorCode, int offset, List<Object>? arguments) {
           listener.errors.add(new TestError(offset, errorCode, arguments));
         });
-        token = token.next;
+        token = token.next!;
       }
     }
 
@@ -113,7 +111,7 @@
 class ScannerTest_Fasta extends ScannerTestBase {
   @override
   Token scanWithListener(String source, ErrorListener listener,
-      {ScannerConfiguration configuration}) {
+      {ScannerConfiguration? configuration}) {
     var result =
         scanString(source, configuration: configuration, includeComments: true);
     var token = result.tokens;
@@ -122,10 +120,10 @@
     if (result.hasErrors) {
       while (token is ErrorToken) {
         translateErrorToken(token,
-            (ScannerErrorCode errorCode, int offset, List<Object> arguments) {
+            (ScannerErrorCode errorCode, int offset, List<Object>? arguments) {
           listener.errors.add(new TestError(offset, errorCode, arguments));
         });
-        token = token.next;
+        token = token.next!;
       }
     }
 
@@ -151,7 +149,7 @@
        } // EOF comment
     ''';
 
-    Token scanSource({bool includeComments}) {
+    Token scanSource({required bool includeComments}) {
       return scanString(source, includeComments: includeComments).tokens;
     }
 
@@ -162,7 +160,7 @@
       // Assert no comments
       expect(token.precedingComments, isNull);
       expect(token.type.kind, isNot(fasta.COMMENT_TOKEN));
-      token = token.next;
+      token = token.next!;
     }
     expect(token.precedingComments, isNull);
     expect(tokenCount, 26);
@@ -175,13 +173,13 @@
     while (!token.isEof) {
       ++tokenCount;
       // Assert valid comments
-      fasta.CommentToken comment = token.precedingComments;
+      fasta.CommentToken? comment = token.precedingComments;
       while (comment != null) {
         ++commentTokenCount;
         expect(comment.type.kind, fasta.COMMENT_TOKEN);
         expect(comment.charOffset, greaterThanOrEqualTo(previousEnd));
         previousEnd = comment.charOffset + comment.charCount;
-        comment = comment.next;
+        comment = comment.next as fasta.CommentToken?;
       }
       expect(token.type.kind, isNot(fasta.COMMENT_TOKEN));
       expect(token.charOffset, greaterThanOrEqualTo(previousEnd));
@@ -206,7 +204,7 @@
         }
       }
 
-      token = token.next;
+      token = token.next!;
     }
     expect(tokenCount, 26);
     expect(spotCheckCount, 3);
@@ -222,19 +220,19 @@
 main() {}
 ''';
 
-    Token token;
-    fasta.CommentToken c1;
-    fasta.CommentToken c2;
-    fasta.CommentToken c3;
+    late Token token;
+    late fasta.CommentToken c1;
+    late fasta.CommentToken c2;
+    late fasta.CommentToken c3;
 
     void prepareTokens() {
       token = scanString(code, includeComments: true).tokens;
 
       expect(token.type.kind, fasta.IDENTIFIER_TOKEN);
 
-      c1 = token.precedingComments;
-      c2 = c1.next;
-      c3 = c2.next;
+      c1 = token.precedingComments!;
+      c2 = c1.next as fasta.CommentToken;
+      c3 = c2.next as fasta.CommentToken;
       expect(c3.next, isNull);
 
       expect(c1.parent, token);
@@ -287,7 +285,7 @@
     // ... but the length does *not* include the additional character
     // so as to be true to the original source.
     expect(token.length, source.length);
-    expect(token.next.isEof, isTrue);
+    expect(token.next!.isEof, isTrue);
     expect(listener.errors, hasLength(1));
     TestError error = listener.errors[0];
     expect(error.errorCode, ScannerErrorCode.MISSING_DIGIT);
@@ -303,16 +301,16 @@
     // where both ')' are synthetic
     ErrorListener listener = new ErrorListener();
     BeginToken interpolationStart =
-        scanWithListener(r'"${({(}}"', listener).next;
-    BeginToken openParen1 = interpolationStart.next;
-    BeginToken openBrace = openParen1.next;
-    BeginToken openParen2 = openBrace.next;
-    var closeParen2 = openParen2.next;
-    var closeBrace = closeParen2.next;
-    var closeParen1 = closeBrace.next;
-    var interpolationEnd = closeParen1.next;
-    var stringEnd = interpolationEnd.next;
-    expect(stringEnd.next.type, TokenType.EOF);
+        scanWithListener(r'"${({(}}"', listener).next as BeginToken;
+    BeginToken openParen1 = interpolationStart.next as BeginToken;
+    BeginToken openBrace = openParen1.next as BeginToken;
+    BeginToken openParen2 = openBrace.next as BeginToken;
+    var closeParen2 = openParen2.next!;
+    var closeBrace = closeParen2.next!;
+    var closeParen1 = closeBrace.next!;
+    var interpolationEnd = closeParen1.next!;
+    var stringEnd = interpolationEnd.next!;
+    expect(stringEnd.next!.type, TokenType.EOF);
     expect(interpolationStart.endToken, same(interpolationEnd));
     expect(openParen1.endToken, same(closeParen1));
     expect(openBrace.endToken, same(closeBrace));
@@ -327,37 +325,37 @@
     const source = 'int a; /*1*/ /*2*/ /*3*/ B f(){if (a < 2) {}}';
     Token token = scanString(source, includeComments: true).tokens;
     while (!token.isEof) {
-      expect(token.next.previous, token);
-      fasta.CommentToken commentToken = token.precedingComments;
+      expect(token.next!.previous, token);
+      fasta.CommentToken? commentToken = token.precedingComments;
       while (commentToken != null) {
         if (commentToken.next != null) {
-          expect(commentToken.next.previous, commentToken);
+          expect(commentToken.next!.previous, commentToken);
         }
-        commentToken = commentToken.next;
+        commentToken = commentToken.next as fasta.CommentToken?;
       }
-      token = token.next;
+      token = token.next!;
     }
   }
 
   void test_spread_operators() {
     ErrorListener listener = new ErrorListener();
     Token openBracket = scanWithListener('[ 1, ...[2], ...?[3], ]', listener);
-    Token spreadToken = openBracket.next.next.next;
+    Token spreadToken = openBracket.next!.next!.next!;
     expect(spreadToken.lexeme, '...');
-    Token spreadQToken = spreadToken.next.next.next.next.next;
+    Token spreadQToken = spreadToken.next!.next!.next!.next!.next!;
     expect(spreadQToken.lexeme, '...?');
   }
 
   @override
   void test_unmatched_openers() {
     ErrorListener listener = new ErrorListener();
-    BeginToken openBrace = scanWithListener('{[(', listener);
-    BeginToken openBracket = openBrace.next;
-    BeginToken openParen = openBracket.next;
-    var closeParen = openParen.next;
-    var closeBracket = closeParen.next;
-    var closeBrace = closeBracket.next;
-    expect(closeBrace.next.type, TokenType.EOF);
+    BeginToken openBrace = scanWithListener('{[(', listener) as BeginToken;
+    BeginToken openBracket = openBrace.next as BeginToken;
+    BeginToken openParen = openBracket.next as BeginToken;
+    var closeParen = openParen.next!;
+    var closeBracket = closeParen.next!;
+    var closeBrace = closeBracket.next!;
+    expect(closeBrace.next!.type, TokenType.EOF);
     expect(openBrace.endToken, same(closeBrace));
     expect(openBracket.endToken, same(closeBracket));
     expect(openParen.endToken, same(closeParen));
@@ -374,7 +372,7 @@
   Token scan(String source);
 
   void expectToken(Token token, TokenType type, int offset, int length,
-      {bool isSynthetic: false, String lexeme}) {
+      {bool isSynthetic: false, String? lexeme}) {
     String description = '${token.type} $token';
     expect(token.type, type, reason: description);
     expect(token.offset, offset, reason: description);
@@ -390,26 +388,26 @@
     expect((token as fasta.ErrorToken).errorCode,
         same(codeUnexpectedDollarInString));
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 0, 1, lexeme: "'");
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING_INTERPOLATION_IDENTIFIER, 1, 1);
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.IDENTIFIER, 2, 1, lexeme: 'x');
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 3, 0, lexeme: '', isSynthetic: true);
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING_INTERPOLATION_IDENTIFIER, 3, 1);
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.IDENTIFIER, 4, 0,
         lexeme: '', isSynthetic: true);
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 4, 1, lexeme: "'");
   }
 
@@ -418,28 +416,28 @@
     expect((token as fasta.ErrorToken).errorCode, same(codeUnmatchedToken));
     var interpolationStartErrorToken = token as fasta.UnmatchedToken;
 
-    token = token.next;
+    token = token.next!;
     expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
     expect((token as fasta.UnterminatedString).start, '"');
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 0, 5, lexeme: '"foo ');
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING_INTERPOLATION_EXPRESSION, 5, 2);
-    BeginToken interpolationStart = token;
+    BeginToken interpolationStart = token as BeginToken;
     expect(interpolationStartErrorToken.begin, same(interpolationStart));
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.IDENTIFIER, 7, 3, lexeme: 'bar');
 
     // Expect interpolation to be terminated before string is closed
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.CLOSE_CURLY_BRACKET, 10, 0,
         isSynthetic: true, lexeme: '}');
     expect(interpolationStart.endToken, same(token));
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 10, 0, isSynthetic: true, lexeme: '"');
   }
 
@@ -448,58 +446,58 @@
     expect((token as fasta.ErrorToken).errorCode, same(codeUnmatchedToken));
     var openSquareBracketErrorToken = token as fasta.UnmatchedToken;
 
-    token = token.next;
+    token = token.next!;
     expect((token as fasta.ErrorToken).errorCode, same(codeUnmatchedToken));
     var openParenErrorToken = token as fasta.UnmatchedToken;
 
-    token = token.next;
+    token = token.next!;
     expect((token as fasta.ErrorToken).errorCode, same(codeUnmatchedToken));
     var interpolationStartErrorToken = token as fasta.UnmatchedToken;
 
-    token = token.next;
+    token = token.next!;
     expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
     expect((token as fasta.UnterminatedString).start, '"');
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 0, 5, lexeme: '"foo ');
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING_INTERPOLATION_EXPRESSION, 5, 2);
-    BeginToken interpolationStart = token;
+    BeginToken interpolationStart = token as BeginToken;
     expect(interpolationStartErrorToken.begin, same(interpolationStart));
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.IDENTIFIER, 7, 3, lexeme: 'bar');
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.OPEN_PAREN, 10, 1);
-    BeginToken openParen = token;
+    BeginToken openParen = token as BeginToken;
     expect(openParenErrorToken.begin, same(openParen));
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.IDENTIFIER, 11, 3, lexeme: 'baz');
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.OPEN_SQUARE_BRACKET, 14, 1);
-    BeginToken openSquareBracket = token;
+    BeginToken openSquareBracket = token as BeginToken;
     expect(openSquareBracketErrorToken.begin, same(openSquareBracket));
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.CLOSE_SQUARE_BRACKET, 15, 0,
         isSynthetic: true, lexeme: ']');
     expect(openSquareBracket.endToken, same(token));
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.CLOSE_PAREN, 15, 0,
         isSynthetic: true, lexeme: ')');
     expect(openParen.endToken, same(token));
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.CLOSE_CURLY_BRACKET, 15, 0,
         isSynthetic: true, lexeme: '}');
     expect(interpolationStart.endToken, same(token));
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 15, 0, isSynthetic: true, lexeme: '"');
   }
 
@@ -508,21 +506,21 @@
     expect((token as fasta.ErrorToken).errorCode,
         same(codeUnexpectedDollarInString));
 
-    token = token.next;
+    token = token.next!;
     expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
     expect((token as fasta.UnterminatedString).start, '"');
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 0, 5, lexeme: '"foo ');
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING_INTERPOLATION_IDENTIFIER, 5, 1);
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.IDENTIFIER, 6, 0,
         isSynthetic: true, lexeme: '');
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 6, 0, isSynthetic: true, lexeme: '"');
   }
 
@@ -531,7 +529,7 @@
     expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
     expect((token as fasta.UnterminatedString).start, "'''");
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 0, 9,
         lexeme: "'''string'''", isSynthetic: true);
   }
@@ -541,7 +539,7 @@
     expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
     expect((token as fasta.UnterminatedString).start, "r'''");
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 0, 10,
         lexeme: "r'''string'''", isSynthetic: true);
   }
@@ -551,7 +549,7 @@
     expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
     expect((token as fasta.UnterminatedString).start, "r'");
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 0, 8,
         lexeme: "r'string'", isSynthetic: true);
   }
@@ -561,7 +559,7 @@
     expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
     expect((token as fasta.UnterminatedString).start, "r'");
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 0, 8,
         lexeme: "r'string'", isSynthetic: true);
   }
@@ -571,7 +569,7 @@
     expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
     expect((token as fasta.UnterminatedString).start, "'");
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 0, 7,
         lexeme: "'string'", isSynthetic: true);
   }
@@ -581,17 +579,17 @@
     expect((token as fasta.ErrorToken).errorCode, same(codeUnterminatedString));
     expect((token as fasta.UnterminatedString).start, "'");
 
-    token = token.next;
+    token = token.next!;
     expectToken(token, TokenType.STRING, 0, 7,
         lexeme: "'string'", isSynthetic: true);
   }
 
   void test_match_angle_brackets() {
     var x = scan('x<y>');
-    BeginToken lessThan = x.next;
-    var y = lessThan.next;
-    var greaterThan = y.next;
-    expect(greaterThan.next.isEof, isTrue);
+    BeginToken lessThan = x.next as BeginToken;
+    var y = lessThan.next!;
+    var greaterThan = y.next!;
+    expect(greaterThan.next!.isEof, isTrue);
     expect(lessThan.endGroup, same(greaterThan));
   }
 
@@ -599,54 +597,54 @@
     // When a ">>" appears in the token stream, Fasta's scanner matches it to
     // the outer "<".  The inner "<" is left unmatched.
     var x = scan('x<y<z>>');
-    BeginToken lessThan1 = x.next;
-    var y = lessThan1.next;
-    BeginToken lessThan2 = y.next;
-    var z = lessThan2.next;
-    var greaterThans = z.next;
-    expect(greaterThans.next.isEof, isTrue);
+    BeginToken lessThan1 = x.next as BeginToken;
+    var y = lessThan1.next!;
+    BeginToken lessThan2 = y.next as BeginToken;
+    var z = lessThan2.next!;
+    var greaterThans = z.next!;
+    expect(greaterThans.next!.isEof, isTrue);
     expect(lessThan1.endGroup, same(greaterThans));
     expect(lessThan2.endGroup, isNull);
   }
 
   void test_match_angle_brackets_interrupted_by_close_brace() {
     // A "}" appearing in the token stream interrupts matching of "<" and ">".
-    BeginToken openBrace = scan('{x<y}>z');
-    var x = openBrace.next;
-    BeginToken lessThan = x.next;
-    var y = lessThan.next;
-    var closeBrace = y.next;
-    var greaterThan = closeBrace.next;
-    var z = greaterThan.next;
-    expect(z.next.isEof, isTrue);
+    BeginToken openBrace = scan('{x<y}>z') as BeginToken;
+    var x = openBrace.next!;
+    BeginToken lessThan = x.next as BeginToken;
+    var y = lessThan.next!;
+    var closeBrace = y.next!;
+    var greaterThan = closeBrace.next!;
+    var z = greaterThan.next!;
+    expect(z.next!.isEof, isTrue);
     expect(openBrace.endGroup, same(closeBrace));
     expect(lessThan.endGroup, isNull);
   }
 
   void test_match_angle_brackets_interrupted_by_close_bracket() {
     // A "]" appearing in the token stream interrupts matching of "<" and ">".
-    BeginToken openBracket = scan('[x<y]>z');
-    var x = openBracket.next;
-    BeginToken lessThan = x.next;
-    var y = lessThan.next;
-    var closeBracket = y.next;
-    var greaterThan = closeBracket.next;
-    var z = greaterThan.next;
-    expect(z.next.isEof, isTrue);
+    BeginToken openBracket = scan('[x<y]>z') as BeginToken;
+    var x = openBracket.next!;
+    BeginToken lessThan = x.next as BeginToken;
+    var y = lessThan.next!;
+    var closeBracket = y.next!;
+    var greaterThan = closeBracket.next!;
+    var z = greaterThan.next!;
+    expect(z.next!.isEof, isTrue);
     expect(openBracket.endGroup, same(closeBracket));
     expect(lessThan.endGroup, isNull);
   }
 
   void test_match_angle_brackets_interrupted_by_close_paren() {
     // A ")" appearing in the token stream interrupts matching of "<" and ">".
-    BeginToken openParen = scan('(x<y)>z');
-    var x = openParen.next;
-    BeginToken lessThan = x.next;
-    var y = lessThan.next;
-    var closeParen = y.next;
-    var greaterThan = closeParen.next;
-    var z = greaterThan.next;
-    expect(z.next.isEof, isTrue);
+    BeginToken openParen = scan('(x<y)>z') as BeginToken;
+    var x = openParen.next!;
+    BeginToken lessThan = x.next as BeginToken;
+    var y = lessThan.next!;
+    var closeParen = y.next!;
+    var greaterThan = closeParen.next!;
+    var z = greaterThan.next!;
+    expect(z.next!.isEof, isTrue);
     expect(openParen.endGroup, same(closeParen));
     expect(lessThan.endGroup, isNull);
   }
@@ -654,15 +652,15 @@
   void test_match_angle_brackets_interrupted_by_interpolation_expr() {
     // A "${" appearing in the token stream interrupts matching of "<" and ">".
     var x = scan(r'x<"${y>z}"');
-    BeginToken lessThan = x.next;
-    var beginString = lessThan.next;
-    BeginToken beginInterpolation = beginString.next;
-    var y = beginInterpolation.next;
-    var greaterThan = y.next;
-    var z = greaterThan.next;
-    var endInterpolation = z.next;
-    var endString = endInterpolation.next;
-    expect(endString.next.isEof, isTrue);
+    BeginToken lessThan = x.next as BeginToken;
+    var beginString = lessThan.next!;
+    BeginToken beginInterpolation = beginString.next as BeginToken;
+    var y = beginInterpolation.next!;
+    var greaterThan = y.next!;
+    var z = greaterThan.next!;
+    var endInterpolation = z.next!;
+    var endString = endInterpolation.next!;
+    expect(endString.next!.isEof, isTrue);
     expect(lessThan.endGroup, isNull);
     expect(beginInterpolation.endGroup, same(endInterpolation));
   }
@@ -670,13 +668,13 @@
   void test_match_angle_brackets_interrupted_by_open_brace() {
     // A "{" appearing in the token stream interrupts matching of "<" and ">".
     var x = scan('x<{y>z}');
-    BeginToken lessThan = x.next;
-    BeginToken openBrace = lessThan.next;
-    var y = openBrace.next;
-    var greaterThan = y.next;
-    var z = greaterThan.next;
-    var closeBrace = z.next;
-    expect(closeBrace.next.isEof, isTrue);
+    BeginToken lessThan = x.next as BeginToken;
+    BeginToken openBrace = lessThan.next as BeginToken;
+    var y = openBrace.next!;
+    var greaterThan = y.next!;
+    var z = greaterThan.next!;
+    var closeBrace = z.next!;
+    expect(closeBrace.next!.isEof, isTrue);
     expect(lessThan.endGroup, isNull);
     expect(openBrace.endGroup, same(closeBrace));
   }
@@ -684,14 +682,14 @@
   void test_match_angle_brackets_interrupted_by_open_bracket() {
     // A "[" appearing in the token stream interrupts matching of "<" and ">".
     var x = scan('x<y[z>a]');
-    BeginToken lessThan = x.next;
-    var y = lessThan.next;
-    BeginToken openBracket = y.next;
-    var z = openBracket.next;
-    var greaterThan = z.next;
-    var a = greaterThan.next;
-    var closeBracket = a.next;
-    expect(closeBracket.next.isEof, isTrue);
+    BeginToken lessThan = x.next as BeginToken;
+    var y = lessThan.next!;
+    BeginToken openBracket = y.next as BeginToken;
+    var z = openBracket.next!;
+    var greaterThan = z.next!;
+    var a = greaterThan.next!;
+    var closeBracket = a.next!;
+    expect(closeBracket.next!.isEof, isTrue);
     expect(lessThan.endGroup, isNull);
     expect(openBracket.endGroup, same(closeBracket));
   }
@@ -699,29 +697,29 @@
   void test_match_angle_brackets_interrupted_by_open_paren() {
     // A "(" appearing in the token stream interrupts matching of "<" and ">".
     var x = scan('x<y(z>a)');
-    BeginToken lessThan = x.next;
-    var y = lessThan.next;
-    BeginToken openParen = y.next;
-    var z = openParen.next;
-    var greaterThan = z.next;
-    var a = greaterThan.next;
-    var closeParen = a.next;
-    expect(closeParen.next.isEof, isTrue);
+    BeginToken lessThan = x.next as BeginToken;
+    var y = lessThan.next!;
+    BeginToken openParen = y.next as BeginToken;
+    var z = openParen.next!;
+    var greaterThan = z.next!;
+    var a = greaterThan.next!;
+    var closeParen = a.next!;
+    expect(closeParen.next!.isEof, isTrue);
     expect(lessThan.endGroup, isNull);
     expect(openParen.endGroup, same(closeParen));
   }
 
   void test_match_angle_brackets_nested() {
     var x = scan('x<y<z>,a>');
-    BeginToken lessThan1 = x.next;
-    var y = lessThan1.next;
-    BeginToken lessThan2 = y.next;
-    var z = lessThan2.next;
-    var greaterThan1 = z.next;
-    var comma = greaterThan1.next;
-    var a = comma.next;
-    var greaterThan2 = a.next;
-    expect(greaterThan2.next.isEof, isTrue);
+    BeginToken lessThan1 = x.next as BeginToken;
+    var y = lessThan1.next!;
+    BeginToken lessThan2 = y.next as BeginToken;
+    var z = lessThan2.next!;
+    var greaterThan1 = z.next!;
+    var comma = greaterThan1.next!;
+    var a = comma.next!;
+    var greaterThan2 = a.next!;
+    expect(greaterThan2.next!.isEof, isTrue);
     expect(lessThan1.endGroup, same(greaterThan2));
     expect(lessThan2.endGroup, same(greaterThan1));
   }
@@ -730,11 +728,11 @@
     // When a ">>" appears in the token stream and there is no outer "<",
     // Fasta's scanner leaves the inner "<" unmatched.
     var x = scan('x<y>>z');
-    BeginToken lessThan = x.next;
-    var y = lessThan.next;
-    var greaterThans = y.next;
-    var z = greaterThans.next;
-    expect(z.next.isEof, isTrue);
+    BeginToken lessThan = x.next as BeginToken;
+    var y = lessThan.next!;
+    var greaterThans = y.next!;
+    var z = greaterThans.next!;
+    expect(z.next!.isEof, isTrue);
     expect(lessThan.endGroup, isNull);
   }
 }
@@ -755,7 +753,7 @@
 /// Scanner tests that exercise the Fasta scanner directly.
 @reflectiveTest
 class ScannerTest_Fasta_Direct extends ScannerTest_Fasta_Base {
-  fasta.LanguageVersionToken languageVersion;
+  fasta.LanguageVersionToken? languageVersion;
 
   void languageVersionChanged(
       Scanner scanner, fasta.LanguageVersionToken languageVersion) {
@@ -773,7 +771,7 @@
     final Token first = result.tokens;
     Token token = first;
     while (!token.isEof) {
-      Token next = token.next;
+      Token next = token.next!;
       expect(token.next, next);
       expect(next.previous, token);
       if (next.isSynthetic && [')', ']', '}'].contains(next.lexeme)) {
@@ -802,8 +800,8 @@
 import 'foo.dart';
 main() {}
 ''');
-    expect(languageVersion.major, 2);
-    expect(languageVersion.minor, 3);
+    expect(languageVersion!.major, 2);
+    expect(languageVersion!.minor, 3);
     expectComments(
         result.tokens,
         [
@@ -819,8 +817,8 @@
 // @dart = 2.3
 main() {}
 ''');
-    expect(languageVersion.major, 2);
-    expect(languageVersion.minor, 3);
+    expect(languageVersion!.major, 2);
+    expect(languageVersion!.minor, 3);
     expectComments(result.tokens, ['// @dart = 2.3'], 0);
   }
 
@@ -838,8 +836,8 @@
 // @dart = 2.3
 main() {}
 ''', includeComments: false);
-    expect(languageVersion.major, 2);
-    expect(languageVersion.minor, 3);
+    expect(languageVersion!.major, 2);
+    expect(languageVersion!.minor, 3);
     expectComments(result.tokens, [], -1);
   }
 
@@ -849,8 +847,8 @@
 import 'foo.dart';
 main() {}
 ''');
-    expect(languageVersion.major, 2);
-    expect(languageVersion.minor, 3);
+    expect(languageVersion!.major, 2);
+    expect(languageVersion!.minor, 3);
     expectComments(result.tokens, ['// @dart = 2.3'], 0);
   }
 
@@ -861,9 +859,9 @@
 import 'foo.dart';
 main() {}
 ''');
-    expect(languageVersion.major, 2);
-    expect(languageVersion.minor, 3);
-    expectComments(result.tokens.next, ['// @dart = 2.3'], 0);
+    expect(languageVersion!.major, 2);
+    expect(languageVersion!.minor, 3);
+    expectComments(result.tokens.next!, ['// @dart = 2.3'], 0);
   }
 
   void test_languageVersion_beforeLibrary() {
@@ -872,8 +870,8 @@
 library foo;
 main() {}
 ''');
-    expect(languageVersion.major, 2);
-    expect(languageVersion.minor, 3);
+    expect(languageVersion!.major, 2);
+    expect(languageVersion!.minor, 3);
     expectComments(result.tokens, ['// @dart = 2.3'], 0);
   }
 
@@ -883,8 +881,8 @@
 library foo;
 main() {}
 ''');
-    expect(languageVersion.major, 2);
-    expect(languageVersion.minor, 3);
+    expect(languageVersion!.major, 2);
+    expect(languageVersion!.minor, 3);
     expectComments(result.tokens, ['// @dart=2.3'], 0);
   }
 
@@ -935,14 +933,14 @@
     expect(token.charOffset, 14, reason: 'error token : $token, ${token.type}');
     expect(token.charCount, 3, reason: 'error token : $token, ${token.type}');
 
-    token = token.next;
+    token = token.next!;
     expect(token.lexeme, 'var');
     int index = 0;
     while (!token.isEof) {
       expect(token.charOffset, lineStarts[index],
           reason: 'token # $index : $token, ${token.type}');
       ++index;
-      token = token.next;
+      token = token.next!;
     }
   }
 
@@ -955,21 +953,21 @@
     expect(token, const TypeMatcher<ErrorToken>());
     expect(token.charOffset, 14, reason: 'token # 0 : $token, ${token.type}');
 
-    token = token.next;
+    token = token.next!;
     expect(token.lexeme, 'var');
     int index = 0;
     while (!token.isEof) {
       expect(token.charOffset, lineStarts[index],
           reason: 'token # $index : $token, ${token.type}');
       ++index;
-      token = token.next;
+      token = token.next!;
     }
   }
 
   void expectComments(
-      Token token, List<String> expectedComments, int versionIndex) {
+      Token? token, List<String> expectedComments, int versionIndex) {
     int index = 0;
-    token = token.precedingComments;
+    token = token!.precedingComments;
     while (token != null) {
       if (index == versionIndex) {
         if (token is! fasta.LanguageVersionToken) {
diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt
index 99bfae9..fcf3a64 100644
--- a/pkg/front_end/test/spell_checking_list_code.txt
+++ b/pkg/front_end/test/spell_checking_list_code.txt
@@ -723,6 +723,8 @@
 lz
 m
 macos
+macro
+macros
 maintaining
 mangled
 manipulation
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index e6c4cf0..86bfe96 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -643,6 +643,8 @@
 lookahead
 loopback
 mac
+macro
+macros
 maker
 matters
 mds
diff --git a/pkg/front_end/test/spell_checking_utils.dart b/pkg/front_end/test/spell_checking_utils.dart
index c560644..19347ef 100644
--- a/pkg/front_end/test/spell_checking_utils.dart
+++ b/pkg/front_end/test/spell_checking_utils.dart
@@ -51,13 +51,13 @@
       }
     }
     if (!found) {
-      wrongWords ??= <String>[];
+      wrongWords ??= [];
       wrongWords.add(word);
-      wrongWordsAlternatives ??= <List<String>>[];
+      wrongWordsAlternatives ??= [];
       wrongWordsAlternatives.add(findAlternatives(word, dictionariesUnpacked));
-      wrongWordsOffset ??= <int>[];
+      wrongWordsOffset ??= [];
       wrongWordsOffset.add(offset);
-      wrongWordDenylisted ??= <bool>[];
+      wrongWordDenylisted ??= [];
       wrongWordDenylisted
           .add(loadedDictionaries![Dictionaries.denylist]!.contains(word));
     }
diff --git a/pkg/front_end/testing.json b/pkg/front_end/testing.json
index cc90a5b..3e12d26 100644
--- a/pkg/front_end/testing.json
+++ b/pkg/front_end/testing.json
@@ -466,6 +466,7 @@
       "test/extensions/data/",
       "test/id_testing/data/",
       "test/language_versioning/data/",
+      "test/macros/data/",
       "test/patching/data",
       "test/predicates/data",
       "test/static_types/data/",
diff --git a/tools/VERSION b/tools/VERSION
index dce1906..fdf498a 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 16
 PATCH 0
-PRERELEASE 35
+PRERELEASE 36
 PRERELEASE_PATCH 0
\ No newline at end of file