Initial support for macro(s) as built into analyzer.

Change-Id: Ib22317b1257b0a4d56d6ed35d5b6a13e39226c59
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/207720
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 666d9be..41a201a 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -2361,6 +2361,15 @@
   /// children of this element's parent.
   String get identifier => name!;
 
+  /// Return `true` if this element was created from a macro-generated code.
+  bool get isFromMacro {
+    return hasModifier(Modifier.IS_FROM_MACRO);
+  }
+
+  set isFromMacro(bool isFromMacro) {
+    setModifier(Modifier.IS_FROM_MACRO, isFromMacro);
+  }
+
   bool get isNonFunctionTypeAliasesEnabled {
     return library!.featureSet.isEnabled(Feature.nonfunction_type_aliases);
   }
@@ -4281,23 +4290,26 @@
   /// type being referred to is the return type.
   static const Modifier IMPLICIT_TYPE = Modifier('IMPLICIT_TYPE', 16);
 
+  /// Indicates that this element was created from macro-generated code.
+  static const Modifier IS_FROM_MACRO = Modifier('IS_FROM_MACRO', 17);
+
   /// Indicates that modifier 'lazy' was applied to the element.
-  static const Modifier LATE = Modifier('LATE', 17);
+  static const Modifier LATE = Modifier('LATE', 18);
 
   /// Indicates that a class is a mixin application.
-  static const Modifier MIXIN_APPLICATION = Modifier('MIXIN_APPLICATION', 18);
+  static const Modifier MIXIN_APPLICATION = Modifier('MIXIN_APPLICATION', 19);
 
   /// Indicates that the pseudo-modifier 'set' was applied to the element.
-  static const Modifier SETTER = Modifier('SETTER', 19);
+  static const Modifier SETTER = Modifier('SETTER', 20);
 
   /// Indicates that the modifier 'static' was applied to the element.
-  static const Modifier STATIC = Modifier('STATIC', 20);
+  static const Modifier STATIC = Modifier('STATIC', 21);
 
   /// Indicates that the element does not appear in the source code but was
   /// implicitly created. For example, if a class does not define any
   /// constructors, an implicit zero-argument constructor will be created and it
   /// will be marked as being synthetic.
