]> git.lizzy.rs Git - rust.git/blob - src/open_options.rs
Support either rustup or multirust environment variables
[rust.git] / src / open_options.rs
1 use rustc::hir::{Expr, ExprMethodCall, ExprLit};
2 use rustc::lint::*;
3 use syntax::ast::LitKind;
4 use syntax::codemap::{Span, Spanned};
5 use utils::{match_type, paths, span_lint, walk_ptrs_ty_depth};
6
7 /// **What it does:** This lint checks for duplicate open options as well as combinations that make no sense.
8 ///
9 /// **Why is this bad?** In the best case, the code will be harder to read than necessary. I don't know the worst case.
10 ///
11 /// **Known problems:** None
12 ///
13 /// **Example:** `OpenOptions::new().read(true).truncate(true)`
14 declare_lint! {
15     pub NONSENSICAL_OPEN_OPTIONS,
16     Warn,
17     "nonsensical combination of options for opening a file"
18 }
19
20
21 #[derive(Copy,Clone)]
22 pub struct NonSensicalOpenOptions;
23
24 impl LintPass for NonSensicalOpenOptions {
25     fn get_lints(&self) -> LintArray {
26         lint_array!(NONSENSICAL_OPEN_OPTIONS)
27     }
28 }
29
30 impl LateLintPass for NonSensicalOpenOptions {
31     fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
32         if let ExprMethodCall(ref name, _, ref arguments) = e.node {
33             let (obj_ty, _) = walk_ptrs_ty_depth(cx.tcx.expr_ty(&arguments[0]));
34             if name.node.as_str() == "open" && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
35                 let mut options = Vec::new();
36                 get_open_options(cx, &arguments[0], &mut options);
37                 check_open_options(cx, &options, e.span);
38             }
39         }
40     }
41 }
42
43 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
44 enum Argument {
45     True,
46     False,
47     Unknown,
48 }
49
50 #[derive(Debug)]
51 enum OpenOption {
52     Write,
53     Read,
54     Truncate,
55     Create,
56     Append,
57 }
58
59 fn get_open_options(cx: &LateContext, argument: &Expr, options: &mut Vec<(OpenOption, Argument)>) {
60     if let ExprMethodCall(ref name, _, ref arguments) = argument.node {
61         let (obj_ty, _) = walk_ptrs_ty_depth(cx.tcx.expr_ty(&arguments[0]));
62
63         // Only proceed if this is a call on some object of type std::fs::OpenOptions
64         if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
65
66             let argument_option = match arguments[1].node {
67                 ExprLit(ref span) => {
68                     if let Spanned { node: LitKind::Bool(lit), .. } = **span {
69                         if lit {
70                             Argument::True
71                         } else {
72                             Argument::False
73                         }
74                     } else {
75                         return; // The function is called with a literal
76                                 // which is not a boolean literal. This is theoretically
77                                 // possible, but not very likely.
78                     }
79                 }
80                 _ => Argument::Unknown,
81             };
82
83             match &*name.node.as_str() {
84                 "create" => {
85                     options.push((OpenOption::Create, argument_option));
86                 }
87                 "append" => {
88                     options.push((OpenOption::Append, argument_option));
89                 }
90                 "truncate" => {
91                     options.push((OpenOption::Truncate, argument_option));
92                 }
93                 "read" => {
94                     options.push((OpenOption::Read, argument_option));
95                 }
96                 "write" => {
97                     options.push((OpenOption::Write, argument_option));
98                 }
99                 _ => (),
100             }
101
102             get_open_options(cx, &arguments[0], options);
103         }
104     }
105 }
106
107 fn check_open_options(cx: &LateContext, options: &[(OpenOption, Argument)], span: Span) {
108     let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
109     let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) = (false,
110                                                                                            false,
111                                                                                            false,
112                                                                                            false,
113                                                                                            false);
114     // This code is almost duplicated (oh, the irony), but I haven't found a way to unify it.
115
116     for option in options {
117         match *option {
118             (OpenOption::Create, arg) => {
119                 if create {
120                     span_lint(cx,
121                               NONSENSICAL_OPEN_OPTIONS,
122                               span,
123                               "the method \"create\" is called more than once");
124                 } else {
125                     create = true
126                 }
127                 create_arg = create_arg || (arg == Argument::True);;
128             }
129             (OpenOption::Append, arg) => {
130                 if append {
131                     span_lint(cx,
132                               NONSENSICAL_OPEN_OPTIONS,
133                               span,
134                               "the method \"append\" is called more than once");
135                 } else {
136                     append = true
137                 }
138                 append_arg = append_arg || (arg == Argument::True);;
139             }
140             (OpenOption::Truncate, arg) => {
141                 if truncate {
142                     span_lint(cx,
143                               NONSENSICAL_OPEN_OPTIONS,
144                               span,
145                               "the method \"truncate\" is called more than once");
146                 } else {
147                     truncate = true
148                 }
149                 truncate_arg = truncate_arg || (arg == Argument::True);
150             }
151             (OpenOption::Read, arg) => {
152                 if read {
153                     span_lint(cx,
154                               NONSENSICAL_OPEN_OPTIONS,
155                               span,
156                               "the method \"read\" is called more than once");
157                 } else {
158                     read = true
159                 }
160                 read_arg = read_arg || (arg == Argument::True);;
161             }
162             (OpenOption::Write, arg) => {
163                 if write {
164                     span_lint(cx,
165                               NONSENSICAL_OPEN_OPTIONS,
166                               span,
167                               "the method \"write\" is called more than once");
168                 } else {
169                     write = true
170                 }
171                 write_arg = write_arg || (arg == Argument::True);;
172             }
173         }
174     }
175
176     if read && truncate && read_arg && truncate_arg {
177         span_lint(cx, NONSENSICAL_OPEN_OPTIONS, span, "file opened with \"truncate\" and \"read\"");
178     }
179     if append && truncate && append_arg && truncate_arg {
180         span_lint(cx,
181                   NONSENSICAL_OPEN_OPTIONS,
182                   span,
183                   "file opened with \"append\" and \"truncate\"");
184     }
185 }