Skip to content

Commit a4d1837

Browse files
committed
unnecessary_string_new
1 parent 984330a commit a4d1837

10 files changed

+148
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3650,6 +3650,7 @@ Released 2018-09-13
36503650
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
36513651
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
36523652
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
3653+
[`unnecessary_owned_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_string
36533654
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
36543655
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
36553656
[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned

clippy_lints/src/lib.register_all.rs

+1
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
310310
LintId::of(unit_types::UNIT_CMP),
311311
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
312312
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
313+
LintId::of(unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING),
313314
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
314315
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
315316
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),

clippy_lints/src/lib.register_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ store.register_lints(&[
523523
unit_types::UNIT_CMP,
524524
unnamed_address::FN_ADDRESS_COMPARISONS,
525525
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
526+
unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING,
526527
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
527528
unnecessary_sort_by::UNNECESSARY_SORT_BY,
528529
unnecessary_wraps::UNNECESSARY_WRAPS,

clippy_lints/src/lib.register_style.rs

+1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
106106
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
107107
LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
108108
LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
109+
LintId::of(unnecessary_owned_empty_string::UNNECESSARY_OWNED_EMPTY_STRING),
109110
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
110111
LintId::of(unused_unit::UNUSED_UNIT),
111112
LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ mod unit_hash;
383383
mod unit_return_expecting_ord;
384384
mod unit_types;
385385
mod unnamed_address;
386+
mod unnecessary_owned_empty_string;
386387
mod unnecessary_self_imports;
387388
mod unnecessary_sort_by;
388389
mod unnecessary_wraps;
@@ -868,6 +869,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
868869
});
869870
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
870871
store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
872+
store.register_late_pass(|| Box::new(unnecessary_owned_empty_string::UnnecessaryOwnedEmptyString));
871873
// add lints here, do not remove this comment, it's used in `new_lint`
872874
}
873875

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item};
2+
use clippy_utils::{match_def_path, paths};
3+
use if_chain::if_chain;
4+
use rustc_ast::ast::LitKind;
5+
use rustc_errors::Applicability;
6+
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
7+
use rustc_lint::{LateContext, LateLintPass};
8+
use rustc_middle::ty;
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+
///
15+
/// Detects cases of owned empty strings being passed as an argument to a function expecting `&str`
16+
///
17+
/// ### Why is this bad?
18+
///
19+
/// This results in longer and less readable code
20+
///
21+
/// ### Example
22+
/// ```rust
23+
/// vec!["1", "2", "3"].join(&String::new());
24+
/// ```
25+
/// Use instead:
26+
/// ```rust
27+
/// vec!["1", "2", "3"].join("");
28+
/// ```
29+
#[clippy::version = "1.62.0"]
30+
pub UNNECESSARY_OWNED_EMPTY_STRING,
31+
style,
32+
"detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`"
33+
}
34+
declare_lint_pass!(UnnecessaryOwnedEmptyString => [UNNECESSARY_OWNED_EMPTY_STRING]);
35+
36+
impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyString {
37+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
38+
if_chain! {
39+
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind;
40+
if let ExprKind::Call(fun, args) = inner_expr.kind;
41+
if let ExprKind::Path(ref qpath) = fun.kind;
42+
if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
43+
if let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
44+
if inner_str.is_str();
45+
then {
46+
if match_def_path(cx, fun_def_id, &paths::STRING_NEW) {
47+
span_lint_and_sugg(
48+
cx,
49+
UNNECESSARY_OWNED_EMPTY_STRING,
50+
expr.span,
51+
"usage of `&String::new()` for a function expecting a `&str` argument",
52+
"try",
53+
"\"\"".to_owned(),
54+
Applicability::MachineApplicable,
55+
);
56+
} else {
57+
if_chain! {
58+
if match_def_path(cx, fun_def_id, &paths::FROM_FROM);
59+
if let [.., last_arg] = args;
60+
if let ExprKind::Lit(spanned) = &last_arg.kind;
61+
if let LitKind::Str(symbol, _) = spanned.node;
62+
if symbol.is_empty();
63+
let inner_expr_type = cx.typeck_results().expr_ty(inner_expr);
64+
if is_type_diagnostic_item(cx, inner_expr_type, sym::String);
65+
then {
66+
span_lint_and_sugg(
67+
cx,
68+
UNNECESSARY_OWNED_EMPTY_STRING,
69+
expr.span,
70+
"usage of `&String::from(\"\")` for a function expecting a `&str` argument",
71+
"try",
72+
"\"\"".to_owned(),
73+
Applicability::MachineApplicable,
74+
);
75+
}
76+
}
77+
}
78+
}
79+
}
80+
}
81+
}

clippy_utils/src/paths.rs

+1
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
148148
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
149149
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
150150
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
151+
pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
151152
pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
152153
pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
153154
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::unnecessary_owned_empty_string)]
4+
5+
fn ref_str_argument(_value: &str) {}
6+
7+
#[allow(clippy::ptr_arg)]
8+
fn ref_string_argument(_value: &String) {}
9+
10+
fn main() {
11+
// should be linted
12+
ref_str_argument("");
13+
14+
// should be linted
15+
ref_str_argument("");
16+
17+
// should not be linted
18+
ref_str_argument("");
19+
20+
// should not be linted
21+
ref_string_argument(&String::new());
22+
}
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::unnecessary_owned_empty_string)]
4+
5+
fn ref_str_argument(_value: &str) {}
6+
7+
#[allow(clippy::ptr_arg)]
8+
fn ref_string_argument(_value: &String) {}
9+
10+
fn main() {
11+
// should be linted
12+
ref_str_argument(&String::new());
13+
14+
// should be linted
15+
ref_str_argument(&String::from(""));
16+
17+
// should not be linted
18+
ref_str_argument("");
19+
20+
// should not be linted
21+
ref_string_argument(&String::new());
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: usage of `&String::new()` for a function expecting a `&str` argument
2+
--> $DIR/unnecessary_owned_empty_string.rs:12:22
3+
|
4+
LL | ref_str_argument(&String::new());
5+
| ^^^^^^^^^^^^^^ help: try: `""`
6+
|
7+
= note: `-D clippy::unnecessary-owned-empty-string` implied by `-D warnings`
8+
9+
error: usage of `&String::from("")` for a function expecting a `&str` argument
10+
--> $DIR/unnecessary_owned_empty_string.rs:15:22
11+
|
12+
LL | ref_str_argument(&String::from(""));
13+
| ^^^^^^^^^^^^^^^^^ help: try: `""`
14+
15+
error: aborting due to 2 previous errors
16+

0 commit comments

Comments
 (0)