+
+ /// Returns a vector of `FormatArgsArg`.
+ pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> {
+ if let Some(expr) = self.fmt_expr {
+ if_chain! {
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind;
+ if let ExprKind::Array(exprs) = expr.kind;
+ then {
+ exprs.iter().map(|fmt| {
+ if_chain! {
+ // struct `core::fmt::rt::v1::Argument`
+ if let ExprKind::Struct(_, fields, _) = fmt.kind;
+ if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position);
+ if let ExprKind::Lit(lit) = &position_field.expr.kind;
+ if let LitKind::Int(position, _) = lit.node;
+ then {
+ let i = usize::try_from(position).unwrap();
+ Some(FormatArgsArg { value: self.value_args[i], arg: &self.args[i], fmt: Some(fmt) })
+ } else {
+ None
+ }
+ }
+ }).collect()
+ } else {
+ None
+ }
+ }
+ } else {
+ Some(
+ self.value_args
+ .iter()
+ .zip(self.args.iter())
+ .map(|(value, arg)| FormatArgsArg { value, arg, fmt: None })
+ .collect(),
+ )
+ }
+ }
+}
+
+/// Type representing a `FormatArgsExpn`'s format arguments
+pub struct FormatArgsArg<'tcx> {
+ /// An element of `value_args` according to `position`
+ pub value: &'tcx Expr<'tcx>,
+ /// An element of `args` according to `position`
+ pub arg: &'tcx Expr<'tcx>,
+ /// An element of `fmt_expn`
+ pub fmt: Option<&'tcx Expr<'tcx>>,
+}
+
+impl<'tcx> FormatArgsArg<'tcx> {
+ /// Returns true if any formatting parameters are used that would have an effect on strings,
+ /// like `{:+2}` instead of just `{}`.
+ pub fn has_string_formatting(&self) -> bool {
+ self.fmt.map_or(false, |fmt| {
+ // `!` because these conditions check that `self` is unformatted.
+ !if_chain! {
+ // struct `core::fmt::rt::v1::Argument`
+ if let ExprKind::Struct(_, fields, _) = fmt.kind;
+ if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
+ // struct `core::fmt::rt::v1::FormatSpec`
+ if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind;
+ let mut precision_found = false;
+ let mut width_found = false;
+ if subfields.iter().all(|field| {
+ match field.ident.name {
+ sym::precision => {
+ precision_found = true;
+ if let ExprKind::Path(ref precision_path) = field.expr.kind {
+ last_path_segment(precision_path).ident.name == sym::Implied
+ } else {
+ false
+ }
+ }
+ sym::width => {
+ width_found = true;
+ if let ExprKind::Path(ref width_qpath) = field.expr.kind {
+ last_path_segment(width_qpath).ident.name == sym::Implied
+ } else {
+ false
+ }
+ }
+ _ => true,
+ }
+ });
+ if precision_found && width_found;
+ then { true } else { false }
+ }
+ })
+ }
+
+ /// Returns true if the argument is formatted using `Display::fmt`.
+ pub fn is_display(&self) -> bool {
+ if_chain! {
+ if let ExprKind::Call(_, [_, format_field]) = self.arg.kind;
+ if let ExprKind::Path(QPath::Resolved(_, path)) = format_field.kind;
+ if let [.., t, _] = path.segments;
+ if t.ident.name == sym::Display;
+ then { true } else { false }
+ }
+ }