Reland "[cfe] Build some annotations during outlining"

Reland the change to build some annotations during outlining, with a
bug fix.  Annotations from patch classes and procedures were not
applied to the actual class or procedure, which is fixed.

The original commit was reviewed at
https://dart-review.googlesource.com/c/sdk/+/103806

The revert was reviewed at
https://dart-review.googlesource.com/c/sdk/+/104220

The original commit message was:

    During the outline phase, after top-level type inference, compile
    annotations for libraries, classes, fields, procedures, and
    constructors.

Change-Id: I836160ddad4114ded72701e8a3938f703228931c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104560
Commit-Queue: Kevin Millikin <kmillikin@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index 3754d84..2d4fad3 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -2008,8 +2008,7 @@
     }
   }
 
-  void finishFunction(
-      List annotations, formals, AsyncMarker asyncModifier, FunctionBody body) {
+  void finishFunction(formals, AsyncMarker asyncModifier, FunctionBody body) {
     debugEvent("finishFunction");
 
     Statement bodyStatement;
diff --git a/pkg/front_end/lib/src/fasta/builder/class_builder.dart b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
index 05441e7..0ef4745 100644
--- a/pkg/front_end/lib/src/fasta/builder/class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/class_builder.dart
@@ -77,6 +77,8 @@
     return library.partOfLibrary ?? library;
   }
 
+  void buildAnnotations(LibraryBuilder library) {}
+
   /// Registers a constructor redirection for this class and returns true if
   /// this redirection gives rise to a cycle that has not been reported before.
   bool checkConstructorCyclic(String source, String target) {
@@ -124,7 +126,7 @@
     return constructors.lookup(name, charOffset, uri);
   }
 
-  void forEach(void f(String name, MemberBuilder builder)) {
+  void forEach(void f(String name, Declaration builder)) {
     scope.forEach(f);
   }
 
diff --git a/pkg/front_end/lib/src/fasta/builder/library_builder.dart b/pkg/front_end/lib/src/fasta/builder/library_builder.dart
index 9dbca65..ed544d6 100644
--- a/pkg/front_end/lib/src/fasta/builder/library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/library_builder.dart
@@ -235,6 +235,8 @@
   }
 
   void recordAccess(int charOffset, int length, Uri fileUri) {}
+
+  void buildAnnotations() {}
 }
 
 class LibraryLocalDeclarationIterator implements Iterator<Declaration> {
diff --git a/pkg/front_end/lib/src/fasta/builder/member_builder.dart b/pkg/front_end/lib/src/fasta/builder/member_builder.dart
index 827fe5f..2dd5099 100644
--- a/pkg/front_end/lib/src/fasta/builder/member_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/member_builder.dart
@@ -37,6 +37,8 @@
     }
   }
 
+  void buildAnnotations(LibraryBuilder library) {}
+
   @override
   String get fullNameForErrors => name;
 }
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 6abe0c6..393be7d 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -601,25 +601,7 @@
       // `invalid-type`.
       buildDartType(pop()); // Type.
     }
-    List<Expression> annotations = pop();
-    // Fields with duplicate names are sorted out in the else branch of the
-    // `declaration.next == null` above.
-    if (annotations != null && fields.isNotEmpty) {
-      inferAnnotations(annotations);
-      Field field = fields.first.target;
-      // The first (and often only field) will not get a clone.
-      for (int i = 0; i < annotations.length; i++) {
-        field.addAnnotation(annotations[i]);
-      }
-      for (int i = 1; i < fields.length; i++) {
-        // We have to clone the annotations on the remaining fields.
-        field = fields[i].target;
-        cloner ??= new CloneVisitor();
-        for (Expression annotation in annotations) {
-          field.addAnnotation(cloner.clone(annotation));
-        }
-      }
-    }
+    pop(); // Annotations.
 
     resolveRedirectingFactoryTargets();
     finishVariableMetadata();
@@ -751,8 +733,8 @@
   }
 
   @override
