From a2906a1598ca16ac080f95658a8a2eae10ff25d5 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 1 Feb 2023 21:31:09 +0000 Subject: [PATCH 1/3] Migrate `write.rs` to `rustc_ast::FormatArgs` --- clippy_lints/src/lib.rs | 1 + clippy_lints/src/write.rs | 158 ++++++++++++++++++++++--------------- clippy_utils/src/macros.rs | 79 ++++++++++++++++++- 3 files changed, 173 insertions(+), 65 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c626e0bd9985..a025e3cc12a0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -870,6 +870,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let allow_dbg_in_tests = conf.allow_dbg_in_tests; store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); let allow_print_in_tests = conf.allow_print_in_tests; + store.register_early_pass(move || Box::new(write::Write::new(allow_print_in_tests))); store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests))); let cargo_ignore_publish = conf.cargo_ignore_publish; store.register_late_pass(move |_| { diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index df3350388817..ce419ec9373c 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,11 +1,14 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall}; +use clippy_utils::macros::{ + find_format_args, format_arg_removal_span, populate_ast_format_args, root_macro_call_first_node, MacroCall, +}; use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use clippy_utils::{is_in_cfg_test, is_in_test_function}; -use rustc_ast::LitKind; +use rustc_ast::token::LitKind; +use rustc_ast::{FormatArgPosition, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatTrait}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_hir::{Expr, Impl, Item, ItemKind}; +use rustc_lint::{EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, BytePos}; @@ -257,6 +260,12 @@ impl_lint_pass!(Write => [ WRITE_LITERAL, ]); +impl EarlyLintPass for Write { + fn check_expr(&mut self, _: &rustc_lint::EarlyContext<'_>, expr: &rustc_ast::Expr) { + populate_ast_format_args(expr); + } +} + impl<'tcx> LateLintPass<'tcx> for Write { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if is_debug_impl(cx, item) { @@ -297,34 +306,40 @@ impl<'tcx> LateLintPass<'tcx> for Write { _ => return, } - let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return }; - - // ignore `writeln!(w)` and `write!(v, some_macro!())` - if format_args.format_string.span.from_expansion() { - return; - } + find_format_args(cx, expr, macro_call.expn, |format_args| { + // ignore `writeln!(w)` and `write!(v, some_macro!())` + if format_args.span.from_expansion() { + return; + } - match diag_name { - sym::print_macro | sym::eprint_macro | sym::write_macro => { - check_newline(cx, &format_args, ¯o_call, name); - }, - sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { - check_empty_string(cx, &format_args, ¯o_call, name); - }, - _ => {}, - } + match diag_name { + sym::print_macro | sym::eprint_macro | sym::write_macro => { + check_newline(cx, format_args, ¯o_call, name); + }, + sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { + check_empty_string(cx, format_args, ¯o_call, name); + }, + _ => {}, + } - check_literal(cx, &format_args, name); + check_literal(cx, format_args, name); - if !self.in_debug_impl { - for arg in &format_args.args { - if arg.format.r#trait == sym::Debug { - span_lint(cx, USE_DEBUG, arg.span, "use of `Debug`-based formatting"); + if !self.in_debug_impl { + for piece in &format_args.template { + if let &FormatArgsPiece::Placeholder(FormatPlaceholder { + span: Some(span), + format_trait: FormatTrait::Debug, + .. + }) = piece + { + span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting"); + } } } - } + }); } } + fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind && let Some(trait_id) = trait_ref.trait_def_id() @@ -335,16 +350,18 @@ fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { } } -fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) { - let format_string_parts = &format_args.format_string.parts; - let mut format_string_span = format_args.format_string.span; - - let Some(last) = format_string_parts.last() else { return }; +fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { + let Some(FormatArgsPiece::Literal(last)) = format_args.template.last() else { return }; let count_vertical_whitespace = || { - format_string_parts + format_args + .template .iter() - .flat_map(|part| part.as_str().chars()) + .filter_map(|piece| match piece { + FormatArgsPiece::Literal(literal) => Some(literal), + FormatArgsPiece::Placeholder(_) => None, + }) + .flat_map(|literal| literal.as_str().chars()) .filter(|ch| matches!(ch, '\r' | '\n')) .count() }; @@ -352,10 +369,9 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c if last.as_str().ends_with('\n') // ignore format strings with other internal vertical whitespace && count_vertical_whitespace() == 1 - - // ignore trailing arguments: `print!("Issue\n{}", 1265);` - && format_string_parts.len() > format_args.args.len() { + let mut format_string_span = format_args.span; + let lint = if name == "write" { format_string_span = expand_past_previous_comma(cx, format_string_span); @@ -373,7 +389,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return }; - if format_string_parts.len() == 1 && last.as_str() == "\n" { + if format_args.template.len() == 1 && last.as_str() == "\n" { // print!("\n"), write!(f, "\n") diag.multipart_suggestion( @@ -398,11 +414,12 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_c } } -fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) { - if let [part] = &format_args.format_string.parts[..] - && let mut span = format_args.format_string.span - && part.as_str() == "\n" +fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { + if let [FormatArgsPiece::Literal(literal)] = &format_args.template[..] + && literal.as_str() == "\n" { + let mut span = format_args.span; + let lint = if name == "writeln" { span = expand_past_previous_comma(cx, span); @@ -428,33 +445,43 @@ fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, ma } } -fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &str) { - let mut counts = HirIdMap::::default(); - for param in format_args.params() { - *counts.entry(param.value.hir_id).or_default() += 1; +fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { + let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos); + + let mut counts = vec![0u32; format_args.arguments.all_args().len()]; + for piece in &format_args.template { + if let FormatArgsPiece::Placeholder(placeholder) = piece { + counts[arg_index(&placeholder.argument)] += 1; + } } - for arg in &format_args.args { - let value = arg.param.value; - - if counts[&value.hir_id] == 1 - && arg.format.is_default() - && let ExprKind::Lit(lit) = &value.kind - && !value.span.from_expansion() - && let Some(value_string) = snippet_opt(cx, value.span) - { - let (replacement, replace_raw) = match lit.node { - LitKind::Str(..) => extract_str_literal(&value_string), - LitKind::Char(ch) => ( - match ch { - '"' => "\\\"", - '\'' => "'", + for piece in &format_args.template { + if let FormatArgsPiece::Placeholder(FormatPlaceholder { + argument, + span: Some(placeholder_span), + format_trait: FormatTrait::Display, + format_options, + }) = piece + && *format_options == FormatOptions::default() + && let index = arg_index(argument) + && counts[index] == 1 + && let Some(arg) = format_args.arguments.by_index(index) + && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind + && !arg.expr.span.from_expansion() + && let Some(value_string) = snippet_opt(cx, arg.expr.span) + { + let (replacement, replace_raw) = match lit.kind { + LitKind::Str | LitKind::StrRaw(_) => extract_str_literal(&value_string), + LitKind::Char => ( + match lit.symbol.as_str() { + "\"" => "\\\"", + "\\'" => "'", _ => &value_string[1..value_string.len() - 1], } .to_string(), false, ), - LitKind::Bool(b) => (b.to_string(), false), + LitKind::Bool => (lit.symbol.to_string(), false), _ => continue, }; @@ -464,7 +491,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: & PRINT_LITERAL }; - let format_string_is_raw = format_args.format_string.style.is_some(); + let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else { continue }; + let format_string_is_raw = format_string_snippet.starts_with('r'); + let replacement = match (format_string_is_raw, replace_raw) { (false, false) => Some(replacement), (false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")), @@ -485,23 +514,24 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: & span_lint_and_then( cx, lint, - value.span, + arg.expr.span, "literal with an empty format string", |diag| { if let Some(replacement) = replacement // `format!("{}", "a")`, `format!("{named}", named = "b") // ~~~~~ ~~~~~~~~~~~~~ - && let Some(value_span) = format_args.value_with_prev_comma_span(value.hir_id) + && let Some(removal_span) = format_arg_removal_span(format_args, index) { let replacement = replacement.replace('{', "{{").replace('}', "}}"); diag.multipart_suggestion( "try this", - vec![(arg.span, replacement), (value_span, String::new())], + vec![(*placeholder_span, replacement), (removal_span, String::new())], Applicability::MachineApplicable, ); } }, ); + } } } diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index be6133d32024..8e0edd72f92f 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -6,6 +6,8 @@ use crate::visitors::{for_each_expr, Descend}; use arrayvec::ArrayVec; use itertools::{izip, Either, Itertools}; use rustc_ast::ast::LitKind; +use rustc_ast::FormatArgs; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind}; use rustc_lexer::unescape::unescape_literal; @@ -15,8 +17,10 @@ use rustc_parse_format::{self as rpf, Alignment}; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol}; +use std::cell::RefCell; use std::iter::{once, zip}; -use std::ops::ControlFlow; +use std::ops::{ControlFlow, Deref}; +use std::sync::atomic::{AtomicBool, Ordering}; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ sym::assert_eq_macro, @@ -339,6 +343,79 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> } } +thread_local! { + /// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be + /// able to access the many features of a [`LateContext`]. + /// + /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an + /// assumption that the early pass the populates the map and the later late passes will all be + /// running on the same thread. + static AST_FORMAT_ARGS: RefCell> = { + static CALLED: AtomicBool = AtomicBool::new(false); + debug_assert!( + !CALLED.swap(true, Ordering::SeqCst), + "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread", + ); + + RefCell::default() + }; +} + +/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this only needs to be called by +/// one lint pass. +pub fn populate_ast_format_args(expr: &rustc_ast::Expr) { + if let rustc_ast::ExprKind::FormatArgs(args) = &expr.kind { + AST_FORMAT_ARGS.with(|ast_format_args| { + ast_format_args.borrow_mut().insert(expr.span, args.deref().clone()); + }); + } +} + +/// Calls `callback` with an AST [`FormatArgs`] node if one is found +pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) { + let format_args_expr = for_each_expr(start, |expr| { + let ctxt = expr.span.ctxt(); + if ctxt == start.span.ctxt() { + ControlFlow::Continue(Descend::Yes) + } else if ctxt.outer_expn().is_descendant_of(expn_id) + && macro_backtrace(expr.span) + .map(|macro_call| cx.tcx.item_name(macro_call.def_id)) + .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl)) + { + ControlFlow::Break(expr) + } else { + ControlFlow::Continue(Descend::No) + } + }); + + if let Some(format_args_expr) = format_args_expr { + AST_FORMAT_ARGS.with(|ast_format_args| { + ast_format_args.borrow().get(&format_args_expr.span).map(callback); + }); + } +} + +/// Returns the [`Span`] of the value at `index` extended to the previous comma, e.g. for the value +/// `10` +/// +/// ```ignore +/// format("{}.{}", 10, 11) +/// // ^^^^ +/// ``` +pub fn format_arg_removal_span(format_args: &FormatArgs, index: usize) -> Option { + let ctxt = format_args.span.ctxt(); + + let current = hygiene::walk_chain(format_args.arguments.by_index(index)?.expr.span, ctxt); + + let prev = if index == 0 { + format_args.span + } else { + hygiene::walk_chain(format_args.arguments.by_index(index - 1)?.expr.span, ctxt) + }; + + Some(current.with_lo(prev.hi())) +} + /// The format string doesn't exist in the HIR, so we reassemble it from source code #[derive(Debug)] pub struct FormatString { From dc23e42fb696b1106d44371170a5fb61c1d725c6 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Thu, 2 Feb 2023 15:11:31 +0000 Subject: [PATCH 2/3] Add `format_args_collector` internal lint --- clippy_lints/src/lib.rs | 2 +- .../src/utils/format_args_collector.rs | 23 +++++++++++++++++++ clippy_lints/src/utils/mod.rs | 1 + clippy_lints/src/write.rs | 12 ++-------- clippy_utils/src/macros.rs | 16 ++++++------- 5 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 clippy_lints/src/utils/format_args_collector.rs diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a025e3cc12a0..f6d14b31fef0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -533,6 +533,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: .collect(), )) }); + store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector)); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::author::Author)); let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); @@ -870,7 +871,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let allow_dbg_in_tests = conf.allow_dbg_in_tests; store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); let allow_print_in_tests = conf.allow_print_in_tests; - store.register_early_pass(move || Box::new(write::Write::new(allow_print_in_tests))); store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests))); let cargo_ignore_publish = conf.cargo_ignore_publish; store.register_late_pass(move |_| { diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs new file mode 100644 index 000000000000..be56b842b98c --- /dev/null +++ b/clippy_lints/src/utils/format_args_collector.rs @@ -0,0 +1,23 @@ +use clippy_utils::macros::collect_ast_format_args; +use rustc_ast::{Expr, ExprKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Collects [`rustc_ast::FormatArgs`] so that future late passes can call + /// [`clippy_utils::macros::find_format_args`] + pub FORMAT_ARGS_COLLECTOR, + internal_warn, + "collects `format_args` AST nodes for use in later lints" +} + +declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]); + +impl EarlyLintPass for FormatArgsCollector { + fn check_expr(&mut self, _: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::FormatArgs(args) = &expr.kind { + collect_ast_format_args(expr.span, args); + } + } +} diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 787e9fd982c8..dc647af264c1 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1,5 +1,6 @@ pub mod author; pub mod conf; pub mod dump_hir; +pub mod format_args_collector; #[cfg(feature = "internal")] pub mod internal_lints; diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index ce419ec9373c..8114a8463faa 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,14 +1,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::macros::{ - find_format_args, format_arg_removal_span, populate_ast_format_args, root_macro_call_first_node, MacroCall, -}; +use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro_call_first_node, MacroCall}; use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_ast::token::LitKind; use rustc_ast::{FormatArgPosition, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, FormatTrait}; use rustc_errors::Applicability; use rustc_hir::{Expr, Impl, Item, ItemKind}; -use rustc_lint::{EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, BytePos}; @@ -260,12 +258,6 @@ impl_lint_pass!(Write => [ WRITE_LITERAL, ]); -impl EarlyLintPass for Write { - fn check_expr(&mut self, _: &rustc_lint::EarlyContext<'_>, expr: &rustc_ast::Expr) { - populate_ast_format_args(expr); - } -} - impl<'tcx> LateLintPass<'tcx> for Write { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if is_debug_impl(cx, item) { diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 8e0edd72f92f..ab1c26fb9ad7 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -19,7 +19,7 @@ use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol}; use std::cell::RefCell; use std::iter::{once, zip}; -use std::ops::{ControlFlow, Deref}; +use std::ops::ControlFlow; use std::sync::atomic::{AtomicBool, Ordering}; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ @@ -361,14 +361,12 @@ thread_local! { }; } -/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this only needs to be called by -/// one lint pass. -pub fn populate_ast_format_args(expr: &rustc_ast::Expr) { - if let rustc_ast::ExprKind::FormatArgs(args) = &expr.kind { - AST_FORMAT_ARGS.with(|ast_format_args| { - ast_format_args.borrow_mut().insert(expr.span, args.deref().clone()); - }); - } +/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by +/// `FormatArgsCollector` +pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) { + AST_FORMAT_ARGS.with(|ast_format_args| { + ast_format_args.borrow_mut().insert(span, format_args.clone()); + }); } /// Calls `callback` with an AST [`FormatArgs`] node if one is found From fa0c3cce516ad39d7ca36efe50b9d02de3b269d6 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 6 Mar 2023 21:54:24 +0000 Subject: [PATCH 3/3] Move test for 10148 to tests/ui/crashes --- tests/ui/crashes/ice-10148.rs | 9 +++++++++ tests/ui/crashes/ice-10148.stderr | 12 ++++++++++++ tests/ui/format.fixed | 6 ------ tests/ui/format.rs | 6 ------ tests/ui/format.stderr | 30 +++++++++++++++--------------- 5 files changed, 36 insertions(+), 27 deletions(-) create mode 100644 tests/ui/crashes/ice-10148.rs create mode 100644 tests/ui/crashes/ice-10148.stderr diff --git a/tests/ui/crashes/ice-10148.rs b/tests/ui/crashes/ice-10148.rs new file mode 100644 index 000000000000..af33b10c6938 --- /dev/null +++ b/tests/ui/crashes/ice-10148.rs @@ -0,0 +1,9 @@ +// aux-build:../../auxiliary/proc_macro_with_span.rs + +extern crate proc_macro_with_span; + +use proc_macro_with_span::with_span; + +fn main() { + println!(with_span!(""something "")); +} diff --git a/tests/ui/crashes/ice-10148.stderr b/tests/ui/crashes/ice-10148.stderr new file mode 100644 index 000000000000..f23e4433f95e --- /dev/null +++ b/tests/ui/crashes/ice-10148.stderr @@ -0,0 +1,12 @@ +error: empty string literal in `println!` + --> $DIR/ice-10148.rs:8:5 + | +LL | println!(with_span!(""something "")); + | ^^^^^^^^^^^^^^^^^^^^-----------^^^^^ + | | + | help: remove the empty string + | + = note: `-D clippy::println-empty-string` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index cd2f70ee8b02..beedf2c1db29 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -1,5 +1,4 @@ // run-rustfix -// aux-build: proc_macro_with_span.rs #![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, @@ -10,8 +9,6 @@ clippy::uninlined_format_args )] -extern crate proc_macro_with_span; - struct Foo(pub String); macro_rules! foo { @@ -90,7 +87,4 @@ fn main() { let _ = abc.to_string(); let xx = "xx"; let _ = xx.to_string(); - - // Issue #10148 - println!(proc_macro_with_span::with_span!(""something "")); } diff --git a/tests/ui/format.rs b/tests/ui/format.rs index c22345a79d43..e805f1818898 100644 --- a/tests/ui/format.rs +++ b/tests/ui/format.rs @@ -1,5 +1,4 @@ // run-rustfix -// aux-build: proc_macro_with_span.rs #![warn(clippy::useless_format)] #![allow( unused_tuple_struct_fields, @@ -10,8 +9,6 @@ clippy::uninlined_format_args )] -extern crate proc_macro_with_span; - struct Foo(pub String); macro_rules! foo { @@ -92,7 +89,4 @@ fn main() { let _ = format!("{abc}"); let xx = "xx"; let _ = format!("{xx}"); - - // Issue #10148 - println!(proc_macro_with_span::with_span!(""something "")); } diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr index a0e5d5c8ad21..0ef0ac655d39 100644 --- a/tests/ui/format.stderr +++ b/tests/ui/format.stderr @@ -1,5 +1,5 @@ error: useless use of `format!` - --> $DIR/format.rs:22:5 + --> $DIR/format.rs:19:5 | LL | format!("foo"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` @@ -7,19 +7,19 @@ LL | format!("foo"); = note: `-D clippy::useless-format` implied by `-D warnings` error: useless use of `format!` - --> $DIR/format.rs:23:5 + --> $DIR/format.rs:20:5 | LL | format!("{{}}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{}".to_string()` error: useless use of `format!` - --> $DIR/format.rs:24:5 + --> $DIR/format.rs:21:5 | LL | format!("{{}} abc {{}}"); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"{} abc {}".to_string()` error: useless use of `format!` - --> $DIR/format.rs:25:5 + --> $DIR/format.rs:22:5 | LL | / format!( LL | | r##"foo {{}} @@ -34,67 +34,67 @@ LL ~ " bar"##.to_string(); | error: useless use of `format!` - --> $DIR/format.rs:30:13 + --> $DIR/format.rs:27:13 | LL | let _ = format!(""); | ^^^^^^^^^^^ help: consider using `String::new()`: `String::new()` error: useless use of `format!` - --> $DIR/format.rs:32:5 + --> $DIR/format.rs:29:5 | LL | format!("{}", "foo"); | ^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `"foo".to_string()` error: useless use of `format!` - --> $DIR/format.rs:40:5 + --> $DIR/format.rs:37:5 | LL | format!("{}", arg); | ^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `arg.to_string()` error: useless use of `format!` - --> $DIR/format.rs:70:5 + --> $DIR/format.rs:67:5 | LL | format!("{}", 42.to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `42.to_string()` error: useless use of `format!` - --> $DIR/format.rs:72:5 + --> $DIR/format.rs:69:5 | LL | format!("{}", x.display().to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.display().to_string()` error: useless use of `format!` - --> $DIR/format.rs:76:18 + --> $DIR/format.rs:73:18 | LL | let _ = Some(format!("{}", a + "bar")); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `a + "bar"` error: useless use of `format!` - --> $DIR/format.rs:80:22 + --> $DIR/format.rs:77:22 | LL | let _s: String = format!("{}", &*v.join("/n")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `(&*v.join("/n")).to_string()` error: useless use of `format!` - --> $DIR/format.rs:86:13 + --> $DIR/format.rs:83:13 | LL | let _ = format!("{x}"); | ^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> $DIR/format.rs:88:13 + --> $DIR/format.rs:85:13 | LL | let _ = format!("{y}", y = x); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `x.to_string()` error: useless use of `format!` - --> $DIR/format.rs:92:13 + --> $DIR/format.rs:89:13 | LL | let _ = format!("{abc}"); | ^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `abc.to_string()` error: useless use of `format!` - --> $DIR/format.rs:94:13 + --> $DIR/format.rs:91:13 | LL | let _ = format!("{xx}"); | ^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `xx.to_string()`