Skip to content

Commit 2e706be

Browse files
authored
Fix bug around ref resolution of type mentioned in type arguments (#3768)
1 parent d4a5f0b commit 2e706be

File tree

5 files changed

+108
-72
lines changed

5 files changed

+108
-72
lines changed

lib/src/markdown_processor.dart

+9-24
Original file line numberDiff line numberDiff line change
@@ -165,30 +165,15 @@ MatchingLinkResult _getMatchingLinkElement(
165165
String referenceText, Warnable element) {
166166
var commentReference = ModelCommentReference(referenceText);
167167

168-
// A filter to be used by [CommentReferable.referenceBy].
169-
bool Function(CommentReferable?) filter;
170-
171-
// An "allow tree" filter to be used by [CommentReferable.referenceBy].
172-
bool Function(CommentReferable?) allowTree;
173-
174-
if (commentReference.hasCallableHint) {
175-
allowTree = (_) => true;
176-
// Trailing parens indicate we are looking for a callable.
177-
filter = _requireCallable;
178-
} else {
179-
allowTree = (_) => true;
180-
// Neither reject, nor require, an unnamed constructor in the event the
181-
// comment reference structure implies one. (We cannot require it in case a
182-
// library name is the same as a member class name and the class is the
183-
// intended lookup).
184-
filter = commentReference.hasCallableHint
185-
? _requireCallable
186-
// Without hints, reject unnamed constructors and their parameters to
187-
// force resolution to the class.
188-
: filter = _rejectUnnamedAndShadowingConstructors;
189-
}
190-
var lookupResult = element.referenceBy(commentReference.referenceBy,
191-
allowTree: allowTree, filter: filter);
168+
var filter = commentReference.hasCallableHint
169+
// Trailing parens indicate we are looking for a callable.
170+
? _requireCallable
171+
// Without hints, reject unnamed constructors and their parameters to
172+
// force resolution to the class.
173+
: _rejectUnnamedAndShadowingConstructors;
174+
175+
var lookupResult =
176+
element.referenceBy(commentReference.referenceBy, filter: filter);
192177

193178
// TODO(jcollins-g): Consider prioritizing analyzer resolution before custom.
194179
return MatchingLinkResult(lookupResult);

lib/src/model/comment_referable.dart

+18-27
Original file line numberDiff line numberDiff line change
@@ -31,46 +31,44 @@ class _ReferenceChildrenLookup {
3131

3232
/// Support comment reference lookups on a Nameable object.
3333
mixin CommentReferable implements Nameable {
34-
//, ModelBuilderInterface {
3534
/// For any [CommentReferable] where an analyzer [Scope] exists (or can
3635
/// be constructed), implement this. This will take priority over
3736
/// lookups via [referenceChildren]. Can be cached.
3837
Scope? get scope => null;
3938

4039
String? get href => null;
4140

42-
/// Look up a comment reference by its component parts.
41+
/// Looks up a comment reference by its component parts.
4342
///
4443
/// If [tryParents] is true, try looking up the same reference in any parents
4544
/// of `this`. Will skip over results that do not pass a given [filter] and
46-
/// keep searching. Will skip over entire subtrees whose parent node does not
47-
/// pass [allowTree].
45+
/// keep searching.
4846
@nonVirtual
4947
CommentReferable? referenceBy(
5048
List<String> reference, {
5149
required bool Function(CommentReferable?) filter,
52-
required bool Function(CommentReferable?) allowTree,
5350
bool tryParents = true,
5451
Iterable<CommentReferable>? parentOverrides,
5552
}) {
5653
parentOverrides ??= referenceParents;
5754
if (reference.isEmpty) {
5855
return tryParents ? null : this;
5956
}
60-
6157
for (var referenceLookup in _childLookups(reference)) {
6258
if (scope != null) {
63-
var result = _lookupViaScope(referenceLookup,
64-
filter: filter, allowTree: allowTree);
59+
var result = _lookupViaScope(referenceLookup, filter: filter);
6560
if (result != null) {
6661
return result;
6762
}
6863
}
6964
final referenceChildren = this.referenceChildren;
7065
final childrenResult = referenceChildren[referenceLookup.lookup];
7166
if (childrenResult != null) {
72-
var result = _recurseChildrenAndFilter(referenceLookup, childrenResult,
73-
allowTree: allowTree, filter: filter);
67+
var result = _recurseChildrenAndFilter(
68+
referenceLookup,
69+
childrenResult,
70+
filter: filter,
71+
);
7472
if (result != null) {
7573
return result;
7674
}
@@ -79,11 +77,12 @@ mixin CommentReferable implements Nameable {
7977
// If we can't find it in children, try searching parents if allowed.
8078
if (tryParents) {
8179
for (var parent in parentOverrides) {
82-
var result = parent.referenceBy(reference,
83-
tryParents: true,
84-
parentOverrides: referenceGrandparentOverrides,
85-
allowTree: allowTree,
86-
filter: filter);
80+
var result = parent.referenceBy(
81+
reference,
82+
tryParents: true,
83+
parentOverrides: referenceGrandparentOverrides,
84+
filter: filter,
85+
);
8786
if (result != null) return result;
8887
}
8988
}
@@ -99,7 +98,6 @@ mixin CommentReferable implements Nameable {
9998
CommentReferable? _lookupViaScope(
10099
_ReferenceChildrenLookup referenceLookup, {
101100
required bool Function(CommentReferable?) filter,
102-
required bool Function(CommentReferable?) allowTree,
103101
}) {
104102
final resultElement = scope!.lookupPreferGetter(referenceLookup.lookup);
105103
if (resultElement == null) return null;
@@ -122,9 +120,7 @@ mixin CommentReferable implements Nameable {
122120
);
123121
return null;
124122
}
125-
if (!allowTree(result)) return null;
126-
return _recurseChildrenAndFilter(referenceLookup, result,
127-
allowTree: allowTree, filter: filter);
123+
return _recurseChildrenAndFilter(referenceLookup, result, filter: filter);
128124
}
129125

130126
/// Given a [result] found in an implementation of [_lookupViaScope] or
@@ -134,19 +130,14 @@ mixin CommentReferable implements Nameable {
134130
_ReferenceChildrenLookup referenceLookup,
135131
CommentReferable result, {
136132
required bool Function(CommentReferable?) filter,
137-
required bool Function(CommentReferable?) allowTree,
138133
}) {
139134
CommentReferable? returnValue = result;
140135
if (referenceLookup.remaining.isNotEmpty) {
141-
if (allowTree(result)) {
142-
returnValue = result.referenceBy(referenceLookup.remaining,
143-
tryParents: false, allowTree: allowTree, filter: filter);
144-
} else {
145-
returnValue = null;
146-
}
136+
returnValue = result.referenceBy(referenceLookup.remaining,
137+
tryParents: false, filter: filter);
147138
} else if (!filter(result)) {
148139
returnValue = result.referenceBy([referenceLookup.lookup],
149-
tryParents: false, allowTree: allowTree, filter: filter);
140+
tryParents: false, filter: filter);
150141
}
151142
if (!filter(returnValue)) {
152143
returnValue = null;

lib/src/model/method.dart

+16-2
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,24 @@ class Method extends ModelElement
138138
return from.referenceChildren;
139139
}
140140
return _referenceChildren ??= <String, CommentReferable>{
141-
...modelType.returnType.typeArguments.explicitOnCollisionWith(this),
142-
...modelType.typeArguments.explicitOnCollisionWith(this),
141+
// If we want to include all types referred to in the signature of this
142+
// method, this is woefully incomplete. Notice we don't currently include
143+
// the element of the returned type itself, nor nested type arguments,
144+
// nor other nested types e.g. in the case of function types or record
145+
// types. But this is all being replaced with analyzer's resolution soon.
146+
...modelType.returnType.typeArguments.modelElements
147+
.explicitOnCollisionWith(this),
143148
...parameters.explicitOnCollisionWith(this),
144149
...typeParameters.explicitOnCollisionWith(this),
145150
};
146151
}
147152
}
153+
154+
extension on Iterable<ElementType> {
155+
/// The [ModelElement] associated with each type, for each type that is a
156+
/// [DefinedElementType].
157+
List<ModelElement> get modelElements => [
158+
for (var type in this)
159+
if (type is DefinedElementType) type.modelElement,
160+
];
161+
}

test/comment_referable/comment_referable_test.dart

+5-19
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ abstract class Base with Nameable, CommentReferable {
2323
/// Returns the added (or already existing) [Base].
2424
Base add(String newName);
2525

26-
CommentReferable? lookup<T extends CommentReferable?>(String value,
27-
{bool Function(CommentReferable?)? allowTree,
28-
bool Function(CommentReferable?)? filter}) {
29-
return referenceBy(value.split(_separator),
30-
allowTree: allowTree ?? (_) => true, filter: filter ?? (_) => true);
26+
CommentReferable? lookup<T extends CommentReferable?>(
27+
String value, {
28+
bool Function(CommentReferable?)? filter,
29+
}) {
30+
return referenceBy(value.split(_separator), filter: filter ?? (_) => true);
3131
}
3232

3333
@override
@@ -165,20 +165,6 @@ void main() {
165165
isA<GenericChild>());
166166
});
167167

168-
test('Check that allowTree works', () {
169-
referable.add('lib4');
170-
var lib4lib4 = referable.add('lib4.lib4');
171-
var tooDeepSub1 = referable.add('lib4.lib4.sub1');
172-
var sub1 = referable.add('lib4.sub1');
173-
var sub2 = referable.add('lib4.sub2');
174-
expect(sub2.lookup('lib4.lib4'), equals(lib4lib4));
175-
expect(sub2.lookup('lib4.sub1'), equals(tooDeepSub1));
176-
expect(
177-
sub2.lookup('lib4.sub1',
178-
allowTree: (r) => r is Base && (r.parent is Top)),
179-
equals(sub1));
180-
});
181-
182168
test('Check that grandparent overrides work', () {
183169
referable.add('lib4');
184170
var i1 = referable.add('lib4.intermediate1');

test/enum_test.dart

+60
Original file line numberDiff line numberDiff line change
@@ -641,4 +641,64 @@ class C {}
641641
'<a href="$linkPrefix/E/values-constant.html">E.values</a>.</p>',
642642
);
643643
}
644+
645+
void test_canBeReferenced_whenFoundAsReturnType() async {
646+
var library = await bootPackageWithLibrary('''
647+
enum E {
648+
one, two
649+
}
650+
651+
class C {
652+
/// [E] can be referenced.
653+
E m() => E.one;
654+
}
655+
''');
656+
var m = library.classes.named('C').instanceMethods.named('m');
657+
658+
expect(m.hasDocumentationComment, true);
659+
expect(
660+
m.documentationAsHtml,
661+
'<p><a href="$linkPrefix/E.html">E</a> can be referenced.</p>',
662+
);
663+
}
664+
665+
void test_canBeReferenced_whenFoundAsReturnType_typeArgument() async {
666+
var library = await bootPackageWithLibrary('''
667+
enum E {
668+
one, two
669+
}
670+
671+
class C {
672+
/// [E] can be referenced.
673+
Future<E> m() async => E.one;
674+
}
675+
''');
676+
var m = library.classes.named('C').instanceMethods.named('m');
677+
678+
expect(m.hasDocumentationComment, true);
679+
expect(
680+
m.documentationAsHtml,
681+
'<p><a href="$linkPrefix/E.html">E</a> can be referenced.</p>',
682+
);
683+
}
684+
685+
void test_canBeReferenced_whenFoundAsParameterType() async {
686+
var library = await bootPackageWithLibrary('''
687+
enum E {
688+
one, two
689+
}
690+
691+
class C {
692+
/// [E] can be referenced.
693+
void m(E p) {}
694+
}
695+
''');
696+
var m = library.classes.named('C').instanceMethods.named('m');
697+
698+
expect(m.hasDocumentationComment, true);
699+
expect(
700+
m.documentationAsHtml,
701+
'<p><a href="$linkPrefix/E.html">E</a> can be referenced.</p>',
702+
);
703+
}
644704
}

0 commit comments

Comments
 (0)