Auto merge of #15559 - Veykril:builtin-format-args, r=Veykril

Implement builtin#format_args, using rustc's format_args parser

`format_args!` now expands to `builtin#format_args(template, args...)`, the actual expansion now instead happens in lowering where we desugar this expression by using lang paths.

As a bonus, we no longer need to evaluate `format_args` as an eager macro which means less macro expansions overall -> less cache thrashing!

Fixes https://github.com/rust-lang/rust-analyzer/issues/15082
This commit is contained in:
bors 2023-09-06 17:44:33 +00:00
commit f29867bd26
40 changed files with 2581 additions and 476 deletions

1
Cargo.lock generated
View file

@ -541,6 +541,7 @@ dependencies = [
"mbe", "mbe",
"once_cell", "once_cell",
"profile", "profile",
"ra-ap-rustc_lexer",
"rustc-hash", "rustc-hash",
"smallvec", "smallvec",
"stdx", "stdx",

View file

@ -33,6 +33,7 @@ triomphe.workspace = true
rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false } rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false } rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
rustc_lexer = { version = "0.1.0", package = "ra-ap-rustc_lexer" }
# local deps # local deps
stdx.workspace = true stdx.workspace = true

View file

@ -25,13 +25,20 @@ use triomphe::Arc;
use crate::{ use crate::{
body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr}, body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
builtin_type::BuiltinUint,
data::adt::StructKind, data::adt::StructKind,
db::DefDatabase, db::DefDatabase,
expander::Expander, expander::Expander,
hir::{ hir::{
dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, dummy_expr_id,
ClosureKind, Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, format_args::{
Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement, self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
FormatPlaceholder, FormatSign, FormatTrait,
},
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
}, },
item_scope::BuiltinShadowMode, item_scope::BuiltinShadowMode,
lang_item::LangItem, lang_item::LangItem,
@ -42,6 +49,8 @@ use crate::{
AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro, AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
}; };
type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
pub(super) fn lower( pub(super) fn lower(
db: &dyn DefDatabase, db: &dyn DefDatabase,
owner: DefWithBodyId, owner: DefWithBodyId,
@ -649,15 +658,15 @@ impl ExprCollector<'_> {
} }
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr), ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
ast::Expr::AsmExpr(e) => { ast::Expr::AsmExpr(e) => {
let expr = Expr::InlineAsm(InlineAsm { e: self.collect_expr_opt(e.expr()) }); let e = self.collect_expr_opt(e.expr());
self.alloc_expr(expr, syntax_ptr) self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr)
} }
ast::Expr::OffsetOfExpr(e) => { ast::Expr::OffsetOfExpr(e) => {
let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
let fields = e.fields().map(|it| it.as_name()).collect(); let fields = e.fields().map(|it| it.as_name()).collect();
self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr) self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr)
} }
ast::Expr::FormatArgsExpr(_) => self.missing_expr(), ast::Expr::FormatArgsExpr(f) => self.collect_format_args(f, syntax_ptr),
}) })
} }
@ -1557,6 +1566,401 @@ impl ExprCollector<'_> {
} }
} }
// endregion: labels // endregion: labels
// region: format
fn expand_macros_to_string(&mut self, expr: ast::Expr) -> Option<(ast::String, bool)> {
let m = match expr {
ast::Expr::MacroExpr(m) => m,
ast::Expr::Literal(l) => {
return match l.kind() {
ast::LiteralKind::String(s) => Some((s, true)),
_ => None,
}
}
_ => return None,
};
let e = m.macro_call()?;
let macro_ptr = AstPtr::new(&e);
let (exp, _) = self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
expansion.and_then(|it| this.expand_macros_to_string(it))
})?;
Some((exp, false))
}
fn collect_format_args(
&mut self,
f: ast::FormatArgsExpr,
syntax_ptr: AstPtr<ast::Expr>,
) -> ExprId {
let mut args = FormatArgumentsCollector::new();
f.args().for_each(|arg| {
args.add(FormatArgument {
kind: match arg.name() {
Some(name) => FormatArgumentKind::Named(name.as_name()),
None => FormatArgumentKind::Normal,
},
expr: self.collect_expr_opt(arg.expr()),
});
});
let template = f.template();
let fmt_snippet = template.as_ref().map(ToString::to_string);
let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) {
Some((s, is_direct_literal)) => {
format_args::parse(&s, fmt_snippet, args, is_direct_literal, |name| {
self.alloc_expr_desugared(Expr::Path(Path::from(name)))
})
}
None => FormatArgs { template: Default::default(), arguments: args.finish() },
};
// Create a list of all _unique_ (argument, format trait) combinations.
// E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
let mut argmap = FxIndexSet::default();
for piece in fmt.template.iter() {
let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
if let Ok(index) = placeholder.argument.index {
argmap.insert((index, ArgumentType::Format(placeholder.format_trait)));
}
}
let lit_pieces =
fmt.template
.iter()
.enumerate()
.filter_map(|(i, piece)| {
match piece {
FormatArgsPiece::Literal(s) => Some(
self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone()))),
),
&FormatArgsPiece::Placeholder(_) => {
// Inject empty string before placeholders when not already preceded by a literal piece.
if i == 0
|| matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_))
{
Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(
"".into(),
))))
} else {
None
}
}
}
})
.collect();
let lit_pieces = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
elements: lit_pieces,
is_assignee_expr: false,
}));
let lit_pieces = self.alloc_expr_desugared(Expr::Ref {
expr: lit_pieces,
rawness: Rawness::Ref,
mutability: Mutability::Shared,
});
let format_options = {
// Generate:
// &[format_spec_0, format_spec_1, format_spec_2]
let elements = fmt
.template
.iter()
.filter_map(|piece| {
let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
Some(self.make_format_spec(placeholder, &mut argmap))
})
.collect();
let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
elements,
is_assignee_expr: false,
}));
self.alloc_expr_desugared(Expr::Ref {
expr: array,
rawness: Rawness::Ref,
mutability: Mutability::Shared,
})
};
let arguments = &*fmt.arguments.arguments;
let args = if arguments.is_empty() {
let expr = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
elements: Box::default(),
is_assignee_expr: false,
}));
self.alloc_expr_desugared(Expr::Ref {
expr,
rawness: Rawness::Ref,
mutability: Mutability::Shared,
})
} else {
// Generate:
// &match (&arg0, &arg1, &…) {
// args => [
// <core::fmt::Argument>::new_display(args.0),
// <core::fmt::Argument>::new_lower_hex(args.1),
// <core::fmt::Argument>::new_debug(args.0),
// …
// ]
// }
let args = argmap
.iter()
.map(|&(arg_index, ty)| {
let arg = self.alloc_expr_desugared(Expr::Ref {
expr: arguments[arg_index].expr,
rawness: Rawness::Ref,
mutability: Mutability::Shared,
});
self.make_argument(arg, ty)
})
.collect();
let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
elements: args,
is_assignee_expr: false,
}));
self.alloc_expr_desugared(Expr::Ref {
expr: array,
rawness: Rawness::Ref,
mutability: Mutability::Shared,
})
};
// Generate:
// <core::fmt::Arguments>::new_v1_formatted(
// lit_pieces,
// args,
// format_options,
// unsafe { ::core::fmt::UnsafeArg::new() }
// )
let Some(new_v1_formatted) =
LangItem::FormatArguments.ty_rel_path(self.db, self.krate, name![new_v1_formatted])
else {
return self.missing_expr();
};
let Some(unsafe_arg_new) =
LangItem::FormatUnsafeArg.ty_rel_path(self.db, self.krate, name![new])
else {
return self.missing_expr();
};
let new_v1_formatted = self.alloc_expr_desugared(Expr::Path(new_v1_formatted));
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new));
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Call {
callee: unsafe_arg_new,
args: Box::default(),
is_assignee_expr: false,
});
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
id: None,
statements: Box::default(),
tail: Some(unsafe_arg_new),
});
self.alloc_expr(
Expr::Call {
callee: new_v1_formatted,
args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),
is_assignee_expr: false,
},
syntax_ptr,
)
}
/// Generate a hir expression for a format_args placeholder specification.
///
/// Generates
///
/// ```text
/// <core::fmt::rt::Placeholder::new(
/// …usize, // position
/// '…', // fill
/// <core::fmt::rt::Alignment>::…, // alignment
/// …u32, // flags
/// <core::fmt::rt::Count::…>, // width
/// <core::fmt::rt::Count::…>, // precision
/// )
/// ```
fn make_format_spec(
&mut self,
placeholder: &FormatPlaceholder,
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
) -> ExprId {
let position = match placeholder.argument.index {
Ok(arg_index) => {
let (i, _) =
argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
i as u128,
Some(BuiltinUint::Usize),
)))
}
Err(_) => self.missing_expr(),
};
let &FormatOptions {
ref width,
ref precision,
alignment,
fill,
sign,
alternate,
zero_pad,
debug_hex,
} = &placeholder.format_options;
let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' '))));
let align = {
let align = LangItem::FormatAlignment.ty_rel_path(
self.db,
self.krate,
match alignment {
Some(FormatAlignment::Left) => name![Left],
Some(FormatAlignment::Right) => name![Right],
Some(FormatAlignment::Center) => name![Center],
None => name![Unknown],
},
);
match align {
Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
None => self.missing_expr(),
}
};
// This needs to match `Flag` in library/core/src/fmt/rt.rs.
let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)
| ((sign == Some(FormatSign::Minus)) as u32) << 1
| (alternate as u32) << 2
| (zero_pad as u32) << 3
| ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4
| ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5;
let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
flags as u128,
Some(BuiltinUint::U32),
)));
let precision = self.make_count(&precision, argmap);
let width = self.make_count(&width, argmap);
let format_placeholder_new = {
let format_placeholder_new =
LangItem::FormatPlaceholder.ty_rel_path(self.db, self.krate, name![new]);
match format_placeholder_new {
Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
None => self.missing_expr(),
}
};
self.alloc_expr_desugared(Expr::Call {
callee: format_placeholder_new,
args: Box::new([position, fill, align, flags, precision, width]),
is_assignee_expr: false,
})
}
/// Generate a hir expression for a format_args Count.
///
/// Generates:
///
/// ```text
/// <core::fmt::rt::Count>::Is(…)
/// ```
///
/// or
///
/// ```text
/// <core::fmt::rt::Count>::Param(…)
/// ```
///
/// or
///
/// ```text
/// <core::fmt::rt::Count>::Implied
/// ```
fn make_count(
&mut self,
count: &Option<FormatCount>,
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
) -> ExprId {
match count {
Some(FormatCount::Literal(n)) => {
match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) {
Some(count_is) => {
let count_is = self.alloc_expr_desugared(Expr::Path(count_is));
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
*n as u128,
Some(BuiltinUint::Usize),
)));
self.alloc_expr_desugared(Expr::Call {
callee: count_is,
args: Box::new([args]),
is_assignee_expr: false,
})
}
None => self.missing_expr(),
}
}
Some(FormatCount::Argument(arg)) => {
if let Ok(arg_index) = arg.index {
let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Param]) {
Some(count_param) => {
let count_param = self.alloc_expr_desugared(Expr::Path(count_param));
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
i as u128,
Some(BuiltinUint::Usize),
)));
self.alloc_expr_desugared(Expr::Call {
callee: count_param,
args: Box::new([args]),
is_assignee_expr: false,
})
}
None => self.missing_expr(),
}
} else {
self.missing_expr()
}
}
None => match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Implied]) {
Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
None => self.missing_expr(),
},
}
}
/// Generate a hir expression representing an argument to a format_args invocation.
///
/// Generates:
///
/// ```text
/// <core::fmt::Argument>::new_…(arg)
/// ```
fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId {
use ArgumentType::*;
use FormatTrait::*;
match LangItem::FormatArgument.ty_rel_path(
self.db,
self.krate,
match ty {
Format(Display) => name![new_display],
Format(Debug) => name![new_debug],
Format(LowerExp) => name![new_lower_exp],
Format(UpperExp) => name![new_upper_exp],
Format(Octal) => name![new_octal],
Format(Pointer) => name![new_pointer],
Format(Binary) => name![new_binary],
Format(LowerHex) => name![new_lower_hex],
Format(UpperHex) => name![new_upper_hex],
Usize => name![from_usize],
},
) {
Some(new_fn) => {
let new_fn = self.alloc_expr_desugared(Expr::Path(new_fn));
self.alloc_expr_desugared(Expr::Call {
callee: new_fn,
args: Box::new([arg]),
is_assignee_expr: false,
})
}
None => self.missing_expr(),
}
}
// endregion: format
} }
fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> { fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> {
@ -1632,3 +2036,9 @@ fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
(|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))() (|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))()
.map_or(false, |it| it.kind() == syntax::T![,]) .map_or(false, |it| it.kind() == syntax::T![,])
} }
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
enum ArgumentType {
Format(FormatTrait),
Usize,
}

View file

