anchor.diff:      return Place.none();
     }
   }
  +
  +class CompletionCoverageMetrics extends AbstractCompletionMetricsComputer {
  +  int includedCount = 0;
  +  int notIncludedCount = 0;
  +  var completionMissedTokenCounter = Counter('missing completion counter');
  +  var completionKindCounter = Counter('completion kind counter');
  +  var completionElementKindCounter = Counter('completion element kind counter');
  +  var mRRComputer = MeanReciprocalRankComputer();
  +
  +  CompletionCoverageMetrics(String rootPath) : super(rootPath);
  +
  +  @override
  +  bool get doComputeCompletionsFromAnalysisServer => true;
  +
  +  @override
  +  void forEachExpectedCompletion(ExpectedCompletion expectedCompletion,
  +      List<CompletionSuggestion> suggestions) {
  +    assert(suggestions != null);
  +
  +    var place = AbstractCompletionMetricsComputer.placementInSuggestionList(
  +        suggestions, expectedCompletion);
  +
  +    mRRComputer.addReciprocalRank(place);
  +
  +    if (place.denominator != 0) {
  +      includedCount++;
  +    } else {
  +      notIncludedCount++;
  +
  +      completionMissedTokenCounter.count(expectedCompletion.completion);
  +      completionKindCounter.count(expectedCompletion.kind.toString());
  +      completionElementKindCounter
  +          .count(expectedCompletion.elementKind.toString());
  +
  +      // The format "/file/path/foo.dart:3:4" makes for easier input
  +      // with the Files dialog in IntelliJ
  +      print(
  +          '$currentFilePath:${expectedCompletion.lineNumber}:${expectedCompletion.columnNumber}');
  +      print(
  +          '\tdid not include the expected completion: \"${expectedCompletion.completion}\", completion kind: ${expectedCompletion.kind.toString()}, element kind: ${expectedCompletion.elementKind.toString()}');
  +      print('');
  +    }
  +  }
  +
  +  @override
  +  void printAndClearComputers() {
  +    final totalCompletionCount = includedCount + notIncludedCount;
  +    final percentIncluded = includedCount / totalCompletionCount;
  +    final percentNotIncluded = 1 - percentIncluded;
  +
  +    completionMissedTokenCounter.printCounterValues();
  +    print('');
  +
  +    completionKindCounter.printCounterValues();
  +    print('');
  +
  +    completionElementKindCounter.printCounterValues();
  +    print('');
  +
  +    mRRComputer.printMean();
  +    print('');
  +
  +    print('Summary for $_rootPath:');
  +    print('Total number of completion tests   = $totalCompletionCount');
  +    print(
  +        'Number of successful completions   = $includedCount (${printPercentage(percentIncluded)})');
  +    print(
  +        'Number of unsuccessful completions = $notIncludedCount (${printPercentage(percentNotIncluded)})');
  +
  +    includedCount = 0;
  +    notIncludedCount = 0;
  +    completionMissedTokenCounter.clear();
  +    completionKindCounter.clear();
  +    completionElementKindCounter.clear();
  +    mRRComputer.clear();
  +  }
  +}
  +
  +class RelevanceAnalyzerMetrics extends AbstractCompletionMetricsComputer {
  +  /// A non-null list of [RelevanceAnalyzer]s to execute when computing the
  +  /// completion metrics.
  +  final List<RelevanceAnalyzer> _relevanceAnalyzers;
  +
  +  RelevanceAnalyzerMetrics(String rootPath, this._relevanceAnalyzers)
  +      : assert(_relevanceAnalyzers.isNotEmpty),
  +        super(rootPath);
  +
  +  @override
  +  bool get doComputeCompletionsFromAnalysisServer => false;
  +
  +  List<RelevanceAnalyzer> get relevanceAnalyzers => _relevanceAnalyzers;
  +
  +  @override
  +  void forEachExpectedCompletion(ExpectedCompletion expectedCompletion,
  +      List<CompletionSuggestion> suggestions) async {
  +    assert(suggestions == null);
  +    var dartCompletionRequest = await DartCompletionRequestImpl.from(
  +        CompletionRequestImpl(
  +            resolvedUnitResult, expectedCompletion.offset, _performance));
  +
  +    _relevanceAnalyzers.forEach((analyzer) =>
  +        analyzer.report(expectedCompletion, dartCompletionRequest));
  +  }
  +
  +  @override
  +  void printAndClearComputers() {
  +    print('\nRelevance Analysis (count: ${_relevanceAnalyzers.length}):');
  +    _relevanceAnalyzers.forEach((analyzer) => analyzer
  +      ..printData()
  +      ..clear());
  +  }
  +}
  
positive.code:         }
        }
      }
  
      final totalCompletionCount = includedCount + notIncludedCount;
      final percentIncluded = includedCount / totalCompletionCount;
      final percentNotIncluded = 1 - percentIncluded;
  
      completionMissedTokenCounter.printCounterValues();
      print('');
  
      completionKindCounter.printCounterValues();
      print('');
  
      completionElementKindCounter.printCounterValues();
      print('');
  
      mRRComputer.printMean();
      print('');
  
      print('Summary for $_rootPath:');
      print('Total number of completion tests   = $totalCompletionCount');
      print(
          'Number of successful completions   = $includedCount (${printPercentage(percentIncluded)})');
      print(
          'Number of unsuccessful completions = $notIncludedCount (${printPercentage(percentNotIncluded)})');
  
      if (_relevanceAnalyzers.isNotEmpty) {
        print('\nRelevance Analysis:');
        _relevanceAnalyzers.forEach((analyzer) => analyzer
          ..printData()
          ..clear());
      }
  
      includedCount = 0;
      notIncludedCount = 0;
      completionMissedTokenCounter.clear();
      completionKindCounter.clear();
      completionElementKindCounter.clear();
      mRRComputer.clear();
    }
  
    Future<List<CompletionSuggestion>> _computeCompletionSuggestions(
        ResolvedUnitResult resolvedUnitResult, int offset,
  
negative.code: 
    /// Given some [ResolvedUnitResult] return the first error of high severity
    /// if such an error exists, null otherwise.
    static err.AnalysisError getFirstErrorOrNull(
        ResolvedUnitResult resolvedUnitResult) {
      for (var error in resolvedUnitResult.errors) {
        if (error.severity == Severity.error) {
  