Support for metadata on augmentation directives.

Change-Id: Id05c1e2bbee6f3adc3399168065bb31f7ddeebfe
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/251589
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index fde98ef..eacb5ab 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -85,7 +85,7 @@
 /// TODO(scheglov) Clean up the list of implicitly analyzed files.
 class AnalysisDriver implements AnalysisDriverGeneric {
   /// The version of data format, should be incremented on every format change.
-  static const int DATA_VERSION = 234;
+  static const int DATA_VERSION = 235;
 
   /// The number of exception contexts allowed to write. Once this field is
   /// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index cbe272d..687f6f6 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -3933,13 +3933,12 @@
 
   @override
   List<AugmentationImportElement> get augmentationImports {
-    linkedData?.read(this);
+    _readLinkedData();
     return super.augmentationImports;
   }
 
   @override
   List<ExportElement2> get exports2 {
-    linkedData?.read(this);
     return _exports2;
   }
 
@@ -3948,7 +3947,7 @@
 
   @override
   List<ImportElement2> get imports2 {
-    linkedData?.read(this);
+    _readLinkedData();
     return _imports2;
   }
 
@@ -3978,6 +3977,11 @@
   T? accept<T>(ElementVisitor<T> visitor) {
     return visitor.visitLibraryAugmentationElement(this);
   }
+
+  @override
+  void _readLinkedData() {
+    augmented._readLinkedData();
+  }
 }
 
 /// A concrete implementation of a [LibraryElement].
@@ -4050,7 +4054,7 @@
 
   @override
   List<AugmentationImportElement> get augmentationImports {
-    linkedData?.read(this);
+    _readLinkedData();
     return super.augmentationImports;
   }
 
@@ -4061,7 +4065,7 @@
 
   @override
   FunctionElement? get entryPoint {
-    linkedData?.read(this);
+    _readLinkedData();
     return _entryPoint;
   }
 
@@ -4108,7 +4112,7 @@
 
   @override
   List<ExportElement2> get exports2 {
-    linkedData?.read(this);
+    _readLinkedData();
     return _exports2;
   }
 
@@ -4142,7 +4146,7 @@
 
   @override
   List<ImportElement2> get imports2 {
-    linkedData?.read(this);
+    _readLinkedData();
     return _imports2;
   }
 
@@ -4221,7 +4225,7 @@
 
   @override
   List<ElementAnnotation> get metadata {
-    linkedData?.read(this);
+    _readLinkedData();
     return super.metadata;
   }
 
@@ -4446,6 +4450,11 @@
     }
   }
 
+  @override
+  void _readLinkedData() {
+    linkedData?.read(this);
+  }
+
   static List<PrefixElement> buildPrefixesFromImports(
       List<ImportElement2> imports) {
     HashSet<PrefixElement> prefixes = HashSet<PrefixElement>();
@@ -4602,6 +4611,8 @@
     safelyVisitChildren(imports2, visitor);
   }
 
+  void _readLinkedData();
+
   static List<PrefixElement> buildPrefixesFromImports(
       List<ImportElement2> imports) {
     HashSet<PrefixElement> prefixes = HashSet<PrefixElement>();
diff --git a/pkg/analyzer/lib/src/summary2/bundle_reader.dart b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
index 841cead..40c321b 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
@@ -350,55 +350,23 @@
   }
 }
 
