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:     );
  
      for (var context in collection.contexts) {
        var declarationsTracker = DeclarationsTracker(
            MemoryByteStore(), PhysicalResourceProvider.INSTANCE);
        declarationsTracker.addContext(context);
  
        while (declarationsTracker.hasWork) {
          declarationsTracker.doWork();
        }
  
        for (var filePath in context.contextRoot.analyzedFiles()) {
          if (AnalysisEngine.isDartFileName(filePath)) {
            try {
              // TODO (jwren) break this block into one or more additional methods
              final resolvedUnitResult =
                  await context.currentSession.getResolvedUnit(filePath);
  
              var error = _getFirstErrorOrNull(resolvedUnitResult);
              if (error != null) {
                print('File $filePath skipped due to errors such as:');
                print('  ${error.toString()}');
  
negative.code:     return null;
    }
  
    static Place placementInSuggestionList(List<CompletionSuggestion> suggestions,
        ExpectedCompletion expectedCompletion) {
      var placeCounter = 1;
      for (var completionSuggestion in suggestions) {
  