@@ -127,8 +127,8 @@ final RegExp trailingIgnoreStuff = new RegExp(r'(<.*>|\(.*\))$');
127127final RegExp leadingIgnoreStuff =
128128 new RegExp (r'^(const|final|var)[\s]+' , multiLine: true );
129129
130- // This is explicitly intended as a reference to a constructor.
131- final RegExp isConstructor = new RegExp (r'^new[\s]+' , multiLine: true );
130+ // If found, this may be intended as a reference to a constructor.
131+ final RegExp isConstructor = new RegExp (r'( ^new[\s]+|\(\)$) ' , multiLine: true );
132132
133133// This is probably not really intended as a doc reference, so don't try or
134134// warn about them.
@@ -299,12 +299,45 @@ class _MarkdownCommentReference {
299299 assert (element != null );
300300 assert (element.packageGraph.allLibrariesAdded);
301301
302- codeRefChomped = codeRef.replaceFirst (isConstructor, '' );
302+ codeRefChomped = codeRef.replaceAll (isConstructor, '' );
303303 library =
304304 element is ModelElement ? (element as ModelElement ).library : null ;
305305 packageGraph = library.packageGraph;
306306 }
307307
308+ String __impliedDefaultConstructor;
309+ bool __impliedDefaultConstructorIsSet = false ;
310+
311+ /// Returns the name of the implied default constructor if there is one, or
312+ /// null if not.
313+ ///
314+ /// Default constructors are a special case in dartdoc. If we look up a name
315+ /// within a class of that class itself, the first thing we find is the
316+ /// default constructor. But we determine whether that's what they actually
317+ /// intended (vs. the enclosing class) by context -- whether they seem
318+ /// to be calling it with () or have a 'new' in front of it, or
319+ /// whether the name is repeated.
320+ ///
321+ /// Similarly, referencing a class by itself might actually refer to its
322+ /// constructor based on these same heuristics.
323+ ///
324+ /// With the name of the implied default constructor, other methods can
325+ /// determine whether or not the constructor and/or class we resolved to
326+ /// is actually matching the user's intent.
327+ String get _impliedDefaultConstructor {
328+ if (! __impliedDefaultConstructorIsSet) {
329+ __impliedDefaultConstructorIsSet = true ;
330+ if (codeRef.contains (isConstructor) ||
331+ (codeRefChompedParts.length >= 2 &&
332+ codeRefChompedParts[codeRefChompedParts.length - 1 ] ==
333+ codeRefChompedParts[codeRefChompedParts.length - 2 ])) {
334+ // If the last two parts of the code reference are equal, this is probably a default constructor.
335+ __impliedDefaultConstructor = codeRefChompedParts.last;
336+ }
337+ }
338+ return __impliedDefaultConstructor;
339+ }
340+
308341 /// Calculate reference to a ModelElement.
309342 ///
310343 /// Uses a series of calls to the _find* methods in this class to get one
@@ -318,8 +351,6 @@ class _MarkdownCommentReference {
318351 for (void Function () findMethod in [
319352 // This might be an operator. Strip the operator prefix and try again.
320353 _findWithoutOperatorPrefix,
321- // Oh, and someone might have some type parameters or other garbage.
322- _findWithoutTrailingIgnoreStuff,
323354 // Oh, and someone might have thrown on a 'const' or 'final' in front.
324355 _findWithoutLeadingIgnoreStuff,
325356 // Maybe this ModelElement has parameters, and this is one of them.
@@ -330,6 +361,8 @@ class _MarkdownCommentReference {
330361 _findTypeParameters,
331362 // This could be local to the class, look there first.
332363 _findWithinTryClasses,
364+ // This could be a reference to a renamed library.
365+ _findReferenceFromPrefixes,
333366 // We now need the ref element cache to keep from repeatedly searching [Package.allModelElements].
334367 // But if not, look for a fully qualified match. (That only makes sense
335368 // if the codeRef might be qualified, and contains periods.)
@@ -340,8 +373,12 @@ class _MarkdownCommentReference {
340373 _findGlobalWithinRefElementCache,
341374 // This could conceivably be a reference to an enum member. They don't show up in allModelElements.
342375 _findEnumReferences,
376+ // Oh, and someone might have some type parameters or other garbage.
377+ // After finding within classes because sometimes parentheses are used
378+ // to imply constructors.
379+ _findWithoutTrailingIgnoreStuff,
343380 // Use the analyzer to resolve a comment reference.
344- _findAnalyzerReferences
381+ _findAnalyzerReferences,
345382 ]) {
346383 findMethod ();
347384 // Remove any "null" objects after each step of trying to add to results.
@@ -355,8 +392,6 @@ class _MarkdownCommentReference {
355392 // This isn't C++. References to class methods are slightly expensive
356393 // in Dart so don't build that list unless you need to.
357394 for (void Function () reduceMethod in [
358- // If this name could refer to a class or a constructor, prefer the class.
359- _reducePreferClass,
360395 // If a result is actually in this library, prefer that.
361396 _reducePreferResultsInSameLibrary,
362397 // If a result is accessible in this library, prefer that.
@@ -404,30 +439,6 @@ class _MarkdownCommentReference {
404439 List <String > get codeRefChompedParts =>
405440 _codeRefChompedParts ?? = codeRefChomped.split ('.' );
406441
407- /// Returns true if this is a constructor we should consider due to its
408- /// name and the code reference, or if this isn't a constructor. False
409- /// otherwise.
410- bool _ConsiderIfConstructor (ModelElement modelElement) {
411- // TODO(jcollins-g): Rewrite this to handle constructors in a less hacky way
412- if (modelElement is ! Constructor ) return true ;
413- if (codeRef.contains (isConstructor)) return true ;
414- Constructor aConstructor = modelElement;
415- if (codeRefParts.length > 1 ) {
416- // Pick the last two parts, in case a specific library was part of the
417- // codeRef.
418- if (codeRefParts[codeRefParts.length - 1 ] ==
419- codeRefParts[codeRefParts.length - 2 ]) {
420- // Foobar.Foobar -- assume they really do mean the constructor for this class.
421- return true ;
422- }
423- }
424- if (aConstructor.name != aConstructor.enclosingElement.name) {
425- // This isn't a default constructor so treat it like any other member.
426- return true ;
427- }
428- return false ;
429- }
430-
431442 void _reducePreferAnalyzerResolution () {
432443 Element refElement = _getRefElementFromCommentRefs (commentRefs, codeRef);
433444 if (results.any ((me) => me.element == refElement)) {
@@ -471,12 +482,6 @@ class _MarkdownCommentReference {
471482 }
472483 }
473484
474- void _reducePreferClass () {
475- if (results.any ((r) => r is Class )) {
476- results.removeWhere ((r) => r is Constructor );
477- }
478- }
479-
480485 void _findTypeParameters () {
481486 if (element is TypeParameters ) {
482487 results.addAll ((element as TypeParameters ).typeParameters.where ((p) =>
@@ -540,14 +545,51 @@ class _MarkdownCommentReference {
540545 }
541546 }
542547
548+ /// Transform members of [toConvert] that are classes to their default constructor,
549+ /// if a constructor is implied. If not, do the reverse conversion for default
550+ /// constructors.
551+ ModelElement _convertConstructors (ModelElement toConvert) {
552+ if (_impliedDefaultConstructor != null ) {
553+ if (toConvert is Class && toConvert.name == _impliedDefaultConstructor) {
554+ return toConvert.defaultConstructor;
555+ }
556+ return toConvert;
557+ } else {
558+ if (toConvert is Constructor &&
559+ (toConvert.enclosingElement as Class ).defaultConstructor ==
560+ toConvert) {
561+ return toConvert.enclosingElement;
562+ }
563+ return toConvert;
564+ }
565+ }
566+
567+ void _findReferenceFromPrefixes () {
568+ if (element is ! ModelElement ) return ;
569+ Map <String , Set <Library >> prefixToLibrary =
570+ (element as ModelElement ).definingLibrary.prefixToLibrary;
571+ if (prefixToLibrary.containsKey (codeRefChompedParts.first)) {
572+ if (codeRefChompedParts.length == 1 ) {
573+ results.addAll (prefixToLibrary[codeRefChompedParts.first]);
574+ } else {
575+ String lookup = codeRefChompedParts.sublist (1 ).join ('.' );
576+ prefixToLibrary[codeRefChompedParts.first]? .forEach ((l) => l
577+ .modelElementsNameMap[lookup]
578+ ? .map (_convertConstructors)
579+ ? .forEach ((m) => _addCanonicalResult (m, _getPreferredClass (m))));
580+ }
581+ }
582+ }
583+
543584 void _findGlobalWithinRefElementCache () {
544585 if (packageGraph.findRefElementCache.containsKey (codeRefChomped)) {
545586 for (final modelElement
546587 in packageGraph.findRefElementCache[codeRefChomped]) {
547588 if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary ||
548589 (modelElement is Library &&
549590 codeRefChomped == modelElement.fullyQualifiedName)) {
550- _addCanonicalResult (modelElement, null );
591+ _addCanonicalResult (
592+ _convertConstructors (modelElement), preferredClass);
551593 }
552594 }
553595 }
@@ -557,8 +599,7 @@ class _MarkdownCommentReference {
557599 // Only look for partially qualified matches if we didn't find a fully qualified one.
558600 if (library.modelElementsNameMap.containsKey (codeRefChomped)) {
559601 for (final modelElement in library.modelElementsNameMap[codeRefChomped]) {
560- if (! _ConsiderIfConstructor (modelElement)) continue ;
561- _addCanonicalResult (modelElement, preferredClass);
602+ _addCanonicalResult (_convertConstructors (modelElement), preferredClass);
562603 }
563604 }
564605 }
@@ -571,13 +612,12 @@ class _MarkdownCommentReference {
571612 packageGraph.findRefElementCache.containsKey (codeRefChomped)) {
572613 for (final ModelElement modelElement
573614 in packageGraph.findRefElementCache[codeRefChomped]) {
574- if (! _ConsiderIfConstructor (modelElement)) continue ;
575615 // For fully qualified matches, the original preferredClass passed
576616 // might make no sense. Instead, use the enclosing class from the
577617 // element in [packageGraph.findRefElementCache], because that element's
578618 // enclosing class will be preferred from [codeRefChomped]'s perspective.
579619 _addCanonicalResult (
580- modelElement,
620+ _convertConstructors ( modelElement) ,
581621 modelElement.enclosingElement is Class
582622 ? modelElement.enclosingElement
583623 : null );
@@ -678,26 +718,17 @@ class _MarkdownCommentReference {
678718 /// Get any possible results for this class in the superChain. Returns
679719 /// true if we found something.
680720 void _getResultsForSuperChainElement (Class c, Class tryClass) {
681- Iterable <ModelElement > membersToCheck;
682- membersToCheck = (c.allModelElementsByNamePart[codeRefChomped] ?? [])
683- . where ((m) => _ConsiderIfConstructor (m) );
721+ Iterable <ModelElement > membersToCheck =
722+ (c.allModelElementsByNamePart[codeRefChomped] ?? [])
723+ . map (_convertConstructors );
684724 for (final ModelElement modelElement in membersToCheck) {
685725 // [thing], a member of this class
686726 _addCanonicalResult (modelElement, tryClass);
687727 }
688728 membersToCheck = (c.allModelElementsByNamePart[codeRefChompedParts.last] ??
689729 < ModelElement > [])
690- .where ((m) => _ConsiderIfConstructor (m));
691- if (codeRefChompedParts.first == c.name) {
692- // [Foo...thing], a member of this class (possibly a parameter).
693- membersToCheck.forEach ((m) => _addCanonicalResult (m, tryClass));
694- } else if (codeRefChompedParts.length > 1 &&
695- codeRefChompedParts[codeRefChompedParts.length - 2 ] == c.name) {
696- // [....Foo.thing], a member of this class partially specified.
697- membersToCheck
698- .whereType <Constructor >()
699- .forEach ((m) => _addCanonicalResult (m, tryClass));
700- }
730+ .map (_convertConstructors);
731+ membersToCheck.forEach ((m) => _addCanonicalResult (m, tryClass));
701732 results.remove (null );
702733 if (results.isNotEmpty) return ;
703734 if (c.fullyQualifiedNameWithoutLibrary == codeRefChomped) {
0 commit comments