Rearrange comment references into a class (#1831)

diff --git a/lib/src/markdown_processor.dart b/lib/src/markdown_processor.dart
index c29222e..d8a8cac 100644
--- a/lib/src/markdown_processor.dart
+++ b/lib/src/markdown_processor.dart
@@ -199,23 +199,7 @@
   if (refModelElement == null && element is ModelElement) {
     Class preferredClass = _getPreferredClass(element);
     refModelElement =
-        _findRefElementInLibrary(codeRef, element, commentRefs, preferredClass);
-  }
-
-  // This is faster but does not know about libraries without imports in the
-  // current context; try only as a last resort.
-  // TODO(jcollins-g): make analyzer comment references dartdoc-canonicalization-aware?
-  if (refModelElement == null) {
-    Element refElement = _getRefElementFromCommentRefs(commentRefs, codeRef);
-    if (refElement != null) {
-      refModelElement = new ModelElement.fromElement(
-          _getRefElementFromCommentRefs(commentRefs, codeRef),
-          element.packageGraph);
-      refModelElement =
-          refModelElement.canonicalModelElement ?? refModelElement;
-    }
-  } else {
-    assert(refModelElement is! Accessor);
+        new _MarkdownCommentReference(codeRef, element, commentRefs, preferredClass).computeReferredElement();
   }
 
   // Did not find it anywhere.
@@ -285,155 +269,159 @@
   return null;
 }
 
