Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--Cargo.lock1
-rw-r--r--crates/hir-def/Cargo.toml1
-rw-r--r--crates/hir-def/src/body/lower.rs422
-rw-r--r--crates/hir-def/src/body/pretty.rs44
-rw-r--r--crates/hir-def/src/body/tests.rs89
-rw-r--r--crates/hir-def/src/hir.rs4
-rw-r--r--crates/hir-def/src/hir/format_args.rs503
-rw-r--r--crates/hir-def/src/hir/format_args/parse.rs1023
-rw-r--r--crates/hir-def/src/item_tree.rs2
-rw-r--r--crates/hir-def/src/item_tree/pretty.rs64
-rw-r--r--crates/hir-def/src/lang_item.rs13
-rw-r--r--crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs39
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe.rs6
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe/regression.rs12
-rw-r--r--crates/hir-def/src/path.rs31
-rw-r--r--crates/hir-def/src/pretty.rs72
-rw-r--r--crates/hir-def/src/resolver.rs45
-rw-r--r--crates/hir-expand/src/builtin_fn_macro.rs160
-rw-r--r--crates/hir-expand/src/name.rs25
-rw-r--r--crates/hir-ty/src/infer/path.rs23
-rw-r--r--crates/hir-ty/src/tests/simple.rs15
-rw-r--r--crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs91
-rw-r--r--crates/ide-assists/src/tests/generated.rs15
-rw-r--r--crates/ide-completion/src/completions/format_string.rs22
-rw-r--r--crates/ide-db/src/syntax_helpers/format_string.rs20
-rw-r--r--crates/ide-diagnostics/src/handlers/macro_error.rs6
-rw-r--r--crates/ide/src/hover/tests.rs20
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs12
-rw-r--r--crates/ide/src/syntax_highlighting/format.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html52
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs13
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs20
-rw-r--r--crates/parser/src/syntax_kind/generated.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast45
-rw-r--r--crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs2
-rw-r--r--crates/syntax/rust.ungram8
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs30
-rw-r--r--crates/syntax/src/tests/ast_src.rs1
-rw-r--r--crates/test-utils/src/minicore.rs103
40 files changed, 2581 insertions, 476 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1b30062557..203d7348be 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -541,6 +541,7 @@ dependencies = [
"mbe",
"once_cell",
"profile",
+ "ra-ap-rustc_lexer",
"rustc-hash",
"smallvec",
"stdx",
diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml
index 30307deb79..4640ee5140 100644
--- a/crates/hir-def/Cargo.toml
+++ b/crates/hir-def/Cargo.toml
@@ -33,6 +33,7 @@ triomphe.workspace = true
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_lexer = { version = "0.1.0", package = "ra-ap-rustc_lexer" }
# local deps
stdx.workspace = true
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 038032e05d..38073ce3da 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -25,13 +25,20 @@ use triomphe::Arc;
use crate::{
body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
+ builtin_type::BuiltinUint,
data::adt::StructKind,
db::DefDatabase,
expander::Expander,
hir::{
- dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy,
- ClosureKind, Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm,
- Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
+ dummy_expr_id,
+ format_args::{
+ 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,
lang_item::LangItem,
@@ -42,6 +49,8 @@ use crate::{
AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
};
+type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
+
pub(super) fn lower(
db: &dyn DefDatabase,
owner: DefWithBodyId,
@@ -649,15 +658,15 @@ impl ExprCollector<'_> {
}
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
ast::Expr::AsmExpr(e) => {
- let expr = Expr::InlineAsm(InlineAsm { e: self.collect_expr_opt(e.expr()) });
- self.alloc_expr(expr, syntax_ptr)
+ let e = self.collect_expr_opt(e.expr());
+ self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr)
}
ast::Expr::OffsetOfExpr(e) => {
let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
let fields = e.fields().map(|it| it.as_name()).collect();
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
+
+ // 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)> {
@@ -1632,3 +2036,9 @@ fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
(|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))()
.map_or(false, |it| it.kind() == syntax::T![,])
}
+
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+enum ArgumentType {
+ Format(FormatTrait),
+ Usize,
+}
diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs
index 602a7983c7..fad4d7a4da 100644
--- a/crates/hir-def/src/body/pretty.rs
+++ b/crates/hir-def/src/body/pretty.rs
@@ -2,7 +2,6 @@
use std::fmt::{self, Write};
-use hir_expand::db::ExpandDatabase;
use itertools::Itertools;
use syntax::ast::HasName;
@@ -52,8 +51,7 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
}
};
- let mut p =
- Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false };
+ let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false };
if let DefWithBodyId::FunctionId(it) = owner {
p.buf.push('(');
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,
expr: ExprId,
) -> String {
- let mut p =
- Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false };
+ let mut p = Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false };
p.print_expr(expr);
p.buf
}
@@ -99,7 +96,7 @@ macro_rules! wln {
}
struct Printer<'a> {
- db: &'a dyn ExpandDatabase,
+ db: &'a dyn DefDatabase,
body: &'a Body,
buf: String,
indent_level: usize,
@@ -143,9 +140,14 @@ impl Printer<'_> {
}
fn newline(&mut self) {
- match self.buf.chars().rev().find(|ch| *ch != ' ') {
- Some('\n') | None => {}
- _ => writeln!(self).unwrap(),
+ match self.buf.chars().rev().find_position(|ch| *ch != ' ') {
+ Some((_, '\n')) | None => {}
+ Some((idx, _)) => {
+ if idx != 0 {
+ self.buf.drain(self.buf.len() - idx..);
+ }
+ writeln!(self).unwrap()
+ }
}
}
@@ -162,7 +164,10 @@ impl Printer<'_> {
w!(
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),
@@ -184,7 +189,7 @@ impl Printer<'_> {
}
Expr::Loop { body, 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 ");
self.print_expr(*body);
@@ -204,7 +209,7 @@ impl Printer<'_> {
}
Expr::MethodCall { receiver, method_name, args, generic_args } => {
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 {
w!(self, "::<");
print_generic_args(self.db, args, self).unwrap();
@@ -242,13 +247,13 @@ impl Printer<'_> {
Expr::Continue { label } => {
w!(self, "continue");
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 } => {
w!(self, "break");
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 {
self.whitespace();
@@ -287,7 +292,7 @@ impl Printer<'_> {
w!(self, "{{");
self.indented(|p| {
for field in &**fields {
- w!(p, "{}: ", field.name.display(self.db));
+ w!(p, "{}: ", field.name.display(self.db.upcast()));
p.print_expr(field.expr);
wln!(p, ",");
}
@@ -304,7 +309,7 @@ impl Printer<'_> {
}
Expr::Field { expr, name } => {
self.print_expr(*expr);
- w!(self, ".{}", name.display(self.db));
+ w!(self, ".{}", name.display(self.db.upcast()));
}
Expr::Await { expr } => {
self.print_expr(*expr);
@@ -442,7 +447,8 @@ impl Printer<'_> {
}
Expr::Literal(lit) => self.print_literal(lit),
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);
}
Expr::Unsafe { id: _, statements, tail } => {
@@ -518,7 +524,7 @@ impl Printer<'_> {
w!(self, " {{");
self.indented(|p| {
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);
wln!(p, ",");
}
@@ -677,6 +683,6 @@ impl Printer<'_> {
BindingAnnotation::Ref => "ref ",
BindingAnnotation::RefMut => "ref mut ",
};
- w!(self, "{}{}", mode, name.display(self.db));
+ w!(self, "{}{}", mode, name.display(self.db.upcast()));
}
}
diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs
index d558201164..1658757d2b 100644
--- a/crates/hir-def/src/body/tests.rs
+++ b/crates/hir-def/src/body/tests.rs
@@ -1,13 +1,13 @@
mod block;
use base_db::{fixture::WithFixture, SourceDatabase};
-use expect_test::Expect;
+use expect_test::{expect, Expect};
use crate::{test_db::TestDB, ModuleDefId};
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 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 {
@@ -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))
+}
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index 1c86af456d..591ee77c70 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -13,6 +13,7 @@
//! See also a neighboring `body` module.
pub mod type_ref;
+pub mod format_args;
use std::fmt;
@@ -117,7 +118,6 @@ impl From<ast::LiteralKind> for Literal {
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
use ast::LiteralKind;
match ast_lit_kind {
- // FIXME: these should have actual values filled in, but unsure on perf impact
LiteralKind::IntNumber(lit) => {
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
Literal::Float(
@@ -355,7 +355,7 @@ impl Expr {
match self {
Expr::Missing => {}
Expr::Path(_) | Expr::OffsetOf(_) => {}
- Expr::InlineAsm(e) => f(e.e),
+ Expr::InlineAsm(it) => f(it.e),
Expr::If { condition, then_branch, else_branch } => {
f(*condition);
f(*then_branch);
diff --git a/crates/hir-def/src/hir/format_args.rs b/crates/hir-def/src/hir/format_args.rs
new file mode 100644
index 0000000000..d8f8e6026a
--- /dev/null
+++ b/crates/hir-def/src/hir/format_args.rs
@@ -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),
+ }
+ }
+}
diff --git a/crates/hir-def/src/hir/format_args/parse.rs b/crates/hir-def/src/hir/format_args/parse.rs
new file mode 100644
index 0000000000..22efa3883d
--- /dev/null
+++ b/crates/hir-def/src/hir/format_args/parse.rs
@@ -0,0 +1,1023 @@
+//! Macro support for format strings
+//!
+//! These structures are used when parsing format strings for the compiler.
+//! Parsing does not happen at runtime: structures of `std::fmt::rt` are
+//! generated instead.
+
+// This is a copy of
+// https://github.com/Veykril/rust/blob/b89d7d6882532686fd90a89cec1a0fd386f0ade3/compiler/rustc_parse_format/src/lib.rs#L999-L1000
+// with the dependency of rustc-data-structures stripped out.
+
+// #![doc(
+// html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/",
+// html_playground_url = "https://play.rust-lang.org/",
+// test(attr(deny(warnings)))
+// )]
+// #![deny(rustc::untranslatable_diagnostic)]
+// #![deny(rustc::diagnostic_outside_of_impl)]
+// We want to be able to build this crate with a stable compiler, so no
+// `#![feature]` attributes should be added.
+#![allow(dead_code, unreachable_pub)]
+
+use rustc_lexer::unescape;
+pub use Alignment::*;
+pub use Count::*;
+pub use Piece::*;
+pub use Position::*;
+
+use std::iter;
+use std::str;
+use std::string;
+
+// Note: copied from rustc_span
+/// Range inside of a `Span` used for diagnostics when we only have access to relative positions.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct InnerSpan {
+ pub start: usize,
+ pub end: usize,
+}
+
+impl InnerSpan {
+ pub fn new(start: usize, end: usize) -> InnerSpan {
+ InnerSpan { start, end }
+ }
+}
+
+/// The location and before/after width of a character whose width has changed from its source code
+/// representation
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct InnerWidthMapping {
+ /// Index of the character in the source
+ pub position: usize,
+ /// The inner width in characters
+ pub before: usize,
+ /// The transformed width in characters
+ pub after: usize,
+}
+
+impl InnerWidthMapping {
+ pub fn new(position: usize, before: usize, after: usize) -> InnerWidthMapping {
+ InnerWidthMapping { position, before, after }
+ }
+}
+
+/// Whether the input string is a literal. If yes, it contains the inner width mappings.
+#[derive(Clone, PartialEq, Eq)]
+enum InputStringKind {
+ NotALiteral,
+ Literal { width_mappings: Vec<InnerWidthMapping> },
+}
+
+/// The type of format string that we are parsing.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum ParseMode {
+ /// A normal format string as per `format_args!`.
+ Format,
+ /// An inline assembly template string for `asm!`.
+ InlineAsm,
+}
+
+#[derive(Copy, Clone)]
+struct InnerOffset(usize);
+
+impl InnerOffset {
+ fn to(self, end: InnerOffset) -> InnerSpan {
+ InnerSpan::new(self.0, end.0)
+ }
+}
+
+/// A piece is a portion of the format string which represents the next part
+/// to emit. These are emitted as a stream by the `Parser` class.
+#[derive(Clone, Debug, PartialEq)]
+pub enum Piece<'a> {
+ /// A literal string which should directly be emitted
+ String(&'a str),
+ /// This describes that formatting should process the next argument (as
+ /// specified inside) for emission.
+ NextArgument(Box<Argument<'a>>),
+}
+
+/// Representation of an argument specification.
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub struct Argument<'a> {
+ /// Where to find this argument
+ pub position: Position<'a>,
+ /// The span of the position indicator. Includes any whitespace in implicit
+ /// positions (`{ }`).
+ pub position_span: InnerSpan,
+ /// How to format the argument
+ pub format: FormatSpec<'a>,
+}
+
+/// Specification for the formatting of an argument in the format string.
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub struct FormatSpec<'a> {
+ /// Optionally specified character to fill alignment with.
+ pub fill: Option<char>,
+ /// Span of the optionally specified fill character.
+ pub fill_span: Option<InnerSpan>,
+ /// Optionally specified alignment.
+ pub align: Alignment,
+ /// The `+` or `-` flag.
+ pub sign: Option<Sign>,
+ /// The `#` flag.
+ pub alternate: bool,
+ /// The `0` flag.
+ pub zero_pad: bool,
+ /// The `x` or `X` flag. (Only for `Debug`.)
+ pub debug_hex: Option<DebugHex>,
+ /// The integer precision to use.
+ pub precision: Count<'a>,
+ /// The span of the precision formatting flag (for diagnostics).
+ pub precision_span: Option<InnerSpan>,
+ /// The string width requested for the resulting format.
+ pub width: Count<'a>,
+ /// The span of the width formatting flag (for diagnostics).
+ pub width_span: Option<InnerSpan>,
+ /// The descriptor string representing the name of the format desired for
+ /// this argument, this can be empty or any number of characters, although
+ /// it is required to be one word.
+ pub ty: &'a str,
+ /// The span of the descriptor string (for diagnostics).
+ pub ty_span: Option<InnerSpan>,
+}
+
+/// Enum describing where an argument for a format can be located.
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum Position<'a> {
+ /// The argument is implied to be located at an index
+ ArgumentImplicitlyIs(usize),
+ /// The argument is located at a specific index given in the format,
+ ArgumentIs(usize),
+ /// The argument has a name.
+ ArgumentNamed(&'a str),
+}
+
+impl Position<'_> {
+ pub fn index(&self) -> Option<usize> {
+ match self {
+ ArgumentIs(i, ..) | ArgumentImplicitlyIs(i) => Some(*i),
+ _ => None,
+ }
+ }
+}
+
+/// Enum of alignments which are supported.
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum Alignment {
+ /// The value will be aligned to the left.
+ AlignLeft,
+ /// The value will be aligned to the right.
+ AlignRight,
+ /// The value will be aligned in the center.
+ AlignCenter,
+ /// The value will take on a default alignment.
+ AlignUnknown,
+}
+
+/// Enum for the sign flags.
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum Sign {
+ /// The `+` flag.
+ Plus,
+ /// The `-` flag.
+ Minus,
+}
+
+/// Enum for the debug hex flags.
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum DebugHex {
+ /// The `x` flag in `{:x?}`.
+ Lower,
+ /// The `X` flag in `{:X?}`.
+ Upper,
+}
+
+/// A count is used for the precision and width parameters of an integer, and
+/// can reference either an argument or a literal integer.
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum Count<'a> {
+ /// The count is specified explicitly.
+ CountIs(usize),
+ /// The count is specified by the argument with the given name.
+ CountIsName(&'a str, InnerSpan),
+ /// The count is specified by the argument at the given index.
+ CountIsParam(usize),
+ /// The count is specified by a star (like in `{:.*}`) that refers to the argument at the given index.
+ CountIsStar(usize),
+ /// The count is implied and cannot be explicitly specified.
+ CountImplied,
+}
+
+pub struct ParseError {
+ pub description: string::String,
+ pub note: Option<string::String>,
+ pub label: string::String,
+ pub span: InnerSpan,
+ pub secondary_label: Option<(string::String, InnerSpan)>,
+ pub should_be_replaced_with_positional_argument: bool,
+}
+
+/// The parser structure for interpreting the input format string. This is
+/// modeled as an iterator over `Piece` structures to form a stream of tokens
+/// being output.
+///
+/// This is a recursive-descent parser for the sake of simplicity, and if
+/// necessary there's probably lots of room for improvement performance-wise.
+pub struct Parser<'a> {
+ mode: ParseMode,
+ input: &'a str,
+ cur: iter::Peekable<str::CharIndices<'a>>,
+ /// Error messages accumulated during parsing
+ pub errors: Vec<ParseError>,
+ /// Current position of implicit positional argument pointer
+ pub curarg: usize,
+ /// `Some(raw count)` when the string is "raw", used to position spans correctly
+ style: Option<usize>,
+ /// Start and end byte offset of every successfully parsed argument
+ pub arg_places: Vec<InnerSpan>,
+ /// Characters whose length has been changed from their in-code representation
+ width_map: Vec<InnerWidthMapping>,
+ /// Span of the last opening brace seen, used for error reporting
+ last_opening_brace: Option<InnerSpan>,
+ /// Whether the source string is comes from `println!` as opposed to `format!` or `print!`
+ append_newline: bool,
+ /// Whether this formatting string was written directly in the source. This controls whether we
+ /// can use spans to refer into it and give better error messages.
+ /// N.B: This does _not_ control whether implicit argument captures can be used.
+ pub is_source_literal: bool,
+ /// Start position of the current line.
+ cur_line_start: usize,
+ /// Start and end byte offset of every line of the format string. Excludes
+ /// newline characters and leading whitespace.
+ pub line_spans: Vec<InnerSpan>,
+}
+
+impl<'a> Iterator for Parser<'a> {
+ type Item = Piece<'a>;
+
+ fn next(&mut self) -> Option<Piece<'a>> {
+ if let Some(&(pos, c)) = self.cur.peek() {
+ match c {
+ '{' => {
+ let curr_last_brace = self.last_opening_brace;
+ let byte_pos = self.to_span_index(pos);
+ let lbrace_end = InnerOffset(byte_pos.0 + self.to_span_width(pos));
+ self.last_opening_brace = Some(byte_pos.to(lbrace_end));
+ self.cur.next();
+ if self.consume('{') {
+ self.last_opening_brace = curr_last_brace;
+
+ Some(String(self.string(pos + 1)))
+ } else {
+ let arg = self.argument(lbrace_end);
+ if let Some(rbrace_pos) = self.consume_closing_brace(&arg) {
+ if self.is_source_literal {
+ let lbrace_byte_pos = self.to_span_index(pos);
+ let rbrace_byte_pos = self.to_span_index(rbrace_pos);
+
+ let width = self.to_span_width(rbrace_pos);
+
+ self.arg_places.push(
+ lbrace_byte_pos.to(InnerOffset(rbrace_byte_pos.0 + width)),
+ );
+ }
+ } else {
+ if let Some(&(_, maybe)) = self.cur.peek() {
+ if maybe == '?' {
+ self.suggest_format();
+ } else {
+ self.suggest_positional_arg_instead_of_captured_arg(arg);
+ }
+ }
+ }
+ Some(NextArgument(Box::new(arg)))
+ }
+ }
+ '}' => {
+ self.cur.next();
+ if self.consume('}') {
+ Some(String(self.string(pos + 1)))
+ } else {
+ let err_pos = self.to_span_index(pos);
+ self.err_with_note(
+ "unmatched `}` found",
+ "unmatched `}`",
+ "if you intended to print `}`, you can escape it using `}}`",
+ err_pos.to(err_pos),
+ );
+ None
+ }
+ }
+ _ => Some(String(self.string(pos))),
+ }
+ } else {
+ if self.is_source_literal {
+ let span = self.span(self.cur_line_start, self.input.len());
+ if self.line_spans.last() != Some(&span) {
+ self.line_spans.push(span);
+ }
+ }
+ None
+ }
+ }
+}
+
+impl<'a> Parser<'a> {
+ /// Creates a new parser for the given format string
+ pub fn new(
+ s: &'a str,
+ style: Option<usize>,
+ snippet: Option<string::String>,
+ append_newline: bool,
+ mode: ParseMode,
+ ) -> Parser<'a> {
+ let input_string_kind = find_width_map_from_snippet(s, snippet, style);
+ let (width_map, is_source_literal) = match input_string_kind {
+ InputStringKind::Literal { width_mappings } => (width_mappings, true),
+ InputStringKind::NotALiteral => (Vec::new(), false),
+ };
+
+ Parser {
+ mode,
+ input: s,
+ cur: s.char_indices().peekable(),
+ errors: vec![],
+ curarg: 0,
+ style,
+ arg_places: vec![],
+ width_map,
+ last_opening_brace: None,
+ append_newline,
+ is_source_literal,
+ cur_line_start: 0,
+ line_spans: vec![],
+ }
+ }
+
+ /// Notifies of an error. The message doesn't actually need to be of type
+ /// String, but I think it does when this eventually uses conditions so it
+ /// might as well start using it now.
+ fn err<S1: Into<string::String>, S2: Into<string::String>>(
+ &mut self,
+ description: S1,
+ label: S2,
+ span: InnerSpan,
+ ) {
+ self.errors.push(ParseError {
+ description: description.into(),
+ note: None,
+ label: label.into(),
+ span,
+ secondary_label: None,
+ should_be_replaced_with_positional_argument: false,
+ });
+ }
+
+ /// Notifies of an error. The message doesn't actually need to be of type
+ /// String, but I think it does when this eventually uses conditions so it
+ /// might as well start using it now.
+ fn err_with_note<
+ S1: Into<string::String>,
+ S2: Into<string::String>,
+ S3: Into<string::String>,
+ >(
+ &mut self,
+ description: S1,
+ label: S2,
+ note: S3,
+ span: InnerSpan,
+ ) {
+ self.errors.push(ParseError {
+ description: description.into(),
+ note: Some(note.into()),
+ label: label.into(),
+ span,
+ secondary_label: None,
+ should_be_replaced_with_positional_argument: false,
+ });
+ }
+
+ /// Optionally consumes the specified character. If the character is not at
+ /// the current position, then the current iterator isn't moved and `false` is
+ /// returned, otherwise the character is consumed and `true` is returned.
+ fn consume(&mut self, c: char) -> bool {
+ self.consume_pos(c).is_some()
+ }
+
+ /// Optionally consumes the specified character. If the character is not at
+ /// the current position, then the current iterator isn't moved and `None` is
+ /// returned, otherwise the character is consumed and the current position is
+ /// returned.
+ fn consume_pos(&mut self, c: char) -> Option<usize> {
+ if let Some(&(pos, maybe)) = self.cur.peek() {
+ if c == maybe {
+ self.cur.next();
+ return Some(pos);
+ }
+ }
+ None
+ }
+
+ fn remap_pos(&self, mut pos: usize) -> InnerOffset {
+ for width in &self.width_map {
+ if pos > width.position {
+ pos += width.before - width.after;
+ } else if pos == width.position && width.after == 0 {
+ pos += width.before;
+ } else {
+ break;
+ }
+ }
+
+ InnerOffset(pos)
+ }
+
+ fn to_span_index(&self, pos: usize) -> InnerOffset {
+ // This handles the raw string case, the raw argument is the number of #
+ // in r###"..."### (we need to add one because of the `r`).
+ let raw = self.style.map_or(0, |raw| raw + 1);
+ let pos = self.remap_pos(pos);
+ InnerOffset(raw + pos.0 + 1)
+ }
+
+ fn to_span_width(&self, pos: usize) -> usize {
+ let pos = self.remap_pos(pos);
+ match self.width_map.iter().find(|w| w.position == pos.0) {
+ Some(w) => w.before,
+ None => 1,
+ }
+ }
+
+ fn span(&self, start_pos: usize, end_pos: usize) -> InnerSpan {
+ let start = self.to_span_index(start_pos);
+ let end = self.to_span_index(end_pos);
+ start.to(end)
+ }
+
+ /// Forces consumption of the specified character. If the character is not
+ /// found, an error is emitted.
+ fn consume_closing_brace(&mut self, arg: &Argument<'_>) -> Option<usize> {
+ self.ws();
+
+ let pos;
+ let description;
+
+ if let Some(&(peek_pos, maybe)) = self.cur.peek() {
+ if maybe == '}' {
+ self.cur.next();
+ return Some(peek_pos);
+ }
+
+ pos = peek_pos;
+ description = format!("expected `'}}'`, found `{maybe:?}`");
+ } else {
+ description = "expected `'}'` but string was terminated".to_owned();
+ // point at closing `"`
+ pos = self.input.len() - if self.append_newline { 1 } else { 0 };
+ }
+
+ let pos = self.to_span_index(pos);
+
+ let label = "expected `'}'`".to_owned();
+ let (note, secondary_label) = if arg.format.fill == Some('}') {
+ (
+ Some("the character `'}'` is interpreted as a fill character because of the `:` that precedes it".to_owned()),
+ arg.format.fill_span.map(|sp| ("this is not interpreted as a formatting closing brace".to_owned(), sp)),
+ )
+ } else {
+ (
+ Some("if you intended to print `{`, you can escape it using `{{`".to_owned()),
+ self.last_opening_brace.map(|sp| ("because of this opening brace".to_owned(), sp)),
+ )
+ };
+
+ self.errors.push(ParseError {
+ description,
+ note,
+ label,
+ span: pos.to(pos),
+ secondary_label,
+ should_be_replaced_with_positional_argument: false,
+ });
+
+ None
+ }
+
+ /// Consumes all whitespace characters until the first non-whitespace character
+ fn ws(&mut self) {
+ while let Some(&(_, c)) = self.cur.peek() {
+ if c.is_whitespace() {
+ self.cur.next();
+ } else {
+ break;
+ }
+ }
+ }
+
+ /// Parses all of a string which is to be considered a "raw literal" in a
+ /// format string. This is everything outside of the braces.
+ fn string(&mut self, start: usize) -> &'a str {
+ // we may not consume the character, peek the iterator
+ while let Some(&(pos, c)) = self.cur.peek() {
+ match c {
+ '{' | '}' => {
+ return &self.input[start..pos];
+ }
+ '\n' if self.is_source_literal => {
+ self.line_spans.push(self.span(self.cur_line_start, pos));
+ self.cur_line_start = pos + 1;
+ self.cur.next();
+ }
+ _ => {
+ if self.is_source_literal && pos == self.cur_line_start && c.is_whitespace() {
+ self.cur_line_start = pos + c.len_utf8();
+ }
+ self.cur.next();
+ }
+ }
+ }
+ &self.input[start..self.input.len()]
+ }
+
+ /// Parses an `Argument` structure, or what's contained within braces inside the format string.
+ fn argument(&mut self, start: InnerOffset) -> Argument<'a> {
+ let pos = self.position();
+
+ let end = self
+ .cur
+ .clone()
+ .find(|(_, ch)| !ch.is_whitespace())
+ .map_or(start, |(end, _)| self.to_span_index(end));
+ let position_span = start.to(end);
+
+ let format = match self.mode {
+ ParseMode::Format => self.format(),
+ ParseMode::InlineAsm => self.inline_asm(),
+ };
+
+ // Resolve position after parsing format spec.
+ let pos = match pos {
+ Some(position) => position,
+ None => {
+ let i = self.curarg;
+ self.curarg += 1;
+ ArgumentImplicitlyIs(i)
+ }
+ };
+
+ Argument { position: pos, position_span, format }
+ }
+
+ /// Parses a positional argument for a format. This could either be an
+ /// integer index of an argument, a named argument, or a blank string.
+ /// Returns `Some(parsed_position)` if the position is not implicitly
+ /// consuming a macro argument, `None` if it's the case.
+ fn position(&mut self) -> Option<Position<'a>> {
+ if let Some(i) = self.integer() {
+ Some(ArgumentIs(i))
+ } else {
+ match self.cur.peek() {
+ Some(&(_, c)) if rustc_lexer::is_id_start(c) => Some(ArgumentNamed(self.word())),
+
+ // This is an `ArgumentNext`.
+ // Record the fact and do the resolution after parsing the
+ // format spec, to make things like `{:.*}` work.
+ _ => None,
+ }
+ }
+ }
+
+ fn current_pos(&mut self) -> usize {
+ if let Some(&(pos, _)) = self.cur.peek() {
+ pos
+ } else {
+ self.input.len()
+ }
+ }
+
+ /// Parses a format specifier at the current position, returning all of the
+ /// relevant information in the `FormatSpec` struct.
+ fn format(&mut self) -> FormatSpec<'a> {
+ let mut spec = FormatSpec {
+ fill: None,
+ fill_span: None,
+ align: AlignUnknown,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
+ precision: CountImplied,
+ precision_span: None,
+ width: CountImplied,
+ width_span: None,
+ ty: &self.input[..0],
+ ty_span: None,
+ };
+ if !self.consume(':') {
+ return spec;
+ }
+
+ // fill character
+ if let Some(&(idx, c)) = self.cur.peek() {
+ if let Some((_, '>' | '<' | '^')) = self.cur.clone().nth(1) {
+ spec.fill = Some(c);
+ spec.fill_span = Some(self.span(idx, idx + 1));
+ self.cur.next();
+ }
+ }
+ // Alignment
+ if self.consume('<') {
+ spec.align = AlignLeft;
+ } else if self.consume('>') {
+ spec.align = AlignRight;
+ } else if self.consume('^') {
+ spec.align = AlignCenter;
+ }
+ // Sign flags
+ if self.consume('+') {
+ spec.sign = Some(Sign::Plus);
+ } else if self.consume('-') {
+ spec.sign = Some(Sign::Minus);
+ }
+ // Alternate marker
+ if self.consume('#') {
+ spec.alternate = true;
+ }
+ // Width and precision
+ let mut havewidth = false;
+
+ if self.consume('0') {
+ // small ambiguity with '0$' as a format string. In theory this is a
+ // '0' flag and then an ill-formatted format string with just a '$'
+ // and no count, but this is better if we instead interpret this as
+ // no '0' flag and '0$' as the width instead.
+ if let Some(end) = self.consume_pos('$') {
+ spec.width = CountIsParam(0);
+ spec.width_span = Some(self.span(end - 1, end + 1));
+ havewidth = true;
+ } else {
+ spec.zero_pad = true;
+ }
+ }
+
+ if !havewidth {
+ let start = self.current_pos();
+ spec.width = self.count(start);
+ if spec.width != CountImplied {
+ let end = self.current_pos();
+ spec.width_span = Some(self.span(start, end));
+ }
+ }
+
+ if let Some(start) = self.consume_pos('.') {
+ if self.consume('*') {
+ // Resolve `CountIsNextParam`.
+ // We can do this immediately as `position` is resolved later.
+ let i = self.curarg;
+ self.curarg += 1;
+ spec.precision = CountIsStar(i);
+ } else {
+ spec.precision = self.count(start + 1);
+ }
+ let end = self.current_pos();
+ spec.precision_span = Some(self.span(start, end));
+ }
+
+ let ty_span_start = self.current_pos();
+ // Optional radix followed by the actual format specifier
+ if self.consume('x') {
+ if self.consume('?') {
+ spec.debug_hex = Some(DebugHex::Lower);
+ spec.ty = "?";
+ } else {
+ spec.ty = "x";
+ }
+ } else if self.consume('X') {
+ if self.consume('?') {
+ spec.debug_hex = Some(DebugHex::Upper);
+ spec.ty = "?";
+ } else {
+ spec.ty = "X";
+ }
+ } else if self.consume('?') {
+ spec.ty = "?";
+ } else {
+ spec.ty = self.word();
+ if !spec.ty.is_empty() {
+ let ty_span_end = self.current_pos();
+ spec.ty_span = Some(self.span(ty_span_start, ty_span_end));
+ }
+ }
+ spec
+ }
+
+ /// Parses an inline assembly template modifier at the current position, returning the modifier
+ /// in the `ty` field of the `FormatSpec` struct.
+ fn inline_asm(&mut self) -> FormatSpec<'a> {
+ let mut spec = FormatSpec {
+ fill: None,
+ fill_span: None,
+ align: AlignUnknown,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
+ precision: CountImplied,
+ precision_span: None,
+ width: CountImplied,
+ width_span: None,
+ ty: &self.input[..0],
+ ty_span: None,
+ };
+ if !self.consume(':') {
+ return spec;
+ }
+
+ let ty_span_start = self.current_pos();
+ spec.ty = self.word();
+ if !spec.ty.is_empty() {
+ let ty_span_end = self.current_pos();
+ spec.ty_span = Some(self.span(ty_span_start, ty_span_end));
+ }
+
+ spec
+ }
+
+ /// Parses a `Count` parameter at the current position. This does not check
+ /// for 'CountIsNextParam' because that is only used in precision, not
+ /// width.
+ fn count(&mut self, start: usize) -> Count<'a> {
+ if let Some(i) = self.integer() {
+ if self.consume('$') {
+ CountIsParam(i)
+ } else {
+ CountIs(i)
+ }
+ } else {
+ let tmp = self.cur.clone();
+ let word = self.word();
+ if word.is_empty() {
+ self.cur = tmp;
+ CountImplied
+ } else if let Some(end) = self.consume_pos('$') {
+ let name_span = self.span(start, end);
+ CountIsName(word, name_span)
+ } else {
+ self.cur = tmp;
+ CountImplied
+ }
+ }
+ }
+
+ /// Parses a word starting at the current position. A word is the same as
+ /// Rust identifier, except that it can't start with `_` character.
+ fn word(&mut self) -> &'a str {
+ let start = match self.cur.peek() {
+ Some(&(pos, c)) if rustc_lexer::is_id_start(c) => {
+ self.cur.next();
+ pos
+ }
+ _ => {
+ return "";
+ }
+ };
+ let mut end = None;
+ while let Some(&(pos, c)) = self.cur.peek() {
+ if rustc_lexer::is_id_continue(c) {
+ self.cur.next();
+ } else {
+ end = Some(pos);
+ break;
+ }
+ }
+ let end = end.unwrap_or(self.input.len());
+ let word = &self.input[start..end];
+ if word == "_" {
+ self.err_with_note(
+ "invalid argument name `_`",
+ "invalid argument name",
+ "argument name cannot be a single underscore",
+ self.span(start, end),
+ );
+ }
+ word
+ }
+
+ fn integer(&mut self) -> Option<usize> {
+ let mut cur: usize = 0;
+ let mut found = false;
+ let mut overflow = false;
+ let start = self.current_pos();
+ while let Some(&(_, c)) = self.cur.peek() {
+ if let Some(i) = c.to_digit(10) {
+ let (tmp, mul_overflow) = cur.overflowing_mul(10);
+ let (tmp, add_overflow) = tmp.overflowing_add(i as usize);
+ if mul_overflow || add_overflow {
+ overflow = true;
+ }
+ cur = tmp;
+ found = true;
+ self.cur.next();
+ } else {
+ break;
+ }
+ }
+
+ if overflow {
+ let end = self.current_pos();
+ let overflowed_int = &self.input[start..end];
+ self.err(
+ format!(
+ "integer `{}` does not fit into the type `usize` whose range is `0..={}`",
+ overflowed_int,
+ usize::MAX
+ ),
+ "integer out of range for `usize`",
+ self.span(start, end),
+ );
+ }
+
+ found.then_some(cur)
+ }
+
+ fn suggest_format(&mut self) {
+ if let (Some(pos), Some(_)) = (self.consume_pos('?'), self.consume_pos(':')) {
+ let word = self.word();
+ let _end = self.current_pos();
+ let pos = self.to_span_index(pos);
+ self.errors.insert(
+ 0,
+ ParseError {
+ description: "expected format parameter to occur after `:`".to_owned(),
+ note: Some(format!("`?` comes after `:`, try `{}:{}` instead", word, "?")),
+ label: "expected `?` to occur after `:`".to_owned(),
+ span: pos.to(pos),
+ secondary_label: None,
+ should_be_replaced_with_positional_argument: false,
+ },
+ );
+ }
+ }
+
+ fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: Argument<'a>) {
+ if let Some(end) = self.consume_pos('.') {
+ let byte_pos = self.to_span_index(end);
+ let start = InnerOffset(byte_pos.0 + 1);
+ let field = self.argument(start);
+ // We can only parse `foo.bar` field access, any deeper nesting,
+ // or another type of expression, like method calls, are not supported
+ if !self.consume('}') {
+ return;
+ }
+ if let ArgumentNamed(_) = arg.position {
+ if let ArgumentNamed(_) = field.position {
+ self.errors.insert(
+ 0,
+ ParseError {
+ description: "field access isn't supported".to_string(),
+ note: None,
+ label: "not supported".to_string(),
+ span: InnerSpan::new(arg.position_span.start, field.position_span.end),
+ secondary_label: None,
+ should_be_replaced_with_positional_argument: true,
+ },
+ );
+ }
+ }
+ }
+ }
+}
+
+/// Finds the indices of all characters that have been processed and differ between the actual
+/// written code (code snippet) and the `InternedString` that gets processed in the `Parser`
+/// in order to properly synthesise the intra-string `Span`s for error diagnostics.
+fn find_width_map_from_snippet(
+ input: &str,
+ snippet: Option<string::String>,
+ str_style: Option<usize>,
+) -> InputStringKind {
+ let snippet = match snippet {
+ Some(ref s) if s.starts_with('"') || s.starts_with("r\"") || s.starts_with("r#") => s,
+ _ => return InputStringKind::NotALiteral,
+ };
+
+ if str_style.is_some() {
+ return InputStringKind::Literal { width_mappings: Vec::new() };
+ }
+
+ // Strip quotes.
+ let snippet = &snippet[1..snippet.len() - 1];
+
+ // Macros like `println` add a newline at the end. That technically doesn't make them "literals" anymore, but it's fine
+ // since we will never need to point our spans there, so we lie about it here by ignoring it.
+ // Since there might actually be newlines in the source code, we need to normalize away all trailing newlines.
+ // If we only trimmed it off the input, `format!("\n")` would cause a mismatch as here we they actually match up.
+ // Alternatively, we could just count the trailing newlines and only trim one from the input if they don't match up.
+ let input_no_nl = input.trim_end_matches('\n');
+ let Some(unescaped) = unescape_string(snippet) else {
+ return InputStringKind::NotALiteral;
+ };
+
+ let unescaped_no_nl = unescaped.trim_end_matches('\n');
+
+ if unescaped_no_nl != input_no_nl {
+ // The source string that we're pointing at isn't our input, so spans pointing at it will be incorrect.
+ // This can for example happen with proc macros that respan generated literals.
+ return InputStringKind::NotALiteral;
+ }
+
+ let mut s = snippet.char_indices();
+ let mut width_mappings = vec![];
+ while let Some((pos, c)) = s.next() {
+ match (c, s.clone().next()) {
+ // skip whitespace and empty lines ending in '\\'
+ ('\\', Some((_, '\n'))) => {
+ let _ = s.next();
+ let mut width = 2;
+
+ while let Some((_, c)) = s.clone().next() {
+ if matches!(c, ' ' | '\n' | '\t') {
+ width += 1;
+ let _ = s.next();
+ } else {
+ break;
+ }
+ }
+
+ width_mappings.push(InnerWidthMapping::new(pos, width, 0));
+ }
+ ('\\', Some((_, 'n' | 't' | 'r' | '0' | '\\' | '\'' | '\"'))) => {
+ width_mappings.push(InnerWidthMapping::new(pos, 2, 1));
+ let _ = s.next();
+ }
+ ('\\', Some((_, 'x'))) => {
+ // consume `\xAB` literal
+ s.nth(2);
+ width_mappings.push(InnerWidthMapping::new(pos, 4, 1));
+ }
+ ('\\', Some((_, 'u'))) => {
+ let mut width = 2;
+ let _ = s.next();
+
+ if let Some((_, next_c)) = s.next() {
+ if next_c == '{' {
+ // consume up to 6 hexanumeric chars
+ let digits_len =
+ s.clone().take(6).take_while(|(_, c)| c.is_digit(16)).count();
+
+ let len_utf8 = s
+ .as_str()
+ .get(..digits_len)
+ .and_then(|digits| u32::from_str_radix(digits, 16).ok())
+ .and_then(char::from_u32)
+ .map_or(1, char::len_utf8);
+
+ // Skip the digits, for chars that encode to more than 1 utf-8 byte
+ // exclude as many digits as it is greater than 1 byte
+ //
+ // So for a 3 byte character, exclude 2 digits
+ let required_skips = digits_len.saturating_sub(len_utf8.saturating_sub(1));
+
+ // skip '{' and '}' also
+ width += required_skips + 2;
+
+ s.nth(digits_len);
+ } else if next_c.is_digit(16) {
+ width += 1;
+
+ // We suggest adding `{` and `}` when appropriate, accept it here as if
+ // it were correct
+ let mut i = 0; // consume up to 6 hexanumeric chars
+ while let (Some((_, c)), _) = (s.next(), i < 6) {
+ if c.is_digit(16) {
+ width += 1;
+ } else {
+ break;
+ }
+ i += 1;
+ }
+ }
+ }
+
+ width_mappings.push(InnerWidthMapping::new(pos, width, 1));
+ }
+ _ => {}
+ }
+ }
+
+ InputStringKind::Literal { width_mappings }
+}
+
+fn unescape_string(string: &str) -> Option<string::String> {
+ let mut buf = string::String::new();
+ let mut ok = true;
+ unescape::unescape_literal(string, unescape::Mode::Str, &mut |_, unescaped_char| {
+ match unescaped_char {
+ Ok(c) => buf.push(c),
+ Err(_) => ok = false,
+ }
+ });
+
+ ok.then_some(buf)
+}
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index 3e1922750b..4c812b62a4 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -177,7 +177,7 @@ impl ItemTree {
}
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 {
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index 4b852dd613..417bd37c8a 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -2,8 +2,6 @@
use std::fmt::{self, Write};
-use hir_expand::db::ExpandDatabase;
-
use crate::{
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
pretty::{print_path, print_type_bounds, print_type_ref},
@@ -12,7 +10,7 @@ use crate::{
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 };
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
@@ -45,7 +43,7 @@ macro_rules! wln {
}
struct Printer<'a> {
- db: &'a dyn ExpandDatabase,
+ db: &'a dyn DefDatabase,
tree: &'a ItemTree,
buf: String,
indent_level: usize,
@@ -91,7 +89,7 @@ impl Printer<'_> {
self,
"#{}[{}{}]{}",
inner,
- attr.path.display(self.db),
+ attr.path.display(self.db.upcast()),
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
separated_by,
);
@@ -106,7 +104,7 @@ impl Printer<'_> {
fn print_visibility(&mut self, vis: RawVisibilityId) {
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 "),
};
}
@@ -121,7 +119,7 @@ impl Printer<'_> {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field, "\n");
this.print_visibility(*visibility);
- w!(this, "{}: ", name.display(self.db));
+ w!(this, "{}: ", name.display(self.db.upcast()));
this.print_type_ref(type_ref);
wln!(this, ",");
}
@@ -135,7 +133,7 @@ impl Printer<'_> {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field, "\n");
this.print_visibility(*visibility);
- w!(this, "{}: ", name.display(self.db));
+ w!(this, "{}: ", name.display(self.db.upcast()));
this.print_type_ref(type_ref);
wln!(this, ",");
}
@@ -168,20 +166,20 @@ impl Printer<'_> {
fn print_use_tree(&mut self, use_tree: &UseTree) {
match &use_tree.kind {
UseTreeKind::Single { path, alias } => {
- w!(self, "{}", path.display(self.db));
+ w!(self, "{}", path.display(self.db.upcast()));
if let Some(alias) = alias {
w!(self, " as {}", alias);
}
}
UseTreeKind::Glob { path } => {
if let Some(path) = path {
- w!(self, "{}::", path.display(self.db));
+ w!(self, "{}::", path.display(self.db.upcast()));
}
w!(self, "*");
}
UseTreeKind::Prefixed { prefix, list } => {
if let Some(prefix) = prefix {
- w!(self, "{}::", prefix.display(self.db));
+ w!(self, "{}::", prefix.display(self.db.upcast()));
}
w!(self, "{{");
for (i, tree) in list.iter().enumerate() {
@@ -209,7 +207,7 @@ impl Printer<'_> {
ModItem::ExternCrate(it) => {
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
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 {
w!(self, " as {}", alias);
}
@@ -256,7 +254,7 @@ impl Printer<'_> {
if let Some(abi) = 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);
w!(self, "(");
if !params.is_empty() {
@@ -290,7 +288,7 @@ impl Printer<'_> {
ModItem::Struct(it) => {
let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
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_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) {
@@ -302,7 +300,7 @@ impl Printer<'_> {
ModItem::Union(it) => {
let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
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_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) {
@@ -314,14 +312,14 @@ impl Printer<'_> {
ModItem::Enum(it) => {
let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
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_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
for variant in variants.clone() {
let Variant { name, fields, ast_id: _ } = &this.tree[variant];
this.print_attrs_of(variant, "\n");
- w!(this, "{}", name.display(self.db));
+ w!(this, "{}", name.display(self.db.upcast()));
this.print_fields(fields);
wln!(this, ",");
}
@@ -333,7 +331,7 @@ impl Printer<'_> {
self.print_visibility(*visibility);
w!(self, "const ");
match name {
- Some(name) => w!(self, "{}", name.display(self.db)),
+ Some(name) => w!(self, "{}", name.display(self.db.upcast())),
None => w!(self, "_"),
}
w!(self, ": ");
@@ -347,7 +345,7 @@ impl Printer<'_> {
if *mutable {
w!(self, "mut ");
}
- w!(self, "{}: ", name.display(self.db));
+ w!(self, "{}: ", name.display(self.db.upcast()));
self.print_type_ref(type_ref);
w!(self, " = _;");
wln!(self);
@@ -369,7 +367,7 @@ impl Printer<'_> {
if *is_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_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
@@ -382,7 +380,7 @@ impl Printer<'_> {
ModItem::TraitAlias(it) => {
let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
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);
w!(self, " = ");
self.print_where_clause(generic_params);
@@ -415,7 +413,7 @@ impl Printer<'_> {
let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
&self.tree[it];
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);
if !bounds.is_empty() {
w!(self, ": ");
@@ -432,7 +430,7 @@ impl Printer<'_> {
ModItem::Mod(it) => {
let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "mod {}", name.display(self.db));
+ w!(self, "mod {}", name.display(self.db.upcast()));
match kind {
ModKind::Inline { items } => {
w!(self, " {{");
@@ -450,16 +448,16 @@ impl Printer<'_> {
}
ModItem::MacroCall(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) => {
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) => {
let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
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;
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() {
if !first {
@@ -501,11 +499,11 @@ impl Printer<'_> {
self.print_attrs_of(idx, " ");
match x {
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()),
},
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);
}
}
@@ -540,8 +538,8 @@ impl Printer<'_> {
wln!(
this,
"{}: {},",
- target.name.display(self.db),
- bound.name.display(self.db)
+ target.name.display(self.db.upcast()),
+ bound.name.display(self.db.upcast())
);
continue;
}
@@ -551,7 +549,7 @@ impl Printer<'_> {
if i != 0 {
w!(this, ", ");
}
- w!(this, "{}", lt.display(self.db));
+ w!(this, "{}", lt.display(self.db.upcast()));
}
w!(this, "> ");
(target, bound)
@@ -562,7 +560,7 @@ impl Printer<'_> {
WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
WherePredicateTypeTarget::TypeOrConstParam(id) => {
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()),
}
}
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index 627479bb7c..1ae6bd4c91 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -2,6 +2,7 @@
//!
//! This attribute to tell the compiler about semi built-in std library
//! features, such as Fn family of traits.
+use hir_expand::name::Name;
use rustc_hash::FxHashMap;
use syntax::SmolStr;
use triomphe::Arc;
@@ -238,7 +239,17 @@ impl LangItem {
pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
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)))
}
}
diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index a350511c7a..4aedb22c6b 100644
--- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -240,7 +240,7 @@ macro_rules! format_args {
}
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() {
format_args!(x = 2);
- format_args!(x =);
- format_args!(x =, x = 2);
- format_args!("{}", x =);
- format_args!(=, "{}", x =);
+ format_args!/*+errors*/(x =);
+ format_args!/*+errors*/(x =, x = 2);
+ format_args!/*+errors*/("{}", x =);
+ format_args!/*+errors*/(=, "{}", x =);
format_args!(x = 2, "{}", 5);
}
"#,
@@ -273,12 +273,19 @@ macro_rules! format_args {
}
fn main() {
- /* error: no rule matches input tokens */;
- /* error: expected expression */;
- /* error: expected expression, expected COMMA */;
- /* error: expected expression */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(), ::core::fmt::Display::fmt), ]);
- /* error: expected expression, expected R_PAREN */;
- ::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(5), ::core::fmt::Display::fmt), ]);
+ builtin #format_args (x = 2);
+ /* parse error: expected expression */
+builtin #format_args (x = );
+ /* parse error: expected expression */
+/* parse error: expected R_PAREN */
+/* 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() {
- ::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() {
- ::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() {
- ::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() {
let _ =
- /* 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), ]);
+ /* parse error: expected field name or number */
+builtin #format_args ("{} {:?}", a.);
}
"##]],
);
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 2170cadcf8..d090621324 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -117,7 +117,7 @@ fn main(foo: ()) {
macro_rules! format_args {}
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: ()) {
- // format_args/*+tokenids*/!("{} {} {}"#1,#3 format_args!("{}", 0#10),#12 foo#13,#14 identity!(10#18),#21 "bar"#22)
-::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
+ // 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)
+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
}
"##]],
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index 97554f93f1..b416f45ff2 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -929,8 +929,8 @@ fn main() {
macro_rules! format_args {}
fn main() {
- /* 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), ]);
+ /* parse error: expected field name or number */
+builtin #format_args ("{}", line.1.);
}
"##]],
@@ -956,19 +956,15 @@ fn main() {
macro_rules! format_args {}
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 COMMA */
/* parse error: expected COMMA */
/* parse error: expected expression */
/* 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 */
-::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(&[0 2]), ::core::fmt::Display::fmt), ]);
+builtin #format_args ("{}", &[0 2]);
}
"##]],
diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs
index 06530cc7eb..3894172a5a 100644
--- a/crates/hir-def/src/path.rs
+++ b/crates/hir-def/src/path.rs
@@ -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
/// 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
@@ -122,33 +122,40 @@ impl Path {
pub fn kind(&self) -> &PathKind {
match self {
Path::Normal { mod_path, .. } => &mod_path.kind,
- Path::LangItem(_) => &PathKind::Abs,
+ Path::LangItem(..) => &PathKind::Abs,
}
}
pub fn type_anchor(&self) -> Option<&TypeRef> {
match self {
Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
- Path::LangItem(_) => None,
+ Path::LangItem(..) => None,
}
}
pub fn segments(&self) -> PathSegments<'_> {
- let Path::Normal { mod_path, generic_args, .. } = self else {
- return PathSegments { segments: &[], generic_args: None };
- };
- let s =
- PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
- if let Some(generic_args) = s.generic_args {
- assert_eq!(s.segments.len(), generic_args.len());
+ match self {
+ Path::Normal { mod_path, generic_args, .. } => {
+ let s = PathSegments {
+ segments: mod_path.segments(),
+ generic_args: generic_args.as_deref(),
+ };
+ if let Some(generic_args) = s.generic_args {
+ assert_eq!(s.segments.len(), generic_args.len());
+ }
+ s
+ }
+ Path::LangItem(_, seg) => PathSegments {
+ segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
+ generic_args: None,
+ },
}
- s
}
pub fn mod_path(&self) -> Option<&ModPath> {
match self {
Path::Normal { mod_path, .. } => Some(&mod_path),
- Path::LangItem(_) => None,
+ Path::LangItem(..) => None,
}
}
diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs
index 11d58a6ba0..f4f5541e37 100644
--- a/crates/hir-def/src/pretty.rs
+++ b/crates/hir-def/src/pretty.rs
@@ -2,18 +2,54 @@
use std::fmt::{self, Write};
-use hir_expand::{db::ExpandDatabase, mod_path::PathKind};
+use hir_expand::mod_path::PathKind;
use intern::Interned;
use itertools::Itertools;
use crate::{
+ db::DefDatabase,
+ lang_item::LangItemTarget,
path::{GenericArg, GenericArgs, Path},
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
};
-pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
- if let Path::LangItem(it) = path {
- return write!(buf, "$lang_item::{it:?}");
+pub(crate) fn print_path(db: &dyn DefDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
+ if let Path::LangItem(it, s) = path {
+ 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() {
Some(anchor) => {
@@ -44,7 +80,7 @@ pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Wri
write!(buf, "::")?;
}
- write!(buf, "{}", segment.name.display(db))?;
+ write!(buf, "{}", segment.name.display(db.upcast()))?;
if let Some(generics) = segment.args_and_bindings {
write!(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(
- db: &dyn ExpandDatabase,
+ db: &dyn DefDatabase,
generics: &GenericArgs,
buf: &mut dyn Write,
) -> fmt::Result {
@@ -83,7 +119,7 @@ pub(crate) fn print_generic_args(
write!(buf, ", ")?;
}
first = false;
- write!(buf, "{}", binding.name.display(db))?;
+ write!(buf, "{}", binding.name.display(db.upcast()))?;
if !binding.bounds.is_empty() {
write!(buf, ": ")?;
print_type_bounds(db, &binding.bounds, buf)?;
@@ -97,19 +133,19 @@ pub(crate) fn print_generic_args(
}
pub(crate) fn print_generic_arg(
- db: &dyn ExpandDatabase,
+ db: &dyn DefDatabase,
arg: &GenericArg,
buf: &mut dyn Write,
) -> fmt::Result {
match arg {
GenericArg::Type(ty) => print_type_ref(db, ty, buf),
- GenericArg::Const(c) => write!(buf, "{}", c.display(db)),
- GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)),
+ GenericArg::Const(c) => write!(buf, "{}", c.display(db.upcast())),
+ GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast())),
}
}
pub(crate) fn print_type_ref(
- db: &dyn ExpandDatabase,
+ db: &dyn DefDatabase,
type_ref: &TypeRef,
buf: &mut dyn Write,
) -> fmt::Result {
@@ -143,7 +179,7 @@ pub(crate) fn print_type_ref(
};
write!(buf, "&")?;
if let Some(lt) = lt {
- write!(buf, "{} ", lt.name.display(db))?;
+ write!(buf, "{} ", lt.name.display(db.upcast()))?;
}
write!(buf, "{mtbl}")?;
print_type_ref(db, pointee, buf)?;
@@ -151,7 +187,7 @@ pub(crate) fn print_type_ref(
TypeRef::Array(elem, len) => {
write!(buf, "[")?;
print_type_ref(db, elem, buf)?;
- write!(buf, "; {}]", len.display(db))?;
+ write!(buf, "; {}]", len.display(db.upcast()))?;
}
TypeRef::Slice(elem) => {
write!(buf, "[")?;
@@ -198,7 +234,7 @@ pub(crate) fn print_type_ref(
}
pub(crate) fn print_type_bounds(
- db: &dyn ExpandDatabase,
+ db: &dyn DefDatabase,
bounds: &[Interned<TypeBound>],
buf: &mut dyn Write,
) -> fmt::Result {
@@ -216,10 +252,14 @@ pub(crate) fn print_type_bounds(
print_path(db, path, buf)?;
}
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)?;
}
- TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?,
+ TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast()))?,
TypeBound::Error => write!(buf, "{{unknown}}")?,
}
}
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 2f9187009e..50da9ed06a 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -156,22 +156,19 @@ impl Resolver {
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
let path = match path {
Path::Normal { mod_path, .. } => mod_path,
- Path::LangItem(l) => {
- return Some((
- 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,
- },
- None,
- None,
- ))
+ Path::LangItem(l, seg) => {
+ 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((type_ns, seg.as_ref().map(|_| 1), None));
}
};
let first_name = path.segments().first()?;
@@ -256,7 +253,7 @@ impl Resolver {
) -> Option<ResolveValueResult> {
let path = match path {
Path::Normal { mod_path, .. } => mod_path,
- Path::LangItem(l) => {
+ Path::LangItem(l, None) => {
return Some(ResolveValueResult::ValueNs(
match *l {
LangItemTarget::Function(it) => ValueNs::FunctionId(it),
@@ -272,6 +269,20 @@ impl Resolver {
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 tmp = name![self];
diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs
index 00ee5e8b9d..30b19b6e51 100644
--- a/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/crates/hir-expand/src/builtin_fn_macro.rs
@@ -1,13 +1,9 @@
//! Builtin macro
-use std::mem;
-
-use ::tt::Ident;
use base_db::{AnchoredPath, Edition, FileId};
use cfg::CfgExpr;
use either::Either;
use mbe::{parse_exprs_with_sep, parse_to_token_tree, TokenMap};
-use rustc_hash::FxHashMap;
use syntax::{
ast::{self, AstToken},
SmolStr,
@@ -97,11 +93,11 @@ register_builtin! {
(unreachable, Unreachable) => unreachable_expand,
(log_syntax, LogSyntax) => log_syntax_expand,
(trace_macros, TraceMacros) => trace_macros_expand,
-
- EAGER:
(format_args, FormatArgs) => format_args_expand,
(const_format_args, ConstFormatArgs) => format_args_expand,
(format_args_nl, FormatArgsNl) => format_args_nl_expand,
+
+ EAGER:
(compile_error, CompileError) => compile_error_expand,
(concat, Concat) => concat_expand,
(concat_idents, ConcatIdents) => concat_idents_expand,
@@ -247,151 +243,15 @@ fn format_args_expand_general(
_db: &dyn ExpandDatabase,
_id: MacroCallId,
tt: &tt::Subtree,
- end_string: &str,
+ // FIXME: Make use of this so that mir interpretation works properly
+ _end_string: &str,
) -> ExpandResult<tt::Subtree> {
- let args = parse_exprs_with_sep(tt, ',');
-
- let expand_error =
- ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into());
-
- 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 pound = quote! {@PUNCT '#'};
+ let mut tt = tt.clone();
+ tt.delimiter.kind = tt::DelimiterKind::Parenthesis;
+ return ExpandResult::ok(quote! {
+ builtin #pound format_args #tt
});
- 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(
@@ -399,8 +259,6 @@ fn asm_expand(
_id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
- // FIXME: parse asm here
-
// We expand all assembly snippets to `format_args!` invocations to get format syntax
// highlighting for them.
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 7c179c0cf9..a876f48bda 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -54,6 +54,12 @@ impl Name {
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 {
Name(Repr::TupleField(idx))
}
@@ -302,6 +308,16 @@ pub mod known {
rust_2018,
rust_2021,
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)
Iterator,
IntoIterator,
@@ -327,6 +343,13 @@ pub mod known {
Not,
None,
Index,
+ Left,
+ Right,
+ Center,
+ Unknown,
+ Is,
+ Param,
+ Implied,
// Components of known path (function name)
filter_map,
next,
@@ -335,6 +358,8 @@ pub mod known {
is_empty,
as_str,
new,
+ new_v1_formatted,
+ none,
// Builtin macros
asm,
assert,
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 2a51c84db3..c6bbf2f614 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -178,13 +178,30 @@ impl InferenceContext<'_> {
remaining_index: usize,
id: ExprOrPatId,
) -> Option<(ValueNs, Substitution)> {
- assert!(remaining_index < path.segments().len());
// 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 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 remaining_segments = path.segments().skip(remaining_index);
+ let _d;
+ 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;
match (def, is_before_last) {
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index e7cb7cd417..8140c4107b 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -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<'_>
+}
+"#,
+ );
+}
diff --git a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
index c3d925cb26..31a1ff496e 100644
--- a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
+++ b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
@@ -15,26 +15,13 @@ use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange};
// Move an expression out of a format string.
//
// ```
-// macro_rules! format_args {
-// ($lit:literal $(tt:tt)*) => { 0 },
-// }
-// macro_rules! print {
-// ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
-// }
-//
+// # //- minicore: fmt
// fn main() {
// 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() {
// print!("{var} {}"$0, x + 1);
// }
@@ -158,37 +145,21 @@ mod tests {
use super::*;
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]
fn multiple_middle_arg() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{} {x + 1:b} {}$0", y + 2, 2);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{} {:b} {}"$0, y + 2, x + 1, 2);
}
"#,
- ),
);
}
@@ -196,20 +167,17 @@ fn main() {
fn single_arg() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{obj.value:b}$0",);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{:b}"$0, obj.value);
}
"#,
- ),
);
}
@@ -217,20 +185,17 @@ fn main() {
fn multiple_middle_placeholders_arg() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{} {x + 1:b} {} {}$0", y + 2, 2);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{} {:b} {} {}"$0, y + 2, x + 1, 2, $1);
}
"#,
- ),
);
}
@@ -238,20 +203,17 @@ fn main() {
fn multiple_trailing_args() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{:b} {x + 1:b} {Struct(1, 2)}$0", 1);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{:b} {:b} {}"$0, 1, x + 1, Struct(1, 2));
}
"#,
- ),
);
}
@@ -259,20 +221,17 @@ fn main() {
fn improper_commas() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{} {x + 1:b} {Struct(1, 2)}$0", 1,);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2));
}
"#,
- ),
);
}
@@ -280,20 +239,17 @@ fn main() {
fn nested_tt() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("My name is {} {x$0 + x}", stringify!(Paperino))
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("My name is {} {}"$0, stringify!(Paperino), x + x)
}
"#,
- ),
);
}
@@ -301,22 +257,19 @@ fn main() {
fn extract_only_expressions() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
let var = 1 + 1;
print!("foobar {var} {var:?} {x$0 + x}")
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
let var = 1 + 1;
print!("foobar {var} {var:?} {}"$0, x + x)
}
"#,
- ),
);
}
}
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 6eadc3dbcb..8bd87e5f5d 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -694,25 +694,12 @@ fn doctest_extract_expressions_from_format_string() {
check_doc_test(
"extract_expressions_from_format_string",
r#####"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
-macro_rules! print {
- ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
-}
-
+//- minicore: fmt
fn main() {
print!("{var} {x + 1}$0");
}
"#####,
r#####"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
-macro_rules! print {
- ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
-}
-
fn main() {
print!("{var} {}"$0, x + 1);
}
diff --git a/crates/ide-completion/src/completions/format_string.rs b/crates/ide-completion/src/completions/format_string.rs
index 8e904fd605..cecbe75391 100644
--- a/crates/ide-completion/src/completions/format_string.rs
+++ b/crates/ide-completion/src/completions/format_string.rs
@@ -51,9 +51,7 @@ mod tests {
fn works_when_wrapped() {
check(
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
+//- minicore: fmt
macro_rules! print {
($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
}
@@ -70,9 +68,7 @@ fn main() {
fn no_completion_without_brace() {
check(
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
+//- minicore: fmt
fn main() {
let foobar = 1;
format_args!("f$0");
@@ -87,18 +83,13 @@ fn main() {
check_edit(
"foobar",
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
+//- minicore: fmt
fn main() {
let foobar = 1;
format_args!("{f$0");
}
"#,
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
fn main() {
let foobar = 1;
format_args!("{foobar");
@@ -108,18 +99,13 @@ fn main() {
check_edit(
"foobar",
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
+//- minicore: fmt
fn main() {
let foobar = 1;
format_args!("{$0");
}
"#,
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
fn main() {
let foobar = 1;
format_args!("{foobar");
diff --git a/crates/ide-db/src/syntax_helpers/format_string.rs b/crates/ide-db/src/syntax_helpers/format_string.rs
index acf0a67de4..8302b015dd 100644
--- a/crates/ide-db/src/syntax_helpers/format_string.rs
+++ b/crates/ide-db/src/syntax_helpers/format_string.rs
@@ -1,10 +1,10 @@
//! 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::{
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 {
// Check if `string` is a format string argument of a macro invocation.
// `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
// strings. It still fails for `concat!("{", "}")`, but that is rare.
(|| {
- let name = macro_call_for_string_token(string)?.path()?.segment()?.name_ref()?;
-
- if !matches!(
- 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(())
+ let lit = string.syntax().parent().and_then(ast::Literal::cast)?;
+ let fa = lit.syntax().parent().and_then(ast::FormatArgsExpr::cast)?;
+ (fa.template()? == ast::Expr::Literal(lit)).then_some(|| ())
})()
.is_some()
}
diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs
index f54cdd63bb..7ca0a0eab2 100644
--- a/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -157,6 +157,7 @@ struct S;
fn macro_diag_builtin() {
check_diagnostics(
r#"
+//- minicore: fmt
#[rustc_builtin_macro]
macro_rules! env {}
@@ -166,9 +167,6 @@ macro_rules! include {}
#[rustc_builtin_macro]
macro_rules! compile_error {}
-#[rustc_builtin_macro]
-macro_rules! format_args { () => {} }
-
fn main() {
// Test a handful of built-in (eager) macros:
@@ -189,7 +187,7 @@ fn main() {
// Lazy:
format_args!();
- //^^^^^^^^^^^ error: no rule matches input tokens
+ //^^^^^^^^^^^ error: Syntax Error in Expansion: expected expression
}
"#,
);
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index d0f9f7b0e1..81d6db564f 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -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
+ ```
+ "#]],
+ );
+}
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index b621a8dda7..8207bec7e8 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -474,7 +474,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 9289..9297,
+ range: 10739..10747,
},
),
tooltip: "",
@@ -487,7 +487,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 9321..9325,
+ range: 10771..10775,
},
),
tooltip: "",
@@ -511,7 +511,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 9289..9297,
+ range: 10739..10747,
},
),
tooltip: "",
@@ -524,7 +524,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 9321..9325,
+ range: 10771..10775,
},
),
tooltip: "",
@@ -548,7 +548,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 9289..9297,
+ range: 10739..10747,
},
),
tooltip: "",
@@ -561,7 +561,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 9321..9325,
+ range: 10771..10775,
},
),
tooltip: "",
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs
index 2ed57e2013..2ef1315945 100644
--- a/crates/ide/src/syntax_highlighting/format.rs
+++ b/crates/ide/src/syntax_highlighting/format.rs
@@ -17,6 +17,7 @@ pub(super) fn highlight_format_string(
return;
}
+ // FIXME: Replace this with the HIR info we have now.
lex_format_specifiers(string, &mut |piece_range, kind| {
if let Some(highlight) = highlight_format_specifier(kind) {
stack.add(HlRange {
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 8e96bfa01a..7d00282fc1 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -617,6 +617,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
CONST => SymbolKind::Const,
STATIC => SymbolKind::Static,
IDENT_PAT => SymbolKind::Local,
+ FORMAT_ARGS_ARG => SymbolKind::Local,
_ => return default.into(),
};
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 9c5c6d50ea..64e614cecd 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -45,17 +45,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
</style>
<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="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="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</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">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="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="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="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="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="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">"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="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="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="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">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">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">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="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="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">"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="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">-</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>
@@ -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="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="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">"</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 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="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">"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="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="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="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="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="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="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="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="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">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">"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">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="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">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">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="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">"{} 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">"{}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">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="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">"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">"mov {0}, {1}"</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="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="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">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="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 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> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index aacd57af58..542d899253 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -401,19 +401,14 @@ fn test_string_highlighting() {
// thus, we have to copy the macro definition from `std`
check_highlighting(
r#"
+//- minicore: fmt
macro_rules! println {
($($arg:tt)*) => ({
- $crate::io::_print($crate::format_args_nl!($($arg)*));
+ $crate::io::_print(format_args_nl!($($arg)*));
})
}
#[rustc_builtin_macro]
#[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 {}
mod panic {
@@ -433,7 +428,7 @@ mod panic {
$crate::panicking::panic_display(&$arg)
),
($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 {
() => ($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() {
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index e13284d1b7..4197f248e0 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -219,7 +219,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
// test builtin_expr
// fn foo() {
// builtin#asm(0);
-// builtin#format_args(0);
+// builtin#format_args("", 0, 1, a = 2 + 3, a + b);
// builtin#offset_of(Foo, bar.baz.0);
// }
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.expect(T!['(']);
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![')']);
Some(m.complete(p, FORMAT_ARGS_EXPR))
} else if p.at_contextual_kw(T![asm]) {
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 3e31e4628b..db5278f89d 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -210,6 +210,7 @@ pub enum SyntaxKind {
OFFSET_OF_EXPR,
ASM_EXPR,
FORMAT_ARGS_EXPR,
+ FORMAT_ARGS_ARG,
CALL_EXPR,
INDEX_EXPR,
METHOD_CALL_EXPR,
diff --git a/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast b/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast
index f127b3e8c8..361900b6d3 100644
--- a/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast
+++ b/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast
@@ -30,7 +30,50 @@ SOURCE_FILE
FORMAT_ARGS_KW "format_args"
L_PAREN "("
LITERAL
- INT_NUMBER "0"
+ STRING "\"\""
+ COMMA ","
+ WHITESPACE " "
+ FORMAT_ARGS_ARG
+ LITERAL
+ 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 ")"
SEMICOLON ";"
WHITESPACE "\n "
diff --git a/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs b/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs
index dbad0a91df..14431b0210 100644
--- a/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs
+++ b/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs
@@ -1,5 +1,5 @@
fn foo() {
builtin#asm(0);
- builtin#format_args(0);
+ builtin#format_args("", 0, 1, a = 2 + 3, a + b);
builtin#offset_of(Foo, bar.baz.0);
}
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 2ce609b97a..3603560d35 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -382,7 +382,13 @@ AsmExpr =
Attr* 'builtin' '#' 'asm' '(' Expr ')'
FormatArgsExpr =
- Attr* 'builtin' '#' 'format_args' '(' ')'
+ Attr* 'builtin' '#' 'format_args' '('
+ template:Expr
+ (',' args:(FormatArgsArg (',' FormatArgsArg)* ','?)? )?
+ ')'
+
+FormatArgsArg =
+ (Name '=')? Expr
MacroExpr =
MacroCall
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 1c5e2282ec..7ba0d4dc65 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -931,6 +931,9 @@ impl FormatArgsExpr {
support::token(&self.syntax, T![format_args])
}
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![')']) }
}
@@ -1164,6 +1167,16 @@ impl UnderscoreExpr {
}
#[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)]
pub struct StmtList {
pub(crate) syntax: SyntaxNode,
}
@@ -2855,6 +2868,17 @@ impl AstNode for UnderscoreExpr {
}
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 {
fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -4254,6 +4278,7 @@ impl AstNode for AnyHasName {
| VARIANT
| CONST_PARAM
| TYPE_PARAM
+ | FORMAT_ARGS_ARG
| IDENT_PAT
)
}
@@ -4860,6 +4885,11 @@ impl std::fmt::Display for UnderscoreExpr {
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 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/tests/ast_src.rs b/crates/syntax/src/tests/ast_src.rs
index 2c1d832d1e..341bda892b 100644
--- a/crates/syntax/src/tests/ast_src.rs
+++ b/crates/syntax/src/tests/ast_src.rs
@@ -169,6 +169,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc {
"OFFSET_OF_EXPR",
"ASM_EXPR",
"FORMAT_ARGS_EXPR",
+ "FORMAT_ARGS_ARG",
// postfix
"CALL_EXPR",
"INDEX_EXPR",
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index c765f42447..573f56b003 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -899,32 +899,90 @@ pub mod fmt {
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
}
- extern "C" {
- type Opaque;
- }
+ mod rt {
- #[lang = "format_argument"]
- pub struct Argument<'a> {
- value: &'a Opaque,
- formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
- }
+ extern "C" {
+ type Opaque;
+ }
+
+ #[lang = "format_argument"]
+ pub struct Argument<'a> {
+ value: &'a Opaque,
+ formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
+ }
+
+ impl<'a> Argument<'a> {
+ pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
+ use crate::mem::transmute;
+ unsafe { Argument { formatter: transmute(f), value: transmute(x) } }
+ }
+ }
+
+ #[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<'a> Argument<'a> {
- pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
- use crate::mem::transmute;
- unsafe { Argument { formatter: transmute(f), value: transmute(x) } }
+ impl UnsafeArg {
+ pub unsafe fn new() -> Self;
}
}
#[lang = "format_arguments"]
pub struct Arguments<'a> {
pieces: &'a [&'static str],
- args: &'a [Argument<'a>],
+ fmt: Option<&'a [rt::Placeholder]>,
+ args: &'a [rt::Argument<'a>],
}
impl<'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 */
};
}
-
- pub(crate) use panic;
// endregion:panic
// region:fmt
@@ -1306,7 +1362,20 @@ mod macros {
($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
// region:derive