Auto merge of #9295 - Guilherme-Vasconcelos:manual-empty-string-creation, r=dswij
Add `manual_empty_string_creations` lint Closes #2972 - [x] Followed [lint naming conventions][lint_naming] - [x] Added passing UI tests (including committed `.stderr` file) - [x] `cargo test` passes locally - [x] Executed `cargo dev update_lints` - [x] Added lint documentation - [x] Run `cargo dev fmt` changelog: [`manual_empty_string_creations`]: Add lint for empty String not being created with `String::new()`
This commit is contained in:
commit
868dba9f65
@ -3830,6 +3830,7 @@ Released 2018-09-13
|
||||
[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
|
||||
[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
|
||||
[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
|
||||
[`manual_empty_string_creations`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_empty_string_creations
|
||||
[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
|
||||
[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
|
||||
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
|
||||
|
@ -155,7 +155,7 @@ fn to_camel_case(name: &str) -> String {
|
||||
name.split('_')
|
||||
.map(|s| {
|
||||
if s.is_empty() {
|
||||
String::from("")
|
||||
String::new()
|
||||
} else {
|
||||
[&s[0..1].to_uppercase(), &s[1..]].concat()
|
||||
}
|
||||
|
@ -124,6 +124,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(main_recursion::MAIN_RECURSION),
|
||||
LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
|
||||
LintId::of(manual_bits::MANUAL_BITS),
|
||||
LintId::of(manual_empty_string_creations::MANUAL_EMPTY_STRING_CREATIONS),
|
||||
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
|
||||
LintId::of(manual_retain::MANUAL_RETAIN),
|
||||
|
@ -244,6 +244,7 @@ store.register_lints(&[
|
||||
manual_assert::MANUAL_ASSERT,
|
||||
manual_async_fn::MANUAL_ASYNC_FN,
|
||||
manual_bits::MANUAL_BITS,
|
||||
manual_empty_string_creations::MANUAL_EMPTY_STRING_CREATIONS,
|
||||
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
|
||||
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
|
||||
manual_ok_or::MANUAL_OK_OR,
|
||||
|
@ -44,6 +44,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(main_recursion::MAIN_RECURSION),
|
||||
LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
|
||||
LintId::of(manual_bits::MANUAL_BITS),
|
||||
LintId::of(manual_empty_string_creations::MANUAL_EMPTY_STRING_CREATIONS),
|
||||
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(map_clone::MAP_CLONE),
|
||||
LintId::of(match_result_ok::MATCH_RESULT_OK),
|
||||
|
@ -273,6 +273,7 @@ mod main_recursion;
|
||||
mod manual_assert;
|
||||
mod manual_async_fn;
|
||||
mod manual_bits;
|
||||
mod manual_empty_string_creations;
|
||||
mod manual_instant_elapsed;
|
||||
mod manual_non_exhaustive;
|
||||
mod manual_ok_or;
|
||||
@ -933,6 +934,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default()));
|
||||
store.register_late_pass(|| Box::new(manual_instant_elapsed::ManualInstantElapsed));
|
||||
store.register_late_pass(|| Box::new(partialeq_to_none::PartialeqToNone));
|
||||
store.register_late_pass(|| Box::new(manual_empty_string_creations::ManualEmptyStringCreations));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
@ -192,7 +192,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str,
|
||||
match output.kind {
|
||||
TyKind::Tup(tys) if tys.is_empty() => {
|
||||
let sugg = "remove the return type";
|
||||
Some((sugg, "".into()))
|
||||
Some((sugg, String::new()))
|
||||
},
|
||||
_ => {
|
||||
let sugg = "return the output of the future directly";
|
||||
|
141
clippy_lints/src/manual_empty_string_creations.rs
Normal file
141
clippy_lints/src/manual_empty_string_creations.rs
Normal file
@ -0,0 +1,141 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability::MachineApplicable;
|
||||
use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{sym, symbol, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Checks for usage of `""` to create a `String`, such as `"".to_string()`, `"".to_owned()`,
|
||||
/// `String::from("")` and others.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Different ways of creating an empty string makes your code less standardized, which can
|
||||
/// be confusing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let a = "".to_string();
|
||||
/// let b: String = "".into();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let a = String::new();
|
||||
/// let b = String::new();
|
||||
/// ```
|
||||
#[clippy::version = "1.65.0"]
|
||||
pub MANUAL_EMPTY_STRING_CREATIONS,
|
||||
style,
|
||||
"empty String is being created manually"
|
||||
}
|
||||
declare_lint_pass!(ManualEmptyStringCreations => [MANUAL_EMPTY_STRING_CREATIONS]);
|
||||
|
||||
impl LateLintPass<'_> for ManualEmptyStringCreations {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
match ty.kind() {
|
||||
ty::Adt(adt_def, _) if adt_def.is_struct() => {
|
||||
if !cx.tcx.is_diagnostic_item(sym::String, adt_def.did()) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::Call(func, args) => {
|
||||
parse_call(cx, expr.span, func, args);
|
||||
},
|
||||
ExprKind::MethodCall(path_segment, args, _) => {
|
||||
parse_method_call(cx, expr.span, path_segment, args);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if an expression's kind corresponds to an empty &str.
|
||||
fn is_expr_kind_empty_str(expr_kind: &ExprKind<'_>) -> bool {
|
||||
if let ExprKind::Lit(lit) = expr_kind &&
|
||||
let LitKind::Str(value, _) = lit.node &&
|
||||
value == symbol::kw::Empty
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Emits the `MANUAL_EMPTY_STRING_CREATION` warning and suggests the appropriate fix.
|
||||
fn warn_then_suggest(cx: &LateContext<'_>, span: Span) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_EMPTY_STRING_CREATIONS,
|
||||
span,
|
||||
"empty String is being created manually",
|
||||
"consider using",
|
||||
"String::new()".into(),
|
||||
MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
/// Tries to parse an expression as a method call, emiting the warning if necessary.
|
||||
fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegment<'_>, args: &[Expr<'_>]) {
|
||||
if args.is_empty() {
|
||||
// When parsing TryFrom::try_from(...).expect(...), we will have more than 1 arg.
|
||||
return;
|
||||
}
|
||||
|
||||
let ident = path_segment.ident.as_str();
|
||||
let method_arg_kind = &args[0].kind;
|
||||
if ["to_string", "to_owned", "into"].contains(&ident) && is_expr_kind_empty_str(method_arg_kind) {
|
||||
warn_then_suggest(cx, span);
|
||||
} else if let ExprKind::Call(func, args) = method_arg_kind {
|
||||
// If our first argument is a function call itself, it could be an `unwrap`-like function.
|
||||
// E.g. String::try_from("hello").unwrap(), TryFrom::try_from("").expect("hello"), etc.
|
||||
parse_call(cx, span, func, args);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to parse an expression as a function call, emiting the warning if necessary.
|
||||
fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
if args.len() != 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
let arg_kind = &args[0].kind;
|
||||
if let ExprKind::Path(qpath) = &func.kind {
|
||||
if let QPath::TypeRelative(_, _) = qpath {
|
||||
// String::from(...) or String::try_from(...)
|
||||
if let QPath::TypeRelative(ty, path_seg) = qpath &&
|
||||
[sym::from, sym::try_from].contains(&path_seg.ident.name) &&
|
||||
let TyKind::Path(qpath) = &ty.kind &&
|
||||
let QPath::Resolved(_, path) = qpath &&
|
||||
let [path_seg] = path.segments &&
|
||||
path_seg.ident.name == sym::String &&
|
||||
is_expr_kind_empty_str(arg_kind)
|
||||
{
|
||||
warn_then_suggest(cx, span);
|
||||
}
|
||||
} else if let QPath::Resolved(_, path) = qpath {
|
||||
// From::from(...) or TryFrom::try_from(...)
|
||||
if let [path_seg1, path_seg2] = path.segments &&
|
||||
is_expr_kind_empty_str(arg_kind) && (
|
||||
(path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) ||
|
||||
(path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)
|
||||
)
|
||||
{
|
||||
warn_then_suggest(cx, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -78,7 +78,7 @@ pub(super) fn check<'tcx>(
|
||||
map_span,
|
||||
String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
|
||||
),
|
||||
(expr.span.with_lo(unwrap_recv.span.hi()), String::from("")),
|
||||
(expr.span.with_lo(unwrap_recv.span.hi()), String::new()),
|
||||
];
|
||||
|
||||
if !unwrap_snippet_none {
|
||||
|
@ -46,7 +46,7 @@ fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
|
||||
"these patterns are unneeded as the `..` pattern can match those elements"
|
||||
},
|
||||
if only_one { "remove it" } else { "remove them" },
|
||||
"".to_string(),
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
|
||||
(
|
||||
ret_expr.span,
|
||||
if inner_type.is_unit() {
|
||||
"".to_string()
|
||||
String::new()
|
||||
} else {
|
||||
snippet(cx, arg.span.source_callsite(), "..").to_string()
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ msrv_aliases! {
|
||||
1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
|
||||
1,28,0 { FROM_BOOL }
|
||||
1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
|
||||
1,24,0 { IS_ASCII_DIGIT }
|
||||
1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
|
||||
1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
|
||||
1,16,0 { STR_REPEAT }
|
||||
1,24,0 { IS_ASCII_DIGIT }
|
||||
}
|
||||
|
@ -14,31 +14,31 @@ fn is_rust_file(filename: &str) -> bool {
|
||||
|
||||
fn main() {
|
||||
// std::string::String and &str should trigger the lint failure with .ext12
|
||||
let _ = String::from("").ends_with(".ext12");
|
||||
let _ = String::new().ends_with(".ext12");
|
||||
let _ = "str".ends_with(".ext12");
|
||||
|
||||
// The test struct should not trigger the lint failure with .ext12
|
||||
TestStruct {}.ends_with(".ext12");
|
||||
|
||||
// std::string::String and &str should trigger the lint failure with .EXT12
|
||||
let _ = String::from("").ends_with(".EXT12");
|
||||
let _ = String::new().ends_with(".EXT12");
|
||||
let _ = "str".ends_with(".EXT12");
|
||||
|
||||
// The test struct should not trigger the lint failure with .EXT12
|
||||
TestStruct {}.ends_with(".EXT12");
|
||||
|
||||
// Should not trigger the lint failure with .eXT12
|
||||
let _ = String::from("").ends_with(".eXT12");
|
||||
let _ = String::new().ends_with(".eXT12");
|
||||
let _ = "str".ends_with(".eXT12");
|
||||
TestStruct {}.ends_with(".eXT12");
|
||||
|
||||
// Should not trigger the lint failure with .EXT123 (too long)
|
||||
let _ = String::from("").ends_with(".EXT123");
|
||||
let _ = String::new().ends_with(".EXT123");
|
||||
let _ = "str".ends_with(".EXT123");
|
||||
TestStruct {}.ends_with(".EXT123");
|
||||
|
||||
// Shouldn't fail if it doesn't start with a dot
|
||||
let _ = String::from("").ends_with("a.ext");
|
||||
let _ = String::new().ends_with("a.ext");
|
||||
let _ = "str".ends_with("a.extA");
|
||||
TestStruct {}.ends_with("a.ext");
|
||||
}
|
||||
|
@ -8,10 +8,10 @@ LL | filename.ends_with(".rs")
|
||||
= help: consider using a case-insensitive comparison instead
|
||||
|
||||
error: case-sensitive file extension comparison
|
||||
--> $DIR/case_sensitive_file_extension_comparisons.rs:17:30
|
||||
--> $DIR/case_sensitive_file_extension_comparisons.rs:17:27
|
||||
|
|
||||
LL | let _ = String::from("").ends_with(".ext12");
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
LL | let _ = String::new().ends_with(".ext12");
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider using a case-insensitive comparison instead
|
||||
|
||||
@ -24,10 +24,10 @@ LL | let _ = "str".ends_with(".ext12");
|
||||
= help: consider using a case-insensitive comparison instead
|
||||
|
||||
error: case-sensitive file extension comparison
|
||||
--> $DIR/case_sensitive_file_extension_comparisons.rs:24:30
|
||||
--> $DIR/case_sensitive_file_extension_comparisons.rs:24:27
|
||||
|
|
||||
LL | let _ = String::from("").ends_with(".EXT12");
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
LL | let _ = String::new().ends_with(".EXT12");
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider using a case-insensitive comparison instead
|
||||
|
||||
|
@ -33,7 +33,7 @@ fn main() {
|
||||
format!("foo {}", "bar");
|
||||
format!("{} bar", "foo");
|
||||
|
||||
let arg: String = "".to_owned();
|
||||
let arg = String::new();
|
||||
arg.to_string();
|
||||
format!("{:?}", arg); // Don't warn about debug.
|
||||
format!("{:8}", arg);
|
||||
|
@ -35,7 +35,7 @@ fn main() {
|
||||
format!("foo {}", "bar");
|
||||
format!("{} bar", "foo");
|
||||
|
||||
let arg: String = "".to_owned();
|
||||
let arg = String::new();
|
||||
format!("{}", arg);
|
||||
format!("{:?}", arg); // Don't warn about debug.
|
||||
format!("{:8}", arg);
|
||||
|
@ -68,7 +68,7 @@ fn main() {
|
||||
&x;
|
||||
x;
|
||||
|
||||
let mut a = A("".into());
|
||||
let mut a = A(String::new());
|
||||
let b = a << 0; // no error: non-integer
|
||||
|
||||
1 * Meter; // no error: non-integer
|
||||
|
@ -68,7 +68,7 @@ fn main() {
|
||||
&x >> 0;
|
||||
x >> &0;
|
||||
|
||||
let mut a = A("".into());
|
||||
let mut a = A(String::new());
|
||||
let b = a << 0; // no error: non-integer
|
||||
|
||||
1 * Meter; // no error: non-integer
|
||||
|
63
tests/ui/manual_empty_string_creations.fixed
Normal file
63
tests/ui/manual_empty_string_creations.fixed
Normal file
@ -0,0 +1,63 @@
|
||||
// run-rustfix
|
||||
|
||||
#![warn(clippy::manual_empty_string_creations)]
|
||||
|
||||
macro_rules! create_strings_from_macro {
|
||||
// When inside a macro, nothing should warn to prevent false positives.
|
||||
($some_str:expr) => {
|
||||
let _: String = $some_str.into();
|
||||
let _ = $some_str.to_string();
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Method calls
|
||||
let _ = String::new();
|
||||
let _ = "no warning".to_string();
|
||||
|
||||
let _ = String::new();
|
||||
let _ = "no warning".to_owned();
|
||||
|
||||
let _: String = String::new();
|
||||
let _: String = "no warning".into();
|
||||
|
||||
let _: SomeOtherStruct = "no warning".into();
|
||||
let _: SomeOtherStruct = "".into(); // No warning too. We are not converting into String.
|
||||
|
||||
// Calls
|
||||
let _ = String::new();
|
||||
let _ = String::new();
|
||||
let _ = String::from("no warning");
|
||||
let _ = SomeOtherStruct::from("no warning");
|
||||
let _ = SomeOtherStruct::from(""); // Again: no warning.
|
||||
|
||||
let _ = String::new();
|
||||
let _ = String::try_from("no warning").unwrap();
|
||||
let _ = String::try_from("no warning").expect("this should not warn");
|
||||
let _ = SomeOtherStruct::try_from("no warning").unwrap();
|
||||
let _ = SomeOtherStruct::try_from("").unwrap(); // Again: no warning.
|
||||
|
||||
let _: String = String::new();
|
||||
let _: String = From::from("no warning");
|
||||
let _: SomeOtherStruct = From::from("no warning");
|
||||
let _: SomeOtherStruct = From::from(""); // Again: no warning.
|
||||
|
||||
let _: String = String::new();
|
||||
let _: String = TryFrom::try_from("no warning").unwrap();
|
||||
let _: String = TryFrom::try_from("no warning").expect("this should not warn");
|
||||
let _: String = String::new();
|
||||
let _: SomeOtherStruct = TryFrom::try_from("no_warning").unwrap();
|
||||
let _: SomeOtherStruct = TryFrom::try_from("").unwrap(); // Again: no warning.
|
||||
|
||||
// Macros (never warn)
|
||||
create_strings_from_macro!("");
|
||||
create_strings_from_macro!("Hey");
|
||||
}
|
||||
|
||||
struct SomeOtherStruct {}
|
||||
|
||||
impl From<&str> for SomeOtherStruct {
|
||||
fn from(_value: &str) -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
63
tests/ui/manual_empty_string_creations.rs
Normal file
63
tests/ui/manual_empty_string_creations.rs
Normal file
@ -0,0 +1,63 @@
|
||||
// run-rustfix
|
||||
|
||||
#![warn(clippy::manual_empty_string_creations)]
|
||||
|
||||
macro_rules! create_strings_from_macro {
|
||||
// When inside a macro, nothing should warn to prevent false positives.
|
||||
($some_str:expr) => {
|
||||
let _: String = $some_str.into();
|
||||
let _ = $some_str.to_string();
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Method calls
|
||||
let _ = "".to_string();
|
||||
let _ = "no warning".to_string();
|
||||
|
||||
let _ = "".to_owned();
|
||||
let _ = "no warning".to_owned();
|
||||
|
||||
let _: String = "".into();
|
||||
let _: String = "no warning".into();
|
||||
|
||||
let _: SomeOtherStruct = "no warning".into();
|
||||
let _: SomeOtherStruct = "".into(); // No warning too. We are not converting into String.
|
||||
|
||||
// Calls
|
||||
let _ = String::from("");
|
||||
let _ = <String>::from("");
|
||||
let _ = String::from("no warning");
|
||||
let _ = SomeOtherStruct::from("no warning");
|
||||
let _ = SomeOtherStruct::from(""); // Again: no warning.
|
||||
|
||||
let _ = String::try_from("").unwrap();
|
||||
let _ = String::try_from("no warning").unwrap();
|
||||
let _ = String::try_from("no warning").expect("this should not warn");
|
||||
let _ = SomeOtherStruct::try_from("no warning").unwrap();
|
||||
let _ = SomeOtherStruct::try_from("").unwrap(); // Again: no warning.
|
||||
|
||||
let _: String = From::from("");
|
||||
let _: String = From::from("no warning");
|
||||
let _: SomeOtherStruct = From::from("no warning");
|
||||
let _: SomeOtherStruct = From::from(""); // Again: no warning.
|
||||
|
||||
let _: String = TryFrom::try_from("").unwrap();
|
||||
let _: String = TryFrom::try_from("no warning").unwrap();
|
||||
let _: String = TryFrom::try_from("no warning").expect("this should not warn");
|
||||
let _: String = TryFrom::try_from("").expect("this should warn");
|
||||
let _: SomeOtherStruct = TryFrom::try_from("no_warning").unwrap();
|
||||
let _: SomeOtherStruct = TryFrom::try_from("").unwrap(); // Again: no warning.
|
||||
|
||||
// Macros (never warn)
|
||||
create_strings_from_macro!("");
|
||||
create_strings_from_macro!("Hey");
|
||||
}
|
||||
|
||||
struct SomeOtherStruct {}
|
||||
|
||||
impl From<&str> for SomeOtherStruct {
|
||||
fn from(_value: &str) -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
58
tests/ui/manual_empty_string_creations.stderr
Normal file
58
tests/ui/manual_empty_string_creations.stderr
Normal file
@ -0,0 +1,58 @@
|
||||
error: empty String is being created manually
|
||||
--> $DIR/manual_empty_string_creations.rs:15:13
|
||||
|
|
||||
LL | let _ = "".to_string();
|
||||
| ^^^^^^^^^^^^^^ help: consider using: `String::new()`
|
||||
|
|
||||
= note: `-D clippy::manual-empty-string-creations` implied by `-D warnings`
|
||||
|
||||
error: empty String is being created manually
|
||||
--> $DIR/manual_empty_string_creations.rs:18:13
|
||||
|
|
||||
LL | let _ = "".to_owned();
|
||||
| ^^^^^^^^^^^^^ help: consider using: `String::new()`
|
||||
|
||||
error: empty String is being created manually
|
||||
--> $DIR/manual_empty_string_creations.rs:21:21
|
||||
|
|
||||
LL | let _: String = "".into();
|
||||
| ^^^^^^^^^ help: consider using: `String::new()`
|
||||
|
||||
error: empty String is being created manually
|
||||
--> $DIR/manual_empty_string_creations.rs:28:13
|
||||
|
|
||||
LL | let _ = String::from("");
|
||||
| ^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
|
||||
|
||||
error: empty String is being created manually
|
||||
--> $DIR/manual_empty_string_creations.rs:29:13
|
||||
|
|
||||
LL | let _ = <String>::from("");
|
||||
| ^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
|
||||
|
||||
error: empty String is being created manually
|
||||
--> $DIR/manual_empty_string_creations.rs:34:13
|
||||
|
|
||||
LL | let _ = String::try_from("").unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
|
||||
|
||||
error: empty String is being created manually
|
||||
--> $DIR/manual_empty_string_creations.rs:40:21
|
||||
|
|
||||
LL | let _: String = From::from("");
|
||||
| ^^^^^^^^^^^^^^ help: consider using: `String::new()`
|
||||
|
||||
error: empty String is being created manually
|
||||
--> $DIR/manual_empty_string_creations.rs:45:21
|
||||
|
|
||||
LL | let _: String = TryFrom::try_from("").unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
|
||||
|
||||
error: empty String is being created manually
|
||||
--> $DIR/manual_empty_string_creations.rs:48:21
|
||||
|
|
||||
LL | let _: String = TryFrom::try_from("").expect("this should warn");
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
|
@ -90,8 +90,8 @@ fn or_fun_call() {
|
||||
let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
|
||||
btree_vec.entry(42).or_insert(vec![]);
|
||||
|
||||
let stringy = Some(String::from(""));
|
||||
let _ = stringy.unwrap_or_else(|| "".to_owned());
|
||||
let stringy = Some(String::new());
|
||||
let _ = stringy.unwrap_or_default();
|
||||
|
||||
let opt = Some(1);
|
||||
let hello = "Hello";
|
||||
|
@ -90,8 +90,8 @@ fn or_fun_call() {
|
||||
let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
|
||||
btree_vec.entry(42).or_insert(vec![]);
|
||||
|
||||
let stringy = Some(String::from(""));
|
||||
let _ = stringy.unwrap_or("".to_owned());
|
||||
let stringy = Some(String::new());
|
||||
let _ = stringy.unwrap_or(String::new());
|
||||
|
||||
let opt = Some(1);
|
||||
let hello = "Hello";
|
||||
|
@ -66,11 +66,11 @@ error: use of `unwrap_or` followed by a function call
|
||||
LL | without_default.unwrap_or(Foo::new());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
|
||||
|
||||
error: use of `unwrap_or` followed by a function call
|
||||
error: use of `unwrap_or` followed by a call to `new`
|
||||
--> $DIR/or_fun_call.rs:94:21
|
||||
|
|
||||
LL | let _ = stringy.unwrap_or("".to_owned());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())`
|
||||
LL | let _ = stringy.unwrap_or(String::new());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
|
||||
|
||||
error: use of `unwrap_or` followed by a function call
|
||||
--> $DIR/or_fun_call.rs:102:21
|
||||
|
@ -7,13 +7,13 @@ extern crate macro_rules;
|
||||
#[allow(clippy::string_add_assign, unused)]
|
||||
fn main() {
|
||||
// ignores assignment distinction
|
||||
let mut x = "".to_owned();
|
||||
let mut x = String::new();
|
||||
|
||||
for _ in 1..3 {
|
||||
x = x + ".";
|
||||
}
|
||||
|
||||
let y = "".to_owned();
|
||||
let y = String::new();
|
||||
let z = y + "...";
|
||||
|
||||
assert_eq!(&x, &z);
|
||||
|
@ -4,13 +4,13 @@
|
||||
#[warn(clippy::string_add_assign)]
|
||||
fn main() {
|
||||
// ignores assignment distinction
|
||||
let mut x = "".to_owned();
|
||||
let mut x = String::new();
|
||||
|
||||
for _ in 1..3 {
|
||||
x += ".";
|
||||
}
|
||||
|
||||
let y = "".to_owned();
|
||||
let y = String::new();
|
||||
let z = y + "...";
|
||||
|
||||
assert_eq!(&x, &z);
|
||||
|
@ -4,13 +4,13 @@
|
||||
#[warn(clippy::string_add_assign)]
|
||||
fn main() {
|
||||
// ignores assignment distinction
|
||||
let mut x = "".to_owned();
|
||||
let mut x = String::new();
|
||||
|
||||
for _ in 1..3 {
|
||||
x = x + ".";
|
||||
}
|
||||
|
||||
let y = "".to_owned();
|
||||
let y = String::new();
|
||||
let z = y + "...";
|
||||
|
||||
assert_eq!(&x, &z);
|
||||
|
@ -12,6 +12,7 @@ fn main() {
|
||||
ref_str_argument("");
|
||||
|
||||
// should be linted
|
||||
#[allow(clippy::manual_empty_string_creations)]
|
||||
ref_str_argument("");
|
||||
|
||||
// should not be linted
|
||||
|
@ -12,6 +12,7 @@ fn main() {
|
||||
ref_str_argument(&String::new());
|
||||
|
||||
// should be linted
|
||||
#[allow(clippy::manual_empty_string_creations)]
|
||||
ref_str_argument(&String::from(""));
|
||||
|
||||
// should not be linted
|
||||
|
@ -7,7 +7,7 @@ LL | ref_str_argument(&String::new());
|
||||
= note: `-D clippy::unnecessary-owned-empty-strings` implied by `-D warnings`
|
||||
|
||||
error: usage of `&String::from("")` for a function expecting a `&str` argument
|
||||
--> $DIR/unnecessary_owned_empty_strings.rs:15:22
|
||||
--> $DIR/unnecessary_owned_empty_strings.rs:16:22
|
||||
|
|
||||
LL | ref_str_argument(&String::from(""));
|
||||
| ^^^^^^^^^^^^^^^^^ help: try: `""`
|
||||
|
@ -29,10 +29,10 @@ fn main() {
|
||||
let _ = String::try_from("foo".to_string()).unwrap();
|
||||
let _ = String::try_from(format!("A: {:04}", 123)).unwrap();
|
||||
let _: String = format!("Hello {}", "world").try_into().unwrap();
|
||||
let _: String = "".to_owned().try_into().unwrap();
|
||||
let _: String = String::new().try_into().unwrap();
|
||||
let _: String = match String::from("_").try_into() {
|
||||
Ok(a) => a,
|
||||
Err(_) => "".into(),
|
||||
Err(_) => String::new(),
|
||||
};
|
||||
// FIXME this is a false negative
|
||||
#[allow(clippy::cmp_owned)]
|
||||
|
@ -62,7 +62,7 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap();
|
||||
error: useless conversion to the same type: `std::string::String`
|
||||
--> $DIR/useless_conversion_try.rs:32:21
|
||||
|
|
||||
LL | let _: String = "".to_owned().try_into().unwrap();
|
||||
LL | let _: String = String::new().try_into().unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider removing `.try_into()`
|
||||
|
Loading…
x
Reference in New Issue
Block a user