-/// Returns true if this is a constructor we should consider in
-/// _findRefElementInLibrary, or if this isn't a constructor.
-bool _ConsiderIfConstructor(String codeRef, ModelElement modelElement) {
-  if (modelElement is! Constructor) return true;
-  if (codeRef.contains(isConstructor)) return true;
-  Constructor aConstructor = modelElement;
-  List<String> codeRefParts = codeRef.split('.');
-  if (codeRefParts.length > 1) {
-    // Pick the last two parts, in case a specific library was part of the
-    // codeRef.
-    if (codeRefParts[codeRefParts.length - 1] ==
-        codeRefParts[codeRefParts.length - 2]) {
-      // Foobar.Foobar -- assume they really do mean the constructor for this class.
+
+/// Represents a single comment reference.
+class _MarkdownCommentReference {
+  /// The code reference text.
+  final String codeRef;
+  /// The element containing the code reference.
+  final Warnable element;
+  /// A list of [CommentReference]s from the analyzer.
+  final List<CommentReference> commentRefs;
+  /// Disambiguate inheritance with this class.
+  final Class preferredClass;
+  /// Current results.  Input/output of all _find and _reduce methods.
+  Set<ModelElement> results;
+  /// codeRef with any leading constructor string, stripped.
+  String codeRefChomped;
+  /// Library associated with this element.
+  Library library;
+  /// PackageGraph associated with this element.
+  PackageGraph packageGraph;
+
+  _MarkdownCommentReference(this.codeRef, this.element, this.commentRefs, this.preferredClass) {
+    assert(element != null);
+    assert(element.packageGraph.allLibrariesAdded);
+
+    codeRefChomped = codeRef.replaceFirst(isConstructor, '');
+    library = element is ModelElement ? (element as ModelElement).library : null;
+    packageGraph = library.packageGraph;
+  }
+
+  /// Calculate reference to a ModelElement.
+  ///
+  /// Uses a series of calls to the _find* methods in this class to get one
+  /// or more possible [ModelElement] matches, then uses the _reduce* methods
+  /// in this class to try to bring it to a single ModelElement.  Calls
+  /// [element.warn] for [PackageWarning.ambiguousDocReference] if there
+  /// are more than one, but does not warn otherwise.
+  ModelElement computeReferredElement() {
+    results = new Set();
+    // TODO(jcollins-g): A complex package winds up spending a lot of cycles in here.  Optimize.
+    for (void Function() findMethod in [
+      // This might be an operator.  Strip the operator prefix and try again.
+      _findWithoutOperatorPrefix,
+      // Oh, and someone might have some type parameters or other garbage.
+      _findWithoutTrailingIgnoreStuff,
+      // Oh, and someone might have thrown on a 'const' or 'final' in front.
+      _findWithoutLeadingIgnoreStuff,
+      // Maybe this ModelElement has parameters, and this is one of them.
+      // We don't link these, but this keeps us from emitting warnings.  Be sure to
+      // get members of parameters too.
+      _findParameters,
+      // Maybe this ModelElement has type parameters, and this is one of them.
+      _findTypeParameters,
+      // This could be local to the class, look there first.
+      _findWithinTryClasses,
+      // We now need the ref element cache to keep from repeatedly searching [Package.allModelElements].
+      // But if not, look for a fully qualified match.  (That only makes sense
+      // if the codeRef might be qualified, and contains periods.)
+      _findWithinRefElementCache,
+      // Only look for partially qualified matches if we didn't find a fully qualified one.
+      _findPartiallyQualifiedMatches,
+      // Only look for partially qualified matches if we didn't find a fully qualified one.
+      _findGlobalWithinRefElementCache,
+      // This could conceivably be a reference to an enum member.  They don't show up in allModelElements.
+      _findEnumReferences,
+      // Use the analyzer to resolve a comment reference.
+      _findAnalyzerReferences]) {
+      findMethod();
+      // Remove any "null" objects after each step of trying to add to results.
+      // TODO(jcollins-g): Eliminate all situations where nulls can be added
+      // to the results set.
+      results.remove(null);
+      if (results.isNotEmpty) break;
+    }
+
+    if (results.length > 1) {
+      // This isn't C++.  References to class methods are slightly expensive
+      // in Dart so don't build that list unless you need to.
+      for (void Function() reduceMethod in [
+        // If this name could refer to a class or a constructor, prefer the class.
+        _reducePreferClass,
+        // If a result is actually in this library, prefer that.
+        _reducePreferResultsInSameLibrary,
+        // If a result is accessible in this library, prefer that.
+        _reducePreferResultsAccessibleInSameLibrary,
+        // This may refer to an element with the same name in multiple libraries
+        // in an external package, e.g. Matrix4 in vector_math and vector_math_64.
+        // Disambiguate by attempting to figure out which of them our package
+        // is actually using by checking the import/export graph.
+        _reducePreferLibrariesInLocalImportExportGraph,
+        // If a result's fully qualified name has pieces of the comment reference,
+        // prefer that.
+        _reducePreferReferencesIncludingFullyQualifiedName,
+        // Prefer the Dart analyzer's resolution of comment references.  We can't
+        // start from this because of the differences in Dartdoc canonicalization.
+        _reducePreferAnalyzerResolution,
+      ]) {
+        reduceMethod();
+        if (results.length <= 1) break;
+      }
+    }
+
+    ModelElement result;
+    // TODO(jcollins-g): further disambiguations based on package information?
+    if (results.isEmpty) {
+      result = null;
+    } else if (results.length == 1) {
+      result = results.first;
+    } else {
+      // Squelch ambiguous doc reference warnings for parameters, because we
+      // don't link those anyway.
+      if (!results.every((r) => r is Parameter)) {
+        element.warn(PackageWarning.ambiguousDocReference,
+            message:
+            "[$codeRef] => ${results.map((r) => "'${r.fullyQualifiedName}'").join(", ")}");
+      }
+      result = results.first;
+    }
+    return result;
+  }
+
+  /// Returns true if this is a constructor we should consider due to its
+  /// name and the code reference, or if this isn't a constructor.  False
+  /// otherwise.
+  bool _ConsiderIfConstructor(ModelElement modelElement) {
+    // TODO(jcollins-g): Rewrite this to handle constructors in a less hacky way
+    if (modelElement is! Constructor) return true;
+    if (codeRef.contains(isConstructor)) return true;
+    Constructor aConstructor = modelElement;
+    List<String> codeRefParts = codeRef.split('.');
+    if (codeRefParts.length > 1) {
+      // Pick the last two parts, in case a specific library was part of the
+      // codeRef.
+      if (codeRefParts[codeRefParts.length - 1] ==
+          codeRefParts[codeRefParts.length - 2]) {
+        // Foobar.Foobar -- assume they really do mean the constructor for this class.
+        return true;
+      }
+    }
+    if (aConstructor.name != aConstructor.enclosingElement.name) {
+      // This isn't a default constructor so treat it like any other member.
       return true;
     }
-  }
-  if (aConstructor.name != aConstructor.enclosingElement.name) {
-    // This isn't a default constructor so treat it like any other member.
-    return true;
-  }
-  return false;
-}
-
-// Basic map of reference to ModelElement, for cases where we're searching
-// outside of scope.
-// TODO(jcollins-g): Rewrite this to handle constructors in a less hacky way
-// TODO(jcollins-g): This function breaks down naturally into many helpers, extract them
-// TODO(jcollins-g): Subcomponents of this function shouldn't be adding nulls to results, strip the
-//                   removes out that are gratuitous and debug the individual pieces.
-// TODO(jcollins-g): A complex package winds up spending a lot of cycles in here.  Optimize.
-ModelElement _findRefElementInLibrary(String codeRef, Warnable element,
-    List<CommentReference> commentRefs, Class preferredClass) {
-  assert(element != null);
-  assert(element.packageGraph.allLibrariesAdded);
-
-  String codeRefChomped = codeRef.replaceFirst(isConstructor, '');
-
-  final Library library = element is ModelElement ? element.library : null;
-  final PackageGraph packageGraph = library.packageGraph;
-  final Set<ModelElement> results = new Set();
-
-  // This might be an operator.  Strip the operator prefix and try again.
-  if (results.isEmpty && codeRef.startsWith(operatorPrefix)) {
-    String newCodeRef = codeRef.replaceFirst(operatorPrefix, '');
-    return _findRefElementInLibrary(
-        newCodeRef, element, commentRefs, preferredClass);
+    return false;
   }
 
-  // Remove any "null" objects after each step of trying to add to results.
-  // TODO(jcollins-g): Eliminate all situations where nulls can be added
-  // to the results set.
-  results.remove(null);
-  // Oh, and someone might have some type parameters or other garbage.
-  if (results.isEmpty && codeRef.contains(trailingIgnoreStuff)) {
-    String newCodeRef = codeRef.replaceFirst(trailingIgnoreStuff, '');
-    return _findRefElementInLibrary(
-        newCodeRef, element, commentRefs, preferredClass);
-  }
-
-  results.remove(null);
-  // Oh, and someone might have thrown on a 'const' or 'final' in front.
-  if (results.isEmpty && codeRef.contains(leadingIgnoreStuff)) {
-    String newCodeRef = codeRef.replaceFirst(leadingIgnoreStuff, '');
-    return _findRefElementInLibrary(
-        newCodeRef, element, commentRefs, preferredClass);
-  }
-
-  // Maybe this ModelElement has parameters, and this is one of them.
-  // We don't link these, but this keeps us from emitting warnings.  Be sure to
-  // get members of parameters too.
-  // TODO(jcollins-g): link to classes that are the types of parameters, where known
-  if (element is ModelElement) {
-    results.addAll(element.allParameters.where((p) =>
-        p.name == codeRefChomped || codeRefChomped.startsWith("${p.name}.")));
-  }
-
-  results.remove(null);
-  // Maybe this ModelElement has type parameters, and this is one of them.
-  if (results.isEmpty) {
-    if (element is TypeParameters) {
-      results.addAll(element.typeParameters.where((p) =>
-          p.name == codeRefChomped || codeRefChomped.startsWith("${p.name}.")));
-    }
-  }
-  results.remove(null);
-
-  // This could be local to the class, look there first.
-  _findWithinTryClasses(results, preferredClass, element, codeRefChomped, codeRef, packageGraph);
-  results.remove(null);
-
-  // We now need the ref element cache to keep from repeatedly searching [Package.allModelElements].
-  // But if not, look for a fully qualified match.  (That only makes sense
-  // if the codeRef might be qualified, and contains periods.)
-  _findWithinRefElementCache(results, codeRefChomped, packageGraph, codeRef);
-  results.remove(null);
-
-  // Only look for partially qualified matches if we didn't find a fully qualified one.
-  _findPartiallyQualifiedMatches(results, library, codeRef, codeRefChomped, packageGraph, preferredClass);
-  results.remove(null);
-
-  // And if we still haven't found anything, just search the whole ball-of-wax.
-  _findGlobalWithinRefElementCache(results, packageGraph, codeRefChomped);
-  results.remove(null);
-
-  // This could conceivably be a reference to an enum member.  They don't show up in allModelElements.
-  // TODO(jcollins-g): Put enum members in allModelElements with useful hrefs without blowing up other assumptions about what that means.
-  // TODO(jcollins-g): This doesn't provide good warnings if an enum and class have the same name in different libraries in the same package.  Fix that.
-  _findEnumReferences(results, codeRefChomped, packageGraph);
-  results.remove(null);
-
-  if (results.length > 1) {
-    // If this name could refer to a class or a constructor, prefer the class.
-    if (results.any((r) => r is Class)) {
-      results.removeWhere((r) => r is Constructor);
+  void _reducePreferAnalyzerResolution() {
+    Element refElement = _getRefElementFromCommentRefs(commentRefs, codeRef);
+    if (results.any((me) => me.element == refElement)) {
+      results.removeWhere((me) => me.element != refElement);
     }
   }
 
-  if (results.length > 1) {
-    if (results.any((r) => r.library?.packageName == library.packageName)) {
-      results.removeWhere((r) => r.library?.packageName != library.packageName);
-    }
-  }
-
-  if (results.length > 1 && element is ModelElement) {
-    // Attempt to disambiguate using the library.
-    // TODO(jcollins-g): we could have saved ourselves some work by using the analyzer
-    //                   to search the namespace, somehow.  Do that instead.
-    if (results.any((r) => r.element.isAccessibleIn(element.library.element))) {
-      results.removeWhere(
-          (r) => !r.element.isAccessibleIn(element.library.element));
-    }
-  }
-
-  if (results.length > 1) {
-    // This may refer to an element with the same name in multiple libraries
-    // in an external package, e.g. Matrix4 in vector_math and vector_math_64.
-    // Disambiguate by attempting to figure out which of them our package
-    // is actually using by checking the import/export graph.
-    if (results.any(
-        (r) => library.packageImportedExportedLibraries.contains(r.library))) {
-      results.removeWhere(
-          (r) => !library.packageImportedExportedLibraries.contains(r.library));
-    }
-  }
-
-  // TODO(jcollins-g): This is only necessary because we had to jettison commentRefs
-  // as a way to figure this out.  We could reintroduce commentRefs, or we could
-  // compute this via other means.
-  if (results.length > 1) {
+  void _reducePreferReferencesIncludingFullyQualifiedName() {
     String startName = "${element.fullyQualifiedName}.";
     String realName = "${element.fullyQualifiedName}.${codeRefChomped}";
     if (results.any((r) => r.fullyQualifiedName == realName)) {
@@ -444,39 +432,76 @@
     }
   }
 
-  // TODO(jcollins-g): As a last resort, try disambiguation with commentRefs.
-  //                   Maybe one of these is the same as what's resolvable with
-  //                   the analyzer, and if so, drop the others.  We can't
-  //                   do this in reverse order because commentRefs don't know
-  //                   about dartdoc canonicalization.
-  if (results.length > 1) {
-    Element refElement = _getRefElementFromCommentRefs(commentRefs, codeRef);
-    if (results.any((me) => me.element == refElement)) {
-      results.removeWhere((me) => me.element != refElement);
+  void _reducePreferLibrariesInLocalImportExportGraph() {
+    if (results.any(
+            (r) => library.packageImportedExportedLibraries.contains(r.library))) {
+      results.removeWhere(
+              (r) => !library.packageImportedExportedLibraries.contains(r.library));
     }
   }
 
-  ModelElement result;
-  // TODO(jcollins-g): further disambiguations based on package information?
-  if (results.isEmpty) {
-    result = null;
-  } else if (results.length == 1) {
-    result = results.first;
-  } else {
-    // Squelch ambiguous doc reference warnings for parameters, because we
-    // don't link those anyway.
-    if (!results.every((r) => r is Parameter)) {
-      element.warn(PackageWarning.ambiguousDocReference,
-          message:
-              "[$codeRef] => ${results.map((r) => "'${r.fullyQualifiedName}'").join(", ")}");
+  void _reducePreferResultsAccessibleInSameLibrary() {
+    // TODO(jcollins-g): we could have saved ourselves some work by using the analyzer
+    //                   to search the namespace, somehow.  Do that instead.
+    if (element is ModelElement && results.any((r) => r.element.isAccessibleIn((element as ModelElement).library.element))) {
+      results.removeWhere(
+              (r) => !r.element.isAccessibleIn((element as ModelElement).library.element));
     }
-    result = results.first;
   }
-  return result;
-}
 
-void _findEnumReferences(Set<ModelElement> results, String codeRefChomped, PackageGraph packageGraph) {
-  if (results.isEmpty) {
+  void _reducePreferResultsInSameLibrary() {
+    if (results.any((r) => r.library?.packageName == library.packageName)) {
+      results.removeWhere((r) => r.library?.packageName != library.packageName);
+    }
+  }
+
+  void _reducePreferClass() {
+    if (results.any((r) => r is Class)) {
+      results.removeWhere((r) => r is Constructor);
+    }
+  }
+
+  void _findTypeParameters() {
+    if (element is TypeParameters) {
+      results.addAll((element as TypeParameters).typeParameters.where((p) =>
+      p.name == codeRefChomped || codeRefChomped.startsWith("${p.name}.")));
+    }
+  }
+
+  void _findParameters() {
+    if (element is ModelElement) {
+      results.addAll((element as ModelElement).allParameters.where((p) =>
+      p.name == codeRefChomped || codeRefChomped.startsWith("${p.name}.")));
+    }
+  }
+
+  void _findWithoutLeadingIgnoreStuff() {
+    if (codeRef.contains(leadingIgnoreStuff)) {
+      String newCodeRef = codeRef.replaceFirst(leadingIgnoreStuff, '');
+      results.add(new _MarkdownCommentReference(
+          newCodeRef, element, commentRefs, preferredClass).computeReferredElement());
+    }
+  }
+
+  void _findWithoutTrailingIgnoreStuff() {
+    if (codeRef.contains(trailingIgnoreStuff)) {
+      String newCodeRef = codeRef.replaceFirst(trailingIgnoreStuff, '');
+      results.add(new _MarkdownCommentReference(
+          newCodeRef, element, commentRefs, preferredClass).computeReferredElement());
+    }
+  }
+
+  void _findWithoutOperatorPrefix() {
+    if (codeRef.startsWith(operatorPrefix)) {
+      String newCodeRef = codeRef.replaceFirst(operatorPrefix, '');
+      results.add(new _MarkdownCommentReference(
+          newCodeRef, element, commentRefs, preferredClass).computeReferredElement());
+    }
+  }
+
+  void _findEnumReferences() {
+    // TODO(jcollins-g): Put enum members in allModelElements with useful hrefs without blowing up other assumptions about what that means.
+    // TODO(jcollins-g): This doesn't provide good warnings if an enum and class have the same name in different libraries in the same package.  Fix that.
     List<String> codeRefChompedParts = codeRefChomped.split('.');
     if (codeRefChompedParts.length >= 2) {
       String maybeEnumName = codeRefChompedParts
@@ -485,7 +510,7 @@
       String maybeEnumMember = codeRefChompedParts.last;
       if (packageGraph.findRefElementCache.containsKey(maybeEnumName)) {
         for (final modelElement
-            in packageGraph.findRefElementCache[maybeEnumName]) {
+        in packageGraph.findRefElementCache[maybeEnumName]) {
           if (modelElement is Enum) {
             if (modelElement.constants.any((e) => e.name == maybeEnumMember)) {
               results.add(modelElement);
@@ -496,77 +521,72 @@
       }
     }
   }
-}
 
-void _findGlobalWithinRefElementCache(Set<ModelElement> results, PackageGraph packageGraph, String codeRefChomped) {
-  if (results.isEmpty &&
-      packageGraph.findRefElementCache.containsKey(codeRefChomped)) {
-    for (final modelElement
-        in packageGraph.findRefElementCache[codeRefChomped]) {
-      if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary ||
-          (modelElement is Library &&
-              codeRefChomped == modelElement.fullyQualifiedName)) {
-        results.add(
-            packageGraph.findCanonicalModelElementFor(modelElement.element));
+  void _findGlobalWithinRefElementCache() {
+    if (packageGraph.findRefElementCache.containsKey(codeRefChomped)) {
+      for (final modelElement
+      in packageGraph.findRefElementCache[codeRefChomped]) {
+        if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary ||
+            (modelElement is Library &&
+                codeRefChomped == modelElement.fullyQualifiedName)) {
+          results.add(
+              packageGraph.findCanonicalModelElementFor(modelElement.element));
+        }
       }
     }
   }
-}
 
-void _findPartiallyQualifiedMatches(Set<ModelElement> results, Library library, String codeRef, String codeRefChomped, PackageGraph packageGraph, Class preferredClass) {
-  // Only look for partially qualified matches if we didn't find a fully qualified one.
-  if (results.isEmpty && library.modelElementsNameMap.containsKey(codeRefChomped)) {
-    for (final modelElement in library.modelElementsNameMap[codeRefChomped]) {
-      if (!_ConsiderIfConstructor(codeRef, modelElement)) continue;
-      results.add(packageGraph.findCanonicalModelElementFor(
-          modelElement.element,
-          preferredClass: preferredClass));
+  void _findPartiallyQualifiedMatches() {
+    // Only look for partially qualified matches if we didn't find a fully qualified one.
+    if (library.modelElementsNameMap.containsKey(codeRefChomped)) {
+      for (final modelElement in library.modelElementsNameMap[codeRefChomped]) {
+        if (!_ConsiderIfConstructor(modelElement)) continue;
+        results.add(packageGraph.findCanonicalModelElementFor(
+            modelElement.element,
+            preferredClass: preferredClass));
+      }
     }
   }
-}
 
-void _findWithinRefElementCache(Set<ModelElement> results, String codeRefChomped, PackageGraph packageGraph, String codeRef) {
-  // We now need the ref element cache to keep from repeatedly searching [Package.allModelElements].
-  // But if not, look for a fully qualified match.  (That only makes sense
-  // if the codeRef might be qualified, and contains periods.)
-  if (results.isEmpty &&
-      codeRefChomped.contains('.') &&
-      packageGraph.findRefElementCache.containsKey(codeRefChomped)) {
-    for (final ModelElement modelElement
-        in packageGraph.findRefElementCache[codeRefChomped]) {
-      if (!_ConsiderIfConstructor(codeRef, modelElement)) continue;
-      // For fully qualified matches, the original preferredClass passed
-      // might make no sense.  Instead, use the enclosing class from the
-      // element in [_findRefElementCache], because that element's enclosing
-      // class will be preferred from [codeRefChomped]'s perspective.
-      results.add(packageGraph.findCanonicalModelElementFor(
-          modelElement.element,
-          preferredClass: modelElement.enclosingElement is Class
-              ? modelElement.enclosingElement
-              : null));
+  void _findWithinRefElementCache() {
+    // We now need the ref element cache to keep from repeatedly searching [Package.allModelElements].
+    // But if not, look for a fully qualified match.  (That only makes sense
+    // if the codeRef might be qualified, and contains periods.)
+    if (
+    codeRefChomped.contains('.') &&
+        packageGraph.findRefElementCache.containsKey(codeRefChomped)) {
+      for (final ModelElement modelElement in packageGraph.findRefElementCache[codeRefChomped]) {
+        if (!_ConsiderIfConstructor(modelElement)) continue;
+        // For fully qualified matches, the original preferredClass passed
+        // might make no sense.  Instead, use the enclosing class from the
+        // element in [packageGraph.findRefElementCache], because that element's
+        // enclosing class will be preferred from [codeRefChomped]'s perspective.
+        results.add(packageGraph.findCanonicalModelElementFor(
+            modelElement.element,
+            preferredClass: modelElement.enclosingElement is Class
+                ? modelElement.enclosingElement
+                : null));
+      }
     }
   }
-}
 
-void _findWithinTryClasses(Set<ModelElement> results, Class preferredClass, Warnable element, String codeRefChomped, String codeRef, PackageGraph packageGraph) {
-  if (results.isEmpty) {
+  void _findWithinTryClasses() {
     // Maybe this is local to a class.
     // TODO(jcollins-g): tryClasses is a strict subset of the superclass chain.  Optimize.
     List<Class> tryClasses = [preferredClass];
     Class realClass = tryClasses.first;
     if (element is Inheritable) {
-      Inheritable overriddenElement = element.overriddenElement;
+      Inheritable overriddenElement = (element as Inheritable).overriddenElement;
       while (overriddenElement != null) {
         tryClasses.add(
-            (element.overriddenElement as EnclosedElement).enclosingElement);
+            ((element as Inheritable).overriddenElement as EnclosedElement).enclosingElement);
         overriddenElement = overriddenElement.overriddenElement;
       }
     }
 
     for (Class tryClass in tryClasses) {
       if (tryClass != null) {
-        _getResultsForClass(
-            tryClass, codeRefChomped, results, codeRef, packageGraph);
+        _getResultsForClass(tryClass);
       }
       results.remove(null);
       if (results.isNotEmpty) break;
@@ -574,96 +594,107 @@
 
     if (results.isEmpty && realClass != null) {
       for (Class superClass
-          in realClass.publicSuperChain.map((et) => et.element as Class)) {
+      in realClass.publicSuperChain.map((et) => et.element as Class)) {
         if (!tryClasses.contains(superClass)) {
-          _getResultsForClass(
-              superClass, codeRefChomped, results, codeRef, packageGraph);
+          _getResultsForClass(superClass);
         }
         results.remove(null);
         if (results.isNotEmpty) break;
       }
     }
   }
-}
 
-// _getResultsForClass assumes codeRefChomped might be a member of tryClass (inherited or not)
-// and will add to [results]
-void _getResultsForClass(Class tryClass, String codeRefChomped,
-    Set<ModelElement> results, String codeRef, PackageGraph packageGraph) {
-  // This might be part of the type arguments for the class, if so, add them.
-  // Otherwise, search the class.
-  if ((tryClass.modelType.typeArguments.map((e) => e.name))
-      .contains(codeRefChomped)) {
-    results.add((tryClass.modelType.typeArguments.firstWhere(
-                (e) => e.name == codeRefChomped && e is DefinedElementType)
-            as DefinedElementType)
-        .element);
-  } else {
-    // People like to use 'this' in docrefs too.
-    if (codeRef == 'this') {
-      results.add(packageGraph.findCanonicalModelElementFor(tryClass.element));
+  void _findAnalyzerReferences() {
+    Element refElement = _getRefElementFromCommentRefs(commentRefs, codeRef);
+    if (refElement != null) {
+      ModelElement refModelElement = new ModelElement.fromElement(
+          _getRefElementFromCommentRefs(commentRefs, codeRef),
+          element.packageGraph);
+      if (refModelElement is Accessor) {
+        refModelElement = (refModelElement as Accessor).enclosingCombo;
+      }
+      refModelElement = refModelElement.canonicalModelElement ?? refModelElement;
+      results.add(refModelElement);
+    }
+  }
+
+  /// _getResultsForClass assumes codeRefChomped might be a member of tryClass (inherited or not)
+  /// and will add to [results]
+  void _getResultsForClass(Class tryClass) {
+    // This might be part of the type arguments for the class, if so, add them.
+    // Otherwise, search the class.
+    if ((tryClass.modelType.typeArguments.map((e) => e.name))
+        .contains(codeRefChomped)) {
+      results.add((tryClass.modelType.typeArguments.firstWhere(
+              (e) => e.name == codeRefChomped && e is DefinedElementType)
+      as DefinedElementType)
+          .element);
     } else {
-      // TODO(jcollins-g): get rid of reimplementation of identifier resolution
-      //                   or integrate into ModelElement in a simpler way.
-      List<Class> superChain = [tryClass];
-      superChain.addAll(tryClass.interfaces.map((t) => t.element as Class));
-      // This seems duplicitous with our caller, but the preferredClass
-      // hint matters with findCanonicalModelElementFor.
-      // TODO(jcollins-g): This makes our caller ~O(n^2) vs length of superChain.
-      //                   Fortunately superChains are short, but optimize this if it matters.
-      superChain.addAll(tryClass.superChain.map((t) => t.element as Class));
-      List<String> codeRefParts = codeRefChomped.split('.');
-      for (final c in superChain) {
-        // TODO(jcollins-g): add a hash-map-enabled lookup function to Class?
-        for (final modelElement in c.allModelElements) {
-          if (!_ConsiderIfConstructor(codeRef, modelElement)) continue;
-          String namePart = modelElement.fullyQualifiedName.split('.').last;
-          if (modelElement is Accessor) {
-            // TODO(jcollins-g): Individual classes should be responsible for
-            // this name comparison munging.
-            namePart = namePart.split('=').first;
-          }
-          // TODO(jcollins-g): fix operators so we can use 'name' here or similar.
-          if (codeRefChomped == namePart) {
-            results.add(packageGraph.findCanonicalModelElementFor(
-                modelElement.element,
-                preferredClass: tryClass));
-            continue;
-          }
-          // Handle non-documented class documentation being imported into a
-          // documented class when it refers to itself (with help from caller's
-          // iteration on tryClasses).
-          // TODO(jcollins-g): Fix partial qualifications in _findRefElementInLibrary so it can tell
-          // when it is referenced from a non-documented element?
-          // TODO(jcollins-g): We could probably check this early.
-          if (codeRefParts.first == c.name && codeRefParts.last == namePart) {
-            results.add(packageGraph.findCanonicalModelElementFor(
-                modelElement.element,
-                preferredClass: tryClass));
-            continue;
-          }
-          if (modelElement is Constructor) {
-            // Constructor names don't include the class, so we might miss them in the above search.
-            List<String> codeRefParts = codeRefChomped.split('.');
-            if (codeRefParts.length > 1) {
-              String codeRefClass = codeRefParts[codeRefParts.length - 2];
-              String codeRefConstructor = codeRefParts.last;
-              if (codeRefClass == c.name &&
-                  codeRefConstructor ==
-                      modelElement.fullyQualifiedName.split('.').last) {
-                results.add(packageGraph.findCanonicalModelElementFor(
-                    modelElement.element,
-                    preferredClass: tryClass));
-                continue;
+      // People like to use 'this' in docrefs too.
+      if (codeRef == 'this') {
+        results.add(packageGraph.findCanonicalModelElementFor(tryClass.element));
+      } else {
+        // TODO(jcollins-g): get rid of reimplementation of identifier resolution
+        //                   or integrate into ModelElement in a simpler way.
+        List<Class> superChain = [tryClass];
+        superChain.addAll(tryClass.interfaces.map((t) => t.element as Class));
+        // This seems duplicitous with our caller, but the preferredClass
+        // hint matters with findCanonicalModelElementFor.
+        // TODO(jcollins-g): This makes our caller ~O(n^2) vs length of superChain.
+        //                   Fortunately superChains are short, but optimize this if it matters.
+        superChain.addAll(tryClass.superChain.map((t) => t.element as Class));
+        List<String> codeRefParts = codeRefChomped.split('.');
+        for (final c in superChain) {
+          // TODO(jcollins-g): add a hash-map-enabled lookup function to Class?
+          for (final modelElement in c.allModelElements) {
+            if (!_ConsiderIfConstructor(modelElement)) continue;
+            String namePart = modelElement.fullyQualifiedName.split('.').last;
+            if (modelElement is Accessor) {
+              // TODO(jcollins-g): Individual classes should be responsible for
+              // this name comparison munging.
+              namePart = namePart.split('=').first;
+            }
+            // TODO(jcollins-g): fix operators so we can use 'name' here or similar.
+            if (codeRefChomped == namePart) {
+              results.add(packageGraph.findCanonicalModelElementFor(
+                  modelElement.element,
+                  preferredClass: tryClass));
+              continue;
+            }
+            // Handle non-documented class documentation being imported into a
+            // documented class when it refers to itself (with help from caller's
+            // iteration on tryClasses).
+            // TODO(jcollins-g): Fix partial qualifications in _findRefElementInLibrary so it can tell
+            // when it is referenced from a non-documented element?
+            // TODO(jcollins-g): We could probably check this early.
+            if (codeRefParts.first == c.name && codeRefParts.last == namePart) {
+              results.add(packageGraph.findCanonicalModelElementFor(
+                  modelElement.element,
+                  preferredClass: tryClass));
+              continue;
+            }
+            if (modelElement is Constructor) {
+              // Constructor names don't include the class, so we might miss them in the above search.
+              if (codeRefParts.length > 1) {
+                String codeRefClass = codeRefParts[codeRefParts.length - 2];
+                String codeRefConstructor = codeRefParts.last;
+                if (codeRefClass == c.name &&
+                    codeRefConstructor ==
+                        modelElement.fullyQualifiedName.split('.').last) {
+                  results.add(packageGraph.findCanonicalModelElementFor(
+                      modelElement.element,
+                      preferredClass: tryClass));
+                  continue;
+                }
               }
             }
           }
-        }
-        results.remove(null);
-        if (results.isNotEmpty) break;
-        if (c.fullyQualifiedNameWithoutLibrary == codeRefChomped) {
-          results.add(c);
-          break;
+          results.remove(null);
+          if (results.isNotEmpty) break;
+          if (c.fullyQualifiedNameWithoutLibrary == codeRefChomped) {
+            results.add(c);
+            break;
+          }
         }
       }
     }