-
Notifications
You must be signed in to change notification settings - Fork 170
/
unnecessary_null_checks.dart
117 lines (100 loc) · 3.04 KB
/
unnecessary_null_checks.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/type.dart';
import '../analyzer.dart';
const _desc = r'Unnecessary null checks.';
const _details = r'''
Don't apply a null check when a nullable value is accepted.
**BAD:**
```dart
f(int? i);
m() {
int? j;
f(j!);
}
```
**GOOD:**
```dart
f(int? i);
m() {
int? j;
f(j);
}
```
''';
DartType? getExpectedType(PostfixExpression node) {
var realNode =
node.thisOrAncestorMatching((e) => e.parent is! ParenthesizedExpression);
var parent = realNode?.parent;
// in return value
if (parent is ReturnStatement || parent is ExpressionFunctionBody) {
var parentExpression = parent?.thisOrAncestorOfType<FunctionExpression>();
if (parentExpression == null) {
return null;
}
var staticType = parentExpression.staticType;
return staticType is FunctionType ? staticType.returnType : null;
}
// assignment
if (parent is AssignmentExpression &&
parent.operator.type == TokenType.EQ &&
(parent.leftHandSide is! Identifier ||
node.operand is! Identifier ||
(parent.leftHandSide as Identifier).name !=
(node.operand as Identifier).name)) {
return parent.writeType;
}
// in variable declaration
if (parent is VariableDeclaration) {
return parent.declaredElement?.type;
}
// as right member of binary operator
if (parent is BinaryExpression && parent.rightOperand == realNode) {
var parentElement = parent.staticElement;
if (parentElement == null) {
return null;
}
return parentElement.parameters.first.type;
}
// as parameter of function
if (parent is NamedExpression) {
realNode = parent;
parent = parent.parent;
}
if (parent is ArgumentList && realNode is Expression) {
return realNode.staticParameterElement?.type;
}
return null;
}
class UnnecessaryNullChecks extends LintRule {
UnnecessaryNullChecks()
: super(
name: 'unnecessary_null_checks',
description: _desc,
details: _details,
maturity: Maturity.experimental,
group: Group.style);
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
var visitor = _Visitor(this, context);
registry.addPostfixExpression(this, visitor);
}
}
class _Visitor extends SimpleAstVisitor<void> {
final LintRule rule;
final LinterContext context;
_Visitor(this.rule, this.context);
@override
void visitPostfixExpression(PostfixExpression node) {
if (node.operator.type != TokenType.BANG) return;
var expectedType = getExpectedType(node);
if (expectedType != null && context.typeSystem.isNullable(expectedType)) {
rule.reportLintForToken(node.operator);
}
}
}