-  static const Modifier SYNTHETIC = Modifier('SYNTHETIC', 21);
+  static const Modifier SYNTHETIC = Modifier('SYNTHETIC', 22);
 
   static const List<Modifier> values = [
     ABSTRACT,
@@ -4316,6 +4328,7 @@
     HAS_INITIALIZER,
     HAS_PART_OF_DIRECTIVE,
     IMPLICIT_TYPE,
+    IS_FROM_MACRO,
     LATE,
     MIXIN_APPLICATION,
     SETTER,
diff --git a/pkg/analyzer/lib/src/macro/api/code.dart b/pkg/analyzer/lib/src/macro/api/code.dart
new file mode 100644
index 0000000..0c06310
--- /dev/null
+++ b/pkg/analyzer/lib/src/macro/api/code.dart
@@ -0,0 +1,58 @@
+// 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.
+
+/// Combines [parts] into a [String].
+/// Must only contain [Code] or [String] instances.
+String _combineParts(List<Object> parts) {
+  var buffer = StringBuffer();
+  for (var part in parts) {
+    if (part is String) {
+      buffer.write(part);
+    } else if (part is Code) {
+      buffer.write(part.code);
+    } else if (part is List<Code>) {
+      buffer.write(part.map((p) => p.code).join());
+    } else {
+      throw UnsupportedError(
+        'Only String, Code, and List<Code> are allowed but got $part',
+      );
+    }
+  }
+  return buffer.toString();
+}
+
+/// The representation of a piece of code.
+abstract class Code {
+  String get code;
+
+  @override
+  String toString() => code;
+}
+
+/// A piece of code representing a syntactically valid declaration.
+class Declaration extends Code {
+  @override
+  final String code;
+
+  Declaration(this.code);
+
+  /// Creates a [Declaration] from [parts], which must be of type [Code],
+  /// `List<Code>`, or [String].
+  factory Declaration.fromParts(List<Object> parts) =>
+      Declaration(_combineParts(parts));
+}
+
+/// A piece of code that can't be parsed into a valid language construct in its
+/// current form. No validation or parsing is performed.
+class Fragment extends Code {
+  @override
+  final String code;
+
+  Fragment(this.code);
+
+  /// Creates a [Fragment] from [parts], which must be of type [Code],
+  /// `List<Code>`, or [String].
+  factory Fragment.fromParts(List<Object> parts) =>
+      Fragment(_combineParts(parts));
+}
diff --git a/pkg/analyzer/lib/src/macro/api/macro.dart b/pkg/analyzer/lib/src/macro/api/macro.dart
new file mode 100644
index 0000000..0f65d38
--- /dev/null
+++ b/pkg/analyzer/lib/src/macro/api/macro.dart
@@ -0,0 +1,72 @@
+// 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:analyzer/dart/ast/ast.dart' as ast;
+import 'package:analyzer/src/macro/api/code.dart';
+
+/// The api used by [DeclarationMacro]s to contribute new declarations to the
+/// current class.
+///
+/// Note that this is available to macros that run directly on classes, as well
+/// as macros that run on any members of a class.
+abstract class ClassDeclarationBuilder implements DeclarationBuilder {
+  /// Adds a new declaration to the surrounding class.
+  void addToClass(Declaration declaration);
+}
+
+/// The api used by [DeclarationMacro]s to contribute new declarations to the
+/// current library.
+abstract class DeclarationBuilder {
+  /// Adds a new regular declaration to the surrounding library.
+  ///
+  /// Note that type declarations are not supported.
+  void addToLibrary(Declaration declaration);
+
+  /// Return the [Code] of the [node].
+  Code typeAnnotationCode(ast.TypeAnnotation node);
+}
+
+/// The marker interface for macros that are allowed to contribute new
+/// declarations to the program, including both top level and class level
+/// declarations.
+///
+/// These macros run after [TypeMacro] macros, but before [DefinitionMacro]
+/// macros.
+///
+/// These macros can resolve type annotations to specific declarations, and
+/// inspect type hierarchies, but they cannot inspect the declarations on those
+/// type annotations, since new declarations could still be added in this phase.
+abstract class DeclarationMacro implements Macro {}
+
+/// The marker interface for macros that are only allowed to implement or wrap
+/// existing declarations in the program. They cannot introduce any new
+/// declarations that are visible to the program, but are allowed to add
+/// declarations that only they can see.
+///
+/// These macros run after all other types of macros.
+///
+/// These macros can fully reflect on the program since the static shape is
+/// fully defined by the time they run.
+abstract class DefinitionMacro implements Macro {}
+
+/// The interface for [DeclarationMacro]s that can be applied to fields.
+abstract class FieldDeclarationMacro implements DeclarationMacro {
+  void visitFieldDeclaration(
+    ast.FieldDeclaration declaration,
+    ClassDeclarationBuilder builder,
+  );
+}
+
+/// The marker interface for all types of macros.
+abstract class Macro {}
+
+/// The marker interface for macros that are allowed to contribute new type
+/// declarations into the program.
+///
+/// These macros run before all other types of macros.
+///
+/// In exchange for the power to add new type declarations, these macros have
+/// limited introspections capabilities, since new types can be added in this
+/// phase you cannot follow type references back to their declarations.
+abstract class TypeMacro implements Macro {}
diff --git a/pkg/analyzer/lib/src/macro/builders/observable.dart b/pkg/analyzer/lib/src/macro/builders/observable.dart
new file mode 100644
index 0000000..6857d34
--- /dev/null
+++ b/pkg/analyzer/lib/src/macro/builders/observable.dart
@@ -0,0 +1,48 @@
+// 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:analyzer/dart/ast/ast.dart' as ast;
+import 'package:analyzer/src/macro/api/code.dart';
+import 'package:analyzer/src/macro/api/macro.dart';
+
+class ObservableMacro implements FieldDeclarationMacro {
+  const ObservableMacro();
+
+  @override
+  void visitFieldDeclaration(
+    ast.FieldDeclaration node,
+    ClassDeclarationBuilder builder,
+  ) {
+    var typeNode = node.fields.type;
+    if (typeNode == null) {
+      throw ArgumentError('@observable can only annotate typed fields.');
+    }
+    var typeCode = builder.typeAnnotationCode(typeNode);
+
+    var fields = node.fields.variables;
+    for (var field in fields) {
+      var name = field.name.name;
+      if (!name.startsWith('_')) {
+        throw ArgumentError(
+          '@observable can only annotate private fields, and it will create '
+          'public getters and setters for them, but the public field '
+          '$name was annotated.',
+        );
+      }
+      var publicName = name.substring(1);
+
+      var getter = Declaration(
+        '$typeCode get $publicName => $name;',
+      );
+      builder.addToClass(getter);
+
+      var setter = Declaration('''
+set $publicName($typeCode val) {
+  print('Setting $publicName to \${val}');
+  $name = val;
+}''');
+      builder.addToClass(setter);
+    }
+  }
+}
diff --git a/pkg/analyzer/lib/src/macro/impl/macro.dart b/pkg/analyzer/lib/src/macro/impl/macro.dart
new file mode 100644
index 0000000..6459a6c
--- /dev/null
+++ b/pkg/analyzer/lib/src/macro/impl/macro.dart
@@ -0,0 +1,54 @@
+// 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:analyzer/dart/analysis/utilities.dart';
+import 'package:analyzer/dart/ast/ast.dart' as ast;
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/src/dart/ast/ast.dart' as ast;
+import 'package:analyzer/src/macro/api/code.dart';
+import 'package:analyzer/src/macro/api/macro.dart';
+
+class ClassDeclarationBuilderImpl extends DeclarationBuilderImpl
+    implements ClassDeclarationBuilder {
+  final ast.ClassDeclarationImpl node;
+
+  ClassDeclarationBuilderImpl(this.node);
+
+  @override
+  void addToClass(Declaration declaration) {
+    // TODO(scheglov) feature set
+    // TODO(scheglov) throw if errors?
+    var parseResult = parseString(
+      content: 'class ${node.name.name} { $declaration }',
+    );
+    var parsedDeclarations = parseResult.unit.declarations;
+    var parsedClass = parsedDeclarations.single as ast.ClassDeclaration;
+    var parsedMember = parsedClass.members.single;
+    _resetOffsets(parsedMember);
+
+    node.members.add(parsedMember);
+  }
+
+  /// We parsed [node] in the context of some synthetic code string, its
+  /// current offsets are meaningless. So, we reset them for now.
+  static void _resetOffsets(ast.AstNode node) {
+    for (Token? t = node.beginToken;
+        t != null && t != node.endToken;
+        t = t.next) {
+      t.offset = -1;
+    }
+  }
+}
+
+class DeclarationBuilderImpl implements DeclarationBuilder {
+  @override
+  void addToLibrary(Declaration declaration) {
+    // TODO: implement addToLibrary
+  }
+
+  @override
+  Code typeAnnotationCode(ast.TypeAnnotation node) {
+    return Fragment(node.toSource());
+  }
+}
diff --git a/pkg/analyzer/lib/src/summary2/element_builder.dart b/pkg/analyzer/lib/src/summary2/element_builder.dart
index 3737f6e..983c60a 100644
--- a/pkg/analyzer/lib/src/summary2/element_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/element_builder.dart
@@ -89,6 +89,34 @@
     }
   }
 
