[fasta] Reland "Handle annotations on formals and variables"

The original CL: https://dart-review.googlesource.com/c/sdk/+/51840

Bug: http://dartbug.com/28434
Change-Id: I0812ef4223337f5bf72218dfac0f26f76a6b9df1
Reviewed-on: https://dart-review.googlesource.com/52449
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
Reviewed-by: Jenny Messerly <jmesserly@google.com>
diff --git a/pkg/dev_compiler/test/nullable_inference_test.dart b/pkg/dev_compiler/test/nullable_inference_test.dart
index 742508b..fd456d2 100644
--- a/pkg/dev_compiler/test/nullable_inference_test.dart
+++ b/pkg/dev_compiler/test/nullable_inference_test.dart
@@ -413,27 +413,25 @@
       useAnnotations = false;
     });
     var imports = "import 'package:meta/meta.dart';";
-    group('(kernel annotation bug)', () {
-      test('variable wihout initializer', () async {
-        await expectNotNull('$imports main() { @notNull var x; print(x); }',
-            ''); // should be: 'x'
+    group('(previously known kernel annotation bug)', () {
+      test('variable without initializer', () async {
+        await expectNotNull(
+            '$imports main() { @notNull var x; print(x); }', 'x');
       });
       test('variable with initializer', () async {
         // TODO(jmesserly): this does not work in the Analyzer backend.
         await expectNotNull(
-            '$imports main() { @notNull var x = null; print(x); }',
-            ''); // should be: 'x'
+            '$imports main() { @notNull var x = null; print(x); }', 'x');
       });
       test('parameters', () async {
         await expectNotNull(
             '$imports f(@notNull x, [@notNull y, @notNull z = 42]) '
             '{ x; y; z; }',
-            '42, z'); // should be: '42, x, y, z'
+            '42, x, y, z');
       });
       test('named parameters', () async {
         await expectNotNull(
-            '$imports f({@notNull x, @notNull y: 42}) { x; y; }',
-            '42, y'); // should be: '42, x, y'
+            '$imports f({@notNull x, @notNull y: 42}) { x; y; }', '42, x, y');
       });
     });
 
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 02d9745..c8c3b63 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -1699,7 +1699,14 @@
     constantContext = pop();
     currentLocalVariableType = pop();
     currentLocalVariableModifiers = pop();
-    pop(); // Metadata.
+    List<Expression> annotations = pop();
+    if (annotations != null) {
+      for (VariableDeclaration variable in variables) {
+        for (Expression annotation in annotations) {
+          variable.addAnnotation(annotation);
+        }
+      }
+    }
     if (variables.length != 1) {
       push(variables);
     } else {
@@ -2134,7 +2141,7 @@
     }
     bool isConst = (modifiers & constMask) != 0;
     bool isFinal = (modifiers & finalMask) != 0;
-    ignore(Unhandled.Metadata);
+    List annotations = pop();
     VariableDeclaration variable;
     if (!inCatchClause &&
         functionNestingLevel == 0 &&
@@ -2162,6 +2169,14 @@
         variable.fileOffset = offsetForToken(name.token);
       }
     }
+    if (annotations != null) {
+      if (functionNestingLevel == 0) {
+        _typeInferrer.inferMetadata(this, annotations);
+      }
+      for (Expression annotation in annotations) {
+        variable.addAnnotation(annotation);
+      }
+    }
     push(variable);
   }
 
@@ -2942,8 +2957,9 @@
     returnType ??= const DynamicType();
     exitFunction();
     List<TypeParameter> typeParameters = typeVariableBuildersToKernel(pop());