@ -2,7 +2,6 @@
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use hir_expand::db::ExpandDatabase;
use itertools::Itertools; use itertools::Itertools;
use syntax::ast::HasName; use syntax::ast::HasName;
@ -52,8 +51,7 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
} }
}; };
let mut p = let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false };
Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false };
if let DefWithBodyId::FunctionId(it) = owner { if let DefWithBodyId::FunctionId(it) = owner {
p.buf.push('('); p.buf.push('(');
body.params.iter().zip(&db.function_data(it).params).for_each(|(&param, ty)| { body.params.iter().zip(&db.function_data(it).params).for_each(|(&param, ty)| {
@ -77,8 +75,7 @@ pub(super) fn print_expr_hir(
_owner: DefWithBodyId, _owner: DefWithBodyId,
expr: ExprId, expr: ExprId,
) -> String { ) -> String {
let mut p = let mut p = Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false };
Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false };
p.print_expr(expr); p.print_expr(expr);
p.buf p.buf
} }
@ -99,7 +96,7 @@ macro_rules! wln {
} }
struct Printer<'a> { struct Printer<'a> {
db: &'a dyn ExpandDatabase, db: &'a dyn DefDatabase,
body: &'a Body, body: &'a Body,
buf: String, buf: String,
indent_level: usize, indent_level: usize,
@ -143,9 +140,14 @@ impl Printer<'_> {
} }
fn newline(&mut self) { fn newline(&mut self) {
match self.buf.chars().rev().find(|ch| *ch != ' ') { match self.buf.chars().rev().find_position(|ch| *ch != ' ') {
Some('\n') | None => {} Some((_, '\n')) | None => {}
_ => writeln!(self).unwrap(), Some((idx, _)) => {
if idx != 0 {
self.buf.drain(self.buf.len() - idx..);
}
writeln!(self).unwrap()
}
} }
} }
@ -162,7 +164,10 @@ impl Printer<'_> {
w!( w!(
self, self,
", {})", ", {})",
offset_of.fields.iter().format_with(".", |field, f| f(&field.display(self.db))) offset_of
.fields
.iter()
.format_with(".", |field, f| f(&field.display(self.db.upcast())))
); );
} }
Expr::Path(path) => self.print_path(path), Expr::Path(path) => self.print_path(path),
@ -184,7 +189,7 @@ impl Printer<'_> {
} }
Expr::Loop { body, label } => { Expr::Loop { body, label } => {
if let Some(lbl) = label { if let Some(lbl) = label {
w!(self, "{}: ", self.body[*lbl].name.display(self.db)); w!(self, "{}: ", self.body[*lbl].name.display(self.db.upcast()));
} }
w!(self, "loop "); w!(self, "loop ");
self.print_expr(*body); self.print_expr(*body);
@ -204,7 +209,7 @@ impl Printer<'_> {
} }
Expr::MethodCall { receiver, method_name, args, generic_args } => { Expr::MethodCall { receiver, method_name, args, generic_args } => {
self.print_expr(*receiver); self.print_expr(*receiver);
w!(self, ".{}", method_name.display(self.db)); w!(self, ".{}", method_name.display(self.db.upcast()));
if let Some(args) = generic_args { if let Some(args) = generic_args {
w!(self, "::<"); w!(self, "::<");
print_generic_args(self.db, args, self).unwrap(); print_generic_args(self.db, args, self).unwrap();
@ -242,13 +247,13 @@ impl Printer<'_> {
Expr::Continue { label } => { Expr::Continue { label } => {
w!(self, "continue"); w!(self, "continue");
if let Some(lbl) = label { if let Some(lbl) = label {
w!(self, " {}", self.body[*lbl].name.display(self.db)); w!(self, " {}", self.body[*lbl].name.display(self.db.upcast()));
} }
} }
Expr::Break { expr, label } => { Expr::Break { expr, label } => {
w!(self, "break"); w!(self, "break");
if let Some(lbl) = label { if let Some(lbl) = label {
w!(self, " {}", self.body[*lbl].name.display(self.db)); w!(self, " {}", self.body[*lbl].name.display(self.db.upcast()));
} }
if let Some(expr) = expr { if let Some(expr) = expr {
self.whitespace(); self.whitespace();
@ -287,7 +292,7 @@ impl Printer<'_> {
w!(self, "{{"); w!(self, "{{");
self.indented(|p| { self.indented(|p| {
for field in &**fields { for field in &**fields {
w!(p, "{}: ", field.name.display(self.db)); w!(p, "{}: ", field.name.display(self.db.upcast()));
p.print_expr(field.expr); p.print_expr(field.expr);
wln!(p, ","); wln!(p, ",");
} }
@ -304,7 +309,7 @@ impl Printer<'_> {
} }
Expr::Field { expr, name } => { Expr::Field { expr, name } => {
self.print_expr(*expr); self.print_expr(*expr);
w!(self, ".{}", name.display(self.db)); w!(self, ".{}", name.display(self.db.upcast()));
} }
Expr::Await { expr } => { Expr::Await { expr } => {
self.print_expr(*expr); self.print_expr(*expr);
@ -442,7 +447,8 @@ impl Printer<'_> {
} }
Expr::Literal(lit) => self.print_literal(lit), Expr::Literal(lit) => self.print_literal(lit),
Expr::Block { id: _, statements, tail, label } => { Expr::Block { id: _, statements, tail, label } => {
let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db))); let label =
label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db.upcast())));
self.print_block(label.as_deref(), statements, tail); self.print_block(label.as_deref(), statements, tail);
} }
Expr::Unsafe { id: _, statements, tail } => { Expr::Unsafe { id: _, statements, tail } => {
@ -518,7 +524,7 @@ impl Printer<'_> {
w!(self, " {{"); w!(self, " {{");
self.indented(|p| { self.indented(|p| {
for arg in args.iter() { for arg in args.iter() {
w!(p, "{}: ", arg.name.display(self.db)); w!(p, "{}: ", arg.name.display(self.db.upcast()));
p.print_pat(arg.pat); p.print_pat(arg.pat);
wln!(p, ","); wln!(p, ",");
} }
@ -677,6 +683,6 @@ impl Printer<'_> {
BindingAnnotation::Ref => "ref ", BindingAnnotation::Ref => "ref ",
BindingAnnotation::RefMut => "ref mut ", BindingAnnotation::RefMut => "ref mut ",
}; };
w!(self, "{}{}", mode, name.display(self.db)); w!(self, "{}{}", mode, name.display(self.db.upcast()));
} }
} }

View file

@ -1,13 +1,13 @@
mod block; mod block;
use base_db::{fixture::WithFixture, SourceDatabase}; use base_db::{fixture::WithFixture, SourceDatabase};
use expect_test::Expect; use expect_test::{expect, Expect};
use crate::{test_db::TestDB, ModuleDefId}; use crate::{test_db::TestDB, ModuleDefId};
use super::*; use super::*;
fn lower(ra_fixture: &str) -> Arc<Body> { fn lower(ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
let db = TestDB::with_files(ra_fixture); let db = TestDB::with_files(ra_fixture);
let krate = db.crate_graph().iter().next().unwrap(); let krate = db.crate_graph().iter().next().unwrap();
@ -21,8 +21,10 @@ fn lower(ra_fixture: &str) -> Arc<Body> {
} }
} }
} }
let fn_def = fn_def.unwrap().into();
db.body(fn_def.unwrap().into()) let body = db.body(fn_def);
(db, body, fn_def)
} }
fn def_map_at(ra_fixture: &str) -> String { fn def_map_at(ra_fixture: &str) -> String {
@ -138,3 +140,84 @@ mod m {
"#, "#,
); );
} }
#[test]
fn desugar_builtin_format_args() {
// Regression test for a path resolution bug introduced with inner item handling.
let (db, body, def) = lower(
r#"
//- minicore: fmt
fn main() {
let are = "are";
let count = 10;
builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
}
"#,
);
expect![[r#"
fn main() {
let are = "are";
let count = 10;
builtin#lang(Arguments::new_v1_formatted)(
&[
"\"hello ", " ", " friends, we ", " ", "", "\"",
],
&[
builtin#lang(Argument::new_display)(
&count,
), builtin#lang(Argument::new_display)(
&"fancy",
), builtin#lang(Argument::new_debug)(
&are,
), builtin#lang(Argument::new_display)(
&"!",
),
],
&[
builtin#lang(Placeholder::new)(
0usize,
' ',
builtin#lang(Alignment::Unknown),
8u32,
builtin#lang(Count::Implied),
builtin#lang(Count::Is)(
2usize,
),
), builtin#lang(Placeholder::new)(
1usize,
' ',
builtin#lang(Alignment::Unknown),
0u32,
builtin#lang(Count::Implied),
builtin#lang(Count::Implied),
), builtin#lang(Placeholder::new)(
2usize,
' ',
builtin#lang(Alignment::Unknown),
0u32,
builtin#lang(Count::Implied),
builtin#lang(Count::Implied),
), builtin#lang(Placeholder::new)(
1usize,
' ',
builtin#lang(Alignment::Unknown),
0u32,
builtin#lang(Count::Implied),
builtin#lang(Count::Implied),
), builtin#lang(Placeholder::new)(
3usize,
' ',
builtin#lang(Alignment::Unknown),
0u32,
builtin#lang(Count::Implied),
builtin#lang(Count::Implied),
),
],
unsafe {
builtin#lang(UnsafeArg::new)()
},
);
}"#]]
.assert_eq(&body.pretty_print(&db, def))
}

View file

