Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 22 additions & 17 deletions clippy_lints/src/strlen_on_c_strings.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::res::MaybeDef;
use clippy_utils::source::snippet_with_context;
use clippy_utils::visitors::is_expr_unsafe;
Expand Down Expand Up @@ -47,7 +47,21 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings {
&& let ExprKind::MethodCall(path, self_arg, [], _) = recv.kind
&& !recv.span.from_expansion()
&& path.ident.name == sym::as_ptr
&& let typeck = cx.typeck_results()
&& typeck
.expr_ty_adjusted(self_arg)
.peel_refs()
.is_lang_item(cx, LangItem::CStr)
{
let ty = typeck.expr_ty(self_arg).peel_refs();
let ty_kind = if ty.is_diag_item(cx, sym::cstring_type) {
"`CString` value"
} else if ty.is_lang_item(cx, LangItem::CStr) {
"`CStr` value"
} else {
"type that dereferences to `CStr`"
};

let ctxt = expr.span.ctxt();
let span = match cx.tcx.parent_hir_node(expr.hir_id) {
Node::Block(&Block {
Expand All @@ -58,25 +72,16 @@ impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings {
_ => expr.span,
};

let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
let mut app = Applicability::MachineApplicable;
let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
let method_name = if ty.is_diag_item(cx, sym::cstring_type) {
"as_bytes"
} else if ty.is_lang_item(cx, LangItem::CStr) {
"to_bytes"
} else {
return;
};

span_lint_and_sugg(
span_lint_and_then(
cx,
STRLEN_ON_C_STRINGS,
span,
"using `libc::strlen` on a `CString` or `CStr` value",
"try",
format!("{val_name}.{method_name}().len()"),
app,
format!("using `libc::strlen` on a {ty_kind}"),
|diag| {
let mut app = Applicability::MachineApplicable;
let val_name = snippet_with_context(cx, self_arg.span, ctxt, "_", &mut app).0;
diag.span_suggestion(span, "use", format!("{val_name}.to_bytes().len()"), app);
},
);
}
}
Expand Down
29 changes: 19 additions & 10 deletions tests/ui/strlen_on_c_strings.fixed
Original file line number Diff line number Diff line change
@@ -1,37 +1,46 @@
#![warn(clippy::strlen_on_c_strings)]
#![allow(dead_code, clippy::manual_c_str_literals)]
#![allow(clippy::manual_c_str_literals, clippy::boxed_local)]

#[allow(unused)]
use libc::strlen;
use std::ffi::{CStr, CString};

fn main() {
// CString
let cstring = CString::new("foo").expect("CString::new failed");
let _ = cstring.as_bytes().len();
//~^ strlen_on_c_strings
let _ = cstring.to_bytes().len();
//~^ ERROR: using `libc::strlen` on a `CString` value

// CStr
let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
let _ = cstr.to_bytes().len();
//~^ strlen_on_c_strings
//~^ ERROR: using `libc::strlen` on a `CStr` value

let _ = cstr.to_bytes().len();
//~^ strlen_on_c_strings
//~^ ERROR: using `libc::strlen` on a `CStr` value

let pcstr: *const &CStr = &cstr;
let _ = unsafe { (*pcstr).to_bytes().len() };
//~^ strlen_on_c_strings
//~^ ERROR: using `libc::strlen` on a `CStr` value

unsafe fn unsafe_identity<T>(x: T) -> T {
x
}
let _ = unsafe { unsafe_identity(cstr).to_bytes().len() };
//~^ strlen_on_c_strings
//~^ ERROR: using `libc::strlen` on a `CStr` value
let _ = unsafe { unsafe_identity(cstr) }.to_bytes().len();
//~^ strlen_on_c_strings
//~^ ERROR: using `libc::strlen` on a `CStr` value

let f: unsafe fn(_) -> _ = unsafe_identity;
let _ = unsafe { f(cstr).to_bytes().len() };
//~^ strlen_on_c_strings
//~^ ERROR: using `libc::strlen` on a `CStr` value
}

// make sure we lint types that _adjust_ to `CStr`
fn adjusted(box_cstring: Box<CString>, box_cstr: Box<CStr>, arc_cstring: std::sync::Arc<CStr>) {
let _ = box_cstring.to_bytes().len();
//~^ ERROR: using `libc::strlen` on a type that dereferences to `CStr`
let _ = box_cstr.to_bytes().len();
//~^ ERROR: using `libc::strlen` on a type that dereferences to `CStr`
let _ = arc_cstring.to_bytes().len();
//~^ ERROR: using `libc::strlen` on a type that dereferences to `CStr`
}
27 changes: 18 additions & 9 deletions tests/ui/strlen_on_c_strings.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,46 @@
#![warn(clippy::strlen_on_c_strings)]
#![allow(dead_code, clippy::manual_c_str_literals)]
#![allow(clippy::manual_c_str_literals, clippy::boxed_local)]

#[allow(unused)]
use libc::strlen;
use std::ffi::{CStr, CString};

fn main() {
// CString
let cstring = CString::new("foo").expect("CString::new failed");
let _ = unsafe { libc::strlen(cstring.as_ptr()) };
//~^ strlen_on_c_strings
//~^ ERROR: using `libc::strlen` on a `CString` value

// CStr
let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
let _ = unsafe { libc::strlen(cstr.as_ptr()) };
//~^ strlen_on_c_strings
//~^ ERROR: using `libc::strlen` on a `CStr` value

let _ = unsafe { strlen(cstr.as_ptr()) };
//~^ strlen_on_c_strings
//~^ ERROR: using `libc::strlen` on a `CStr` value

let pcstr: *const &CStr = &cstr;
let _ = unsafe { strlen((*pcstr).as_ptr()) };
//~^ strlen_on_c_strings
//~^ ERROR: using `libc::strlen` on a `CStr` value

unsafe fn unsafe_identity<T>(x: T) -> T {
x
}
let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) };
//~^ strlen_on_c_strings
//~^ ERROR: using `libc::strlen` on a `CStr` value
let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) };
//~^ strlen_on_c_strings
//~^ ERROR: using `libc::strlen` on a `CStr` value

let f: unsafe fn(_) -> _ = unsafe_identity;
let _ = unsafe { strlen(f(cstr).as_ptr()) };
//~^ strlen_on_c_strings
//~^ ERROR: using `libc::strlen` on a `CStr` value
}

