@@ -8,15 +8,16 @@ import 'package:analyzer/dart/ast/ast.dart';
88import 'package:analyzer/dart/ast/visitor.dart' ;
99import 'package:analyzer/dart/element/element.dart' ;
1010import 'package:analyzer/dart/element/type.dart' ;
11+ import 'package:collection/collection.dart' ;
1112
1213import '../analyzer.dart' ;
1314
1415const _desc = 'Unreachable top-level members in executable libraries.' ;
1516
1617const _details = r'''
17- Top-level members in an executable library should be used directly inside this
18- library. An executable library is a library that contains a `main` top-level
19- function or that contains a top-level function annotated with
18+ Top-level members and static members in an executable library should be used
19+ directly inside this library. An executable library is a library that contains
20+ a `main` top-level function or that contains a top-level function annotated with
2021`@pragma('vm:entry-point')`). Executable libraries are not usually imported
2122and it's better to avoid defining unused members.
2223
@@ -43,7 +44,7 @@ void f() {}
4344
4445class UnreachableFromMain extends LintRule {
4546 static const LintCode code = LintCode ('unreachable_from_main' ,
46- ' Unreachable top-level member in an executable library.' ,
47+ " Unreachable member '{0}' in an executable library." ,
4748 correctionMessage: 'Try referencing the member or removing it.' );
4849
4950 UnreachableFromMain ()
@@ -99,7 +100,12 @@ class _DeclarationGatherer {
99100 }
100101
101102 void _addStaticMember (ClassMember member) {
102- if (member is FieldDeclaration && member.isStatic) {
103+ if (member is ConstructorDeclaration ) {
104+ var e = member.declaredElement;
105+ if (e != null && e.isPublic && member.parent is ! EnumDeclaration ) {
106+ declarations.add (member);
107+ }
108+ } else if (member is FieldDeclaration && member.isStatic) {
103109 for (var field in member.fields.variables) {
104110 var e = field.declaredElement;
105111 if (e != null && e.isPublic) {
@@ -115,20 +121,72 @@ class _DeclarationGatherer {
115121 }
116122}
117123
118- /// A visitor which gathers the declarations of the identifiers it visits.
119- class _IdentifierVisitor extends RecursiveAstVisitor {
124+ /// A visitor which gathers the declarations of the "references" it visits.
125+ ///
126+ /// "References" are most often [SimpleIdentifier] s, but can also be other
127+ /// nodes which refer to a declaration.
128+ // TODO(srawlins): Add support for patterns.
129+ class _ReferenceVisitor extends RecursiveAstVisitor {
120130 Map <Element , Declaration > declarationMap;
121131
122132 Set <Declaration > declarations = {};
123133
124- _IdentifierVisitor (this .declarationMap);
134+ _ReferenceVisitor (this .declarationMap);
135+
136+ @override
137+ void visitAnnotation (Annotation node) {
138+ var e = node.element;
139+ if (e != null ) {
140+ _addDeclaration (e);
141+ }
142+ super .visitAnnotation (node);
143+ }
125144
126145 @override
127146 void visitAssignmentExpression (AssignmentExpression node) {
128147 _visitCompoundAssignmentExpression (node);
129148 super .visitAssignmentExpression (node);
130149 }
131150
151+ @override
152+ void visitClassDeclaration (ClassDeclaration node) {
153+ var element = node.declaredElement;
154+ if (element != null ) {
155+ var hasConstructors =
156+ node.members.any ((e) => e is ConstructorDeclaration );
157+ if (! hasConstructors) {
158+ // The default constructor will have an implicit super-initializer to
159+ // the super-type's unnamed constructor.
160+ _addDefaultSuperConstructorDeclaration (node);
161+ }
162+ }
163+ super .visitClassDeclaration (node);
164+ }
165+
166+ @override
167+ void visitConstructorDeclaration (ConstructorDeclaration node) {
168+ // If a constructor does not have an explicit super-initializer (or redirection?)
169+ // then it has an implicit super-initializer to the super-type's unnamed constructor.
170+ var hasSuperInitializer =
171+ node.initializers.any ((e) => e is SuperConstructorInvocation );
172+ if (! hasSuperInitializer) {
173+ var enclosingClass = node.parent;
174+ if (enclosingClass is ClassDeclaration ) {
175+ _addDefaultSuperConstructorDeclaration (enclosingClass);
176+ }
177+ }
178+ super .visitConstructorDeclaration (node);
179+ }
180+
181+ @override
182+ void visitConstructorName (ConstructorName node) {
183+ var e = node.staticElement;
184+ if (e != null ) {
185+ _addDeclaration (e);
186+ }
187+ super .visitConstructorName (node);
188+ }
189+
132190 @override
133191 void visitPostfixExpression (PostfixExpression node) {
134192 _visitCompoundAssignmentExpression (node);
@@ -141,6 +199,16 @@ class _IdentifierVisitor extends RecursiveAstVisitor {
141199 super .visitPrefixExpression (node);
142200 }
143201
202+ @override
203+ void visitRedirectingConstructorInvocation (
204+ RedirectingConstructorInvocation node) {
205+ var element = node.staticElement;
206+ if (element != null ) {
207+ _addDeclaration (element);
208+ }
209+ super .visitRedirectingConstructorInvocation (node);
210+ }
211+
144212 @override
145213 void visitSimpleIdentifier (SimpleIdentifier node) {
146214 if (! node.inDeclarationContext ()) {
@@ -152,6 +220,15 @@ class _IdentifierVisitor extends RecursiveAstVisitor {
152220 super .visitSimpleIdentifier (node);
153221 }
154222
223+ @override
224+ void visitSuperConstructorInvocation (SuperConstructorInvocation node) {
225+ var e = node.staticElement;
226+ if (e != null ) {
227+ _addDeclaration (e);
228+ }
229+ super .visitSuperConstructorInvocation (node);
230+ }
231+
155232 /// Adds the declaration of the top-level element which contains [element] to
156233 /// [declarations] , if it is found in [declarationMap] .
157234 ///
@@ -167,8 +244,8 @@ class _IdentifierVisitor extends RecursiveAstVisitor {
167244 declarations.add (enclosingTopLevelDeclaration);
168245 }
169246
170- // Also add [element]'s declaration if it is a static accessor or static
171- // method.
247+ // Also add [element]'s declaration if it is a constructor, static accessor,
248+ // or static method.
172249 if (element.isPrivate) {
173250 return ;
174251 }
@@ -178,7 +255,7 @@ class _IdentifierVisitor extends RecursiveAstVisitor {
178255 }
179256 if (enclosingElement is InterfaceElement ||
180257 enclosingElement is ExtensionElement ) {
181- if (element is PropertyAccessorElement && element.isStatic ) {
258+ if (element is ConstructorElement ) {
182259 var declaration = declarationMap[element];
183260 if (declaration != null ) {
184261 declarations.add (declaration);
@@ -188,6 +265,23 @@ class _IdentifierVisitor extends RecursiveAstVisitor {
188265 if (declaration != null ) {
189266 declarations.add (declaration);
190267 }
268+ } else if (element is PropertyAccessorElement && element.isStatic) {
269+ var declaration = declarationMap[element];
270+ if (declaration != null ) {
271+ declarations.add (declaration);
272+ }
273+ }
274+ }
275+ }
276+
277+ void _addDefaultSuperConstructorDeclaration (ClassDeclaration class_) {
278+ var classElement = class_.declaredElement;
279+ var supertype = classElement? .supertype;
280+ if (supertype != null ) {
281+ var unnamedConstructor =
282+ supertype.constructors.firstWhereOrNull ((e) => e.name.isEmpty);
283+ if (unnamedConstructor != null ) {
284+ _addDeclaration (unnamedConstructor);
191285 }
192286 }
193287 }
@@ -241,13 +335,14 @@ class _Visitor extends SimpleAstVisitor<void> {
241335 }
242336 }
243337
244- // The set of the declarations which each top-level declaration references.
338+ // The set of the declarations which each top-level and static declaration
339+ // references.
245340 var dependencies = < Declaration , Set <Declaration >> {};
246341
247342 // Map each declaration to the collection of declarations which are
248343 // referenced within its body.
249344 for (var declaration in declarations) {
250- var visitor = _IdentifierVisitor (declarationByElement);
345+ var visitor = _ReferenceVisitor (declarationByElement);
251346 declaration.accept (visitor);
252347 dependencies[declaration] = visitor.declarations;
253348 }
@@ -276,15 +371,25 @@ class _Visitor extends SimpleAstVisitor<void> {
276371 });
277372
278373 for (var member in unusedMembers) {
279- if (member is NamedCompilationUnitMember ) {
280- rule.reportLintForToken (member.name);
374+ if (member is ConstructorDeclaration ) {
375+ if (member.name == null ) {
376+ rule.reportLint (member.returnType, arguments: [member.nameForError]);
377+ } else {
378+ rule.reportLintForToken (member.name,
379+ arguments: [member.nameForError]);
380+ }
381+ } else if (member is NamedCompilationUnitMember ) {
382+ rule.reportLintForToken (member.name, arguments: [member.nameForError]);
281383 } else if (member is VariableDeclaration ) {
282- rule.reportLintForToken (member.name);
384+ rule.reportLintForToken (member.name, arguments : [member.nameForError] );
283385 } else if (member is ExtensionDeclaration ) {
386+ var memberName = member.name;
284387 rule.reportLintForToken (
285- member.name ?? member.firstTokenAfterCommentAndMetadata);
388+ memberName ?? member.firstTokenAfterCommentAndMetadata,
389+ arguments: [member.nameForError]);
286390 } else {
287- rule.reportLintForToken (member.firstTokenAfterCommentAndMetadata);
391+ rule.reportLintForToken (member.firstTokenAfterCommentAndMetadata,
392+ arguments: [member.nameForError]);
288393 }
289394 }
290395 }
@@ -323,3 +428,28 @@ extension on Annotation {
323428 return type is InterfaceType && type.element.isPragma;
324429 }
325430}
431+
432+ extension on Declaration {
433+ String get nameForError {
434+ // TODO(srawlins): Move this to analyzer when other uses are found.
435+ // TODO(srawlins): Convert to switch-expression, hopefully.
436+ var self = this ;
437+ if (self is ConstructorDeclaration ) {
438+ return self.name? .lexeme ?? self.returnType.name;
439+ } else if (self is EnumConstantDeclaration ) {
440+ return self.name.lexeme;
441+ } else if (self is ExtensionDeclaration ) {
442+ var name = self.name;
443+ return name? .lexeme ?? 'the unnamed extension' ;
444+ } else if (self is MethodDeclaration ) {
445+ return self.name.lexeme;
446+ } else if (self is NamedCompilationUnitMember ) {
447+ return self.name.lexeme;
448+ } else if (self is VariableDeclaration ) {
449+ return self.name.lexeme;
450+ }
451+
452+ assert (false , 'Uncovered Declaration subtype: ${self .runtimeType }' );
453+ return '' ;
454+ }
455+ }
0 commit comments