@ -13,6 +13,7 @@
//! See also a neighboring `body` module. //! See also a neighboring `body` module.
pub mod type_ref; pub mod type_ref;
pub mod format_args;
use std::fmt; use std::fmt;
@ -117,7 +118,6 @@ impl From<ast::LiteralKind> for Literal {
fn from(ast_lit_kind: ast::LiteralKind) -> Self { fn from(ast_lit_kind: ast::LiteralKind) -> Self {
use ast::LiteralKind; use ast::LiteralKind;
match ast_lit_kind { match ast_lit_kind {
// FIXME: these should have actual values filled in, but unsure on perf impact
LiteralKind::IntNumber(lit) => { LiteralKind::IntNumber(lit) => {
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) { if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
Literal::Float( Literal::Float(
@ -355,7 +355,7 @@ impl Expr {
match self { match self {
Expr::Missing => {} Expr::Missing => {}
Expr::Path(_) | Expr::OffsetOf(_) => {} Expr::Path(_) | Expr::OffsetOf(_) => {}
Expr::InlineAsm(e) => f(e.e), Expr::InlineAsm(it) => f(it.e),
Expr::If { condition, then_branch, else_branch } => { Expr::If { condition, then_branch, else_branch } => {
f(*condition); f(*condition);
f(*then_branch); f(*then_branch);

View file

@ -0,0 +1,503 @@
//! Parses `format_args` input.
use std::mem;
use hir_expand::name::Name;
use syntax::{
ast::{self, IsString},
AstToken, SmolStr, TextRange,
};
use crate::hir::ExprId;
mod parse;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FormatArgs {
pub template: Box<[FormatArgsPiece]>,
pub arguments: FormatArguments,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FormatArguments {
pub arguments: Box<[FormatArgument]>,
pub num_unnamed_args: usize,
pub num_explicit_args: usize,
pub names: Box<[(Name, usize)]>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FormatArgsPiece {
Literal(Box<str>),
Placeholder(FormatPlaceholder),
}
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
pub struct FormatPlaceholder {
/// Index into [`FormatArgs::arguments`].
pub argument: FormatArgPosition,
/// The span inside the format string for the full `{…}` placeholder.
pub span: Option<TextRange>,
/// `{}`, `{:?}`, or `{:x}`, etc.
pub format_trait: FormatTrait,
/// `{}` or `{:.5}` or `{:-^20}`, etc.
pub format_options: FormatOptions,
}
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
pub struct FormatArgPosition {
/// Which argument this position refers to (Ok),
/// or would've referred to if it existed (Err).
pub index: Result<usize, usize>,
/// What kind of position this is. See [`FormatArgPositionKind`].
pub kind: FormatArgPositionKind,
/// The span of the name or number.
pub span: Option<TextRange>,
}
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
pub enum FormatArgPositionKind {
/// `{}` or `{:.*}`
Implicit,
/// `{1}` or `{:1$}` or `{:.1$}`
Number,
/// `{a}` or `{:a$}` or `{:.a$}`
Named,
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum FormatTrait {
/// `{}`
Display,
/// `{:?}`
Debug,
/// `{:e}`
LowerExp,
/// `{:E}`
UpperExp,
/// `{:o}`
Octal,
/// `{:p}`
Pointer,
/// `{:b}`
Binary,
/// `{:x}`
LowerHex,
/// `{:X}`
UpperHex,
}
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
pub struct FormatOptions {
/// The width. E.g. `{:5}` or `{:width$}`.
pub width: Option<FormatCount>,
/// The precision. E.g. `{:.5}` or `{:.precision$}`.
pub precision: Option<FormatCount>,
/// The alignment. E.g. `{:>}` or `{:<}` or `{:^}`.
pub alignment: Option<FormatAlignment>,
/// The fill character. E.g. the `.` in `{:.>10}`.
pub fill: Option<char>,
/// The `+` or `-` flag.
pub sign: Option<FormatSign>,
/// The `#` flag.
pub alternate: bool,
/// The `0` flag. E.g. the `0` in `{:02x}`.
pub zero_pad: bool,
/// The `x` or `X` flag (for `Debug` only). E.g. the `x` in `{:x?}`.
pub debug_hex: Option<FormatDebugHex>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FormatSign {
/// The `+` flag.
Plus,
/// The `-` flag.
Minus,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FormatDebugHex {
/// The `x` flag in `{:x?}`.
Lower,
/// The `X` flag in `{:X?}`.
Upper,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FormatAlignment {
/// `{:<}`
Left,
/// `{:>}`
Right,
/// `{:^}`
Center,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FormatCount {
/// `{:5}` or `{:.5}`
Literal(usize),
/// `{:.*}`, `{:.5$}`, or `{:a$}`, etc.
Argument(FormatArgPosition),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FormatArgument {
pub kind: FormatArgumentKind,
pub expr: ExprId,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum FormatArgumentKind {
/// `format_args(…, arg)`
Normal,
/// `format_args(…, arg = 1)`
Named(Name),
/// `format_args("… {arg} …")`
Captured(Name),
}
// Only used in parse_args and report_invalid_references,
// to indicate how a referred argument was used.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum PositionUsedAs {
Placeholder(Option<TextRange>),
Precision,
Width,
}
use PositionUsedAs::*;
pub(crate) fn parse(
s: &ast::String,
fmt_snippet: Option<String>,
mut args: FormatArgumentsCollector,
is_direct_literal: bool,
mut synth: impl FnMut(Name) -> ExprId,
) -> FormatArgs {
let text = s.text();
let str_style = match s.quote_offsets() {
Some(offsets) => {
let raw = u32::from(offsets.quotes.0.len()) - 1;
(raw != 0).then_some(raw as usize)
}
None => None,
};
let mut parser =
parse::Parser::new(text, str_style, fmt_snippet, false, parse::ParseMode::Format);
let mut pieces = Vec::new();
while let Some(piece) = parser.next() {
if !parser.errors.is_empty() {
break;
} else {
pieces.push(piece);
}
}
let is_source_literal = parser.is_source_literal;
if !parser.errors.is_empty() {
// FIXME: Diagnose
return FormatArgs { template: Default::default(), arguments: args.finish() };
}
let to_span = |inner_span: parse::InnerSpan| {
is_source_literal.then(|| {
TextRange::new(inner_span.start.try_into().unwrap(), inner_span.end.try_into().unwrap())
})
};
let mut used = vec![false; args.explicit_args().len()];
let mut invalid_refs = Vec::new();
let mut numeric_refences_to_named_arg = Vec::new();
enum ArgRef<'a> {
Index(usize),
Name(&'a str, Option<TextRange>),
}
let mut lookup_arg = |arg: ArgRef<'_>,
span: Option<TextRange>,
used_as: PositionUsedAs,
kind: FormatArgPositionKind|
-> FormatArgPosition {
let index = match arg {
ArgRef::Index(index) => {
if let Some(arg) = args.by_index(index) {
used[index] = true;
if arg.kind.ident().is_some() {
// This was a named argument, but it was used as a positional argument.
numeric_refences_to_named_arg.push((index, span, used_as));
}
Ok(index)
} else {
// Doesn't exist as an explicit argument.
invalid_refs.push((index, span, used_as, kind));
Err(index)
}
}
ArgRef::Name(name, _span) => {
let name = Name::new_text_dont_use(SmolStr::new(name));
if let Some((index, _)) = args.by_name(&name) {
// Name found in `args`, so we resolve it to its index.
if index < args.explicit_args().len() {
// Mark it as used, if it was an explicit argument.
used[index] = true;
}
Ok(index)
} else {
// Name not found in `args`, so we add it as an implicitly captured argument.
if !is_direct_literal {
// For the moment capturing variables from format strings expanded from macros is
// disabled (see RFC #2795)
// FIXME: Diagnose
}
Ok(args.add(FormatArgument {
kind: FormatArgumentKind::Captured(name.clone()),
// FIXME: This is problematic, we might want to synthesize a dummy
// expression proper and/or desugar these.
expr: synth(name),
}))
}
}
};
FormatArgPosition { index, kind, span }
};
let mut template = Vec::new();
let mut unfinished_literal = String::new();
let mut placeholder_index = 0;
for piece in pieces {
match piece {
parse::Piece::String(s) => {
unfinished_literal.push_str(s);
}
parse::Piece::NextArgument(arg) => {
let parse::Argument { position, position_span, format } = *arg;
if !unfinished_literal.is_empty() {
template.push(FormatArgsPiece::Literal(
mem::take(&mut unfinished_literal).into_boxed_str(),
));
}
let span = parser.arg_places.get(placeholder_index).and_then(|&s| to_span(s));
placeholder_index += 1;
let position_span = to_span(position_span);
let argument = match position {
parse::ArgumentImplicitlyIs(i) => lookup_arg(
ArgRef::Index(i),
position_span,
Placeholder(span),
FormatArgPositionKind::Implicit,
),
parse::ArgumentIs(i) => lookup_arg(
ArgRef::Index(i),
position_span,
Placeholder(span),
FormatArgPositionKind::Number,
),
parse::ArgumentNamed(name) => lookup_arg(
ArgRef::Name(name, position_span),
position_span,
Placeholder(span),
FormatArgPositionKind::Named,
),
};
let alignment = match format.align {
parse::AlignUnknown => None,
parse::AlignLeft => Some(FormatAlignment::Left),
parse::AlignRight => Some(FormatAlignment::Right),
parse::AlignCenter => Some(FormatAlignment::Center),
};
let format_trait = match format.ty {
"" => FormatTrait::Display,
"?" => FormatTrait::Debug,
"e" => FormatTrait::LowerExp,
"E" => FormatTrait::UpperExp,
"o" => FormatTrait::Octal,
"p" => FormatTrait::Pointer,
"b" => FormatTrait::Binary,
"x" => FormatTrait::LowerHex,
"X" => FormatTrait::UpperHex,
_ => {
// FIXME: Diagnose
FormatTrait::Display
}
};
let precision_span = format.precision_span.and_then(to_span);
let precision = match format.precision {
parse::CountIs(n) => Some(FormatCount::Literal(n)),
parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg(
ArgRef::Name(name, to_span(name_span)),
precision_span,
Precision,
FormatArgPositionKind::Named,
))),
parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg(
ArgRef::Index(i),
precision_span,
Precision,
FormatArgPositionKind::Number,
))),
parse::CountIsStar(i) => Some(FormatCount::Argument(lookup_arg(
ArgRef::Index(i),
precision_span,
Precision,
FormatArgPositionKind::Implicit,
))),
parse::CountImplied => None,
};
let width_span = format.width_span.and_then(to_span);
let width = match format.width {
parse::CountIs(n) => Some(FormatCount::Literal(n)),
parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg(
ArgRef::Name(name, to_span(name_span)),
width_span,
Width,
FormatArgPositionKind::Named,
))),
parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg(
ArgRef::Index(i),
width_span,
Width,
FormatArgPositionKind::Number,
))),
parse::CountIsStar(_) => unreachable!(),
parse::CountImplied => None,
};
template.push(FormatArgsPiece::Placeholder(FormatPlaceholder {
argument,
span,
format_trait,
format_options: FormatOptions {
fill: format.fill,
alignment,
sign: format.sign.map(|s| match s {
parse::Sign::Plus => FormatSign::Plus,
parse::Sign::Minus => FormatSign::Minus,
}),
alternate: format.alternate,
zero_pad: format.zero_pad,
debug_hex: format.debug_hex.map(|s| match s {
parse::DebugHex::Lower => FormatDebugHex::Lower,
parse::DebugHex::Upper => FormatDebugHex::Upper,
}),
precision,
width,
},
}));
}
}
}
if !unfinished_literal.is_empty() {
template.push(FormatArgsPiece::Literal(unfinished_literal.into_boxed_str()));
}
if !invalid_refs.is_empty() {
// FIXME: Diagnose
}
let unused = used
.iter()
.enumerate()
.filter(|&(_, used)| !used)
.map(|(i, _)| {
let named = matches!(args.explicit_args()[i].kind, FormatArgumentKind::Named(_));
(args.explicit_args()[i].expr, named)
})
.collect::<Vec<_>>();
if !unused.is_empty() {
// FIXME: Diagnose
}
FormatArgs { template: template.into_boxed_slice(), arguments: args.finish() }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FormatArgumentsCollector {
arguments: Vec<FormatArgument>,
num_unnamed_args: usize,
num_explicit_args: usize,
names: Vec<(Name, usize)>,
}
impl FormatArgumentsCollector {
pub(crate) fn finish(self) -> FormatArguments {
FormatArguments {
arguments: self.arguments.into_boxed_slice(),
num_unnamed_args: self.num_unnamed_args,
num_explicit_args: self.num_explicit_args,
names: self.names.into_boxed_slice(),
}
}
pub fn new() -> Self {
Self { arguments: vec![], names: vec![], num_unnamed_args: 0, num_explicit_args: 0 }
}
pub fn add(&mut self, arg: FormatArgument) -> usize {
let index = self.arguments.len();
if let Some(name) = arg.kind.ident() {
self.names.push((name.clone(), index));
} else if self.names.is_empty() {
// Only count the unnamed args before the first named arg.
// (Any later ones are errors.)
self.num_unnamed_args += 1;
}
if !matches!(arg.kind, FormatArgumentKind::Captured(..)) {
// This is an explicit argument.
// Make sure that all arguments so far are explicit.
assert_eq!(
self.num_explicit_args,
self.arguments.len(),
"captured arguments must be added last"
);
self.num_explicit_args += 1;
}
self.arguments.push(arg);
index
}
pub fn by_name(&self, name: &Name) -> Option<(usize, &FormatArgument)> {
let &(_, i) = self.names.iter().find(|(n, _)| n == name)?;
Some((i, &self.arguments[i]))
}
pub fn by_index(&self, i: usize) -> Option<&FormatArgument> {
(i < self.num_explicit_args).then(|| &self.arguments[i])
}
pub fn unnamed_args(&self) -> &[FormatArgument] {
&self.arguments[..self.num_unnamed_args]
}
pub fn named_args(&self) -> &[FormatArgument] {
&self.arguments[self.num_unnamed_args..self.num_explicit_args]
}
pub fn explicit_args(&self) -> &[FormatArgument] {
&self.arguments[..self.num_explicit_args]
}
pub fn all_args(&self) -> &[FormatArgument] {
&self.arguments[..]
}
pub fn all_args_mut(&mut self) -> &mut Vec<FormatArgument> {
&mut self.arguments
}
}
impl FormatArgumentKind {
pub fn ident(&self) -> Option<&Name> {
match self {
Self::Normal => None,
Self::Named(id) => Some(id),
Self::Captured(id) => Some(id),
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -177,7 +177,7 @@ impl ItemTree {
} }
pub fn pretty_print(&self, db: &dyn DefDatabase) -> String { pub fn pretty_print(&self, db: &dyn DefDatabase) -> String {
pretty::print_item_tree(db.upcast(), self) pretty::print_item_tree(db, self)
} }
fn data(&self) -> &ItemTreeData { fn data(&self) -> &ItemTreeData {

View file

@ -2,8 +2,6 @@
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use hir_expand::db::ExpandDatabase;
use crate::{ use crate::{
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget}, generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
pretty::{print_path, print_type_bounds, print_type_ref}, pretty::{print_path, print_type_bounds, print_type_ref},
@ -12,7 +10,7 @@ use crate::{
use super::*; use super::*;
pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String { pub(super) fn print_item_tree(db: &dyn DefDatabase, tree: &ItemTree) -> String {
let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true }; let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) { if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
@ -45,7 +43,7 @@ macro_rules! wln {
} }
struct Printer<'a> { struct Printer<'a> {
db: &'a dyn ExpandDatabase, db: &'a dyn DefDatabase,
tree: &'a ItemTree, tree: &'a ItemTree,
buf: String, buf: String,
indent_level: usize, indent_level: usize,
@ -91,7 +89,7 @@ impl Printer<'_> {
self, self,
"#{}[{}{}]{}", "#{}[{}{}]{}",
inner, inner,
attr.path.display(self.db), attr.path.display(self.db.upcast()),
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(), attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
separated_by, separated_by,
); );
@ -106,7 +104,7 @@ impl Printer<'_> {
fn print_visibility(&mut self, vis: RawVisibilityId) { fn print_visibility(&mut self, vis: RawVisibilityId) {
match &self.tree[vis] { match &self.tree[vis] {
RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db)), RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db.upcast())),
RawVisibility::Public => w!(self, "pub "), RawVisibility::Public => w!(self, "pub "),
}; };
} }
@ -121,7 +119,7 @@ impl Printer<'_> {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field, "\n"); this.print_attrs_of(field, "\n");
this.print_visibility(*visibility); this.print_visibility(*visibility);
w!(this, "{}: ", name.display(self.db)); w!(this, "{}: ", name.display(self.db.upcast()));
this.print_type_ref(type_ref); this.print_type_ref(type_ref);
wln!(this, ","); wln!(this, ",");
} }
@ -135,7 +133,7 @@ impl Printer<'_> {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field]; let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field, "\n"); this.print_attrs_of(field, "\n");
this.print_visibility(*visibility); this.print_visibility(*visibility);
w!(this, "{}: ", name.display(self.db)); w!(this, "{}: ", name.display(self.db.upcast()));
this.print_type_ref(type_ref); this.print_type_ref(type_ref);
wln!(this, ","); wln!(this, ",");
} }
@ -168,20 +166,20 @@ impl Printer<'_> {
fn print_use_tree(&mut self, use_tree: &UseTree) { fn print_use_tree(&mut self, use_tree: &UseTree) {
match &use_tree.kind { match &use_tree.kind {
UseTreeKind::Single { path, alias } => { UseTreeKind::Single { path, alias } => {
w!(self, "{}", path.display(self.db)); w!(self, "{}", path.display(self.db.upcast()));
if let Some(alias) = alias { if let Some(alias) = alias {
w!(self, " as {}", alias); w!(self, " as {}", alias);
} }
} }
UseTreeKind::Glob { path } => { UseTreeKind::Glob { path } => {
if let Some(path) = path { if let Some(path) = path {
w!(self, "{}::", path.display(self.db)); w!(self, "{}::", path.display(self.db.upcast()));
} }
w!(self, "*"); w!(self, "*");
} }
UseTreeKind::Prefixed { prefix, list } => { UseTreeKind::Prefixed { prefix, list } => {
if let Some(prefix) = prefix { if let Some(prefix) = prefix {
w!(self, "{}::", prefix.display(self.db)); w!(self, "{}::", prefix.display(self.db.upcast()));
} }
w!(self, "{{"); w!(self, "{{");
for (i, tree) in list.iter().enumerate() { for (i, tree) in list.iter().enumerate() {
@ -209,7 +207,7 @@ impl Printer<'_> {
ModItem::ExternCrate(it) => { ModItem::ExternCrate(it) => {
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it]; let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "extern crate {}", name.display(self.db)); w!(self, "extern crate {}", name.display(self.db.upcast()));
if let Some(alias) = alias { if let Some(alias) = alias {
w!(self, " as {}", alias); w!(self, " as {}", alias);
} }
@ -256,7 +254,7 @@ impl Printer<'_> {
if let Some(abi) = abi { if let Some(abi) = abi {
w!(self, "extern \"{}\" ", abi); w!(self, "extern \"{}\" ", abi);
} }
w!(self, "fn {}", name.display(self.db)); w!(self, "fn {}", name.display(self.db.upcast()));
self.print_generic_params(explicit_generic_params); self.print_generic_params(explicit_generic_params);
w!(self, "("); w!(self, "(");
if !params.is_empty() { if !params.is_empty() {
@ -290,7 +288,7 @@ impl Printer<'_> {
ModItem::Struct(it) => { ModItem::Struct(it) => {
let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it]; let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "struct {}", name.display(self.db)); w!(self, "struct {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params); self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params); self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) { if matches!(fields, Fields::Record(_)) {
@ -302,7 +300,7 @@ impl Printer<'_> {
ModItem::Union(it) => { ModItem::Union(it) => {
let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it]; let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "union {}", name.display(self.db)); w!(self, "union {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params); self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params); self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) { if matches!(fields, Fields::Record(_)) {
@ -314,14 +312,14 @@ impl Printer<'_> {
ModItem::Enum(it) => { ModItem::Enum(it) => {
let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it]; let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "enum {}", name.display(self.db)); w!(self, "enum {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params); self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params); self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| { self.indented(|this| {
for variant in variants.clone() { for variant in variants.clone() {
let Variant { name, fields, ast_id: _ } = &this.tree[variant]; let Variant { name, fields, ast_id: _ } = &this.tree[variant];
this.print_attrs_of(variant, "\n"); this.print_attrs_of(variant, "\n");
w!(this, "{}", name.display(self.db)); w!(this, "{}", name.display(self.db.upcast()));
this.print_fields(fields); this.print_fields(fields);
wln!(this, ","); wln!(this, ",");
} }
@ -333,7 +331,7 @@ impl Printer<'_> {
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "const "); w!(self, "const ");
match name { match name {
Some(name) => w!(self, "{}", name.display(self.db)), Some(name) => w!(self, "{}", name.display(self.db.upcast())),
None => w!(self, "_"), None => w!(self, "_"),
} }
w!(self, ": "); w!(self, ": ");
@ -347,7 +345,7 @@ impl Printer<'_> {
if *mutable { if *mutable {
w!(self, "mut "); w!(self, "mut ");
} }
w!(self, "{}: ", name.display(self.db)); w!(self, "{}: ", name.display(self.db.upcast()));
self.print_type_ref(type_ref); self.print_type_ref(type_ref);
w!(self, " = _;"); w!(self, " = _;");
wln!(self); wln!(self);
@ -369,7 +367,7 @@ impl Printer<'_> {
if *is_auto { if *is_auto {
w!(self, "auto "); w!(self, "auto ");
} }
w!(self, "trait {}", name.display(self.db)); w!(self, "trait {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params); self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params); self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| { self.indented(|this| {
@ -382,7 +380,7 @@ impl Printer<'_> {
ModItem::TraitAlias(it) => { ModItem::TraitAlias(it) => {
let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it]; let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "trait {}", name.display(self.db)); w!(self, "trait {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params); self.print_generic_params(generic_params);
w!(self, " = "); w!(self, " = ");
self.print_where_clause(generic_params); self.print_where_clause(generic_params);
@ -415,7 +413,7 @@ impl Printer<'_> {
let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } = let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
&self.tree[it]; &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "type {}", name.display(self.db)); w!(self, "type {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params); self.print_generic_params(generic_params);
if !bounds.is_empty() { if !bounds.is_empty() {
w!(self, ": "); w!(self, ": ");
@ -432,7 +430,7 @@ impl Printer<'_> {
ModItem::Mod(it) => { ModItem::Mod(it) => {
let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it]; let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
w!(self, "mod {}", name.display(self.db)); w!(self, "mod {}", name.display(self.db.upcast()));
match kind { match kind {
ModKind::Inline { items } => { ModKind::Inline { items } => {
w!(self, " {{"); w!(self, " {{");
@ -450,16 +448,16 @@ impl Printer<'_> {
} }
ModItem::MacroCall(it) => { ModItem::MacroCall(it) => {
let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it]; let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it];
wln!(self, "{}!(...);", path.display(self.db)); wln!(self, "{}!(...);", path.display(self.db.upcast()));
} }
ModItem::MacroRules(it) => { ModItem::MacroRules(it) => {
let MacroRules { name, ast_id: _ } = &self.tree[it]; let MacroRules { name, ast_id: _ } = &self.tree[it];
wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db)); wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db.upcast()));
} }
ModItem::MacroDef(it) => { ModItem::MacroDef(it) => {
let MacroDef { name, visibility, ast_id: _ } = &self.tree[it]; let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility); self.print_visibility(*visibility);
wln!(self, "macro {} {{ ... }}", name.display(self.db)); wln!(self, "macro {} {{ ... }}", name.display(self.db.upcast()));
} }
} }
@ -491,7 +489,7 @@ impl Printer<'_> {
} }
first = false; first = false;
self.print_attrs_of(idx, " "); self.print_attrs_of(idx, " ");
w!(self, "{}", lt.name.display(self.db)); w!(self, "{}", lt.name.display(self.db.upcast()));
} }
for (idx, x) in params.type_or_consts.iter() { for (idx, x) in params.type_or_consts.iter() {
if !first { if !first {
@ -501,11 +499,11 @@ impl Printer<'_> {
self.print_attrs_of(idx, " "); self.print_attrs_of(idx, " ");
match x { match x {
TypeOrConstParamData::TypeParamData(ty) => match &ty.name { TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
Some(name) => w!(self, "{}", name.display(self.db)), Some(name) => w!(self, "{}", name.display(self.db.upcast())),
None => w!(self, "_anon_{}", idx.into_raw()), None => w!(self, "_anon_{}", idx.into_raw()),
}, },
TypeOrConstParamData::ConstParamData(konst) => { TypeOrConstParamData::ConstParamData(konst) => {
w!(self, "const {}: ", konst.name.display(self.db)); w!(self, "const {}: ", konst.name.display(self.db.upcast()));
self.print_type_ref(&konst.ty); self.print_type_ref(&konst.ty);
} }
} }
@ -540,8 +538,8 @@ impl Printer<'_> {
wln!( wln!(
this, this,
"{}: {},", "{}: {},",
target.name.display(self.db), target.name.display(self.db.upcast()),
bound.name.display(self.db) bound.name.display(self.db.upcast())
); );
continue; continue;
} }
@ -551,7 +549,7 @@ impl Printer<'_> {
if i != 0 { if i != 0 {
w!(this, ", "); w!(this, ", ");
} }
w!(this, "{}", lt.display(self.db)); w!(this, "{}", lt.display(self.db.upcast()));
} }
w!(this, "> "); w!(this, "> ");
(target, bound) (target, bound)
@ -562,7 +560,7 @@ impl Printer<'_> {
WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty), WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
WherePredicateTypeTarget::TypeOrConstParam(id) => { WherePredicateTypeTarget::TypeOrConstParam(id) => {
match &params.type_or_consts[*id].name() { match &params.type_or_consts[*id].name() {
Some(name) => w!(this, "{}", name.display(self.db)), Some(name) => w!(this, "{}", name.display(self.db.upcast())),
None => w!(this, "_anon_{}", id.into_raw()), None => w!(this, "_anon_{}", id.into_raw()),
} }
} }

View file

@ -2,6 +2,7 @@
//! //!
//! This attribute to tell the compiler about semi built-in std library //! This attribute to tell the compiler about semi built-in std library
//! features, such as Fn family of traits. //! features, such as Fn family of traits.
use hir_expand::name::Name;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use syntax::SmolStr; use syntax::SmolStr;
use triomphe::Arc; use triomphe::Arc;
@ -238,7 +239,17 @@ impl LangItem {
pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> { pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
let t = db.lang_item(start_crate, *self)?; let t = db.lang_item(start_crate, *self)?;
Some(Path::LangItem(t)) Some(Path::LangItem(t, None))
}
pub fn ty_rel_path(
&self,
db: &dyn DefDatabase,
start_crate: CrateId,
seg: Name,
) -> Option<Path> {
let t = db.lang_item(start_crate, *self)?;
Some(Path::LangItem(t, Some(seg)))
} }
} }

View file

@ -240,7 +240,7 @@ macro_rules! format_args {
} }
fn main() { fn main() {
::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(arg2), ::core::fmt::Debug::fmt), ]); builtin #format_args ("{} {:?}", arg1(a, b, c), arg2);
} }
"##]], "##]],
); );
@ -258,10 +258,10 @@ macro_rules! format_args {
fn main() { fn main() {
format_args!(x = 2); format_args!(x = 2);
format_args!(x =); format_args!/*+errors*/(x =);
format_args!(x =, x = 2); format_args!/*+errors*/(x =, x = 2);
format_args!("{}", x =); format_args!/*+errors*/("{}", x =);
format_args!(=, "{}", x =); format_args!/*+errors*/(=, "{}", x =);
format_args!(x = 2, "{}", 5); format_args!(x = 2, "{}", 5);
} }
"#, "#,
@ -273,12 +273,19 @@ macro_rules! format_args {
} }
fn main() { fn main() {
/* error: no rule matches input tokens */; builtin #format_args (x = 2);
/* error: expected expression */; /* parse error: expected expression */
/* error: expected expression, expected COMMA */; builtin #format_args (x = );
/* error: expected expression */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(), ::core::fmt::Display::fmt), ]); /* parse error: expected expression */
/* error: expected expression, expected R_PAREN */; /* parse error: expected R_PAREN */
::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(5), ::core::fmt::Display::fmt), ]); /* parse error: expected expression, item or let statement */
builtin #format_args (x = , x = 2);
/* parse error: expected expression */
builtin #format_args ("{}", x = );
/* parse error: expected expression */
/* parse error: expected expression */
builtin #format_args ( = , "{}", x = );
builtin #format_args (x = 2, "{}", 5);
} }
"##]], "##]],
); );
@ -306,7 +313,7 @@ macro_rules! format_args {
} }
fn main() { fn main() {
::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a::<A, B>()), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]); builtin #format_args ("{} {:?}", a::<A, B>(), b);
} }
"##]], "##]],
); );
@ -339,7 +346,7 @@ macro_rules! format_args {
} }
fn main() { fn main() {
::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::ArgumentV1::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]); builtin #format_args (r#"{},mismatch,"{}","{}""#, location_csv_pat(db, &analysis, vfs, &sm, pat_id), mismatch.expected.display(db), mismatch.actual.display(db));
} }
"##]], "##]],
); );
@ -373,7 +380,7 @@ macro_rules! format_args {
} }
fn main() { fn main() {
::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::ArgumentV1::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]); builtin #format_args (concat!("xxx{}y", "{:?}zzz"), 2, b);
} }
"##]], "##]],
); );
@ -403,8 +410,8 @@ macro_rules! format_args {
fn main() { fn main() {
let _ = let _ =
/* error: expected field name or number *//* parse error: expected field name or number */ /* parse error: expected field name or number */
::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(), ::core::fmt::Debug::fmt), ]); builtin #format_args ("{} {:?}", a.);
} }
"##]], "##]],
); );

