]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_utils/src/macros.rs
Rollup merge of #104672 - Voultapher:unify-sort-modules, r=thomcc
[rust.git] / src / tools / clippy / clippy_utils / src / macros.rs
1 #![allow(clippy::similar_names)] // `expr` and `expn`
2
3 use crate::is_path_diagnostic_item;
4 use crate::source::snippet_opt;
5 use crate::visitors::{for_each_expr, Descend};
6
7 use arrayvec::ArrayVec;
8 use itertools::{izip, Either, Itertools};
9 use rustc_ast::ast::LitKind;
10 use rustc_hir::intravisit::{walk_expr, Visitor};
11 use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, Node, QPath};
12 use rustc_lexer::unescape::unescape_literal;
13 use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
14 use rustc_lint::LateContext;
15 use rustc_parse_format::{self as rpf, Alignment};
16 use rustc_span::def_id::DefId;
17 use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
18 use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol};
19 use std::iter::{once, zip};
20 use std::ops::ControlFlow;
21
22 const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
23     sym::assert_eq_macro,
24     sym::assert_macro,
25     sym::assert_ne_macro,
26     sym::debug_assert_eq_macro,
27     sym::debug_assert_macro,
28     sym::debug_assert_ne_macro,
29     sym::eprint_macro,
30     sym::eprintln_macro,
31     sym::format_args_macro,
32     sym::format_macro,
33     sym::print_macro,
34     sym::println_macro,
35     sym::std_panic_macro,
36     sym::write_macro,
37     sym::writeln_macro,
38 ];
39
40 /// Returns true if a given Macro `DefId` is a format macro (e.g. `println!`)
41 pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool {
42     if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) {
43         FORMAT_MACRO_DIAG_ITEMS.contains(&name)
44     } else {
45         false
46     }
47 }
48
49 /// A macro call, like `vec![1, 2, 3]`.
50 ///
51 /// Use `tcx.item_name(macro_call.def_id)` to get the macro name.
52 /// Even better is to check if it is a diagnostic item.
53 ///
54 /// This structure is similar to `ExpnData` but it precludes desugaring expansions.
55 #[derive(Debug)]
56 pub struct MacroCall {
57     /// Macro `DefId`
58     pub def_id: DefId,
59     /// Kind of macro
60     pub kind: MacroKind,
61     /// The expansion produced by the macro call
62     pub expn: ExpnId,
63     /// Span of the macro call site
64     pub span: Span,
65 }
66
67 impl MacroCall {
68     pub fn is_local(&self) -> bool {
69         span_is_local(self.span)
70     }
71 }
72
73 /// Returns an iterator of expansions that created the given span
74 pub fn expn_backtrace(mut span: Span) -> impl Iterator<Item = (ExpnId, ExpnData)> {
75     std::iter::from_fn(move || {
76         let ctxt = span.ctxt();
77         if ctxt == SyntaxContext::root() {
78             return None;
79         }
80         let expn = ctxt.outer_expn();
81         let data = expn.expn_data();
82         span = data.call_site;
83         Some((expn, data))
84     })
85 }
86
87 /// Checks whether the span is from the root expansion or a locally defined macro
88 pub fn span_is_local(span: Span) -> bool {
89     !span.from_expansion() || expn_is_local(span.ctxt().outer_expn())
90 }
91
92 /// Checks whether the expansion is the root expansion or a locally defined macro
93 pub fn expn_is_local(expn: ExpnId) -> bool {
94     if expn == ExpnId::root() {
95         return true;
96     }
97     let data = expn.expn_data();
98     let backtrace = expn_backtrace(data.call_site);
99     std::iter::once((expn, data))
100         .chain(backtrace)
101         .find_map(|(_, data)| data.macro_def_id)
102         .map_or(true, DefId::is_local)
103 }
104
105 /// Returns an iterator of macro expansions that created the given span.
106 /// Note that desugaring expansions are skipped.
107 pub fn macro_backtrace(span: Span) -> impl Iterator<Item = MacroCall> {
108     expn_backtrace(span).filter_map(|(expn, data)| match data {
109         ExpnData {
110             kind: ExpnKind::Macro(kind, _),
111             macro_def_id: Some(def_id),
112             call_site: span,
113             ..
114         } => Some(MacroCall {
115             def_id,
116             kind,
117             expn,
118             span,
119         }),
120         _ => None,
121     })
122 }
123
124 /// If the macro backtrace of `span` has a macro call at the root expansion
125 /// (i.e. not a nested macro call), returns `Some` with the `MacroCall`
126 pub fn root_macro_call(span: Span) -> Option<MacroCall> {
127     macro_backtrace(span).last()
128 }
129
130 /// Like [`root_macro_call`], but only returns `Some` if `node` is the "first node"
131 /// produced by the macro call, as in [`first_node_in_macro`].
132 pub fn root_macro_call_first_node(cx: &LateContext<'_>, node: &impl HirNode) -> Option<MacroCall> {
133     if first_node_in_macro(cx, node) != Some(ExpnId::root()) {
134         return None;
135     }
136     root_macro_call(node.span())
137 }
138
139 /// Like [`macro_backtrace`], but only returns macro calls where `node` is the "first node" of the
140 /// macro call, as in [`first_node_in_macro`].
141 pub fn first_node_macro_backtrace(cx: &LateContext<'_>, node: &impl HirNode) -> impl Iterator<Item = MacroCall> {
142     let span = node.span();
143     first_node_in_macro(cx, node)
144         .into_iter()
145         .flat_map(move |expn| macro_backtrace(span).take_while(move |macro_call| macro_call.expn != expn))
146 }
147
148 /// If `node` is the "first node" in a macro expansion, returns `Some` with the `ExpnId` of the
149 /// macro call site (i.e. the parent of the macro expansion). This generally means that `node`
150 /// is the outermost node of an entire macro expansion, but there are some caveats noted below.
151 /// This is useful for finding macro calls while visiting the HIR without processing the macro call
152 /// at every node within its expansion.
153 ///
154 /// If you already have immediate access to the parent node, it is simpler to
155 /// just check the context of that span directly (e.g. `parent.span.from_expansion()`).
156 ///
157 /// If a macro call is in statement position, it expands to one or more statements.
158 /// In that case, each statement *and* their immediate descendants will all yield `Some`
159 /// with the `ExpnId` of the containing block.
160 ///
161 /// A node may be the "first node" of multiple macro calls in a macro backtrace.
162 /// The expansion of the outermost macro call site is returned in such cases.
163 pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<ExpnId> {
164     // get the macro expansion or return `None` if not found
165     // `macro_backtrace` importantly ignores desugaring expansions
166     let expn = macro_backtrace(node.span()).next()?.expn;
167
168     // get the parent node, possibly skipping over a statement
169     // if the parent is not found, it is sensible to return `Some(root)`
170     let hir = cx.tcx.hir();
171     let mut parent_iter = hir.parent_iter(node.hir_id());
172     let (parent_id, _) = match parent_iter.next() {
173         None => return Some(ExpnId::root()),
174         Some((_, Node::Stmt(_))) => match parent_iter.next() {
175             None => return Some(ExpnId::root()),
176             Some(next) => next,
177         },
178         Some(next) => next,
179     };
180
181     // get the macro expansion of the parent node
182     let parent_span = hir.span(parent_id);
183     let Some(parent_macro_call) = macro_backtrace(parent_span).next() else {
184         // the parent node is not in a macro
185         return Some(ExpnId::root());
186     };
187
188     if parent_macro_call.expn.is_descendant_of(expn) {
189         // `node` is input to a macro call
190         return None;
191     }
192
193     Some(parent_macro_call.expn)
194 }
195
196 /* Specific Macro Utils */
197
198 /// Is `def_id` of `std::panic`, `core::panic` or any inner implementation macros
199 pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool {
200     let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false };
201     matches!(
202         name,
203         sym::core_panic_macro
204             | sym::std_panic_macro
205             | sym::core_panic_2015_macro
206             | sym::std_panic_2015_macro
207             | sym::core_panic_2021_macro
208     )
209 }
210
211 /// Is `def_id` of `assert!` or `debug_assert!`
212 pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool {
213     let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false };
214     matches!(name, sym::assert_macro | sym::debug_assert_macro)
215 }
216
217 pub enum PanicExpn<'a> {
218     /// No arguments - `panic!()`
219     Empty,
220     /// A string literal or any `&str` - `panic!("message")` or `panic!(message)`
221     Str(&'a Expr<'a>),
222     /// A single argument that implements `Display` - `panic!("{}", object)`
223     Display(&'a Expr<'a>),
224     /// Anything else - `panic!("error {}: {}", a, b)`
225     Format(FormatArgsExpn<'a>),
226 }
227
228 impl<'a> PanicExpn<'a> {
229     pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
230         if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) {
231             return None;
232         }
233         let ExprKind::Call(callee, [arg]) = &expr.kind else { return None };
234         let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None };
235         let result = match path.segments.last().unwrap().ident.as_str() {
236             "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
237             "panic" | "panic_str" => Self::Str(arg),
238             "panic_display" => {
239                 let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None };
240                 Self::Display(e)
241             },
242             "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
243             _ => return None,
244         };
245         Some(result)
246     }
247 }
248
249 /// Finds the arguments of an `assert!` or `debug_assert!` macro call within the macro expansion
250 pub fn find_assert_args<'a>(
251     cx: &LateContext<'_>,
252     expr: &'a Expr<'a>,
253     expn: ExpnId,
254 ) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> {
255     find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p))
256 }
257
258 /// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro
259 /// expansion
260 pub fn find_assert_eq_args<'a>(
261     cx: &LateContext<'_>,
262     expr: &'a Expr<'a>,
263     expn: ExpnId,
264 ) -> Option<(&'a Expr<'a>, &'a Expr<'a>, PanicExpn<'a>)> {
265     find_assert_args_inner(cx, expr, expn).map(|([a, b], p)| (a, b, p))
266 }
267
268 fn find_assert_args_inner<'a, const N: usize>(
269     cx: &LateContext<'_>,
270     expr: &'a Expr<'a>,
271     expn: ExpnId,
272 ) -> Option<([&'a Expr<'a>; N], PanicExpn<'a>)> {
273     let macro_id = expn.expn_data().macro_def_id?;
274     let (expr, expn) = match cx.tcx.item_name(macro_id).as_str().strip_prefix("debug_") {
275         None => (expr, expn),
276         Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
277     };
278     let mut args = ArrayVec::new();
279     let mut panic_expn = None;
280     let _: Option<!> = for_each_expr(expr, |e| {
281         if args.is_full() {
282             if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
283                 panic_expn = PanicExpn::parse(cx, e);
284             }
285             ControlFlow::Continue(Descend::from(panic_expn.is_none()))
286         } else if is_assert_arg(cx, e, expn) {
287             args.push(e);
288             ControlFlow::Continue(Descend::No)
289         } else {
290             ControlFlow::Continue(Descend::Yes)
291         }
292     });
293     let args = args.into_inner().ok()?;
294     // if no `panic!(..)` is found, use `PanicExpn::Empty`
295     // to indicate that the default assertion message is used
296     let panic_expn = panic_expn.unwrap_or(PanicExpn::Empty);
297     Some((args, panic_expn))
298 }
299
300 fn find_assert_within_debug_assert<'a>(
301     cx: &LateContext<'_>,
302     expr: &'a Expr<'a>,
303     expn: ExpnId,
304     assert_name: Symbol,
305 ) -> Option<(&'a Expr<'a>, ExpnId)> {
306     for_each_expr(expr, |e| {
307         if !e.span.from_expansion() {
308             return ControlFlow::Continue(Descend::No);
309         }
310         let e_expn = e.span.ctxt().outer_expn();
311         if e_expn == expn {
312             ControlFlow::Continue(Descend::Yes)
313         } else if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) {
314             ControlFlow::Break((e, e_expn))
315         } else {
316             ControlFlow::Continue(Descend::No)
317         }
318     })
319 }
320
321 fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool {
322     if !expr.span.from_expansion() {
323         return true;
324     }
325     let result = macro_backtrace(expr.span).try_for_each(|macro_call| {
326         if macro_call.expn == assert_expn {
327             ControlFlow::Break(false)
328         } else {
329             match cx.tcx.item_name(macro_call.def_id) {
330                 // `cfg!(debug_assertions)` in `debug_assert!`
331                 sym::cfg => ControlFlow::CONTINUE,
332                 // assert!(other_macro!(..))
333                 _ => ControlFlow::Break(true),
334             }
335         }
336     });
337     match result {
338         ControlFlow::Break(is_assert_arg) => is_assert_arg,
339         ControlFlow::Continue(()) => true,
340     }
341 }
342
343 /// The format string doesn't exist in the HIR, so we reassemble it from source code
344 #[derive(Debug)]
345 pub struct FormatString {
346     /// Span of the whole format string literal, including `[r#]"`.
347     pub span: Span,
348     /// Snippet of the whole format string literal, including `[r#]"`.
349     pub snippet: String,
350     /// If the string is raw `r"..."`/`r#""#`, how many `#`s does it have on each side.
351     pub style: Option<usize>,
352     /// The unescaped value of the format string, e.g. `"val â€“ {}"` for the literal
353     /// `"val \u{2013} {}"`.
354     pub unescaped: String,
355     /// The format string split by format args like `{..}`.
356     pub parts: Vec<Symbol>,
357 }
358
359 impl FormatString {
360     fn new(cx: &LateContext<'_>, pieces: &Expr<'_>) -> Option<Self> {
361         // format_args!(r"a {} b \", 1);
362         //
363         // expands to
364         //
365         // ::core::fmt::Arguments::new_v1(&["a ", " b \\"],
366         //      &[::core::fmt::ArgumentV1::new_display(&1)]);
367         //
368         // where `pieces` is the expression `&["a ", " b \\"]`. It has the span of `r"a {} b \"`
369         let span = pieces.span;
370         let snippet = snippet_opt(cx, span)?;
371
372         let (inner, style) = match tokenize(&snippet).next()?.kind {
373             TokenKind::Literal { kind, .. } => {
374                 let style = match kind {
375                     LiteralKind::Str { .. } => None,
376                     LiteralKind::RawStr { n_hashes: Some(n), .. } => Some(n.into()),
377                     _ => return None,
378                 };
379
380                 let start = style.map_or(1, |n| 2 + n);
381                 let end = snippet.len() - style.map_or(1, |n| 1 + n);
382
383                 (&snippet[start..end], style)
384             },
385             _ => return None,
386         };
387
388         let mode = if style.is_some() {
389             unescape::Mode::RawStr
390         } else {
391             unescape::Mode::Str
392         };
393
394         let mut unescaped = String::with_capacity(inner.len());
395         unescape_literal(inner, mode, &mut |_, ch| match ch {
396             Ok(ch) => unescaped.push(ch),
397             Err(e) if !e.is_fatal() => (),
398             Err(e) => panic!("{e:?}"),
399         });
400
401         let mut parts = Vec::new();
402         let _: Option<!> = for_each_expr(pieces, |expr| {
403             if let ExprKind::Lit(lit) = &expr.kind
404                 && let LitKind::Str(symbol, _) = lit.node
405             {
406                 parts.push(symbol);
407             }
408             ControlFlow::Continue(())
409         });
410
411         Some(Self {
412             span,
413             snippet,
414             style,
415             unescaped,
416             parts,
417         })
418     }
419 }
420
421 struct FormatArgsValues<'tcx> {
422     /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for
423     /// `format!("{x} {} {}", 1, z + 2)`.
424     value_args: Vec<&'tcx Expr<'tcx>>,
425     /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in
426     /// `value_args`
427     pos_to_value_index: Vec<usize>,
428     /// Used to check if a value is declared inline & to resolve `InnerSpan`s.
429     format_string_span: SpanData,
430 }
431
432 impl<'tcx> FormatArgsValues<'tcx> {
433     fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self {
434         let mut pos_to_value_index = Vec::new();
435         let mut value_args = Vec::new();
436         let _: Option<!> = for_each_expr(args, |expr| {
437             if expr.span.ctxt() == args.span.ctxt() {
438                 // ArgumentV1::new_<format_trait>(<val>)
439                 // ArgumentV1::from_usize(<val>)
440                 if let ExprKind::Call(callee, [val]) = expr.kind
441                     && let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind
442                     && let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind
443                     && path.segments.last().unwrap().ident.name == sym::ArgumentV1
444                 {
445                     let val_idx = if val.span.ctxt() == expr.span.ctxt()
446                         && let ExprKind::Field(_, field) = val.kind
447                         && let Ok(idx) = field.name.as_str().parse()
448                     {
449                         // tuple index
450                         idx
451                     } else {
452                         // assume the value expression is passed directly
453                         pos_to_value_index.len()
454                     };
455
456                     pos_to_value_index.push(val_idx);
457                 }
458                 ControlFlow::Continue(Descend::Yes)
459             } else {
460                 // assume that any expr with a differing span is a value
461                 value_args.push(expr);
462                 ControlFlow::Continue(Descend::No)
463             }
464         });
465
466         Self {
467             value_args,
468             pos_to_value_index,
469             format_string_span,
470         }
471     }
472 }
473
474 /// The positions of a format argument's value, precision and width
475 ///
476 /// A position is an index into the second argument of `Arguments::new_v1[_formatted]`
477 #[derive(Debug, Default, Copy, Clone)]
478 struct ParamPosition {
479     /// The position stored in `rt::v1::Argument::position`.
480     value: usize,
481     /// The position stored in `rt::v1::FormatSpec::width` if it is a `Count::Param`.
482     width: Option<usize>,
483     /// The position stored in `rt::v1::FormatSpec::precision` if it is a `Count::Param`.
484     precision: Option<usize>,
485 }
486
487 impl<'tcx> Visitor<'tcx> for ParamPosition {
488     fn visit_expr_field(&mut self, field: &'tcx ExprField<'tcx>) {
489         fn parse_count(expr: &Expr<'_>) -> Option<usize> {
490             // ::core::fmt::rt::v1::Count::Param(1usize),
491             if let ExprKind::Call(ctor, [val]) = expr.kind
492                 && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind
493                 && path.segments.last()?.ident.name == sym::Param
494                 && let ExprKind::Lit(lit) = &val.kind
495                 && let LitKind::Int(pos, _) = lit.node
496             {
497                 Some(pos as usize)
498             } else {
499                 None
500             }
501         }
502
503         match field.ident.name {
504             sym::position => {
505                 if let ExprKind::Lit(lit) = &field.expr.kind
506                     && let LitKind::Int(pos, _) = lit.node
507                 {
508                     self.value = pos as usize;
509                 }
510             },
511             sym::precision => {
512                 self.precision = parse_count(field.expr);
513             },
514             sym::width => {
515                 self.width = parse_count(field.expr);
516             },
517             _ => walk_expr(self, field.expr),
518         }
519     }
520 }
521
522 /// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)`
523 fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> {
524     if let ExprKind::AddrOf(.., array) = fmt_arg.kind
525         && let ExprKind::Array(specs) = array.kind
526     {
527         Some(specs.iter().map(|spec| {
528             let mut position = ParamPosition::default();
529             position.visit_expr(spec);
530             position
531         }))
532     } else {
533         None
534     }
535 }
536
537 /// `Span::from_inner`, but for `rustc_parse_format`'s `InnerSpan`
538 fn span_from_inner(base: SpanData, inner: rpf::InnerSpan) -> Span {
539     Span::new(
540         base.lo + BytePos::from_usize(inner.start),
541         base.lo + BytePos::from_usize(inner.end),
542         base.ctxt,
543         base.parent,
544     )
545 }
546
547 /// How a format parameter is used in the format string
548 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
549 pub enum FormatParamKind {
550     /// An implicit parameter , such as `{}` or `{:?}`.
551     Implicit,
552     /// A parameter with an explicit number, e.g. `{1}`, `{0:?}`, or `{:.0$}`
553     Numbered,
554     /// A parameter with an asterisk precision. e.g. `{:.*}`.
555     Starred,
556     /// A named parameter with a named `value_arg`, such as the `x` in `format!("{x}", x = 1)`.
557     Named(Symbol),
558     /// An implicit named parameter, such as the `y` in `format!("{y}")`.
559     NamedInline(Symbol),
560 }
561
562 /// Where a format parameter is being used in the format string
563 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
564 pub enum FormatParamUsage {
565     /// Appears as an argument, e.g. `format!("{}", foo)`
566     Argument,
567     /// Appears as a width, e.g. `format!("{:width$}", foo, width = 1)`
568     Width,
569     /// Appears as a precision, e.g. `format!("{:.precision$}", foo, precision = 1)`
570     Precision,
571 }
572
573 /// A `FormatParam` is any place in a `FormatArgument` that refers to a supplied value, e.g.
574 ///
575 /// ```
576 /// let precision = 2;
577 /// format!("{:.precision$}", 0.1234);
578 /// ```
579 ///
580 /// has two `FormatParam`s, a [`FormatParamKind::Implicit`] `.kind` with a `.value` of `0.1234`
581 /// and a [`FormatParamKind::NamedInline("precision")`] `.kind` with a `.value` of `2`
582 #[derive(Debug, Copy, Clone)]
583 pub struct FormatParam<'tcx> {
584     /// The expression this parameter refers to.
585     pub value: &'tcx Expr<'tcx>,
586     /// How this parameter refers to its `value`.
587     pub kind: FormatParamKind,
588     /// Where this format param is being used - argument/width/precision
589     pub usage: FormatParamUsage,
590     /// Span of the parameter, may be zero width. Includes the whitespace of implicit parameters.
591     ///
592     /// ```text
593     /// format!("{}, {  }, {0}, {name}", ...);
594     ///          ^    ~~    ~    ~~~~
595     /// ```
596     pub span: Span,
597 }
598
599 impl<'tcx> FormatParam<'tcx> {
600     fn new(
601         mut kind: FormatParamKind,
602         usage: FormatParamUsage,
603         position: usize,
604         inner: rpf::InnerSpan,
605         values: &FormatArgsValues<'tcx>,
606     ) -> Option<Self> {
607         let value_index = *values.pos_to_value_index.get(position)?;
608         let value = *values.value_args.get(value_index)?;
609         let span = span_from_inner(values.format_string_span, inner);
610
611         // if a param is declared inline, e.g. `format!("{x}")`, the generated expr's span points
612         // into the format string
613         if let FormatParamKind::Named(name) = kind && values.format_string_span.contains(value.span.data()) {
614             kind = FormatParamKind::NamedInline(name);
615         }
616
617         Some(Self {
618             value,
619             kind,
620             usage,
621             span,
622         })
623     }
624 }
625
626 /// Used by [width](https://doc.rust-lang.org/std/fmt/#width) and
627 /// [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers.
628 #[derive(Debug, Copy, Clone)]
629 pub enum Count<'tcx> {
630     /// Specified with a literal number, stores the value.
631     Is(usize, Span),
632     /// Specified using `$` and `*` syntaxes. The `*` format is still considered to be
633     /// `FormatParamKind::Numbered`.
634     Param(FormatParam<'tcx>),
635     /// Not specified.
636     Implied(Option<Span>),
637 }
638
639 impl<'tcx> Count<'tcx> {
640     fn new(
641         usage: FormatParamUsage,
642         count: rpf::Count<'_>,
643         position: Option<usize>,
644         inner: Option<rpf::InnerSpan>,
645         values: &FormatArgsValues<'tcx>,
646     ) -> Option<Self> {
647         let span = inner.map(|inner| span_from_inner(values.format_string_span, inner));
648
649         Some(match count {
650             rpf::Count::CountIs(val) => Self::Is(val, span?),
651             rpf::Count::CountIsName(name, _) => Self::Param(FormatParam::new(
652                 FormatParamKind::Named(Symbol::intern(name)),
653                 usage,
654                 position?,
655                 inner?,
656                 values,
657             )?),
658             rpf::Count::CountIsParam(_) => Self::Param(FormatParam::new(
659                 FormatParamKind::Numbered,
660                 usage,
661                 position?,
662                 inner?,
663                 values,
664             )?),
665             rpf::Count::CountIsStar(_) => Self::Param(FormatParam::new(
666                 FormatParamKind::Starred,
667                 usage,
668                 position?,
669                 inner?,
670                 values,
671             )?),
672             rpf::Count::CountImplied => Self::Implied(span),
673         })
674     }
675
676     pub fn is_implied(self) -> bool {
677         matches!(self, Count::Implied(_))
678     }
679
680     pub fn param(self) -> Option<FormatParam<'tcx>> {
681         match self {
682             Count::Param(param) => Some(param),
683             _ => None,
684         }
685     }
686
687     pub fn span(self) -> Option<Span> {
688         match self {
689             Count::Is(_, span) => Some(span),
690             Count::Param(param) => Some(param.span),
691             Count::Implied(span) => span,
692         }
693     }
694 }
695
696 /// Specification for the formatting of an argument in the format string. See
697 /// <https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters> for the precise meanings.
698 #[derive(Debug)]
699 pub struct FormatSpec<'tcx> {
700     /// Optionally specified character to fill alignment with.
701     pub fill: Option<char>,
702     /// Optionally specified alignment.
703     pub align: Alignment,
704     /// Packed version of various flags provided, see [`rustc_parse_format::Flag`].
705     pub flags: u32,
706     /// Represents either the maximum width or the integer precision.
707     pub precision: Count<'tcx>,
708     /// The minimum width, will be padded according to `width`/`align`
709     pub width: Count<'tcx>,
710     /// The formatting trait used by the argument, e.g. `sym::Display` for `{}`, `sym::Debug` for
711     /// `{:?}`.
712     pub r#trait: Symbol,
713     pub trait_span: Option<Span>,
714 }
715
716 impl<'tcx> FormatSpec<'tcx> {
717     fn new(spec: rpf::FormatSpec<'_>, positions: ParamPosition, values: &FormatArgsValues<'tcx>) -> Option<Self> {
718         Some(Self {
719             fill: spec.fill,
720             align: spec.align,
721             flags: spec.flags,
722             precision: Count::new(
723                 FormatParamUsage::Precision,
724                 spec.precision,
725                 positions.precision,
726                 spec.precision_span,
727                 values,
728             )?,
729             width: Count::new(
730                 FormatParamUsage::Width,
731                 spec.width,
732                 positions.width,
733                 spec.width_span,
734                 values,
735             )?,
736             r#trait: match spec.ty {
737                 "" => sym::Display,
738                 "?" => sym::Debug,
739                 "o" => sym!(Octal),
740                 "x" => sym!(LowerHex),
741                 "X" => sym!(UpperHex),
742                 "p" => sym::Pointer,
743                 "b" => sym!(Binary),
744                 "e" => sym!(LowerExp),
745                 "E" => sym!(UpperExp),
746                 _ => return None,
747             },
748             trait_span: spec
749                 .ty_span
750                 .map(|span| span_from_inner(values.format_string_span, span)),
751         })
752     }
753
754     /// Returns true if this format spec is unchanged from the default. e.g. returns true for `{}`,
755     /// `{foo}` and `{2}`, but false for `{:?}`, `{foo:5}` and `{3:.5}`
756     pub fn is_default(&self) -> bool {
757         self.r#trait == sym::Display && self.is_default_for_trait()
758     }
759
760     /// Has no other formatting specifiers than setting the format trait. returns true for `{}`,
761     /// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}`
762     pub fn is_default_for_trait(&self) -> bool {
763         self.width.is_implied()
764             && self.precision.is_implied()
765             && self.align == Alignment::AlignUnknown
766             && self.flags == 0
767     }
768 }
769
770 /// A format argument, such as `{}`, `{foo:?}`.
771 #[derive(Debug)]
772 pub struct FormatArg<'tcx> {
773     /// The parameter the argument refers to.
774     pub param: FormatParam<'tcx>,
775     /// How to format `param`.
776     pub format: FormatSpec<'tcx>,
777     /// span of the whole argument, `{..}`.
778     pub span: Span,
779 }
780
781 impl<'tcx> FormatArg<'tcx> {
782     /// Span of the `:` and format specifiers
783     ///
784     /// ```ignore
785     /// format!("{:.}"), format!("{foo:.}")
786     ///           ^^                  ^^
787     /// ```
788     pub fn format_span(&self) -> Span {
789         let base = self.span.data();
790
791         // `base.hi` is `{...}|`, subtract 1 byte (the length of '}') so that it points before the closing
792         // brace `{...|}`
793         Span::new(self.param.span.hi(), base.hi - BytePos(1), base.ctxt, base.parent)
794     }
795 }
796
797 /// A parsed `format_args!` expansion.
798 #[derive(Debug)]
799 pub struct FormatArgsExpn<'tcx> {
800     /// The format string literal.
801     pub format_string: FormatString,
802     /// The format arguments, such as `{:?}`.
803     pub args: Vec<FormatArg<'tcx>>,
804     /// Has an added newline due to `println!()`/`writeln!()`/etc. The last format string part will
805     /// include this added newline.
806     pub newline: bool,
807     /// Spans of the commas between the format string and explicit values, excluding any trailing
808     /// comma
809     ///
810     /// ```ignore
811     /// format!("..", 1, 2, 3,)
812     /// //          ^  ^  ^
813     /// ```
814     comma_spans: Vec<Span>,
815     /// Explicit values passed after the format string, ignoring implicit captures. `[1, z + 2]` for
816     /// `format!("{x} {} {y}", 1, z + 2)`.
817     explicit_values: Vec<&'tcx Expr<'tcx>>,
818 }
819
820 impl<'tcx> FormatArgsExpn<'tcx> {
821     /// Gets the spans of the commas inbetween the format string and explicit args, not including
822     /// any trailing comma
823     ///
824     /// ```ignore
825     /// format!("{} {}", a, b)
826     /// //             ^  ^
827     /// ```
828     ///
829     /// Ensures that the format string and values aren't coming from a proc macro that sets the
830     /// output span to that of its input
831     fn comma_spans(cx: &LateContext<'_>, explicit_values: &[&Expr<'_>], fmt_span: Span) -> Option<Vec<Span>> {
832         // `format!("{} {} {c}", "one", "two", c = "three")`
833         //                       ^^^^^  ^^^^^      ^^^^^^^
834         let value_spans = explicit_values
835             .iter()
836             .map(|val| hygiene::walk_chain(val.span, fmt_span.ctxt()));
837
838         // `format!("{} {} {c}", "one", "two", c = "three")`
839         //                     ^^     ^^     ^^^^^^
840         let between_spans = once(fmt_span)
841             .chain(value_spans)
842             .tuple_windows()
843             .map(|(start, end)| start.between(end));
844
845         let mut comma_spans = Vec::new();
846         for between_span in between_spans {
847             let mut offset = 0;
848             let mut seen_comma = false;
849
850             for token in tokenize(&snippet_opt(cx, between_span)?) {
851                 match token.kind {
852                     TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {},
853                     TokenKind::Comma if !seen_comma => {
854                         seen_comma = true;
855
856                         let base = between_span.data();
857                         comma_spans.push(Span::new(
858                             base.lo + BytePos(offset),
859                             base.lo + BytePos(offset + 1),
860                             base.ctxt,
861                             base.parent,
862                         ));
863                     },
864                     // named arguments, `start_val, name = end_val`
865                     //                            ^^^^^^^^^ between_span
866                     TokenKind::Ident | TokenKind::Eq if seen_comma => {},
867                     // An unexpected token usually indicates the format string or a value came from a proc macro output
868                     // that sets the span of its output to an input, e.g. `println!(some_proc_macro!("input"), ..)` that
869                     // emits a string literal with the span set to that of `"input"`
870                     _ => return None,
871                 }
872                 offset += token.len;
873             }
874
875             if !seen_comma {
876                 return None;
877             }
878         }
879
880         Some(comma_spans)
881     }
882
883     pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
884         let macro_name = macro_backtrace(expr.span)
885             .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
886             .find(|&name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))?;
887         let newline = macro_name == sym::format_args_nl;
888
889         // ::core::fmt::Arguments::new_v1(pieces, args)
890         // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg)
891         if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind
892             && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind
893             && is_path_diagnostic_item(cx, ty, sym::Arguments)
894             && matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted")
895         {
896             let format_string = FormatString::new(cx, pieces)?;
897
898             let mut parser = rpf::Parser::new(
899                 &format_string.unescaped,
900                 format_string.style,
901                 Some(format_string.snippet.clone()),
902                 // `format_string.unescaped` does not contain the appended newline
903                 false,
904                 rpf::ParseMode::Format,
905             );
906
907             let parsed_args = parser
908                 .by_ref()
909                 .filter_map(|piece| match piece {
910                     rpf::Piece::NextArgument(a) => Some(a),
911                     rpf::Piece::String(_) => None,
912                 })
913                 .collect_vec();
914             if !parser.errors.is_empty() {
915                 return None;
916             }
917
918             let positions = if let Some(fmt_arg) = rest.first() {
919                 // If the argument contains format specs, `new_v1_formatted(_, _, fmt, _)`, parse
920                 // them.
921
922                 Either::Left(parse_rt_fmt(fmt_arg)?)
923             } else {
924                 // If no format specs are given, the positions are in the given order and there are
925                 // no `precision`/`width`s to consider.
926
927                 Either::Right((0..).map(|n| ParamPosition {
928                     value: n,
929                     width: None,
930                     precision: None,
931                 }))
932             };
933
934             let values = FormatArgsValues::new(args, format_string.span.data());
935
936             let args = izip!(positions, parsed_args, parser.arg_places)
937                 .map(|(position, parsed_arg, arg_span)| {
938                     Some(FormatArg {
939                         param: FormatParam::new(
940                             match parsed_arg.position {
941                                 rpf::Position::ArgumentImplicitlyIs(_) => FormatParamKind::Implicit,
942                                 rpf::Position::ArgumentIs(_) => FormatParamKind::Numbered,
943                                 // NamedInline is handled by `FormatParam::new()`
944                                 rpf::Position::ArgumentNamed(name) => FormatParamKind::Named(Symbol::intern(name)),
945                             },
946                             FormatParamUsage::Argument,
947                             position.value,
948                             parsed_arg.position_span,
949                             &values,
950                         )?,
951                         format: FormatSpec::new(parsed_arg.format, position, &values)?,
952                         span: span_from_inner(values.format_string_span, arg_span),
953                     })
954                 })
955                 .collect::<Option<Vec<_>>>()?;
956
957             let mut explicit_values = values.value_args;
958             // remove values generated for implicitly captured vars
959             let len = explicit_values
960                 .iter()
961                 .take_while(|val| !format_string.span.contains(val.span))
962                 .count();
963             explicit_values.truncate(len);
964
965             let comma_spans = Self::comma_spans(cx, &explicit_values, format_string.span)?;
966
967             Some(Self {
968                 format_string,
969                 args,
970                 newline,
971                 comma_spans,
972                 explicit_values,
973             })
974         } else {
975             None
976         }
977     }
978
979     pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
980         for_each_expr(expr, |e| {
981             let e_ctxt = e.span.ctxt();
982             if e_ctxt == expr.span.ctxt() {
983                 ControlFlow::Continue(Descend::Yes)
984             } else if e_ctxt.outer_expn().is_descendant_of(expn_id) {
985                 if let Some(args) = FormatArgsExpn::parse(cx, e) {
986                     ControlFlow::Break(args)
987                 } else {
988                     ControlFlow::Continue(Descend::No)
989                 }
990             } else {
991                 ControlFlow::Continue(Descend::No)
992             }
993         })
994     }
995
996     /// Source callsite span of all inputs
997     pub fn inputs_span(&self) -> Span {
998         match *self.explicit_values {
999             [] => self.format_string.span,
1000             [.., last] => self
1001                 .format_string
1002                 .span
1003                 .to(hygiene::walk_chain(last.span, self.format_string.span.ctxt())),
1004         }
1005     }
1006
1007     /// Get the span of a value expanded to the previous comma, e.g. for the value `10`
1008     ///
1009     /// ```ignore
1010     /// format("{}.{}", 10, 11)
1011     /// //            ^^^^
1012     /// ```
1013     pub fn value_with_prev_comma_span(&self, value_id: HirId) -> Option<Span> {
1014         for (comma_span, value) in zip(&self.comma_spans, &self.explicit_values) {
1015             if value.hir_id == value_id {
1016                 return Some(comma_span.to(hygiene::walk_chain(value.span, comma_span.ctxt())));
1017             }
1018         }
1019
1020         None
1021     }
1022
1023     /// Iterator of all format params, both values and those referenced by `width`/`precision`s.
1024     pub fn params(&'tcx self) -> impl Iterator<Item = FormatParam<'tcx>> {
1025         self.args
1026             .iter()
1027             .flat_map(|arg| [Some(arg.param), arg.format.precision.param(), arg.format.width.param()])
1028             .flatten()
1029     }
1030 }
1031
1032 /// A node with a `HirId` and a `Span`
1033 pub trait HirNode {
1034     fn hir_id(&self) -> HirId;
1035     fn span(&self) -> Span;
1036 }
1037
1038 macro_rules! impl_hir_node {
1039     ($($t:ident),*) => {
1040         $(impl HirNode for hir::$t<'_> {
1041             fn hir_id(&self) -> HirId {
1042                 self.hir_id
1043             }
1044             fn span(&self) -> Span {
1045                 self.span
1046             }
1047         })*
1048     };
1049 }
1050
1051 impl_hir_node!(Expr, Pat);
1052
1053 impl HirNode for hir::Item<'_> {
1054     fn hir_id(&self) -> HirId {
1055         self.hir_id()
1056     }
1057
1058     fn span(&self) -> Span {
1059         self.span
1060     }
1061 }