-  void finishFunction(List<Expression> annotations, FormalParameters formals,
-      AsyncMarker asyncModifier, Statement body) {
+  void finishFunction(
+      FormalParameters formals, AsyncMarker asyncModifier, Statement body) {
     debugEvent("finishFunction");
     typePromoter?.finished();
 
@@ -903,11 +885,6 @@
           ..fileOffset = body.fileOffset;
       }
     }
-    Member target = builder.target;
-    inferAnnotations(annotations);
-    for (Expression annotation in annotations ?? const []) {
-      target.addAnnotation(annotation);
-    }
     if (builder is KernelConstructorBuilder) {
       finishConstructor(builder, asyncModifier);
     } else if (builder is KernelProcedureBuilder) {
@@ -1155,6 +1132,14 @@
     return expression;
   }
 
+  Expression parseAnnotation(Token token) {
+    Parser parser = new Parser(this);
+    token = parser.parseMetadata(parser.syntheticPreviousToken(token));
+    Expression annotation = pop();
+    checkEmpty(token.charOffset);
+    return annotation;
+  }
+
   void finishConstructor(
       KernelConstructorBuilder builder, AsyncMarker asyncModifier) {
     /// Quotes below are from [Dart Programming Language Specification, 4th
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_body_builder.dart
index 1886808..c37724b 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_body_builder.dart
@@ -42,6 +42,27 @@
       : forest = const Fangorn(),
         super.forField(member, typeInferrer);
 
+  KernelBodyBuilder.forAnnotation(
+      KernelLibraryBuilder library,
+      KernelClassBuilder classBuilder,
+      ModifierBuilder member,
+      Scope scope,
+      Scope parameterScope,
+      Uri fileUri)
+      : forest = const Fangorn(),
+        super(
+            library,
+            member,
+            scope,
+            parameterScope,
+            library.loader.hierarchy,
+            library.loader.coreTypes,
+            classBuilder,
+            member?.isInstanceMember ?? false,
+            fileUri,
+            library.loader.typeInferenceEngine
+                ?.createLocalTypeInferrer(fileUri, null, library));
+
   @override
   void enterThenForTypePromotion(Expression condition) {
     typePromoter?.enterThen(condition);
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
index 6ad083c..31214d6 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_class_builder.dart
@@ -105,6 +105,7 @@
         Declaration,
         KernelFunctionBuilder,
         KernelLibraryBuilder,
+        KernelMetadataBuilder,
         KernelNamedTypeBuilder,
         KernelProcedureBuilder,
         KernelRedirectingFactoryBuilder,
@@ -224,6 +225,19 @@
     }
   }
 
+  @override
+  void buildAnnotations(LibraryBuilder library) {
+    void build(String ignore, Declaration declaration) {
+      MemberBuilder member = declaration;
+      member.buildAnnotations(library);
+    }
+
+    KernelMetadataBuilder.buildAnnotations(
+        isPatch ? origin.target : cls, metadata, library, this, null, null);
+    constructors.forEach(build);
+    scope.forEach(build);
+  }
+
   void checkSupertypes(CoreTypes coreTypes) {
     // This method determines whether the class (that's being built) its super
     // class appears both in 'extends' and 'implements' clauses and whether any
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_field_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_field_builder.dart
index 904f47d..39c70c9 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_field_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_field_builder.dart
@@ -23,7 +23,9 @@
         FieldBuilder,
         ImplicitFieldType,
         KernelLibraryBuilder,
+        KernelMetadataBuilder,
         KernelTypeBuilder,
+        LibraryBuilder,
         MetadataBuilder;
 
 import 'kernel_shadow_ast.dart' show ShadowField;
@@ -91,6 +93,13 @@
     return field;
   }
 
+  @override
+  void buildAnnotations(LibraryBuilder library) {
+    ClassBuilder classBuilder = isClassMember ? parent : null;
+    KernelMetadataBuilder.buildAnnotations(
+        field, metadata, library, classBuilder, this, null);
+  }
+
   Field get target => field;
 
   void prepareTopLevelInference() {
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
index 80db2c8..983b7c7 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
@@ -135,6 +135,7 @@
         KernelTypeAliasBuilder,
         KernelFunctionTypeBuilder,
         KernelInvalidTypeBuilder,
+        KernelMetadataBuilder,
         KernelMixinApplicationBuilder,
         KernelNamedTypeBuilder,
         KernelProcedureBuilder,
@@ -902,6 +903,12 @@
   }
 
   @override
