eliminate crash on warnings generated by README.md (#1410)

* Fix readme warnings

* Update test to generate warnings from the readme

* dartfmt

* Shift around abstract classes to make this more understandable

* dartfmt
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0ef7c44..e811c1c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+## 0.11.2
+* Fix regression where warnings generated by the README could result in a fatal exception.  #1409
+
 ## 0.11.1
 * Fix regression where a property or top level variable can be listed twice
   under some conditions.  #1401
diff --git a/bin/dartdoc.dart b/bin/dartdoc.dart
index c368664..94dd366 100644
--- a/bin/dartdoc.dart
+++ b/bin/dartdoc.dart
@@ -177,10 +177,10 @@
     print('\nSuccess! Docs generated into ${results.outDir.absolute.path}');
   }, onError: (e, Chain chain) {
     if (e is DartDocFailure) {
-      stderr.writeln('Generation failed: ${e}.');
+      stderr.writeln('\nGeneration failed: ${e}.');
       exit(1);
     } else {
-      stderr.writeln('Generation failed: ${e}\n${chain.terse}');
+      stderr.writeln('\nGeneration failed: ${e}\n${chain.terse}');
       exit(255);
     }
   });
diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart
index 69d11da..b0026ce 100644
--- a/lib/dartdoc.dart
+++ b/lib/dartdoc.dart
@@ -44,7 +44,7 @@
 
 const String name = 'dartdoc';
 // Update when pubspec version changes.
-const String version = '0.11.1';
+const String version = '0.11.2';
 
 final String defaultOutDir = path.join('doc', 'api');
 
@@ -241,7 +241,7 @@
     }
     if (referenceElement == null && source == 'index.html')
       referenceElement = package;
-    package.warn(referenceElement, kind, p);
+    package.warnOnElement(referenceElement, kind, p);
   }
 
   void _doOrphanCheck(Package package, String origin, Set<String> visited) {
diff --git a/lib/src/html/html_generator_instance.dart b/lib/src/html/html_generator_instance.dart
index 436e6a2..3de8f9b 100644
--- a/lib/src/html/html_generator_instance.dart
+++ b/lib/src/html/html_generator_instance.dart
@@ -189,7 +189,7 @@
     stdout
         .write('\ngenerating docs for library ${lib.name} from ${lib.path}...');
     if (!lib.isAnonymous && !lib.hasDocumentation) {
-      package.warn(lib, PackageWarning.noLibraryLevelDocs);
+      package.warnOnElement(lib, PackageWarning.noLibraryLevelDocs);
     }
     TemplateData data =
         new LibraryTemplateData(this, package, lib, useCategories);
diff --git a/lib/src/markdown_processor.dart b/lib/src/markdown_processor.dart
index 093f664..4c1f441 100644
--- a/lib/src/markdown_processor.dart
+++ b/lib/src/markdown_processor.dart
@@ -164,7 +164,11 @@
 }
 
 // TODO: this is in the wrong place
