11use rustc_data_structures:: fx:: FxHashMap ;
22
3- use clippy_utils:: diagnostics:: span_lint;
3+ use clippy_utils:: diagnostics:: { span_lint, span_lint_and_then } ;
44use clippy_utils:: ty:: is_type_diagnostic_item;
55use rustc_ast:: ast:: LitKind ;
66use rustc_hir:: { Expr , ExprKind } ;
@@ -13,7 +13,11 @@ use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS};
1313pub ( super ) fn check < ' tcx > ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > , recv : & ' tcx Expr < ' _ > ) {
1414 if let Some ( method_id) = cx. typeck_results ( ) . type_dependent_def_id ( e. hir_id )
1515 && 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+
1721 {
1822 let mut options = Vec :: new ( ) ;
1923 get_open_options ( cx, recv, & mut options) ;
@@ -49,12 +53,12 @@ impl std::fmt::Display for OpenOption {
4953 }
5054}
5155
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 {
5458 let obj_ty = cx. typeck_results ( ) . expr_ty ( receiver) . peel_refs ( ) ;
5559
5660 // 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 ) ) {
5862 let argument_option = match arguments[ 0 ] . kind {
5963 ExprKind :: Lit ( span) => {
6064 if let Spanned {
@@ -74,22 +78,22 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
7478
7579 match path. ident . as_str ( ) {
7680 "create" => {
77- options. push ( ( OpenOption :: Create , argument_option) ) ;
81+ options. push ( ( OpenOption :: Create , argument_option, span ) ) ;
7882 } ,
7983 "create_new" => {
80- options. push ( ( OpenOption :: CreateNew , argument_option) ) ;
84+ options. push ( ( OpenOption :: CreateNew , argument_option, span ) ) ;
8185 } ,
8286 "append" => {
83- options. push ( ( OpenOption :: Append , argument_option) ) ;
87+ options. push ( ( OpenOption :: Append , argument_option, span ) ) ;
8488 } ,
8589 "truncate" => {
86- options. push ( ( OpenOption :: Truncate , argument_option) ) ;
90+ options. push ( ( OpenOption :: Truncate , argument_option, span ) ) ;
8791 } ,
8892 "read" => {
89- options. push ( ( OpenOption :: Read , argument_option) ) ;
93+ options. push ( ( OpenOption :: Read , argument_option, span ) ) ;
9094 } ,
9195 "write" => {
92- options. push ( ( OpenOption :: Write , argument_option) ) ;
96+ options. push ( ( OpenOption :: Write , argument_option, span ) ) ;
9397 } ,
9498 _ => ( ) ,
9599 }
@@ -99,24 +103,25 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
99103 }
100104}
101105
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 ) {
103107 // The args passed to these methods, if they have been called
104108 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 ) ) {
107111 span_lint (
108112 cx,
109113 NONSENSICAL_OPEN_OPTIONS ,
110- span ,
114+ prev_span ,
111115 & format ! ( "the method `{}` is called more than once" , & option) ,
112116 ) ;
113117 }
114118 }
115119
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 {
120125 span_lint(
121126 cx,
122127 NONSENSICAL_OPEN_OPTIONS ,
@@ -126,10 +131,11 @@ fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument)],
126131 }
127132 }
128133
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 {
133139 span_lint(
134140 cx,
135141 NONSENSICAL_OPEN_OPTIONS ,
@@ -139,12 +145,21 @@ fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument)],
139145 }
140146 }
141147
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+ }
149164 }
150165}
0 commit comments