Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add redundant_as_str lint #11526

Merged
merged 8 commits into from
Sep 18, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5281,6 +5281,7 @@ Released 2018-09-13
[`readonly_write_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#readonly_write_lock
[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
[`redundant_as_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_as_str
[`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block
[`redundant_at_rest_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_at_rest_pattern
[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::RANGE_ZIP_WITH_LEN_INFO,
crate::methods::READONLY_WRITE_LOCK_INFO,
crate::methods::READ_LINE_WITHOUT_TRIM_INFO,
crate::methods::REDUNDANT_AS_STR_INFO,
crate::methods::REPEAT_ONCE_INFO,
crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO,
crate::methods::SEARCH_IS_SOME_INFO,
Expand Down
29 changes: 29 additions & 0 deletions clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ mod path_ends_with_ext;
mod range_zip_with_len;
mod read_line_without_trim;
mod readonly_write_lock;
mod redundant_as_str;
mod repeat_once;
mod search_is_some;
mod seek_from_current;
Expand Down Expand Up @@ -3605,6 +3606,32 @@ declare_clippy_lint! {
"attempting to compare file extensions using `Path::ends_with`"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `as_str()` on a `String`` chained with a method available on the `String` itself.
///
/// ### Why is this bad?
/// The `as_str()` conversion is pointless and can be removed for simplicity and cleanliness.
///
/// ### Example
/// ```rust
/// # #![allow(unused)]
/// let owned_string = "This is a string".to_owned();
/// owned_string.as_str().as_bytes();
/// ```
///
/// Use instead:
/// ```rust
/// # #![allow(unused)]
/// let owned_string = "This is a string".to_owned();
/// owned_string.as_bytes();
/// ```
#[clippy::version = "1.74.0"]
pub REDUNDANT_AS_STR,
complexity,
"`as_str` used to call a method on `str` that is also available on `String`"
}

pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
Expand Down Expand Up @@ -3749,6 +3776,7 @@ impl_lint_pass!(Methods => [
READONLY_WRITE_LOCK,
ITER_OUT_OF_BOUNDS,
PATH_ENDS_WITH_EXT,
REDUNDANT_AS_STR,
]);

/// Extracts a method call name, args, and `Span` of the method name.
Expand Down Expand Up @@ -3975,6 +4003,7 @@ impl Methods {
("as_deref" | "as_deref_mut", []) => {
needless_option_as_deref::check(cx, expr, recv, name);
},
("as_bytes" | "is_empty", []) => if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); },
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
Expand Down
34 changes: 34 additions & 0 deletions clippy_lints/src/methods/redundant_as_str.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use super::REDUNDANT_AS_STR;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::query::Key;
use rustc_span::Span;

pub(super) fn check(
cx: &LateContext<'_>,
_expr: &Expr<'_>,
recv: &Expr<'_>,
as_str_span: Span,
other_method_span: Span,
) {
if cx
.tcx
.lang_items()
.string()
.is_some_and(|id| Some(id) == cx.typeck_results().expr_ty(recv).ty_adt_id())
{
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
REDUNDANT_AS_STR,
as_str_span.to(other_method_span),
"this `as_str` is redundant and can be removed as the method immediately following exists on `String` too",
"try",
snippet_with_applicability(cx, other_method_span, "..", &mut applicability).into_owned(),
applicability,
);
}
}
24 changes: 24 additions & 0 deletions tests/ui/redundant_as_str.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![warn(clippy::redundant_as_str)]

fn main() {
let string = "Hello, world!".to_owned();

// These methods are redundant and the `as_str` can be removed
let _redundant = string.as_bytes();
let _redundant = string.is_empty();

// These methods don't use `as_str` when they are redundant
let _no_as_str = string.as_bytes();
let _no_as_str = string.is_empty();

// These methods are not redundant, and are equivelant to
// doing dereferencing the string and applying the method
let _not_redundant = string.as_str().escape_unicode();
let _not_redundant = string.as_str().trim();
let _not_redundant = string.as_str().split_whitespace();

// These methods don't use `as_str` and are applied on a `str` directly
let borrowed_str = "Hello, world!";
let _is_str = borrowed_str.as_bytes();
let _is_str = borrowed_str.is_empty();
}
24 changes: 24 additions & 0 deletions tests/ui/redundant_as_str.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![warn(clippy::redundant_as_str)]

fn main() {
let string = "Hello, world!".to_owned();

// These methods are redundant and the `as_str` can be removed
let _redundant = string.as_str().as_bytes();
let _redundant = string.as_str().is_empty();

// These methods don't use `as_str` when they are redundant
let _no_as_str = string.as_bytes();
let _no_as_str = string.is_empty();

// These methods are not redundant, and are equivelant to
// doing dereferencing the string and applying the method
let _not_redundant = string.as_str().escape_unicode();
let _not_redundant = string.as_str().trim();
let _not_redundant = string.as_str().split_whitespace();

// These methods don't use `as_str` and are applied on a `str` directly
let borrowed_str = "Hello, world!";
let _is_str = borrowed_str.as_bytes();
let _is_str = borrowed_str.is_empty();
}
17 changes: 17 additions & 0 deletions tests/ui/redundant_as_str.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too
--> $DIR/redundant_as_str.rs:7:29
|
LL | let _redundant = string.as_str().as_bytes();
| ^^^^^^^^^^^^^^^^^ help: try: `as_bytes`
|
= note: `-D clippy::redundant-as-str` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::redundant_as_str)]`

error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too
--> $DIR/redundant_as_str.rs:8:29
|
LL | let _redundant = string.as_str().is_empty();
| ^^^^^^^^^^^^^^^^^ help: try: `is_empty`

error: aborting due to 2 previous errors