Separate AugmentationImportDirective from ImportDirective.

Change-Id: I202320513390dbb218ec86fed18fc2943b168dd8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/250413
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/dart/ast/ast.dart b/pkg/analyzer/lib/dart/ast/ast.dart
index a00f66a..5f2d688 100644
--- a/pkg/analyzer/lib/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/dart/ast/ast.dart
@@ -365,6 +365,8 @@
 
   R? visitAssignmentExpression(AssignmentExpression node);
 
+  R? visitAugmentationImportDirective(AugmentationImportDirective node);
+
   R? visitAwaitExpression(AwaitExpression node);
 
   R? visitBinaryExpression(BinaryExpression node);
@@ -617,6 +619,29 @@
   R? visitYieldStatement(YieldStatement node);
 }
 
+/// An augmentation import directive.
+///
+///    importDirective ::=
+///        [Annotation] 'import' 'augment' [StringLiteral] ';'
+///
+/// Clients may not extend, implement or mix-in this class.
+@experimental
+abstract class AugmentationImportDirective implements UriBasedDirective {
+  /// The token representing the 'augment' keyword.
+  Token get augmentKeyword;
+
+  /// Return the element associated with this directive, or `null` if the AST
+  /// structure has not been resolved.
+  @override
+  AugmentationImportElement? get element;
+
+  /// The token representing the 'import' keyword.
+  Token get importKeyword;
+
+  /// Return the semicolon terminating the directive.
+  Token get semicolon;
+}
+
 /// An await expression.
 ///
 ///    awaitExpression ::=
diff --git a/pkg/analyzer/lib/dart/ast/visitor.dart b/pkg/analyzer/lib/dart/ast/visitor.dart
index 92ab86a..2c43bc6 100644
--- a/pkg/analyzer/lib/dart/ast/visitor.dart
+++ b/pkg/analyzer/lib/dart/ast/visitor.dart
@@ -154,6 +154,10 @@
       visitExpression(node);
 
   @override
+  R? visitAugmentationImportDirective(AugmentationImportDirective node) =>
+      visitUriBasedDirective(node);
+
+  @override
   R? visitAwaitExpression(AwaitExpression node) => visitExpression(node);
 
   @override
@@ -698,6 +702,12 @@
   }
 
   @override
+  R? visitAugmentationImportDirective(AugmentationImportDirective node) {
+    node.visitChildren(this);
+    return null;
+  }
+
+  @override
   R? visitAwaitExpression(AwaitExpression node) {
     node.visitChildren(this);
     return null;
@@ -1481,6 +1491,9 @@
   R? visitAssignmentExpression(AssignmentExpression node) => null;
 
   @override
+  R? visitAugmentationImportDirective(AugmentationImportDirective node) => null;
+
+  @override
   R? visitAwaitExpression(AwaitExpression node) => null;
 
   @override
@@ -1897,6 +1910,10 @@
   R? visitAssignmentExpression(AssignmentExpression node) => _throw(node);
 
   @override
+  R? visitAugmentationImportDirective(AugmentationImportDirective node) =>
+      _throw(node);
+
+  @override
   R? visitAwaitExpression(AwaitExpression node) => _throw(node);
 
   @override
@@ -2363,6 +2380,14 @@
   }
 
   @override
