1
1
use rustc:: lint:: * ;
2
2
use syntax:: ast:: * ;
3
3
use syntax:: ext:: quote:: rt:: Span ;
4
- use utils:: span_note_and_lint;
4
+ use utils:: { span_lint , span_note_and_lint} ;
5
5
6
6
/// **What it does:** Checks for
7
7
/// - () being assigned to a variable
@@ -24,6 +24,12 @@ declare_lint! {
24
24
"unintended assignment or use of a unit typed value"
25
25
}
26
26
27
+ #[ derive( Copy , Clone ) ]
28
+ enum UnitCause {
29
+ SemiColon ,
30
+ EmptyBlock ,
31
+ }
32
+
27
33
#[ derive( Copy , Clone ) ]
28
34
pub struct UnitExpr ;
29
35
@@ -36,43 +42,16 @@ impl LintPass for UnitExpr {
36
42
impl EarlyLintPass for UnitExpr {
37
43
fn check_expr ( & mut self , cx : & EarlyContext , expr : & Expr ) {
38
44
if let ExprKind :: Assign ( ref _left, ref right) = expr. node {
39
- if let Some ( span) = is_unit_expr ( right) {
40
- span_note_and_lint (
41
- cx,
42
- UNIT_EXPR ,
43
- expr. span ,
44
- "This expression evaluates to the Unit type ()" ,
45
- span,
46
- "Consider removing the trailing semicolon" ,
47
- ) ;
48
- }
45
+ check_for_unit ( cx, right) ;
49
46
}
50
47
if let ExprKind :: MethodCall ( ref _left, ref args) = expr. node {
51
48
for arg in args {
52
- if let Some ( span) = is_unit_expr ( arg) {
53
- span_note_and_lint (
54
- cx,
55
- UNIT_EXPR ,
56
- expr. span ,
57
- "This expression evaluates to the Unit type ()" ,
58
- span,
59
- "Consider removing the trailing semicolon" ,
60
- ) ;
61
- }
49
+ check_for_unit ( cx, arg) ;
62
50
}
63
51
}
64
52
if let ExprKind :: Call ( _, ref args) = expr. node {
65
53
for arg in args {
66
- if let Some ( span) = is_unit_expr ( arg) {
67
- span_note_and_lint (
68
- cx,
69
- UNIT_EXPR ,
70
- expr. span ,
71
- "This expression evaluates to the Unit type ()" ,
72
- span,
73
- "Consider removing the trailing semicolon" ,
74
- ) ;
75
- }
54
+ check_for_unit ( cx, arg) ;
76
55
}
77
56
}
78
57
}
@@ -83,28 +62,41 @@ impl EarlyLintPass for UnitExpr {
83
62
return ;
84
63
}
85
64
if let Some ( ref expr) = local. init {
86
- if let Some ( span) = is_unit_expr ( expr) {
87
- span_note_and_lint (
88
- cx,
89
- UNIT_EXPR ,
90
- expr. span ,
91
- "This expression evaluates to the Unit type ()" ,
92
- span,
93
- "Consider removing the trailing semicolon" ,
94
- ) ;
95
- }
65
+ check_for_unit ( cx, expr) ;
96
66
}
97
67
}
98
68
}
99
69
}
100
70
101
- fn is_unit_expr ( expr : & Expr ) -> Option < Span > {
71
+ fn check_for_unit ( cx : & EarlyContext , expr : & Expr ) {
72
+ match is_unit_expr ( expr) {
73
+ Some ( ( span, UnitCause :: SemiColon ) ) => span_note_and_lint (
74
+ cx,
75
+ UNIT_EXPR ,
76
+ expr. span ,
77
+ "This expression evaluates to the Unit type ()" ,
78
+ span,
79
+ "Consider removing the trailing semicolon" ,
80
+ ) ,
81
+ Some ( ( _span, UnitCause :: EmptyBlock ) ) => span_lint (
82
+ cx,
83
+ UNIT_EXPR ,
84
+ expr. span ,
85
+ "This expression evaluates to the Unit type ()" ,
86
+ ) ,
87
+ None => ( ) ,
88
+ }
89
+ }
90
+
91
+ fn is_unit_expr ( expr : & Expr ) -> Option < ( Span , UnitCause ) > {
102
92
match expr. node {
103
- ExprKind :: Block ( ref block) => if check_last_stmt_in_block ( block) {
104
- Some ( block. stmts [ block. stmts . len ( ) - 1 ] . span )
105
- } else {
106
- None
107
- } ,
93
+ ExprKind :: Block ( ref block) => match check_last_stmt_in_block ( block) {
94
+ Some ( UnitCause :: SemiColon ) =>
95
+ Some ( ( block. stmts [ block. stmts . len ( ) - 1 ] . span , UnitCause :: SemiColon ) ) ,
96
+ Some ( UnitCause :: EmptyBlock ) =>
97
+ Some ( ( block. span , UnitCause :: EmptyBlock ) ) ,
98
+ None => None
99
+ }
108
100
ExprKind :: If ( _, ref then, ref else_) => {
109
101
let check_then = check_last_stmt_in_block ( then) ;
110
102
if let Some ( ref else_) = * else_ {
@@ -113,16 +105,15 @@ fn is_unit_expr(expr: &Expr) -> Option<Span> {
113
105
return Some ( * expr_else) ;
114
106
}
115
107
}
116
- if check_then {
117
- Some ( expr. span )
118
- } else {
119
- None
108
+ match check_then {
109
+ Some ( c) => Some ( ( expr. span , c) ) ,
110
+ None => None ,
120
111
}
121
112
} ,
122
113
ExprKind :: Match ( ref _pattern, ref arms) => {
123
114
for arm in arms {
124
- if let Some ( expr ) = is_unit_expr ( & arm. body ) {
125
- return Some ( expr ) ;
115
+ if let Some ( r ) = is_unit_expr ( & arm. body ) {
116
+ return Some ( r ) ;
126
117
}
127
118
}
128
119
None
@@ -131,18 +122,19 @@ fn is_unit_expr(expr: &Expr) -> Option<Span> {
131
122
}
132
123
}
133
124
134
- fn check_last_stmt_in_block ( block : & Block ) -> bool {
125
+ fn check_last_stmt_in_block ( block : & Block ) -> Option < UnitCause > {
126
+ if block. stmts . is_empty ( ) { return Some ( UnitCause :: EmptyBlock ) ; }
135
127
let final_stmt = & block. stmts [ block. stmts . len ( ) - 1 ] ;
136
128
137
129
138
130
// Made a choice here to risk false positives on divergent macro invocations
139
131
// like `panic!()`
140
132
match final_stmt. node {
141
- StmtKind :: Expr ( _) => false ,
133
+ StmtKind :: Expr ( _) => None ,
142
134
StmtKind :: Semi ( ref expr) => match expr. node {
143
- ExprKind :: Break ( _, _) | ExprKind :: Continue ( _) | ExprKind :: Ret ( _) => false ,
144
- _ => true ,
135
+ ExprKind :: Break ( _, _) | ExprKind :: Continue ( _) | ExprKind :: Ret ( _) => None ,
136
+ _ => Some ( UnitCause :: SemiColon ) ,
145
137
} ,
146
- _ => true ,
138
+ _ => Some ( UnitCause :: SemiColon ) , // not sure what's happening here
147
139
}
148
140
}
0 commit comments