@@ -17,8 +17,10 @@ const _desc = 'Unreachable top-level members in executable libraries.';
17
17
const _details = r'''
18
18
19
19
Top-level members in an executable library should be used directly inside this
20
- library. Executable libraries are usually never imported and it's better to
21
- avoid defining unused members.
20
+ library. An executable library is a library that contains a `main` top-level
21
+ function or that contains a top-level function annotated with
22
+ `@pragma('vm:entry-point')`). Executable libraries are usually never imported
23
+ and it's better to avoid defining unused members.
22
24
23
25
This rule assumes that an executable library isn't imported by other files
24
26
except to execute its `main` function.
@@ -100,6 +102,8 @@ class _Visitor extends SimpleAstVisitor<void> {
100
102
}
101
103
}
102
104
105
+ // The following map contains for every declaration the set of the
106
+ // declarations it references.
103
107
var dependencies = Map <Declaration , Set <Declaration >>.fromIterable (
104
108
topDeclarations,
105
109
value: (declaration) =>
@@ -113,21 +117,22 @@ class _Visitor extends SimpleAstVisitor<void> {
113
117
],
114
118
])
115
119
.whereNotNull ()
116
- .map ((e) {
117
- while (e.enclosingElement2 != null &&
118
- e.enclosingElement2 is ! CompilationUnitElement ) {
119
- e = e.enclosingElement2! ;
120
- }
121
- return e;
122
- })
120
+ .map ((e) => e.thisOrAncestorMatching ((a) =>
121
+ a.enclosingElement2 == null ||
122
+ a.enclosingElement2 is CompilationUnitElement ))
123
123
.map ((e) => declarationByElement[e])
124
124
.whereNotNull ()
125
125
.where ((e) => e != declaration)
126
126
.toSet (),
127
127
);
128
128
129
129
var usedMembers = entryPoints.toSet ();
130
- var toTraverse = Queue .from (usedMembers);
130
+ // The following variable will be used to visit every reachable declaration
131
+ // starting from entry-points. At every loop an element is removed. This
132
+ // element is marked as used and we add its dependencies in the declaration
133
+ // list to traverse. Once this list is empty `usedMembers` contains every
134
+ // declarations reachable from an entry-point.
135
+ var toTraverse = Queue .of (usedMembers);
131
136
while (toTraverse.isNotEmpty) {
132
137
var declaration = toTraverse.removeLast ();
133
138
for (var dep in dependencies[declaration]! ) {
@@ -163,26 +168,17 @@ class _Visitor extends SimpleAstVisitor<void> {
163
168
(e.name.name == 'main' || e.metadata.any (_isPragmaVmEntry));
164
169
165
170
bool _isPragmaVmEntry (Annotation annotation) {
166
- var elementAnnotation = annotation.elementAnnotation;
167
- if (elementAnnotation != null ) {
168
- var value = elementAnnotation.computeConstantValue ();
169
- if (value != null ) {
170
- var type = value.type;
171
- if (type != null ) {
172
- var element = type.element;
173
- if (element != null ) {
174
- var library = element.library;
175
- if (library != null && library.isDartCore ||
176
- element.name == 'pragma' ) {
177
- var name = value.getField ('name' );
178
- return name != null &&
179
- name.hasKnownValue &&
180
- name.toStringValue () == 'vm:entry-point' ;
181
- }
182
- }
183
- }
184
- }
185
- }
186
- return false ;
171
+ var value = annotation.elementAnnotation? .computeConstantValue ();
172
+ if (value == null ) return false ;
173
+ var element = value.type? .element;
174
+ if (element == null || ! element.isPragma) return false ;
175
+ var name = value.getField ('name' );
176
+ return name != null &&
177
+ name.hasKnownValue &&
178
+ name.toStringValue () == 'vm:entry-point' ;
187
179
}
188
180
}
181
+
182
+ extension on Element {
183
+ bool get isPragma => (library? .isDartCore ?? false ) && name == 'pragma' ;
184
+ }
0 commit comments