Skip to content

Commit 487bf5f

Browse files
committed
new lint needless_raw_string + refactor a bit
Thanks, #112373, for the snippet at line 75!
1 parent 14f8e3b commit 487bf5f

24 files changed

+256
-139
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5012,6 +5012,7 @@ Released 2018-09-13
50125012
[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
50135013
[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
50145014
[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
5015+
[`needless_raw_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string
50155016
[`needless_raw_string_hashes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes
50165017
[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
50175018
[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn

clippy_lints/src/declared_lints.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
456456
crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
457457
crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,
458458
crate::needless_question_mark::NEEDLESS_QUESTION_MARK_INFO,
459-
crate::needless_raw_string_hashes::NEEDLESS_RAW_STRING_HASHES_INFO,
460459
crate::needless_update::NEEDLESS_UPDATE_INFO,
461460
crate::neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD_INFO,
462461
crate::neg_multiply::NEG_MULTIPLY_INFO,
@@ -528,6 +527,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
528527
crate::ranges::RANGE_MINUS_ONE_INFO,
529528
crate::ranges::RANGE_PLUS_ONE_INFO,
530529
crate::ranges::REVERSED_EMPTY_RANGES_INFO,
530+
crate::raw_strings::NEEDLESS_RAW_STRING_INFO,
531+
crate::raw_strings::NEEDLESS_RAW_STRING_HASHES_INFO,
531532
crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO,
532533
crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO,
533534
crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO,

clippy_lints/src/lib.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,6 @@ mod needless_late_init;
224224
mod needless_parens_on_range_literals;
225225
mod needless_pass_by_value;
226226
mod needless_question_mark;
227-
mod needless_raw_string_hashes;
228227
mod needless_update;
229228
mod neg_cmp_op_on_partial_ord;
230229
mod neg_multiply;
@@ -257,6 +256,7 @@ mod pub_use;
257256
mod question_mark;
258257
mod question_mark_used;
259258
mod ranges;
259+
mod raw_strings;
260260
mod rc_clone_in_vec_init;
261261
mod read_zero_byte_vec;
262262
mod redundant_async_block;
@@ -1008,7 +1008,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10081008
store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs));
10091009
store.register_early_pass(|| Box::new(needless_else::NeedlessElse));
10101010
store.register_late_pass(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug));
1011-
store.register_early_pass(|| Box::new(needless_raw_string_hashes::NeedlessRawStringHashes));
1011+
let needless_raw_string_hashes_allow_one = conf.allow_one_hash_in_raw_string;
1012+
store.register_early_pass(move || {
1013+
Box::new(raw_strings::RawStrings {
1014+
needless_raw_string_hashes_allow_one,
1015+
})
1016+
});
10121017
// add lints here, do not remove this comment, it's used in `new_lint`
10131018
}
10141019

clippy_lints/src/needless_raw_string_hashes.rs

-73
This file was deleted.

clippy_lints/src/raw_strings.rs

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
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_tool_lint, impl_lint_pass};
10+
11+
declare_clippy_lint! {
12+
/// ### What it does
13+
/// Checks for raw string literals where a string literal can be used instead.
14+
///
15+
/// ### Why is this bad?
16+
/// It's just unnecessary.
17+
///
18+
/// ### Example
19+
/// ```rust
20+
/// let r = r"Hello, world!";
21+
/// ```
22+
/// Use instead:
23+
/// ```rust
24+
/// let r = "Hello, "world"!";
25+
/// ```
26+
#[clippy::version = "1.72.0"]
27+
pub NEEDLESS_RAW_STRING,
28+
complexity,
29+
"suggests using a string literal when a raw string literal is unnecessary"
30+
}
31+
declare_clippy_lint! {
32+
/// ### What it does
33+
/// Checks for raw string literals with an unnecessary amount of hashes around them.
34+
///
35+
/// ### Why is this bad?
36+
/// It's just unnecessary, and makes it look like there's more escaping needed than is actually
37+
/// necessary.
38+
///
39+
/// ### Example
40+
/// ```rust
41+
/// let r = r###"Hello, "world"!"###;
42+
/// ```
43+
/// Use instead:
44+
/// ```rust
45+
/// let r = r#"Hello, "world"!"#;
46+
/// ```
47+
#[clippy::version = "1.72.0"]
48+
pub NEEDLESS_RAW_STRING_HASHES,
49+
complexity,
50+
"suggests reducing the number of hashes around a raw string literal"
51+
}
52+
impl_lint_pass!(RawStrings => [NEEDLESS_RAW_STRING, NEEDLESS_RAW_STRING_HASHES]);
53+
54+
pub struct RawStrings {
55+
pub needless_raw_string_hashes_allow_one: bool,
56+
}
57+
58+
impl EarlyLintPass for RawStrings {
59+
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
60+
if !in_external_macro(cx.sess(), expr.span)
61+
&& let ExprKind::Lit(lit) = expr.kind
62+
&& let LitKind::StrRaw(num) | LitKind::ByteStrRaw(num) | LitKind::CStrRaw(num) = lit.kind
63+
{
64+
let prefix = match lit.kind {
65+
LitKind::StrRaw(..) => "r",
66+
LitKind::ByteStrRaw(..) => "br",
67+
LitKind::CStrRaw(..) => "cr",
68+
_ => unreachable!(),
69+
};
70+
if !snippet(cx, expr.span, prefix).trim().starts_with(prefix) {
71+
return;
72+
}
73+
74+
#[allow(clippy::cast_possible_truncation)]
75+
let req = lit.symbol.as_str().as_bytes()
76+
.split(|&b| b == b'"')
77+
.skip(1)
78+
.map(|bs| 1 + bs.iter().take_while(|&&b| b == b'#').count() as u8)
79+
.max()
80+
.unwrap_or(0);
81+
82+
if req < num {
83+
let hashes = "#".repeat(req as usize);
84+
85+
span_lint_and_sugg(
86+
cx,
87+
NEEDLESS_RAW_STRING_HASHES,
88+
expr.span,
89+
"unnecessary hashes around raw string literal",
90+
"try",
91+
format!(r#"{prefix}{hashes}"{}"{hashes}"#, lit.symbol),
92+
Applicability::MachineApplicable,
93+
);
94+
}
95+
96+
if !lit.symbol.as_str().contains(['\\', '"']) {
97+
span_lint_and_sugg(
98+
cx,
99+
NEEDLESS_RAW_STRING,
100+
expr.span,
101+
"unnecessary raw string literal",
102+
"try",
103+
format!("{}\"{}\"", prefix.replace('r', ""), lit.symbol),
104+
Applicability::MachineApplicable,
105+
);
106+
}
107+
}
108+
}
109+
}

clippy_lints/src/utils/conf.rs

+4
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,10 @@ define_Conf! {
514514
///
515515
/// The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint
516516
(unnecessary_box_size: u64 = 128),
517+
/// Lint: UNNECESSARY_RAW_STRING_HASHES.
518+
///
519+
/// Whether to allow `r#""#` when `r""` can be used
520+
(allow_one_hash_in_raw_string: bool = false),
517521
}
518522

519523
/// Search for the configuration file.

src/main.rs

+1-1
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 = "Checks a package to catch common mistakes and improve your Rust code.
1010
1111
Usage:
1212
cargo clippy [options] [--] [<opts>...]

tests/lint_message_convention.rs

+15-15
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@ impl Message {
2020
// also no punctuation (except for "?" ?) at the end of a line
2121
static REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {
2222
RegexSet::new([
23-
r"error: [A-Z]",
24-
r"help: [A-Z]",
25-
r"warning: [A-Z]",
26-
r"note: [A-Z]",
27-
r"try this: [A-Z]",
28-
r"error: .*[.!]$",
29-
r"help: .*[.!]$",
30-
r"warning: .*[.!]$",
31-
r"note: .*[.!]$",
32-
r"try this: .*[.!]$",
23+
"error: [A-Z]",
24+
"help: [A-Z]",
25+
"warning: [A-Z]",
26+
"note: [A-Z]",
27+
"try this: [A-Z]",
28+
"error: .*[.!]$",
29+
"help: .*[.!]$",
30+
"warning: .*[.!]$",
31+
"note: .*[.!]$",
32+
"try this: .*[.!]$",
3333
])
3434
.unwrap()
3535
});
@@ -39,11 +39,11 @@ impl Message {
3939
static EXCEPTIONS_SET: LazyLock<RegexSet> = LazyLock::new(|| {
4040
RegexSet::new([
4141
r"\.\.\.$",
42-
r".*C-like enum variant discriminant is not portable to 32-bit targets",
43-
r".*Intel x86 assembly syntax used",
44-
r".*AT&T x86 assembly syntax used",
45-
r"note: Clippy version: .*",
46-
r"the compiler unexpectedly panicked. this is a bug.",
42+
".*C-like enum variant discriminant is not portable to 32-bit targets",
43+
".*Intel x86 assembly syntax used",
44+
".*AT&T x86 assembly syntax used",
45+
"note: Clippy version: .*",
46+
"the compiler unexpectedly panicked. this is a bug.",
4747
])
4848
.unwrap()
4949
});

tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@compile-flags: --crate-name conf_disallowed_methods
22

3+
#![allow(clippy::needless_raw_string)]
34
#![warn(clippy::disallowed_methods)]
45

56
extern crate futures;

0 commit comments

Comments
 (0)