Chromium Code Reviews| 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 |