+  void buildAnnotations() {
+    KernelMetadataBuilder.buildAnnotations(
+        library, metadata, this, null, null, null);
+  }
+
+  @override
   void buildBuilder(Declaration declaration, LibraryBuilder coreLibrary) {
     Class cls;
     Member member;
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_metadata_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_metadata_builder.dart
index 7cfa901..7bc39e6 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_metadata_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_metadata_builder.dart
@@ -4,12 +4,48 @@
 
 library fasta.kernel_metadata_builder;
 
-import 'kernel_builder.dart' show MetadataBuilder;
+import 'package:kernel/ast.dart' show Annotatable, Class, Library;
+
+import 'kernel_body_builder.dart' show KernelBodyBuilder;
+
+import 'kernel_builder.dart'
+    show
+        KernelClassBuilder,
+        KernelLibraryBuilder,
+        MetadataBuilder,
+        MemberBuilder;
 
 import '../scanner.dart' show Token;
 
-class KernelMetadataBuilder extends MetadataBuilder {
-  final int charOffset;
+import '../scope.dart' show Scope;
 
-  KernelMetadataBuilder(Token beginToken) : charOffset = beginToken.charOffset;
+class KernelMetadataBuilder extends MetadataBuilder {
+  final Token beginToken;
+
+  int get charOffset => beginToken.charOffset;
+
+  KernelMetadataBuilder(this.beginToken);
+
+  static void buildAnnotations(
+      Annotatable parent,
+      List<MetadataBuilder> metadata,
+      KernelLibraryBuilder library,
+      KernelClassBuilder classBuilder,
+      MemberBuilder member,
+      Scope parameterScope) {
+    if (metadata == null) return;
+    Uri fileUri = member?.fileUri ?? classBuilder?.fileUri ?? library.fileUri;
+    Scope scope = parent is Library || parent is Class || classBuilder == null
+        ? library.scope
+        : classBuilder.scope;
+    KernelBodyBuilder bodyBuilder = new KernelBodyBuilder.forAnnotation(
+        library, classBuilder, member, scope, parameterScope, fileUri);
+    for (int i = 0; i < metadata.length; ++i) {
+      KernelMetadataBuilder annotationBuilder = metadata[i];
+      parent.addAnnotation(
+          bodyBuilder.parseAnnotation(annotationBuilder.beginToken));
+    }
+    bodyBuilder.inferAnnotations(parent.annotations);
+    bodyBuilder.resolveRedirectingFactoryTargets();
+  }
 }
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_procedure_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_procedure_builder.dart
index 2434d09..c8d217b 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_procedure_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_procedure_builder.dart
@@ -65,6 +65,7 @@
         FormalParameterBuilder,
         KernelFormalParameterBuilder,
         KernelLibraryBuilder,
+        KernelMetadataBuilder,
         KernelTypeBuilder,
         KernelTypeVariableBuilder,
         LibraryBuilder,
@@ -361,6 +362,18 @@
     return procedure;
   }
 
+  @override
+  void buildAnnotations(LibraryBuilder library) {
+    ClassBuilder classBuilder = isClassMember ? parent : null;
+    KernelMetadataBuilder.buildAnnotations(
+        target,
+        metadata,
+        library,
+        classBuilder,
+        this,
+        computeFormalParameterScope(classBuilder?.scope ?? library.scope));
+  }
+
   Procedure get target => origin.procedure;
 
   @override
@@ -483,6 +496,18 @@
     return constructor;
   }
 
