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:: ty:: is_type_diagnostic_item;
5
5
use rustc_ast:: ast:: LitKind ;
6
6
use rustc_hir:: { Expr , ExprKind } ;
@@ -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
- && is_type_diagnostic_item ( cx, cx. tcx . type_of ( impl_id) . instantiate_identity ( ) , sym:: FsOpenOptions )
16
+ && (
17
+ is_type_diagnostic_item ( cx, cx. tcx . type_of ( impl_id) . instantiate_identity ( ) , sym:: FsOpenOptions ) ||
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,12 @@ 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
60
// Only proceed if this is a call on some object of type std::fs::OpenOptions
57
- if is_type_diagnostic_item ( cx, obj_ty, sym:: FsOpenOptions ) && !arguments . is_empty ( ) {
61
+ if !arguments . is_empty ( ) && ( is_type_diagnostic_item ( cx, obj_ty, sym:: FsOpenOptions ) ) {
58
62
let argument_option = match arguments[ 0 ] . kind {
59
63
ExprKind :: Lit ( span) => {
60
64
if let Spanned {
@@ -74,22 +78,22 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
74
78
75
79
match path. ident . as_str ( ) {
76
80
"create" => {
77
- options. push ( ( OpenOption :: Create , argument_option) ) ;
81
+ options. push ( ( OpenOption :: Create , argument_option, span ) ) ;
78
82
} ,
79
83
"create_new" => {
80
- options. push ( ( OpenOption :: CreateNew , argument_option) ) ;
84
+ options. push ( ( OpenOption :: CreateNew , argument_option, span ) ) ;
81
85
} ,
82
86
"append" => {
83
- options. push ( ( OpenOption :: Append , argument_option) ) ;
87
+ options. push ( ( OpenOption :: Append , argument_option, span ) ) ;
84
88
} ,
85
89
"truncate" => {
86
- options. push ( ( OpenOption :: Truncate , argument_option) ) ;
90
+ options. push ( ( OpenOption :: Truncate , argument_option, span ) ) ;
87
91
} ,
88
92
"read" => {
89
- options. push ( ( OpenOption :: Read , argument_option) ) ;
93
+ options. push ( ( OpenOption :: Read , argument_option, span ) ) ;
90
94
} ,
91
95
"write" => {
92
- options. push ( ( OpenOption :: Write , argument_option) ) ;
96
+ options. push ( ( OpenOption :: Write , argument_option, span ) ) ;
93
97
} ,
94
98
_ => ( ) ,
95
99
}
@@ -99,24 +103,25 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
99
103
}
100
104
}
101
105
102
- fn check_open_options ( cx : & LateContext < ' _ > , settings : & [ ( OpenOption , Argument ) ] , span : Span ) {
106
+ fn check_open_options ( cx : & LateContext < ' _ > , settings : & [ ( OpenOption , Argument , Span ) ] , span : Span ) {
103
107
// The args passed to these methods, if they have been called
104
108
let mut options = FxHashMap :: default ( ) ;
105
- for ( option, arg) in settings {
106
- if options. insert ( option. clone ( ) , arg. clone ( ) ) . is_some ( ) {
109
+ for ( option, arg, sp ) in settings {
110
+ if let Some ( ( _ , prev_span ) ) = options. insert ( option. clone ( ) , ( arg. clone ( ) , * sp ) ) {
107
111
span_lint (
108
112
cx,
109
113
NONSENSICAL_OPEN_OPTIONS ,
110
- span ,
114
+ prev_span ,
111
115
& format ! ( "the method `{}` is called more than once" , & option) ,
112
116
) ;
113
117
}
114
118
}
115
119
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 ) {
120
+ if_chain ! {
121
+ if let Some ( ( Argument :: Set ( true ) , _) ) = options. get( & OpenOption :: Read ) ;
122
+ if let Some ( ( Argument :: Set ( true ) , _) ) = options. get( & OpenOption :: Truncate ) ;
123
+ if let None | Some ( ( Argument :: Set ( false ) , _) ) = options. get( & OpenOption :: Write ) ;
124
+ then {
120
125
span_lint(
121
126
cx,
122
127
NONSENSICAL_OPEN_OPTIONS ,
@@ -126,10 +131,11 @@ fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument)],
126
131
}
127
132
}
128
133
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 ) {
134
+ if_chain ! {
135
+ if let Some ( ( Argument :: Set ( true ) , _) ) = options. get( & OpenOption :: Append ) ;
136
+ if let Some ( ( Argument :: Set ( true ) , _) ) = options. get( & OpenOption :: Truncate ) ;
137
+ if let None | Some ( ( Argument :: Set ( false ) , _) ) = options. get( & OpenOption :: Write ) ;
138
+ then {
133
139
span_lint(
134
140
cx,
135
141
NONSENSICAL_OPEN_OPTIONS ,
@@ -139,12 +145,21 @@ fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument)],
139
145
}
140
146
}
141
147
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
- ) ;
148
+ if_chain ! {
149
+ if let Some ( ( Argument :: Set ( true ) , create_span) ) = options. get( & OpenOption :: Create ) ;
150
+ if let None = options. get( & OpenOption :: Truncate ) ;
151
+ then {
152
+ span_lint_and_then(
153
+ cx,
154
+ SUSPICIOUS_OPEN_OPTIONS ,
155
+ * create_span,
156
+ "file opened with `create`, but `truncate` behavior not defined" ,
157
+ |diag| {
158
+ diag
159
+ //.span_suggestion(create_span.shrink_to_hi(), "add", ".truncate(true)".to_string(), rustc_errors::Applicability::MaybeIncorrect)
160
+ . 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)`" ) ;
161
+ } ,
162
+ ) ;
163
+ }
149
164
}
150
165
}
0 commit comments