OLD | NEW |
---|---|
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library linter.src.rules.public_member_api_docs; | 5 library linter.src.rules.public_member_api_docs; |
6 | 6 |
7 import 'package:analyzer/dart/ast/ast.dart'; | 7 import 'package:analyzer/dart/ast/ast.dart'; |
8 import 'package:analyzer/dart/ast/visitor.dart'; | 8 import 'package:analyzer/dart/ast/visitor.dart'; |
9 import 'package:analyzer/dart/element/element.dart'; | 9 import 'package:analyzer/dart/element/element.dart'; |
10 import 'package:analyzer/src/generated/resolver.dart'; | 10 import 'package:analyzer/src/generated/resolver.dart'; |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
48 /// Initialize the base. | 48 /// Initialize the base. |
49 void init(); | 49 void init(); |
50 } | 50 } |
51 | 51 |
52 /// A sub base. | 52 /// A sub base. |
53 class Sub extends Base { | 53 class Sub extends Base { |
54 @override | 54 @override |
55 void init() { ... } | 55 void init() { ... } |
56 } | 56 } |
57 ``` | 57 ``` |
58 | |
59 Note that consistent with `dartdoc`, an exception to the rule is made when | |
60 documented getters have corresponding undocumented setters. In this case the | |
61 setters inherit the docs from the getters. | |
58 '''; | 62 '''; |
59 | 63 |
60 class PublicMemberApiDocs extends LintRule { | 64 class PublicMemberApiDocs extends LintRule { |
61 PublicMemberApiDocs() | 65 PublicMemberApiDocs() |
62 : super( | 66 : super( |
63 name: 'public_member_api_docs', | 67 name: 'public_member_api_docs', |
64 description: desc, | 68 description: desc, |
65 details: details, | 69 details: details, |
66 group: Group.style); | 70 group: Group.style); |
67 | 71 |
68 @override | 72 @override |
69 AstVisitor getVisitor() => new Visitor(this); | 73 AstVisitor getVisitor() => new Visitor(this); |
70 } | 74 } |
71 | 75 |
72 class Visitor extends GeneralizingAstVisitor { | 76 class Visitor extends GeneralizingAstVisitor { |
73 InheritanceManager manager; | 77 InheritanceManager manager; |
74 | 78 |
75 final LintRule rule; | 79 final LintRule rule; |
76 Visitor(this.rule); | 80 Visitor(this.rule); |
77 | 81 |
78 void check(Declaration node) { | 82 bool check(Declaration node) { |
79 if (node.documentationComment == null && !isOverridingMember(node)) { | 83 if (node.documentationComment == null && !isOverridingMember(node)) { |
80 rule.reportLint(getNodeToAnnotate(node)); | 84 rule.reportLint(getNodeToAnnotate(node)); |
85 return true; | |
81 } | 86 } |
87 return false; | |
82 } | 88 } |
83 | 89 |
84 ExecutableElement getOverriddenMember(Element member) { | 90 ExecutableElement getOverriddenMember(Element member) { |
85 if (member == null || manager == null) { | 91 if (member == null || manager == null) { |
86 return null; | 92 return null; |
87 } | 93 } |
88 | 94 |
89 ClassElement classElement = | 95 ClassElement classElement = |
90 member.getAncestor((element) => element is ClassElement); | 96 member.getAncestor((element) => element is ClassElement); |
91 if (classElement == null) { | 97 if (classElement == null) { |
92 return null; | 98 return null; |
93 } | 99 } |
94 return manager.lookupInheritance(classElement, member.name); | 100 return manager.lookupInheritance(classElement, member.name); |
95 } | 101 } |
96 | 102 |
97 bool isOverridingMember(Declaration node) => | 103 bool isOverridingMember(Declaration node) => |
98 getOverriddenMember(node.element) != null; | 104 getOverriddenMember(node.element) != null; |
99 | 105 |
100 @override | 106 @override |
101 visitClassDeclaration(ClassDeclaration node) { | 107 visitClassDeclaration(ClassDeclaration node) { |
102 if (!isPrivate(node.name)) { | 108 if (isPrivate(node.name)) { |
103 check(node); | 109 return; |
104 } | 110 } |
111 | |
112 check(node); | |
113 | |
114 // Check methods | |
115 | |
116 Map<String, MethodDeclaration> getters = <String, MethodDeclaration>{}; | |
117 Map<String, MethodDeclaration> setters = <String, MethodDeclaration>{}; | |
118 | |
119 // Non-getters/setters. | |
120 List<MethodDeclaration> methods = <MethodDeclaration>[]; | |
121 | |
122 // Identify getter/setter pairs. | |
123 for (ClassMember member in node.members) { | |
124 if (member is MethodDeclaration && !isPrivate(member.name)) { | |
125 if (member.isGetter) { | |
126 getters[member.name.name] = member; | |
127 } else if (member.isSetter) { | |
128 setters[member.name.name] = member; | |
129 } else { | |
130 methods.add(member); | |
131 } | |
132 } | |
133 } | |
134 | |
135 // Check all getters, and collect offenders along the way. | |
136 List<MethodDeclaration> missingDocs = <MethodDeclaration>[]; | |
137 for (MethodDeclaration getter in getters.values) { | |
138 if (check(getter)) { | |
139 missingDocs.add(getter); | |
140 } | |
141 } | |
142 | |
143 // But only setters whose getter is missing a doc. | |
144 for (MethodDeclaration setter in setters.values) { | |
145 MethodDeclaration getter = getters[setter.name.name]; | |
146 if (getter == null) { | |
147 //Look for an inherited getter. | |
148 ExecutableElement getter = | |
149 manager.lookupMember(node.element, setter.name.name); | |
150 if (getter is PropertyAccessorElement) { | |
151 if (getter.documentationComment != null) { | |
152 continue; | |
153 } | |
154 } | |
155 check(setter); | |
156 } else if (missingDocs.contains(getter)) { | |
157 check(setter); | |
158 } | |
159 } | |
160 | |
161 // Check remaining methods. | |
162 methods.forEach(check); | |
105 } | 163 } |
106 | 164 |
107 @override | 165 @override |
108 visitClassTypeAlias(ClassTypeAlias node) { | 166 visitClassTypeAlias(ClassTypeAlias node) { |
109 if (!isPrivate(node.name)) { | 167 if (!isPrivate(node.name)) { |
110 check(node); | 168 check(node); |
111 } | 169 } |
112 } | 170 } |
113 | 171 |
114 @override | 172 @override |
115 visitCompilationUnit(CompilationUnit node) { | 173 visitCompilationUnit(CompilationUnit node) { |
116 LibraryElement library = node?.element?.library; | 174 LibraryElement library = node?.element?.library; |
117 manager = library == null ? null : new InheritanceManager(library); | 175 manager = library == null ? null : new InheritanceManager(library); |
176 | |
177 Map<String, FunctionDeclaration> getters = <String, FunctionDeclaration>{}; | |
178 Map<String, FunctionDeclaration> setters = <String, FunctionDeclaration>{}; | |
pquitslund
2016/05/18 17:21:16
Note that I did play with a few approaches to avoi
| |
179 | |
180 // Check functions. | |
181 | |
182 // Non-getters/setters. | |
183 List<FunctionDeclaration> functions = <FunctionDeclaration>[]; | |
184 | |
185 // Identify getter/setter pairs. | |
186 for (CompilationUnitMember member in node.declarations) { | |
187 if (member is FunctionDeclaration) { | |
188 Identifier name = member.name; | |
189 if (!isPrivate(name) && name.name != 'main') { | |
190 if (member.isGetter) { | |
191 getters[member.name.name] = member; | |
192 } else if (member.isSetter) { | |
193 setters[member.name.name] = member; | |
194 } else { | |
195 functions.add(member); | |
196 } | |
197 } | |
198 } | |
199 } | |
200 | |
201 // Check all getters, and collect offenders along the way. | |
202 List<FunctionDeclaration> missingDocs = <FunctionDeclaration>[]; | |
203 for (FunctionDeclaration getter in getters.values) { | |
204 if (check(getter)) { | |
205 missingDocs.add(getter); | |
206 } | |
207 } | |
208 | |
209 // But only setters whose getter is missing a doc. | |
210 for (FunctionDeclaration setter in setters.values) { | |
211 FunctionDeclaration getter = getters[setter.name.name]; | |
212 if (getter != null && missingDocs.contains(getter)) { | |
213 check(setter); | |
214 } | |
215 } | |
216 | |
217 // Check remaining functions. | |
218 functions.forEach(check); | |
219 | |
118 super.visitCompilationUnit(node); | 220 super.visitCompilationUnit(node); |
119 } | 221 } |
120 | 222 |
121 @override | 223 @override |
122 visitConstructorDeclaration(ConstructorDeclaration node) { | 224 visitConstructorDeclaration(ConstructorDeclaration node) { |
123 if (!inPrivateMember(node) && !isPrivate(node.name)) { | 225 if (!inPrivateMember(node) && !isPrivate(node.name)) { |
124 check(node); | 226 check(node); |
125 } | 227 } |
126 } | 228 } |
127 | 229 |
(...skipping 16 matching lines...) Expand all Loading... | |
144 if (!inPrivateMember(node)) { | 246 if (!inPrivateMember(node)) { |
145 for (VariableDeclaration field in node.fields.variables) { | 247 for (VariableDeclaration field in node.fields.variables) { |
146 if (!isPrivate(field.name)) { | 248 if (!isPrivate(field.name)) { |
147 check(field); | 249 check(field); |
148 } | 250 } |
149 } | 251 } |
150 } | 252 } |
151 } | 253 } |
152 | 254 |
153 @override | 255 @override |
154 visitFunctionDeclaration(FunctionDeclaration node) { | |
155 if (node.parent is! CompilationUnit) { | |
156 return; // Skip inner functions. | |
157 } | |
158 if (!isPrivate(node.name) && node.name.name != 'main') { | |
159 check(node); | |
160 } | |
161 } | |
162 | |
163 @override | |
164 visitFunctionTypeAlias(FunctionTypeAlias node) { | 256 visitFunctionTypeAlias(FunctionTypeAlias node) { |
165 if (!isPrivate(node.name)) { | 257 if (!isPrivate(node.name)) { |
166 check(node); | 258 check(node); |
167 } | 259 } |
168 } | 260 } |
169 | 261 |
170 @override | 262 @override |
171 visitMethodDeclaration(MethodDeclaration node) { | |
172 if (!inPrivateMember(node) && !isPrivate(node.name)) { | |
173 check(node); | |
174 } | |
175 } | |
176 | |
177 @override | |
178 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 263 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
179 for (VariableDeclaration decl in node.variables.variables) { | 264 for (VariableDeclaration decl in node.variables.variables) { |
180 if (!isPrivate(decl.name)) { | 265 if (!isPrivate(decl.name)) { |
181 check(decl); | 266 check(decl); |
182 } | 267 } |
183 } | 268 } |
184 } | 269 } |
185 } | 270 } |
OLD | NEW |