mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-14 00:47:16 +00:00
Auto merge of #11444 - Alexendoo:find-format-args-lifetime-crimes, r=flip1995
Return a value from find_format_args instead of using a callback r? `@flip1995` changelog: none
This commit is contained in:
commit
b27fc10aa8
9 changed files with 126 additions and 129 deletions
|
@ -57,54 +57,52 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
&& let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root())
|
||||||
{
|
{
|
||||||
find_format_args(cx, write_arg, ExpnId::root(), |format_args| {
|
// ordering is important here, since `writeln!` uses `write!` internally
|
||||||
let calling_macro =
|
let calling_macro = if is_expn_of(write_call.span, "writeln").is_some() {
|
||||||
// ordering is important here, since `writeln!` uses `write!` internally
|
Some("writeln")
|
||||||
if is_expn_of(write_call.span, "writeln").is_some() {
|
} else if is_expn_of(write_call.span, "write").is_some() {
|
||||||
Some("writeln")
|
Some("write")
|
||||||
} else if is_expn_of(write_call.span, "write").is_some() {
|
} else {
|
||||||
Some("write")
|
None
|
||||||
} else {
|
};
|
||||||
None
|
let prefix = if dest_name == "stderr" {
|
||||||
};
|
"e"
|
||||||
let prefix = if dest_name == "stderr" {
|
} else {
|
||||||
"e"
|
""
|
||||||
} else {
|
};
|
||||||
""
|
|
||||||
};
|
|
||||||
|
|
||||||
// We need to remove the last trailing newline from the string because the
|
// We need to remove the last trailing newline from the string because the
|
||||||
// underlying `fmt::write` function doesn't know whether `println!` or `print!` was
|
// underlying `fmt::write` function doesn't know whether `println!` or `print!` was
|
||||||
// used.
|
// used.
|
||||||
let (used, sugg_mac) = if let Some(macro_name) = calling_macro {
|
let (used, sugg_mac) = if let Some(macro_name) = calling_macro {
|
||||||
(
|
(
|
||||||
format!("{macro_name}!({dest_name}(), ...)"),
|
format!("{macro_name}!({dest_name}(), ...)"),
|
||||||
macro_name.replace("write", "print"),
|
macro_name.replace("write", "print"),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
format!("{dest_name}().write_fmt(...)"),
|
format!("{dest_name}().write_fmt(...)"),
|
||||||
"print".into(),
|
"print".into(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let inputs_snippet = snippet_with_applicability(
|
let inputs_snippet = snippet_with_applicability(
|
||||||
cx,
|
cx,
|
||||||
format_args_inputs_span(format_args),
|
format_args_inputs_span(&format_args),
|
||||||
"..",
|
"..",
|
||||||
&mut applicability,
|
&mut applicability,
|
||||||
);
|
);
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
EXPLICIT_WRITE,
|
EXPLICIT_WRITE,
|
||||||
expr.span,
|
expr.span,
|
||||||
&format!("use of `{used}.unwrap()`"),
|
&format!("use of `{used}.unwrap()`"),
|
||||||
"try",
|
"try",
|
||||||
format!("{prefix}{sugg_mac}!({inputs_snippet})"),
|
format!("{prefix}{sugg_mac}!({inputs_snippet})"),
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,14 +43,10 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
|
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||||
return;
|
&& cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id)
|
||||||
};
|
&& let Some(format_args) = find_format_args(cx, expr, macro_call.expn)
|
||||||
if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
|
{
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
find_format_args(cx, expr, macro_call.expn, |format_args| {
|
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let call_site = macro_call.span;
|
let call_site = macro_call.span;
|
||||||
|
|
||||||
|
@ -91,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -186,15 +186,10 @@ impl FormatArgs {
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
|
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||||
return;
|
&& is_format_macro(cx, macro_call.def_id)
|
||||||
};
|
&& let Some(format_args) = find_format_args(cx, expr, macro_call.expn)
|
||||||
if !is_format_macro(cx, macro_call.def_id) {
|
{
|
||||||
return;
|
|
||||||
}
|
|
||||||
let name = cx.tcx.item_name(macro_call.def_id);
|
|
||||||
|
|
||||||
find_format_args(cx, expr, macro_call.expn, |format_args| {
|
|
||||||
for piece in &format_args.template {
|
for piece in &format_args.template {
|
||||||
if let FormatArgsPiece::Placeholder(placeholder) = piece
|
if let FormatArgsPiece::Placeholder(placeholder) = piece
|
||||||
&& let Ok(index) = placeholder.argument.index
|
&& let Ok(index) = placeholder.argument.index
|
||||||
|
@ -206,12 +201,13 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
||||||
|
|
||||||
if placeholder.format_trait != FormatTrait::Display
|
if placeholder.format_trait != FormatTrait::Display
|
||||||
|| placeholder.format_options != FormatOptions::default()
|
|| placeholder.format_options != FormatOptions::default()
|
||||||
|| is_aliased(format_args, index)
|
|| is_aliased(&format_args, index)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(arg_hir_expr) = arg_expr {
|
if let Ok(arg_hir_expr) = arg_expr {
|
||||||
|
let name = cx.tcx.item_name(macro_call.def_id);
|
||||||
check_format_in_format_args(cx, macro_call.span, name, arg_hir_expr);
|
check_format_in_format_args(cx, macro_call.span, name, arg_hir_expr);
|
||||||
check_to_string_in_format_args(cx, name, arg_hir_expr);
|
check_to_string_in_format_args(cx, name, arg_hir_expr);
|
||||||
}
|
}
|
||||||
|
@ -219,9 +215,9 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
|
if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) {
|
||||||
check_uninlined_args(cx, format_args, macro_call.span, macro_call.def_id, self.ignore_mixed);
|
check_uninlined_args(cx, &format_args, macro_call.span, macro_call.def_id, self.ignore_mixed);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extract_msrv_attr!(LateContext);
|
extract_msrv_attr!(LateContext);
|
||||||
|
|
|
@ -170,30 +170,29 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
|
||||||
if let Some(outer_macro) = root_macro_call_first_node(cx, expr)
|
if let Some(outer_macro) = root_macro_call_first_node(cx, expr)
|
||||||
&& let macro_def_id = outer_macro.def_id
|
&& let macro_def_id = outer_macro.def_id
|
||||||
&& is_format_macro(cx, macro_def_id)
|
&& is_format_macro(cx, macro_def_id)
|
||||||
|
&& let Some(format_args) = find_format_args(cx, expr, outer_macro.expn)
|
||||||
{
|
{
|
||||||
find_format_args(cx, expr, outer_macro.expn, |format_args| {
|
for piece in &format_args.template {
|
||||||
for piece in &format_args.template {
|
if let FormatArgsPiece::Placeholder(placeholder) = piece
|
||||||
if let FormatArgsPiece::Placeholder(placeholder) = piece
|
&& let trait_name = match placeholder.format_trait {
|
||||||
&& let trait_name = match placeholder.format_trait {
|
FormatTrait::Display => sym::Display,
|
||||||
FormatTrait::Display => sym::Display,
|
FormatTrait::Debug => sym::Debug,
|
||||||
FormatTrait::Debug => sym::Debug,
|
FormatTrait::LowerExp => sym!(LowerExp),
|
||||||
FormatTrait::LowerExp => sym!(LowerExp),
|
FormatTrait::UpperExp => sym!(UpperExp),
|
||||||
FormatTrait::UpperExp => sym!(UpperExp),
|
FormatTrait::Octal => sym!(Octal),
|
||||||
FormatTrait::Octal => sym!(Octal),
|
FormatTrait::Pointer => sym::Pointer,
|
||||||
FormatTrait::Pointer => sym::Pointer,
|
FormatTrait::Binary => sym!(Binary),
|
||||||
FormatTrait::Binary => sym!(Binary),
|
FormatTrait::LowerHex => sym!(LowerHex),
|
||||||
FormatTrait::LowerHex => sym!(LowerHex),
|
FormatTrait::UpperHex => sym!(UpperHex),
|
||||||
FormatTrait::UpperHex => sym!(UpperHex),
|
|
||||||
}
|
|
||||||
&& trait_name == impl_trait.name
|
|
||||||
&& let Ok(index) = placeholder.argument.index
|
|
||||||
&& let Some(arg) = format_args.arguments.all_args().get(index)
|
|
||||||
&& let Ok(arg_expr) = find_format_arg_expr(expr, arg)
|
|
||||||
{
|
|
||||||
check_format_arg_self(cx, expr.span, arg_expr, impl_trait);
|
|
||||||
}
|
}
|
||||||
|
&& trait_name == impl_trait.name
|
||||||
|
&& let Ok(index) = placeholder.argument.index
|
||||||
|
&& let Some(arg) = format_args.arguments.all_args().get(index)
|
||||||
|
&& let Ok(arg_expr) = find_format_arg_expr(expr, arg)
|
||||||
|
{
|
||||||
|
check_format_arg_self(cx, expr.span, arg_expr, impl_trait);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -609,7 +609,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||||
.collect(),
|
.collect(),
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
store.register_early_pass(|| Box::new(utils::format_args_collector::FormatArgsCollector));
|
store.register_early_pass(|| Box::<utils::format_args_collector::FormatArgsCollector>::default());
|
||||||
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
|
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
|
||||||
store.register_late_pass(|_| Box::new(utils::author::Author));
|
store.register_late_pass(|_| Box::new(utils::author::Author));
|
||||||
let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
|
let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
|
||||||
|
|
|
@ -131,13 +131,12 @@ pub(super) fn check<'tcx>(
|
||||||
|
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
|
||||||
//Special handling for `format!` as arg_root
|
// Special handling for `format!` as arg_root
|
||||||
if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) {
|
if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) {
|
||||||
if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
|
if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id)
|
||||||
return;
|
&& let Some(format_args) = find_format_args(cx, arg_root, macro_call.expn)
|
||||||
}
|
{
|
||||||
find_format_args(cx, arg_root, macro_call.expn, |format_args| {
|
let span = format_args_inputs_span(&format_args);
|
||||||
let span = format_args_inputs_span(format_args);
|
|
||||||
let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
|
let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
|
@ -148,7 +147,7 @@ pub(super) fn check<'tcx>(
|
||||||
format!("unwrap_or_else({closure_args} panic!({sugg}))"),
|
format!("unwrap_or_else({closure_args} panic!({sugg}))"),
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
use clippy_utils::macros::collect_ast_format_args;
|
use clippy_utils::macros::AST_FORMAT_ARGS;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_ast::{Expr, ExprKind, FormatArgs};
|
use rustc_ast::{Crate, Expr, ExprKind, FormatArgs};
|
||||||
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_lexer::{tokenize, TokenKind};
|
use rustc_lexer::{tokenize, TokenKind};
|
||||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
use rustc_span::hygiene;
|
use rustc_span::{hygiene, Span};
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
|
use std::mem;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
@ -17,7 +20,12 @@ declare_clippy_lint! {
|
||||||
"collects `format_args` AST nodes for use in later lints"
|
"collects `format_args` AST nodes for use in later lints"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
|
#[derive(Default)]
|
||||||
|
pub struct FormatArgsCollector {
|
||||||
|
format_args: FxHashMap<Span, Rc<FormatArgs>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_lint_pass!(FormatArgsCollector => [FORMAT_ARGS_COLLECTOR]);
|
||||||
|
|
||||||
impl EarlyLintPass for FormatArgsCollector {
|
impl EarlyLintPass for FormatArgsCollector {
|
||||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||||
|
@ -26,9 +34,17 @@ impl EarlyLintPass for FormatArgsCollector {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
collect_ast_format_args(expr.span, args);
|
self.format_args
|
||||||
|
.insert(expr.span.with_parent(None), Rc::new((**args).clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) {
|
||||||
|
AST_FORMAT_ARGS.with(|ast_format_args| {
|
||||||
|
let result = ast_format_args.set(mem::take(&mut self.format_args));
|
||||||
|
debug_assert!(result.is_ok());
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Detects if the format string or an argument has its span set by a proc macro to something inside
|
/// Detects if the format string or an argument has its span set by a proc macro to something inside
|
||||||
|
|
|
@ -304,7 +304,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
|
||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
|
|
||||||
find_format_args(cx, expr, macro_call.expn, |format_args| {
|
if let Some(format_args) = find_format_args(cx, expr, macro_call.expn) {
|
||||||
// ignore `writeln!(w)` and `write!(v, some_macro!())`
|
// ignore `writeln!(w)` and `write!(v, some_macro!())`
|
||||||
if format_args.span.from_expansion() {
|
if format_args.span.from_expansion() {
|
||||||
return;
|
return;
|
||||||
|
@ -312,15 +312,15 @@ impl<'tcx> LateLintPass<'tcx> for Write {
|
||||||
|
|
||||||
match diag_name {
|
match diag_name {
|
||||||
sym::print_macro | sym::eprint_macro | sym::write_macro => {
|
sym::print_macro | sym::eprint_macro | sym::write_macro => {
|
||||||
check_newline(cx, format_args, ¯o_call, name);
|
check_newline(cx, &format_args, ¯o_call, name);
|
||||||
},
|
},
|
||||||
sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
|
sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
|
||||||
check_empty_string(cx, format_args, ¯o_call, name);
|
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 {
|
if !self.in_debug_impl {
|
||||||
for piece in &format_args.template {
|
for piece in &format_args.template {
|
||||||
|
@ -334,7 +334,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,9 @@ use rustc_lint::LateContext;
|
||||||
use rustc_span::def_id::DefId;
|
use rustc_span::def_id::DefId;
|
||||||
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
|
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
|
||||||
use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol};
|
use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol};
|
||||||
use std::cell::RefCell;
|
use std::cell::OnceCell;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
|
const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
|
||||||
|
@ -374,30 +375,21 @@ thread_local! {
|
||||||
/// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
|
/// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
|
||||||
/// assumption that the early pass that populates the map and the later late passes will all be
|
/// assumption that the early pass that populates the map and the later late passes will all be
|
||||||
/// running on the same thread.
|
/// running on the same thread.
|
||||||
static AST_FORMAT_ARGS: RefCell<FxHashMap<Span, FormatArgs>> = {
|
#[doc(hidden)]
|
||||||
|
pub static AST_FORMAT_ARGS: OnceCell<FxHashMap<Span, Rc<FormatArgs>>> = {
|
||||||
static CALLED: AtomicBool = AtomicBool::new(false);
|
static CALLED: AtomicBool = AtomicBool::new(false);
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
!CALLED.swap(true, Ordering::SeqCst),
|
!CALLED.swap(true, Ordering::SeqCst),
|
||||||
"incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
|
"incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
|
||||||
);
|
);
|
||||||
|
|
||||||
RefCell::default()
|
OnceCell::new()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by
|
/// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of
|
||||||
/// `FormatArgsCollector`
|
/// `expn_id`
|
||||||
pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) {
|
pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<Rc<FormatArgs>> {
|
||||||
AST_FORMAT_ARGS.with(|ast_format_args| {
|
|
||||||
ast_format_args
|
|
||||||
.borrow_mut()
|
|
||||||
.insert(span.with_parent(None), format_args.clone());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calls `callback` with an AST [`FormatArgs`] node if a `format_args` expansion is found as a
|
|
||||||
/// descendant of `expn_id`
|
|
||||||
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 format_args_expr = for_each_expr(start, |expr| {
|
||||||
let ctxt = expr.span.ctxt();
|
let ctxt = expr.span.ctxt();
|
||||||
if ctxt.outer_expn().is_descendant_of(expn_id) {
|
if ctxt.outer_expn().is_descendant_of(expn_id) {
|
||||||
|
@ -412,13 +404,14 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId,
|
||||||
} else {
|
} else {
|
||||||
ControlFlow::Continue(Descend::No)
|
ControlFlow::Continue(Descend::No)
|
||||||
}
|
}
|
||||||
});
|
})?;
|
||||||
|
|
||||||
if let Some(expr) = format_args_expr {
|
AST_FORMAT_ARGS.with(|ast_format_args| {
|
||||||
AST_FORMAT_ARGS.with(|ast_format_args| {
|
ast_format_args
|
||||||
ast_format_args.borrow().get(&expr.span.with_parent(None)).map(callback);
|
.get()?
|
||||||
});
|
.get(&format_args_expr.span.with_parent(None))
|
||||||
}
|
.map(Rc::clone)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
|
/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
|
||||||
|
|
Loading…
Reference in a new issue