Auto merge of #106745 - m-ou-se:format-args-ast, r=oli-obk

Move format_args!() into AST (and expand it during AST lowering)

Implements https://github.com/rust-lang/compiler-team/issues/541

This moves FormatArgs from rustc_builtin_macros to rustc_ast_lowering. For now, the end result is the same. But this allows for future changes to do smarter things with format_args!(). It also allows Clippy to directly access the ast::FormatArgs, making things a lot easier.

This change turns the format args types into lang items. The builtin macro used to refer to them by their path. After this change, the path is no longer relevant, making it easier to make changes in `core`.

This updates clippy to use the new language items, but this doesn't yet make clippy use the ast::FormatArgs structure that's now available. That should be done after this is merged.
This commit is contained in:
bors 2023-01-26 12:44:47 +00:00
commit a64940f948
3 changed files with 36 additions and 25 deletions

View file

@ -7,14 +7,14 @@ use clippy_utils::macros::{
};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::ty::{implements_trait, is_type_lang_item};
use if_chain::if_chain;
use itertools::Itertools;
use rustc_errors::{
Applicability,
SuggestionStyle::{CompletelyHidden, ShowCode},
};
use rustc_hir::{Expr, ExprKind, HirId, QPath};
use rustc_hir::{Expr, ExprKind, HirId, LangItem, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_middle::ty::Ty;
@ -237,7 +237,7 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
);
}
if is_type_diagnostic_item(cx, param_ty, sym::Arguments) && !arg.format.is_default_for_trait() {
if is_type_lang_item(cx, param_ty, LangItem::FormatArguments) && !arg.format.is_default_for_trait() {
span_lint_and_then(
cx,
UNUSED_FORMAT_SPECS,

View file

@ -1,6 +1,5 @@
#![allow(clippy::similar_names)] // `expr` and `expn`
use crate::is_path_diagnostic_item;
use crate::source::snippet_opt;
use crate::visitors::{for_each_expr, Descend};
@ -8,7 +7,7 @@ use arrayvec::ArrayVec;
use itertools::{izip, Either, Itertools};
use rustc_ast::ast::LitKind;
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, Node, QPath};
use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind};
use rustc_lexer::unescape::unescape_literal;
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
use rustc_lint::LateContext;
@ -439,8 +438,7 @@ impl<'tcx> FormatArgsValues<'tcx> {
// ArgumentV1::from_usize(<val>)
if let ExprKind::Call(callee, [val]) = expr.kind
&& let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind
&& let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind
&& path.segments.last().unwrap().ident.name == sym::ArgumentV1
&& let TyKind::Path(QPath::LangItem(LangItem::FormatArgument, _, _)) = ty.kind
{
let val_idx = if val.span.ctxt() == expr.span.ctxt()
&& let ExprKind::Field(_, field) = val.kind
@ -486,20 +484,6 @@ struct ParamPosition {
impl<'tcx> Visitor<'tcx> for ParamPosition {
fn visit_expr_field(&mut self, field: &'tcx ExprField<'tcx>) {
fn parse_count(expr: &Expr<'_>) -> Option<usize> {
// ::core::fmt::rt::v1::Count::Param(1usize),
if let ExprKind::Call(ctor, [val]) = expr.kind
&& let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind
&& path.segments.last()?.ident.name == sym::Param
&& let ExprKind::Lit(lit) = &val.kind
&& let LitKind::Int(pos, _) = lit.node
{
Some(pos as usize)
} else {
None
}
}
match field.ident.name {
sym::position => {
if let ExprKind::Lit(lit) = &field.expr.kind
@ -519,15 +503,41 @@ impl<'tcx> Visitor<'tcx> for ParamPosition {
}
}
fn parse_count(expr: &Expr<'_>) -> Option<usize> {
// <::core::fmt::rt::v1::Count>::Param(1usize),
if let ExprKind::Call(ctor, [val]) = expr.kind
&& let ExprKind::Path(QPath::TypeRelative(_, path)) = ctor.kind
&& path.ident.name == sym::Param
&& let ExprKind::Lit(lit) = &val.kind
&& let LitKind::Int(pos, _) = lit.node
{
Some(pos as usize)
} else {
None
}
}
/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)`
fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> {
if let ExprKind::AddrOf(.., array) = fmt_arg.kind
&& let ExprKind::Array(specs) = array.kind
{
Some(specs.iter().map(|spec| {
let mut position = ParamPosition::default();
position.visit_expr(spec);
position
if let ExprKind::Call(f, args) = spec.kind
&& let ExprKind::Path(QPath::TypeRelative(ty, f)) = f.kind
&& let TyKind::Path(QPath::LangItem(LangItem::FormatPlaceholder, _, _)) = ty.kind
&& f.ident.name == sym::new
&& let [position, _fill, _align, _flags, precision, width] = args
&& let ExprKind::Lit(position) = &position.kind
&& let LitKind::Int(position, _) = position.node {
ParamPosition {
value: position as usize,
width: parse_count(width),
precision: parse_count(precision),
}
} else {
ParamPosition::default()
}
}))
} else {
None
@ -890,7 +900,7 @@ impl<'tcx> FormatArgsExpn<'tcx> {
// ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg)
if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind
&& let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind
&& is_path_diagnostic_item(cx, ty, sym::Arguments)
&& let TyKind::Path(QPath::LangItem(LangItem::FormatArguments, _, _)) = ty.kind
&& matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted")
{
let format_string = FormatString::new(cx, pieces)?;

View file

@ -219,6 +219,7 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::Repeat(..)
| ast::ExprKind::Ret(..)
| ast::ExprKind::Yeet(..)
| ast::ExprKind::FormatArgs(..)
| ast::ExprKind::Struct(..)
| ast::ExprKind::Try(..)
| ast::ExprKind::TryBlock(..)