• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dart-lang / linter / 3690449303

pending completion
3690449303

push

github

GitHub
Fix `unnecessary_parenthesis` with postfix bang operator. (#3904)

2 of 2 new or added lines in 1 file covered. (100.0%)

8263 of 8640 relevant lines covered (95.64%)

1.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.18
/lib/src/rules/public_member_api_docs.dart
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
3
// BSD-style license that can be found in the LICENSE file.
4

5
import 'package:analyzer/dart/ast/ast.dart';
6
import 'package:analyzer/dart/ast/token.dart';
7
import 'package:analyzer/dart/ast/visitor.dart';
8
import 'package:analyzer/dart/element/element.dart';
9

10
import '../analyzer.dart';
11
import '../ast.dart';
12

13
const _desc = r'Document all public members.';
14

15
const _details = r'''
16
**DO** document all public members.
17

18
All non-overriding public members should be documented with `///` doc-style
19
comments.
20

21
**BAD:**
22
```dart
23
class Bad {
24
  void meh() { }
25
}
26
```
27

28
**GOOD:**
29
```dart
30
/// A good thing.
31
abstract class Good {
32
  /// Start doing your thing.
33
  void start() => _start();
34

35
  _start();
36
}
37
```
38

39
In case a public member overrides a member it is up to the declaring member
40
to provide documentation.  For example, in the following, `Sub` needn't
41
document `init` (though it certainly may, if there's need).
42

43
**GOOD:**
44
```dart
45
/// Base of all things.
46
abstract class Base {
47
  /// Initialize the base.
48
  void init();
49
}
50

51
/// A sub base.
52
class Sub extends Base {
53
  @override
54
  void init() { ... }
55
}
56
```
57

58
Note that consistent with `dart doc`, an exception to the rule is made when
59
documented getters have corresponding undocumented setters.  In this case the
60
setters inherit the docs from the getters.
61

62
''';
63

64
// TODO(devoncarew): This lint is very slow - we should profile and optimize it.
65

66
// TODO(devoncarew): Longer term, this lint could benefit from being more aware
67
// of the actual API surface area of a package - including that defined by
68
// exports - and linting against that.
69

70
class PublicMemberApiDocs extends LintRule {
71
  PublicMemberApiDocs()
1✔
72
      : super(
1✔
73
            name: 'public_member_api_docs',
74
            description: _desc,
75
            details: _details,
76
            group: Group.style);
77

78
  @override
1✔
79
  void registerNodeProcessors(
80
      NodeLintRegistry registry, LinterContext context) {
81
    if (!isInLibDir(context.currentUnit.unit, context.package)) {
4✔
82
      return;
83
    }
84

85
    var visitor = _Visitor(this, context);
1✔
86
    registry.addClassDeclaration(this, visitor);
1✔
87
    registry.addClassTypeAlias(this, visitor);
1✔
88
    registry.addCompilationUnit(this, visitor);
1✔
89
    registry.addConstructorDeclaration(this, visitor);
1✔
90
    registry.addEnumConstantDeclaration(this, visitor);
1✔
91
    registry.addEnumDeclaration(this, visitor);
1✔
92
    registry.addExtensionDeclaration(this, visitor);
1✔
93
    registry.addFieldDeclaration(this, visitor);
1✔
94
    registry.addFunctionTypeAlias(this, visitor);
1✔
95
    registry.addGenericTypeAlias(this, visitor);
1✔
96
    registry.addMixinDeclaration(this, visitor);
1✔
97
    registry.addTopLevelVariableDeclaration(this, visitor);
1✔
98
  }
99
}
100

101
class _Visitor extends SimpleAstVisitor {
102
  final LintRule rule;
103
  final LinterContext context;
104

105
  _Visitor(this.rule, this.context);
1✔
106

107
  bool check(Declaration node) {
1✔
108
    if (node.documentationComment == null && !isOverridingMember(node)) {
2✔
109
      var errorNode = getNodeToAnnotate(node);
1✔
110
      rule.reportLintForOffset(errorNode.offset, errorNode.length);
4✔
111
      return true;
112
    }
113
    return false;
114
  }
115

116
  void checkMethods(List<ClassMember> members) {
1✔
117
    // Check methods
118

119
    var getters = <String, MethodDeclaration>{};
1✔
120
    var setters = <MethodDeclaration>[];
1✔
121

122
    // Non-getters/setters.
123
    var methods = <MethodDeclaration>[];
1✔
124

125
    // Identify getter/setter pairs.
126
    for (var member in members) {
2✔
127
      if (member is MethodDeclaration && !isPrivate(member.name)) {
3✔
128
        if (member.isGetter) {
1✔
129
          getters[member.name.lexeme] = member;
3✔
130
        } else if (member.isSetter) {
1✔
131
          setters.add(member);
×
132
        } else {
133
          methods.add(member);
1✔
134
        }
135
      }
136
    }
137

138
    // Check all getters, and collect offenders along the way.
139
    var missingDocs = <MethodDeclaration>{};
140
    for (var getter in getters.values) {
2✔
141
      if (check(getter)) {
1✔
142
        missingDocs.add(getter);
1✔
143
      }
144
    }
145

146
    // But only setters whose getter is missing a doc.
147
    for (var setter in setters) {
1✔
148
      var getter = getters[setter.name.lexeme];
×
149
      if (getter != null && missingDocs.contains(getter)) {
×
150
        check(setter);
×
151
      }
152
    }
153

154
    // Check remaining methods.
155
    methods.forEach(check);
2✔
156
  }
157

158
  Element? getOverriddenMember(Element? member) {
1✔
159
    if (member == null) {
160
      return null;
161
    }
162

163
    var interfaceElement = member.thisOrAncestorOfType<InterfaceElement>();
1✔
164
    if (interfaceElement == null) {
165
      return null;
166
    }
167
    var name = member.name;
1✔
168
    if (name == null) {
169
      return null;
170
    }
171

172
    var libraryUri = interfaceElement.library.source.uri;
3✔
173
    return context.inheritanceManager.getInherited(
3✔
174
      interfaceElement.thisType,
1✔
175
      Name(libraryUri, name),
1✔
176
    );
177
  }
178

179
  bool isOverridingMember(Declaration node) =>
1✔
180
      getOverriddenMember(node.declaredElement) != null;
2✔
181

182
  @override
×
183
  void visitClassDeclaration(ClassDeclaration node) {
184
    _visitMembers(node, node.name, node.members);
×
185
  }
186

187
  @override
×
188
  void visitClassTypeAlias(ClassTypeAlias node) {
189
    if (!isPrivate(node.name)) {
×
190
      check(node);
×
191
    }
192
  }
193

194
  @override
1✔
195
  void visitCompilationUnit(CompilationUnit node) {
196
    var getters = <String, FunctionDeclaration>{};
1✔
197
    var setters = <FunctionDeclaration>[];
1✔
198

199
    // Check functions.
200

201
    // Non-getters/setters.
202
    var functions = <FunctionDeclaration>[];
1✔
203

204
    // Identify getter/setter pairs.
205
    for (var member in node.declarations) {
2✔
206
      if (member is FunctionDeclaration) {
1✔
207
        var name = member.name;
×
208
        if (!isPrivate(name) && name.lexeme != 'main') {
×
209
          if (member.isGetter) {
×
210
            getters[member.name.lexeme] = member;
×
211
          } else if (member.isSetter) {
×
212
            setters.add(member);
×
213
          } else {
214
            functions.add(member);
×
215
          }
216
        }
217
      }
218
    }
219

220
    // Check all getters, and collect offenders along the way.
221
    var missingDocs = <FunctionDeclaration>{};
222
    for (var getter in getters.values) {
1✔
223
      if (check(getter)) {
×
224
        missingDocs.add(getter);
×
225
      }
226
    }
227

228
    // But only setters whose getter is missing a doc.
229
    for (var setter in setters) {
1✔
230
      var getter = getters[setter.name.lexeme];
×
231
      if (getter != null && missingDocs.contains(getter)) {
×
232
        check(setter);
×
233
      }
234
    }
235

236
    // Check remaining functions.
237
    functions.forEach(check);
2✔
238

239
    super.visitCompilationUnit(node);
1✔
240
  }
241

242
  @override
×
243
  void visitConstructorDeclaration(ConstructorDeclaration node) {
244
    if (!inPrivateMember(node) && !isPrivate(node.name)) {
×
245
      check(node);
×
246
    }
247
  }
248

249
  @override
1✔
250
  void visitEnumConstantDeclaration(EnumConstantDeclaration node) {
251
    if (!inPrivateMember(node) && !isPrivate(node.name)) {
3✔
252
      check(node);
1✔
253
    }
254
  }
255

256
  @override
1✔
257
  void visitEnumDeclaration(EnumDeclaration node) {
258
    if (isPrivate(node.name)) return;
2✔
259

260
    check(node);
1✔
261
    checkMethods(node.members);
2✔
262
  }
263

264
  @override
1✔
265
  void visitExtensionDeclaration(ExtensionDeclaration node) {
266
    if (node.name == null || isPrivate(node.name)) {
3✔
267
      return;
268
    }
269

270
    check(node);
1✔
271
    checkMethods(node.members);
2✔
272
  }
273

274
  @override
×
275
  void visitFieldDeclaration(FieldDeclaration node) {
276
    if (!inPrivateMember(node)) {
×
277
      for (var field in node.fields.variables) {
×
278
        if (!isPrivate(field.name)) {
×
279
          check(field);
×
280
        }
281
      }
282
    }
283
  }
284

285
  @override
×
286
  void visitFunctionTypeAlias(FunctionTypeAlias node) {
287
    if (!isPrivate(node.name)) {
×
288
      check(node);
×
289
    }
290
  }
291

292
  @override
×
293
  void visitGenericTypeAlias(GenericTypeAlias node) {
294
    if (!isPrivate(node.name)) {
×
295
      check(node);
×
296
    }
297
  }
298

299
  @override
1✔
300
  void visitMixinDeclaration(MixinDeclaration node) {
301
    _visitMembers(node, node.name, node.members);
3✔
302
  }
303

304
  @override
×
305
  void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
306
    for (var decl in node.variables.variables) {
×
307
      if (!isPrivate(decl.name)) {
×
308
        check(decl);
×
309
      }
310
    }
311
  }
312

313
  void _visitMembers(Declaration node, Token name, List<ClassMember> members) {
1✔
314
    if (isPrivate(name)) return;
1✔
315

316
    check(node);
1✔
317
    checkMethods(members);
1✔
318
  }
319
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc