Skip to content
This repository was archived by the owner on Nov 20, 2024. It is now read-only.

Commit a088573

Browse files
New lint: prefer_void_to_null (#1100)
* New lint: prefer_void_to_null To help encourage people to use void, and discourage use of the academic & suprising Null type now that void is here to better replace it and serve its functions. Allow Null in generic function types, empty literals. In the errors group, with maturity set to experimental.
1 parent 97a6ec9 commit a088573

File tree

5 files changed

+246
-0
lines changed

5 files changed

+246
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
* new lint: `prefer_void_to_null`
2+
13
# 0.1.58
24

35
* roll-back to explicit uses of `new` and `const` to be compatible w/ VMs running `--no-preview-dart-2`

example/all.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ linter:
9595
- prefer_iterable_whereType
9696
- prefer_single_quotes
9797
- prefer_typing_uninitialized_variables
98+
- prefer_void_to_null
9899
- public_member_api_docs
99100
- recursive_getters
100101
- slash_for_doc_comments

lib/src/rules.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ import 'package:linter/src/rules/prefer_is_not_empty.dart';
9595
import 'package:linter/src/rules/prefer_iterable_whereType.dart';
9696
import 'package:linter/src/rules/prefer_single_quotes.dart';
9797
import 'package:linter/src/rules/prefer_typing_uninitialized_variables.dart';
98+
import 'package:linter/src/rules/prefer_void_to_null.dart';
9899
import 'package:linter/src/rules/pub/package_names.dart';
99100
import 'package:linter/src/rules/public_member_api_docs.dart';
100101
import 'package:linter/src/rules/recursive_getters.dart';
@@ -221,6 +222,7 @@ void registerLintRules() {
221222
..register(new PublicMemberApiDocs())
222223
..register(new PreferSingleQuotes())
223224
..register(new PreferTypingUninitializedVariables())
225+
..register(new PreferVoidToNull())
224226
..register(new PubPackageNames())
225227
..register(new RecursiveGetters())
226228
..registerDefault(new SlashForDocComments())
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright (c) 2018, 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/visitor.dart';
7+
import 'package:analyzer/dart/element/element.dart';
8+
import 'package:analyzer/dart/element/type.dart';
9+
import 'package:linter/src/analyzer.dart';
10+
11+
const _desc =
12+
r"Don't use the Null type, unless you are positive that you don't want void.";
13+
14+
const _details = r'''
15+
16+
**DO NOT** use the type Null where void would work.
17+
18+
**BAD:**
19+
```
20+
Null f() {}
21+
Future<Null> f() {}
22+
Stream<Null> f() {}
23+
f(Null x) {}
24+
```
25+
26+
**GOOD:**
27+
```
28+
void f() {}
29+
Future<void> f() {}
30+
Stream<void> f() {}
31+
f(void x) {}
32+
```
33+
34+
Some exceptions include formulating special function types:
35+
36+
```
37+
Null Function(Null, Null);
38+
```
39+
40+
and for making empty literals which are safe to pass into read-only locations
41+
for any type of map or list:
42+
43+
```
44+
<Null>[];
45+
<int, Null>{};
46+
```
47+
''';
48+
49+
class PreferVoidToNull extends LintRule implements NodeLintRule {
50+
PreferVoidToNull()
51+
: super(
52+
name: 'prefer_void_to_null',
53+
description: _desc,
54+
details: _details,
55+
group: Group.errors,
56+
maturity: Maturity.experimental);
57+
58+
@override
59+
void registerNodeProcessors(NodeLintRegistry registry) {
60+
final visitor = new _Visitor(this);
61+
registry.addSimpleIdentifier(this, visitor);
62+
}
63+
}
64+
65+
class _Visitor extends SimpleAstVisitor<void> {
66+
final LintRule rule;
67+
68+
_Visitor(this.rule);
69+
70+
@override
71+
void visitSimpleIdentifier(SimpleIdentifier id) {
72+
final element = id.staticElement;
73+
if (element is ClassElement && element.type.isDartCoreNull) {
74+
final typeName =
75+
id.parent is PrefixedIdentifier ? id.parent.parent : id.parent;
76+
77+
final parent = typeName.parent;
78+
79+
// Null Function()
80+
if (parent is GenericFunctionType) {
81+
return;
82+
}
83+
84+
// Function(Null)
85+
if (parent is SimpleFormalParameter &&
86+
parent.parent is FormalParameterList &&
87+
parent.parent.parent is GenericFunctionType) {
88+
return;
89+
}
90+
91+
// <Null>[] or <Null, Null>{}
92+
if (parent is TypeArgumentList) {
93+
final literal = parent.parent;
94+
if (literal is ListLiteral && literal.elements.isEmpty) {
95+
return;
96+
} else if (literal is MapLiteral && literal.entries.isEmpty) {
97+
return;
98+
}
99+
}
100+
101+
rule.reportLintForToken(id.token);
102+
}
103+
}
104+
}

test/rules/prefer_void_to_null.dart

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright (c) 2018, 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+
// test w/ `pub run test -N prefer_void_to_null`
6+
7+
// TODO(mfairhurst) test void with a prefix, except that causes bugs.
8+
// TODO(mfairhurst) test defining a class named Null (requires a 2nd file)
9+
10+
import 'dart:async';
11+
import 'dart:core';
12+
import 'dart:core' as core;
13+
14+
void void_; // OK
15+
Null null_; // LINT
16+
core.Null core_null; // LINT
17+
Future<void> future_void; // OK
18+
Future<Null> future_null; // LINT
19+
Future<core.Null> future_core_null; // LINT
20+
21+
void void_f() {} // OK
22+
Null null_f() {} // LINT
23+
core.Null core_null_f() {} // LINT
24+
f_void(void x) {} // OK
25+
f_null(Null x) {} // LINT
26+
f_core_null(core.Null x) {} // LINT
27+
28+
void Function(Null) voidFunctionNull; // OK
29+
Null Function() nullFunctionVoid; // OK
30+
Future<Null> Function() FutureNullFunction; // LINT
31+
void Function(Future<Null>) voidFunctionFutureNull; // LINT
32+
33+
usage() {
34+
void void_; // OK
35+
Null null_; // LINT
36+
core.Null core_null; // LINT
37+
Future<void> future_void; // OK
38+
Future<Null> future_null; // LINT
39+
Future<core.Null> future_core_null; // LINT
40+
41+
future_void.then<Null>((_) {}); // LINT
42+
future_void.then<void>((_) {}); // OK
43+
}
44+
45+
void inference() {
46+
final _null = null; // OK
47+
final nullReturnInferred = () {}; // OK
48+
final nullInferred = nullReturnInferred(); // OK
49+
}
50+
51+
void emptyLiterals() {
52+
<Null>[]; // OK
53+
<Null>[null]; // LINT
54+
<void>[]; // OK
55+
<void>[null]; // OK
56+
<int, Null>{}; // OK
57+
<String, Null>{}; // OK
58+
<Object, Null>{}; // OK
59+
<Null, int>{}; // OK
60+
<Null, String>{}; // OK
61+
<Null, Object>{}; // OK
62+
<Null, Null>{}; // OK
63+
<int, Null>{1: null}; // LINT
64+
<String, Null>{"foo": null}; // LINT
65+
<Object, Null>{null: null}; // LINT
66+
<Null, int>{null: 1}; // LINT
67+
<Null, String>{null: "foo"}; // LINT
68+
<Null, Object>{null: null}; // LINT
69+
<Null, // LINT
70+
Null>{null: null}; // LINT
71+
<int, void>{}; // OK
72+
<String, void>{}; // OK
73+
<Object, void>{}; // OK
74+
<void, int>{}; // OK
75+
<void, String>{}; // OK
76+
<void, Object>{}; // OK
77+
<void, void>{}; // OK
78+
<int, void>{1: null}; // OK
79+
<String, void>{"foo": null}; // OK
80+
<Object, void>{null: null}; // OK
81+
<void, int>{null: 1}; // OK
82+
<void, String>{null: "foo"}; // OK
83+
<void, Object>{null: null}; // OK
84+
<void, void>{null: null}; // OK
85+
86+
// TODO(mfairhurst): is it worth handling more complex literals?
87+
}
88+
89+
variableNamedNull() {
90+
var Null; // OK
91+
return Null; // OK
92+
}
93+
94+
parameterNamedNull(Object Null) {
95+
Null; // OK
96+
}
97+
98+
class AsMembers {
99+
void void_; // OK
100+
Null null_; // LINT
101+
core.Null core_null; // LINT
102+
Future<void> future_void; // OK
103+
Future<Null> future_null; // LINT
104+
Future<core.Null> future_core_null; // LINT
105+
106+
void void_f() {} // OK
107+
Null null_f() {} // LINT
108+
core.Null core_null_f() {} // LINT
109+
f_void(void x) {} // OK
110+
f_null(Null x) {} // LINT
111+
f_core_null(core.Null x) {} // LINT
112+
113+
void usage() {
114+
void void_; // OK
115+
Null null_; // LINT
116+
core.Null core_null; // LINT
117+
Future<void> future_void; // OK
118+
Future<Null> future_null; // LINT
119+
Future<core.Null> future_core_null; // LINT
120+
121+
future_void.then<Null>((_) {}); // LINT
122+
future_void.then<void>((_) {}); // OK
123+
}
124+
125+
parameterNamedNull(Object Null) {
126+
Null; // OK
127+
}
128+
129+
variableNamedNull() {
130+
var Null; // OK
131+
return Null; // OK
132+
}
133+
}
134+
135+
class MemberNamedNull {
136+
final Null = null; // OK
137+
}

0 commit comments

Comments
 (0)