// make sure we lint types that _adjust_ to `CStr`
fn adjusted(box_cstring: Box<CString>, box_cstr: Box<CStr>, arc_cstring: std::sync::Arc<CStr>) {
let _ = unsafe { libc::strlen(box_cstring.as_ptr()) };
//~^ ERROR: using `libc::strlen` on a type that dereferences to `CStr`
let _ = unsafe { libc::strlen(box_cstr.as_ptr()) };
//~^ ERROR: using `libc::strlen` on a type that dereferences to `CStr`
let _ = unsafe { libc::strlen(arc_cstring.as_ptr()) };
//~^ ERROR: using `libc::strlen` on a type that dereferences to `CStr`
}
62 changes: 40 additions & 22 deletions tests/ui/strlen_on_c_strings.stderr
Original file line number Diff line number Diff line change
@@ -1,47 +1,65 @@
error: using `libc::strlen` on a `CString` or `CStr` value
--> tests/ui/strlen_on_c_strings.rs:11:13
error: using `libc::strlen` on a `CString` value
--> tests/ui/strlen_on_c_strings.rs:10:13
|
LL | let _ = unsafe { libc::strlen(cstring.as_ptr()) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cstring.as_bytes().len()`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `cstring.to_bytes().len()`
|
= note: `-D clippy::strlen-on-c-strings` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::strlen_on_c_strings)]`

error: using `libc::strlen` on a `CString` or `CStr` value
--> tests/ui/strlen_on_c_strings.rs:16:13
error: using `libc::strlen` on a `CStr` value
--> tests/ui/strlen_on_c_strings.rs:15:13
|
LL | let _ = unsafe { libc::strlen(cstr.as_ptr()) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cstr.to_bytes().len()`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `cstr.to_bytes().len()`

error: using `libc::strlen` on a `CString` or `CStr` value
--> tests/ui/strlen_on_c_strings.rs:19:13
error: using `libc::strlen` on a `CStr` value
--> tests/ui/strlen_on_c_strings.rs:18:13
|
LL | let _ = unsafe { strlen(cstr.as_ptr()) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cstr.to_bytes().len()`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `cstr.to_bytes().len()`

error: using `libc::strlen` on a `CString` or `CStr` value
--> tests/ui/strlen_on_c_strings.rs:23:22
error: using `libc::strlen` on a `CStr` value
--> tests/ui/strlen_on_c_strings.rs:22:22
|
LL | let _ = unsafe { strlen((*pcstr).as_ptr()) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*pcstr).to_bytes().len()`
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `(*pcstr).to_bytes().len()`

error: using `libc::strlen` on a `CString` or `CStr` value
--> tests/ui/strlen_on_c_strings.rs:29:22
error: using `libc::strlen` on a `CStr` value
--> tests/ui/strlen_on_c_strings.rs:28:22
|
LL | let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unsafe_identity(cstr).to_bytes().len()`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `unsafe_identity(cstr).to_bytes().len()`

error: using `libc::strlen` on a `CString` or `CStr` value
--> tests/ui/strlen_on_c_strings.rs:31:13
error: using `libc::strlen` on a `CStr` value
--> tests/ui/strlen_on_c_strings.rs:30:13
|
LL | let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unsafe { unsafe_identity(cstr) }.to_bytes().len()`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `unsafe { unsafe_identity(cstr) }.to_bytes().len()`

error: using `libc::strlen` on a `CString` or `CStr` value
--> tests/ui/strlen_on_c_strings.rs:35:22
error: using `libc::strlen` on a `CStr` value
--> tests/ui/strlen_on_c_strings.rs:34:22
|
LL | let _ = unsafe { strlen(f(cstr).as_ptr()) };
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `f(cstr).to_bytes().len()`
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `f(cstr).to_bytes().len()`

error: aborting due to 7 previous errors
error: using `libc::strlen` on a type that dereferences to `CStr`
--> tests/ui/strlen_on_c_strings.rs:40:13
|
LL | let _ = unsafe { libc::strlen(box_cstring.as_ptr()) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `box_cstring.to_bytes().len()`

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

error: using `libc::strlen` on a type that dereferences to `CStr`
--> tests/ui/strlen_on_c_strings.rs:44:13
|
LL | let _ = unsafe { libc::strlen(arc_cstring.as_ptr()) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `arc_cstring.to_bytes().len()`

error: aborting due to 10 previous errors