-class LibraryAugmentationElementLinkedData
-    extends LibraryOrAugmentationElementLinkedData<
-        LibraryAugmentationElementImpl> {
+/// Not a [ElementLinkedData], just a bundle with data.
+class LibraryAugmentationElementLinkedData {
+  final int offset;
+  ApplyConstantOffsets? applyConstantOffsets;
+
   LibraryAugmentationElementLinkedData({
-    required super.reference,
-    required super.libraryReader,
-    required super.unitElement,
-    required super.offset,
+    required this.offset,
   });
-
-  @override
-  void read(ElementImpl element) {
-    final libraryData = element.library?.linkedData;
-    if (libraryData != null && !libraryData._isLocked) {
-      super.read(element);
-    }
-  }
 }
 
-class LibraryElementLinkedData
-    extends LibraryOrAugmentationElementLinkedData<LibraryElementImpl> {
-  LibraryElementLinkedData({
-    required super.reference,
-    required super.libraryReader,
-    required super.unitElement,
-    required super.offset,
-  });
-
-  @override
-  void _readAdditional(element, reader) {
-    for (final part in element.parts2) {
-      part as PartElementImpl;
-      part.metadata = reader._readAnnotationList(
-        unitElement: unitElement,
-      );
-    }
-
-    element.entryPoint = reader.readElement() as FunctionElement?;
-  }
-}
-
-class LibraryOrAugmentationElementLinkedData<
-    E extends LibraryOrAugmentationElementImpl> extends ElementLinkedData<E> {
+class LibraryElementLinkedData extends ElementLinkedData<LibraryElementImpl> {
   ApplyConstantOffsets? applyConstantOffsets;
 
   /// When we are applying offsets to a library, we want to lock it.
   bool _isLocked = false;
 
-  LibraryOrAugmentationElementLinkedData({
+  LibraryElementLinkedData({
     required Reference reference,
     required LibraryReader libraryReader,
     required CompilationUnitElementImpl unitElement,
@@ -428,6 +396,23 @@
 
   @override
   void _read(element, reader) {
+    _readLibraryOrAugmentation(element, reader);
+    for (final part in element.parts2) {
+      part as PartElementImpl;
+      part.metadata = reader._readAnnotationList(
+        unitElement: unitElement,
+      );
+    }
+
+    element.entryPoint = reader.readElement() as FunctionElement?;
+
+    applyConstantOffsets?.perform();
+  }
+
+  void _readLibraryOrAugmentation(
+    LibraryOrAugmentationElementImpl element,
+    ResolutionReader reader,
+  ) {
     element.metadata = reader._readAnnotationList(
       unitElement: unitElement,
     );
@@ -454,14 +439,21 @@
       }
     }
 
-    // TODO(scheglov) metadata on augmentation imports
-
-    _readAdditional(element, reader);
-
-    applyConstantOffsets?.perform();
+    for (var import in element.augmentationImports) {
+      import as AugmentationImportElementImpl;
+      import.metadata = reader._readAnnotationList(
+        // TODO(scheglov) Here and for parts, unit is not valid. Test and fix.
+        unitElement: unitElement,
+      );
+      final importedAugmentation = import.importedAugmentation;
+      if (importedAugmentation != null) {
+        final linkedData = importedAugmentation.linkedData!;
+        reader.setOffset(linkedData.offset);
+        _readLibraryOrAugmentation(importedAugmentation, reader);
+        linkedData.applyConstantOffsets?.perform();
+      }
+    }
   }
-
-  void _readAdditional(E element, ResolutionReader reader) {}
 }
 
 class LibraryReader {
@@ -570,9 +562,6 @@
     _readLibraryOrAugmentationElement(augmentation);
 
     augmentation.linkedData = LibraryAugmentationElementLinkedData(
-      reference: _reference,
-      libraryReader: this,
-      unitElement: definingUnit,
       offset: resolutionOffset,
     );
 
@@ -1758,6 +1747,10 @@
     return _reader.readUInt32();
   }
 
+  void setOffset(int offset) {
+    _reader.offset = offset;
+  }
+
   void _addFormalParameters(List<ParameterElement> parameters) {
     for (var parameter in parameters) {
       _localElements.add(parameter);
diff --git a/pkg/analyzer/lib/src/summary2/bundle_writer.dart b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
index 43a2151..d44e85a 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
@@ -124,6 +124,8 @@
   void _writeAugmentationElement(LibraryAugmentationElement augmentation) {
     augmentation as LibraryAugmentationElementImpl;
     _writeUnitElement(augmentation.definingCompilationUnit);
+    // The offset where resolution for the augmentation starts.
+    // We need it to skip resolution information from the unit.
     _sink.writeUInt30(_resolutionSink.offset);
     _writeLibraryOrAugmentationElement(augmentation);
   }
diff --git a/pkg/analyzer/lib/src/summary2/element_builder.dart b/pkg/analyzer/lib/src/summary2/element_builder.dart
index c21d763..1912980 100644
--- a/pkg/analyzer/lib/src/summary2/element_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/element_builder.dart
@@ -751,8 +751,7 @@
 
   @override
   void visitLibraryAugmentationDirective(LibraryAugmentationDirective node) {
-    // TODO: implement visitLibraryAugmentationDirective
-    // super.visitLibraryAugmentationDirective(node);
+    _container.metadata = _buildAnnotations(node.metadata);
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/summary2/informative_data.dart b/pkg/analyzer/lib/src/summary2/informative_data.dart
index 95cb3b4..39e50e1 100644
--- a/pkg/analyzer/lib/src/summary2/informative_data.dart
+++ b/pkg/analyzer/lib/src/summary2/informative_data.dart
@@ -78,8 +78,13 @@
         var unitReader = SummaryDataReader(unitInfoBytes);
         var unitInfo = _InfoUnit(unitReader);
 
-        if (i == 0) {
-          _applyToLibrary(libraryElement, unitInfo);
+        final enclosing = unitElement.enclosingElement2;
+        if (enclosing is LibraryElementImpl) {
+          if (identical(enclosing.definingCompilationUnit, unitElement)) {
+            _applyToLibrary(enclosing, unitInfo);
+          }
+        } else if (enclosing is LibraryAugmentationElementImpl) {
+          _applyToAugmentation(enclosing, unitInfo);
         }
 
         unitElement.setCodeRange(unitInfo.codeOffset, unitInfo.codeLength);
@@ -176,6 +181,29 @@
     );
   }
 
+  void _applyToAugmentation(
+    LibraryAugmentationElementImpl element,
+    _InfoUnit info,
+  ) {
+    if (info.docComment.isNotEmpty) {
+      element.documentationComment = info.docComment;
+    }
+
+    _applyToImports(element, info);
+    _applyToExports(element, info);
+
+    var linkedData = element.linkedData as LibraryAugmentationElementLinkedData;
+    linkedData.applyConstantOffsets = ApplyConstantOffsets(
+      info.libraryConstantOffsets,
+      (applier) {
+        applier.applyToMetadata(element);
+        applier.applyToImports(element.imports2);
+        applier.applyToExports(element.exports2);
+        applier.applyToAugmentationImports(element.augmentationImports);
+      },
+    );
+  }
+
   void _applyToClassDeclaration(
     ClassElement element,
     _InfoClassDeclaration info,
@@ -305,6 +333,21 @@
     );
   }
 
+  void _applyToExports(
+    LibraryOrAugmentationElementImpl element,
+    _InfoUnit info,
+  ) {
+    forCorrespondingPairs<ExportElement2, _InfoExport>(
+      element.exports_unresolved,
+      info.exports,
+      (element, info) {
+        element as ExportElement2Impl;
+        element.nameOffset = info.nameOffset;
+        _applyToCombinators(element.combinators, info.combinators);
+      },
+    );
+  }
+
   void _applyToExtensionDeclaration(
     ExtensionElement element,
     _InfoClassDeclaration info,
@@ -442,14 +485,10 @@
     );
   }
 
-  void _applyToLibrary(LibraryElementImpl element, _InfoUnit info) {
-    element.nameOffset = info.libraryName.offset;
-    element.nameLength = info.libraryName.length;
-
-    if (info.docComment.isNotEmpty) {
-      element.documentationComment = info.docComment;
-    }
-
+  void _applyToImports(
+    LibraryOrAugmentationElementImpl element,
+    _InfoUnit info,
+  ) {
     forCorrespondingPairs<ImportElement2, _InfoImport>(
       element.imports_unresolved,
       info.imports,
@@ -465,16 +504,18 @@
         _applyToCombinators(element.combinators, info.combinators);
       },
     );
+  }
 
-    forCorrespondingPairs<ExportElement2, _InfoExport>(
-      element.exports_unresolved,
-      info.exports,
-      (element, info) {
-        element as ExportElement2Impl;
-        element.nameOffset = info.nameOffset;
-        _applyToCombinators(element.combinators, info.combinators);
-      },
-    );
+  void _applyToLibrary(LibraryElementImpl element, _InfoUnit info) {
+    element.nameOffset = info.libraryName.offset;
+    element.nameLength = info.libraryName.length;
+
+    if (info.docComment.isNotEmpty) {
+      element.documentationComment = info.docComment;
+    }
+
+    _applyToImports(element, info);
+    _applyToExports(element, info);
 
     forCorrespondingPairs<PartElement, _InfoPart>(
       element.parts2,
@@ -491,6 +532,7 @@
         applier.applyToMetadata(element);
         applier.applyToImports(element.imports2);
         applier.applyToExports(element.exports2);
+        applier.applyToAugmentationImports(element.augmentationImports);
         applier.applyToPartDirectives(element.parts2);
       },
     );
@@ -1392,6 +1434,8 @@
       metadata: firstDirective?.metadata,
       importDirectives: unit.directives.whereType<ImportDirective>(),
       exportDirectives: unit.directives.whereType<ExportDirective>(),
+      augmentationImportDirectives:
+          unit.directives.whereType<AugmentationImportDirective>(),
       partDirectives: unit.directives.whereType<PartDirective>(),
     );
   }
@@ -1422,6 +1466,7 @@
     NodeList<Annotation>? metadata,
     Iterable<ImportDirective>? importDirectives,
     Iterable<ExportDirective>? exportDirectives,
+    Iterable<AugmentationImportDirective>? augmentationImportDirectives,
     Iterable<PartDirective>? partDirectives,
     TypeParameterList? typeParameters,
     FormalParameterList? formalParameters,
@@ -1468,6 +1513,7 @@
     metadata?.accept(collector);
     addDirectives(importDirectives);
     addDirectives(exportDirectives);
+    addDirectives(augmentationImportDirectives);
     addDirectives(partDirectives);
     addTypeParameters(typeParameters);
     addFormalParameters(formalParameters);
@@ -1588,7 +1634,6 @@
       libraryName: _InfoLibraryName(reader),
       libraryConstantOffsets: reader.readUInt30List(),
       docComment: reader.readStringUtf8(),
-      // TODO(scheglov)
       imports: reader.readTypedList(
         () => _InfoImport(reader),
       ),
@@ -1659,6 +1704,12 @@
 
   _OffsetsApplier(this._iterator);
 
+  void applyToAugmentationImports(List<AugmentationImportElement> elements) {
+    for (var element in elements) {
+      applyToMetadata(element);
+    }
+  }
+
   void applyToConstantInitializer(Element element) {
     if (element is ConstFieldElementImpl && element.isEnumConstant) {
       _applyToEnumConstantInitializer(element);
diff --git a/pkg/analyzer/test/src/summary/elements_test.dart b/pkg/analyzer/test/src/summary/elements_test.dart
index c9bab89..1c9331c 100644
--- a/pkg/analyzer/test/src/summary/elements_test.dart
+++ b/pkg/analyzer/test/src/summary/elements_test.dart
@@ -78,6 +78,24 @@
 ''');
   }
 
+  test_augmentation_documented() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+/// My documentation.
+library augment 'test.dart';
+''');
+    final library = await buildLibrary(r'''
+import augment 'a.dart';
+''');
+    checkElementText(library, r'''
+library
+  augmentationImports
+    package:test/a.dart
+      documentationComment: /// My documentation.
+      definingUnit
+  definingUnit
+''');
+  }
+
   test_augmentation_libraryExports_library() async {
     newFile('$testPackageLibPath/a.dart', r'''
 library augment 'test.dart';
@@ -26283,6 +26301,149 @@
 ''');
   }
 
+  test_metadata_augmentation_class() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+library augment 'test.dart';
+@deprecated
+class A {}
+''');
+    var library = await buildLibrary('''
+import augment 'a.dart';
+''');
+    checkElementText(library, r'''
+library
+  augmentationImports
+    package:test/a.dart
+      definingUnit
+        classes
+          class A @47
+            metadata
+              Annotation
+                atSign: @ @29
+                name: SimpleIdentifier
+                  token: deprecated @30
+                  staticElement: dart:core::@getter::deprecated
+                  staticType: null
+                element: dart:core::@getter::deprecated
+            constructors
+              synthetic @-1
+  definingUnit
+''');
+  }
+
+  test_metadata_augmentation_directive() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+@deprecated
+library augment 'test.dart';
+''');
+    var library = await buildLibrary(r'''
+import augment 'a.dart';
+''');
+    checkElementText(library, r'''
+library
+  augmentationImports
+    package:test/a.dart
+      metadata
+        Annotation
+          atSign: @ @0
+          name: SimpleIdentifier
+            token: deprecated @1
+            staticElement: dart:core::@getter::deprecated
+            staticType: null
+          element: dart:core::@getter::deprecated
+      definingUnit
+  definingUnit
+''');
+  }
+
+  test_metadata_augmentation_exportLibrary() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+library augment 'test.dart';
+@deprecated
+export 'dart:math';
+''');
+    var library = await buildLibrary('''
+import augment 'a.dart';
+''');
+    checkElementText(library, r'''
+library
+  augmentationImports
+    package:test/a.dart
+      exports
+        dart:math
+          metadata
+            Annotation
+              atSign: @ @29
+              name: SimpleIdentifier
+                token: deprecated @30
+                staticElement: dart:core::@getter::deprecated
+                staticType: null
+              element: dart:core::@getter::deprecated
+      definingUnit
+  definingUnit
+''');
+  }
+
+  test_metadata_augmentation_importAugmentation() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+library augment 'b.dart';
+''');
+    newFile('$testPackageLibPath/b.dart', r'''
+library augment 'test.dart';
+@deprecated
+import augment 'a.dart';
+''');
+    var library = await buildLibrary('''
+import augment 'b.dart';
+''');
+    checkElementText(library, r'''
+library
+  augmentationImports
+    package:test/b.dart
+      augmentationImports
+        package:test/a.dart
+          metadata
+            Annotation
+              atSign: @ @29
+              name: SimpleIdentifier
+                token: deprecated @30
+                staticElement: dart:core::@getter::deprecated
+                staticType: null
+              element: dart:core::@getter::deprecated
+          definingUnit
+      definingUnit
+  definingUnit
+''');
+  }
+
+  test_metadata_augmentation_importLibrary() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+library augment 'test.dart';
+@deprecated
+import 'dart:math';
+''');
+    var library = await buildLibrary('''
+import augment 'a.dart';
+''');
+    checkElementText(library, r'''
+library
+  augmentationImports
+    package:test/a.dart
+      imports
+        dart:math
+          metadata
+            Annotation
+              atSign: @ @29
+              name: SimpleIdentifier
+                token: deprecated @30
+                staticElement: dart:core::@getter::deprecated
+                staticType: null
+              element: dart:core::@getter::deprecated
+      definingUnit
+  definingUnit
+''');
+  }
+
   test_metadata_class_field_first() async {
     var library = await buildLibrary(r'''
 const a = 0;