View file

@ -117,7 +117,7 @@ fn main(foo: ()) {
macro_rules! format_args {} macro_rules! format_args {}
fn main(foo: ()) { fn main(foo: ()) {
/* error: unresolved macro identity */::core::fmt::Arguments::new_v1(&["", " ", " ", ], &[::core::fmt::ArgumentV1::new(&(::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(0), ::core::fmt::Display::fmt), ])), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(foo), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(identity!(10)), ::core::fmt::Display::fmt), ]) builtin #format_args ("{} {} {}", format_args!("{}", 0), foo, identity!(10), "bar")
} }
"##]], "##]],
); );
@ -150,8 +150,8 @@ macro_rules! identity {
} }
fn main(foo: ()) { fn main(foo: ()) {
// format_args/*+tokenids*/!("{} {} {}"#1,#3 format_args!("{}", 0#10),#12 foo#13,#14 identity!(10#18),#21 "bar"#22) // format_args/*+tokenids*/!("{} {} {}"#1,#2 format_args#3!#4("{}"#6,#7 0#8),#9 foo#10,#11 identity#12!#13(10#15),#16 "bar"#17)
::core#4294967295::fmt#4294967295::Arguments#4294967295::new_v1#4294967295(&#4294967295[#4294967295""#4294967295,#4294967295 " "#4294967295,#4294967295 " "#4294967295,#4294967295 ]#4294967295,#4294967295 &#4294967295[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(&#4294967295(::core#4294967295::fmt#4294967295::Arguments#4294967295::new_v1#4294967295(&#4294967295[#4294967295""#4294967295,#4294967295 ]#4294967295,#4294967295 &#4294967295[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(&#4294967295(#42949672950#10)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ]#4294967295)#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(&#4294967295(#4294967295foo#13)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(&#4294967295(#429496729510#18)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ]#4294967295)#4294967295 builtin#4294967295 ##4294967295format_args#4294967295 (#0"{} {} {}"#1,#2 format_args#3!#4(#5"{}"#6,#7 0#8)#5,#9 foo#10,#11 identity#12!#13(#1410#15)#14,#16 "bar"#17)#0
} }
"##]], "##]],

