Skip to content

Commit 2f3b9ce

Browse files
committed
feat(strlen_on_c_strings): suggest .count_bytes()
1 parent c267b59 commit 2f3b9ce

File tree

6 files changed

+116
-29
lines changed

6 files changed

+116
-29
lines changed

clippy_lints/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
718718
Box::new(|_| Box::<unused_async::UnusedAsync>::default()),
719719
Box::new(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))),
720720
Box::new(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))),
721-
Box::new(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings)),
721+
Box::new(move |_| Box::new(strlen_on_c_strings::StrlenOnCStrings::new(conf))),
722722
Box::new(move |_| Box::new(self_named_constructors::SelfNamedConstructors)),
723723
Box::new(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)),
724724
Box::new(move |_| Box::new(manual_assert::ManualAssert)),

clippy_lints/src/strlen_on_c_strings.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1+
use clippy_config::Conf;
12
use clippy_utils::diagnostics::span_lint_and_then;
3+
use clippy_utils::msrvs::{self, Msrv};
24
use clippy_utils::res::MaybeDef;
35
use clippy_utils::source::snippet_with_context;
46
use clippy_utils::visitors::is_expr_unsafe;
57
use clippy_utils::{match_libc_symbol, sym};
68
use rustc_errors::Applicability;
79
use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, LangItem, Node, UnsafeSource};
810
use rustc_lint::{LateContext, LateLintPass};
9-
use rustc_session::declare_lint_pass;
11+
use rustc_session::impl_lint_pass;
1012

1113
declare_clippy_lint! {
1214
/// ### What it does
1315
/// Checks for usage of `libc::strlen` on a `CString` or `CStr` value,
14-
/// and suggest calling `as_bytes().len()` or `to_bytes().len()` respectively instead.
16+
/// and suggest calling `count_bytes()` instead.
1517
///
1618
/// ### Why is this bad?
1719
/// libc::strlen is an unsafe function, which we don't need to call
@@ -27,15 +29,25 @@ declare_clippy_lint! {
2729
/// ```rust, no_run
2830
/// use std::ffi::CString;
2931
/// let cstring = CString::new("foo").expect("CString::new failed");
30-
/// let len = cstring.as_bytes().len();
32+
/// let len = cstring.count_bytes();
3133
/// ```
3234
#[clippy::version = "1.55.0"]
3335
pub STRLEN_ON_C_STRINGS,
3436
complexity,
35-
"using `libc::strlen` on a `CString` or `CStr` value, while `as_bytes().len()` or `to_bytes().len()` respectively can be used instead"
37+
"using `libc::strlen` on a `CString` or `CStr` value, while `count_bytes()` can be used instead"
3638
}
3739

38-
declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]);
40+
pub struct StrlenOnCStrings {
41+
msrv: Msrv,
42+
}
43+
44+
impl StrlenOnCStrings {
45+
pub fn new(conf: &Conf) -> Self {
46+
Self { msrv: conf.msrv }
47+
}
48+
}
49+
50+
impl_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]);
3951

4052
impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings {
4153
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
@@ -80,7 +92,14 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings {
8092
|diag| {
8193
let mut app = Applicability::MachineApplicable;
8294
let val_name = snippet_with_context(cx, self_arg.span, ctxt, "_", &mut app).0;
83-
diag.span_suggestion(span, "use", format!("{val_name}.to_bytes().len()"), app);
95+
96+
let suggestion = if self.msrv.meets(cx, msrvs::CSTR_COUNT_BYTES) {
97+
format!("{val_name}.count_bytes()")
98+
} else {
99+
format!("{val_name}.to_bytes().len()")
100+
};
101+
102+
diag.span_suggestion(span, "use", suggestion, app);
84103
},
85104
);
86105
}

clippy_utils/src/msrvs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ msrv_aliases! {
3232
1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP, SPECIALIZED_TO_STRING_FOR_REFS }
3333
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION, DURATION_ABS_DIFF }
3434
1,80,0 { BOX_INTO_ITER, LAZY_CELL }
35-
1,79,0 { CONST_BLOCKS }
35+
1,79,0 { CONST_BLOCKS, CSTR_COUNT_BYTES }
3636
1,77,0 { C_STR_LITERALS }
3737
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
3838
1,75,0 { OPTION_AS_SLICE }

