1
1
use rustc_data_structures:: fx:: FxHashMap ;
2
2
3
- use clippy_utils:: diagnostics:: span_lint;
3
+ use clippy_utils:: diagnostics:: { span_lint, span_lint_and_then } ;
4
4
use clippy_utils:: paths;
5
5
use clippy_utils:: ty:: match_type;
6
6
use rustc_ast:: ast:: LitKind ;
@@ -13,7 +13,11 @@ use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS};
13
13
pub ( super ) fn check < ' tcx > ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > , recv : & ' tcx Expr < ' _ > ) {
14
14
if let Some ( method_id) = cx. typeck_results ( ) . type_dependent_def_id ( e. hir_id )
15
15
&& let Some ( impl_id) = cx. tcx . impl_of_method ( method_id)
16
- && match_type ( cx, cx. tcx . type_of ( impl_id) . instantiate_identity ( ) , & paths:: OPEN_OPTIONS )
16
+ && (
17
+ match_type ( cx, cx. tcx . type_of ( impl_id) . instantiate_identity ( ) , & paths:: OPEN_OPTIONS ) ||
18
+ match_type ( cx, cx. tcx . type_of ( impl_id) . instantiate_identity ( ) , & paths:: TOKIO_IO_OPEN_OPTIONS )
19
+ )
20
+
17
21
{
18
22
let mut options = Vec :: new ( ) ;
19
23
get_open_options ( cx, recv, & mut options) ;
@@ -49,12 +53,15 @@ impl std::fmt::Display for OpenOption {
49
53
}
50
54
}
51
55
52
- fn get_open_options ( cx : & LateContext < ' _ > , argument : & Expr < ' _ > , options : & mut Vec < ( OpenOption , Argument ) > ) {
53
- if let ExprKind :: MethodCall ( path, receiver, arguments, _ ) = argument. kind {
56
+ fn get_open_options ( cx : & LateContext < ' _ > , argument : & Expr < ' _ > , options : & mut Vec < ( OpenOption , Argument , Span ) > ) {
57
+ if let ExprKind :: MethodCall ( path, receiver, arguments, span ) = argument. kind {
54
58
let obj_ty = cx. typeck_results ( ) . expr_ty ( receiver) . peel_refs ( ) ;
55
59
56
- // Only proceed if this is a call on some object of type std::fs::OpenOptions
57
- if match_type ( cx, obj_ty, & paths:: OPEN_OPTIONS ) && !arguments. is_empty ( ) {
60
+ // Only proceed if this is a call on some object of type std::fs::OpenOptions or
61
+ // tokio::fs::OpenOptions
62
+ if !arguments. is_empty ( )
63
+ && ( match_type ( cx, obj_ty, & paths:: OPEN_OPTIONS ) || match_type ( cx, obj_ty, & paths:: TOKIO_IO_OPEN_OPTIONS ) )
64
+ {
58
65
let argument_option = match arguments[ 0 ] . kind {
59
66
ExprKind :: Lit ( span) => {
60
67
if let Spanned {
@@ -74,22 +81,22 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
74
81
75
82
match path. ident . as_str ( ) {
76
83
"create" => {
77
- options. push ( ( OpenOption :: Create , argument_option) ) ;
84
+ options. push ( ( OpenOption :: Create , argument_option, span ) ) ;
78
85
} ,
79
86
"create_new" => {
80
- options. push ( ( OpenOption :: CreateNew , argument_option) ) ;
87
+ options. push ( ( OpenOption :: CreateNew , argument_option, span ) ) ;
81
88
} ,
82
89
"append" => {
83
- options. push ( ( OpenOption :: Append , argument_option) ) ;
90
+ options. push ( ( OpenOption :: Append , argument_option, span ) ) ;
84
91
} ,
85
92
"truncate" => {
86
- options. push ( ( OpenOption :: Truncate , argument_option) ) ;
93
+ options. push ( ( OpenOption :: Truncate , argument_option, span ) ) ;
87
94
} ,
88
95
"read" => {
89
- options. push ( ( OpenOption :: Read , argument_option) ) ;
96
+ options. push ( ( OpenOption :: Read , argument_option, span ) ) ;
90
97
} ,
91
98
"write" => {
92
- options. push ( ( OpenOption :: Write , argument_option) ) ;
99
+ options. push ( ( OpenOption :: Write , argument_option, span ) ) ;
93
100
} ,
94
101
_ => ( ) ,
95
102
}
@@ -99,24 +106,25 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
99
106
}
100
107
}
101
108
102
- fn check_open_options ( cx : & LateContext < ' _ > , settings : & [ ( OpenOption , Argument ) ] , span : Span ) {
109
+ fn check_open_options ( cx : & LateContext < ' _ > , settings : & [ ( OpenOption , Argument , Span ) ] , span : Span ) {
103
110
// The args passed to these methods, if they have been called
104
111
let mut options = FxHashMap :: default ( ) ;
105
- for ( option, arg) in settings {
106
- if options. insert ( option. clone ( ) , arg. clone ( ) ) . is_some ( ) {
112
+ for ( option, arg, sp ) in settings {
113
+ if let Some ( ( _ , prev_span ) ) = options. insert ( option. clone ( ) , ( arg. clone ( ) , * sp ) ) {
107
114
span_lint (
108
115
cx,
109
116
NONSENSICAL_OPEN_OPTIONS ,
110
- span ,
117
+ prev_span ,
111
118
& format ! ( "the method `{}` is called more than once" , & option) ,
112
119
) ;
113
120
}
114
121
}
115
122
116
- if let ( Some ( Argument :: Set ( true ) ) , Some ( Argument :: Set ( true ) ) ) =
117
- ( options. get ( & OpenOption :: Read ) , options. get ( & OpenOption :: Truncate ) )
118
- {
119
- if options. get ( & OpenOption :: Write ) . unwrap_or ( & Argument :: Set ( false ) ) == & Argument :: Set ( false ) {
123
+ if_chain ! {
124
+ if let Some ( ( Argument :: Set ( true ) , _) ) = options. get( & OpenOption :: Read ) ;
125
+ if let Some ( ( Argument :: Set ( true ) , _) ) = options. get( & OpenOption :: Truncate ) ;
126
+ if let None | Some ( ( Argument :: Set ( false ) , _) ) = options. get( & OpenOption :: Write ) ;
127
+ then {
120
128
span_lint(
121
129
cx,
122
130
NONSENSICAL_OPEN_OPTIONS ,
@@ -126,10 +134,11 @@ fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument)],
126
134
}
127
135
}
128
136
129
- if let ( Some ( Argument :: Set ( true ) ) , Some ( Argument :: Set ( true ) ) ) =
130
- ( options. get ( & OpenOption :: Append ) , options. get ( & OpenOption :: Truncate ) )
131
- {
132
- if options. get ( & OpenOption :: Write ) . unwrap_or ( & Argument :: Set ( false ) ) == & Argument :: Set ( false ) {
137
+ if_chain ! {
138
+ if let Some ( ( Argument :: Set ( true ) , _) ) = options. get( & OpenOption :: Append ) ;
139
+ if let Some ( ( Argument :: Set ( true ) , _) ) = options. get( & OpenOption :: Truncate ) ;
140
+ if let None | Some ( ( Argument :: Set ( false ) , _) ) = options. get( & OpenOption :: Write ) ;
141
+ then {
133
142
span_lint(
134
143
cx,
135
144
NONSENSICAL_OPEN_OPTIONS ,
@@ -139,12 +148,21 @@ fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument)],
139
148
}
140
149
}
141
150
142
- if let ( Some ( Argument :: Set ( true ) ) , None ) = ( options. get ( & OpenOption :: Create ) , options. get ( & OpenOption :: Truncate ) ) {
143
- span_lint (
144
- cx,
145
- SUSPICIOUS_OPEN_OPTIONS ,
146
- span,
147
- "file opened with `create`, but `truncate` behavior not defined" ,
148
- ) ;
151
+ if_chain ! {
152
+ if let Some ( ( Argument :: Set ( true ) , create_span) ) = options. get( & OpenOption :: Create ) ;
153
+ if let None = options. get( & OpenOption :: Truncate ) ;
154
+ then {
155
+ span_lint_and_then(
156
+ cx,
157
+ SUSPICIOUS_OPEN_OPTIONS ,
158
+ * create_span,
159
+ "file opened with `create`, but `truncate` behavior not defined" ,
160
+ |diag| {
161
+ diag
162
+ //.span_suggestion(create_span.shrink_to_hi(), "add", ".truncate(true)".to_string(), rustc_errors::Applicability::MaybeIncorrect)
163
+ . help( "if you intend to overwrite an existing file entirely, call `.truncate(true)`. if you instead know that you may want to keep some parts of the old file, call `.truncate(false)`" ) ;
164
+ } ,
165
+ ) ;
166
+ }
149
167
}
150
168
}
0 commit comments