View file

@ -929,8 +929,8 @@ fn main() {
macro_rules! format_args {} macro_rules! format_args {}
fn main() { fn main() {
/* error: expected field name or number *//* parse error: expected field name or number */ /* parse error: expected field name or number */
::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(line.1.), ::core::fmt::Display::fmt), ]); builtin #format_args ("{}", line.1.);
} }
"##]], "##]],
@ -956,19 +956,15 @@ fn main() {
macro_rules! format_args {} macro_rules! format_args {}
fn main() { fn main() {
/* error: expected COMMA, expected R_BRACK, expected COMMA, expected COMMA, expected expression, expected R_PAREN *//* parse error: expected COMMA */ /* parse error: expected COMMA */
/* parse error: expected R_BRACK */ /* parse error: expected R_BRACK */
/* parse error: expected COMMA */ /* parse error: expected COMMA */
/* parse error: expected COMMA */ /* parse error: expected COMMA */
/* parse error: expected expression */ /* parse error: expected expression */
/* parse error: expected R_PAREN */ /* parse error: expected R_PAREN */
/* parse error: expected R_PAREN */
/* parse error: expected expression, item or let statement */ /* parse error: expected expression, item or let statement */
/* parse error: expected expression, item or let statement */ /* parse error: expected expression, item or let statement */
/* parse error: expected expression, item or let statement */ builtin #format_args ("{}", &[0 2]);
/* parse error: expected expression, item or let statement */
/* parse error: expected expression, item or let statement */
::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(&[0 2]), ::core::fmt::Display::fmt), ]);
} }
"##]], "##]],

View file

@ -47,7 +47,7 @@ pub enum Path {
}, },
/// A link to a lang item. It is used in desugaring of things like `it?`. We can show these /// A link to a lang item. It is used in desugaring of things like `it?`. We can show these
/// links via a normal path since they might be private and not accessible in the usage place. /// links via a normal path since they might be private and not accessible in the usage place.
LangItem(LangItemTarget), LangItem(LangItemTarget, Option<Name>),
} }
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This /// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
@ -122,33 +122,40 @@ impl Path {
pub fn kind(&self) -> &PathKind { pub fn kind(&self) -> &PathKind {
match self { match self {
Path::Normal { mod_path, .. } => &mod_path.kind, Path::Normal { mod_path, .. } => &mod_path.kind,
Path::LangItem(_) => &PathKind::Abs, Path::LangItem(..) => &PathKind::Abs,
} }
} }
pub fn type_anchor(&self) -> Option<&TypeRef> { pub fn type_anchor(&self) -> Option<&TypeRef> {
match self { match self {
Path::Normal { type_anchor, .. } => type_anchor.as_deref(), Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
Path::LangItem(_) => None, Path::LangItem(..) => None,
} }
} }
pub fn segments(&self) -> PathSegments<'_> { pub fn segments(&self) -> PathSegments<'_> {
let Path::Normal { mod_path, generic_args, .. } = self else { match self {
return PathSegments { segments: &[], generic_args: None }; Path::Normal { mod_path, generic_args, .. } => {
let s = PathSegments {
segments: mod_path.segments(),
generic_args: generic_args.as_deref(),
}; };
let s =
PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
if let Some(generic_args) = s.generic_args { if let Some(generic_args) = s.generic_args {
assert_eq!(s.segments.len(), generic_args.len()); assert_eq!(s.segments.len(), generic_args.len());
} }
s s
} }
Path::LangItem(_, seg) => PathSegments {
segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
generic_args: None,
},
}
}
pub fn mod_path(&self) -> Option<&ModPath> { pub fn mod_path(&self) -> Option<&ModPath> {
match self { match self {
Path::Normal { mod_path, .. } => Some(&mod_path), Path::Normal { mod_path, .. } => Some(&mod_path),
Path::LangItem(_) => None, Path::LangItem(..) => None,
} }
} }

View file

@ -2,18 +2,54 @@
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use hir_expand::{db::ExpandDatabase, mod_path::PathKind}; use hir_expand::mod_path::PathKind;
use intern::Interned; use intern::Interned;
use itertools::Itertools; use itertools::Itertools;
use crate::{ use crate::{
db::DefDatabase,
lang_item::LangItemTarget,
path::{GenericArg, GenericArgs, Path}, path::{GenericArg, GenericArgs, Path},
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef}, type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
}; };
pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result { pub(crate) fn print_path(db: &dyn DefDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
if let Path::LangItem(it) = path { if let Path::LangItem(it, s) = path {
return write!(buf, "$lang_item::{it:?}"); write!(buf, "builtin#lang(")?;
match *it {
LangItemTarget::ImplDef(it) => write!(buf, "{it:?}")?,
LangItemTarget::EnumId(it) => {
write!(buf, "{}", db.enum_data(it).name.display(db.upcast()))?
}
LangItemTarget::Function(it) => {
write!(buf, "{}", db.function_data(it).name.display(db.upcast()))?
}
LangItemTarget::Static(it) => {
write!(buf, "{}", db.static_data(it).name.display(db.upcast()))?
}
LangItemTarget::Struct(it) => {
write!(buf, "{}", db.struct_data(it).name.display(db.upcast()))?
}
LangItemTarget::Union(it) => {
write!(buf, "{}", db.union_data(it).name.display(db.upcast()))?
}
LangItemTarget::TypeAlias(it) => {
write!(buf, "{}", db.type_alias_data(it).name.display(db.upcast()))?
}
LangItemTarget::Trait(it) => {
write!(buf, "{}", db.trait_data(it).name.display(db.upcast()))?
}
LangItemTarget::EnumVariant(it) => write!(
buf,
"{}",
db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast())
)?,
}
if let Some(s) = s {
write!(buf, "::{}", s.display(db.upcast()))?;
}
return write!(buf, ")");
} }
match path.type_anchor() { match path.type_anchor() {
Some(anchor) => { Some(anchor) => {
@ -44,7 +80,7 @@ pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Wri
write!(buf, "::")?; write!(buf, "::")?;
} }
write!(buf, "{}", segment.name.display(db))?; write!(buf, "{}", segment.name.display(db.upcast()))?;
if let Some(generics) = segment.args_and_bindings { if let Some(generics) = segment.args_and_bindings {
write!(buf, "::<")?; write!(buf, "::<")?;
print_generic_args(db, generics, buf)?; print_generic_args(db, generics, buf)?;
@ -57,7 +93,7 @@ pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Wri
} }
pub(crate) fn print_generic_args( pub(crate) fn print_generic_args(
db: &dyn ExpandDatabase, db: &dyn DefDatabase,
generics: &GenericArgs, generics: &GenericArgs,
buf: &mut dyn Write, buf: &mut dyn Write,
) -> fmt::Result { ) -> fmt::Result {
@ -83,7 +119,7 @@ pub(crate) fn print_generic_args(
write!(buf, ", ")?; write!(buf, ", ")?;
} }
first = false; first = false;
write!(buf, "{}", binding.name.display(db))?; write!(buf, "{}", binding.name.display(db.upcast()))?;
if !binding.bounds.is_empty() { if !binding.bounds.is_empty() {
write!(buf, ": ")?; write!(buf, ": ")?;
print_type_bounds(db, &binding.bounds, buf)?; print_type_bounds(db, &binding.bounds, buf)?;
@ -97,19 +133,19 @@ pub(crate) fn print_generic_args(
} }
pub(crate) fn print_generic_arg( pub(crate) fn print_generic_arg(
db: &dyn ExpandDatabase, db: &dyn DefDatabase,
arg: &GenericArg, arg: &GenericArg,
buf: &mut dyn Write, buf: &mut dyn Write,
) -> fmt::Result { ) -> fmt::Result {
match arg { match arg {
GenericArg::Type(ty) => print_type_ref(db, ty, buf), GenericArg::Type(ty) => print_type_ref(db, ty, buf),
GenericArg::Const(c) => write!(buf, "{}", c.display(db)), GenericArg::Const(c) => write!(buf, "{}", c.display(db.upcast())),
GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)), GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast())),
} }
} }
pub(crate) fn print_type_ref( pub(crate) fn print_type_ref(
db: &dyn ExpandDatabase, db: &dyn DefDatabase,
type_ref: &TypeRef, type_ref: &TypeRef,
buf: &mut dyn Write, buf: &mut dyn Write,
) -> fmt::Result { ) -> fmt::Result {
@ -143,7 +179,7 @@ pub(crate) fn print_type_ref(
}; };
write!(buf, "&")?; write!(buf, "&")?;
if let Some(lt) = lt { if let Some(lt) = lt {
write!(buf, "{} ", lt.name.display(db))?; write!(buf, "{} ", lt.name.display(db.upcast()))?;
} }
write!(buf, "{mtbl}")?; write!(buf, "{mtbl}")?;
print_type_ref(db, pointee, buf)?; print_type_ref(db, pointee, buf)?;
@ -151,7 +187,7 @@ pub(crate) fn print_type_ref(
TypeRef::Array(elem, len) => { TypeRef::Array(elem, len) => {
write!(buf, "[")?; write!(buf, "[")?;
print_type_ref(db, elem, buf)?; print_type_ref(db, elem, buf)?;
write!(buf, "; {}]", len.display(db))?; write!(buf, "; {}]", len.display(db.upcast()))?;
} }
TypeRef::Slice(elem) => { TypeRef::Slice(elem) => {
write!(buf, "[")?; write!(buf, "[")?;
@ -198,7 +234,7 @@ pub(crate) fn print_type_ref(
} }
pub(crate) fn print_type_bounds( pub(crate) fn print_type_bounds(
db: &dyn ExpandDatabase, db: &dyn DefDatabase,
bounds: &[Interned<TypeBound>], bounds: &[Interned<TypeBound>],
buf: &mut dyn Write, buf: &mut dyn Write,
) -> fmt::Result { ) -> fmt::Result {
@ -216,10 +252,14 @@ pub(crate) fn print_type_bounds(
print_path(db, path, buf)?; print_path(db, path, buf)?;
} }
TypeBound::ForLifetime(lifetimes, path) => { TypeBound::ForLifetime(lifetimes, path) => {
write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?; write!(
buf,
"for<{}> ",
lifetimes.iter().map(|it| it.display(db.upcast())).format(", ")
)?;
print_path(db, path, buf)?; print_path(db, path, buf)?;
} }
TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?, TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast()))?,
TypeBound::Error => write!(buf, "{{unknown}}")?, TypeBound::Error => write!(buf, "{{unknown}}")?,
} }
} }

View file