+  @override
+  void buildAnnotations(LibraryBuilder library) {
+    ClassBuilder classBuilder = isClassMember ? parent : null;
+    KernelMetadataBuilder.buildAnnotations(
+        target,
+        metadata,
+        library,
+        classBuilder,
+        this,
+        computeFormalParameterScope(classBuilder?.scope ?? library.scope));
+  }
+
   FunctionNode buildFunction(LibraryBuilder library) {
     // According to the specification §9.3 the return type of a constructor
     // function is its enclosing class.
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index 49d0344..83e30e9 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -279,6 +279,7 @@
       loader.checkRedirectingFactories(myClasses);
       loader.addNoSuchMethodForwarders(myClasses);
       loader.checkMixins(myClasses);
+      loader.buildAnnotations();
       installAllComponentProblems(loader.allComponentProblems);
       loader.allComponentProblems.clear();
       return component;
diff --git a/pkg/front_end/lib/src/fasta/source/diet_listener.dart b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
index 1125c14..cf3c27f 100644
--- a/pkg/front_end/lib/src/fasta/source/diet_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/diet_listener.dart
@@ -363,10 +363,8 @@
   @override
   void endLibraryName(Token libraryKeyword, Token semicolon) {
     debugEvent("endLibraryName");
-    pop(); // name
-
-    Token metadata = pop();
-    parseMetadata(library, metadata, library.target);
+    pop(); // Name.
+    pop(); // Annotations.
   }
 
   @override
@@ -537,7 +535,6 @@
 
     ProcedureBuilder builder = lookupConstructor(beginToken, name);
     if (bodyToken == null || optional("=", bodyToken.endGroup.next)) {
-      parseMetadata(builder, metadata, builder.target);
       buildRedirectingFactoryMethod(
           bodyToken, builder, MemberKind.Factory, metadata);
     } else {
@@ -709,18 +706,14 @@
     debugEvent("beginClassOrMixinBody");
     Token beginToken = pop();
     Object name = pop();
-    Token metadata = pop();
+    pop(); // Annotation begin token.
     assert(currentClass == null);
     assert(memberScope == library.scope);
     if (name is ParserRecovery) {
       currentClassIsParserRecovery = true;
       return;
     }
-
-    Declaration classBuilder = lookupBuilder(beginToken, null, name);
-    parseMetadata(classBuilder, metadata, classBuilder.target);
-
-    currentClass = classBuilder;
+    currentClass = lookupBuilder(beginToken, null, name);
     memberScope = currentClass.scope;
   }
 
@@ -747,26 +740,9 @@
   @override
   void endEnum(Token enumKeyword, Token leftBrace, int count) {
     debugEvent("Enum");
-    List<Object> metadataAndValues =
-        const FixedNullableList<Object>().pop(stack, count * 2);
-    Object name = pop();
-    Token metadata = pop();
-    checkEmpty(enumKeyword.charOffset);
-    if (name is ParserRecovery) return;
-
-    ClassBuilder enumBuilder = lookupBuilder(enumKeyword, null, name);
-    parseMetadata(enumBuilder, metadata, enumBuilder.target);
-    if (metadataAndValues != null) {
-      for (int i = 0; i < metadataAndValues.length; i += 2) {
-        Token metadata = metadataAndValues[i];
-        String valueName = metadataAndValues[i + 1];
-        Declaration declaration = enumBuilder.scope.local[valueName];
-        if (metadata != null) {
-          parseMetadata(declaration, metadata, declaration.target);
-        }
-      }
-    }
-
+    const FixedNullableList<Object>().pop(stack, count * 2);
+    pop(); // Name.
+    pop(); // Annotations begin token.
     checkEmpty(enumKeyword.charOffset);
   }
 
@@ -775,20 +751,9 @@
       Token equals, Token implementsKeyword, Token endToken) {
     debugEvent("NamedMixinApplication");
 
-    Object name = pop();
-    Token metadata = pop();
+    pop(); // Name.
+    pop(); // Annotations begin token.
     checkEmpty(beginToken.charOffset);
-    if (name is ParserRecovery) return;
-
-    Declaration classBuilder = library.scopeBuilder[name];
-    if (classBuilder != null) {
-      // TODO(ahe): We shouldn't have to check for null here. The problem is
-      // that we don't create a named mixin application if the mixins or
-      // supertype are missing. Could we create a class instead? The nested
-      // declarations wouldn't match up.
-      parseMetadata(classBuilder, metadata, classBuilder.target);
-      checkEmpty(beginToken.charOffset);
-    }
   }
 
   AsyncMarker getAsyncMarker(StackListener listener) => listener.pop();
