1+ use rustc_data_structures:: fx:: FxHashMap ;
2+
13use clippy_utils:: diagnostics:: span_lint;
24use clippy_utils:: ty:: is_type_diagnostic_item;
35use rustc_ast:: ast:: LitKind ;
@@ -6,7 +8,7 @@ use rustc_lint::LateContext;
68use rustc_span:: source_map:: Spanned ;
79use rustc_span:: { sym, Span } ;
810
9- use super :: NONSENSICAL_OPEN_OPTIONS ;
11+ use super :: { NONSENSICAL_OPEN_OPTIONS , SUSPICIOUS_OPEN_OPTIONS } ;
1012
1113pub ( super ) fn check < ' tcx > ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > , recv : & ' tcx Expr < ' _ > ) {
1214 if let Some ( method_id) = cx. typeck_results ( ) . type_dependent_def_id ( e. hir_id )
@@ -19,20 +21,32 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx
1921 }
2022}
2123
22- #[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
24+ #[ derive( Eq , PartialEq , Clone ) ]
2325enum Argument {
24- True ,
25- False ,
26+ Set ( bool ) ,
2627 Unknown ,
2728}
2829
29- #[ derive( Debug ) ]
30+ #[ derive( Debug , Eq , PartialEq , Hash , Clone ) ]
3031enum OpenOption {
31- Write ,
32+ Append ,
33+ Create ,
34+ CreateNew ,
3235 Read ,
3336 Truncate ,
34- Create ,
35- Append ,
37+ Write ,
38+ }
39+ impl std:: fmt:: Display for OpenOption {
40+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
41+ match self {
42+ OpenOption :: Append => write ! ( f, "append" ) ,
43+ OpenOption :: Create => write ! ( f, "create" ) ,
44+ OpenOption :: CreateNew => write ! ( f, "create_new" ) ,
45+ OpenOption :: Read => write ! ( f, "read" ) ,
46+ OpenOption :: Truncate => write ! ( f, "truncate" ) ,
47+ OpenOption :: Write => write ! ( f, "write" ) ,
48+ }
49+ }
3650}
3751
3852fn get_open_options ( cx : & LateContext < ' _ > , argument : & Expr < ' _ > , options : & mut Vec < ( OpenOption , Argument ) > ) {
@@ -48,7 +62,7 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
4862 ..
4963 } = span
5064 {
51- if * lit { Argument :: True } else { Argument :: False }
65+ Argument :: Set ( * lit )
5266 } else {
5367 // The function is called with a literal which is not a boolean literal.
5468 // This is theoretically possible, but not very likely.
@@ -62,6 +76,9 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
6276 "create" => {
6377 options. push ( ( OpenOption :: Create , argument_option) ) ;
6478 } ,
79+ "create_new" => {
80+ options. push ( ( OpenOption :: CreateNew , argument_option) ) ;
81+ } ,
6582 "append" => {
6683 options. push ( ( OpenOption :: Append , argument_option) ) ;
6784 } ,
@@ -82,97 +99,52 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
8299 }
83100}
84101
85- fn check_open_options ( cx : & LateContext < ' _ > , options : & [ ( OpenOption , Argument ) ] , span : Span ) {
86- let ( mut create, mut append, mut truncate, mut read, mut write) = ( false , false , false , false , false ) ;
87- let ( mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
88- ( false , false , false , false , false ) ;
89- // This code is almost duplicated (oh, the irony), but I haven't found a way to
90- // unify it.
102+ fn check_open_options ( cx : & LateContext < ' _ > , settings : & [ ( OpenOption , Argument ) ] , span : Span ) {
103+ // The args passed to these methods, if they have been called
104+ let mut options = FxHashMap :: default ( ) ;
105+ for ( option, arg) in settings {
106+ if options. insert ( option. clone ( ) , arg. clone ( ) ) . is_some ( ) {
107+ span_lint (
108+ cx,
109+ NONSENSICAL_OPEN_OPTIONS ,
110+ span,
111+ & format ! ( "the method `{}` is called more than once" , & option) ,
112+ ) ;
113+ }
114+ }
91115
92- for option in options {
93- match * option {
94- ( OpenOption :: Create , arg) => {
95- if create {
96- span_lint (
97- cx,
98- NONSENSICAL_OPEN_OPTIONS ,
99- span,
100- "the method `create` is called more than once" ,
101- ) ;
102- } else {
103- create = true ;
104- }
105- create_arg = create_arg || ( arg == Argument :: True ) ;
106- } ,
107- ( OpenOption :: Append , arg) => {
108- if append {
109- span_lint (
110- cx,
111- NONSENSICAL_OPEN_OPTIONS ,
112- span,
113- "the method `append` is called more than once" ,
114- ) ;
115- } else {
116- append = true ;
117- }
118- append_arg = append_arg || ( arg == Argument :: True ) ;
119- } ,
120- ( OpenOption :: Truncate , arg) => {
121- if truncate {
122- span_lint (
123- cx,
124- NONSENSICAL_OPEN_OPTIONS ,
125- span,
126- "the method `truncate` is called more than once" ,
127- ) ;
128- } else {
129- truncate = true ;
130- }
131- truncate_arg = truncate_arg || ( arg == Argument :: True ) ;
132- } ,
133- ( OpenOption :: Read , arg) => {
134- if read {
135- span_lint (
136- cx,
137- NONSENSICAL_OPEN_OPTIONS ,
138- span,
139- "the method `read` is called more than once" ,
140- ) ;
141- } else {
142- read = true ;
143- }
144- read_arg = read_arg || ( arg == Argument :: True ) ;
145- } ,
146- ( OpenOption :: Write , arg) => {
147- if write {
148- span_lint (
149- cx,
150- NONSENSICAL_OPEN_OPTIONS ,
151- span,
152- "the method `write` is called more than once" ,
153- ) ;
154- } else {
155- write = true ;
156- }
157- write_arg = write_arg || ( arg == Argument :: True ) ;
158- } ,
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+ span_lint (
121+ cx,
122+ NONSENSICAL_OPEN_OPTIONS ,
123+ span,
124+ "file opened with `truncate` and `read`" ,
125+ ) ;
159126 }
160127 }
161128
162- if read && truncate && read_arg && truncate_arg && !( write && write_arg) {
163- span_lint (
164- cx,
165- NONSENSICAL_OPEN_OPTIONS ,
166- span,
167- "file opened with `truncate` and `read`" ,
168- ) ;
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 ) {
133+ span_lint (
134+ cx,
135+ NONSENSICAL_OPEN_OPTIONS ,
136+ span,
137+ "file opened with `append` and `truncate`" ,
138+ ) ;
139+ }
169140 }
170- if append && truncate && append_arg && truncate_arg {
141+
142+ if let ( Some ( Argument :: Set ( true ) ) , None ) = ( options. get ( & OpenOption :: Create ) , options. get ( & OpenOption :: Truncate ) ) {
171143 span_lint (
172144 cx,
173- NONSENSICAL_OPEN_OPTIONS ,
145+ SUSPICIOUS_OPEN_OPTIONS ,
174146 span,
175- "file opened with `append` and `truncate`" ,
147+ "file opened with `create`, but `truncate` behavior not defined " ,
176148 ) ;
177149 }
178150}
0 commit comments