@@ -28841,6 +29002,53 @@
 ''');
   }
 
+  test_metadata_library_importAugmentation_augmentation() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+library augment 'test.dart';
+''');
+    var library = await buildLibrary('''
+@deprecated
+import augment 'a.dart';
+''');
+    checkElementText(library, r'''
+library
+  augmentationImports
+    package:test/a.dart
+      metadata
+        Annotation
+          atSign: @ @0
+          name: SimpleIdentifier
+            token: deprecated @1
+            staticElement: dart:core::@getter::deprecated
+            staticType: null
+          element: dart:core::@getter::deprecated
+      definingUnit
+  definingUnit
+''');
+  }
+
+  /// Even though the target is not an augmentation, metadata is available.
+  test_metadata_library_importAugmentation_notAugmentation_library() async {
+    var library = await buildLibrary('''
+@deprecated
+import augment 'dart:math';
+''');
+    checkElementText(library, r'''
+library
+  augmentationImports
+    source 'dart:math'
+      metadata
+        Annotation
+          atSign: @ @0
+          name: SimpleIdentifier
+            token: deprecated @1
+            staticElement: dart:core::@getter::deprecated
+            staticType: null
+          element: dart:core::@getter::deprecated
+  definingUnit
+''');
+  }
+
   test_metadata_libraryDirective() async {
     var library = await buildLibrary('@a library L; const a = null;');
     checkElementText(library, r'''