tests/ui/strlen_on_c_strings.fixed

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,62 @@ use std::ffi::{CStr, CString};
77
fn main() {
88
// CString
99
let cstring = CString::new("foo").expect("CString::new failed");
10-
let _ = cstring.to_bytes().len();
10+
let _ = cstring.count_bytes();
1111
//~^ ERROR: using `libc::strlen` on a `CString` value
1212

1313
// CStr
1414
let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
15-
let _ = cstr.to_bytes().len();
15+
let _ = cstr.count_bytes();
1616
//~^ ERROR: using `libc::strlen` on a `CStr` value
1717

18-
let _ = cstr.to_bytes().len();
18+
let _ = cstr.count_bytes();
1919
//~^ ERROR: using `libc::strlen` on a `CStr` value
2020

2121
let pcstr: *const &CStr = &cstr;
22-
let _ = unsafe { (*pcstr).to_bytes().len() };
22+
let _ = unsafe { (*pcstr).count_bytes() };
2323
//~^ ERROR: using `libc::strlen` on a `CStr` value
2424

2525
unsafe fn unsafe_identity<T>(x: T) -> T {
2626
x
2727
}
28-
let _ = unsafe { unsafe_identity(cstr).to_bytes().len() };
28+
let _ = unsafe { unsafe_identity(cstr).count_bytes() };
2929
//~^ ERROR: using `libc::strlen` on a `CStr` value
30-
let _ = unsafe { unsafe_identity(cstr) }.to_bytes().len();
30+
let _ = unsafe { unsafe_identity(cstr) }.count_bytes();
3131
//~^ ERROR: using `libc::strlen` on a `CStr` value
3232

3333
let f: unsafe fn(_) -> _ = unsafe_identity;
34-
let _ = unsafe { f(cstr).to_bytes().len() };
34+
let _ = unsafe { f(cstr).count_bytes() };
3535
//~^ ERROR: using `libc::strlen` on a `CStr` value
3636
}
3737

3838
// make sure we lint types that _adjust_ to `CStr`
3939
fn adjusted(box_cstring: Box<CString>, box_cstr: Box<CStr>, arc_cstring: std::sync::Arc<CStr>) {
40-
let _ = box_cstring.to_bytes().len();
40+
let _ = box_cstring.count_bytes();
4141
//~^ ERROR: using `libc::strlen` on a type that dereferences to `CStr`
42-
let _ = box_cstr.to_bytes().len();
42+
let _ = box_cstr.count_bytes();
4343
//~^ ERROR: using `libc::strlen` on a type that dereferences to `CStr`
44-
let _ = arc_cstring.to_bytes().len();
44+
let _ = arc_cstring.count_bytes();
4545
//~^ ERROR: using `libc::strlen` on a type that dereferences to `CStr`
4646
}
47+
48+
#[clippy::msrv = "1.78"]
49+
fn msrv_1_78() {
50+
let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
51+
let _ = cstr.to_bytes().len();
52+
//~^ strlen_on_c_strings
53+
54+
let cstring = CString::new("foo").expect("CString::new failed");
55+
let _ = cstring.to_bytes().len();
56+
//~^ strlen_on_c_strings
57+
}
58+
59+
#[clippy::msrv = "1.79"]
60+
fn msrv_1_79() {
61+
let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
62+
let _ = cstr.count_bytes();
63+
//~^ strlen_on_c_strings
64+
65+
let cstring = CString::new("foo").expect("CString::new failed");
66+
let _ = cstring.count_bytes();
67+
//~^ strlen_on_c_strings
68+
}

tests/ui/strlen_on_c_strings.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,25 @@ fn adjusted(box_cstring: Box<CString>, box_cstr: Box<CStr>, arc_cstring: std::sy
4444
let _ = unsafe { libc::strlen(arc_cstring.as_ptr()) };
4545
//~^ ERROR: using `libc::strlen` on a type that dereferences to `CStr`
4646
}
47+
48+
#[clippy::msrv = "1.78"]
49+
fn msrv_1_78() {
50+
let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
51+
let _ = unsafe { libc::strlen(cstr.as_ptr()) };
52+
//~^ strlen_on_c_strings
53+
54+
let cstring = CString::new("foo").expect("CString::new failed");
55+
let _ = unsafe { libc::strlen(cstring.as_ptr()) };
56+
//~^ strlen_on_c_strings
57+
}
58+
59+
#[clippy::msrv = "1.79"]
60+
fn msrv_1_79() {
61+
let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
62+
let _ = unsafe { libc::strlen(cstr.as_ptr()) };
63+
//~^ strlen_on_c_strings
64+
65+
let cstring = CString::new("foo").expect("CString::new failed");
66+
let _ = unsafe { libc::strlen(cstring.as_ptr()) };
67+
//~^ strlen_on_c_strings
68+
}

