Skip to content

Commit 1bf74fc

Browse files
committed
add needless_raw_string_hashes lint
add semicolon in doctest
1 parent ecdea8c commit 1bf74fc

18 files changed

+216
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5048,6 +5048,7 @@ Released 2018-09-13
50485048
[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
50495049
[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
50505050
[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
5051+
[`needless_raw_string_hashes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes
50515052
[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
50525053
[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
50535054
[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
469469
crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
470470
crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,
471471
crate::needless_question_mark::NEEDLESS_QUESTION_MARK_INFO,
472+
crate::needless_raw_string_hashes::NEEDLESS_RAW_STRING_HASHES_INFO,
472473
crate::needless_update::NEEDLESS_UPDATE_INFO,
473474
crate::neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD_INFO,
474475
crate::neg_multiply::NEG_MULTIPLY_INFO,

clippy_lints/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ mod needless_late_init;
230230
mod needless_parens_on_range_literals;
231231
mod needless_pass_by_value;
232232
mod needless_question_mark;
233+
mod needless_raw_string_hashes;
233234
mod needless_update;
234235
mod neg_cmp_op_on_partial_ord;
235236
mod neg_multiply;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use rustc_ast::{
3+
ast::{Expr, ExprKind},
4+
token::LitKind,
5+
};
6+
use rustc_errors::Applicability;
7+
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
8+
use rustc_middle::lint::in_external_macro;
9+
use rustc_session::{declare_lint_pass, declare_tool_lint};
10+
11+
declare_clippy_lint! {
12+
/// ### What it does
13+
/// Checks for raw string literals with an unnecessary amount of hashes around them.
14+
///
15+
/// ### Why is this bad?
16+
/// It's just unnecessary, and makes it look like there's more escaping needed than is actually
17+
/// necessary.
18+
///
19+
/// ### Example
20+
/// ```rust
21+
/// let r = r###"Hello, "world"!"###;
22+
/// ```
23+
/// Use instead:
24+
/// ```rust
25+
/// let r = r#"Hello, "world"!"#;
26+
/// ```
27+
#[clippy::version = "1.72.0"]
28+
pub NEEDLESS_RAW_STRING_HASHES,
29+
complexity,
30+
"suggests reducing the number of hashes around a raw string literal"
31+
}
32+
declare_lint_pass!(NeedlessRawStringHashes => [NEEDLESS_RAW_STRING_HASHES]);
33+
34+
impl EarlyLintPass for NeedlessRawStringHashes {
35+
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
36+
if_chain! {
37+
if !in_external_macro(cx.sess(), expr.span);
38+
if let ExprKind::Lit(lit) = expr.kind;
39+
if let LitKind::StrRaw(num) | LitKind::ByteStrRaw(num) | LitKind::CStrRaw(num) = lit.kind;
40+
then {
41+
let str = lit.symbol.as_str();
42+
let mut lowest = 0;
43+
44+
for i in (0..num).rev() {
45+
if str.contains(&format!("\"{}", "#".repeat(i as usize))) {
46+
lowest = i + 1;
47+
break;
48+
}
49+
}
50+
51+
if lowest < num {
52+
let hashes = "#".repeat(lowest as usize);
53+
let prefix = match lit.kind {
54+
LitKind::StrRaw(..) => "r",
55+
LitKind::ByteStrRaw(..) => "br",
56+
LitKind::CStrRaw(..) => "cr",
57+
_ => unreachable!(),
58+
};
59+
60+
span_lint_and_sugg(
61+
cx,
62+
NEEDLESS_RAW_STRING_HASHES,
63+
expr.span,
64+
"unnecessary hashes around raw string literal",
65+
"try",
66+
format!(r#"{prefix}{hashes}"{}"{hashes}"#, lit.symbol),
67+
Applicability::MachineApplicable,
68+
);
69+
}
70+
}
71+
}
72+
}
73+
}

src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::env;
66
use std::path::PathBuf;
77
use std::process::{self, Command};
88

9-
const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.
9+
const CARGO_CLIPPY_HELP: &str = r"Checks a package to catch common mistakes and improve your Rust code.
1010
1111
Usage:
1212
cargo clippy [options] [--] [<opts>...]
@@ -31,7 +31,7 @@ with:
3131
You can use tool lints to allow or deny lints from your code, e.g.:
3232
3333
#[allow(clippy::needless_lifetimes)]
34-
"#;
34+
";
3535

3636
fn show_help() {
3737
println!("{CARGO_CLIPPY_HELP}");

tests/ui/format.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
clippy::to_string_in_format_args,
88
clippy::needless_borrow,
99
clippy::uninlined_format_args,
10+
clippy::needless_raw_string_hashes,
1011
clippy::useless_vec
1112
)]
1213

tests/ui/format.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
clippy::to_string_in_format_args,
88
clippy::needless_borrow,
99
clippy::uninlined_format_args,
10+
clippy::needless_raw_string_hashes,
1011
clippy::useless_vec
1112
)]
1213

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@run-rustfix
2+
#![allow(clippy::no_effect, unused)]
3+
#![warn(clippy::needless_raw_string_hashes)]
4+
#![feature(c_str_literals)]
5+
6+
fn main() {
7+
r"aaa";
8+
r#"Hello "world"!"#;
9+
r####" "### "## "# "####;
10+
r###" "aa" "# "## "###;
11+
br"aaa";
12+
br#"Hello "world"!"#;
13+
br####" "### "## "# "####;
14+
br###" "aa" "# "## "###;
15+
cr"aaa";
16+
cr#"Hello "world"!"#;
17+
cr####" "### "## "# "####;
18+
cr###" "aa" "# "## "###;
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@run-rustfix
2+
#![allow(clippy::no_effect, unused)]
3+
#![warn(clippy::needless_raw_string_hashes)]
4+
#![feature(c_str_literals)]
5+
6+
fn main() {
7+
r#"aaa"#;
8+
r##"Hello "world"!"##;
9+
r######" "### "## "# "######;
10+
r######" "aa" "# "## "######;
11+
br#"aaa"#;
12+
br##"Hello "world"!"##;
13+
br######" "### "## "# "######;
14+
br######" "aa" "# "## "######;
15+
cr#"aaa"#;
16+
cr##"Hello "world"!"##;
17+
cr######" "### "## "# "######;
18+
cr######" "aa" "# "## "######;
19+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
error: unnecessary hashes around raw string literal
2+
--> $DIR/needless_raw_string_hashes.rs:7:5
3+
|
4+
LL | r#"aaa"#;
5+
| ^^^^^^^^ help: try: `r"aaa"`
6+
|
7+
= note: `-D clippy::needless-raw-string-hashes` implied by `-D warnings`
8+
9+
error: unnecessary hashes around raw string literal
10+
--> $DIR/needless_raw_string_hashes.rs:8:5
11+
|
12+
LL | r##"Hello "world"!"##;
13+
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `r#"Hello "world"!"#`
14+
15+
error: unnecessary hashes around raw string literal
16+
--> $DIR/needless_raw_string_hashes.rs:9:5
17+
|
18+
LL | r######" "### "## "# "######;
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r####" "### "## "# "####`
20+
21+
error: unnecessary hashes around raw string literal
22+
--> $DIR/needless_raw_string_hashes.rs:10:5
23+
|
24+
LL | r######" "aa" "# "## "######;
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r###" "aa" "# "## "###`
26+
27+
error: unnecessary hashes around raw string literal
28+
--> $DIR/needless_raw_string_hashes.rs:11:5
29+
|
30+
LL | br#"aaa"#;
31+
| ^^^^^^^^^ help: try: `br"aaa"`
32+
33+
error: unnecessary hashes around raw string literal
34+
--> $DIR/needless_raw_string_hashes.rs:12:5
35+
|
36+
LL | br##"Hello "world"!"##;
37+
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `br#"Hello "world"!"#`
38+
39+
error: unnecessary hashes around raw string literal
40+
--> $DIR/needless_raw_string_hashes.rs:13:5
41+
|
42+
LL | br######" "### "## "# "######;
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `br####" "### "## "# "####`
44+
45+
error: unnecessary hashes around raw string literal
46+
--> $DIR/needless_raw_string_hashes.rs:14:5
47+
|
48+
LL | br######" "aa" "# "## "######;
49+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `br###" "aa" "# "## "###`
50+
51+
error: unnecessary hashes around raw string literal
52+
--> $DIR/needless_raw_string_hashes.rs:15:5
53+
|
54+
LL | cr#"aaa"#;
55+
| ^^^^^^^^^ help: try: `cr"aaa"`
56+
57+
error: unnecessary hashes around raw string literal
58+
--> $DIR/needless_raw_string_hashes.rs:16:5
59+
|
60+
LL | cr##"Hello "world"!"##;
61+
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `cr#"Hello "world"!"#`
62+
63+
error: unnecessary hashes around raw string literal
64+
--> $DIR/needless_raw_string_hashes.rs:17:5
65+
|
66+
LL | cr######" "### "## "# "######;
67+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cr####" "### "## "# "####`
68+
69+
error: unnecessary hashes around raw string literal
70+
--> $DIR/needless_raw_string_hashes.rs:18:5
71+
|
72+
LL | cr######" "aa" "# "## "######;
73+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cr###" "aa" "# "## "###`
74+
75+
error: aborting due to 12 previous errors
76+

tests/ui/regex.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![allow(unused, clippy::needless_borrow)]
1+
#![allow(unused, clippy::needless_raw_string_hashes, clippy::needless_borrow)]
22
#![warn(clippy::invalid_regex, clippy::trivial_regex)]
33

44
extern crate regex;

tests/ui/single_char_add_str.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@run-rustfix
22
#![warn(clippy::single_char_add_str)]
3+
#![allow(clippy::needless_raw_string_hashes)]
34

45
macro_rules! get_string {
56
() => {

tests/ui/single_char_add_str.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@run-rustfix
22
#![warn(clippy::single_char_add_str)]
3+
#![allow(clippy::needless_raw_string_hashes)]
34

45
macro_rules! get_string {
56
() => {

tests/ui/single_char_add_str.stderr

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,91 @@
11
error: calling `push_str()` using a single-character string literal
2-
--> $DIR/single_char_add_str.rs:14:5
2+
--> $DIR/single_char_add_str.rs:15:5
33
|
44
LL | string.push_str("R");
55
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('R')`
66
|
77
= note: `-D clippy::single-char-add-str` implied by `-D warnings`
88

99
error: calling `push_str()` using a single-character string literal
10-
--> $DIR/single_char_add_str.rs:15:5
10+
--> $DIR/single_char_add_str.rs:16:5
1111
|
1212
LL | string.push_str("'");
1313
| ^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/'')`
1414

1515
error: calling `push_str()` using a single-character string literal
16-
--> $DIR/single_char_add_str.rs:20:5
16+
--> $DIR/single_char_add_str.rs:21:5
1717
|
1818
LL | string.push_str("/x52");
1919
| ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/x52')`
2020

2121
error: calling `push_str()` using a single-character string literal
22-
--> $DIR/single_char_add_str.rs:21:5
22+
--> $DIR/single_char_add_str.rs:22:5
2323
|
2424
LL | string.push_str("/u{0052}");
2525
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('/u{0052}')`
2626

2727
error: calling `push_str()` using a single-character string literal
28-
--> $DIR/single_char_add_str.rs:22:5
28+
--> $DIR/single_char_add_str.rs:23:5
2929
|
3030
LL | string.push_str(r##"a"##);
3131
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `string.push('a')`
3232

3333
error: calling `push_str()` using a single-character string literal
34-
--> $DIR/single_char_add_str.rs:24:5
34+
--> $DIR/single_char_add_str.rs:25:5
3535
|
3636
LL | get_string!().push_str("ö");
3737
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `push` with a character literal: `get_string!().push('ö')`
3838

3939
error: calling `insert_str()` using a single-character string literal
40-
--> $DIR/single_char_add_str.rs:29:5
40+
--> $DIR/single_char_add_str.rs:30:5
4141
|
4242
LL | string.insert_str(0, "R");
4343
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, 'R')`
4444

4545
error: calling `insert_str()` using a single-character string literal
46-
--> $DIR/single_char_add_str.rs:30:5
46+
--> $DIR/single_char_add_str.rs:31:5
4747
|
4848
LL | string.insert_str(1, "'");
4949
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(1, '/'')`
5050

5151
error: calling `insert_str()` using a single-character string literal
52-
--> $DIR/single_char_add_str.rs:35:5
52+
--> $DIR/single_char_add_str.rs:36:5
5353
|
5454
LL | string.insert_str(0, "/x52");
5555
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/x52')`
5656

5757
error: calling `insert_str()` using a single-character string literal
58-
--> $DIR/single_char_add_str.rs:36:5
58+
--> $DIR/single_char_add_str.rs:37:5
5959
|
6060
LL | string.insert_str(0, "/u{0052}");
6161
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(0, '/u{0052}')`
6262

6363
error: calling `insert_str()` using a single-character string literal
64-
--> $DIR/single_char_add_str.rs:38:5
64+
--> $DIR/single_char_add_str.rs:39:5
6565
|
6666
LL | string.insert_str(x, r##"a"##);
6767
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(x, 'a')`
6868

6969
error: calling `insert_str()` using a single-character string literal
70-
--> $DIR/single_char_add_str.rs:40:5
70+
--> $DIR/single_char_add_str.rs:41:5
7171
|
7272
LL | string.insert_str(Y, r##"a"##);
7373
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, 'a')`
7474

7575
error: calling `insert_str()` using a single-character string literal
76-
--> $DIR/single_char_add_str.rs:41:5
76+
--> $DIR/single_char_add_str.rs:42:5
7777
|
7878
LL | string.insert_str(Y, r##"""##);
7979
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '"')`
8080

8181
error: calling `insert_str()` using a single-character string literal
82-
--> $DIR/single_char_add_str.rs:42:5
82+
--> $DIR/single_char_add_str.rs:43:5
8383
|
8484
LL | string.insert_str(Y, r##"'"##);
8585
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `string.insert(Y, '/'')`
8686

8787
error: calling `insert_str()` using a single-character string literal
88-
--> $DIR/single_char_add_str.rs:44:5
88+
--> $DIR/single_char_add_str.rs:45:5
8989
|
9090
LL | get_string!().insert_str(1, "?");
9191
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `insert` with a character literal: `get_string!().insert(1, '?')`

tests/ui/single_char_pattern.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@run-rustfix
22

3-
#![allow(unused_must_use)]
3+
#![allow(clippy::needless_raw_string_hashes, unused_must_use)]
44

55
use std::collections::HashSet;
66

tests/ui/single_char_pattern.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@run-rustfix
22

3-
#![allow(unused_must_use)]
3+
#![allow(clippy::needless_raw_string_hashes, unused_must_use)]
44

55
use std::collections::HashSet;
66

0 commit comments

Comments
 (0)