@@ -800,13 +765,11 @@
   void listenerFinishFunction(
       StackListener listener,
       Token token,
-      Token metadata,
       MemberKind kind,
-      List metadataConstants,
       dynamic formals,
       AsyncMarker asyncModifier,
       dynamic body) {
-    listener.finishFunction(metadataConstants, formals, asyncModifier, body);
+    listener.finishFunction(formals, asyncModifier, body);
   }
 
   /// Invokes the listener's [finishFields] method.
@@ -823,10 +786,9 @@
     Token token = startToken;
     try {
       Parser parser = new Parser(listener);
-      List metadataConstants;
       if (metadata != null) {
         parser.parseMetadataStar(parser.syntheticPreviousToken(metadata));
-        metadataConstants = listener.pop();
+        listener.pop(); // Annotations.
       }
       token = parser.parseFormalParametersOpt(
           parser.syntheticPreviousToken(token), kind);
@@ -840,8 +802,8 @@
       parser.parseFunctionBody(token, isExpression, allowAbstract);
       var body = listener.pop();
       listener.checkEmpty(token.charOffset);
-      listenerFinishFunction(listener, startToken, metadata, kind,
-          metadataConstants, formals, asyncModifier, body);
+      listenerFinishFunction(
+          listener, startToken, kind, formals, asyncModifier, body);
     } on DebugAbort {
       rethrow;
     } catch (e, s) {
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index 88e4621..adffcbd 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -82,6 +82,7 @@
         KernelProcedureBuilder,
         KernelTypeBuilder,
         LibraryBuilder,
+        MemberBuilder,
         NamedTypeBuilder,
         TypeBuilder;
 
@@ -947,6 +948,23 @@
     ticker.logMs("Checked mixin declaration applications");
   }
 