+  T? visitAugmentationImportDirective(AugmentationImportDirective node) {
+    stopwatch.start();
+    T? result = _baseVisitor.visitAugmentationImportDirective(node);
+    stopwatch.stop();
+    return result;
+  }
+
+  @override
   T? visitAwaitExpression(AwaitExpression node) {
     stopwatch.start();
     T? result = _baseVisitor.visitAwaitExpression(node);
@@ -3401,6 +3426,10 @@
   R? visitAssignmentExpression(AssignmentExpression node) => visitNode(node);
 
   @override
+  R? visitAugmentationImportDirective(AugmentationImportDirective node) =>
+      visitNode(node);
+
+  @override
   R? visitAwaitExpression(AwaitExpression node) => visitNode(node);
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index 65fc403..6c7347f 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -931,19 +931,17 @@
       if (directive is ExportDirective) {
         var builder = _serializeNamespaceDirective(directive);
         exports.add(builder);
+      } else if (directive is AugmentationImportDirectiveImpl) {
+        augmentations.add(
+          UnlinkedImportAugmentationDirective(
+            uri: directive.uri.stringValue,
+          ),
+        );
       } else if (directive is ImportDirectiveImpl) {
-        if (directive.augmentKeyword != null) {
-          augmentations.add(
-            UnlinkedImportAugmentationDirective(
-              uri: directive.uri.stringValue,
-            ),
-          );
-        } else {
-          var builder = _serializeNamespaceDirective(directive);
-          imports.add(builder);
-          if (builder.uri == 'dart:core') {
-            hasDartCoreImport = true;
-          }
+        var builder = _serializeNamespaceDirective(directive);
+        imports.add(builder);
+        if (builder.uri == 'dart:core') {
+          hasDartCoreImport = true;
         }
       } else if (directive is LibraryAugmentationDirective) {
         final uri = directive.uri;
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 998b077..7983d90 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -570,19 +570,17 @@
       if (directive is LibraryDirectiveImpl) {
         libraryNameNode = directive.name;
         directivesToResolve.add(directive);
+      } else if (directive is AugmentationImportDirective) {
+        // TODO(scheglov) implement
+        throw UnimplementedError();
       } else if (directive is ImportDirectiveImpl) {
-        if (directive.augmentKeyword != null) {
-          // TODO(scheglov) implement
-          throw UnimplementedError();
-        } else {
-          _resolveImportDirective(
-            directive: directive,
-            importElement: _libraryElement.imports[importIndex],
-            importState: _library.imports[importIndex],
-            libraryErrorReporter: libraryErrorReporter,
-          );
-          importIndex++;
-        }
+        _resolveImportDirective(
+          directive: directive,
+          importElement: _libraryElement.imports[importIndex],
+          importState: _library.imports[importIndex],
+          libraryErrorReporter: libraryErrorReporter,
+        );
+        importIndex++;
       } else if (directive is ExportDirectiveImpl) {
         _resolveExportDirective(
           directive: directive,
diff --git a/pkg/analyzer/lib/src/dart/ast/ast.dart b/pkg/analyzer/lib/src/dart/ast/ast.dart
index 7a61f73..463df80 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast.dart
@@ -875,6 +875,64 @@
   }
 }
 
+/// An augmentation import directive.
+///
+///    importDirective ::=
+///        [Annotation] 'import' 'augment' [StringLiteral] ';'
+class AugmentationImportDirectiveImpl extends UriBasedDirectiveImpl
+    implements AugmentationImportDirective {
+  @override
+  Token importKeyword;
+
+  @override
+  Token augmentKeyword;
+
+  @override
+  Token semicolon;
+
+  AugmentationImportDirectiveImpl({
+    required CommentImpl? comment,
+    required List<Annotation>? metadata,
+    required this.importKeyword,
+    required this.augmentKeyword,
+    required this.semicolon,
+    required StringLiteralImpl uri,
+  }) : super(comment, metadata, uri) {
+    _becomeParentOf(_uri);
+  }
+
+  @override
+  AugmentationImportElement? get element {
+    return super.element as AugmentationImportElement?;
+  }
+
+  @override
+  Token get endToken => semicolon;
+
+  @override
+  Token get firstTokenAfterCommentAndMetadata => keyword;
+
+  @override
+  Token get keyword => importKeyword;
+
+  @override
+  LibraryAugmentationElement? get uriElement {
+    return element?.augmentation;
+  }
+
+  @override
+  ChildEntities get _childEntities => super._childEntities
+    ..addToken('keyword', keyword)
+    ..addToken('augmentKeyword', augmentKeyword)
+    ..addNode('uri', uri)
+    ..addToken('semicolon', semicolon);
+
+  @override
+  E? accept<E>(AstVisitor<E> visitor) {
+    return visitor.visitAugmentationImportDirective(this);
+  }
+}
+
 /// An await expression.
 ///
 ///    awaitExpression ::=
@@ -6311,10 +6369,6 @@
 //         [Combinator]* ';'
 class ImportDirectiveImpl extends NamespaceDirectiveImpl
     implements ImportDirective {
-  /// The token representing the 'augment' keyword, or `null` if the import is
-  /// not a library augmentation import.
-  Token? augmentKeyword;
-
   /// The token representing the 'deferred' keyword, or `null` if the imported
   /// is not deferred.
   @override
@@ -6339,7 +6393,6 @@
       CommentImpl? comment,
       List<Annotation>? metadata,
       Token keyword,
-      this.augmentKeyword,
       StringLiteralImpl libraryUri,
       List<Configuration>? configurations,
       this.deferredKeyword,
@@ -6370,7 +6423,6 @@
   @override
   ChildEntities get _childEntities => super._childEntities
     ..addToken('keyword', keyword)
-    ..addToken('augmentKeyword', augmentKeyword)
     ..addNode('uri', uri)
     ..addToken('deferredKeyword', deferredKeyword)
     ..addToken('asKeyword', asKeyword)
diff --git a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
index 6bade88..0e45fe4 100644
--- a/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
+++ b/pkg/analyzer/lib/src/dart/ast/ast_factory.dart
@@ -735,22 +735,21 @@
           typeArgumentTypes: typeArgumentTypes);
 
   ImportDirectiveImpl importDirective(
-          Comment? comment,
-          List<Annotation>? metadata,
-          Token keyword,
-          StringLiteral libraryUri,
-          List<Configuration>? configurations,
-          Token? deferredKeyword,
-          Token? asKeyword,
-          SimpleIdentifier? prefix,
-          List<Combinator>? combinators,
-          Token semicolon,
-          {Token? augmentKeyword}) =>
+    Comment? comment,
+    List<Annotation>? metadata,
+    Token keyword,
+    StringLiteral libraryUri,
+    List<Configuration>? configurations,
+    Token? deferredKeyword,
+    Token? asKeyword,
+    SimpleIdentifier? prefix,
+    List<Combinator>? combinators,
+    Token semicolon,
+  ) =>
       ImportDirectiveImpl(
           comment as CommentImpl?,
           metadata,
           keyword,
-          augmentKeyword,
           libraryUri as StringLiteralImpl,
           configurations,
           deferredKeyword,
diff --git a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
index becc0af..5a83d20 100644
--- a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
@@ -78,6 +78,14 @@
   }
 
   @override
+  void visitAugmentationImportDirective(AugmentationImportDirective node) {
+    _visitNodeList(node.metadata, separator: ' ', suffix: ' ');
+    sink.write('import augment ');
+    _visitNode(node.uri);
+    sink.write(';');
+  }
+
+  @override
   void visitAwaitExpression(AwaitExpression node) {
     sink.write('await ');
     _visitNode(node.expression);
diff --git a/pkg/analyzer/lib/src/dart/ast/utilities.dart b/pkg/analyzer/lib/src/dart/ast/utilities.dart
index f024ba6..46b2fb6 100644
--- a/pkg/analyzer/lib/src/dart/ast/utilities.dart
+++ b/pkg/analyzer/lib/src/dart/ast/utilities.dart
@@ -162,6 +162,17 @@
   }
 
   @override
+  bool visitAugmentationImportDirective(AugmentationImportDirective node) {
+    final other = _other as AugmentationImportDirective;
+    return isEqualNodes(
+            node.documentationComment, other.documentationComment) &&
+        _isEqualNodeLists(node.metadata, other.metadata) &&
+        isEqualTokens(node.keyword, other.keyword) &&
+        isEqualNodes(node.uri, other.uri) &&
+        isEqualTokens(node.semicolon, other.semicolon);
+  }
+
+  @override
   bool visitAwaitExpression(AwaitExpression node) {
     AwaitExpression other = _other as AwaitExpression;
     return isEqualTokens(node.awaitKeyword, other.awaitKeyword) &&
@@ -1698,6 +1709,13 @@
   }
 
   @override
+  bool visitAugmentationImportDirective(
+    covariant AugmentationImportDirectiveImpl node,
+  ) {
+    return visitUriBasedDirective(node);
+  }
+
+  @override
   bool visitAwaitExpression(covariant AwaitExpressionImpl node) {
     if (identical(node.expression, _oldNode)) {
       node.expression = _newNode as Expression;
diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart
index a84eac9..b40172c 100644
--- a/pkg/analyzer/lib/src/fasta/ast_builder.dart
+++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart
@@ -1824,7 +1824,7 @@
     var asKeyword = pop(NullValue.As) as Token?;
     var prefix = pop(NullValue.Prefix) as SimpleIdentifier?;
     var configurations = pop() as List<Configuration>?;
-    var uri = pop() as StringLiteral;
+    var uri = pop() as StringLiteralImpl;
     var metadata = pop() as List<Annotation>?;
     var comment = _findComment(metadata, importKeyword);
 
@@ -1839,18 +1839,33 @@
       }
     }
 
-    directives.add(ast.importDirective(
-        comment,
-        metadata,
-        importKeyword,
-        uri,
-        configurations,
-        deferredKeyword,
-        asKeyword,
-        prefix,
-        combinators,
-        semicolon ?? Tokens.semicolon(),
-        augmentKeyword: augmentToken));
+    if (augmentToken != null) {
+      directives.add(
+        AugmentationImportDirectiveImpl(
+          comment: comment,
+          uri: uri,
+          importKeyword: importKeyword,
+          augmentKeyword: augmentToken,
+          metadata: metadata,
+          semicolon: semicolon ?? Tokens.semicolon(),
+        ),
+      );
+    } else {
+      directives.add(
+        ast.importDirective(
+          comment,
+          metadata,
+          importKeyword,
+          uri,
+          configurations,
+          deferredKeyword,
+          asKeyword,
+          prefix,
+          combinators,
+          semicolon ?? Tokens.semicolon(),
+        ),
+      );
+    }
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/test_utilities/find_node.dart b/pkg/analyzer/lib/src/test_utilities/find_node.dart
index 2c0aee8..e933186 100644
--- a/pkg/analyzer/lib/src/test_utilities/find_node.dart
+++ b/pkg/analyzer/lib/src/test_utilities/find_node.dart
@@ -55,6 +55,10 @@
     return _node(search, (n) => n is AssignmentExpression);
   }
 
+  AugmentationImportDirective augmentationImportDirective(String search) {
+    return _node(search, (n) => n is AugmentationImportDirective);
+  }
+
   AwaitExpression awaitExpression(String search) {
     return _node(search, (n) => n is AwaitExpression);
   }
diff --git a/pkg/analyzer/test/generated/utilities_test.dart b/pkg/analyzer/test/generated/utilities_test.dart
index 4db863b..711e8e8 100644
--- a/pkg/analyzer/test/generated/utilities_test.dart
+++ b/pkg/analyzer/test/generated/utilities_test.dart
@@ -277,6 +277,24 @@
     );
   }
 
+  void test_augmentationImportDirective() {
+    var findNode = _parseStringToFindNode(r'''
+@myA1
+@myA2
+import augment 'a.dart';
+import augment 'b.dart';
+''');
+    var node = findNode.augmentationImportDirective('a.dart');
+    _assertAnnotatedNode(node);
+    _assertReplacementForChildren<AugmentationImportDirective>(
+      destination: findNode.augmentationImportDirective('b.dart'),
+      source: node,
+      childAccessors: [
+        (node) => node.uri,
+      ],
+    );
+  }
+
   void test_awaitExpression() {
     var findNode = _parseStringToFindNode(r'''
 void f() async {
diff --git a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
index 9a9a099..9fd743c 100644
--- a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
+++ b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
@@ -105,6 +105,16 @@
             TokenType.EQ, AstTestFactory.identifier3("b")));
   }
 
+  void test_visitAugmentationImportDirective() {
+    var findNode = _parseStringToFindNode(r'''
+import augment 'a.dart';
+''');
+    _assertSource(
+      "import augment 'a.dart';",
+      findNode.augmentationImportDirective('import'),
+    );
+  }
+
   void test_visitAwaitExpression() {
     var findNode = _parseStringToFindNode(r'''
 void f() async => await e;