Skip to content

Commit 89e3a63

Browse files
scheglovCommit Queue
authored andcommitted
Fine. Tracking annotations for element model objects.
WDYT? I annotated `ClassElementImpl2` as an example. Eventually we will have a lint rule to enforce these annotation. So, if a new getter or method is added, the developer who adds it will have to make a decision, how to track it. Change-Id: I303b2b2627ff1868e349356b637c62ce4394b30e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/426982 Reviewed-by: Paul Berry <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
1 parent 8b33588 commit 89e3a63

File tree

3 files changed

+156
-4
lines changed

3 files changed

+156
-4
lines changed

pkg/analyzer/lib/src/dart/element/element.dart

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import 'package:analyzer/src/dart/element/type_system.dart';
4141
import 'package:analyzer/src/dart/resolver/scope.dart'
4242
show Namespace, NamespaceBuilder;
4343
import 'package:analyzer/src/error/inference_error.dart';
44+
import 'package:analyzer/src/fine/annotations.dart';
4445
import 'package:analyzer/src/fine/library_manifest.dart';
4546
import 'package:analyzer/src/fine/requirements.dart';
4647
import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
@@ -135,14 +136,15 @@ class BindPatternVariableFragmentImpl extends PatternVariableFragmentImpl
135136
super.previousFragment as BindPatternVariableFragmentImpl?;
136137
}
137138

