Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(479)

Side by Side Diff: lib/src/rules/public_member_api_docs.dart

Issue 1992863002: Check for documented getters when checking setters (#237). (Closed) Base URL: https://github.com/dart-lang/linter.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | test/rules/public_member_api_docs.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | test/rules/public_member_api_docs.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698