+  /// Build elements for [members] and add into the [element].
+  void buildMacroClassMembers(
+    ClassElementImpl element,
+    List<ClassMember> members,
+  ) {
+    var holder = _buildClassMembers(element, members);
+
+    for (var newElement in holder.propertyAccessors) {
+      newElement.isFromMacro = true;
+      element.accessors.add(newElement);
+    }
+
+    for (var newElement in holder.constructors) {
+      newElement.isFromMacro = true;
+      element.constructors.add(newElement);
+    }
+
+    for (var newElement in holder.properties.whereType<FieldElementImpl>()) {
+      newElement.isFromMacro = true;
+      element.fields.add(newElement);
+    }
+
+    for (var newElement in holder.methods) {
+      newElement.isFromMacro = true;
+      element.methods.add(newElement);
+    }
+  }
+
   @override
   void visitClassDeclaration(covariant ClassDeclarationImpl node) {
     var nameNode = node.name;
@@ -868,7 +896,7 @@
   }
 
   _EnclosingContext _buildClassMembers(
-      ElementImpl element, NodeList<ClassMember> members) {
+      ElementImpl element, List<ClassMember> members) {
     var hasConstConstructor = members.any((e) {
       return e is ConstructorDeclaration && e.constKeyword != null;
     });
diff --git a/pkg/analyzer/lib/src/summary2/element_flags.dart b/pkg/analyzer/lib/src/summary2/element_flags.dart
index e808000..f43df16 100644
--- a/pkg/analyzer/lib/src/summary2/element_flags.dart
+++ b/pkg/analyzer/lib/src/summary2/element_flags.dart
@@ -8,12 +8,14 @@
 
 class ClassElementFlags {
   static const int _isAbstract = 1 << 0;
-  static const int _isMixinApplication = 1 << 1;
-  static const int _isSimplyBounded = 1 << 2;
+  static const int _isFromMacro = 1 << 1;
+  static const int _isMixinApplication = 1 << 2;
+  static const int _isSimplyBounded = 1 << 3;
 
   static void read(SummaryDataReader reader, ClassElementImpl element) {
     var byte = reader.readByte();
     element.isAbstract = (byte & _isAbstract) != 0;
+    element.isFromMacro = (byte & _isFromMacro) != 0;
     element.isMixinApplication = (byte & _isMixinApplication) != 0;
     element.isSimplyBounded = (byte & _isSimplyBounded) != 0;
   }
@@ -21,6 +23,7 @@
   static void write(BufferedSink sink, ClassElementImpl element) {
     var result = 0;
     result |= element.isAbstract ? _isAbstract : 0;
+    result |= element.isFromMacro ? _isFromMacro : 0;
     result |= element.isMixinApplication ? _isMixinApplication : 0;
     result |= element.isSimplyBounded ? _isSimplyBounded : 0;
     sink.writeByte(result);
@@ -60,8 +63,9 @@
   static const int _isCovariant = 1 << 5;
   static const int _isExternal = 1 << 6;
   static const int _isFinal = 1 << 7;
-  static const int _isLate = 1 << 8;
-  static const int _isStatic = 1 << 9;
+  static const int _isFromMacro = 1 << 8;
+  static const int _isLate = 1 << 9;
+  static const int _isStatic = 1 << 10;
 
   static void read(SummaryDataReader reader, FieldElementImpl element) {
     var byte = reader.readUInt30();
@@ -73,6 +77,7 @@
     element.isCovariant = (byte & _isCovariant) != 0;
     element.isExternal = (byte & _isExternal) != 0;
     element.isFinal = (byte & _isFinal) != 0;
+    element.isFromMacro = (byte & _isFromMacro) != 0;
     element.isLate = (byte & _isLate) != 0;
     element.isStatic = (byte & _isStatic) != 0;
   }
@@ -87,6 +92,7 @@
     result |= element.isCovariant ? _isCovariant : 0;
     result |= element.isExternal ? _isExternal : 0;
     result |= element.isFinal ? _isFinal : 0;
+    result |= element.isFromMacro ? _isFromMacro : 0;
     result |= element.isLate ? _isLate : 0;
     result |= element.isStatic ? _isStatic : 0;
     sink.writeUInt30(result);
@@ -161,8 +167,9 @@
   static const int _isAbstract = 1 << 1;
   static const int _isAsynchronous = 1 << 2;
   static const int _isExternal = 1 << 3;
-  static const int _isGenerator = 1 << 4;
-  static const int _isStatic = 1 << 5;
+  static const int _isFromMacro = 1 << 4;
+  static const int _isGenerator = 1 << 5;
+  static const int _isStatic = 1 << 6;
 
   static void read(SummaryDataReader reader, MethodElementImpl element) {
     var byte = reader.readByte();
@@ -170,6 +177,7 @@
     element.isAbstract = (byte & _isAbstract) != 0;
     element.isAsynchronous = (byte & _isAsynchronous) != 0;
     element.isExternal = (byte & _isExternal) != 0;
+    element.isFromMacro = (byte & _isFromMacro) != 0;
     element.isGenerator = (byte & _isGenerator) != 0;
     element.isStatic = (byte & _isStatic) != 0;
   }
@@ -180,6 +188,7 @@
     result |= element.isAbstract ? _isAbstract : 0;
     result |= element.isAsynchronous ? _isAsynchronous : 0;
     result |= element.isExternal ? _isExternal : 0;
+    result |= element.isFromMacro ? _isFromMacro : 0;
     result |= element.isGenerator ? _isGenerator : 0;
     result |= element.isStatic ? _isStatic : 0;
     sink.writeByte(result);
@@ -217,8 +226,9 @@
   static const int _isAbstract = 1 << 3;
   static const int _isAsynchronous = 1 << 4;
   static const int _isExternal = 1 << 5;
-  static const int _isGenerator = 1 << 6;
-  static const int _isStatic = 1 << 7;
+  static const int _isFromMacro = 1 << 6;
+  static const int _isGenerator = 1 << 7;
+  static const int _isStatic = 1 << 8;
 
   static void read(
     SummaryDataReader reader,
@@ -231,6 +241,7 @@
     element.isAbstract = (byte & _isAbstract) != 0;
     element.isAsynchronous = (byte & _isAsynchronous) != 0;
     element.isExternal = (byte & _isExternal) != 0;
+    element.isFromMacro = (byte & _isFromMacro) != 0;
     element.isGenerator = (byte & _isGenerator) != 0;
     element.isStatic = (byte & _isStatic) != 0;
   }
@@ -243,6 +254,7 @@
     result |= element.isAbstract ? _isAbstract : 0;
     result |= element.isAsynchronous ? _isAsynchronous : 0;
     result |= element.isExternal ? _isExternal : 0;
+    result |= element.isFromMacro ? _isFromMacro : 0;
     result |= element.isGenerator ? _isGenerator : 0;
     result |= element.isStatic ? _isStatic : 0;
     sink.writeUInt30(result);
diff --git a/pkg/analyzer/lib/src/summary2/library_builder.dart b/pkg/analyzer/lib/src/summary2/library_builder.dart
index ba1e230..e500aa1 100644
--- a/pkg/analyzer/lib/src/summary2/library_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/library_builder.dart
@@ -9,6 +9,8 @@
 import 'package:analyzer/src/dart/ast/mixin_super_invoked_names.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/resolver/scope.dart';
+import 'package:analyzer/src/macro/builders/observable.dart' as macro;
+import 'package:analyzer/src/macro/impl/macro.dart' as macro;
 import 'package:analyzer/src/summary2/combinator.dart';
 import 'package:analyzer/src/summary2/constructor_initializer_resolver.dart';
 import 'package:analyzer/src/summary2/default_value_resolver.dart';
@@ -159,18 +161,69 @@
 
   void resolveTypes(NodesToBuildType nodesToBuildType) {
     for (var linkingUnit in units) {
-      var resolver = ReferenceResolver(
-        linker,
-        nodesToBuildType,
-        linker.elementFactory,
-        element,
-        linkingUnit.reference,
-        linkingUnit.node.featureSet.isEnabled(Feature.non_nullable),
-      );
+      var resolver = _newTypeReferenceResolver(nodesToBuildType, linkingUnit);
       linkingUnit.node.accept(resolver);
     }
   }
 
+  /// Run built-in declaration macros.
+  void runDeclarationMacros() {
+    bool hasMacroAnnotation(ast.AnnotatedNode node, String name) {
+      for (var annotation in node.metadata) {
+        var nameNode = annotation.name;
+        if (nameNode is ast.SimpleIdentifier &&
+            annotation.arguments == null &&
+            annotation.constructorName == null &&
+            nameNode.name == name) {
+          var nameElement = element.scope.lookup(name).getter;
+          return nameElement != null &&
+              nameElement.library?.name == 'analyzer.macro.annotations';
+        }
+      }
+      return false;
+    }
+
+    for (var linkingUnit in units) {
+      for (var declaration in linkingUnit.node.declarations) {
+        if (declaration is ast.ClassDeclarationImpl) {
+          var members = declaration.members.toList();
+          for (var member in members) {
+            if (member is ast.FieldDeclarationImpl) {
+              if (hasMacroAnnotation(member, 'observable')) {
+                macro.ObservableMacro().visitFieldDeclaration(
+                  member,
+                  macro.ClassDeclarationBuilderImpl(declaration),
+                );
+              }
+            }
+          }
+
+          var newMembers = declaration.members.sublist(members.length);
+          if (newMembers.isNotEmpty) {
+            var elementBuilder = ElementBuilder(
+              libraryBuilder: this,
+              unitReference: linkingUnit.reference,
+              unitElement: linkingUnit.element,
+            );
+            var classElement = declaration.declaredElement as ClassElementImpl;
+            elementBuilder.buildMacroClassMembers(classElement, newMembers);
+
+            // TODO(scheglov) extract
+            {
+              var nodesToBuildType = NodesToBuildType();
+              var resolver =
+                  _newTypeReferenceResolver(nodesToBuildType, linkingUnit);
+              for (var newMember in newMembers) {
+                newMember.accept(resolver);
+              }
+              TypesBuilder(linker).build(nodesToBuildType);
+            }
+          }
+        }
+      }
+    }
+  }
+
   void storeExportScope() {
     exports = exportScope.map.values.toList();
     linker.elementFactory.setExportsOfLibrary('$uri', exports);
@@ -205,6 +258,21 @@
     }
   }
 
+  ReferenceResolver _newTypeReferenceResolver(
+    NodesToBuildType nodesToBuildType,
+    LinkingUnit linkingUnit,
+  ) {
+    /// TODO(scheglov) Do we need all these parameters?
+    return ReferenceResolver(
+      linker,
+      nodesToBuildType,
+      linker.elementFactory,
+      element,
+      linkingUnit.reference,
+      linkingUnit.node.featureSet.isEnabled(Feature.non_nullable),
+    );
+  }
+
   static void build(Linker linker, LinkInputLibrary inputLibrary) {
     var elementFactory = linker.elementFactory;
 
diff --git a/pkg/analyzer/lib/src/summary2/link.dart b/pkg/analyzer/lib/src/summary2/link.dart
index f0b0174..d0fe559 100644
--- a/pkg/analyzer/lib/src/summary2/link.dart
+++ b/pkg/analyzer/lib/src/summary2/link.dart
@@ -93,6 +93,7 @@
     _createTypeSystem();
     _buildEnumChildren();
     _resolveTypes();
+    _runDeclarationMacros();
     _performTopLevelInference();
     _resolveConstructors();
     _resolveConstantInitializers();
@@ -213,6 +214,12 @@
     TypesBuilder(this).build(nodesToBuildType);
   }
 
+  void _runDeclarationMacros() {
+    for (var library in builders.values) {
+      library.runDeclarationMacros();
+    }
+  }
+
   void _writeLibraries() {
     var bundleWriter = BundleWriter(
       elementFactory.dynamicRef,
diff --git a/pkg/analyzer/test/src/dart/resolution/macro_test.dart b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
new file mode 100644
index 0000000..4dd8ebc
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
@@ -0,0 +1,47 @@
+// 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:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../../generated/elements_types_mixin.dart';
+import 'context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(MacroResolutionTest);
+  });
+}
+
+@reflectiveTest
+class MacroResolutionTest extends PubPackageResolutionTest
+    with ElementsTypesMixin {
+  @override
+  void setUp() {
+    super.setUp();
+
+    newFile('$testPackageLibPath/macro_annotations.dart', content: r'''
+library analyzer.macro.annotations;
+const observable = 0;
+''');
+  }
+
+  test_observable() async {
+    await assertErrorsInCode(r'''
+import 'macro_annotations.dart';
+
+class A {
+  @observable
+  int _foo = 0;
+}
+
+void f(A a) {
+  a.foo;
+  a.foo = 2;
+}
+''', [
+      error(HintCode.UNUSED_FIELD, 64, 4),
+    ]);
+  }
+}
diff --git a/pkg/analyzer/test/src/dart/resolution/test_all.dart b/pkg/analyzer/test/src/dart/resolution/test_all.dart
index 5f1c71d..08c23ad 100644
--- a/pkg/analyzer/test/src/dart/resolution/test_all.dart
+++ b/pkg/analyzer/test/src/dart/resolution/test_all.dart
@@ -42,6 +42,7 @@
 import 'library_element_test.dart' as library_element;
 import 'local_function_test.dart' as local_function;
 import 'local_variable_test.dart' as local_variable;
+import 'macro_test.dart' as macro;
 import 'metadata_test.dart' as metadata;
 import 'method_declaration_test.dart' as method_declaration;
 import 'method_invocation_test.dart' as method_invocation;
@@ -101,6 +102,7 @@
     library_element.main();
     local_function.main();
     local_variable.main();
+    macro.main();
     metadata.main();
     method_declaration.main();
     method_invocation.main();
diff --git a/pkg/analyzer/test/src/summary/element_text.dart b/pkg/analyzer/test/src/summary/element_text.dart
index acbd956..e7884a5 100644
--- a/pkg/analyzer/test/src/summary/element_text.dart
+++ b/pkg/analyzer/test/src/summary/element_text.dart
@@ -645,6 +645,8 @@
   }
 
   void _writePropertyAccessorElement(PropertyAccessorElement e) {
+    e as PropertyAccessorElementImpl;
+
     PropertyInducingElement variable = e.variable;
     expect(variable, isNotNull);
 
@@ -667,7 +669,7 @@
       }
     }
 