-NodeList<CommentReference> _getCommentRefs(ModelElement modelElement) {
+NodeList<CommentReference> _getCommentRefs(Documentable documentable) {
+  // Documentable items that aren't related to analyzer elements have no
+  // CommentReference list.
+  if (documentable is! ModelElement) return null;
+  ModelElement modelElement = documentable;
   Class preferredClass = _getPreferredClass(modelElement);
   ModelElement cModelElement = modelElement.package
       .findCanonicalModelElementFor(modelElement.element,
@@ -219,7 +223,7 @@
 
 /// Returns null if element is a parameter.
 MatchingLinkResult _getMatchingLinkElement(
-    String codeRef, ModelElement element, List<CommentReference> commentRefs) {
+    String codeRef, Documentable element, List<CommentReference> commentRefs) {
   // By debugging inspection, it seems correct to not warn when we don't have
   // CommentReferences; there's actually nothing that needs resolving in
   // that case.
@@ -244,6 +248,9 @@
   // dartdoc-canonicalization-aware?
   if (refElement == null) {
     refElement = _getRefElementFromCommentRefs(commentRefs, codeRef);
+  } else {
+    assert(refElement is! PropertyAccessorElement);
+    assert(refElement is! PrefixElement);
   }
 
   // Did not find it anywhere.
@@ -251,12 +258,6 @@
     return new MatchingLinkResult(null, null);
   }
 
-  if (refElement is PropertyAccessorElement) {
-    // yay we found an accessor that wraps a const, but we really
-    // want the top-level field itself
-    refElement = (refElement as PropertyAccessorElement).variable;
-  }
-
   // Ignore all parameters.
   if (refElement is ParameterElement || refElement is TypeParameterElement)
     return new MatchingLinkResult(null, null, warn: false);
@@ -300,7 +301,17 @@
       bool isConstrElement = ref.identifier.staticElement is ConstructorElement;
       // Constructors are now handled by library search.
       if (!isConstrElement) {
-        return ref.identifier.staticElement;
+        Element refElement = ref.identifier.staticElement;
+        if (refElement is PropertyAccessorElement) {
+          // yay we found an accessor that wraps a const, but we really
+          // want the top-level field itself
+          refElement = (refElement as PropertyAccessorElement).variable;
+        }
+        if (refElement is PrefixElement) {
+          // We found a prefix element, but what we really want is the library element.
+          refElement = (refElement as PrefixElement).enclosingElement;
+        }
+        return refElement;
       }
     }
   }
@@ -340,6 +351,7 @@
 // TODO(jcollins-g): A complex package winds up spending a lot of cycles in here.  Optimize.
 Element _findRefElementInLibrary(
     String codeRef, ModelElement element, List<CommentReference> commentRefs) {
+  assert(element != null);
   assert(element.package.allLibrariesAdded);
 
   String codeRefChomped = codeRef.replaceFirst(isConstructor, '');
@@ -625,14 +637,13 @@
   }
 }
 
-String _linkDocReference(String codeRef, ModelElement element,
+String _linkDocReference(String codeRef, Documentable documentable,
     NodeList<CommentReference> commentRefs) {
   // TODO(jcollins-g): Refactor so that doc operations work on the
   //                   documented element.
-  element = element.overriddenDocumentedElement;
-
+  documentable = documentable.overriddenDocumentedElement;
   MatchingLinkResult result;
-  result = _getMatchingLinkElement(codeRef, element, commentRefs);
+  result = _getMatchingLinkElement(codeRef, documentable, commentRefs);
   final ModelElement linkedElement = result.element;
   final String label = result.label ?? codeRef;
   if (linkedElement != null) {
@@ -649,18 +660,19 @@
     }
   } else {
     if (result.warn) {
-      element.warn(PackageWarning.unresolvedDocReference, codeRef);
+      documentable.warn(PackageWarning.unresolvedDocReference, codeRef);
     }
     return '<code>${HTML_ESCAPE.convert(label)}</code>';
   }
 }
 
