Special case in completion_metrics.dart, some keyword suggestions come out of the DAS completion engine such as "import '';", instead of "import"

Change-Id: I6de32a6a2c770a78a2fd3e1103969b5f2f62f9e1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/131716
Commit-Queue: Jaime Wren <jwren@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
diff --git a/pkg/analysis_server/tool/completion_metrics/completion_metrics.dart b/pkg/analysis_server/tool/completion_metrics/completion_metrics.dart
index f97c717..792ee56 100644
--- a/pkg/analysis_server/tool/completion_metrics/completion_metrics.dart
+++ b/pkg/analysis_server/tool/completion_metrics/completion_metrics.dart
@@ -20,6 +20,8 @@
 int includedCount = 0;
 int notIncludedCount = 0;
 
+const bool doPrintExpectedCompletions = true;
+
 main() {
   List<String> analysisRoots = [''];
   _computeCompletionMetrics(PhysicalResourceProvider.INSTANCE, analysisRoots);
@@ -29,7 +31,7 @@
 Future _computeCompletionMetrics(
     ResourceProvider resourceProvider, List<String> analysisRoots) async {
   for (var root in analysisRoots) {
-    print('Analyzing root... $root');
+    print('Analyzing root: $root');
     final collection = AnalysisContextCollection(
       includedPaths: [root],
       resourceProvider: resourceProvider,
@@ -38,7 +40,6 @@
     for (var context in collection.contexts) {
       for (var filePath in context.contextRoot.analyzedFiles()) {
         if (AnalysisEngine.isDartFileName(filePath)) {
-          print('  Analyzing file: $filePath');
           try {
             final result =
                 await context.currentSession.getResolvedUnit(filePath);
@@ -62,10 +63,12 @@
                   _placementInSuggestionList(suggestions, expectedCompletion);
               if (fraction.y != 0) {
                 includedCount++;
-//                print(
-//                    '  Bug - $filePath@${entity.offset} did not include ${entity.toString()}');
               } else {
                 notIncludedCount++;
+                if (doPrintExpectedCompletions) {
+                  print(
+                      '\t$filePath at ${expectedCompletion.offset} did not include \'${expectedCompletion.completion}\'');
+                }
               }
             }
           } catch (e) {
diff --git a/pkg/analysis_server/tool/completion_metrics/visitors.dart b/pkg/analysis_server/tool/completion_metrics/visitors.dart
index 5800f37..f569338 100644
--- a/pkg/analysis_server/tool/completion_metrics/visitors.dart
+++ b/pkg/analysis_server/tool/completion_metrics/visitors.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analysis_server/src/protocol_server.dart' as protocol;
+import 'package:analysis_server/src/services/completion/dart/keyword_contributor.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/syntactic_entity.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
@@ -21,8 +22,37 @@
   safelyRecordEntity(SyntacticEntity entity,
       {protocol.CompletionSuggestionKind kind,
       protocol.ElementKind elementKind}) {
+    // Only record if this entity is not null, has a length, etc.
     if (entity != null && entity.offset > 0 && entity.length > 0) {
-      expectedCompletions.add(ExpectedCompletion(entity, kind, elementKind));
+      // Some special cases in the if and if-else blocks, 'import' from the
+      // DAS is "import '';" which we want to be sure to match.
+      if (entity.toString() == 'async') {
+        expectedCompletions.add(ExpectedCompletion.specialCompletionString(
+            entity, ASYNC_STAR, kind, elementKind));
+      } else if (entity.toString() == 'default') {
+        expectedCompletions.add(ExpectedCompletion.specialCompletionString(
+            entity, DEFAULT_COLON, kind, elementKind));
+      } else if (entity.toString() == 'deferred') {
+        expectedCompletions.add(ExpectedCompletion.specialCompletionString(
+            entity, DEFERRED_AS, kind, elementKind));
+      } else if (entity.toString() == 'export') {
+        expectedCompletions.add(ExpectedCompletion.specialCompletionString(
+            entity, EXPORT_STATEMENT, kind, elementKind));
+      } else if (entity.toString() == 'import') {
+        expectedCompletions.add(ExpectedCompletion.specialCompletionString(
+            entity, IMPORT_STATEMENT, kind, elementKind));
+      } else if (entity.toString() == 'part') {
+        expectedCompletions.add(ExpectedCompletion.specialCompletionString(
+            entity, PART_STATEMENT, kind, elementKind));
+      } else if (entity.toString() == 'sync') {
+        expectedCompletions.add(ExpectedCompletion.specialCompletionString(
+            entity, SYNC_STAR, kind, elementKind));
+      } else if (entity.toString() == 'yield') {
+        expectedCompletions.add(ExpectedCompletion.specialCompletionString(
+            entity, YIELD_STAR, kind, elementKind));
+      } else {
+        expectedCompletions.add(ExpectedCompletion(entity, kind, elementKind));
+      }
     }
   }
 
@@ -60,14 +90,25 @@
 
 class ExpectedCompletion {
   final SyntacticEntity _entity;
+
+  /// Some completions are special cased from the DAS "import" for instance is
+  /// suggested as a completion "import '';", the completion string here in this
+  /// instance would have the value "import '';".
+  final String _completionString;
+
   final protocol.CompletionSuggestionKind _kind;
+
   final protocol.ElementKind _elementKind;
 
-  ExpectedCompletion(this._entity, this._kind, this._elementKind);
+  ExpectedCompletion(this._entity, this._kind, this._elementKind)
+      : _completionString = null;
+
+  ExpectedCompletion.specialCompletionString(
+      this._entity, this._completionString, this._kind, this._elementKind);
 
   SyntacticEntity get syntacticEntity => _entity;
 
-  String get completion => _entity.toString();
+  String get completion => _completionString ?? _entity.toString();
 
   int get offset => _entity.offset;