Skip to content

Commit 36f1fc7

Browse files
authored
Store comment reference data on ModelNode (#3797)
This is work towards #3798. The analyzer is now tagging Comment nodes with references with various static elements, all backed by the new @docImport syntax, via a new DocImportScope (see https://dart-review.googlesource.com/c/sdk/+/345361 and https://dart-review.googlesource.com/c/sdk/+/353232). But they're not available in the element model. So while we have the syntax nodes, in PackageGraph, we must "side-car" the data, by storing it in separate classes (with no references to any AST nodes). So in this change we introduce the CommentReferenceData class, attach instances to ModelNode.
1 parent 7ed6ef8 commit 36f1fc7

File tree

4 files changed

+308
-63
lines changed

4 files changed

+308
-63
lines changed

lib/src/model/model_node.dart

+37-7
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,22 @@ import 'package:analyzer/dart/element/element.dart';
1111
import 'package:meta/meta.dart';
1212

1313
/// Stripped down information derived from [AstNode] containing only information
14-
/// needed to resurrect the source code of [element].
14+
/// needed to resurrect the source code of [_element].
1515
class ModelNode {
1616
final Element _element;
1717
final AnalysisContext _analysisContext;
1818
final int _sourceEnd;
1919
final int _sourceOffset;
2020

21+
/// Data about each comment reference found in the doc comment of this node.
22+
final Map<String, CommentReferenceData>? commentReferenceData;
23+
2124
factory ModelNode(
22-
AstNode? sourceNode, Element element, AnalysisContext analysisContext) {
25+
AstNode? sourceNode,
26+
Element element,
27+
AnalysisContext analysisContext, {
28+
required Map<String, CommentReferenceData>? commentReferenceData,
29+
}) {
2330
if (sourceNode == null) {
2431
return ModelNode._(element, analysisContext,
2532
sourceEnd: -1, sourceOffset: -1);
@@ -32,14 +39,23 @@ class ModelNode {
3239
assert(sourceNode is FieldDeclaration ||
3340
sourceNode is TopLevelVariableDeclaration);
3441
}
35-
return ModelNode._(element, analysisContext,
36-
sourceEnd: sourceNode.end, sourceOffset: sourceNode.offset);
42+
return ModelNode._(
43+
element,
44+
analysisContext,
45+
sourceEnd: sourceNode.end,
46+
sourceOffset: sourceNode.offset,
47+
commentReferenceData: commentReferenceData,
48+
);
3749
}
3850
}
3951

40-
ModelNode._(this._element, this._analysisContext,
41-
{required int sourceEnd, required int sourceOffset})
42-
: _sourceEnd = sourceEnd,
52+
ModelNode._(
53+
this._element,
54+
this._analysisContext, {
55+
required int sourceEnd,
56+
required int sourceOffset,
57+
this.commentReferenceData = const {},
58+
}) : _sourceEnd = sourceEnd,
4359
_sourceOffset = sourceOffset;
4460

4561
bool get _isSynthetic => _sourceEnd < 0 || _sourceOffset < 0;
@@ -63,6 +79,20 @@ class ModelNode {
6379
}();
6480
}
6581

82+
/// Comment reference data from the syntax tree.
83+
///
84+
/// Comment reference data is not available on the analyzer's Element model, so
85+
/// we store it after resolving libraries in instances of this class.
86+
class CommentReferenceData {
87+
final Element element;
88+
final String name;
89+
final int offset;
90+
final int length;
91+
92+
CommentReferenceData(this.element, String? name, this.offset, this.length)
93+
: name = name ?? '';
94+
}
95+
6696
@visibleForTesting
6797
extension SourceStringExtensions on String {
6898
String substringFromLineStart(int offset, int endOffset) {

lib/src/model/package_graph.dart

+83-4
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
import 'dart:collection';
66

77
import 'package:analyzer/dart/analysis/analysis_context.dart';
8-
import 'package:analyzer/dart/ast/ast.dart';
98
import 'package:analyzer/dart/element/element.dart';
109
import 'package:analyzer/file_system/file_system.dart';
1110
import 'package:analyzer/source/source.dart';
1211
// ignore: implementation_imports
12+
import 'package:analyzer/src/dart/ast/ast.dart';
13+
// ignore: implementation_imports
1314
import 'package:analyzer/src/dart/element/inheritance_manager3.dart'
1415
show InheritanceManager3;
1516
// ignore: implementation_imports
@@ -234,6 +235,20 @@ class PackageGraph with CommentReferable, Nameable {
234235
// me how, because the data is on AST nodes, not the element model.
235236
void gatherModelNodes(DartDocResolvedLibrary resolvedLibrary) {
236237
for (var unit in resolvedLibrary.units) {
238+
for (var directive in unit.directives.whereType<LibraryDirective>()) {
239+
// There should be only one library directive. If there are more, there
240+
// is no harm in grabbing ModelNode for each.
241+
var commentReferenceData = directive.documentationComment?.data;
242+
_modelNodes.putIfAbsent(
243+
resolvedLibrary.element,
244+
() => ModelNode(
245+
directive,
246+
resolvedLibrary.element,
247+
analysisContext,
248+
commentReferenceData: commentReferenceData,
249+
));
250+
}
251+
237252
for (var declaration in unit.declarations) {
238253
_populateModelNodeFor(declaration);
239254
switch (declaration) {
@@ -243,6 +258,9 @@ class PackageGraph with CommentReferable, Nameable {
243258
}
244259
case EnumDeclaration():
245260
if (declaration.declaredElement?.isPublic ?? false) {
261+
for (var constant in declaration.constants) {
262+
_populateModelNodeFor(constant);
263+
}
246264
for (var member in declaration.members) {
247265
_populateModelNodeFor(member);
248266
}
@@ -269,12 +287,21 @@ class PackageGraph with CommentReferable, Nameable {
269287
}
270288

271289
void _populateModelNodeFor(Declaration declaration) {
290+
var commentReferenceData = declaration.documentationComment?.data;
291+
272292
if (declaration is FieldDeclaration) {
273293
var fields = declaration.fields.variables;
274294
for (var field in fields) {
275295
var element = field.declaredElement!;
276296
_modelNodes.putIfAbsent(
277-
element, () => ModelNode(field, element, analysisContext));
297+
element,
298+
() => ModelNode(
299+
field,
300+
element,
301+
analysisContext,
302+
commentReferenceData: commentReferenceData,
303+
),
304+
);
278305
}
279306
return;
280307
}
@@ -283,13 +310,27 @@ class PackageGraph with CommentReferable, Nameable {
283310
for (var field in fields) {
284311
var element = field.declaredElement!;
285312
_modelNodes.putIfAbsent(
286-
element, () => ModelNode(field, element, analysisContext));
313+
element,
314+
() => ModelNode(
315+
field,
316+
element,
317+
analysisContext,
318+
commentReferenceData: commentReferenceData,
319+
),
320+
);
287321
}
288322
return;
289323
}
290324
var element = declaration.declaredElement!;
291325
_modelNodes.putIfAbsent(
292-
element, () => ModelNode(declaration, element, analysisContext));
326+
element,
327+
() => ModelNode(
328+
declaration,
329+
element,
330+
analysisContext,
331+
commentReferenceData: commentReferenceData,
332+
),
333+
);
293334
}
294335

295336
ModelNode? getModelNodeFor(Element element) => _modelNodes[element];
@@ -1029,3 +1070,41 @@ class InheritableElementsKey {
10291070

10301071
InheritableElementsKey(this.element, this.library);
10311072
}
1073+
1074+
extension on Comment {
1075+
/// A mapping of all comment references to their various data.
1076+
Map<String, CommentReferenceData> get data {
1077+
if (references.isEmpty) return const {};
1078+
1079+
var data = <String, CommentReferenceData>{};
1080+
for (var reference in references) {
1081+
var commentReferable = reference.expression;
1082+
String name;
1083+
Element? staticElement;
1084+
if (commentReferable case PropertyAccessImpl(:var propertyName)) {
1085+
var target = commentReferable.target;
1086+
if (target is! PrefixedIdentifierImpl) continue;
1087+
name = '${target.name}.${propertyName.name}';
1088+
staticElement = propertyName.staticElement;
1089+
} else if (commentReferable case PrefixedIdentifier(:var identifier)) {
1090+
name = commentReferable.name;
1091+
staticElement = identifier.staticElement;
1092+
} else if (commentReferable case SimpleIdentifier()) {
1093+
name = commentReferable.name;
1094+
staticElement = commentReferable.staticElement;
1095+
} else {
1096+
continue;
1097+
}
1098+
1099+
if (staticElement != null && !data.containsKey(name)) {
1100+
data[name] = CommentReferenceData(
1101+
staticElement,
1102+
name,
1103+
commentReferable.offset,
1104+
commentReferable.length,
1105+
);
1106+
}
1107+
}
1108+
return data;
1109+
}
1110+
}

test/dartdoc_test_base.dart

+7
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,11 @@ $libraryContent
154154
);
155155
return await Dartdoc.fromContext(context, packageBuilder);
156156
}
157+
158+
/// The real offset in a library generated with [bootPackageWithLibrary].
159+
///
160+
/// When a library is written via [bootPackageWithLibrary], the test author
161+
/// provides `libraryContent`, which is a snippet of Dart library text.
162+
int realOffsetFor(int offsetInContent) =>
163+
'\n\nlibrary $libraryName\n\n'.length + offsetInContent;
157164
}

0 commit comments

Comments
 (0)