@ -156,9 +156,8 @@ impl Resolver {
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> { ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
let path = match path { let path = match path {
Path::Normal { mod_path, .. } => mod_path, Path::Normal { mod_path, .. } => mod_path,
Path::LangItem(l) => { Path::LangItem(l, seg) => {
return Some(( let type_ns = match *l {
match *l {
LangItemTarget::Union(it) => TypeNs::AdtId(it.into()), LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it), LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()), LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
@ -168,10 +167,8 @@ impl Resolver {
LangItemTarget::Function(_) LangItemTarget::Function(_)
| LangItemTarget::ImplDef(_) | LangItemTarget::ImplDef(_)
| LangItemTarget::Static(_) => return None, | LangItemTarget::Static(_) => return None,
}, };
None, return Some((type_ns, seg.as_ref().map(|_| 1), None));
None,
))
} }
}; };
let first_name = path.segments().first()?; let first_name = path.segments().first()?;
@ -256,7 +253,7 @@ impl Resolver {
) -> Option<ResolveValueResult> { ) -> Option<ResolveValueResult> {
let path = match path { let path = match path {
Path::Normal { mod_path, .. } => mod_path, Path::Normal { mod_path, .. } => mod_path,
Path::LangItem(l) => { Path::LangItem(l, None) => {
return Some(ResolveValueResult::ValueNs( return Some(ResolveValueResult::ValueNs(
match *l { match *l {
LangItemTarget::Function(it) => ValueNs::FunctionId(it), LangItemTarget::Function(it) => ValueNs::FunctionId(it),
@ -272,6 +269,20 @@ impl Resolver {
None, None,
)) ))
} }
Path::LangItem(l, Some(_)) => {
let type_ns = match *l {
LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
LangItemTarget::Trait(it) => TypeNs::TraitId(it),
LangItemTarget::Function(_)
| LangItemTarget::ImplDef(_)
| LangItemTarget::Static(_) => return None,
};
return Some(ResolveValueResult::Partial(type_ns, 1, None));
}
}; };
let n_segments = path.segments().len(); let n_segments = path.segments().len();
let tmp = name![self]; let tmp = name![self];

View file

@ -1,13 +1,9 @@
//! Builtin macro //! Builtin macro
use std::mem;
use ::tt::Ident;
use base_db::{AnchoredPath, Edition, FileId}; use base_db::{AnchoredPath, Edition, FileId};
use cfg::CfgExpr; use cfg::CfgExpr;
use either::Either; use either::Either;
use mbe::{parse_exprs_with_sep, parse_to_token_tree, TokenMap}; use mbe::{parse_exprs_with_sep, parse_to_token_tree, TokenMap};
use rustc_hash::FxHashMap;
use syntax::{ use syntax::{
ast::{self, AstToken}, ast::{self, AstToken},
SmolStr, SmolStr,
@ -97,11 +93,11 @@ register_builtin! {
(unreachable, Unreachable) => unreachable_expand, (unreachable, Unreachable) => unreachable_expand,
(log_syntax, LogSyntax) => log_syntax_expand, (log_syntax, LogSyntax) => log_syntax_expand,
(trace_macros, TraceMacros) => trace_macros_expand, (trace_macros, TraceMacros) => trace_macros_expand,
EAGER:
(format_args, FormatArgs) => format_args_expand, (format_args, FormatArgs) => format_args_expand,
(const_format_args, ConstFormatArgs) => format_args_expand, (const_format_args, ConstFormatArgs) => format_args_expand,
(format_args_nl, FormatArgsNl) => format_args_nl_expand, (format_args_nl, FormatArgsNl) => format_args_nl_expand,
EAGER:
(compile_error, CompileError) => compile_error_expand, (compile_error, CompileError) => compile_error_expand,
(concat, Concat) => concat_expand, (concat, Concat) => concat_expand,
(concat_idents, ConcatIdents) => concat_idents_expand, (concat_idents, ConcatIdents) => concat_idents_expand,
@ -247,160 +243,22 @@ fn format_args_expand_general(
_db: &dyn ExpandDatabase, _db: &dyn ExpandDatabase,
_id: MacroCallId, _id: MacroCallId,
tt: &tt::Subtree, tt: &tt::Subtree,
end_string: &str, // FIXME: Make use of this so that mir interpretation works properly
_end_string: &str,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let args = parse_exprs_with_sep(tt, ','); let pound = quote! {@PUNCT '#'};
let mut tt = tt.clone();
let expand_error = tt.delimiter.kind = tt::DelimiterKind::Parenthesis;
ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into()); return ExpandResult::ok(quote! {
builtin #pound format_args #tt
let mut key_args = FxHashMap::default();
let mut args = args.into_iter().filter_map(|mut arg| {
// Remove `key =`.
if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
{
// but not with `==`
if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
{
let key = arg.token_trees.drain(..2).next().unwrap();
key_args.insert(key.to_string(), arg);
return None;
}
}
Some(arg)
}).collect::<Vec<_>>().into_iter();
// ^^^^^^^ we need this collect, to enforce the side effect of the filter_map closure (building the `key_args`)
let Some(format_subtree) = args.next() else {
return expand_error;
};
let format_string = (|| {
let token_tree = format_subtree.token_trees.get(0)?;
match token_tree {
tt::TokenTree::Leaf(l) => match l {
tt::Leaf::Literal(l) => {
if let Some(mut text) = l.text.strip_prefix('r') {
let mut raw_sharps = String::new();
while let Some(t) = text.strip_prefix('#') {
text = t;
raw_sharps.push('#');
}
text =
text.strip_suffix(&raw_sharps)?.strip_prefix('"')?.strip_suffix('"')?;
Some((text, l.span, Some(raw_sharps)))
} else {
let text = l.text.strip_prefix('"')?.strip_suffix('"')?;
let span = l.span;
Some((text, span, None))
}
}
_ => None,
},
tt::TokenTree::Subtree(_) => None,
}
})();
let Some((format_string, _format_string_span, raw_sharps)) = format_string else {
return expand_error;
};
let mut format_iter = format_string.chars().peekable();
let mut parts = vec![];
let mut last_part = String::new();
let mut arg_tts = vec![];
let mut err = None;
while let Some(c) = format_iter.next() {
// Parsing the format string. See https://doc.rust-lang.org/std/fmt/index.html#syntax for the grammar and more info
match c {
'{' => {
if format_iter.peek() == Some(&'{') {
format_iter.next();
last_part.push('{');
continue;
}
let mut argument = String::new();
while ![Some(&'}'), Some(&':')].contains(&format_iter.peek()) {
argument.push(match format_iter.next() {
Some(c) => c,
None => return expand_error,
}); });
} }
let format_spec = match format_iter.next().unwrap() {
'}' => "".to_owned(),
':' => {
let mut s = String::new();
while let Some(c) = format_iter.next() {
if c == '}' {
break;
}
s.push(c);
}
s
}
_ => unreachable!(),
};
parts.push(mem::take(&mut last_part));
let arg_tree = if argument.is_empty() {
match args.next() {
Some(it) => it,
None => {
err = Some(mbe::ExpandError::NoMatchingRule.into());
tt::Subtree::empty()
}
}
} else if let Some(tree) = key_args.get(&argument) {
tree.clone()
} else {
// FIXME: we should pick the related substring of the `_format_string_span` as the span. You
// can use `.char_indices()` instead of `.char()` for `format_iter` to find the substring interval.
let ident = Ident::new(argument, tt::TokenId::unspecified());
quote!(#ident)
};
let formatter = match &*format_spec {
"?" => quote!(::core::fmt::Debug::fmt),
"" => quote!(::core::fmt::Display::fmt),
_ => {
// FIXME: implement the rest and return expand error here
quote!(::core::fmt::Display::fmt)
}
};
arg_tts.push(quote! { ::core::fmt::ArgumentV1::new(&(#arg_tree), #formatter), });
}
'}' => {
if format_iter.peek() == Some(&'}') {
format_iter.next();
last_part.push('}');
} else {
return expand_error;
}
}
_ => last_part.push(c),
}
}
last_part += end_string;
if !last_part.is_empty() {
parts.push(last_part);
}
let part_tts = parts.into_iter().map(|it| {
let text = if let Some(raw) = &raw_sharps {
format!("r{raw}\"{}\"{raw}", it).into()
} else {
format!("\"{}\"", it).into()
};
let l = tt::Literal { span: tt::TokenId::unspecified(), text };
quote!(#l ,)
});
let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees);
let expanded = quote! {
::core::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts])
};
ExpandResult { value: expanded, err }
}
fn asm_expand( fn asm_expand(
_db: &dyn ExpandDatabase, _db: &dyn ExpandDatabase,
_id: MacroCallId, _id: MacroCallId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
// FIXME: parse asm here
// We expand all assembly snippets to `format_args!` invocations to get format syntax // We expand all assembly snippets to `format_args!` invocations to get format syntax
// highlighting for them. // highlighting for them.

View file

@ -54,6 +54,12 @@ impl Name {
Name(Repr::Text(text)) Name(Repr::Text(text))
} }
// FIXME: See above, unfortunately some places really need this right now
#[doc(hidden)]
pub const fn new_text_dont_use(text: SmolStr) -> Name {
Name(Repr::Text(text))
}
pub fn new_tuple_field(idx: usize) -> Name { pub fn new_tuple_field(idx: usize) -> Name {
Name(Repr::TupleField(idx)) Name(Repr::TupleField(idx))
} }
@ -302,6 +308,16 @@ pub mod known {
rust_2018, rust_2018,
rust_2021, rust_2021,
v1, v1,
new_display,
new_debug,
new_lower_exp,
new_upper_exp,
new_octal,
new_pointer,
new_binary,
new_lower_hex,
new_upper_hex,
from_usize,
// Components of known path (type name) // Components of known path (type name)
Iterator, Iterator,
IntoIterator, IntoIterator,
@ -327,6 +343,13 @@ pub mod known {
Not, Not,
None, None,
Index, Index,
Left,
Right,
Center,
Unknown,
Is,
Param,
Implied,
// Components of known path (function name) // Components of known path (function name)
filter_map, filter_map,
next, next,
@ -335,6 +358,8 @@ pub mod known {
is_empty, is_empty,
as_str, as_str,
new, new,
new_v1_formatted,
none,
// Builtin macros // Builtin macros
asm, asm,
assert, assert,

View file

@ -178,13 +178,30 @@ impl InferenceContext<'_> {
remaining_index: usize, remaining_index: usize,
id: ExprOrPatId, id: ExprOrPatId,
) -> Option<(ValueNs, Substitution)> { ) -> Option<(ValueNs, Substitution)> {
assert!(remaining_index < path.segments().len());
// there may be more intermediate segments between the resolved one and // there may be more intermediate segments between the resolved one and
// the end. Only the last segment needs to be resolved to a value; from // the end. Only the last segment needs to be resolved to a value; from
// the segments before that, we need to get either a type or a trait ref. // the segments before that, we need to get either a type or a trait ref.
let resolved_segment = path.segments().get(remaining_index - 1).unwrap(); let _d;
let remaining_segments = path.segments().skip(remaining_index); let (resolved_segment, remaining_segments) = match path {
Path::Normal { .. } => {
assert!(remaining_index < path.segments().len());
(
path.segments().get(remaining_index - 1).unwrap(),
path.segments().skip(remaining_index),
)
}
Path::LangItem(..) => (
PathSegment {
name: {
_d = hir_expand::name::known::Unknown;
&_d
},
args_and_bindings: None,
},
path.segments(),
),
};
let is_before_last = remaining_segments.len() == 1; let is_before_last = remaining_segments.len() == 1;
match (def, is_before_last) { match (def, is_before_last) {

View file

@ -3612,3 +3612,18 @@ fn main() {
"#, "#,
); );
} }
#[test]
fn builtin_format_args() {
check(
r#"
//- minicore: fmt
fn main() {
let are = "are";
let count = 10;
builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'_>
}
"#,
);
}

View file

@ -15,26 +15,13 @@ use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange};
// Move an expression out of a format string. // Move an expression out of a format string.
// //
// ``` // ```
// macro_rules! format_args { // # //- minicore: fmt
// ($lit:literal $(tt:tt)*) => { 0 },
// }
// macro_rules! print {
// ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
// }
//
// fn main() { // fn main() {
// print!("{var} {x + 1}$0"); // print!("{var} {x + 1}$0");
// } // }
// ``` // ```
// -> // ->
// ``` // ```
// macro_rules! format_args {
// ($lit:literal $(tt:tt)*) => { 0 },
// }
// macro_rules! print {
// ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
// }
//
// fn main() { // fn main() {
// print!("{var} {}"$0, x + 1); // print!("{var} {}"$0, x + 1);
// } // }
@ -158,37 +145,21 @@ mod tests {
use super::*; use super::*;
use crate::tests::check_assist; use crate::tests::check_assist;
const MACRO_DECL: &'static str = r#"
macro_rules! format_args {
($lit:literal $(tt:tt)*) => { 0 },
}
macro_rules! print {
($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
}
"#;
fn add_macro_decl(s: &'static str) -> String {
MACRO_DECL.to_string() + s
}
#[test] #[test]
fn multiple_middle_arg() { fn multiple_middle_arg() {
check_assist( check_assist(
extract_expressions_from_format_string, extract_expressions_from_format_string,
&add_macro_decl(
r#" r#"
//- minicore: fmt
fn main() { fn main() {
print!("{} {x + 1:b} {}$0", y + 2, 2); print!("{} {x + 1:b} {}$0", y + 2, 2);
} }
"#, "#,
),
&add_macro_decl(
r#" r#"
fn main() { fn main() {
print!("{} {:b} {}"$0, y + 2, x + 1, 2); print!("{} {:b} {}"$0, y + 2, x + 1, 2);
} }
"#, "#,
),
); );
} }
@ -196,20 +167,17 @@ fn main() {
fn single_arg() { fn single_arg() {
check_assist( check_assist(
extract_expressions_from_format_string, extract_expressions_from_format_string,
&add_macro_decl(
r#" r#"
//- minicore: fmt
fn main() { fn main() {
print!("{obj.value:b}$0",); print!("{obj.value:b}$0",);
} }
"#, "#,
),
&add_macro_decl(
r#" r#"
fn main() { fn main() {
print!("{:b}"$0, obj.value); print!("{:b}"$0, obj.value);
} }
"#, "#,
),
); );
} }
@ -217,20 +185,17 @@ fn main() {
fn multiple_middle_placeholders_arg() { fn multiple_middle_placeholders_arg() {
check_assist( check_assist(
extract_expressions_from_format_string, extract_expressions_from_format_string,
&add_macro_decl(
r#" r#"
//- minicore: fmt
fn main() { fn main() {
print!("{} {x + 1:b} {} {}$0", y + 2, 2); print!("{} {x + 1:b} {} {}$0", y + 2, 2);
} }
"#, "#,
),
&add_macro_decl(
r#" r#"
fn main() { fn main() {
print!("{} {:b} {} {}"$0, y + 2, x + 1, 2, $1); print!("{} {:b} {} {}"$0, y + 2, x + 1, 2, $1);
} }
"#, "#,
),
); );
} }
@ -238,20 +203,17 @@ fn main() {
fn multiple_trailing_args() { fn multiple_trailing_args() {
check_assist( check_assist(
extract_expressions_from_format_string, extract_expressions_from_format_string,
&add_macro_decl(
r#" r#"
//- minicore: fmt
fn main() { fn main() {
print!("{:b} {x + 1:b} {Struct(1, 2)}$0", 1); print!("{:b} {x + 1:b} {Struct(1, 2)}$0", 1);
} }
"#, "#,
),
&add_macro_decl(
r#" r#"
fn main() { fn main() {
print!("{:b} {:b} {}"$0, 1, x + 1, Struct(1, 2)); print!("{:b} {:b} {}"$0, 1, x + 1, Struct(1, 2));
} }
"#, "#,
),
); );
} }
@ -259,20 +221,17 @@ fn main() {
fn improper_commas() { fn improper_commas() {
check_assist( check_assist(
extract_expressions_from_format_string, extract_expressions_from_format_string,
&add_macro_decl(
r#" r#"
//- minicore: fmt
fn main() { fn main() {
print!("{} {x + 1:b} {Struct(1, 2)}$0", 1,); print!("{} {x + 1:b} {Struct(1, 2)}$0", 1,);
} }
"#, "#,
),
&add_macro_decl(
r#" r#"
fn main() { fn main() {
print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2)); print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2));
} }
"#, "#,
),
); );
} }
@ -280,20 +239,17 @@ fn main() {
fn nested_tt() { fn nested_tt() {
check_assist( check_assist(
extract_expressions_from_format_string, extract_expressions_from_format_string,
&add_macro_decl(
r#" r#"
//- minicore: fmt
fn main() { fn main() {
print!("My name is {} {x$0 + x}", stringify!(Paperino)) print!("My name is {} {x$0 + x}", stringify!(Paperino))
} }
"#, "#,
),
&add_macro_decl(
r#" r#"
fn main() { fn main() {
print!("My name is {} {}"$0, stringify!(Paperino), x + x) print!("My name is {} {}"$0, stringify!(Paperino), x + x)
} }
"#, "#,
),
); );
} }
@ -301,22 +257,19 @@ fn main() {
fn extract_only_expressions() { fn extract_only_expressions() {
check_assist( check_assist(
extract_expressions_from_format_string, extract_expressions_from_format_string,
&add_macro_decl(
r#" r#"
//- minicore: fmt
fn main() { fn main() {
let var = 1 + 1; let var = 1 + 1;
print!("foobar {var} {var:?} {x$0 + x}") print!("foobar {var} {var:?} {x$0 + x}")
} }
"#, "#,
),
&add_macro_decl(
r#" r#"
fn main() { fn main() {
let var = 1 + 1; let var = 1 + 1;
print!("foobar {var} {var:?} {}"$0, x + x) print!("foobar {var} {var:?} {}"$0, x + x)
} }
"#, "#,
),
); );
} }
} }

View file

@ -694,25 +694,12 @@ fn doctest_extract_expressions_from_format_string() {
check_doc_test( check_doc_test(
"extract_expressions_from_format_string", "extract_expressions_from_format_string",
r#####" r#####"
macro_rules! format_args { //- minicore: fmt
($lit:literal $(tt:tt)*) => { 0 },
}
macro_rules! print {
($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
}
fn main() { fn main() {
print!("{var} {x + 1}$0"); print!("{var} {x + 1}$0");
} }
"#####, "#####,
r#####" r#####"
macro_rules! format_args {
($lit:literal $(tt:tt)*) => { 0 },
}
macro_rules! print {
($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
}
fn main() { fn main() {
print!("{var} {}"$0, x + 1); print!("{var} {}"$0, x + 1);
} }

View file

@ -51,9 +51,7 @@ mod tests {
fn works_when_wrapped() { fn works_when_wrapped() {
check( check(
r#" r#"
macro_rules! format_args { //- minicore: fmt
($lit:literal $(tt:tt)*) => { 0 },
}
macro_rules! print { macro_rules! print {
($($arg:tt)*) => (std::io::_print(format_args!($($arg)*))); ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
} }
@ -70,9 +68,7 @@ fn main() {
fn no_completion_without_brace() { fn no_completion_without_brace() {
check( check(
r#" r#"
macro_rules! format_args { //- minicore: fmt
($lit:literal $(tt:tt)*) => { 0 },
}
fn main() { fn main() {
let foobar = 1; let foobar = 1;
format_args!("f$0"); format_args!("f$0");
@ -87,18 +83,13 @@ fn main() {
check_edit( check_edit(
"foobar", "foobar",
r#" r#"
macro_rules! format_args { //- minicore: fmt
($lit:literal $(tt:tt)*) => { 0 },
}
fn main() { fn main() {
let foobar = 1; let foobar = 1;
format_args!("{f$0"); format_args!("{f$0");
} }
"#, "#,
r#" r#"
macro_rules! format_args {
($lit:literal $(tt:tt)*) => { 0 },
}
fn main() { fn main() {
let foobar = 1; let foobar = 1;
format_args!("{foobar"); format_args!("{foobar");
@ -108,18 +99,13 @@ fn main() {
check_edit( check_edit(
"foobar", "foobar",
r#" r#"
macro_rules! format_args { //- minicore: fmt
($lit:literal $(tt:tt)*) => { 0 },
}
fn main() { fn main() {
let foobar = 1; let foobar = 1;
format_args!("{$0"); format_args!("{$0");
} }
"#, "#,
r#" r#"
macro_rules! format_args {
($lit:literal $(tt:tt)*) => { 0 },
}
fn main() { fn main() {
let foobar = 1; let foobar = 1;
format_args!("{foobar"); format_args!("{foobar");

View file

@ -1,10 +1,10 @@
//! Tools to work with format string literals for the `format_args!` family of macros. //! Tools to work with format string literals for the `format_args!` family of macros.
use crate::syntax_helpers::node_ext::macro_call_for_string_token;
use syntax::{ use syntax::{
ast::{self, IsString}, ast::{self, IsString},
TextRange, TextSize, AstNode, AstToken, TextRange, TextSize,
}; };
// FIXME: This can probably be re-implemented via the HIR?
pub fn is_format_string(string: &ast::String) -> bool { pub fn is_format_string(string: &ast::String) -> bool {
// Check if `string` is a format string argument of a macro invocation. // Check if `string` is a format string argument of a macro invocation.
// `string` is a string literal, mapped down into the innermost macro expansion. // `string` is a string literal, mapped down into the innermost macro expansion.
@ -15,19 +15,9 @@ pub fn is_format_string(string: &ast::String) -> bool {
// This setup lets us correctly highlight the components of `concat!("{}", "bla")` format // This setup lets us correctly highlight the components of `concat!("{}", "bla")` format
// strings. It still fails for `concat!("{", "}")`, but that is rare. // strings. It still fails for `concat!("{", "}")`, but that is rare.
(|| { (|| {
let name = macro_call_for_string_token(string)?.path()?.segment()?.name_ref()?; let lit = string.syntax().parent().and_then(ast::Literal::cast)?;
let fa = lit.syntax().parent().and_then(ast::FormatArgsExpr::cast)?;
if !matches!( (fa.template()? == ast::Expr::Literal(lit)).then_some(|| ())
name.text().as_str(),
"format_args" | "format_args_nl" | "const_format_args" | "panic_2015" | "panic_2021"
) {
return None;
}
// NB: we match against `panic_2015`/`panic_2021` here because they have a special-cased arm for
// `"{}"`, which otherwise wouldn't get highlighted.
Some(())
})() })()
.is_some() .is_some()
} }

View file

@ -157,6 +157,7 @@ struct S;
fn macro_diag_builtin() { fn macro_diag_builtin() {
check_diagnostics( check_diagnostics(
r#" r#"
//- minicore: fmt
#[rustc_builtin_macro] #[rustc_builtin_macro]
macro_rules! env {} macro_rules! env {}
@ -166,9 +167,6 @@ macro_rules! include {}
#[rustc_builtin_macro] #[rustc_builtin_macro]
macro_rules! compile_error {} macro_rules! compile_error {}
#[rustc_builtin_macro]
macro_rules! format_args { () => {} }
fn main() { fn main() {
// Test a handful of built-in (eager) macros: // Test a handful of built-in (eager) macros:
@ -189,7 +187,7 @@ fn main() {
// Lazy: // Lazy:
format_args!(); format_args!();
//^^^^^^^^^^^ error: no rule matches input tokens //^^^^^^^^^^^ error: Syntax Error in Expansion: expected expression
} }
"#, "#,
); );

View file

@ -6574,3 +6574,23 @@ fn test() {
"#]], "#]],
); );
} }
#[test]
fn format_args_arg() {
check(
r#"
//- minicore: fmt
fn test() {
let foo = 0;
format_args!("{}", foo$0);
}
"#,
expect![[r#"
*foo*
```rust
let foo: i32 // size = 4, align = 4
```
"#]],
);
}

View file

@ -474,7 +474,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 9289..9297, range: 10739..10747,
}, },
), ),
tooltip: "", tooltip: "",
@ -487,7 +487,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 9321..9325, range: 10771..10775,
}, },
), ),
tooltip: "", tooltip: "",
@ -511,7 +511,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 9289..9297, range: 10739..10747,
}, },
), ),
tooltip: "", tooltip: "",
@ -524,7 +524,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 9321..9325, range: 10771..10775,
}, },
), ),
tooltip: "", tooltip: "",
@ -548,7 +548,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 9289..9297, range: 10739..10747,
}, },
), ),
tooltip: "", tooltip: "",
@ -561,7 +561,7 @@ fn main() {
file_id: FileId( file_id: FileId(
1, 1,
), ),
range: 9321..9325, range: 10771..10775,
}, },
), ),
tooltip: "", tooltip: "",

View file

@ -17,6 +17,7 @@ pub(super) fn highlight_format_string(
return; return;
} }
// FIXME: Replace this with the HIR info we have now.
lex_format_specifiers(string, &mut |piece_range, kind| { lex_format_specifiers(string, &mut |piece_range, kind| {
if let Some(highlight) = highlight_format_specifier(kind) { if let Some(highlight) = highlight_format_specifier(kind) {
stack.add(HlRange { stack.add(HlRange {

View file

@ -617,6 +617,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
CONST => SymbolKind::Const, CONST => SymbolKind::Const,
STATIC => SymbolKind::Static, STATIC => SymbolKind::Static,
IDENT_PAT => SymbolKind::Local, IDENT_PAT => SymbolKind::Local,
FORMAT_ARGS_ARG => SymbolKind::Local,
_ => return default.into(), _ => return default.into(),
}; };

View file

@ -45,17 +45,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
</style> </style>
<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">println</span> <span class="brace">{</span> <pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">println</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="brace">{</span>
<span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span><span class="parenthesis">)</span> <span class="brace">}</span><span class="parenthesis">)</span>
<span class="brace">}</span> <span class="brace">}</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span> <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute_bracket attribute">]</span> <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute_bracket attribute">]</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">format_args</span> <span class="brace">{</span><span class="brace">}</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute_bracket attribute">]</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">const_format_args</span> <span class="brace">{</span><span class="brace">}</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute_bracket attribute">]</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">format_args_nl</span> <span class="brace">{</span><span class="brace">}</span> <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">format_args_nl</span> <span class="brace">{</span><span class="brace">}</span>
<span class="keyword">mod</span> <span class="module declaration">panic</span> <span class="brace">{</span> <span class="keyword">mod</span> <span class="module declaration">panic</span> <span class="brace">{</span>
@ -75,7 +69,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_display<span class="parenthesis">(</span><span class="punctuation">&</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span> <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_display<span class="parenthesis">(</span><span class="punctuation">&</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span>
<span class="parenthesis">)</span><span class="comma">,</span> <span class="parenthesis">)</span><span class="comma">,</span>
<span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="colon">:</span>expr<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span> <span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="colon">:</span>expr<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span>
<span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_fmt<span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>const_format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span> <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_fmt<span class="parenthesis">(</span>const_format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
<span class="parenthesis">)</span><span class="comma">,</span> <span class="parenthesis">)</span><span class="comma">,</span>
<span class="brace">}</span> <span class="brace">}</span>
<span class="brace">}</span> <span class="brace">}</span>
@ -92,7 +86,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span> <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span> <span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
@ -114,18 +108,18 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello, world!"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello, world!"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "The number is 1"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "The number is 1"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "(3, 4)"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "(3, 4)"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "4"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "4"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "1 2"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "1 2"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "0042" with leading zerosV</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "0042" with leading zerosV</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1 1 2"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1 1 2"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "test"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "test"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="none macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="none macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="none macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "a 3 b"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="variable declaration macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="variable declaration macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "a 3 b"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "{2}"</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "{2}"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
@ -140,10 +134,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="none macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="variable declaration macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{}"</span> <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{}"</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{{}}"</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{{}}"</span><span class="semicolon">;</span>
@ -167,24 +161,24 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">c"</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\xFF</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, valid unicodes</span> <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">c"</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\xFF</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, valid unicodes</span>
<span class="keyword">let</span> <span class="variable declaration reference">backslash</span> <span class="operator">=</span> <span class="string_literal">r"\\"</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration reference">backslash</span> <span class="operator">=</span> <span class="string_literal">r"\\"</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more {}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"{} asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span>
<span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span> <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
<span class="string_literal macro">"mov </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">, </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"mov {0}, {1}"</span><span class="comma macro">,</span>
<span class="string_literal macro">"add </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">, 5"</span><span class="comma macro">,</span> <span class="string_literal macro">"add {0}, 5"</span><span class="comma macro">,</span>
<span class="none macro">out</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">o</span><span class="comma macro">,</span> <span class="none macro">out</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">o</span><span class="comma macro">,</span>
<span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">i</span><span class="comma macro">,</span> <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">i</span><span class="comma macro">,</span>
<span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">concat</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="none macro">format_args</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="none macro">toho</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre> <span class="brace">}</span></code></pre>

View file

@ -401,19 +401,14 @@ fn test_string_highlighting() {
// thus, we have to copy the macro definition from `std` // thus, we have to copy the macro definition from `std`
check_highlighting( check_highlighting(
r#" r#"
//- minicore: fmt
macro_rules! println { macro_rules! println {
($($arg:tt)*) => ({ ($($arg:tt)*) => ({
$crate::io::_print($crate::format_args_nl!($($arg)*)); $crate::io::_print(format_args_nl!($($arg)*));
}) })
} }
#[rustc_builtin_macro] #[rustc_builtin_macro]
#[macro_export] #[macro_export]
macro_rules! format_args {}
#[rustc_builtin_macro]
#[macro_export]
macro_rules! const_format_args {}
#[rustc_builtin_macro]
#[macro_export]
macro_rules! format_args_nl {} macro_rules! format_args_nl {}
mod panic { mod panic {
@ -433,7 +428,7 @@ mod panic {
$crate::panicking::panic_display(&$arg) $crate::panicking::panic_display(&$arg)
), ),
($fmt:expr, $($arg:tt)+) => ( ($fmt:expr, $($arg:tt)+) => (
$crate::panicking::panic_fmt($crate::const_format_args!($fmt, $($arg)+)) $crate::panicking::panic_fmt(const_format_args!($fmt, $($arg)+))
), ),
} }
} }
@ -450,7 +445,7 @@ macro_rules! concat {}
macro_rules! toho { macro_rules! toho {
() => ($crate::panic!("not yet implemented")); () => ($crate::panic!("not yet implemented"));
($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", $crate::format_args!($($arg)+))); ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", format_args!($($arg)+)));
} }
fn main() { fn main() {

View file

@ -219,7 +219,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
// test builtin_expr // test builtin_expr
// fn foo() { // fn foo() {
// builtin#asm(0); // builtin#asm(0);
// builtin#format_args(0); // builtin#format_args("", 0, 1, a = 2 + 3, a + b);
// builtin#offset_of(Foo, bar.baz.0); // builtin#offset_of(Foo, bar.baz.0);
// } // }
fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> { fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
@ -249,6 +249,24 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
p.bump_remap(T![format_args]); p.bump_remap(T![format_args]);
p.expect(T!['(']); p.expect(T!['(']);
expr(p); expr(p);
if p.eat(T![,]) {
while !p.at(EOF) && !p.at(T![')']) {
let m = p.start();
if p.at(IDENT) && p.nth_at(1, T![=]) {
name(p);
p.bump(T![=]);
}
if expr(p).is_none() {
m.abandon(p);
break;
}
m.complete(p, FORMAT_ARGS_ARG);
if !p.at(T![')']) {
p.expect(T![,]);
}
}
}
p.expect(T![')']); p.expect(T![')']);
Some(m.complete(p, FORMAT_ARGS_EXPR)) Some(m.complete(p, FORMAT_ARGS_EXPR))
} else if p.at_contextual_kw(T![asm]) { } else if p.at_contextual_kw(T![asm]) {

View file

@ -210,6 +210,7 @@ pub enum SyntaxKind {
OFFSET_OF_EXPR, OFFSET_OF_EXPR,
ASM_EXPR, ASM_EXPR,
FORMAT_ARGS_EXPR, FORMAT_ARGS_EXPR,
FORMAT_ARGS_ARG,
CALL_EXPR, CALL_EXPR,
INDEX_EXPR, INDEX_EXPR,
METHOD_CALL_EXPR, METHOD_CALL_EXPR,

View file

@ -29,8 +29,51 @@ SOURCE_FILE
POUND "#" POUND "#"
FORMAT_ARGS_KW "format_args" FORMAT_ARGS_KW "format_args"
L_PAREN "(" L_PAREN "("
LITERAL
STRING "\"\""
COMMA ","
WHITESPACE " "
FORMAT_ARGS_ARG
LITERAL LITERAL
INT_NUMBER "0" INT_NUMBER "0"
COMMA ","
WHITESPACE " "
FORMAT_ARGS_ARG
LITERAL
INT_NUMBER "1"
COMMA ","
WHITESPACE " "
FORMAT_ARGS_ARG
NAME
IDENT "a"
WHITESPACE " "
EQ "="
WHITESPACE " "
BIN_EXPR
LITERAL
INT_NUMBER "2"
WHITESPACE " "
PLUS "+"
WHITESPACE " "
LITERAL
INT_NUMBER "3"
COMMA ","
WHITESPACE " "
FORMAT_ARGS_ARG
BIN_EXPR
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "a"
WHITESPACE " "
PLUS "+"
WHITESPACE " "
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "b"
R_PAREN ")" R_PAREN ")"
SEMICOLON ";" SEMICOLON ";"
WHITESPACE "\n " WHITESPACE "\n "

View file

@ -1,5 +1,5 @@
fn foo() { fn foo() {
builtin#asm(0); builtin#asm(0);
builtin#format_args(0); builtin#format_args("", 0, 1, a = 2 + 3, a + b);
builtin#offset_of(Foo, bar.baz.0); builtin#offset_of(Foo, bar.baz.0);
} }

View file

@ -382,7 +382,13 @@ AsmExpr =
Attr* 'builtin' '#' 'asm' '(' Expr ')' Attr* 'builtin' '#' 'asm' '(' Expr ')'
FormatArgsExpr = FormatArgsExpr =
Attr* 'builtin' '#' 'format_args' '(' ')' Attr* 'builtin' '#' 'format_args' '('
template:Expr
(',' args:(FormatArgsArg (',' FormatArgsArg)* ','?)? )?
')'
FormatArgsArg =
(Name '=')? Expr
MacroExpr = MacroExpr =
MacroCall MacroCall

View file

@ -931,6 +931,9 @@ impl FormatArgsExpr {
support::token(&self.syntax, T![format_args]) support::token(&self.syntax, T![format_args])
} }
pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) } pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
pub fn template(&self) -> Option<Expr> { support::child(&self.syntax) }
pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
pub fn args(&self) -> AstChildren<FormatArgsArg> { support::children(&self.syntax) }
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
} }
@ -1163,6 +1166,16 @@ impl UnderscoreExpr {
pub fn underscore_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![_]) } pub fn underscore_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![_]) }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FormatArgsArg {
pub(crate) syntax: SyntaxNode,
}
impl ast::HasName for FormatArgsArg {}
impl FormatArgsArg {
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StmtList { pub struct StmtList {
pub(crate) syntax: SyntaxNode, pub(crate) syntax: SyntaxNode,
@ -2855,6 +2868,17 @@ impl AstNode for UnderscoreExpr {
} }
fn syntax(&self) -> &SyntaxNode { &self.syntax } fn syntax(&self) -> &SyntaxNode { &self.syntax }
} }
impl AstNode for FormatArgsArg {
fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_ARG }
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for StmtList { impl AstNode for StmtList {
fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST } fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST }
fn cast(syntax: SyntaxNode) -> Option<Self> { fn cast(syntax: SyntaxNode) -> Option<Self> {
@ -4254,6 +4278,7 @@ impl AstNode for AnyHasName {
| VARIANT | VARIANT
| CONST_PARAM | CONST_PARAM
| TYPE_PARAM | TYPE_PARAM
| FORMAT_ARGS_ARG
| IDENT_PAT | IDENT_PAT
) )
} }
@ -4860,6 +4885,11 @@ impl std::fmt::Display for UnderscoreExpr {
std::fmt::Display::fmt(self.syntax(), f) std::fmt::Display::fmt(self.syntax(), f)
} }
} }
impl std::fmt::Display for FormatArgsArg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for StmtList { impl std::fmt::Display for StmtList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f) std::fmt::Display::fmt(self.syntax(), f)

View file

@ -169,6 +169,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc {
"OFFSET_OF_EXPR", "OFFSET_OF_EXPR",
"ASM_EXPR", "ASM_EXPR",
"FORMAT_ARGS_EXPR", "FORMAT_ARGS_EXPR",
"FORMAT_ARGS_ARG",
// postfix // postfix
"CALL_EXPR", "CALL_EXPR",
"INDEX_EXPR", "INDEX_EXPR",

View file

@ -899,6 +899,8 @@ pub mod fmt {
fn fmt(&self, f: &mut Formatter<'_>) -> Result; fn fmt(&self, f: &mut Formatter<'_>) -> Result;
} }
mod rt {
extern "C" { extern "C" {
type Opaque; type Opaque;
} }
@ -916,15 +918,71 @@ pub mod fmt {
} }
} }
#[lang = "format_alignment"]
pub enum Alignment {
Left,
Right,
Center,
Unknown,
}
#[lang = "format_count"]
pub enum Count {
Is(usize),
Param(usize),
Implied,
}
#[lang = "format_placeholder"]
pub struct Placeholder {
pub position: usize,
pub fill: char,
pub align: Alignment,
pub flags: u32,
pub precision: Count,
pub width: Count,
}
impl Placeholder {
pub const fn new(
position: usize,
fill: char,
align: Alignment,
flags: u32,
precision: Count,
width: Count,
) -> Self;
}
#[lang = "format_unsafe_arg"]
pub struct UnsafeArg {
_private: (),
}
impl UnsafeArg {
pub unsafe fn new() -> Self;
}
}
#[lang = "format_arguments"] #[lang = "format_arguments"]
pub struct Arguments<'a> { pub struct Arguments<'a> {
pieces: &'a [&'static str], pieces: &'a [&'static str],
args: &'a [Argument<'a>], fmt: Option<&'a [rt::Placeholder]>,
args: &'a [rt::Argument<'a>],
} }
impl<'a> Arguments<'a> { impl<'a> Arguments<'a> {
pub const fn new_v1(pieces: &'a [&'static str], args: &'a [Argument<'a>]) -> Arguments<'a> { pub const fn new_v1(pieces: &'a [&'static str], args: &'a [Argument<'a>]) -> Arguments<'a> {
Arguments { pieces, args } Arguments { pieces, fmt: None, args }
}
pub fn new_v1_formatted(
pieces: &'a [&'static str],
args: &'a [rt::Argument<'a>],
fmt: &'a [rt::Placeholder],
_unsafe_arg: rt::UnsafeArg,
) -> Arguments<'a> {
Arguments { pieces, fmt: Some(fmt), args }
} }
} }
@ -1294,8 +1352,6 @@ mod macros {
/* compiler built-in */ /* compiler built-in */
}; };
} }
pub(crate) use panic;
// endregion:panic // endregion:panic
// region:fmt // region:fmt
@ -1306,7 +1362,20 @@ mod macros {
($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
} }
pub(crate) use const_format_args; #[macro_export]
#[rustc_builtin_macro]
macro_rules! format_args {
($fmt:expr) => {{ /* compiler built-in */ }};
($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => {{
$crate::io::_print($crate::format_args!($($arg)*));
}};
}
// endregion:fmt // endregion:fmt
// region:derive // region:derive