139+
@elementClass
138140
class ClassElementImpl2 extends InterfaceElementImpl2 implements ClassElement {
139141
@override
142+
@trackedIncludedIntoId
140143
final Reference reference;
141144

142-
@override
143-
final ClassFragmentImpl firstFragment;
145+
final ClassFragmentImpl _firstFragment;
144146

145-
ClassElementImpl2(this.reference, this.firstFragment) {
147+
ClassElementImpl2(this.reference, this._firstFragment) {
146148
reference.element2 = this;
147149
firstFragment.augmentedInternal = this;
148150
}
@@ -153,7 +155,10 @@ class ClassElementImpl2 extends InterfaceElementImpl2 implements ClassElement {
153155
///
154156
/// If the class is sealed, and all its subtypes are either final or sealed,
155157
/// then these subtypes are all subtypes that are possible.
158+
@trackedDirectlyExpensive
156159
List<InterfaceTypeImpl>? get allSubtypes {
160+
globalResultRequirements?.record_classElement_allSubtypes(element: this);
161+
157162
if (isFinal) {
158163
var result = <InterfaceTypeImpl>[];
159164
for (var element in library2.children2) {
@@ -199,7 +204,16 @@ class ClassElementImpl2 extends InterfaceElementImpl2 implements ClassElement {
199204
}
200205

201206
@override
207+
@trackedDirectlyDisable
208+
ClassFragmentImpl get firstFragment {
209+
globalResultRequirements?.record_disable(this, 'firstFragment');
210+
return _firstFragment;
211+
}
212+
213+
@override
214+
@trackedDirectlyDisable
202215
List<ClassFragmentImpl> get fragments {
216+
globalResultRequirements?.record_disable(this, 'fragments');
203217
return [
204218
for (
205219
ClassFragmentImpl? fragment = firstFragment;
@@ -211,8 +225,12 @@ class ClassElementImpl2 extends InterfaceElementImpl2 implements ClassElement {
211225
}
212226

213227
@override
214-
@override
228+
@trackedDirectlyExpensive
215229
bool get hasNonFinalField {
230+
globalResultRequirements?.record_classElement_hasNonFinalField(
231+
element: this,
232+
);
233+
216234
var classesToVisit = <InterfaceElementImpl2>[];
217235
var visitedClasses = <InterfaceElementImpl2>{};
218236
classesToVisit.add(this);
@@ -244,25 +262,34 @@ class ClassElementImpl2 extends InterfaceElementImpl2 implements ClassElement {
244262
}
245263

246264
@override
265+
@trackedIncludedIntoId
247266
bool get isAbstract => firstFragment.isAbstract;
248267

249268
@override
269+
@trackedIncludedIntoId
250270
bool get isBase => firstFragment.isBase;
251271

252272
@override
273+
@trackedIncludedIntoId
253274
bool get isConstructable => firstFragment.isConstructable;
254275

255276
@override
277+
@trackedIncludedIntoId
256278
bool get isDartCoreEnum => firstFragment.isDartCoreEnum;
257279

258280
@override
281+
@trackedIncludedIntoId
259282
bool get isDartCoreObject => firstFragment.isDartCoreObject;
260283

284+
@trackedIncludedIntoId
261285
bool get isDartCoreRecord {
262286
return name3 == 'Record' && library2.isDartCore;
263287
}
264288

289+
@trackedDirectlyExpensive
265290
bool get isEnumLike {
291+
globalResultRequirements?.record_classElement_isEnumLike(element: this);
292+
266293
// Must be a concrete class.
267294
if (isAbstract) {
268295
return false;
@@ -297,32 +324,42 @@ class ClassElementImpl2 extends InterfaceElementImpl2 implements ClassElement {
297324
}
298325

299326
@override
327+
@trackedIncludedIntoId
300328
bool get isExhaustive => firstFragment.isExhaustive;
301329

302330
@override
331+
@trackedIncludedIntoId
303332
bool get isFinal => firstFragment.isFinal;
304333

305334
@override
335+
@trackedIncludedIntoId
306336
bool get isInterface => firstFragment.isInterface;
307337

308338
@override
339+
@trackedIncludedIntoId
309340
bool get isMixinApplication => firstFragment.isMixinApplication;
310341

311342
@override
343+
@trackedIncludedIntoId
312344
bool get isMixinClass => firstFragment.isMixinClass;
313345

314346
@override
347+
@trackedIncludedIntoId
315348
bool get isSealed => firstFragment.isSealed;
316349

317350
@override
351+
@trackedIncludedIntoId
318352
bool get isValidMixin => firstFragment.isValidMixin;
319353

320354
@override
355+
@trackedDirectlyDisable
321356
T? accept2<T>(ElementVisitor2<T> visitor) {
357+
globalResultRequirements?.record_disable(this, 'accept2');
322358
return visitor.visitClassElement(this);
323359
}
324360

325361
@override
362+
@trackedIndirectly
326363
bool isExtendableIn2(LibraryElement library) {
327364
if (library == library2) {
328365
return true;
@@ -331,6 +368,7 @@ class ClassElementImpl2 extends InterfaceElementImpl2 implements ClassElement {
331368
}
332369

333370
@override
371+
@trackedIndirectly
334372
bool isImplementableIn2(LibraryElement library) {
335373
if (library == library2) {
336374
return true;
@@ -339,6 +377,7 @@ class ClassElementImpl2 extends InterfaceElementImpl2 implements ClassElement {
339377
}
340378

341379
@override
380+
@trackedIndirectly
342381
bool isMixableIn2(LibraryElement library) {
343382
if (library == library2) {
344383
return true;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// Annotation for classes that are part of the element model.
6+
///
7+
/// Classes marked with this annotation require some of their members to be
8+
/// annotated, as follows:
9+
///
10+
/// Public getters and fields must be annotated with one of:
11+
/// [trackedIncludedIntoId]
12+
/// [trackedDirectly]
13+
/// [trackedIndirectly]
14+
///
15+
/// Public methods must be annotated with one of:
16+
/// [trackedDirectly]
17+
/// [trackedIndirectly]
18+
const elementClass = _ElementClass();
19+
20+
/// Annotation for methods that must record the fact of the invocation.
21+
///
22+
/// In contrast to [trackedIncludedIntoId], the result of the method is not
23+
/// reflected in the ID of the target class, so we need to record this
24+
/// requirement separately.
25+
///
26+
/// Examples: `getMethod(name)`, `getNamedConstructor(name)`.
27+
const trackedDirectly = _TrackedDirectly();
28+
29+
/// Annotation for methods that are too invasive, and if invoked for anything
30+
/// other than the current library, disable fine-grained dependencies
31+
/// optimizations. So, the result will be re-computed if just anything in
32+
/// its transitive closure of imported libraries changed.
33+
///
34+
/// Examples: `fragments`, `visitChildren()`.
35+
///
36+
/// We must make sure that the analyzer itself does not use such APIs.
37+
/// We should try to fix any popular lint rules that use such APIs.
38+
const trackedDirectlyDisable = _TrackedDirectlyDisable();
39+
40+
/// As [trackedDirectly], but for methods that are expensive.
41+
///
42+
/// For example, `get methods` adds dependency on all declared methods, but
43+
/// if the client then filters these methods in some way, it would be better
44+
/// to use (or add if absent) a more specific method, e.g. `getMethod(name)`.
45+
///
46+
/// Examples: `constructors`, `methods`.
47+
///
48+
/// We must make sure that the analyzer itself does not use such APIs.
49+
/// We should try to fix any popular lint rules that use such APIs.
50+
const trackedDirectlyExpensive = _TrackedDirectlyExpensive();
51+
52+
/// Annotation for getters that don't require recording.
53+
///
54+
/// The library manifest builder must compare the value of the getter with
55+
/// the previous value in the manifest, and give the element a new ID if the
56+
/// value is different.
57+
///
58+
/// Examples: `isAbstract`, `interfaces`, `returnType`.
59+
const trackedIncludedIntoId = _TrackedIncludedIntoId();
60+
61+
/// Annotation for methods that don't require recording.
62+
///
63+
/// The implementation of the method uses only getters and methods which
64+
/// do record requirements, or already would cause a new ID for the target
65+
/// class.
66+
const trackedIndirectly = _TrackedIndirectly();
67+
68+
final class _ElementClass {
69+
const _ElementClass();
70+
}
71+
72+
final class _TrackedDirectly extends _TrackedKind {
73+
const _TrackedDirectly();
74+
}
75+
76+
final class _TrackedDirectlyDisable extends _TrackedDirectly {
77+
const _TrackedDirectlyDisable();
78+
}
79+
80+
final class _TrackedDirectlyExpensive extends _TrackedDirectly {
81+
const _TrackedDirectlyExpensive();
82+
}
83+
84+
final class _TrackedIncludedIntoId extends _TrackedKind {
85+
const _TrackedIncludedIntoId();
86+
}
87+
88+
final class _TrackedIndirectly extends _TrackedKind {
89+
const _TrackedIndirectly();
90+
}
91+
92+
/// Superclass for all specific kinds of tracking annotations.
93+
sealed class _TrackedKind {
94+
const _TrackedKind();
95+
}

pkg/analyzer/lib/src/fine/requirements.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,24 @@ class RequirementsManifest {
511511
}
512512
}
513513

514+
void record_classElement_allSubtypes({required ClassElementImpl2 element}) {
515+
// TODO(scheglov): implement.
516+
}
517+
518+
void record_classElement_hasNonFinalField({
519+
required ClassElementImpl2 element,
520+
}) {
521+
// TODO(scheglov): implement.
522+
}
523+
524+
void record_classElement_isEnumLike({required ClassElementImpl2 element}) {
525+
// TODO(scheglov): implement.
526+
}
527+
528+
void record_disable(Object target, String method) {
529+
// TODO(scheglov): implement.
530+
}
531+
514532
void record_instanceElement_getGetter({
515533
required InstanceElementImpl2 element,
516534
required String name,

0 commit comments

Comments
 (0)