Skip to content

Commit a491b92

Browse files
committed
Implement builtin#format_args, using rustc's format_args parser
1 parent 3431d58 commit a491b92

File tree

17 files changed

+206
-14
lines changed

17 files changed

+206
-14
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hir-def/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ triomphe.workspace = true
3333

3434
rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
3535
rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
36+
rustc_lexer = { version = "0.1.0", package = "ra-ap-rustc_lexer" }
3637

3738
# local deps
3839
stdx.workspace = true

crates/hir-def/src/body/lower.rs

+53-6
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@ use crate::{
2929
db::DefDatabase,
3030
expander::Expander,
3131
hir::{
32-
dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy,
33-
ClosureKind, Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm,
34-
Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
32+
dummy_expr_id,
33+
format_args::{
34+
self, FormatArgs, FormatArgument, FormatArgumentKind, FormatArgumentsCollector,
35+
},
36+
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
37+
Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
38+
OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
3539
},
3640
item_scope::BuiltinShadowMode,
3741
lang_item::LangItem,
@@ -649,15 +653,58 @@ impl ExprCollector<'_> {
649653
}
650654
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
651655
ast::Expr::AsmExpr(e) => {
652-
let expr = Expr::InlineAsm(InlineAsm { e: self.collect_expr_opt(e.expr()) });
653-
self.alloc_expr(expr, syntax_ptr)
656+
let e = self.collect_expr_opt(e.expr());
657+
self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr)
654658
}
655659
ast::Expr::OffsetOfExpr(e) => {
656660
let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
657661
let fields = e.fields().map(|it| it.as_name()).collect();
658662
self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr)
659663
}
660-
ast::Expr::FormatArgsExpr(_) => self.missing_expr(),
664+
ast::Expr::FormatArgsExpr(f) => {
665+
let mut args = FormatArgumentsCollector::new();
666+
f.args().for_each(|arg| {
667+
args.add(FormatArgument {
668+
kind: match arg.name() {
669+
Some(name) => FormatArgumentKind::Named(name.as_name()),
670+
None => FormatArgumentKind::Normal,
671+
},
672+
expr: self.collect_expr_opt(arg.expr()),
673+
});
674+
});
675+
let template = f.template();
676+
let fmt_snippet = template.as_ref().map(ToString::to_string);
677+
let expr = self.collect_expr_opt(f.template());
678+
if let Expr::Literal(Literal::String(_)) = self.body[expr] {
679+
let source = self.source_map.expr_map_back[expr].clone();
680+
let is_direct_literal = source.file_id == self.expander.current_file_id;
681+
if let ast::Expr::Literal(l) =
682+
source.value.to_node(&self.db.parse_or_expand(source.file_id))
683+
{
684+
if let ast::LiteralKind::String(s) = l.kind() {
685+
return Some(self.alloc_expr(
686+
Expr::FormatArgs(format_args::parse(
687+
expr,
688+
&s,
689+
fmt_snippet,
690+
args,
691+
is_direct_literal,
692+
)),
693+
syntax_ptr,
694+
));
695+
}
696+
}
697+
}
698+
699+
self.alloc_expr(
700+
Expr::FormatArgs(FormatArgs {
701+
template_expr: expr,
702+
template: Default::default(),
703+
arguments: args.finish(),
704+
}),
705+
syntax_ptr,
706+
)
707+
}
661708
})
662709
}
663710

crates/hir-def/src/body/pretty.rs

