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 ;
@@ -49,8 +49,8 @@ impl std::fmt::Display for OpenOption {
49
49
}
50
50
}
51
51
52
- fn get_open_options ( cx : & LateContext < ' _ > , argument : & Expr < ' _ > , options : & mut Vec < ( OpenOption , Argument ) > ) {
53
- if let ExprKind :: MethodCall ( path, receiver, arguments, _ ) = argument. kind {
52
+ fn get_open_options ( cx : & LateContext < ' _ > , argument : & Expr < ' _ > , options : & mut Vec < ( OpenOption , Argument , Span ) > ) {
53
+ if let ExprKind :: MethodCall ( path, receiver, arguments, span ) = argument. kind {
54
54
let obj_ty = cx. typeck_results ( ) . expr_ty ( receiver) . peel_refs ( ) ;
55
55
56
56
// Only proceed if this is a call on some object of type std::fs::OpenOptions
@@ -74,22 +74,22 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
74
74
75
75
match path. ident . as_str ( ) {
76
76
"create" => {
77
- options. push ( ( OpenOption :: Create , argument_option) ) ;
77
+ options. push ( ( OpenOption :: Create , argument_option, span ) ) ;
78
78
} ,
79
79
"create_new" => {
80
- options. push ( ( OpenOption :: CreateNew , argument_option) ) ;
80
+ options. push ( ( OpenOption :: CreateNew , argument_option, span ) ) ;
81
81
} ,
82
82
"append" => {
83
- options. push ( ( OpenOption :: Append , argument_option) ) ;
83
+ options. push ( ( OpenOption :: Append , argument_option, span ) ) ;
84
84
} ,
85
85
"truncate" => {
86
- options. push ( ( OpenOption :: Truncate , argument_option) ) ;
86
+ options. push ( ( OpenOption :: Truncate , argument_option, span ) ) ;
87
87
} ,
88
88
"read" => {
89
- options. push ( ( OpenOption :: Read , argument_option) ) ;
89
+ options. push ( ( OpenOption :: Read , argument_option, span ) ) ;
90
90
} ,
91
91
"write" => {
92
- options. push ( ( OpenOption :: Write , argument_option) ) ;
92
+ options. push ( ( OpenOption :: Write , argument_option, span ) ) ;
93
93
} ,
94
94
_ => ( ) ,
95
95
}
@@ -99,24 +99,25 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
99
99
}
100
100
}
101
101
102
- fn check_open_options ( cx : & LateContext < ' _ > , settings : & [ ( OpenOption , Argument ) ] , span : Span ) {
102
+ fn check_open_options ( cx : & LateContext < ' _ > , settings : & [ ( OpenOption , Argument , Span ) ] , span : Span ) {
103
103
// The args passed to these methods, if they have been called
104
104
let mut options = FxHashMap :: default ( ) ;
105
- for ( option, arg) in settings {
106
- if options. insert ( option. clone ( ) , arg. clone ( ) ) . is_some ( ) {
105
+ for ( option, arg, sp ) in settings {
106
+ if let Some ( ( _ , prev_span ) ) = options. insert ( option. clone ( ) , ( arg. clone ( ) , sp . clone ( ) ) ) {
107
107
span_lint (
108
108
cx,
109
109
NONSENSICAL_OPEN_OPTIONS ,
110
- span ,
110
+ prev_span ,
111
111
& format ! ( "the method `{}` is called more than once" , & option) ,
112
112
) ;
113
113
}
114
114
}
115
115
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 ) {
116
+ if_chain ! {
117
+ if let Some ( ( Argument :: Set ( true ) , _) ) = options. get( & OpenOption :: Read ) ;
118
+ if let Some ( ( Argument :: Set ( true ) , _) ) = options. get( & OpenOption :: Truncate ) ;
119
+ if let None | Some ( ( Argument :: Set ( false ) , _) ) = options. get( & OpenOption :: Write ) ;
120
+ then {
120
121
span_lint(
121
122
cx,
122
123
NONSENSICAL_OPEN_OPTIONS ,
@@ -126,10 +127,11 @@ fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument)],
126
127
}
127
128
}
128
129
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 ) {
130
+ if_chain ! {
131
+ if let Some ( ( Argument :: Set ( true ) , _) ) = options. get( & OpenOption :: Append ) ;
132
+ if let Some ( ( Argument :: Set ( true ) , _) ) = options. get( & OpenOption :: Truncate ) ;
133
+ if let None | Some ( ( Argument :: Set ( false ) , _) ) = options. get( & OpenOption :: Write ) ;
134
+ then {
133
135
span_lint(
134
136
cx,
135
137
NONSENSICAL_OPEN_OPTIONS ,
@@ -139,12 +141,21 @@ fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument)],
139
141
}
140
142
}
141
143
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
- ) ;
144
+ if_chain ! {
145
+ if let Some ( ( Argument :: Set ( true ) , create_span) ) = options. get( & OpenOption :: Create ) ;
146
+ if let None = options. get( & OpenOption :: Truncate ) ;
147
+ then {
148
+ span_lint_and_then(
149
+ cx,
150
+ SUSPICIOUS_OPEN_OPTIONS ,
151
+ * create_span,
152
+ "file opened with `create`, but `truncate` behavior not defined" ,
153
+ |diag| {
154
+ diag
155
+ //.span_suggestion(create_span.shrink_to_hi(), "add", ".truncate(true)".to_string(), rustc_errors::Applicability::MaybeIncorrect)
156
+ . 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)`" ) ;
157
+ } ,
158
+ ) ;
159
+ }
149
160
}
150
161
}
0 commit comments