1
- use crate :: { lints:: FnNullCheckDiag , LateContext , LateLintPass , LintContext } ;
1
+ use crate :: { lints:: NonNullCheckDiag , LateContext , LateLintPass , LintContext } ;
2
2
use rustc_ast:: LitKind ;
3
3
use rustc_hir:: { BinOpKind , Expr , ExprKind , TyKind } ;
4
+ use rustc_middle:: ty:: Ty ;
4
5
use rustc_session:: { declare_lint, declare_lint_pass} ;
5
6
use rustc_span:: sym;
6
7
7
8
declare_lint ! {
8
- /// The `incorrect_fn_null_checks ` lint checks for expression that checks if a
9
- /// function pointer is null.
9
+ /// The `incorrect_non_null_checks ` lint checks for expressions that check if a
10
+ /// non-nullable type is null.
10
11
///
11
12
/// ### Example
12
13
///
@@ -22,85 +23,108 @@ declare_lint! {
22
23
///
23
24
/// ### Explanation
24
25
///
25
- /// Function pointers are assumed to be non- null, checking them for null will always
26
- /// return false .
27
- INCORRECT_FN_NULL_CHECKS ,
26
+ /// A non-nullable type is assumed to never be null, and therefore having an actual
27
+ /// non-null pointer is ub .
28
+ INCORRECT_NON_NULL_CHECKS ,
28
29
Warn ,
29
- "incorrect checking of null function pointer "
30
+ "incorrect checking of non null pointers "
30
31
}
31
32
32
- declare_lint_pass ! ( IncorrectFnNullChecks => [ INCORRECT_FN_NULL_CHECKS ] ) ;
33
+ declare_lint_pass ! ( IncorrectNonNullChecks => [ INCORRECT_NON_NULL_CHECKS ] ) ;
33
34
34
- fn is_fn_ptr_cast ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
35
+ /// Is the cast to a nonnull type?
36
+ /// If yes, return (ty, nullable_version) where former is the nonnull type while latter
37
+ /// is a nullable version (e.g. (fn, Option<fn>) or (&u8, *const u8)).
38
+ fn is_nonnull_cast < ' a > ( cx : & LateContext < ' a > , expr : & Expr < ' _ > ) -> Option < Ty < ' a > > {
35
39
let mut expr = expr. peel_blocks ( ) ;
36
40
let mut had_at_least_one_cast = false ;
37
41
while let ExprKind :: Cast ( cast_expr, cast_ty) = expr. kind
38
42
&& let TyKind :: Ptr ( _) = cast_ty. kind {
39
43
expr = cast_expr. peel_blocks ( ) ;
40
44
had_at_least_one_cast = true ;
41
45
}
42
- had_at_least_one_cast && cx. typeck_results ( ) . expr_ty_adjusted ( expr) . is_fn ( )
46
+ if !had_at_least_one_cast {
47
+ return None ;
48
+ }
49
+ let ty = cx. typeck_results ( ) . expr_ty_adjusted ( expr) ;
50
+ if ty. is_fn ( ) || ty. is_ref ( ) {
51
+ return Some ( ty) ;
52
+ }
53
+ // Usually, references get coerced to pointers in a casting situation.
54
+ // Therefore, we give also give a look to the original type.
55
+ let ty_unadjusted = cx. typeck_results ( ) . expr_ty_opt ( expr) ;
56
+ if let Some ( ty_unadjusted) = ty_unadjusted && ty_unadjusted. is_ref ( ) {
57
+ return Some ( ty_unadjusted) ;
58
+ }
59
+ None
43
60
}
44
61
45
- impl < ' tcx > LateLintPass < ' tcx > for IncorrectFnNullChecks {
62
+ impl < ' tcx > LateLintPass < ' tcx > for IncorrectNonNullChecks {
46
63
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
47
64
match expr. kind {
48
65
// Catching:
49
- // <*<const/mut> <ty>>::is_null(fn_ptr as *<const/mut> <ty>)
66
+ // <*<const/mut> <ty>>::is_null(test_ptr as *<const/mut> <ty>)
50
67
ExprKind :: Call ( path, [ arg] )
51
68
if let ExprKind :: Path ( ref qpath) = path. kind
52
69
&& let Some ( def_id) = cx. qpath_res ( qpath, path. hir_id ) . opt_def_id ( )
53
70
&& matches ! (
54
71
cx. tcx. get_diagnostic_name( def_id) ,
55
72
Some ( sym:: ptr_const_is_null | sym:: ptr_is_null)
56
73
)
57
- && is_fn_ptr_cast ( cx, arg) =>
74
+ && let Some ( ty ) = is_nonnull_cast ( cx, arg) =>
58
75
{
59
- cx. emit_spanned_lint ( INCORRECT_FN_NULL_CHECKS , expr. span , FnNullCheckDiag )
76
+ let diag = NonNullCheckDiag { ty_desc : ty. prefix_string ( cx. tcx ) } ;
77
+ cx. emit_spanned_lint ( INCORRECT_NON_NULL_CHECKS , expr. span , diag)
60
78
}
61
79
62
80
// Catching:
63
- // (fn_ptr as *<const/mut> <ty>).is_null()
81
+ // (test_ptr as *<const/mut> <ty>).is_null()
64
82
ExprKind :: MethodCall ( _, receiver, _, _)
65
83
if let Some ( def_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
66
84
&& matches ! (
67
85
cx. tcx. get_diagnostic_name( def_id) ,
68
86
Some ( sym:: ptr_const_is_null | sym:: ptr_is_null)
69
87
)
70
- && is_fn_ptr_cast ( cx, receiver) =>
88
+ && let Some ( ty ) = is_nonnull_cast ( cx, receiver) =>
71
89
{
72
- cx. emit_spanned_lint ( INCORRECT_FN_NULL_CHECKS , expr. span , FnNullCheckDiag )
90
+ let diag = NonNullCheckDiag { ty_desc : ty. prefix_string ( cx. tcx ) } ;
91
+ cx. emit_spanned_lint ( INCORRECT_NON_NULL_CHECKS , expr. span , diag)
73
92
}
74
93
75
94
ExprKind :: Binary ( op, left, right) if matches ! ( op. node, BinOpKind :: Eq ) => {
76
95
let to_check: & Expr < ' _ > ;
77
- if is_fn_ptr_cast ( cx, left) {
96
+ let ty: Ty < ' _ > ;
97
+ if let Some ( ty_) = is_nonnull_cast ( cx, left) {
78
98
to_check = right;
79
- } else if is_fn_ptr_cast ( cx, right) {
99
+ ty = ty_;
100
+ } else if let Some ( ty_) = is_nonnull_cast ( cx, right) {
80
101
to_check = left;
102
+ ty = ty_;
81
103
} else {
82
104
return ;
83
105
}
84
106
85
107
match to_check. kind {
86
108
// Catching:
87
- // (fn_ptr as *<const/mut> <ty>) == (0 as <ty>)
109
+ // (test_ptr as *<const/mut> <ty>) == (0 as <ty>)
88
110
ExprKind :: Cast ( cast_expr, _)
89
111
if let ExprKind :: Lit ( spanned) = cast_expr. kind
90
112
&& let LitKind :: Int ( v, _) = spanned. node && v == 0 =>
91
113
{
92
- cx. emit_spanned_lint ( INCORRECT_FN_NULL_CHECKS , expr. span , FnNullCheckDiag )
114
+ let diag = NonNullCheckDiag { ty_desc : ty. prefix_string ( cx. tcx ) } ;
115
+ cx. emit_spanned_lint ( INCORRECT_NON_NULL_CHECKS , expr. span , diag)
93
116
} ,
94
117
95
118
// Catching:
96
- // (fn_ptr as *<const/mut> <ty>) == std::ptr::null()
119
+ // (test_ptr as *<const/mut> <ty>) == std::ptr::null()
97
120
ExprKind :: Call ( path, [ ] )
98
121
if let ExprKind :: Path ( ref qpath) = path. kind
99
122
&& let Some ( def_id) = cx. qpath_res ( qpath, path. hir_id ) . opt_def_id ( )
100
123
&& let Some ( diag_item) = cx. tcx . get_diagnostic_name ( def_id)
101
124
&& ( diag_item == sym:: ptr_null || diag_item == sym:: ptr_null_mut) =>
102
125
{
103
- cx. emit_spanned_lint ( INCORRECT_FN_NULL_CHECKS , expr. span , FnNullCheckDiag )
126
+ let diag = NonNullCheckDiag { ty_desc : ty. prefix_string ( cx. tcx ) } ;
127
+ cx. emit_spanned_lint ( INCORRECT_NON_NULL_CHECKS , expr. span , diag)
104
128
} ,
105
129
106
130
_ => { } ,
0 commit comments