1 use rustc::hir::{Expr, ExprMethodCall, ExprLit};
3 use syntax::ast::LitKind;
4 use syntax::codemap::{Span, Spanned};
5 use utils::{match_type, paths, span_lint, walk_ptrs_ty_depth};
7 /// **What it does:** This lint checks for duplicate open options as well as combinations that make no sense.
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.
11 /// **Known problems:** None
13 /// **Example:** `OpenOptions::new().read(true).truncate(true)`
15 pub NONSENSICAL_OPEN_OPTIONS,
17 "nonsensical combination of options for opening a file"
22 pub struct NonSensicalOpenOptions;
24 impl LintPass for NonSensicalOpenOptions {
25 fn get_lints(&self) -> LintArray {
26 lint_array!(NONSENSICAL_OPEN_OPTIONS)
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);
43 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
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]));
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 {
66 let argument_option = match arguments[1].node {
67 ExprLit(ref span) => {
68 if let Spanned { node: LitKind::Bool(lit), .. } = **span {
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.
80 _ => Argument::Unknown,
83 match &*name.node.as_str() {
85 options.push((OpenOption::Create, argument_option));
88 options.push((OpenOption::Append, argument_option));
91 options.push((OpenOption::Truncate, argument_option));
94 options.push((OpenOption::Read, argument_option));
97 options.push((OpenOption::Write, argument_option));
102 get_open_options(cx, &arguments[0], options);
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,
114 // This code is almost duplicated (oh, the irony), but I haven't found a way to unify it.
116 for option in options {
118 (OpenOption::Create, arg) => {
121 NONSENSICAL_OPEN_OPTIONS,
123 "the method \"create\" is called more than once");
127 create_arg = create_arg || (arg == Argument::True);;
129 (OpenOption::Append, arg) => {
132 NONSENSICAL_OPEN_OPTIONS,
134 "the method \"append\" is called more than once");
138 append_arg = append_arg || (arg == Argument::True);;
140 (OpenOption::Truncate, arg) => {
143 NONSENSICAL_OPEN_OPTIONS,
145 "the method \"truncate\" is called more than once");
149 truncate_arg = truncate_arg || (arg == Argument::True);
151 (OpenOption::Read, arg) => {
154 NONSENSICAL_OPEN_OPTIONS,
156 "the method \"read\" is called more than once");
160 read_arg = read_arg || (arg == Argument::True);;
162 (OpenOption::Write, arg) => {
165 NONSENSICAL_OPEN_OPTIONS,
167 "the method \"write\" is called more than once");
171 write_arg = write_arg || (arg == Argument::True);;
176 if read && truncate && read_arg && truncate_arg {
177 span_lint(cx, NONSENSICAL_OPEN_OPTIONS, span, "file opened with \"truncate\" and \"read\"");
179 if append && truncate && append_arg && truncate_arg {
181 NONSENSICAL_OPEN_OPTIONS,
183 "file opened with \"append\" and \"truncate\"");