new lint needless_raw_string + refactor a bit

Thanks, #112373, for the snippet at line 75!
This commit is contained in:
Centri3 2023-06-08 06:55:47 -05:00 committed by Catherine
parent 1bf74fc303
commit bc744eb82b
26 changed files with 267 additions and 155 deletions

View file

@ -469,7 +469,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,
crate::needless_question_mark::NEEDLESS_QUESTION_MARK_INFO,
crate::needless_raw_string_hashes::NEEDLESS_RAW_STRING_HASHES_INFO,
crate::needless_update::NEEDLESS_UPDATE_INFO,
crate::neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD_INFO,
crate::neg_multiply::NEG_MULTIPLY_INFO,
@ -541,6 +540,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::ranges::RANGE_MINUS_ONE_INFO,
crate::ranges::RANGE_PLUS_ONE_INFO,
crate::ranges::REVERSED_EMPTY_RANGES_INFO,
crate::raw_strings::NEEDLESS_RAW_STRING_INFO,
crate::raw_strings::NEEDLESS_RAW_STRING_HASHES_INFO,
crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO,
crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO,
crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO,

View file

@ -230,7 +230,6 @@ mod needless_late_init;
mod needless_parens_on_range_literals;
mod needless_pass_by_value;
mod needless_question_mark;
mod needless_raw_string_hashes;
mod needless_update;
mod neg_cmp_op_on_partial_ord;
mod neg_multiply;
@ -263,6 +262,7 @@ mod pub_use;
mod question_mark;
mod question_mark_used;
mod ranges;
mod raw_strings;
mod rc_clone_in_vec_init;
mod read_zero_byte_vec;
mod redundant_async_block;

View file

@ -1,73 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_ast::{
ast::{Expr, ExprKind},
token::LitKind,
};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Checks for raw string literals with an unnecessary amount of hashes around them.
///
/// ### Why is this bad?
/// It's just unnecessary, and makes it look like there's more escaping needed than is actually
/// necessary.
///
/// ### Example
/// ```rust
/// let r = r###"Hello, "world"!"###;
/// ```
/// Use instead:
/// ```rust
/// let r = r#"Hello, "world"!"#;
/// ```
#[clippy::version = "1.72.0"]
pub NEEDLESS_RAW_STRING_HASHES,
complexity,
"suggests reducing the number of hashes around a raw string literal"
}
declare_lint_pass!(NeedlessRawStringHashes => [NEEDLESS_RAW_STRING_HASHES]);
impl EarlyLintPass for NeedlessRawStringHashes {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
if_chain! {
if !in_external_macro(cx.sess(), expr.span);
if let ExprKind::Lit(lit) = expr.kind;
if let LitKind::StrRaw(num) | LitKind::ByteStrRaw(num) | LitKind::CStrRaw(num) = lit.kind;
then {
let str = lit.symbol.as_str();
let mut lowest = 0;
for i in (0..num).rev() {
if str.contains(&format!("\"{}", "#".repeat(i as usize))) {
lowest = i + 1;
break;
}
}
if lowest < num {
let hashes = "#".repeat(lowest as usize);
let prefix = match lit.kind {
LitKind::StrRaw(..) => "r",
LitKind::ByteStrRaw(..) => "br",
LitKind::CStrRaw(..) => "cr",
_ => unreachable!(),
};
span_lint_and_sugg(
cx,
NEEDLESS_RAW_STRING_HASHES,
expr.span,
"unnecessary hashes around raw string literal",
"try",
format!(r#"{prefix}{hashes}"{}"{hashes}"#, lit.symbol),
Applicability::MachineApplicable,
);
}
}
}
}
}

View file

@ -0,0 +1,109 @@
use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
use rustc_ast::{
ast::{Expr, ExprKind},
token::LitKind,
};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! {
/// ### What it does
/// Checks for raw string literals where a string literal can be used instead.
///
/// ### Why is this bad?
/// It's just unnecessary.
///
/// ### Example
/// ```rust
/// let r = r"Hello, world!";
/// ```
/// Use instead:
/// ```rust
/// let r = "Hello, world!";
/// ```
#[clippy::version = "1.72.0"]
pub NEEDLESS_RAW_STRING,
complexity,
"suggests using a string literal when a raw string literal is unnecessary"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for raw string literals with an unnecessary amount of hashes around them.
///
/// ### Why is this bad?
/// It's just unnecessary, and makes it look like there's more escaping needed than is actually
/// necessary.
///
/// ### Example
/// ```rust
/// let r = r###"Hello, "world"!"###;
/// ```
/// Use instead:
/// ```rust
/// let r = r#"Hello, "world"!"#;
/// ```
#[clippy::version = "1.72.0"]
pub NEEDLESS_RAW_STRING_HASHES,
complexity,
"suggests reducing the number of hashes around a raw string literal"
}
impl_lint_pass!(RawStrings => [NEEDLESS_RAW_STRING, NEEDLESS_RAW_STRING_HASHES]);
pub struct RawStrings {
pub needless_raw_string_hashes_allow_one: bool,
}
impl EarlyLintPass for RawStrings {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
if !in_external_macro(cx.sess(), expr.span)
&& let ExprKind::Lit(lit) = expr.kind
&& let LitKind::StrRaw(num) | LitKind::ByteStrRaw(num) | LitKind::CStrRaw(num) = lit.kind
{
let prefix = match lit.kind {
LitKind::StrRaw(..) => "r",
LitKind::ByteStrRaw(..) => "br",
LitKind::CStrRaw(..) => "cr",
_ => unreachable!(),
};
if !snippet(cx, expr.span, prefix).trim().starts_with(prefix) {
return;
}
#[allow(clippy::cast_possible_truncation)]
let req = lit.symbol.as_str().as_bytes()
.split(|&b| b == b'"')
.skip(1)
.map(|bs| 1 + bs.iter().take_while(|&&b| b == b'#').count() as u8)
.max()
.unwrap_or(0);
if req < num {
let hashes = "#".repeat(req as usize);
span_lint_and_sugg(
cx,
NEEDLESS_RAW_STRING_HASHES,
expr.span,
"unnecessary hashes around raw string literal",
"try",
format!(r#"{prefix}{hashes}"{}"{hashes}"#, lit.symbol),
Applicability::MachineApplicable,
);
}
if !lit.symbol.as_str().contains(['\\', '"']) {
span_lint_and_sugg(
cx,
NEEDLESS_RAW_STRING,
expr.span,
"unnecessary raw string literal",
"try",
format!("{}\"{}\"", prefix.replace('r', ""), lit.symbol),
Applicability::MachineApplicable,
);
}
}
}
}

View file

@ -543,10 +543,14 @@ define_Conf! {
///
/// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block
(accept_comment_above_statement: bool = false),
/// Lint: UNDOCUMENTED_UNSAFE_BLOCKS.
/// Lint: UNDOCUMENTED_UNSAFE_BLOCKS.
///
/// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block
(accept_comment_above_attributes: bool = false),
/// Lint: UNNECESSARY_RAW_STRING_HASHES.
///
/// Whether to allow `r#""#` when `r""` can be used
(allow_one_hash_in_raw_string: bool = false),
}
/// Search for the configuration file.

View file

@ -35,7 +35,7 @@ struct StandardFormulations<'a> {
impl AlmostStandardFormulation {
pub fn new() -> Self {
let standard_formulations = vec![StandardFormulations {
wrong_pattern: Regex::new(r"^(Check for|Detects? uses?)").unwrap(),
wrong_pattern: Regex::new("^(Check for|Detects? uses?)").unwrap(),
correction: "Checks for",
}];
Self { standard_formulations }