Add new literal_string_with_formatting_arg lint
This commit is contained in:
parent
53994bda92
commit
cd7cec9066
4 changed files with 106 additions and 0 deletions
|
|
@ -5639,6 +5639,7 @@ Released 2018-09-13
|
|||
[`lines_filter_map_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok
|
||||
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
|
||||
[`lint_groups_priority`]: https://rust-lang.github.io/rust-clippy/master/index.html#lint_groups_priority
|
||||
[`literal_string_with_formatting_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#literal_string_with_formatting_arg
|
||||
[`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes
|
||||
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
|
||||
[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
|
||||
|
|
|
|||
|
|
@ -276,6 +276,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::literal_representation::MISTYPED_LITERAL_SUFFIXES_INFO,
|
||||
crate::literal_representation::UNREADABLE_LITERAL_INFO,
|
||||
crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO,
|
||||
crate::literal_string_with_formatting_arg::LITERAL_STRING_WITH_FORMATTING_ARG_INFO,
|
||||
crate::loops::EMPTY_LOOP_INFO,
|
||||
crate::loops::EXPLICIT_COUNTER_LOOP_INFO,
|
||||
crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO,
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ extern crate rustc_lexer;
|
|||
extern crate rustc_lint;
|
||||
extern crate rustc_middle;
|
||||
extern crate rustc_parse;
|
||||
extern crate rustc_parse_format;
|
||||
extern crate rustc_resolve;
|
||||
extern crate rustc_session;
|
||||
extern crate rustc_span;
|
||||
|
|
@ -196,6 +197,7 @@ mod let_with_type_underscore;
|
|||
mod lifetimes;
|
||||
mod lines_filter_map_ok;
|
||||
mod literal_representation;
|
||||
mod literal_string_with_formatting_arg;
|
||||
mod loops;
|
||||
mod macro_metavars_in_unsafe;
|
||||
mod macro_use;
|
||||
|
|
@ -959,6 +961,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo));
|
||||
store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions));
|
||||
store.register_early_pass(|| Box::new(literal_string_with_formatting_arg::LiteralStringWithFormattingArg));
|
||||
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
|
||||
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
|
||||
|
|
|
|||
101
clippy_lints/src/literal_string_with_formatting_arg.rs
Normal file
101
clippy_lints/src/literal_string_with_formatting_arg.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
use rustc_ast::ast::{Expr, ExprKind};
|
||||
use rustc_ast::token::LitKind;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_parse_format::{ParseMode, Parser, Piece};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::BytePos;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if string literals have formatting arguments outside of macros
|
||||
/// using them (like `format!`).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It will likely not generate the expected content.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x: Option<usize> = None;
|
||||
/// let y = "hello";
|
||||
/// x.expect("{y:?}");
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let x: Option<usize> = None;
|
||||
/// let y = "hello";
|
||||
/// x.expect(&format!("{y:?}"));
|
||||
/// ```
|
||||
#[clippy::version = "1.83.0"]
|
||||
pub LITERAL_STRING_WITH_FORMATTING_ARG,
|
||||
suspicious,
|
||||
"Checks if string literals have formatting arguments"
|
||||
}
|
||||
|
||||
declare_lint_pass!(LiteralStringWithFormattingArg => [LITERAL_STRING_WITH_FORMATTING_ARG]);
|
||||
|
||||
impl EarlyLintPass for LiteralStringWithFormattingArg {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::Lit(lit) = expr.kind {
|
||||
let add = match lit.kind {
|
||||
LitKind::Str => 1,
|
||||
LitKind::StrRaw(nb) => nb as usize + 2,
|
||||
_ => return,
|
||||
};
|
||||
let fmt_str = lit.symbol.as_str();
|
||||
let lo = expr.span.lo();
|
||||
let mut current = fmt_str;
|
||||
let mut diff_len = 0;
|
||||
|
||||
let mut parser = Parser::new(current, None, None, false, ParseMode::Format);
|
||||
let mut spans = Vec::new();
|
||||
while let Some(piece) = parser.next() {
|
||||
if let Some(error) = parser.errors.last() {
|
||||
// We simply ignore the errors and move after them.
|
||||
if error.span.end >= current.len() {
|
||||
break;
|
||||
}
|
||||
current = ¤t[error.span.end + 1..];
|
||||
diff_len = fmt_str.len() - current.len();
|
||||
parser = Parser::new(current, None, None, false, ParseMode::Format);
|
||||
} else if let Piece::NextArgument(arg) = piece {
|
||||
let mut pos = arg.position_span;
|
||||
pos.start += diff_len;
|
||||
pos.end += diff_len;
|
||||
|
||||
let start = fmt_str[..pos.start].rfind('{').unwrap_or(pos.start);
|
||||
// If this is a unicode character escape, we don't want to lint.
|
||||
if start > 1 && fmt_str[..start].ends_with("\\u") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut end = fmt_str[pos.end..].find('}').map_or(pos.end, |found| found + pos.end);
|
||||
if fmt_str[start..end].contains(':') {
|
||||
end += 1;
|
||||
}
|
||||
spans.push(
|
||||
expr.span
|
||||
.with_hi(lo + BytePos((start + add) as _))
|
||||
.with_lo(lo + BytePos((end + add) as _)),
|
||||
);
|
||||
}
|
||||
}
|
||||
if spans.len() == 1 {
|
||||
span_lint(
|
||||
cx,
|
||||
LITERAL_STRING_WITH_FORMATTING_ARG,
|
||||
spans,
|
||||
"this looks like a formatting argument but it is not part of a formatting macro",
|
||||
);
|
||||
} else if spans.len() > 1 {
|
||||
span_lint(
|
||||
cx,
|
||||
LITERAL_STRING_WITH_FORMATTING_ARG,
|
||||
spans,
|
||||
"these look like formatting arguments but are not part of a formatting macro",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue