Escape type parameters inside comment references (#1740)

* Escape type parameters and other things inside comment references

* Rebuild package docs
diff --git a/lib/src/markdown_processor.dart b/lib/src/markdown_processor.dart
index a064053..b2d0685 100644
--- a/lib/src/markdown_processor.dart
+++ b/lib/src/markdown_processor.dart
@@ -154,9 +154,8 @@
 
 class MatchingLinkResult {
   final ModelElement element;
-  final String label;
   final bool warn;
-  MatchingLinkResult(this.element, this.label, {this.warn: true});
+  MatchingLinkResult(this.element, {this.warn: true});
 }
 
 class IterableBlockParser extends md.BlockParser {
@@ -244,13 +243,12 @@
   // 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.
-  if (commentRefs == null)
-    return new MatchingLinkResult(null, null, warn: false);
+  if (commentRefs == null) return new MatchingLinkResult(null, warn: false);
 
   if (!codeRef.contains(isConstructor) &&
       codeRef.contains(notARealDocReference)) {
     // Don't waste our time on things we won't ever find.
-    return new MatchingLinkResult(null, null, warn: false);
+    return new MatchingLinkResult(null, warn: false);
   }
 
   ModelElement refModelElement;
@@ -283,12 +281,12 @@
     // TODO(jcollins-g): remove squelching of non-canonical warnings here
     //                   once we no longer process full markdown for
     //                   oneLineDocs (#1417)
-    return new MatchingLinkResult(null, null, warn: element.isCanonical);
+    return new MatchingLinkResult(null, warn: element.isCanonical);
   }
 
   // Ignore all parameters.
   if (refModelElement is Parameter || refModelElement is TypeParameter)
-    return new MatchingLinkResult(null, null, warn: false);
+    return new MatchingLinkResult(null, warn: false);
 
   // There have been places in the code which helpfully cache entities
   // regardless of what package they are associated with.  This assert
@@ -296,7 +294,7 @@
   assert(refModelElement == null ||
       refModelElement.packageGraph == element.packageGraph);
   if (refModelElement != null) {
-    return new MatchingLinkResult(refModelElement, null);
+    return new MatchingLinkResult(refModelElement);
   }
   // From this point on, we haven't been able to find a canonical ModelElement.
   if (!refModelElement.isCanonical) {
@@ -306,7 +304,7 @@
     }
     // Don't warn about doc references because that's covered by the no
     // canonical library found message.
-    return new MatchingLinkResult(null, null, warn: false);
+    return new MatchingLinkResult(null, warn: false);
   }
   // We should never get here unless there's a bug in findCanonicalModelElementFor.
   // findCanonicalModelElementFor(searchElement, preferredClass: preferredClass)
@@ -314,7 +312,7 @@
   // would return a non-canonical element.  However, outside of checked mode,
   // at least we have a canonical element, so proceed.
   assert(false);
-  return new MatchingLinkResult(refModelElement, null);
+  return new MatchingLinkResult(refModelElement);
 }
 
 /// Given a set of commentRefs, return the one whose name matches the codeRef.
@@ -725,7 +723,6 @@
   MatchingLinkResult result;
   result = _getMatchingLinkElement(codeRef, warnable, commentRefs);
   final ModelElement linkedElement = result.element;
-  final String label = result.label ?? codeRef;
   if (linkedElement != null) {
     var classContent = '';
     if (linkedElement.isDeprecated) {
@@ -734,16 +731,16 @@
     // This would be linkedElement.linkedName, but link bodies are slightly
     // different for doc references.
     if (linkedElement.href == null) {
-      return '<code>${htmlEscape.convert(label)}</code>';
+      return '<code>${htmlEscape.convert(codeRef)}</code>';
     } else {
-      return '<a ${classContent}href="${linkedElement.href}">$label</a>';
+      return '<a ${classContent}href="${linkedElement.href}">${htmlEscape.convert(codeRef)}</a>';
     }
   } else {
     if (result.warn) {
       warnable.warn(PackageWarning.unresolvedDocReference,
           message: codeRef, referredFrom: warnable.documentationFrom);
     }
-    return '<code>${htmlEscape.convert(label)}</code>';
+    return '<code>${htmlEscape.convert(codeRef)}</code>';
   }
 }
 
diff --git a/test/model_test.dart b/test/model_test.dart
index 8be534d..0297d4b 100644
--- a/test/model_test.dart
+++ b/test/model_test.dart
@@ -927,6 +927,18 @@
             contains(
                 '<a href="fake/NAME_SINGLEUNDERSCORE-constant.html">NAME_SINGLEUNDERSCORE</a>'));
       });
+
+      test('correctly escapes type parameters in links', () {
+        expect(
+            docsAsHtml,
+            contains(
+                '<a href="fake/ExtraSpecialList-class.html">ExtraSpecialList&lt;Object&gt;</a>'));
+      });
+
+      test('correctly escapes type parameters in broken links', () {
+        expect(docsAsHtml,
+            contains('<code>ThisIsNotHereNoWay&lt;MyType&gt;</code>'));
+      });
     });
 
     test('multi-underscore names in brackets do not become italicized', () {
diff --git a/testing/test_package/lib/fake.dart b/testing/test_package/lib/fake.dart
index 84ead50..9a50230 100644
--- a/testing/test_package/lib/fake.dart
+++ b/testing/test_package/lib/fake.dart
@@ -744,6 +744,10 @@
   /// Reference to a bracket operator within this class [operator []] xxx
   ///
   /// Reference to a bracket operator in another class [SpecialList.operator []] xxx
+  ///
+  /// Reference containing a type parameter [ExtraSpecialList<Object>]
+  ///
+  /// Reference to something that doesn't exist containing a type parameter [ThisIsNotHereNoWay<MyType>]
   String doAwesomeStuff(int value) => null;
 
   void anotherMethod() {}
diff --git a/testing/test_package_docs/fake/BaseForDocComments/doAwesomeStuff.html b/testing/test_package_docs/fake/BaseForDocComments/doAwesomeStuff.html
index 8ea0bfd..35c1672 100644
--- a/testing/test_package_docs/fake/BaseForDocComments/doAwesomeStuff.html
+++ b/testing/test_package_docs/fake/BaseForDocComments/doAwesomeStuff.html
@@ -91,6 +91,8 @@
 the name <a href="two_exports/BaseClass-class.html">BaseClass</a> xx</p>
 <p>Reference to a bracket operator within this class <a href="fake/BaseForDocComments/operator_get.html">operator []</a> xxx</p>
 <p>Reference to a bracket operator in another class <code>SpecialList.operator []</code> xxx</p>
+<p>Reference containing a type parameter <a href="fake/ExtraSpecialList-class.html">ExtraSpecialList&lt;Object&gt;</a></p>
+<p>Reference to something that doesn't exist containing a type parameter <code>ThisIsNotHereNoWay&lt;MyType&gt;</code></p>
     </section>