+  void buildAnnotations() {
+    builders.forEach((Uri uri, LibraryBuilder library) {
+      if (library.loader == this) {
+        library.buildAnnotations();
+        Iterator<Declaration> iterator = library.iterator;
+        while (iterator.moveNext()) {
+          Declaration declaration = iterator.current;
+          if (declaration is ClassBuilder) {
+            declaration.buildAnnotations(library);
+          } else if (declaration is MemberBuilder) {
+            declaration.buildAnnotations(library);
+          }
+        }
+      }
+    });
+  }
+
   ClassHierarchyBuilder buildClassHierarchy(
       List<SourceClassBuilder> sourceClasses, ClassBuilder objectClass) {
     ClassHierarchyBuilder hierarchy = ClassHierarchyBuilder.build(
diff --git a/pkg/front_end/lib/src/fasta/source/stack_listener.dart b/pkg/front_end/lib/src/fasta/source/stack_listener.dart
index db6a7cd..443b623 100644
--- a/pkg/front_end/lib/src/fasta/source/stack_listener.dart
+++ b/pkg/front_end/lib/src/fasta/source/stack_listener.dart
@@ -86,8 +86,8 @@
 
   // TODO(ahe): This doesn't belong here. Only implemented by body_builder.dart
   // and ast_builder.dart.
-  void finishFunction(covariant List<Object> annotations, covariant formals,
-      AsyncMarker asyncModifier, covariant body) {
+  void finishFunction(
+      covariant formals, AsyncMarker asyncModifier, covariant body) {
     return unsupported("finishFunction", -1, uri);
   }
 
diff --git a/pkg/front_end/testcases/annotation_on_enum_values.dart.outline.expect b/pkg/front_end/testcases/annotation_on_enum_values.dart.outline.expect
index 319403e..a04857a 100644
--- a/pkg/front_end/testcases/annotation_on_enum_values.dart.outline.expect
+++ b/pkg/front_end/testcases/annotation_on_enum_values.dart.outline.expect
@@ -11,7 +11,9 @@
   final field core::int index;
   final field core::String _name;
   static const field core::List<self::Foo> values = const <self::Foo>[self::Foo::bar, self::Foo::baz, self::Foo::cafebabe];
+  @self::hest
   static const field self::Foo bar = const self::Foo::•(0, "Foo.bar");
+  @self::Fisk::fisk<dynamic>(self::hest)
   static const field self::Foo baz = const self::Foo::•(1, "Foo.baz");
   static const field self::Foo cafebabe = const self::Foo::•(2, "Foo.cafebabe");
   const constructor •(core::int index, core::String _name) → self::Foo
diff --git a/pkg/front_end/testcases/annotation_top.dart.outline.expect b/pkg/front_end/testcases/annotation_top.dart.outline.expect
index 6839b80..579ecba 100644
--- a/pkg/front_end/testcases/annotation_top.dart.outline.expect
+++ b/pkg/front_end/testcases/annotation_top.dart.outline.expect
@@ -1,3 +1,5 @@
+@test::a
+@test::A::•(1)
 library test;
 import self as self;
 import "dart:core" as core;
@@ -8,12 +10,20 @@
   const constructor •(core::int value) → self::A
     ;
 }
+@self::a
+@self::A::•(2)
 class C extends core::Object {
   synthetic constructor •() → self::C
     ;
 }
 static const field core::Object a;
+@self::a
+@self::A::•(3)
 static field core::int f1;
+@self::a
+@self::A::•(3)
 static field core::int f2;
+@self::a
+@self::A::•(4)
 static method main() → void
   ;
diff --git a/pkg/front_end/testcases/bug33099.dart.outline.expect b/pkg/front_end/testcases/bug33099.dart.outline.expect
index 42a5b20..24e4823 100644
--- a/pkg/front_end/testcases/bug33099.dart.outline.expect
+++ b/pkg/front_end/testcases/bug33099.dart.outline.expect
@@ -12,6 +12,7 @@
 class MyTest extends core::Object {
   synthetic constructor •() → self::MyTest
     ;
+  @self::failingTest
   method foo() → void
     ;
 }
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations.dart.outline.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations.dart.outline.expect
index 459d870..6ddb363 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations.dart.outline.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations.dart.outline.expect
@@ -8,10 +8,12 @@
   const constructor named(core::List<core::String> l) → self::Foo
     ;
 }
+@self::Foo::•(const <dynamic>[])
 class Bar extends core::Object {
   synthetic constructor •() → self::Bar
     ;
 }
+@self::Foo::named(const <dynamic>[])
 class Baz extends core::Object {
   synthetic constructor •() → self::Baz
     ;
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_class_members.dart.outline.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_class_members.dart.outline.expect
index b4da95f..62d5559 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_class_members.dart.outline.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_class_members.dart.outline.expect
@@ -7,9 +7,12 @@
     ;
 }
 abstract class Bar extends core::Object {
+  @self::Foo::•(const <dynamic>[])
   field dynamic x;
+  @self::Foo::•(const <dynamic>[])
   constructor •() → self::Bar
     ;
+  @self::Foo::•(const <dynamic>[])
   abstract method f() → void;
 }
 static method main() → dynamic
diff --git a/pkg/front_end/testcases/inference/override_equals.dart.outline.expect b/pkg/front_end/testcases/inference/override_equals.dart.outline.expect
index bf42edb..41bfd6e 100644
--- a/pkg/front_end/testcases/inference/override_equals.dart.outline.expect
+++ b/pkg/front_end/testcases/inference/override_equals.dart.outline.expect
@@ -5,6 +5,7 @@
 class NullEquality extends core::Object {
   synthetic constructor •() → self::NullEquality
     ;
+  @core::override
   operator ==(core::Object other) → core::Null
     ;
 }
diff --git a/pkg/front_end/testcases/metadata_enum.dart.outline.expect b/pkg/front_end/testcases/metadata_enum.dart.outline.expect
index 6fd993b..1ecf0f8 100644
--- a/pkg/front_end/testcases/metadata_enum.dart.outline.expect
+++ b/pkg/front_end/testcases/metadata_enum.dart.outline.expect
@@ -2,6 +2,7 @@
 import self as self;
 import "dart:core" as core;
 
+@self::a
 class E extends core::Object {
   final field core::int index;
   final field core::String _name;
diff --git a/pkg/front_end/testcases/metadata_named_mixin_application.dart.outline.expect b/pkg/front_end/testcases/metadata_named_mixin_application.dart.outline.expect
index cec02ba..ec64387 100644
--- a/pkg/front_end/testcases/metadata_named_mixin_application.dart.outline.expect
+++ b/pkg/front_end/testcases/metadata_named_mixin_application.dart.outline.expect
@@ -2,6 +2,7 @@
 import self as self;
 import "dart:core" as core;
 
+@self::a
 class C = self::D with self::E {
   synthetic constructor •() → self::C
     : super self::D::•()
diff --git a/pkg/front_end/testcases/native_as_name.dart.outline.expect b/pkg/front_end/testcases/native_as_name.dart.outline.expect
index 6c2884c..b78f8ae 100644
--- a/pkg/front_end/testcases/native_as_name.dart.outline.expect
+++ b/pkg/front_end/testcases/native_as_name.dart.outline.expect
@@ -21,6 +21,7 @@
 class Y2 extends self::Y1 {
   synthetic constructor •() → self::Y2
     ;
+  @core::override
   get native() → core::String
     ;
 }
diff --git a/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.outline.expect b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.outline.expect
index 9051099..cc16f53 100644
--- a/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.outline.expect
+++ b/pkg/front_end/testcases/override_check_generic_method_f_bounded.dart.outline.expect
@@ -14,6 +14,7 @@
 class Hest extends core::Object implements self::Bar {
   synthetic constructor •() → self::Hest
     ;
+  @core::override
   method fisk<U extends self::Foo<self::Hest::fisk::U> = dynamic>() → void
     ;
 }
diff --git a/pkg/front_end/testcases/rasta/issue_000033.dart.outline.expect b/pkg/front_end/testcases/rasta/issue_000033.dart.outline.expect
index 6a28c0d..44fbbbc 100644
--- a/pkg/front_end/testcases/rasta/issue_000033.dart.outline.expect
+++ b/pkg/front_end/testcases/rasta/issue_000033.dart.outline.expect
@@ -1,5 +1,15 @@
 library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/rasta/issue_000033.dart:5:2: Warning: Method not found: 'JS'.
+// @JS()
+//  ^^
+//
 import self as self;
 
+@invalid-expression "pkg/front_end/testcases/rasta/issue_000033.dart:5:2: Error: Method not found: 'JS'.
+@JS()
+ ^^"
 static method main() → dynamic
   ;
diff --git a/pkg/front_end/testcases/redirecting_factory_metadata.dart.outline.expect b/pkg/front_end/testcases/redirecting_factory_metadata.dart.outline.expect
index 5b95c2b..dc87a52 100644
--- a/pkg/front_end/testcases/redirecting_factory_metadata.dart.outline.expect
+++ b/pkg/front_end/testcases/redirecting_factory_metadata.dart.outline.expect
@@ -6,6 +6,7 @@
   static field dynamic _redirecting# = <dynamic>[self::Foo::•];
   constructor named(dynamic p) → self::Foo
     ;
+  @self::forFactoryItself
   static factory •(dynamic p) → self::Foo
     let dynamic #redirecting_factory = self::Foo::named in invalid-expression;
 }
diff --git a/pkg/front_end/testcases/regress/issue_36793.dart.legacy.expect b/pkg/front_end/testcases/regress/issue_36793.dart.legacy.expect
index 4735cfe..69ebf3f 100644
--- a/pkg/front_end/testcases/regress/issue_36793.dart.legacy.expect
+++ b/pkg/front_end/testcases/regress/issue_36793.dart.legacy.expect
@@ -13,6 +13,7 @@
 import "dart:core" as core;
 
 static const field core::int y = 42;
+@self::y
 static field core::int x;
 static method main() → dynamic {
   core::print(self::y);
diff --git a/pkg/front_end/testcases/regress/issue_36793.dart.legacy.transformed.expect b/pkg/front_end/testcases/regress/issue_36793.dart.legacy.transformed.expect
index 4735cfe..69ebf3f 100644
--- a/pkg/front_end/testcases/regress/issue_36793.dart.legacy.transformed.expect
+++ b/pkg/front_end/testcases/regress/issue_36793.dart.legacy.transformed.expect
@@ -13,6 +13,7 @@
 import "dart:core" as core;
 
 static const field core::int y = 42;
+@self::y
 static field core::int x;
 static method main() → dynamic {
   core::print(self::y);
diff --git a/pkg/front_end/testcases/regress/issue_36793.dart.outline.expect b/pkg/front_end/testcases/regress/issue_36793.dart.outline.expect
index 659526a..b13488b 100644
--- a/pkg/front_end/testcases/regress/issue_36793.dart.outline.expect
+++ b/pkg/front_end/testcases/regress/issue_36793.dart.outline.expect
@@ -13,6 +13,7 @@
 import "dart:core" as core;
 
 static const field core::int y;
+@self::y
 static field core::int x;
 static method main() → dynamic
   ;
diff --git a/pkg/front_end/testcases/regress/issue_36793.dart.strong.expect b/pkg/front_end/testcases/regress/issue_36793.dart.strong.expect
index 4735cfe..69ebf3f 100644
--- a/pkg/front_end/testcases/regress/issue_36793.dart.strong.expect
+++ b/pkg/front_end/testcases/regress/issue_36793.dart.strong.expect
@@ -13,6 +13,7 @@
 import "dart:core" as core;
 
 static const field core::int y = 42;
+@self::y
 static field core::int x;
 static method main() → dynamic {
   core::print(self::y);
diff --git a/pkg/front_end/testcases/regress/issue_36793.dart.strong.transformed.expect b/pkg/front_end/testcases/regress/issue_36793.dart.strong.transformed.expect
index 4735cfe..69ebf3f 100644
--- a/pkg/front_end/testcases/regress/issue_36793.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/regress/issue_36793.dart.strong.transformed.expect
@@ -13,6 +13,7 @@
 import "dart:core" as core;
 
 static const field core::int y = 42;
+@self::y
 static field core::int x;
 static method main() → dynamic {
   core::print(self::y);
diff --git a/pkg/front_end/tool/fasta_perf.dart b/pkg/front_end/tool/fasta_perf.dart
index bd1ed2c..0ba6da6 100644
--- a/pkg/front_end/tool/fasta_perf.dart
+++ b/pkg/front_end/tool/fasta_perf.dart
@@ -217,7 +217,7 @@
 
   // Note: this method converts the body to kernel, so we skip that here.
   @override
-  finishFunction(annotations, formals, asyncModifier, body) {}
+  finishFunction(formals, asyncModifier, body) {}
 }
 
 // Invoke the fasta kernel generator for the program starting in [entryUri]
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 30a5f5c..0a9e01b 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -192,6 +192,11 @@
   Uri get fileUri;
 }
 
+abstract class Annotatable {
+  List<Expression> get annotations;
+  void addAnnotation(Expression node);
+}
+
 /// Indirection between a reference and its definition.
 ///
 /// There is only one reference object per [NamedNode].
@@ -267,7 +272,8 @@
 // ------------------------------------------------------------------------
 
 @coq
-class Library extends NamedNode implements Comparable<Library>, FileUriNode {
+class Library extends NamedNode
+    implements Annotatable, Comparable<Library>, FileUriNode {
   /// An import path to this library.
   ///
   /// The [Uri] should have the `dart`, `package`, `app`, or `file` scheme.
@@ -721,7 +727,7 @@
 /// rule directly, as doing so can obstruct transformations.  It is possible to
 /// transform a mixin application to become a regular class, and vice versa.
 @coq
-class Class extends NamedNode implements FileUriNode {
+class Class extends NamedNode implements Annotatable, FileUriNode {
   /// Start offset of the class in the source file it comes from.
   ///
   /// Note that this includes annotations if any.
@@ -1086,7 +1092,7 @@
 // ------------------------------------------------------------------------
 
 @coq
-abstract class Member extends NamedNode implements FileUriNode {
+abstract class Member extends NamedNode implements Annotatable, FileUriNode {
   /// End offset in the source file it comes from.
   ///
   /// Valid values are from 0 and up, or -1 ([TreeNode.noOffset]) if the file