-    if (e.isSynthetic) {
+    if (e.isSynthetic || e.isFromMacro) {
       expect(e.nameOffset, -1);
     } else {
       expect(e.nameOffset, isPositive);
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index caee60d..8c5f16e 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -21246,6 +21246,58 @@
 ''');
   }
 
+  test_macro_observable() async {
+    addLibrarySource('/macro_annotations.dart', r'''
+library analyzer.macro.annotations;
+const observable = 0;
+''');
+    var library = await checkLibrary(r'''
+import 'macro_annotations.dart';
+class A {
+  @observable
+  int _f = 0;
+}
+''');
+    checkElementText(library, r'''
+library
+  imports
+    macro_annotations.dart
+  definingUnit
+    classes
+      class A @39
+        fields
+          _f @63
+            metadata
+              Annotation
+                atSign: @ @45
+                element: macro_annotations.dart::@getter::observable
+                name: SimpleIdentifier
+                  staticElement: macro_annotations.dart::@getter::observable
+                  staticType: null
+                  token: observable @46
+            type: int
+          synthetic f @-1
+            type: int
+        constructors
+          synthetic @-1
+        accessors
+          synthetic get _f @-1
+            returnType: int
+          synthetic set _f @-1
+            parameters
+              requiredPositional __f @-1
+                type: int
+            returnType: void
+          get f @-1
+            returnType: int
+          set f @-1
+            parameters
+              requiredPositional val @-1
+                type: int
+            returnType: void
+''');
+  }
+
   test_main_class() async {
     var library = await checkLibrary('class main {}');
     checkElementText(library, r'''