@@ -17,8 +17,10 @@ const _desc = 'Unreachable top-level members in executable libraries.';
1717const _details = r'''
1818
1919Top-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.
2224
2325This rule assumes that an executable library isn't imported by other files
2426except to execute its `main` function.
@@ -100,6 +102,8 @@ class _Visitor extends SimpleAstVisitor<void> {
100102 }
101103 }
102104
105+ // The following map contains for every declaration the set of the
106+ // declarations it references.
103107 var dependencies = Map <Declaration , Set <Declaration >>.fromIterable (
104108 topDeclarations,
105109 value: (declaration) =>
@@ -113,21 +117,22 @@ class _Visitor extends SimpleAstVisitor<void> {
113117 ],
114118 ])
115119 .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 ))
123123 .map ((e) => declarationByElement[e])
124124 .whereNotNull ()
125125 .where ((e) => e != declaration)
126126 .toSet (),
127127 );
128128
129129 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);
131136 while (toTraverse.isNotEmpty) {
132137 var declaration = toTraverse.removeLast ();
133138 for (var dep in dependencies[declaration]! ) {
@@ -163,26 +168,17 @@ class _Visitor extends SimpleAstVisitor<void> {
163168 (e.name.name == 'main' || e.metadata.any (_isPragmaVmEntry));
164169
165170 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' ;
187179 }
188180}
181+
182+ extension on Element {
183+ bool get isPragma => (library? .isDartCore ?? false ) && name == 'pragma' ;
184+ }
0 commit comments