1 // Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
11 use crate::rustc::hir::{Expr, ExprKind};
12 use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
13 use crate::rustc::{declare_tool_lint, lint_array};
14 use crate::syntax::ast::LitKind;
15 use crate::syntax::source_map::{Span, Spanned};
16 use crate::utils::{match_type, paths, span_lint, walk_ptrs_ty};
18 /// **What it does:** Checks for duplicate open options as well as combinations
19 /// that make no sense.
21 /// **Why is this bad?** In the best case, the code will be harder to read than
22 /// necessary. I don't know the worst case.
24 /// **Known problems:** None.
28 /// OpenOptions::new().read(true).truncate(true)
30 declare_clippy_lint! {
31 pub NONSENSICAL_OPEN_OPTIONS,
33 "nonsensical combination of options for opening a file"
36 #[derive(Copy, Clone)]
37 pub struct NonSensical;
39 impl LintPass for NonSensical {
40 fn get_lints(&self) -> LintArray {
41 lint_array!(NONSENSICAL_OPEN_OPTIONS)
45 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonSensical {
46 fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
47 if let ExprKind::MethodCall(ref path, _, ref arguments) = e.node {
48 let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&arguments[0]));
49 if path.ident.name == "open" && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
50 let mut options = Vec::new();
51 get_open_options(cx, &arguments[0], &mut options);
52 check_open_options(cx, &options, e.span);
58 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
74 fn get_open_options(cx: &LateContext<'_, '_>, argument: &Expr, options: &mut Vec<(OpenOption, Argument)>) {
75 if let ExprKind::MethodCall(ref path, _, ref arguments) = argument.node {
76 let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(&arguments[0]));
78 // Only proceed if this is a call on some object of type std::fs::OpenOptions
79 if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
80 let argument_option = match arguments[1].node {
81 ExprKind::Lit(ref span) => {
83 node: LitKind::Bool(lit),
93 return; // The function is called with a literal
94 // which is not a boolean literal. This is theoretically
95 // possible, but not very likely.
98 _ => Argument::Unknown,
101 match &*path.ident.as_str() {
103 options.push((OpenOption::Create, argument_option));
106 options.push((OpenOption::Append, argument_option));
109 options.push((OpenOption::Truncate, argument_option));
112 options.push((OpenOption::Read, argument_option));
115 options.push((OpenOption::Write, argument_option));
120 get_open_options(cx, &arguments[0], options);
125 fn check_open_options(cx: &LateContext<'_, '_>, options: &[(OpenOption, Argument)], span: Span) {
126 let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
127 let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
128 (false, false, false, false, false);
129 // This code is almost duplicated (oh, the irony), but I haven't found a way to
132 for option in options {
134 (OpenOption::Create, arg) => {
138 NONSENSICAL_OPEN_OPTIONS,
140 "the method \"create\" is called more than once",
145 create_arg = create_arg || (arg == Argument::True);;
147 (OpenOption::Append, arg) => {
151 NONSENSICAL_OPEN_OPTIONS,
153 "the method \"append\" is called more than once",
158 append_arg = append_arg || (arg == Argument::True);;
160 (OpenOption::Truncate, arg) => {
164 NONSENSICAL_OPEN_OPTIONS,
166 "the method \"truncate\" is called more than once",
171 truncate_arg = truncate_arg || (arg == Argument::True);
173 (OpenOption::Read, arg) => {
177 NONSENSICAL_OPEN_OPTIONS,
179 "the method \"read\" is called more than once",
184 read_arg = read_arg || (arg == Argument::True);;
186 (OpenOption::Write, arg) => {
190 NONSENSICAL_OPEN_OPTIONS,
192 "the method \"write\" is called more than once",
197 write_arg = write_arg || (arg == Argument::True);;
202 if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
203 span_lint(cx, NONSENSICAL_OPEN_OPTIONS, span, "file opened with \"truncate\" and \"read\"");
205 if append && truncate && append_arg && truncate_arg {
208 NONSENSICAL_OPEN_OPTIONS,
210 "file opened with \"append\" and \"truncate\"",