+    List<Expression> annotations;
     if (!isFunctionExpression) {
-      pop(); // Metadata.
+      annotations = pop(); // Metadata.
     }
     FunctionNode function = formals.addToFunction(new FunctionNode(body,
         typeParameters: typeParameters,
@@ -2954,6 +2970,11 @@
 
     if (declaration is FunctionDeclaration) {
       VariableDeclaration variable = declaration.variable;
+      if (annotations != null) {
+        for (Expression annotation in annotations) {
+          variable.addAnnotation(annotation);
+        }
+      }
       ShadowFunctionDeclaration.setHasImplicitReturnType(
           declaration, hasImplicitReturnType);
 
@@ -3545,8 +3566,7 @@
     debugEvent("TypeVariable");
     DartType bound = pop();
     Identifier name = pop();
-    // TODO(ahe): Do not discard metadata.
-    pop(); // Metadata.
+    List<Expression> annotations = pop();
     KernelTypeVariableBuilder variable;
     Object inScope = scopeLookup(scope, name.name, token);
     if (inScope is TypeDeclarationAccessor) {
@@ -3558,6 +3578,12 @@
           name.name, library, offsetForToken(name.token), null);
     }
     variable.parameter.bound = bound;
+    if (annotations != null) {
+      _typeInferrer.inferMetadata(this, annotations);
+      for (Expression annotation in annotations) {
+        variable.parameter.addAnnotation(annotation);
+      }
+    }
     push(variable
       ..finish(library, library.loader.target.objectClassBuilder,
           library.loader.target.dynamicType));
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
index b4c9192..71372b9 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
@@ -34,6 +34,7 @@
 import 'package:front_end/src/fasta/type_inference/type_schema_elimination.dart';
 import 'package:front_end/src/fasta/type_inference/type_schema_environment.dart';
 import 'package:kernel/ast.dart' hide InvalidExpression, InvalidInitializer;
+import 'package:kernel/clone.dart' show CloneVisitor;
 import 'package:kernel/frontend/accessors.dart';
 import 'package:kernel/type_algebra.dart';
 
@@ -879,6 +880,7 @@
     }
     inferrer.inferStatement(body);
     if (_declaresVariable) {
+      inferrer.inferMetadataKeepingHelper(variable.annotations);
       var tempVar =
           new VariableDeclaration(null, type: inferredType, isFinal: true);
       var variableGet = new VariableGet(tempVar)
@@ -941,6 +943,7 @@
 
   @override
   void _inferStatement(ShadowTypeInferrer inferrer) {
+    inferrer.inferMetadataKeepingHelper(variable.annotations);
     inferrer.inferLocalFunction(
         function,
         null,
@@ -2270,6 +2273,26 @@
 
   @override
   void _inferStatement(ShadowTypeInferrer inferrer) {
+    inferrer.inferMetadataKeepingHelper(annotations);
+
+    // After the inference was done on the annotations, we may clone them for
+    // this instance of VariableDeclaration in order to avoid having the same
+    // annotation node for two VariableDeclaration nodes in a situation like
+    // the following:
+    //
+    //     class Foo { const Foo(List<String> list); }
+    //
+    //     @Foo(const [])
+    //     var x, y;
+    CloneVisitor cloner = new CloneVisitor();
+    for (int i = 0; i < annotations.length; ++i) {
+      Expression annotation = annotations[i];
+      if (annotation.parent != this) {
+        annotations[i] = cloner.clone(annotation);
+        annotations[i].parent = this;
+      }
+    }
+
     var declaredType = _implicitlyTyped ? const UnknownType() : type;
     DartType inferredType;
     DartType initializerType;
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 4971952..719ca60 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -337,6 +337,10 @@
   /// Performs type inference on the given metadata annotations.
   void inferMetadata(BuilderHelper helper, List<Expression> annotations);
 
+  /// Performs type inference on the given metadata annotations keeping the
+  /// existing helper if possible.
+  void inferMetadataKeepingHelper(List<Expression> annotations);
+
   /// Performs type inference on the given function parameter initializer
   /// expression.
   void inferParameterInitializer(
@@ -374,6 +378,9 @@
   void inferMetadata(BuilderHelper helper, List<Expression> annotations) {}
 
   @override
+  void inferMetadataKeepingHelper(List<Expression> annotations) {}
+
+  @override
   void inferParameterInitializer(
       BuilderHelper helper, Expression initializer, DartType declaredType) {}
 }
@@ -1100,6 +1107,7 @@
       var positionalParameters = function.positionalParameters;
       for (var i = 0; i < positionalParameters.length; i++) {
         var parameter = positionalParameters[i];
+        inferMetadataKeepingHelper(parameter.annotations);
         if (i >= function.requiredParameterCount &&
             parameter.initializer == null) {
           parameter.initializer = new ShadowNullLiteral()..parent = parameter;
@@ -1109,6 +1117,7 @@
         }
       }
       for (var parameter in function.namedParameters) {
+        inferMetadataKeepingHelper(parameter.annotations);
         if (parameter.initializer == null) {
           parameter.initializer = new ShadowNullLiteral()..parent = parameter;
         }
@@ -1239,14 +1248,25 @@
   void inferMetadata(BuilderHelper helper, List<Expression> annotations) {
     if (annotations != null) {
       this.helper = helper;
+      inferMetadataKeepingHelper(annotations);
+      this.helper = null;
+    }
+  }
+
+  @override
+  void inferMetadataKeepingHelper(List<Expression> annotations) {
+    if (annotations != null) {
       // Place annotations in a temporary list literal so that they will have a
       // parent.  This is necessary in case any of the annotations need to get
       // replaced during type inference.
+      var parents = annotations.map((e) => e.parent).toList();
       new ListLiteral(annotations);
       for (var annotation in annotations) {
         inferExpression(annotation, const UnknownType(), false);
       }
-      this.helper = null;
+      for (int i = 0; i < annotations.length; ++i) {
+        annotations[i].parent = parents[i];
+      }
     }
   }
 
diff --git a/pkg/front_end/testcases/annotation_variable_declaration.dart b/pkg/front_end/testcases/annotation_variable_declaration.dart
new file mode 100644
index 0000000..c64fad7
--- /dev/null
+++ b/pkg/front_end/testcases/annotation_variable_declaration.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2018, 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.
+
+const int foo = 42;
+
+class Bar {
+  const Bar();
+  const Bar.named(x);
+}
+
+class Baz {
+  Baz(@foo constructorFormal);
+
+  factory Baz.bazFactory(@foo factoryFormal) => null;
+
+  fisk(@foo formal1, @Bar() formal2, @Bar.named(foo) formal3,
+      @foo @Bar.named(foo) formal4,
+      [@foo optional]) {
+    @foo
+    var local1;
+
+    @Bar()
+    var local2;
+
+    @Bar.named(foo)
+    var local3;
+
+    @foo
+    @Bar.named(foo)
+    var local4;
+
+    @foo
+    var localWithInitializer = "hello";
+
+    @foo
+    @Bar.named(foo)
+    var localGroupPart1, localGroupPart2;
+
+    naebdyr(@foo nestedFormal) => null;
+
+    var roedmus = (@foo closureFormal) => null;
+  }
+
+  hest({@foo named}) => null;
+}
+
+typedef hest_t({@foo named});
+
+main() {}
diff --git a/pkg/front_end/testcases/annotation_variable_declaration.dart.direct.expect b/pkg/front_end/testcases/annotation_variable_declaration.dart.direct.expect
new file mode 100644
index 0000000..95f1aa8
--- /dev/null
+++ b/pkg/front_end/testcases/annotation_variable_declaration.dart.direct.expect
@@ -0,0 +1,36 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+typedef hest_t = ({named: dynamic}) → dynamic;
+class Bar extends core::Object {
+  const constructor •() → void
+    : super core::Object::•()
+    ;
+  const constructor named(dynamic x) → void
+    : super core::Object::•()
+    ;
+}
+class Baz extends core::Object {
+  constructor •(@self::foo dynamic constructorFormal) → void
+    : super core::Object::•()
+    ;
+  static factory bazFactory(@self::foo dynamic factoryFormal) → self::Baz
+    return null;
+  method fisk(@self::foo dynamic formal1, @self::Bar::•() dynamic formal2, @self::Bar::named(self::foo) dynamic formal3, @self::foo @self::Bar::named(self::foo) dynamic formal4, [@self::foo dynamic optional = null]) → dynamic {
+    @self::foo dynamic local1;
+    @self::Bar::•() dynamic local2;
+    @self::Bar::named(self::foo) dynamic local3;
+    @self::foo @self::Bar::named(self::foo) dynamic local4;
+    @self::foo dynamic localWithInitializer = "hello";
+    @self::foo @self::Bar::named(self::foo) dynamic localGroupPart1;
+    @self::foo @self::Bar::named(self::foo) dynamic localGroupPart2;
+    function naebdyr(@self::foo dynamic nestedFormal) → dynamic
+      return null;
+    dynamic roedmus = (@self::foo dynamic closureFormal) → dynamic => null;
+  }
+  method hest({@self::foo dynamic named = null}) → dynamic
+    return null;
+}
+static const field core::int foo = 42;
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/annotation_variable_declaration.dart.direct.transformed.expect b/pkg/front_end/testcases/annotation_variable_declaration.dart.direct.transformed.expect
new file mode 100644
index 0000000..95f1aa8
--- /dev/null
+++ b/pkg/front_end/testcases/annotation_variable_declaration.dart.direct.transformed.expect
@@ -0,0 +1,36 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+typedef hest_t = ({named: dynamic}) → dynamic;
+class Bar extends core::Object {
+  const constructor •() → void
+    : super core::Object::•()
+    ;
+  const constructor named(dynamic x) → void
+    : super core::Object::•()
+    ;
+}
+class Baz extends core::Object {
+  constructor •(@self::foo dynamic constructorFormal) → void
+    : super core::Object::•()
+    ;
+  static factory bazFactory(@self::foo dynamic factoryFormal) → self::Baz
+    return null;
+  method fisk(@self::foo dynamic formal1, @self::Bar::•() dynamic formal2, @self::Bar::named(self::foo) dynamic formal3, @self::foo @self::Bar::named(self::foo) dynamic formal4, [@self::foo dynamic optional = null]) → dynamic {
+    @self::foo dynamic local1;
+    @self::Bar::•() dynamic local2;
+    @self::Bar::named(self::foo) dynamic local3;
+    @self::foo @self::Bar::named(self::foo) dynamic local4;
+    @self::foo dynamic localWithInitializer = "hello";
+    @self::foo @self::Bar::named(self::foo) dynamic localGroupPart1;
+    @self::foo @self::Bar::named(self::foo) dynamic localGroupPart2;
+    function naebdyr(@self::foo dynamic nestedFormal) → dynamic
+      return null;
+    dynamic roedmus = (@self::foo dynamic closureFormal) → dynamic => null;
+  }
+  method hest({@self::foo dynamic named = null}) → dynamic
+    return null;
+}
+static const field core::int foo = 42;
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/annotation_variable_declaration.dart.outline.expect b/pkg/front_end/testcases/annotation_variable_declaration.dart.outline.expect
new file mode 100644
index 0000000..6382194
--- /dev/null
+++ b/pkg/front_end/testcases/annotation_variable_declaration.dart.outline.expect
@@ -0,0 +1,24 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+typedef hest_t = ({named: dynamic}) → dynamic;
+class Bar extends core::Object {
+  const constructor •() → void
+    ;
+  const constructor named(dynamic x) → void
+    ;
+}
+class Baz extends core::Object {
+  constructor •(dynamic constructorFormal) → void
+    ;
+  static factory bazFactory(dynamic factoryFormal) → self::Baz
+    ;
+  method fisk(dynamic formal1, dynamic formal2, dynamic formal3, dynamic formal4, [dynamic optional]) → dynamic
+    ;
+  method hest({dynamic named}) → dynamic
+    ;
+}
+static const field core::int foo;
+static method main() → dynamic
+  ;
diff --git a/pkg/front_end/testcases/annotation_variable_declaration.dart.strong.expect b/pkg/front_end/testcases/annotation_variable_declaration.dart.strong.expect
new file mode 100644
index 0000000..9ab4cb7
--- /dev/null
+++ b/pkg/front_end/testcases/annotation_variable_declaration.dart.strong.expect
@@ -0,0 +1,36 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+typedef hest_t = ({named: dynamic}) → dynamic;
+class Bar extends core::Object {
+  const constructor •() → void
+    : super core::Object::•()
+    ;
+  const constructor named(dynamic x) → void
+    : super core::Object::•()
+    ;
+}
+class Baz extends core::Object {
+  constructor •(@self::foo dynamic constructorFormal) → void
+    : super core::Object::•()
+    ;
+  static factory bazFactory(@self::foo dynamic factoryFormal) → self::Baz
+    return null;
+  method fisk(@self::foo dynamic formal1, @self::Bar::•() dynamic formal2, @self::Bar::named(self::foo) dynamic formal3, @self::foo @self::Bar::named(self::foo) dynamic formal4, [@self::foo dynamic optional = null]) → dynamic {
+    @self::foo dynamic local1;
+    @self::Bar::•() dynamic local2;
+    @self::Bar::named(self::foo) dynamic local3;
+    @self::foo @self::Bar::named(self::foo) dynamic local4;
+    @self::foo core::String localWithInitializer = "hello";
+    @self::foo @self::Bar::named(self::foo) dynamic localGroupPart1;
+    @self::foo @self::Bar::named(self::foo) dynamic localGroupPart2;
+    function naebdyr(@self::foo dynamic nestedFormal) → core::Null
+      return null;
+    (dynamic) → core::Null roedmus = (@self::foo dynamic closureFormal) → core::Null => null;
+  }
+  method hest({@self::foo dynamic named = null}) → dynamic
+    return null;
+}
+static const field core::int foo = 42;
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/annotation_variable_declaration.dart.strong.transformed.expect b/pkg/front_end/testcases/annotation_variable_declaration.dart.strong.transformed.expect
new file mode 100644
index 0000000..9ab4cb7
--- /dev/null
+++ b/pkg/front_end/testcases/annotation_variable_declaration.dart.strong.transformed.expect
@@ -0,0 +1,36 @@
+library;
+import self as self;
+import "dart:core" as core;
+
+typedef hest_t = ({named: dynamic}) → dynamic;
+class Bar extends core::Object {
+  const constructor •() → void
+    : super core::Object::•()
+    ;
+  const constructor named(dynamic x) → void
+    : super core::Object::•()
+    ;
+}
+class Baz extends core::Object {
+  constructor •(@self::foo dynamic constructorFormal) → void
+    : super core::Object::•()
+    ;
+  static factory bazFactory(@self::foo dynamic factoryFormal) → self::Baz
+    return null;
+  method fisk(@self::foo dynamic formal1, @self::Bar::•() dynamic formal2, @self::Bar::named(self::foo) dynamic formal3, @self::foo @self::Bar::named(self::foo) dynamic formal4, [@self::foo dynamic optional = null]) → dynamic {
+    @self::foo dynamic local1;
+    @self::Bar::•() dynamic local2;
+    @self::Bar::named(self::foo) dynamic local3;
+    @self::foo @self::Bar::named(self::foo) dynamic local4;
+    @self::foo core::String localWithInitializer = "hello";
+    @self::foo @self::Bar::named(self::foo) dynamic localGroupPart1;
+    @self::foo @self::Bar::named(self::foo) dynamic localGroupPart2;
+    function naebdyr(@self::foo dynamic nestedFormal) → core::Null
+      return null;
+    (dynamic) → core::Null roedmus = (@self::foo dynamic closureFormal) → core::Null => null;
+  }
+  method hest({@self::foo dynamic named = null}) → dynamic
+    return null;
+}
+static const field core::int foo = 42;
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/ast_builder.status b/pkg/front_end/testcases/ast_builder.status
index 0007d0b..cbb6e2d 100644
--- a/pkg/front_end/testcases/ast_builder.status
+++ b/pkg/front_end/testcases/ast_builder.status
@@ -11,13 +11,7 @@
 inference/abstract_class_instantiation: Fail
 inference/constructors_too_many_positional_arguments: Fail
 inference/downwards_inference_annotations: Crash
-inference/downwards_inference_annotations_for_loop_variable: Fail
-inference/downwards_inference_annotations_locals: Fail
-inference/downwards_inference_annotations_locals_referring_to_locals: Fail
-inference/downwards_inference_annotations_parameter: Fail
-inference/downwards_inference_annotations_parameter_local: Fail
 inference/downwards_inference_annotations_type_variable: Fail
-inference/downwards_inference_annotations_type_variable_local: Fail
 inference/downwards_inference_annotations_typedef: Crash
 inference/downwards_inference_on_function_of_t_using_the_t: Fail
 inference/downwards_inference_yield_yield_star: Fail
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.direct.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.direct.expect
index 84e4744..c7a86c1 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.direct.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.direct.expect
@@ -8,9 +8,9 @@
     ;
 }
 static method test() → void {
-  for (core::int i = 0; i.<(1); i = i.+(1)) {
+  for (@self::Foo::•(const <dynamic>[]) core::int i = 0; i.<(1); i = i.+(1)) {
   }
-  for (core::int i in <dynamic>[0]) {
+  for (@self::Foo::•(const <dynamic>[]) core::int i in <dynamic>[0]) {
   }
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.direct.transformed.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.direct.transformed.expect
index 84e4744..c7a86c1 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.direct.transformed.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.direct.transformed.expect
@@ -8,9 +8,9 @@
     ;
 }
 static method test() → void {
-  for (core::int i = 0; i.<(1); i = i.+(1)) {
+  for (@self::Foo::•(const <dynamic>[]) core::int i = 0; i.<(1); i = i.+(1)) {
   }
-  for (core::int i in <dynamic>[0]) {
+  for (@self::Foo::•(const <dynamic>[]) core::int i in <dynamic>[0]) {
   }
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.strong.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.strong.expect
new file mode 100644
index 0000000..e4d43d3
--- /dev/null
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.strong.expect
@@ -0,0 +1,16 @@
+library test;
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::Object {
+  const constructor •(core::List<core::String> l) → void
+    : super core::Object::•()
+    ;
+}
+static method test() → void {
+  for (@self::Foo::•(const <core::String>[]) core::int i = 0; i.{core::num::<}(1); i = i.{core::num::+}(1)) {
+  }
+  for (@self::Foo::•(const <core::String>[]) core::int i in <core::int>[0]) {
+  }
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.strong.transformed.expect
new file mode 100644
index 0000000..e4d43d3
--- /dev/null
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_for_loop_variable.dart.strong.transformed.expect
@@ -0,0 +1,16 @@
+library test;
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::Object {
+  const constructor •(core::List<core::String> l) → void
+    : super core::Object::•()
+    ;
+}
+static method test() → void {
+  for (@self::Foo::•(const <core::String>[]) core::int i = 0; i.{core::num::<}(1); i = i.{core::num::+}(1)) {
+  }
+  for (@self::Foo::•(const <core::String>[]) core::int i in <core::int>[0]) {
+  }
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_locals.dart.strong.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals.dart.strong.expect
new file mode 100644
index 0000000..70f1ea3
--- /dev/null
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals.dart.strong.expect
@@ -0,0 +1,15 @@
+library test;
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::Object {
+  const constructor •(core::List<core::String> l) → void
+    : super core::Object::•()
+    ;
+}
+static method test() → void {
+  @self::Foo::•(const <core::String>[]) dynamic x;
+  @self::Foo::•(const <core::String>[])
+  function f() → void {}
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_locals.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals.dart.strong.transformed.expect
new file mode 100644
index 0000000..70f1ea3
--- /dev/null
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals.dart.strong.transformed.expect
@@ -0,0 +1,15 @@
+library test;
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::Object {
+  const constructor •(core::List<core::String> l) → void
+    : super core::Object::•()
+    ;
+}
+static method test() → void {
+  @self::Foo::•(const <core::String>[]) dynamic x;
+  @self::Foo::•(const <core::String>[])
+  function f() → void {}
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart
index 9d389ca..b28b2ed 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart
@@ -14,6 +14,11 @@
 
   @Foo(/*@typeArgs=int*/ const [x])
   var /*@type=dynamic*/ y;
+
+  @Foo(/*@typeArgs=int*/ const [x])
+  void bar() {}
+
+  void baz(@Foo(/*@typeArgs=int*/ const [x]) dynamic formal) {}
 }
 
 main() {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.direct.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.direct.expect
index d3afdb6..a0385be 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.direct.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.direct.expect
@@ -9,6 +9,9 @@
 }
 static method test() → void {
   const dynamic x = 0;
-  dynamic y;
+  @self::Foo::•(const <dynamic>[x]) dynamic y;
+  @self::Foo::•(const <dynamic>[x])
+  function bar() → void {}
+  function baz(@self::Foo::•(const <dynamic>[x]) dynamic formal) → void {}
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.direct.transformed.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.direct.transformed.expect
index d3afdb6..a0385be 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.direct.transformed.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.direct.transformed.expect
@@ -9,6 +9,9 @@
 }
 static method test() → void {
   const dynamic x = 0;
-  dynamic y;
+  @self::Foo::•(const <dynamic>[x]) dynamic y;
+  @self::Foo::•(const <dynamic>[x])
+  function bar() → void {}
+  function baz(@self::Foo::•(const <dynamic>[x]) dynamic formal) → void {}
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.strong.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.strong.expect
new file mode 100644
index 0000000..eac77b2
--- /dev/null
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.strong.expect
@@ -0,0 +1,17 @@
+library test;
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::Object {
+  const constructor •(dynamic l) → void
+    : super core::Object::•()
+    ;
+}
+static method test() → void {
+  const core::int x = 0;
+  @self::Foo::•(const <core::int>[x]) dynamic y;
+  @self::Foo::•(const <core::int>[x])
+  function bar() → void {}
+  function baz(@self::Foo::•(const <core::int>[x]) dynamic formal) → void {}
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.strong.transformed.expect
new file mode 100644
index 0000000..eac77b2
--- /dev/null
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_locals_referring_to_locals.dart.strong.transformed.expect
@@ -0,0 +1,17 @@
+library test;
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::Object {
+  const constructor •(dynamic l) → void
+    : super core::Object::•()
+    ;
+}
+static method test() → void {
+  const core::int x = 0;
+  @self::Foo::•(const <core::int>[x]) dynamic y;
+  @self::Foo::•(const <core::int>[x])
+  function bar() → void {}
+  function baz(@self::Foo::•(const <core::int>[x]) dynamic formal) → void {}
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.direct.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.direct.expect
index 306b574..9d2c6e3 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.direct.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.direct.expect
@@ -11,7 +11,7 @@
   synthetic constructor •() → void
     : super core::Object::•()
     ;
-  method m(dynamic x) → void {}
+  method m(@self::Foo::•(const <dynamic>[]) dynamic x) → void {}
 }
-static method f(dynamic x) → void {}
+static method f(@self::Foo::•(const <dynamic>[]) dynamic x) → void {}
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.direct.transformed.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.direct.transformed.expect
index 306b574..9d2c6e3 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.direct.transformed.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.direct.transformed.expect
@@ -11,7 +11,7 @@
   synthetic constructor •() → void
     : super core::Object::•()
     ;
-  method m(dynamic x) → void {}
+  method m(@self::Foo::•(const <dynamic>[]) dynamic x) → void {}
 }
-static method f(dynamic x) → void {}
+static method f(@self::Foo::•(const <dynamic>[]) dynamic x) → void {}
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.strong.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.strong.expect
new file mode 100644
index 0000000..7775e23
--- /dev/null
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.strong.expect
@@ -0,0 +1,17 @@
+library test;
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::Object {
+  const constructor •(core::List<core::String> l) → void
+    : super core::Object::•()
+    ;
+}
+class C extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  method m(@self::Foo::•(const <core::String>[]) dynamic x) → void {}
+}
+static method f(@self::Foo::•(const <core::String>[]) dynamic x) → void {}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.strong.transformed.expect
new file mode 100644
index 0000000..7775e23
--- /dev/null
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter.dart.strong.transformed.expect
@@ -0,0 +1,17 @@
+library test;
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::Object {
+  const constructor •(core::List<core::String> l) → void
+    : super core::Object::•()
+    ;
+}
+class C extends core::Object {
+  synthetic constructor •() → void
+    : super core::Object::•()
+    ;
+  method m(@self::Foo::•(const <core::String>[]) dynamic x) → void {}
+}
+static method f(@self::Foo::•(const <core::String>[]) dynamic x) → void {}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart
index 6cc32b3..e3198ae 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart
@@ -11,7 +11,7 @@
 
 void test() {
   void f(@Foo(/*@typeArgs=String*/ const []) /*@type=dynamic*/ x) {}
-  var /*@type=(dynamic) -> Null*/ x = /*@returnType=Null*/ (/*@type=dynamic*/ @Foo(/*@typeArgs=String*/ const [])
+  var /*@type=(dynamic) -> Null*/ x = /*@returnType=Null*/ (@Foo(/*@typeArgs=String*/ const []) /*@type=dynamic*/
       x) {};
 }
 
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.direct.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.direct.expect
index d53d84c..9163b9d 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.direct.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.direct.expect
@@ -8,7 +8,7 @@
     ;
 }
 static method test() → void {
-  function f(dynamic x) → void {}
-  dynamic x = (dynamic x) → dynamic {};
+  function f(@self::Foo::•(const <dynamic>[]) dynamic x) → void {}
+  dynamic x = (@self::Foo::•(const <dynamic>[]) dynamic x) → dynamic {};
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.direct.transformed.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.direct.transformed.expect
index d53d84c..9163b9d 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.direct.transformed.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.direct.transformed.expect
@@ -8,7 +8,7 @@
     ;
 }
 static method test() → void {
-  function f(dynamic x) → void {}
-  dynamic x = (dynamic x) → dynamic {};
+  function f(@self::Foo::•(const <dynamic>[]) dynamic x) → void {}
+  dynamic x = (@self::Foo::•(const <dynamic>[]) dynamic x) → dynamic {};
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.strong.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.strong.expect
new file mode 100644
index 0000000..90d59e7
--- /dev/null
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.strong.expect
@@ -0,0 +1,14 @@
+library test;
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::Object {
+  const constructor •(core::List<core::String> l) → void
+    : super core::Object::•()
+    ;
+}
+static method test() → void {
+  function f(@self::Foo::•(const <core::String>[]) dynamic x) → void {}
+  (dynamic) → core::Null x = (@self::Foo::•(const <core::String>[]) dynamic x) → core::Null {};
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.strong.transformed.expect
new file mode 100644
index 0000000..90d59e7
--- /dev/null
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_parameter_local.dart.strong.transformed.expect
@@ -0,0 +1,14 @@
+library test;
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::Object {
+  const constructor •(core::List<core::String> l) → void
+    : super core::Object::•()
+    ;
+}
+static method test() → void {
+  function f(@self::Foo::•(const <core::String>[]) dynamic x) → void {}
+  (dynamic) → core::Null x = (@self::Foo::•(const <core::String>[]) dynamic x) → core::Null {};
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart b/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart
index 3ec0b43..b048a1b 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart
@@ -11,7 +11,7 @@
 
 void test() {
   void f<@Foo(/*@typeArgs=String*/ const []) T>() {}
-  var /*@type=<T extends Object>() -> Null*/ x =
+  var /*@type=<@Foo::•(const <String>[]) T extends Object = dynamic>() -> Null*/ x =
       <@Foo(/*@typeArgs=String*/ const []) T> /*@returnType=Null*/ () {};
 }
 
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.direct.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.direct.expect
index 7f49df8..d94bf7a 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.direct.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.direct.expect
@@ -8,7 +8,7 @@
     ;
 }
 static method test() → void {
-  function f<T extends core::Object = dynamic>() → void {}
-  dynamic x = <T extends core::Object = dynamic>() → dynamic {};
+  function f<@self::Foo::•(const <dynamic>[]) T extends core::Object = dynamic>() → void {}
+  dynamic x = <@self::Foo::•(const <dynamic>[]) T extends core::Object = dynamic>() → dynamic {};
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.direct.transformed.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.direct.transformed.expect
index 7f49df8..d94bf7a 100644
--- a/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.direct.transformed.expect
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.direct.transformed.expect
@@ -8,7 +8,7 @@
     ;
 }
 static method test() → void {
-  function f<T extends core::Object = dynamic>() → void {}
-  dynamic x = <T extends core::Object = dynamic>() → dynamic {};
+  function f<@self::Foo::•(const <dynamic>[]) T extends core::Object = dynamic>() → void {}
+  dynamic x = <@self::Foo::•(const <dynamic>[]) T extends core::Object = dynamic>() → dynamic {};
 }
 static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.strong.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.strong.expect
new file mode 100644
index 0000000..657f6f6
--- /dev/null
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.strong.expect
@@ -0,0 +1,14 @@
+library test;
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::Object {
+  const constructor •(core::List<core::String> l) → void
+    : super core::Object::•()
+    ;
+}
+static method test() → void {
+  function f<@self::Foo::•(const <core::String>[]) T extends core::Object = dynamic>() → void {}
+  <@self::Foo::•(const <core::String>[]) T extends core::Object = dynamic>() → core::Null x = <@self::Foo::•(const <core::String>[]) T extends core::Object = dynamic>() → core::Null {};
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.strong.transformed.expect b/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.strong.transformed.expect
new file mode 100644
index 0000000..657f6f6
--- /dev/null
+++ b/pkg/front_end/testcases/inference/downwards_inference_annotations_type_variable_local.dart.strong.transformed.expect
@@ -0,0 +1,14 @@
+library test;
+import self as self;
+import "dart:core" as core;
+
+class Foo extends core::Object {
+  const constructor •(core::List<core::String> l) → void
+    : super core::Object::•()
+    ;
+}
+static method test() → void {
+  function f<@self::Foo::•(const <core::String>[]) T extends core::Object = dynamic>() → void {}
+  <@self::Foo::•(const <core::String>[]) T extends core::Object = dynamic>() → core::Null x = <@self::Foo::•(const <core::String>[]) T extends core::Object = dynamic>() → core::Null {};
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index 6f767dc..3ee0f85 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -77,13 +77,7 @@
 inference/constructors_infer_from_arguments_argument_not_assignable: TypeCheckError
 inference/constructors_too_many_positional_arguments: Fail # Issue #30040
 inference/do_not_infer_overridden_fields_that_explicitly_say_dynamic_infer: TypeCheckError
-inference/downwards_inference_annotations_for_loop_variable: Fail # Issue #28434
-inference/downwards_inference_annotations_locals: Fail # Issue #30031 and Issue #28434
-inference/downwards_inference_annotations_locals_referring_to_locals: Fail # Issue #28434
-inference/downwards_inference_annotations_parameter: Fail # Issue #28434
-inference/downwards_inference_annotations_parameter_local: Fail # Issue #28434
-inference/downwards_inference_annotations_type_variable: Fail # Issue #28434
-inference/downwards_inference_annotations_type_variable_local: Fail # Issue #28434
+inference/downwards_inference_annotations_type_variable: Fail # Issue 28981
 inference/downwards_inference_on_function_of_t_using_the_t: Fail # Issue #29798
 inference/downwards_inference_on_list_literals_infer_downwards: RuntimeError
 inference/downwards_inference_yield_yield_star: TypeCheckError
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 2d27d28..cc6aa3c 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -4451,11 +4451,13 @@
   accept1(StatementVisitor1 v, arg) => v.visitVariableDeclaration(this, arg);
 
   visitChildren(Visitor v) {
+    visitList(annotations, v);
     type?.accept(v);
     initializer?.accept(v);
   }
 
   transformChildren(Transformer v) {
+    transformList(annotations, v, this);
     type = v.visitDartType(type);
     if (initializer != null) {
       initializer = initializer.accept(v);
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 4ec4cbd..e039b20 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -869,11 +869,17 @@
     }
   }
 
-  void writeAnnotationList(List<Expression> nodes) {
+  void writeAnnotationList(List<Expression> nodes, {bool separateLines: true}) {
     for (Expression node in nodes) {
-      writeIndentation();
+      if (separateLines) {
+        writeIndentation();
+      }
       writeAnnotation(node);
-      endLine();
+      if (separateLines) {
+        endLine();
+      } else {
+        writeSpace();
+      }
     }
   }
 
@@ -1671,6 +1677,7 @@
   }
 
   visitFunctionDeclaration(FunctionDeclaration node) {
+    writeAnnotationList(node.variable.annotations);
     writeIndentation();
     writeWord('function');
     if (node.function != null) {
@@ -1685,7 +1692,7 @@
       {bool useVarKeyword: false}) {
     if (showOffsets) writeWord("[${node.fileOffset}]");
     if (showMetadata) writeMetadata(node);
-    writeAnnotationList(node.annotations);
+    writeAnnotationList(node.annotations, separateLines: false);
     writeModifier(node.isCovariant, 'covariant');
     writeModifier(node.isGenericCovariantImpl, 'generic-covariant-impl');
     writeModifier(
@@ -1836,7 +1843,7 @@
     writeModifier(node.isGenericCovariantImpl, 'generic-covariant-impl');
     writeModifier(
         node.isGenericCovariantInterface, 'generic-covariant-interface');
-    writeAnnotationList(node.annotations);
+    writeAnnotationList(node.annotations, separateLines: false);
     writeWord(getTypeParameterName(node));
     writeSpaced('extends');
     writeType(node.bound);