diff --git a/crates/hir-def/src/expr_store/lower/format_args.rs b/crates/hir-def/src/expr_store/lower/format_args.rs
index 5552213aba55..ad548c6758c1 100644
--- a/crates/hir-def/src/expr_store/lower/format_args.rs
+++ b/crates/hir-def/src/expr_store/lower/format_args.rs
@@ -1,10 +1,13 @@
//! Lowering of `format_args!()`.
use base_db::FxIndexSet;
-use hir_expand::name::Name;
+use hir_expand::name::{AsName, Name};
use intern::{Symbol, sym};
use span::SyntaxContext;
-use syntax::{AstPtr, AstToken as _, ast};
+use syntax::{
+ AstPtr, AstToken as _,
+ ast::{self, HasName},
+};
use crate::{
expr_store::{HygieneId, lower::ExprCollector, path::Path},
@@ -29,8 +32,8 @@ impl<'db> ExprCollector<'db> {
f.args().for_each(|arg| {
let expr = arg.expr();
args.add(FormatArgument {
- kind: match arg.arg_name() {
- Some(name) => FormatArgumentKind::Named(Name::new_root(name.name().text())),
+ kind: match arg.name() {
+ Some(name) => FormatArgumentKind::Named(name.as_name()),
None => FormatArgumentKind::Normal,
},
syntax: expr.as_ref().map(AstPtr::new),
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 da553cebb6b7..b5ace7287d89 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -101,18 +101,18 @@
println!("Hello, {}!", "world");
println!("The number is {}", 1);
println!("{:?}", (3, 4));
- println!("{value}", value=4);
+ println!("{value}", value=4);
println!("{} {}", 1, 2);
println!("{:04}", 42);
println!("{1} {} {0} {}", 1, 2);
- println!("{argument}", argument = "test");
- println!("{name} {}", 1, name = 2);
- println!("{a} {c} {b}", a="a", b='b', c=3);
+ println!("{argument}", argument = "test");
+ println!("{name} {}", 1, name = 2);
+ println!("{a} {c} {b}", a="a", b='b', c=3);
println!("{{{}}}", 2);
println!("Hello {:5}!", "x");
println!("Hello {:1$}!", "x", 5);
println!("Hello {1:0$}!", 5, "x");
- println!("Hello {:width$}!", "x", width = 5);
+ println!("Hello {:width$}!", "x", width = 5);
println!("Hello {:<5}!", "x");
println!("Hello {:-<5}!", "x");
println!("Hello {:^5}!", "x");
@@ -127,10 +127,10 @@
println!("Hello {0} is {2:.1$}", "x", 5, 0.01);
println!("Hello {} is {:.*}", "x", 5, 0.01);
println!("Hello {} is {2:.*}", "x", 5, 0.01);
- println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01);
- println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56);
- println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56");
- println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56");
+ println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01);
+ println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56);
+ println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56");
+ println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56");
let _ = "{}"
let _ = "{{}}";
@@ -154,8 +154,8 @@
let _ = c"\u{FF}\xFF";
let backslash = r"\\";
- println!("{\x41}", A = 92);
- println!("{ничоси}", ничоси = 92);
+ println!("{\x41}", A = 92);
+ println!("{ничоси}", ничоси = 92);
println!("{:x?} {} ", thingy, n2);
panic!("{}", 0);
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 0623e7ea19ab..0305733af2d4 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -343,6 +343,12 @@ fn name(p: &mut Parser<'_>) {
name_r(p, TokenSet::EMPTY);
}
+fn name_any_identifier(p: &mut Parser<'_>) {
+ let m = p.start();
+ p.bump_remap_any_ident();
+ m.complete(p, NAME);
+}
+
fn name_ref_or_self(p: &mut Parser<'_>) {
if matches!(p.current(), T![ident] | T![self]) {
let m = p.start();
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 7ce1fc2c4895..d1f4d4553b77 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -291,10 +291,8 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option {
while !p.at(EOF) && !p.at(T![')']) {
let m = p.start();
if p.current().is_any_identifier() && p.nth_at(1, T![=]) && !p.nth_at(2, T![=]) {
- let m = p.start();
- p.bump_any();
+ name_any_identifier(p);
p.bump(T![=]);
- m.complete(p, FORMAT_ARGS_ARG_NAME);
}
if expr(p).is_none() {
m.abandon(p);
@@ -376,8 +374,12 @@ pub(crate) fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option Parser<'t> {
true
}
+ /// This consumes the next token as an identifier, even if it is a strict keyword.
+ pub(crate) fn bump_remap_any_ident(&mut self) {
+ assert!(self.current().is_any_identifier());
+ self.bump_remap(T![ident]);
+ }
+
fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool {
self.inp.kind(self.pos + n) == k1
&& self.inp.kind(self.pos + n + 1) == k2
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 1c4fcf080f4a..5604da5026e9 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -214,7 +214,6 @@ pub enum SyntaxKind {
FN,
FN_PTR_TYPE,
FORMAT_ARGS_ARG,
- FORMAT_ARGS_ARG_NAME,
FORMAT_ARGS_EXPR,
FOR_BINDER,
FOR_EXPR,
@@ -402,7 +401,6 @@ impl SyntaxKind {
| FN
| FN_PTR_TYPE
| FORMAT_ARGS_ARG
- | FORMAT_ARGS_ARG_NAME
| FORMAT_ARGS_EXPR
| FOR_BINDER
| FOR_EXPR
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs
index 22b568458125..f559c8ee2ff5 100644
--- a/crates/parser/test_data/generated/runner.rs
+++ b/crates/parser/test_data/generated/runner.rs
@@ -21,6 +21,10 @@ mod ok {
#[test]
fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); }
#[test]
+ fn asm_keyword_name() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/asm_keyword_name.rs");
+ }
+ #[test]
fn asm_kinds() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_kinds.rs"); }
#[test]
fn asm_label() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_label.rs"); }
diff --git a/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rast b/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rast
new file mode 100644
index 000000000000..4c97562f9a43
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rast
@@ -0,0 +1,42 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ ASM_EXPR
+ BUILTIN_KW "builtin"
+ WHITESPACE " "
+ POUND "#"
+ WHITESPACE " "
+ ASM_KW "asm"
+ L_PAREN "("
+ LITERAL
+ STRING "\"\""
+ COMMA ","
+ WHITESPACE " "
+ ASM_OPERAND_NAMED
+ NAME
+ IDENT "fn"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ ASM_CONST
+ CONST_KW "const"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "0"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rs b/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rs
new file mode 100644
index 000000000000..a8f1ab285868
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rs
@@ -0,0 +1,3 @@
+fn foo() {
+ builtin # asm("", fn = const 0);
+}
diff --git a/crates/parser/test_data/parser/inline/ok/builtin_expr.rast b/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
index 78c3bee4c88a..19a84ac54096 100644
--- a/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
+++ b/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
@@ -44,10 +44,10 @@ SOURCE_FILE
COMMA ","
WHITESPACE " "
FORMAT_ARGS_ARG
- FORMAT_ARGS_ARG_NAME
+ NAME
IDENT "a"
- WHITESPACE " "
- EQ "="
+ WHITESPACE " "
+ EQ "="
WHITESPACE " "
BIN_EXPR
LITERAL
diff --git a/crates/parser/test_data/parser/inline/ok/format_args_named_arg_keyword.rast b/crates/parser/test_data/parser/inline/ok/format_args_named_arg_keyword.rast
index 03bc2ecf7c21..50b9208b3e54 100644
--- a/crates/parser/test_data/parser/inline/ok/format_args_named_arg_keyword.rast
+++ b/crates/parser/test_data/parser/inline/ok/format_args_named_arg_keyword.rast
@@ -23,9 +23,9 @@ SOURCE_FILE
COMMA ","
WHITESPACE " "
FORMAT_ARGS_ARG
- FORMAT_ARGS_ARG_NAME
- TYPE_KW "type"
- EQ "="
+ NAME
+ IDENT "type"
+ EQ "="
LITERAL
INT_NUMBER "1"
R_PAREN ")"
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 05df8b9b1488..7a24b32c87cf 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -488,10 +488,7 @@ FormatArgsExpr =
')'
FormatArgsArg =
- arg_name:FormatArgsArgName? Expr
-
-FormatArgsArgName =
- '=' // This also has a name, but it's any token and we can't put it here
+ (Name '=')? Expr
MacroExpr =
MacroCall
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index e0992ab9cfba..5fa56cf33c5d 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -723,16 +723,10 @@ impl ForType {
pub struct FormatArgsArg {
pub(crate) syntax: SyntaxNode,
}
+impl ast::HasName for FormatArgsArg {}
impl FormatArgsArg {
- #[inline]
- pub fn arg_name(&self) -> Option { support::child(&self.syntax) }
#[inline]
pub fn expr(&self) -> Option { support::child(&self.syntax) }
-}
-pub struct FormatArgsArgName {
- pub(crate) syntax: SyntaxNode,
-}
-impl FormatArgsArgName {
#[inline]
pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) }
}
@@ -4105,38 +4099,6 @@ impl fmt::Debug for FormatArgsArg {
f.debug_struct("FormatArgsArg").field("syntax", &self.syntax).finish()
}
}
-impl AstNode for FormatArgsArgName {
- #[inline]
- fn kind() -> SyntaxKind
- where
- Self: Sized,
- {
- FORMAT_ARGS_ARG_NAME
- }
- #[inline]
- fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_ARG_NAME }
- #[inline]
- fn cast(syntax: SyntaxNode) -> Option {
- if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
- }
- #[inline]
- fn syntax(&self) -> &SyntaxNode { &self.syntax }
-}
-impl hash::Hash for FormatArgsArgName {
- fn hash(&self, state: &mut H) { self.syntax.hash(state); }
-}
-impl Eq for FormatArgsArgName {}
-impl PartialEq for FormatArgsArgName {
- fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
-}
-impl Clone for FormatArgsArgName {
- fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
-}
-impl fmt::Debug for FormatArgsArgName {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("FormatArgsArgName").field("syntax", &self.syntax).finish()
- }
-}
impl AstNode for FormatArgsExpr {
#[inline]
fn kind() -> SyntaxKind
@@ -9810,6 +9772,7 @@ impl AstNode for AnyHasName {
| CONST_PARAM
| ENUM
| FN
+ | FORMAT_ARGS_ARG
| IDENT_PAT
| MACRO_DEF
| MACRO_RULES
@@ -9868,6 +9831,10 @@ impl From for AnyHasName {
#[inline]
fn from(node: Fn) -> AnyHasName { AnyHasName { syntax: node.syntax } }
}
+impl From for AnyHasName {
+ #[inline]
+ fn from(node: FormatArgsArg) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
impl From for AnyHasName {
#[inline]
fn from(node: IdentPat) -> AnyHasName { AnyHasName { syntax: node.syntax } }
@@ -10434,11 +10401,6 @@ impl std::fmt::Display for FormatArgsArg {
std::fmt::Display::fmt(self.syntax(), f)
}
}
-impl std::fmt::Display for FormatArgsArgName {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- std::fmt::Display::fmt(self.syntax(), f)
- }
-}
impl std::fmt::Display for FormatArgsExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 43bca6ed9e63..672e2fd233e4 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -1169,15 +1169,6 @@ impl From for ast::AnyHasAttrs {
}
}
-impl ast::FormatArgsArgName {
- /// This is not a [`ast::Name`], because the name may be a keyword.
- pub fn name(&self) -> SyntaxToken {
- let name = self.syntax.first_token().unwrap();
- assert!(name.kind().is_any_identifier());
- name
- }
-}
-
impl ast::OrPat {
pub fn leading_pipe(&self) -> Option {
self.syntax