• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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