Unit test some corner cases of annotation resolution.

Currently, if a type parameter of a class, mixin, or extension
declaration has an annotation, that annotation is resolved in the type
parameter scope of the class, mixin, or extension declaration (meaning
that static methods are not in scope), however if scope lookup fails,
then static methods are accessible via implicit `this`.

AFAICT, this odd behavior is not spec compliant, so I've filed
https://github.com/dart-lang/language/issues/1790 to discuss the
possibilty of changing it.  However, for now I want to unit test the
behavior in order to ensure that if we make any changes to the
analyzer that affect it, the change won't go unnoticed.

Bug: https://github.com/dart-lang/language/issues/1790
Change-Id: I02df7e6e8939ecc6222085f28a6d6b7d072179c1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/209662
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart b/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart
index d5538cc..2b0b5a5 100644
--- a/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart
@@ -18,6 +18,61 @@
 @reflectiveTest
 class UndefinedIdentifierTest extends PubPackageResolutionTest
     with WithoutNullSafetyMixin {
+  test_annotation_favors_scope_resolution_over_this_resolution_class() async {
+    // If an annotation on a class type parameter cannot be resolved using the
+    // normal scope resolution mechanism, it is resolved via implicit `this`.
+    // Note: this behavior doesn't match the spec so we may change it - see
+    // https://github.com/dart-lang/language/issues/1790
+    await assertNoErrorsInCode('''
+class C<@Annotation.function(foo) @Annotation.type(B) T> {
+  static void foo() {}
+  static void B() {}
+}
+class B {}
+class Annotation {
+  const Annotation.function(void Function() f);
+  const Annotation.type(Type t);
+}
+''');
+  }
+
+  test_annotation_favors_scope_resolution_over_this_resolution_extension() async {
+    // If an annotation on an extension type parameter cannot be resolved using
+    // the normal scope resolution mechanism, it is resolved via implicit
+    // `this`.  Note: this behavior doesn't match the spec so we may change it -
+    // see https://github.com/dart-lang/language/issues/1790
+    await assertNoErrorsInCode('''
+extension E<@Annotation.function(foo) @Annotation.type(B) T> on C {}
+class C {
+  static void foo() {}
+  static void B() {}
+}
+class B {}
+class Annotation {
+  const Annotation.function(void Function() f);
+  const Annotation.type(Type t);
+}
+''');
+  }
+
+  test_annotation_favors_scope_resolution_over_this_resolution_mixin() async {
+    // If an annotation on a mixin type parameter cannot be resolved using the
+    // normal scope resolution mechanism, it is resolved via implicit `this`.
+    // Note: this behavior doesn't match the spec so we may change it - see
+    // https://github.com/dart-lang/language/issues/1790
+    await assertNoErrorsInCode('''
+mixin M<@Annotation.function(foo) @Annotation.type(B) T> {
+  static void foo() {}
+  static void B() {}
+}
+class B {}
+class Annotation {
+  const Annotation.function(void Function() f);
+  const Annotation.type(Type t);
+}
+''');
+  }
+
   test_annotation_references_static_method_in_class() async {
     await assertErrorsInCode('''
 @Annotation(foo)
@@ -33,6 +88,21 @@
     ]);
   }
 
+  test_annotation_references_static_method_in_class_from_type_parameter() async {
+    // It is allowed for an annotation of a class type parameter to refer to
+    // a method in a class (note: this doesn't match the spec but we currently
+    // test it to make sure we match CFE behavior - see
+    // https://github.com/dart-lang/language/issues/1790)
+    await assertNoErrorsInCode('''
+class C<@Annotation(foo) T> {
+  static void foo() {}
+}
+class Annotation {
+  const Annotation(dynamic d);
+}
+''');
+  }
+
   test_annotation_references_static_method_in_extension() async {
     await assertErrorsInCode('''
 @Annotation(foo)
@@ -48,6 +118,21 @@
     ]);
   }
 
+  test_annotation_references_static_method_in_extension_from_type_parameter() async {
+    // It is allowed for an annotation of a mixin type parameter to refer to
+    // a method in a class (note: this doesn't match the spec but we currently
+    // test it to make sure we match CFE behavior - see
+    // https://github.com/dart-lang/language/issues/1790)
+    await assertNoErrorsInCode('''
+extension E<@Annotation(foo) T> on T {
+  static void foo() {}
+}
+class Annotation {
+  const Annotation(dynamic d);
+}
+''');
+  }
+
   test_annotation_references_static_method_in_mixin() async {
     await assertErrorsInCode('''
 @Annotation(foo)
@@ -63,6 +148,21 @@
     ]);
   }
 
+  test_annotation_references_static_method_in_mixin_from_type_parameter() async {
+    // It is allowed for an annotation of a mixin type parameter to refer to
+    // a method in a class (note: this doesn't match the spec but we currently
+    // test it to make sure we match CFE behavior - see
+    // https://github.com/dart-lang/language/issues/1790)
+    await assertNoErrorsInCode('''
+mixin M<@Annotation(foo) T> {
+  static void foo() {}
+}
+class Annotation {
+  const Annotation(dynamic d);
+}
+''');
+  }
+
   @failingTest
   test_commentReference() async {
     await assertErrorsInCode('''