diff options
| author | Cameron Steffen <cam.steffen94@gmail.com> | 2021-07-06 11:51:15 -0500 |
|---|---|---|
| committer | Cameron Steffen <cam.steffen94@gmail.com> | 2021-07-13 08:57:16 -0500 |
| commit | e6ab222b814f20375041a5dd39e7605eed079c70 (patch) | |
| tree | 82ae7ab2e8dac56761496438d8ace4f44c317390 /clippy_utils | |
| parent | 20dbb277cf1b12546e7e604066b46d7b0f575bb0 (diff) | |
| download | rust-e6ab222b814f20375041a5dd39e7605eed079c70.tar.gz rust-e6ab222b814f20375041a5dd39e7605eed079c70.zip | |
Refactor format macro parsing
Diffstat (limited to 'clippy_utils')
| -rw-r--r-- | clippy_utils/src/higher.rs | 108 | ||||
| -rw-r--r-- | clippy_utils/src/paths.rs | 4 |
2 files changed, 106 insertions, 6 deletions
diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 8be36756b33..5ffd88f4140 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -5,11 +5,11 @@ use crate::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; -use rustc_ast::ast; +use rustc_ast::ast::{self, LitKind}; use rustc_hir as hir; use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp}; use rustc_lint::LateContext; -use rustc_span::source_map::Span; +use rustc_span::{sym, ExpnKind, Span, Symbol}; /// Converts a hir binary operator to the corresponding `ast` type. #[must_use] @@ -266,3 +266,107 @@ pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx } None } + +/// A parsed `format!` expansion +pub struct FormatExpn<'tcx> { + /// Span of `format!(..)` + pub call_site: Span, + /// Inner `format_args!` expansion + pub format_args: FormatArgsExpn<'tcx>, +} + +impl FormatExpn<'tcx> { + /// Parses an expanded `format!` invocation + pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> { + if_chain! { + if let ExprKind::Block(block, _) = expr.kind; + if let [stmt] = block.stmts; + if let StmtKind::Local(local) = stmt.kind; + if let Some(init) = local.init; + if let ExprKind::Call(_, [format_args]) = init.kind; + let expn_data = expr.span.ctxt().outer_expn_data(); + if let ExpnKind::Macro { name: sym::format, .. } = expn_data.kind; + if let Some(format_args) = FormatArgsExpn::parse(format_args); + then { + Some(FormatExpn { + call_site: expn_data.call_site, + format_args, + }) + } else { + None + } + } + } +} + +/// A parsed `format_args!` expansion +pub struct FormatArgsExpn<'tcx> { + /// Span of the first argument, the format string + pub format_string_span: Span, + /// Values passed after the format string + pub value_args: Vec<&'tcx Expr<'tcx>>, + + /// String literal expressions which represent the format string split by "{}" + pub format_string_parts: &'tcx [Expr<'tcx>], + /// Symbols corresponding to [`format_string_parts`] + pub format_string_symbols: Vec<Symbol>, + /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)` + pub args: &'tcx [Expr<'tcx>], + /// The final argument passed to `Arguments::new_v1_formatted`, if applicable + pub fmt_expr: Option<&'tcx Expr<'tcx>>, +} + +impl FormatArgsExpn<'tcx> { + /// Parses an expanded `format_args!` or `format_args_nl!` invocation + pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> { + if_chain! { + if let ExpnKind::Macro { name, .. } = expr.span.ctxt().outer_expn_data().kind; + let name = name.as_str(); + if name.ends_with("format_args") || name.ends_with("format_args_nl"); + if let ExprKind::Call(_, args) = expr.kind; + if let Some((strs_ref, args, fmt_expr)) = match args { + // Arguments::new_v1 + [strs_ref, args] => Some((strs_ref, args, None)), + // Arguments::new_v1_formatted + [strs_ref, args, fmt_expr] => Some((strs_ref, args, Some(fmt_expr))), + _ => None, + }; + if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind; + if let ExprKind::Array(format_string_parts) = strs_arr.kind; + if let Some(format_string_symbols) = format_string_parts + .iter() + .map(|e| { + if let ExprKind::Lit(lit) = &e.kind { + if let LitKind::Str(symbol, _style) = lit.node { + return Some(symbol); + } + } + None + }) + .collect(); + if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args.kind; + if let ExprKind::Match(args, [arm], _) = args.kind; + if let ExprKind::Tup(value_args) = args.kind; + if let Some(value_args) = value_args + .iter() + .map(|e| match e.kind { + ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }) + .collect(); + if let ExprKind::Array(args) = arm.body.kind; + then { + Some(FormatArgsExpn { + format_string_span: strs_ref.span, + value_args, + format_string_parts, + format_string_symbols, + args, + fmt_expr, + }) + } else { + None + } + } + } +} diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 9ebfbd6b423..c960eec3064 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -38,7 +38,6 @@ pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "defa pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"]; pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"]; pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"]; -pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"]; pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"]; pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"]; pub const DROP: [&str; 3] = ["core", "mem", "drop"]; @@ -50,9 +49,6 @@ pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"]; pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"]; pub const FILE: [&str; 3] = ["std", "fs", "File"]; pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; -pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; -pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; -pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"]; pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; |
