1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::source::{snippet_opt, snippet_with_applicability};
3 use clippy_utils::ty::{is_type_diagnostic_item, match_type};
4 use clippy_utils::{match_def_path, paths};
5 use if_chain::if_chain;
6 use rustc_errors::Applicability;
7 use rustc_hir::{Expr, ExprKind};
8 use rustc_lint::{LateContext, LateLintPass};
9 use rustc_session::{declare_lint_pass, declare_tool_lint};
10 use rustc_span::sym;
11
12 declare_clippy_lint! {
13 /// ### What it does
14 /// Checks for non-octal values used to set Unix file permissions.
15 ///
16 /// ### Why is this bad?
17 /// They will be converted into octal, creating potentially
18 /// unintended file permissions.
19 ///
20 /// ### Example
21 /// ```rust,ignore
22 /// use std::fs::OpenOptions;
23 /// use std::os::unix::fs::OpenOptionsExt;
24 ///
25 /// let mut options = OpenOptions::new();
26 /// options.mode(644);
27 /// ```
28 /// Use instead:
29 /// ```rust,ignore
30 /// use std::fs::OpenOptions;
31 /// use std::os::unix::fs::OpenOptionsExt;
32 ///
33 /// let mut options = OpenOptions::new();
34 /// options.mode(0o644);
35 /// ```
36 #[clippy::version = "1.53.0"]
37 pub NON_OCTAL_UNIX_PERMISSIONS,
38 correctness,
39 "use of non-octal value to set unix file permissions, which will be translated into octal"
40 }
41
42 declare_lint_pass!(NonOctalUnixPermissions => [NON_OCTAL_UNIX_PERMISSIONS]);
43
44 impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)45 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
46 match &expr.kind {
47 ExprKind::MethodCall(path, func, [param], _) => {
48 let obj_ty = cx.typeck_results().expr_ty(func).peel_refs();
49
50 if_chain! {
51 if (path.ident.name == sym!(mode)
52 && (match_type(cx, obj_ty, &paths::OPEN_OPTIONS)
53 || is_type_diagnostic_item(cx, obj_ty, sym::DirBuilder)))
54 || (path.ident.name == sym!(set_mode) && match_type(cx, obj_ty, &paths::PERMISSIONS));
55 if let ExprKind::Lit(_) = param.kind;
56 if param.span.ctxt() == expr.span.ctxt();
57
58 then {
59 let Some(snip) = snippet_opt(cx, param.span) else {
60 return
61 };
62
63 if !snip.starts_with("0o") {
64 show_error(cx, param);
65 }
66 }
67 }
68 },
69 ExprKind::Call(func, [param]) => {
70 if_chain! {
71 if let ExprKind::Path(ref path) = func.kind;
72 if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
73 if match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE);
74 if let ExprKind::Lit(_) = param.kind;
75 if param.span.ctxt() == expr.span.ctxt();
76 if let Some(snip) = snippet_opt(cx, param.span);
77 if !snip.starts_with("0o");
78 then {
79 show_error(cx, param);
80 }
81 }
82 },
83 _ => {},
84 };
85 }
86 }
87
show_error(cx: &LateContext<'_>, param: &Expr<'_>)88 fn show_error(cx: &LateContext<'_>, param: &Expr<'_>) {
89 let mut applicability = Applicability::MachineApplicable;
90 span_lint_and_sugg(
91 cx,
92 NON_OCTAL_UNIX_PERMISSIONS,
93 param.span,
94 "using a non-octal value to set unix file permissions",
95 "consider using an octal literal instead",
96 format!(
97 "0o{}",
98 snippet_with_applicability(cx, param.span, "0o..", &mut applicability,),
99 ),
100 applicability,
101 );
102 }
103