-String _renderMarkdownToHtml(String text, [ModelElement element]) {
+String _renderMarkdownToHtml(Documentable element) {
   md.Node _linkResolver(String name) {
     NodeList<CommentReference> commentRefs = _getCommentRefs(element);
     return new md.Text(_linkDocReference(name, element, commentRefs));
   }
 
+  String text = element.documentation;
   _showWarningsForGenericsOutsideSquareBracketsBlocks(text, element);
   return md.markdownToHtml(text,
       inlineSyntaxes: _markdown_syntaxes, linkResolver: _linkResolver);
@@ -676,7 +688,7 @@
 // like a non HTML tag (a generic?) outside of a `[]` block.
 // https://github.com/dart-lang/dartdoc/issues/1250#issuecomment-269257942
 void _showWarningsForGenericsOutsideSquareBracketsBlocks(
-    String text, ModelElement element) {
+    String text, Warnable element) {
   List<int> tagPositions = findFreeHangingGenericsPositions(text);
   if (tagPositions.isNotEmpty) {
     tagPositions.forEach((int position) {
@@ -732,13 +744,8 @@
   final String asHtml;
   final String asOneLiner;
 
-  factory Documentation(String markdown) {
-    String tempHtml = _renderMarkdownToHtml(markdown);
-    return new Documentation._internal(markdown, tempHtml);
-  }
-
-  factory Documentation.forElement(ModelElement element) {
-    String tempHtml = _renderMarkdownToHtml(element.documentation, element);
+  factory Documentation.forElement(Documentable element) {
+    String tempHtml = _renderMarkdownToHtml(element);
     return new Documentation._internal(element.documentation, tempHtml);
   }
 
diff --git a/lib/src/model.dart b/lib/src/model.dart
index 6c710ea..8153456 100644
--- a/lib/src/model.dart
+++ b/lib/src/model.dart
@@ -895,11 +895,13 @@
 
 /// Bridges the gap between model elements and packages,
 /// both of which have documentation.
-abstract class Documentable {
+abstract class Documentable implements Warnable {
   String get documentation;
   String get documentationAsHtml;
   bool get hasDocumentation;
   String get oneLineDoc;
+  Documentable get overriddenDocumentedElement;
+  Package get package;
 }
 
 class Dynamic extends ModelElement {
@@ -1709,8 +1711,7 @@
 /// helps prevent subtle bugs as generated output for a non-canonical
 /// ModelElement will reference itself as part of the "wrong" [Library]
 /// from the public interface perspective.
-abstract class ModelElement
-    implements Comparable, Nameable, Documentable, Locatable {
+abstract class ModelElement implements Comparable, Nameable, Documentable {
   final Element _element;
   final Library _library;
 
@@ -2152,6 +2153,7 @@
   bool _overriddenDocumentedElementIsSet = false;
   // TODO(jcollins-g): This method prefers canonical elements, but it isn't
   // guaranteed and is probably the source of bugs or confusing warnings.
+  @override
   ModelElement get overriddenDocumentedElement {
     if (!_overriddenDocumentedElementIsSet) {
       ModelElement found = this;
@@ -2180,6 +2182,7 @@
     return _overriddenDepth;
   }
 
+  @override
   Package get package =>
       (this is Library) ? (this as Library).package : this.library.package;
 
@@ -2236,6 +2239,7 @@
     return _parameters;
   }
 
+  @override
   void warn(PackageWarning kind, [String message]) {
     if (kind == PackageWarning.unresolvedDocReference &&
         overriddenElement != null) {
@@ -2243,7 +2247,7 @@
       // Attach the warning to that element to deduplicate.
       overriddenElement.warn(kind, message);
     } else {
-      library.package.warn(this, kind, message);
+      library.package.warnOnElement(this, kind, message);
     }
   }
 
@@ -2763,6 +2767,11 @@
   ],
 };
 
+// Something that package warnings can be called on.
+abstract class Warnable implements Locatable {
+  void warn(PackageWarning warning, [String message]);
+}
+
 // Something that can be located for warning purposes.
 abstract class Locatable {
   String get fullyQualifiedName;
@@ -2889,7 +2898,7 @@
   }
 }
 
-class Package implements Nameable, Documentable, Locatable {
+class Package implements Nameable, Documentable {
   // Library objects serving as entry points for documentation.
   final List<Library> _libraries = [];
   // All library objects related to this package; a superset of _libraries.
@@ -2911,6 +2920,12 @@
 
   final PackageMeta packageMeta;
 
+  @override
+  Package get package => this;
+
+  @override
+  Documentable get overriddenDocumentedElement => this;
+
   final Map<Element, Library> _elementToLibrary = {};
   String _docsAsHtml;
   final Map<String, String> _macros = {};
@@ -2958,29 +2973,33 @@
 
   PackageWarningCounter get packageWarningCounter => _packageWarningCounter;
 
-  void warn(Locatable modelElement, PackageWarning kind, [String message]) {
-    if (modelElement != null) {
+  @override
+  void warn(PackageWarning kind, [String message]) {
+    warnOnElement(this, kind, message);
+  }
+
+  void warnOnElement(Warnable warnable, PackageWarning kind, [String message]) {
+    if (warnable != null) {
       // This sort of warning is only applicable to top level elements.
       if (kind == PackageWarning.ambiguousReexport) {
-        Element topLevelElement = modelElement.element;
+        Element topLevelElement = warnable.element;
         while (topLevelElement.enclosingElement is! CompilationUnitElement) {
           topLevelElement = topLevelElement.enclosingElement;
         }
-        modelElement = new ModelElement.from(
+        warnable = new ModelElement.from(
             topLevelElement, findOrCreateLibraryFor(topLevelElement));
       }
-      if (modelElement is Accessor) {
+      if (warnable is Accessor) {
         // This might be part of a Field, if so, assign this warning to the field
         // rather than the Accessor.
-        if ((modelElement as Accessor).enclosingCombo != null)
-          modelElement = (modelElement as Accessor).enclosingCombo;
+        if ((warnable as Accessor).enclosingCombo != null)
+          warnable = (warnable as Accessor).enclosingCombo;
       }
     } else {
       // If we don't have an element, we need a message to disambiguate.
       assert(message != null);
     }
-    if (_packageWarningCounter.hasWarning(
-        modelElement?.element, kind, message)) {
+    if (_packageWarningCounter.hasWarning(warnable?.element, kind, message)) {
       return;
     }
     // Elements that are part of the Dart SDK can have colons in their FQNs.
@@ -2991,9 +3010,9 @@
     //                   them out doesn't work as well there since it might confuse
     //                   the user, yet we still want IntelliJ to link properly.
     String nameSplitFromColonPieces;
-    if (modelElement != null) {
+    if (warnable != null) {
       nameSplitFromColonPieces =
-          modelElement.fullyQualifiedName.replaceFirst(':', '-');
+          warnable.fullyQualifiedName.replaceFirst(':', '-');
     }
     String warningMessage;
     switch (kind) {
@@ -3014,7 +3033,7 @@
         break;
       case PackageWarning.noLibraryLevelDocs:
         warningMessage =
-            "${modelElement.fullyQualifiedName} has no library level documentation comments";
+            "${warnable.fullyQualifiedName} has no library level documentation comments";
         break;
       case PackageWarning.ambiguousDocReference:
         warningMessage =
@@ -3047,9 +3066,9 @@
         break;
     }
     String fullMessage =
-        "${warningMessage} ${modelElement != null ? modelElement.elementLocation : ''}";
+        "${warningMessage} ${warnable != null ? warnable.elementLocation : ''}";
     packageWarningCounter.addWarning(
-        modelElement?.element, kind, message, fullMessage);
+        warnable?.element, kind, message, fullMessage);
   }
 
   static Package _withAutoIncludedDependencies(
@@ -3110,7 +3129,7 @@
     // Help the user if they pass us a category that doesn't exist.
     for (String categoryName in config.categoryOrder) {
       if (!result.containsKey(categoryName))
-        warn(null, PackageWarning.categoryOrderGivesMissingPackageName,
+        warnOnElement(null, PackageWarning.categoryOrderGivesMissingPackageName,
             "${categoryName}, categories: ${result.keys.join(',')}");
     }
     List<PackageCategory> packageCategories = result.values.toList()..sort();
@@ -3167,8 +3186,7 @@
   @override
   String get documentationAsHtml {
     if (_docsAsHtml != null) return _docsAsHtml;
-
-    _docsAsHtml = new Documentation(documentation).asHtml;
+    _docsAsHtml = new Documentation.forElement(this).asHtml;
 
     return _docsAsHtml;
   }
diff --git a/pubspec.yaml b/pubspec.yaml
index 1192a83..ca637ef 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
 name: dartdoc
 # Also update the `version` field in lib/dartdoc.dart.
-version: 0.11.1
+version: 0.11.2
 author: Dart Team <misc@dartlang.org>
 description: A documentation generator for Dart.
 homepage: https://github.com/dart-lang/dartdoc
diff --git a/testing/test_package/README.md b/testing/test_package/README.md
index e0b03ab..f507880 100644
--- a/testing/test_package/README.md
+++ b/testing/test_package/README.md
@@ -23,6 +23,8 @@
   - 3.14
 ```
 
+It sometimes generates warnings in commentRefs like this: [unknownThingy.FromSomewhere]
+
 Be sure to check out other awesome packages on [pub][].
 
 [pub]: https://pub.dartlang.org
diff --git a/testing/test_package_docs/index.html b/testing/test_package_docs/index.html
index c18a607..51787a7 100644
--- a/testing/test_package_docs/index.html
+++ b/testing/test_package_docs/index.html
@@ -4,7 +4,7 @@
   <meta charset="utf-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1">
-  <meta name="generator" content="made with love by dartdoc 0.11.1">
+  <meta name="generator" content="made with love by dartdoc 0.11.2">
   <meta name="description" content="test_package API docs, for the Dart programming language.">
   <title>test_package - Dart API docs</title>
 
@@ -92,6 +92,7 @@
   - "value"
   - 3.14
 </code></pre>
+<p>It sometimes generates warnings in commentRefs like this: <code>unknownThingy.FromSomewhere</code></p>
 <p>Be sure to check out other awesome packages on <a href="https://pub.dartlang.org">pub</a>.</p>
       </section>