forked from dart-lang/dartdoc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathaccessor.dart
240 lines (202 loc) · 7.7 KB
/
accessor.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:convert';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/source/line_info.dart';
// ignore: implementation_imports
import 'package:analyzer/src/dart/element/member.dart' show ExecutableMember;
import 'package:collection/collection.dart' show IterableExtension;
import 'package:dartdoc/src/element_type.dart';
import 'package:dartdoc/src/model/comment_referable.dart';
import 'package:dartdoc/src/model/model.dart';
import 'package:dartdoc/src/utils.dart';
import 'package:dartdoc/src/warnings.dart';
/// Getters and setters.
class Accessor extends ModelElement {
@override
final PropertyAccessorElement element;
/// The combo ([Field] or [TopLevelVariable]) containing this accessor.
///
/// Initialized in [Field]'s constructor and in [TopLevelVariable]'s
/// constructor.
// TODO(srawlins): This might be super fragile. This field should somehow be
// initialized by code inside this library.
late GetterSetterCombo enclosingCombo;
Accessor(this.element, super.library, super.packageGraph,
[ExecutableMember? super.originalMember]);
@override
CharacterLocation? get characterLocation => element.isSynthetic
? enclosingCombo.characterLocation
: super.characterLocation;
@override
ExecutableMember? get originalMember =>
super.originalMember as ExecutableMember?;
late final Callable modelType =
getTypeFor((originalMember ?? element).type, library) as Callable;
bool get isSynthetic => element.isSynthetic;
/// The [enclosingCombo] where this element was defined.
late final GetterSetterCombo definingCombo =
getModelForElement(element.variable2!) as GetterSetterCombo;
String get _sourceCode {
if (!isSynthetic) {
return super.sourceCode;
}
var modelNode = packageGraph.getModelNodeFor(definingCombo.element);
return modelNode == null
? ''
: const HtmlEscape().convert(modelNode.sourceCode);
}
@override
String get sourceCode => _sourceCode;
@override
late final String documentationComment = () {
if (isSynthetic) {
/// Build a documentation comment for this accessor.
return _hasSyntheticDocumentationComment
? definingCombo.documentationComment
: '';
}
// TODO(srawlins): This doesn't seem right... the super value has delimiters
// (like `///`), but this one doesn't?
return stripComments(super.documentationComment);
}();
/// If this is a getter, assume we want synthetic documentation.
///
/// If the [definingCombo] has a `nodoc` tag, we want synthetic documentation
/// for a synthetic accessor just in case it is inherited somewhere down the
/// line due to split inheritance.
bool get _hasSyntheticDocumentationComment =>
(isGetter || definingCombo.hasNodoc || _comboDocsAreIndependent) &&
definingCombo.hasDocumentationComment;
// If we're a setter, and a getter exists, do not add synthetic documentation
// if the combo's documentation is actually derived from that getter.
bool get _comboDocsAreIndependent {
if (isSetter && definingCombo.hasGetter) {
if (definingCombo.getter!.isSynthetic ||
!definingCombo.documentationFrom.contains(this)) {
return true;
}
}
return false;
}
@override
bool get hasDocumentationComment => isSynthetic
? _hasSyntheticDocumentationComment
: element.documentationComment != null;
@override
void warn(
PackageWarning kind, {
String? message,
Iterable<Locatable> referredFrom = const [],
Iterable<String> extendedDebug = const [],
}) {
enclosingCombo.warn(kind,
message: message,
referredFrom: referredFrom,
extendedDebug: extendedDebug);
}
@override
ModelElement get enclosingElement {
if (element.enclosingElement is CompilationUnitElement) {
return getModelForElement(element.enclosingElement.enclosingElement!);
}
return packageGraph.getModelFor(element.enclosingElement, library);
}
@override
String get filePath => enclosingCombo.filePath;
@override
String get aboveSidebarPath {
final enclosingElement = this.enclosingElement;
return switch (enclosingElement) {
Container() => enclosingElement.sidebarPath,
Library() => enclosingElement.sidebarPath,
_ => throw StateError(
'Enclosing element of $this should be Container or Library, but was '
'${enclosingElement.runtimeType}')
};
}
@override
String? get belowSidebarPath => null;
@override
bool get isCanonical => enclosingCombo.isCanonical;
@override
String? get href {
return enclosingCombo.href;
}
bool get isGetter => element.isGetter;
bool get isSetter => element.isSetter;
@override
Kind get kind => Kind.accessor;
/// Accessors should never be participating directly in comment reference
/// lookups.
@override
Map<String, CommentReferable> get referenceChildren =>
enclosingCombo.referenceChildren;
/// Accessors should never be participating directly in comment reference
/// lookups.
@override
Iterable<CommentReferable> get referenceParents =>
enclosingCombo.referenceParents;
}
/// A getter or setter that is a member of a [Container].
class ContainerAccessor extends Accessor with ContainerMember, Inheritable {
/// The index and values fields are never declared, and must be special cased.
bool get _isEnumSynthetic =>
enclosingCombo is EnumField && (name == 'index' || name == 'values');
@override
CharacterLocation? get characterLocation {
if (_isEnumSynthetic) return enclosingElement.characterLocation;
// TODO(jcollins-g): Remove the enclosingCombo case below once
// https://github.com/dart-lang/sdk/issues/46154 is fixed.
if (enclosingCombo is EnumField) return enclosingCombo.characterLocation;
return super.characterLocation;
}
late final Container _enclosingElement;
bool _isInherited = false;
@override
bool get isCovariant => isSetter && parameters.first.isCovariant;
ContainerAccessor(super.element, super.library, super.packageGraph) {
_enclosingElement = super.enclosingElement as Container;
}
ContainerAccessor.inherited(PropertyAccessorElement element, Library library,
PackageGraph packageGraph, this._enclosingElement,
{ExecutableMember? originalMember})
: super(element, library, packageGraph, originalMember) {
_isInherited = true;
}
@override
bool get isInherited => _isInherited;
@override
Container get enclosingElement => _enclosingElement;
@override
ContainerAccessor? get overriddenElement {
assert(packageGraph.allLibrariesAdded);
final parent = element.enclosingElement;
if (parent is! InterfaceElement) {
return null;
}
for (final supertype in parent.allSupertypes) {
var accessor = isGetter
? supertype.getGetter(element.name)?.declaration
: supertype.getSetter(element.name)?.declaration;
if (accessor == null) {
continue;
}
final parentContainer =
getModelForElement(supertype.element) as InheritingContainer;
final possibleFields =
parentContainer.declaredFields.where((f) => !f.isStatic);
final fieldName = accessor.name.replaceFirst('=', '');
final foundField =
possibleFields.firstWhereOrNull((f) => f.element.name == fieldName);
if (foundField == null) {
continue;
}
final overridden = isGetter ? foundField.getter! : foundField.setter!;
assert(!overridden.isInherited);
return overridden;
}
return null;
}
}