]>
Commit | Line | Data |
---|---|---|
136023e0 | 1 | use clippy_utils::diagnostics::span_lint_and_sugg; |
a2a8927a XL |
2 | use clippy_utils::source::snippet_with_context; |
3 | use clippy_utils::ty::is_type_diagnostic_item; | |
4 | use clippy_utils::visitors::is_expr_unsafe; | |
5 | use clippy_utils::{get_parent_node, match_libc_symbol}; | |
136023e0 XL |
6 | use if_chain::if_chain; |
7 | use rustc_errors::Applicability; | |
a2a8927a | 8 | use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, UnsafeSource}; |
136023e0 XL |
9 | use rustc_lint::{LateContext, LateLintPass}; |
10 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
a2a8927a | 11 | use rustc_span::symbol::sym; |
136023e0 XL |
12 | |
13 | declare_clippy_lint! { | |
94222f64 XL |
14 | /// ### What it does |
15 | /// Checks for usage of `libc::strlen` on a `CString` or `CStr` value, | |
136023e0 XL |
16 | /// and suggest calling `as_bytes().len()` or `to_bytes().len()` respectively instead. |
17 | /// | |
94222f64 XL |
18 | /// ### Why is this bad? |
19 | /// This avoids calling an unsafe `libc` function. | |
136023e0 XL |
20 | /// Currently, it also avoids calculating the length. |
21 | /// | |
94222f64 | 22 | /// ### Example |
136023e0 XL |
23 | /// ```rust, ignore |
24 | /// use std::ffi::CString; | |
25 | /// let cstring = CString::new("foo").expect("CString::new failed"); | |
26 | /// let len = unsafe { libc::strlen(cstring.as_ptr()) }; | |
27 | /// ``` | |
28 | /// Use instead: | |
29 | /// ```rust, no_run | |
30 | /// use std::ffi::CString; | |
31 | /// let cstring = CString::new("foo").expect("CString::new failed"); | |
32 | /// let len = cstring.as_bytes().len(); | |
33 | /// ``` | |
a2a8927a | 34 | #[clippy::version = "1.55.0"] |
136023e0 XL |
35 | pub STRLEN_ON_C_STRINGS, |
36 | complexity, | |
37 | "using `libc::strlen` on a `CString` or `CStr` value, while `as_bytes().len()` or `to_bytes().len()` respectively can be used instead" | |
38 | } | |
39 | ||
40 | declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]); | |
41 | ||
5099ac24 | 42 | impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { |
a2a8927a | 43 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { |
136023e0 | 44 | if_chain! { |
a2a8927a XL |
45 | if !expr.span.from_expansion(); |
46 | if let ExprKind::Call(func, [recv]) = expr.kind; | |
47 | if let ExprKind::Path(path) = &func.kind; | |
48 | if let Some(did) = cx.qpath_res(path, func.hir_id).opt_def_id(); | |
49 | if match_libc_symbol(cx, did, "strlen"); | |
f2b60f7d | 50 | if let ExprKind::MethodCall(path, self_arg, [], _) = recv.kind; |
a2a8927a | 51 | if !recv.span.from_expansion(); |
136023e0 XL |
52 | if path.ident.name == sym::as_ptr; |
53 | then { | |
a2a8927a XL |
54 | let ctxt = expr.span.ctxt(); |
55 | let span = match get_parent_node(cx.tcx, expr.hir_id) { | |
56 | Some(Node::Block(&Block { | |
57 | rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), span, .. | |
58 | })) | |
59 | if span.ctxt() == ctxt && !is_expr_unsafe(cx, self_arg) => { | |
60 | span | |
61 | } | |
62 | _ => expr.span, | |
63 | }; | |
64 | ||
65 | let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); | |
66 | let mut app = Applicability::MachineApplicable; | |
67 | let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; | |
68 | let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) { | |
69 | "as_bytes" | |
70 | } else if is_type_diagnostic_item(cx, ty, sym::CStr) { | |
71 | "to_bytes" | |
136023e0 XL |
72 | } else { |
73 | return; | |
74 | }; | |
75 | ||
76 | span_lint_and_sugg( | |
77 | cx, | |
78 | STRLEN_ON_C_STRINGS, | |
a2a8927a | 79 | span, |
136023e0 | 80 | "using `libc::strlen` on a `CString` or `CStr` value", |
a2a8927a | 81 | "try this", |
2b03887a | 82 | format!("{val_name}.{method_name}().len()"), |
a2a8927a | 83 | app, |
136023e0 XL |
84 | ); |
85 | } | |
86 | } | |
87 | } | |
88 | } |