Skip to content

Commit f295391

Browse files
authored
Allow ? and ! to trail in doc comment references (#2328)
1 parent 9849d74 commit f295391

File tree

3 files changed

+77
-30
lines changed

3 files changed

+77
-30
lines changed

Diff for: lib/src/markdown_processor.dart

+59-29
Original file line numberDiff line numberDiff line change
@@ -119,19 +119,44 @@ const validHtmlTags = [
119119
final RegExp nonHTML =
120120
RegExp("</?(?!(${validHtmlTags.join("|")})[> ])\\w+[> ]");
121121

122-
// Type parameters and other things to ignore at the end of doc references.
123-
final RegExp trailingIgnoreStuff = RegExp(r'(<.*>|\(.*\))$');
124-
125-
// Things to ignore at the beginning of doc references
126-
final RegExp leadingIgnoreStuff =
122+
/// Things to ignore at the end of a doc reference.
123+
///
124+
/// This is intended to catch type parameters/arguments, and function call
125+
/// parentheses.
126+
final _trailingIgnorePattern = RegExp(r'(<.*>|\(.*\)|\?|!)$');
127+
128+
@Deprecated('Public variable intended to be private; will be removed as early '
129+
'as Dartdoc 1.0.0')
130+
RegExp get trailingIgnoreStuff => _trailingIgnorePattern;
131+
132+
/// Things to ignore at the beginning of a doc reference.
133+
///
134+
/// This is intended to catch various keywords that a developer may include in
135+
/// front of a variable name.
136+
// TODO(srawlins): I cannot find any tests asserting we can resolve such
137+
// references.
138+
final _leadingIgnorePattern =
127139
RegExp(r'^(const|final|var)[\s]+', multiLine: true);
128140

129-
// If found, this may be intended as a reference to a constructor.
130-
final RegExp isConstructor = RegExp(r'(^new[\s]+|\(\)$)', multiLine: true);
131-
132-
// This is probably not really intended as a doc reference, so don't try or
133-
// warn about them.
134-
// Covers anything with leading digits/symbols, empty string, weird punctuation, spaces.
141+
@Deprecated('Public variable intended to be private; will be removed as early '
142+
'as Dartdoc 1.0.0')
143+
RegExp get leadingIgnoreStuff => _leadingIgnorePattern;
144+
145+
/// If found, this pattern may indicate a reference to a constructor.
146+
final _constructorIndicationPattern =
147+
RegExp(r'(^new[\s]+|\(\)$)', multiLine: true);
148+
149+
@Deprecated('Public variable intended to be private; will be removed as early '
150+
'as Dartdoc 1.0.0')
151+
RegExp get isConstructor => _constructorIndicationPattern;
152+
153+
/// A pattern indicating that text which looks like a doc reference is not
154+
/// intended to be.
155+
///
156+
/// This covers anything with leading digits/symbols, empty strings, weird
157+
/// punctuation, spaces.
158+
///
159+
/// The idea is to catch such cases and not produce warnings about the contents.
135160
final RegExp notARealDocReference = RegExp(r'''(^[^\w]|^[\d]|[,"'/]|^$)''');
136161

137162
final RegExp operatorPrefix = RegExp(r'^operator[ ]*');
@@ -194,7 +219,7 @@ ModelElement _getPreferredClass(ModelElement modelElement) {
194219
/// Returns null if element is a parameter.
195220
MatchingLinkResult _getMatchingLinkElement(
196221
String codeRef, Warnable element, List<ModelCommentReference> commentRefs) {
197-
if (!codeRef.contains(isConstructor) &&
222+
if (!codeRef.contains(_constructorIndicationPattern) &&
198223
codeRef.contains(notARealDocReference)) {
199224
// Don't waste our time on things we won't ever find.
200225
return MatchingLinkResult(null, warn: false);
@@ -314,7 +339,7 @@ class _MarkdownCommentReference {
314339
assert(element != null);
315340
assert(element.packageGraph.allLibrariesAdded);
316341

317-
codeRefChomped = codeRef.replaceAll(isConstructor, '');
342+
codeRefChomped = codeRef.replaceAll(_constructorIndicationPattern, '');
318343
library =
319344
element is ModelElement ? (element as ModelElement).library : null;
320345
packageGraph = library.packageGraph;
@@ -342,7 +367,7 @@ class _MarkdownCommentReference {
342367
String get _impliedDefaultConstructor {
343368
if (!__impliedDefaultConstructorIsSet) {
344369
__impliedDefaultConstructorIsSet = true;
345-
if (codeRef.contains(isConstructor) ||
370+
if (codeRef.contains(_constructorIndicationPattern) ||
346371
(codeRefChompedParts.length >= 2 &&
347372
codeRefChompedParts[codeRefChompedParts.length - 1] ==
348373
codeRefChompedParts[codeRefChompedParts.length - 2])) {
@@ -362,35 +387,40 @@ class _MarkdownCommentReference {
362387
/// are more than one, but does not warn otherwise.
363388
ModelElement computeReferredElement() {
364389
results = {};
365-
// TODO(jcollins-g): A complex package winds up spending a lot of cycles in here. Optimize.
390+
// TODO(jcollins-g): A complex package winds up spending a lot of cycles in
391+
// here. Optimize.
366392
for (var findMethod in [
367393
// This might be an operator. Strip the operator prefix and try again.
368394
_findWithoutOperatorPrefix,
369395
// Oh, and someone might have thrown on a 'const' or 'final' in front.
370396
_findWithoutLeadingIgnoreStuff,
371397
// Maybe this ModelElement has parameters, and this is one of them.
372-
// We don't link these, but this keeps us from emitting warnings. Be sure to
373-
// get members of parameters too.
398+
// We don't link these, but this keeps us from emitting warnings. Be sure
399+
// to get members of parameters too.
374400
_findParameters,
375401
// Maybe this ModelElement has type parameters, and this is one of them.
376402
_findTypeParameters,
377403
// This could be local to the class, look there first.
378404
_findWithinTryClasses,
379405
// This could be a reference to a renamed library.
380406
_findReferenceFromPrefixes,
381-
// We now need the ref element cache to keep from repeatedly searching [Package.allModelElements].
382-
// But if not, look for a fully qualified match. (That only makes sense
383-
// if the codeRef might be qualified, and contains periods.)
407+
// We now need the ref element cache to keep from repeatedly searching
408+
// [Package.allModelElements]. But if not, look for a fully qualified
409+
// match. (That only makes sense if the code reference might be
410+
// qualified, and contains periods.)
384411
_findWithinRefElementCache,
385-
// Only look for partially qualified matches if we didn't find a fully qualified one.
412+
// Only look for partially qualified matches if we didn't find a fully
413+
// qualified one.
386414
_findPartiallyQualifiedMatches,
387-
// Only look for partially qualified matches if we didn't find a fully qualified one.
415+
// Only look for partially qualified matches if we didn't find a fully
416+
// qualified one.
388417
_findGlobalWithinRefElementCache,
389-
// This could conceivably be a reference to an enum member. They don't show up in allModelElements.
418+
// This could conceivably be a reference to an enum member. They don't
419+
// show up in [allModelElements].
390420
_findEnumReferences,
391421
// Oh, and someone might have some type parameters or other garbage.
392-
// After finding within classes because sometimes parentheses are used
393-
// to imply constructors.
422+
// After finding within classes because sometimes parentheses are used to
423+
// imply constructors.
394424
_findWithoutTrailingIgnoreStuff,
395425
// Use the analyzer to resolve a comment reference.
396426
_findAnalyzerReferences,
@@ -514,17 +544,17 @@ class _MarkdownCommentReference {
514544
}
515545

516546
void _findWithoutLeadingIgnoreStuff() {
517-
if (codeRef.contains(leadingIgnoreStuff)) {
518-
var newCodeRef = codeRef.replaceFirst(leadingIgnoreStuff, '');
547+
if (codeRef.contains(_leadingIgnorePattern)) {
548+
var newCodeRef = codeRef.replaceFirst(_leadingIgnorePattern, '');
519549
results.add(_MarkdownCommentReference(
520550
newCodeRef, element, commentRefs, preferredClass)
521551
.computeReferredElement());
522552
}
523553
}
524554

525555
void _findWithoutTrailingIgnoreStuff() {
526-
if (codeRef.contains(trailingIgnoreStuff)) {
527-
var newCodeRef = codeRef.replaceFirst(trailingIgnoreStuff, '');
556+
if (codeRef.contains(_trailingIgnorePattern)) {
557+
var newCodeRef = codeRef.replaceFirst(_trailingIgnorePattern, '');
528558
results.add(_MarkdownCommentReference(
529559
newCodeRef, element, commentRefs, preferredClass)
530560
.computeReferredElement());

Diff for: test/end2end/model_test.dart

+15-1
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,20 @@ void main() {
13221322
'<a href="${HTMLBASE_PLACEHOLDER}ex/Apple/Apple.fromString.html">new Apple.fromString</a>'));
13231323
});
13241324

1325+
test('references to nullable type and null-checked variable', () {
1326+
var RefsWithQsAndBangs =
1327+
exLibrary.classes.firstWhere((c) => c.name == 'RefsWithQsAndBangs');
1328+
var comment = RefsWithQsAndBangs.documentationAsHtml;
1329+
expect(
1330+
comment,
1331+
contains(
1332+
'nullable type: <a href="${HTMLBASE_PLACEHOLDER}ex/Apple-class.html">Apple?</a>'));
1333+
expect(
1334+
comment,
1335+
contains(
1336+
'null-checked variable <a href="${HTMLBASE_PLACEHOLDER}ex/myNumber.html">myNumber!</a>'));
1337+
});
1338+
13251339
test('reference to class from another library', () {
13261340
var comment = superAwesomeClass.documentationAsHtml;
13271341
expect(
@@ -1627,7 +1641,7 @@ void main() {
16271641
});
16281642

16291643
test('correctly finds all the classes', () {
1630-
expect(classes, hasLength(34));
1644+
expect(classes, hasLength(35));
16311645
});
16321646

16331647
test('abstract', () {

Diff for: testing/test_package/lib/example.dart

+3
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ class B extends Apple with Cat {
259259
void abstractMethod() {}
260260
}
261261

262+
/// Reference to nullable type: [Apple?] and null-checked variable [myNumber!].
263+
class RefsWithQsAndBangs {}
264+
262265
// Do NOT add a doc comment to C. Testing blank comments.
263266

264267
abstract class Cat {

0 commit comments

Comments
 (0)