tests/ui/strlen_on_c_strings.stderr

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: using `libc::strlen` on a `CString` value
22
--> tests/ui/strlen_on_c_strings.rs:10:13
33
|
44
LL | let _ = unsafe { libc::strlen(cstring.as_ptr()) };
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `cstring.to_bytes().len()`
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `cstring.count_bytes()`
66
|
77
= note: `-D clippy::strlen-on-c-strings` implied by `-D warnings`
88
= help: to override `-D warnings` add `#[allow(clippy::strlen_on_c_strings)]`
@@ -11,55 +11,79 @@ error: using `libc::strlen` on a `CStr` value
1111
--> tests/ui/strlen_on_c_strings.rs:15:13
1212
|
1313
LL | let _ = unsafe { libc::strlen(cstr.as_ptr()) };
14-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `cstr.to_bytes().len()`
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `cstr.count_bytes()`
1515

1616
error: using `libc::strlen` on a `CStr` value
1717
--> tests/ui/strlen_on_c_strings.rs:18:13
1818
|
1919
LL | let _ = unsafe { strlen(cstr.as_ptr()) };
20-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `cstr.to_bytes().len()`
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `cstr.count_bytes()`
2121

2222
error: using `libc::strlen` on a `CStr` value
2323
--> tests/ui/strlen_on_c_strings.rs:22:22
2424
|
2525
LL | let _ = unsafe { strlen((*pcstr).as_ptr()) };
26-
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `(*pcstr).to_bytes().len()`
26+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `(*pcstr).count_bytes()`
2727

2828
error: using `libc::strlen` on a `CStr` value
2929
--> tests/ui/strlen_on_c_strings.rs:28:22
3030
|
3131
LL | let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) };
32-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `unsafe_identity(cstr).to_bytes().len()`
32+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `unsafe_identity(cstr).count_bytes()`
3333

3434
error: using `libc::strlen` on a `CStr` value
3535
--> tests/ui/strlen_on_c_strings.rs:30:13
3636
|
3737
LL | let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) };
38-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `unsafe { unsafe_identity(cstr) }.to_bytes().len()`
38+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `unsafe { unsafe_identity(cstr) }.count_bytes()`
3939

4040
error: using `libc::strlen` on a `CStr` value
4141
--> tests/ui/strlen_on_c_strings.rs:34:22
4242
|
4343
LL | let _ = unsafe { strlen(f(cstr).as_ptr()) };
44-
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `f(cstr).to_bytes().len()`
44+
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `f(cstr).count_bytes()`
4545

4646
error: using `libc::strlen` on a type that dereferences to `CStr`
4747
--> tests/ui/strlen_on_c_strings.rs:40:13
4848
|
4949
LL | let _ = unsafe { libc::strlen(box_cstring.as_ptr()) };
50-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `box_cstring.to_bytes().len()`
50+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `box_cstring.count_bytes()`
5151

5252
error: using `libc::strlen` on a type that dereferences to `CStr`
5353
--> tests/ui/strlen_on_c_strings.rs:42:13
5454
|
5555
LL | let _ = unsafe { libc::strlen(box_cstr.as_ptr()) };
56-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `box_cstr.to_bytes().len()`
56+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `box_cstr.count_bytes()`
5757

5858
error: using `libc::strlen` on a type that dereferences to `CStr`
5959
--> tests/ui/strlen_on_c_strings.rs:44:13
6060
|
6161
LL | let _ = unsafe { libc::strlen(arc_cstring.as_ptr()) };
62-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `arc_cstring.to_bytes().len()`
62+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `arc_cstring.count_bytes()`
63+
64+
error: using `libc::strlen` on a `CStr` value
65+
--> tests/ui/strlen_on_c_strings.rs:51:13
66+
|
67+
LL | let _ = unsafe { libc::strlen(cstr.as_ptr()) };
68+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `cstr.to_bytes().len()`
69+
70+
error: using `libc::strlen` on a `CString` value
71+
--> tests/ui/strlen_on_c_strings.rs:55:13
72+
|
73+
LL | let _ = unsafe { libc::strlen(cstring.as_ptr()) };
74+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `cstring.to_bytes().len()`
75+
76+
error: using `libc::strlen` on a `CStr` value
77+
--> tests/ui/strlen_on_c_strings.rs:62:13
78+
|
79+
LL | let _ = unsafe { libc::strlen(cstr.as_ptr()) };
80+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `cstr.count_bytes()`
81+
82+
error: using `libc::strlen` on a `CString` value
83+
--> tests/ui/strlen_on_c_strings.rs:66:13
84+
|
85+
LL | let _ = unsafe { libc::strlen(cstring.as_ptr()) };
86+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `cstring.count_bytes()`
6387

64-
error: aborting due to 10 previous errors
88+
error: aborting due to 14 previous errors
6589

0 commit comments

Comments
 (0)