diff --git a/lib/src/model/comment_referable.dart b/lib/src/model/comment_referable.dart
index feb12902c6..578bbdbe83 100644
--- a/lib/src/model/comment_referable.dart
+++ b/lib/src/model/comment_referable.dart
@@ -10,7 +10,6 @@ import 'dart:core';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/scope.dart';
import 'package:collection/collection.dart';
-import 'package:dartdoc/src/model/container.dart';
import 'package:dartdoc/src/model/library.dart';
import 'package:dartdoc/src/model/model_element.dart';
import 'package:dartdoc/src/model/nameable.dart';
@@ -53,12 +52,13 @@ mixin CommentReferable implements Nameable {
return tryParents ? null : this;
}
for (var referenceLookup in _childLookups(reference)) {
- if (scope != null) {
- var result = _lookupViaScope(referenceLookup, filter: filter);
- if (result != null) {
- return result;
- }
+ // First attempt: Ask analyzer's `Scope.lookup` API.
+ var result = _lookupViaScope(referenceLookup, filter: filter);
+ if (result != null) {
+ return result;
}
+
+ // Second attempt: Look through `referenceChildren`.
final referenceChildren = this.referenceChildren;
final childrenResult = referenceChildren[referenceLookup.lookup];
if (childrenResult != null) {
@@ -97,12 +97,33 @@ mixin CommentReferable implements Nameable {
_ReferenceChildrenLookup referenceLookup, {
required bool Function(CommentReferable?) filter,
}) {
- final resultElement = scope!.lookupPreferGetter(referenceLookup.lookup);
- if (resultElement == null) return null;
+ Element? resultElement;
+ final scope = this.scope;
+ if (scope != null) {
+ resultElement = scope.lookupPreferGetter(referenceLookup.lookup);
+ if (resultElement == null) return null;
+ }
+
+ if (this case ModelElement(:var modelNode?) when resultElement == null) {
+ var references = modelNode.commentData?.references;
+ if (references != null) {
+ resultElement = references[referenceLookup.lookup]?.element;
+ }
+ }
+
+ if (resultElement == null) {
+ return null;
+ }
+
ModelElement result;
if (resultElement is PropertyAccessorElement) {
final variable = resultElement.variable2!;
if (variable.isSynthetic) {
+ // First, cache the synthetic variable, so that the
+ // PropertyAccessorElement getter and/or setter are set (see
+ // `Field.new` regarding `enclosingCombo`).
+ packageGraph.getModelForElement(variable);
+ // Then, use the result for the PropertyAccessorElement.
result = packageGraph.getModelForElement(resultElement);
} else {
result = packageGraph.getModelForElement(variable);
@@ -110,14 +131,6 @@ mixin CommentReferable implements Nameable {
} else {
result = packageGraph.getModelForElement(resultElement);
}
- if (result.enclosingElement is Container) {
- assert(
- false,
- '[Container] member detected, support not implemented for analyzer '
- 'scope inside containers',
- );
- return null;
- }
return _recurseChildrenAndFilter(referenceLookup, result, filter: filter);
}
diff --git a/lib/src/model/extension.dart b/lib/src/model/extension.dart
index 50c1a859e3..1427cef05f 100644
--- a/lib/src/model/extension.dart
+++ b/lib/src/model/extension.dart
@@ -75,7 +75,7 @@ class Extension extends Container {
setter = ContainerAccessor(fieldSetter, library, packageGraph);
}
return getModelForPropertyInducingElement(field, library,
- getter: getter, setter: setter) as Field;
+ getter: getter, setter: setter, enclosingContainer: this) as Field;
}).toList(growable: false);
@override
diff --git a/lib/src/model/inheriting_container.dart b/lib/src/model/inheriting_container.dart
index 77bf68c70b..cae5d7e7cd 100644
--- a/lib/src/model/inheriting_container.dart
+++ b/lib/src/model/inheriting_container.dart
@@ -566,7 +566,7 @@ abstract class InheritingContainer extends Container {
(setter == null || setter.isInherited)) {
// Field is 100% inherited.
return getModelForPropertyInducingElement(field, library,
- enclosingContainer: this, getter: getter, setter: setter) as Field;
+ getter: getter, setter: setter, enclosingContainer: this) as Field;
} else {
// Field is <100% inherited (could be half-inherited).
// TODO(jcollins-g): Navigation is probably still confusing for
diff --git a/lib/src/model/model_element.dart b/lib/src/model/model_element.dart
index 6a15eccbc6..cc7e979f8a 100644
--- a/lib/src/model/model_element.dart
+++ b/lib/src/model/model_element.dart
@@ -133,8 +133,15 @@ abstract class ModelElement
}
ModelElement newModelElement;
- if (e is FieldElement) {
- if (enclosingContainer == null) {
+ if (e is TopLevelVariableElement) {
+ assert(getter != null || setter != null);
+ newModelElement =
+ TopLevelVariable(e, library, packageGraph, getter, setter);
+ } else if (e is FieldElement) {
+ if (enclosingContainer is Extension) {
+ newModelElement = Field(e, library, packageGraph,
+ getter as ContainerAccessor?, setter as ContainerAccessor?);
+ } else if (enclosingContainer == null) {
if (e.isEnumConstant) {
var constantValue = e.computeConstantValue();
if (constantValue == null) {
@@ -157,7 +164,6 @@ abstract class ModelElement
getter as ContainerAccessor?, setter as ContainerAccessor?);
}
} else {
- // EnumFields can't be inherited, so this case is simpler.
newModelElement = Field.inherited(
e,
enclosingContainer,
@@ -167,16 +173,11 @@ abstract class ModelElement
setter as ContainerAccessor?,
);
}
- } else if (e is TopLevelVariableElement) {
- assert(getter != null || setter != null);
- newModelElement =
- TopLevelVariable(e, library, packageGraph, getter, setter);
} else {
throw UnimplementedError(
'Unrecognized property inducing element: $e (${e.runtimeType})');
}
- if (enclosingContainer != null) assert(newModelElement is Inheritable);
_cacheNewModelElement(e, newModelElement, library,
enclosingContainer: enclosingContainer);
@@ -193,8 +194,6 @@ abstract class ModelElement
// clean that up.
// TODO(jcollins-g): Enforce construction restraint.
// TODO(jcollins-g): Allow e to be null and drop extraneous null checks.
- // TODO(jcollins-g): Auto-vivify element's defining library for library
- // parameter when given a null.
factory ModelElement.for_(
Element e, Library library, PackageGraph packageGraph,
{Container? enclosingContainer}) {
diff --git a/test/end2end/model_test.dart b/test/end2end/model_test.dart
index 80689caf0f..9fbe1c7271 100644
--- a/test/end2end/model_test.dart
+++ b/test/end2end/model_test.dart
@@ -2317,8 +2317,7 @@ void main() async {
late final Class TypeParameterThings,
TypeParameterThingsExtended,
TypeParameterThingsExtendedQ;
- late final Extension UnboundTypeTargetExtension;
- late final Field aName, aThing, doesNotCrash;
+ late final Field aName, aThing;
late final TypeParameter ATypeParam,
BTypeParam,
CTypeParam,
@@ -2329,11 +2328,6 @@ void main() async {
late final ModelFunction aTopLevelTypeParameterFunction;
setUpAll(() {
- UnboundTypeTargetExtension =
- fakeLibrary.extensions.named('UnboundTypeTargetExtension');
- doesNotCrash =
- UnboundTypeTargetExtension.instanceFields.named('doesNotCrash');
-
aTopLevelTypeParameterFunction =
fakeLibrary.functions.named('aTopLevelTypeParameterFunction');
// TODO(jcollins-g): dart-lang/dartdoc#2704, HTML and type parameters
@@ -2368,11 +2362,6 @@ void main() async {
QTypeParam = aMethodExtendedQ.typeParameters.named('QTypeParam');
});
- test('on extension targeting an unbound type', () {
- expect(referenceLookup(UnboundTypeTargetExtension, 'doesNotCrash'),
- equals(MatchingLinkResult(doesNotCrash)));
- });
-
test('on inherited documentation', () {
expect(referenceLookup(aMethodExtended, 'ATypeParam'),
equals(MatchingLinkResult(ATypeParam)));
diff --git a/test/extensions_test.dart b/test/extensions_test.dart
index f23e7f982e..c61ff49f20 100644
--- a/test/extensions_test.dart
+++ b/test/extensions_test.dart
@@ -53,6 +53,38 @@ var f() {}
);
}
+ void test_referenceToExtensionGetter() async {
+ var library = await bootPackageWithLibrary('''
+extension Ex on int {
+ bool get b => true;
+}
+
+/// Text [Ex.b].
+var f() {}
+''');
+
+ expect(
+ library.functions.named('f').documentationAsHtml,
+ contains('Ex.b'),
+ );
+ }
+
+ void test_referenceToExtensionSetter() async {
+ var library = await bootPackageWithLibrary('''
+extension Ex on int {
+ set b(int value) {}
+}
+
+/// Text [Ex.b].
+var f() {}
+''');
+
+ expect(
+ library.functions.named('f').documentationAsHtml,
+ contains('Ex.b'),
+ );
+ }
+
// TODO(srawlins): Test everything else about extensions.
}
diff --git a/testing/test_package/lib/fake.dart b/testing/test_package/lib/fake.dart
index fc0bda08fd..7b0097afc7 100644
--- a/testing/test_package/lib/fake.dart
+++ b/testing/test_package/lib/fake.dart
@@ -1076,12 +1076,6 @@ extension Leg on Megatron {
bool get hasRightLeg => true;
}
-/// Refer to [doesNotCrash] here.
-extension UnboundTypeTargetExtension on T {
- /// We hope so!
- bool get doesNotCrash => true;
-}
-
class Megatron {}
class SuperMegaTron extends Megatron {}