+5
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ impl Printer<'_> {
156156
Expr::Missing => w!(self, "�"),
157157
Expr::Underscore => w!(self, "_"),
158158
Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
159+
Expr::FormatArgs(_fmt_args) => {
160+
w!(self, "builtin#format_args(");
161+
// FIXME
162+
w!(self, ")");
163+
}
159164
Expr::OffsetOf(offset_of) => {
160165
w!(self, "builtin#offset_of(");
161166
self.print_type_ref(&offset_of.container);

crates/hir-def/src/hir.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
//! See also a neighboring `body` module.
1414
1515
pub mod type_ref;
16+
pub mod format_args;
1617

1718
use std::fmt;
1819

@@ -24,6 +25,7 @@ use syntax::ast;
2425

2526
use crate::{
2627
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
28+
hir::format_args::{FormatArgs, FormatArgumentKind},
2729
path::{GenericArgs, Path},
2830
type_ref::{Mutability, Rawness, TypeRef},
2931
BlockId, ConstBlockId,
@@ -117,7 +119,6 @@ impl From<ast::LiteralKind> for Literal {
117119
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
118120
use ast::LiteralKind;
119121
match ast_lit_kind {
120-
// FIXME: these should have actual values filled in, but unsure on perf impact
121122
LiteralKind::IntNumber(lit) => {
122123
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
123124
Literal::Float(
@@ -283,6 +284,7 @@ pub enum Expr {
283284
Underscore,
284285
OffsetOf(OffsetOf),
285286
InlineAsm(InlineAsm),
287+
FormatArgs(FormatArgs),
286288
}
287289

288290
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -355,7 +357,15 @@ impl Expr {
355357
match self {
356358
Expr::Missing => {}
357359
Expr::Path(_) | Expr::OffsetOf(_) => {}
358-
Expr::InlineAsm(e) => f(e.e),
360+
Expr::InlineAsm(it) => f(it.e),
361+
Expr::FormatArgs(it) => {
362+
f(it.template_expr);
363+
it.arguments
364+
.arguments
365+
.iter()
366+
.filter(|it| !matches!(it.kind, FormatArgumentKind::Captured(_)))
367+
.for_each(|it| f(it.expr));
368+
}
359369
Expr::If { condition, then_branch, else_branch } => {
360370
f(*condition);
361371
f(*then_branch);

crates/hir-expand/src/name.rs

+6
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ impl Name {
5454
Name(Repr::Text(text))
5555
}
5656

57+
// FIXME: See above, unfortunately some places really need this right now
58+
#[doc(hidden)]
59+
pub const fn new_text_dont_use(text: SmolStr) -> Name {
60+
Name(Repr::Text(text))
61+
}
62+
5763
pub fn new_tuple_field(idx: usize) -> Name {
5864
Name(Repr::TupleField(idx))
5965
}

crates/hir-ty/src/infer/closure.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ use chalk_ir::{
99
};
1010
use hir_def::{
1111
data::adt::VariantData,
12-
hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp},
12+
hir::{
13+
format_args::FormatArgumentKind, Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat,
14+
PatId, Statement, UnaryOp,
15+
},
1316
lang_item::LangItem,
1417
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
1518
DefWithBodyId, FieldId, HasModule, VariantId,
@@ -453,6 +456,14 @@ impl InferenceContext<'_> {
453456
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
454457
match &self.body[tgt_expr] {
455458
Expr::OffsetOf(_) => (),
459+
Expr::FormatArgs(fa) => {
460+
self.walk_expr_without_adjust(fa.template_expr);
461+
fa.arguments
462+
.arguments
463+
.iter()
464+
.filter(|it| !matches!(it.kind, FormatArgumentKind::Captured(_)))
465+
.for_each(|it| self.walk_expr_without_adjust(it.expr));
466+
}
456467
Expr::InlineAsm(e) => self.walk_expr_without_adjust(e.e),
457468
Expr::If { condition, then_branch, else_branch } => {
458469
self.consume_expr(*condition);

crates/hir-ty/src/infer/expr.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin
99
use hir_def::{
1010
generics::TypeOrConstParamData,
1111
hir::{
12-
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
12+
format_args::FormatArgumentKind, ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId,
13+
LabelId, Literal, Statement, UnaryOp,
1314
},
1415
lang_item::{LangItem, LangItemTarget},
1516
path::{GenericArg, GenericArgs},
@@ -848,6 +849,25 @@ impl InferenceContext<'_> {
848849
self.infer_expr_no_expect(it.e);
849850
self.result.standard_types.unit.clone()
850851
}
852+
Expr::FormatArgs(fa) => {
853+
fa.arguments
854+
.arguments
855+
.iter()
856+
.filter(|it| !matches!(it.kind, FormatArgumentKind::Captured(_)))
857+
.for_each(|it| _ = self.infer_expr_no_expect(it.expr));
858+
859+
match self
860+
.resolve_lang_item(LangItem::FormatArguments)
861+
.and_then(|it| it.as_struct())
862+
{
863+
Some(s) => {
864+
// NOTE: This struct has a lifetime parameter, but we don't currently emit
865+
// those to chalk
866+
TyKind::Adt(AdtId(s.into()), Substitution::empty(Interner)).intern(Interner)
867+
}
868+
None => self.err_ty(),
869+
}
870+
}
851871
};
852872
// use a new type variable if we got unknown here
853873
let ty = self.insert_type_vars_shallow(ty);

crates/hir-ty/src/infer/mutability.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
44
use chalk_ir::Mutability;
55
use hir_def::{
6-
hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
6+
hir::{
7+
format_args::FormatArgumentKind, Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId,
8+
Statement, UnaryOp,
9+
},
710
lang_item::LangItem,
811
};
912
use hir_expand::name;
@@ -37,6 +40,13 @@ impl InferenceContext<'_> {
3740
Expr::Missing => (),
3841
Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not),
3942
Expr::OffsetOf(_) => (),
43+
Expr::FormatArgs(fa) => {
44+
fa.arguments
45+
.arguments
46+
.iter()
47+
.filter(|it| !matches!(it.kind, FormatArgumentKind::Captured(_)))
48+
.for_each(|arg| self.infer_mut_expr_without_adjust(arg.expr, Mutability::Not));
49+
}
4050
&Expr::If { condition, then_branch, else_branch } => {
4151
self.infer_mut_expr(condition, Mutability::Not);
4252
self.infer_mut_expr(then_branch, Mutability::Not);

crates/hir-ty/src/mir/lower.rs

+3
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,9 @@ impl<'ctx> MirLowerCtx<'ctx> {
376376
Expr::InlineAsm(_) => {
377377
not_supported!("builtin#asm")
378378
}
379+
Expr::FormatArgs(_) => {
380+
not_supported!("builtin#format_args")
381+
}
379382
Expr::Missing => {
380383
if let DefWithBodyId::FunctionId(f) = self.owner {
381384
let assoc = f.lookup(self.db.upcast());

crates/hir-ty/src/tests/simple.rs

+22
Original file line numberDiff line numberDiff line change
@@ -3612,3 +3612,25 @@ fn main() {
36123612
"#,
36133613
);
36143614
}
3615+
3616+
#[test]
3617+
fn builtin_format_args() {
3618+
check_infer(
3619+
r#"
3620+
#[lang = "format_arguments"]
3621+
pub struct Arguments<'a>;
3622+
fn main() {
3623+
let are = "are";
3624+
builtin#format_args("hello {} friends, we {are} {0}{last}", "fancy", last = "!");
3625+
}
3626+
"#,
3627+
expect![[r#"
3628+
65..175 '{ ...!"); }': ()
3629+
75..78 'are': &str
3630+
81..86 '"are"': &str
3631+
92..172 'builti...= "!")': Arguments<'_>
3632+
152..159 '"fancy"': &str
3633+
168..171 '"!"': &str
3634+
"#]],
3635+
);
3636+
}

crates/parser/src/grammar/expressions/atom.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
219219
// test builtin_expr
220220
// fn foo() {
221221
// builtin#asm(0);
222-
// builtin#format_args(0);
222+
// builtin#format_args("", 0, 1, a = 2 + 3, a + b);
223223
// builtin#offset_of(Foo, bar.baz.0);
224224
// }
225225
fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
@@ -249,6 +249,24 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
249249
p.bump_remap(T![format_args]);
250250
p.expect(T!['(']);
251251
expr(p);
252+
if p.eat(T![,]) {
253+
while !p.at(EOF) && !p.at(T![')']) {
254+
let m = p.start();
255+
if p.at(IDENT) && p.nth_at(1, T![=]) {
256+
name(p);
257+
p.bump(T![=]);
258+
}
259+
if expr(p).is_none() {
260+
m.abandon(p);
261+
break;
262+
}
263+
m.complete(p, FORMAT_ARGS_ARG);
264+
265+
if !p.at(T![')']) {
266+
p.expect(T![,]);
267+
}
268+
}
269+
}
252270
p.expect(T![')']);
253271
Some(m.complete(p, FORMAT_ARGS_EXPR))
254272
} else if p.at_contextual_kw(T![asm]) {

crates/parser/src/syntax_kind/generated.rs

+1
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ pub enum SyntaxKind {
210210
OFFSET_OF_EXPR,
211211
ASM_EXPR,
212212
FORMAT_ARGS_EXPR,
213+
FORMAT_ARGS_ARG,
213214
CALL_EXPR,
214215
INDEX_EXPR,
215216
METHOD_CALL_EXPR,
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
fn foo() {
22
builtin#asm(0);
3-
builtin#format_args(0);
3+
builtin#format_args("", 0, 1, a = 2 + 3, a + b);
44
builtin#offset_of(Foo, bar.baz.0);
55
}

crates/syntax/rust.ungram

+7-1
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,13 @@ AsmExpr =
382382
Attr* 'builtin' '#' 'asm' '(' Expr ')'
383383

384384
FormatArgsExpr =
385-
Attr* 'builtin' '#' 'format_args' '(' ')'
385+
Attr* 'builtin' '#' 'format_args' '('
386+
template:Expr
387+
(',' args:(FormatArgsArg (',' FormatArgsArg)* ','?)? )?
388+
')'
389+
390+
FormatArgsArg =
391+
(Name '=')? Expr
386392

387393
MacroExpr =
388394
MacroCall

0 commit comments

Comments
 (0)