about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2024-09-05 12:40:41 +0200
committerLukas Wirth <lukastw97@gmail.com>2024-09-05 12:40:48 +0200
commitf3b6965f90e764fc29ff627a6965ccfc68d38ead (patch)
tree25dc9463dc851470d0651309d8028c92475e75e4
parent304f54e023cf5784163a319b22316950c71c58f2 (diff)
downloadrust-f3b6965f90e764fc29ff627a6965ccfc68d38ead.tar.gz
rust-f3b6965f90e764fc29ff627a6965ccfc68d38ead.zip
Give InlineAsmOperand a HIR representation
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs27
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs148
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs69
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/defs.rs32
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/rename.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs29
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/moniker.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/navigation_target.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html32
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html8
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs16
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast52
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs109
23 files changed, 409 insertions, 217 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index f5fe8f87701..03507189fb9 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -100,7 +100,14 @@ pub struct BodySourceMap {
     field_map_back: FxHashMap<ExprId, FieldSource>,
     pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
 
-    format_args_template_map: FxHashMap<ExprId, Vec<(syntax::TextRange, Name)>>,
+    template_map: Option<
+        Box<(
+            // format_args!
+            FxHashMap<ExprId, Vec<(syntax::TextRange, Name)>>,
+            // asm!
+            FxHashMap<ExprId, Vec<(syntax::TextRange, usize)>>,
+        )>,
+    >,
 
     expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, MacroFileId>,
 
@@ -426,7 +433,16 @@ impl BodySourceMap {
         node: InFile<&ast::FormatArgsExpr>,
     ) -> Option<&[(syntax::TextRange, Name)]> {
         let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
-        self.format_args_template_map.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref)
+        self.template_map.as_ref()?.0.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref)
+    }
+
+    pub fn asm_template_args(
+        &self,
+        node: InFile<&ast::AsmExpr>,
+    ) -> Option<(ExprId, &[(syntax::TextRange, usize)])> {
+        let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
+        let expr = self.expr_map.get(&src)?;
+        Some(*expr).zip(self.template_map.as_ref()?.1.get(expr).map(std::ops::Deref::deref))
     }
 
     /// Get a reference to the body source map's diagnostics.
@@ -446,11 +462,14 @@ impl BodySourceMap {
             field_map_back,
             pat_field_map_back,
             expansions,
-            format_args_template_map,
+            template_map,
             diagnostics,
             binding_definitions,
         } = self;
-        format_args_template_map.shrink_to_fit();
+        if let Some(template_map) = template_map {
+            template_map.0.shrink_to_fit();
+            template_map.1.shrink_to_fit();
+        }
         expr_map.shrink_to_fit();
         expr_map_back.shrink_to_fit();
         pat_map.shrink_to_fit();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
index dfcb8b81204..9c547574ecb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
@@ -1847,7 +1847,7 @@ impl ExprCollector<'_> {
             },
             syntax_ptr,
         );
-        self.source_map.format_args_template_map.insert(idx, mappings);
+        self.source_map.template_map.get_or_insert_with(Default::default).0.insert(idx, mappings);
         idx
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs
index dafa3a859de..59b348b77db 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs
@@ -1,4 +1,3 @@
-use hir_expand::name::Name;
 use intern::Symbol;
 use rustc_hash::{FxHashMap, FxHashSet};
 use syntax::{
@@ -25,7 +24,7 @@ impl ExprCollector<'_> {
         let mut named_pos: FxHashMap<usize, Symbol> = Default::default();
         let mut named_args: FxHashMap<Symbol, usize> = Default::default();
         let mut reg_args: FxHashSet<usize> = Default::default();
-        for operand in asm.asm_operands() {
+        for piece in asm.asm_pieces() {
             let slot = operands.len();
             let mut lower_reg = |reg: Option<ast::AsmRegSpec>| {
                 let reg = reg?;
@@ -39,14 +38,14 @@ impl ExprCollector<'_> {
                 }
             };
 
-            let op = match operand {
-                ast::AsmOperand::AsmClobberAbi(clobber_abi) => {
+            let op = match piece {
+                ast::AsmPiece::AsmClobberAbi(clobber_abi) => {
                     if let Some(abi_name) = clobber_abi.string_token() {
                         clobber_abis.insert(Symbol::intern(abi_name.text()));
                     }
                     continue;
                 }
-                ast::AsmOperand::AsmOptions(opt) => {
+                ast::AsmPiece::AsmOptions(opt) => {
                     opt.asm_options().for_each(|opt| {
                         options |= match opt.syntax().first_token().map_or(T![$], |it| it.kind()) {
                             T![att_syntax] => AsmOptions::ATT_SYNTAX,
@@ -63,71 +62,82 @@ impl ExprCollector<'_> {
                     });
                     continue;
                 }
-                ast::AsmOperand::AsmRegOperand(op) => {
-                    let Some(dir_spec) = op.asm_dir_spec() else {
-                        continue;
-                    };
-                    let Some(reg) = lower_reg(op.asm_reg_spec()) else {
-                        continue;
-                    };
+                ast::AsmPiece::AsmOperandNamed(op) => {
                     if let Some(name) = op.name() {
                         let sym = Symbol::intern(&name.text());
                         named_args.insert(sym.clone(), slot);
                         named_pos.insert(slot, sym);
                     }
-                    if dir_spec.in_token().is_some() {
-                        let expr = self
-                            .collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr()));
-                        AsmOperand::In { reg, expr }
-                    } else if dir_spec.out_token().is_some() {
-                        let expr = self
-                            .collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr()));
-                        AsmOperand::Out { reg, expr: Some(expr), late: false }
-                    } else if dir_spec.lateout_token().is_some() {
-                        let expr = self
-                            .collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr()));
-                        AsmOperand::Out { reg, expr: Some(expr), late: true }
-                    } else if dir_spec.inout_token().is_some() {
-                        let Some(op_expr) = op.asm_operand_expr() else { continue };
-                        let in_expr = self.collect_expr_opt(op_expr.in_expr());
-                        let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it));
-                        match out_expr {
-                            Some(out_expr) => AsmOperand::SplitInOut {
-                                reg,
-                                in_expr,
-                                out_expr: Some(out_expr),
-                                late: false,
-                            },
-                            None => AsmOperand::InOut { reg, expr: in_expr, late: false },
+                    let Some(op) = op.asm_operand() else { continue };
+                    match op {
+                        ast::AsmOperand::AsmRegOperand(op) => {
+                            let Some(dir_spec) = op.asm_dir_spec() else {
+                                continue;
+                            };
+                            let Some(reg) = lower_reg(op.asm_reg_spec()) else {
+                                continue;
+                            };
+                            if dir_spec.in_token().is_some() {
+                                let expr = self.collect_expr_opt(
+                                    op.asm_operand_expr().and_then(|it| it.in_expr()),
+                                );
+                                AsmOperand::In { reg, expr }
+                            } else if dir_spec.out_token().is_some() {
+                                let expr = self.collect_expr_opt(
+                                    op.asm_operand_expr().and_then(|it| it.in_expr()),
+                                );
+                                AsmOperand::Out { reg, expr: Some(expr), late: false }
+                            } else if dir_spec.lateout_token().is_some() {
+                                let expr = self.collect_expr_opt(
+                                    op.asm_operand_expr().and_then(|it| it.in_expr()),
+                                );
+                                AsmOperand::Out { reg, expr: Some(expr), late: true }
+                            } else if dir_spec.inout_token().is_some() {
+                                let Some(op_expr) = op.asm_operand_expr() else { continue };
+                                let in_expr = self.collect_expr_opt(op_expr.in_expr());
+                                let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it));
+                                match out_expr {
+                                    Some(out_expr) => AsmOperand::SplitInOut {
+                                        reg,
+                                        in_expr,
+                                        out_expr: Some(out_expr),
+                                        late: false,
+                                    },
+                                    None => AsmOperand::InOut { reg, expr: in_expr, late: false },
+                                }
+                            } else if dir_spec.inlateout_token().is_some() {
+                                let Some(op_expr) = op.asm_operand_expr() else { continue };
+                                let in_expr = self.collect_expr_opt(op_expr.in_expr());
+                                let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it));
+                                match out_expr {
+                                    Some(out_expr) => AsmOperand::SplitInOut {
+                                        reg,
+                                        in_expr,
+                                        out_expr: Some(out_expr),
+                                        late: false,
+                                    },
+                                    None => AsmOperand::InOut { reg, expr: in_expr, late: false },
+                                }
+                            } else {
+                                continue;
+                            }
                         }
-                    } else if dir_spec.inlateout_token().is_some() {
-                        let Some(op_expr) = op.asm_operand_expr() else { continue };
-                        let in_expr = self.collect_expr_opt(op_expr.in_expr());
-                        let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it));
-                        match out_expr {
-                            Some(out_expr) => AsmOperand::SplitInOut {
-                                reg,
-                                in_expr,
-                                out_expr: Some(out_expr),
-                                late: false,
-                            },
-                            None => AsmOperand::InOut { reg, expr: in_expr, late: false },
+                        ast::AsmOperand::AsmLabel(l) => {
+                            AsmOperand::Label(self.collect_block_opt(l.block_expr()))
+                        }
+                        ast::AsmOperand::AsmConst(c) => {
+                            AsmOperand::Const(self.collect_expr_opt(c.expr()))
+                        }
+                        ast::AsmOperand::AsmSym(s) => {
+                            let Some(path) =
+                                s.path().and_then(|p| self.expander.parse_path(self.db, p))
+                            else {
+                                continue;
+                            };
+                            AsmOperand::Sym(path)
                         }
-                    } else {
-                        continue;
                     }
                 }
-                ast::AsmOperand::AsmLabel(l) => {
-                    AsmOperand::Label(self.collect_block_opt(l.block_expr()))
-                }
-                ast::AsmOperand::AsmConst(c) => AsmOperand::Const(self.collect_expr_opt(c.expr())),
-                ast::AsmOperand::AsmSym(s) => {
-                    let Some(path) = s.path().and_then(|p| self.expander.parse_path(self.db, p))
-                    else {
-                        continue;
-                    };
-                    AsmOperand::Sym(path)
-                }
             };
             operands.push(op);
         }
@@ -192,7 +202,7 @@ impl ExprCollector<'_> {
                             rustc_parse_format::Piece::NextArgument(arg) => {
                                 // let span = arg_spans.next();
 
-                                let _operand_idx = match arg.position {
+                                let operand_idx = match arg.position {
                                     rustc_parse_format::ArgumentIs(idx)
                                     | rustc_parse_format::ArgumentImplicitlyIs(idx) => {
                                         if idx >= operands.len()
@@ -206,15 +216,15 @@ impl ExprCollector<'_> {
                                     }
                                     rustc_parse_format::ArgumentNamed(name) => {
                                         let name = Symbol::intern(name);
-                                        if let Some(position_span) = to_span(arg.position_span) {
-                                            mappings.push((
-                                                position_span,
-                                                Name::new_symbol_root(name.clone()),
-                                            ));
-                                        }
                                         named_args.get(&name).copied()
                                     }
                                 };
+
+                                if let Some(operand_idx) = operand_idx {
+                                    if let Some(position_span) = to_span(arg.position_span) {
+                                        mappings.push((position_span, operand_idx));
+                                    }
+                                }
                             }
                         }
                     }
@@ -224,7 +234,7 @@ impl ExprCollector<'_> {
             Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }),
             syntax_ptr,
         );
-        self.source_map.format_args_template_map.insert(idx, mappings);
+        self.source_map.template_map.get_or_insert_with(Default::default).1.insert(idx, mappings);
         idx
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 81d6466c2f3..c8b227123f1 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -43,7 +43,7 @@ use hir_def::{
     body::{BodyDiagnostic, SyntheticSyntax},
     data::adt::VariantData,
     generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
-    hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
+    hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat},
     item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode},
     lang_item::LangItemTarget,
     layout::{self, ReprOptions, TargetDataLayout},
@@ -5246,6 +5246,13 @@ impl Type {
     }
 }
 
+#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
+pub struct InlineAsmOperand {
+    owner: DefWithBodyId,
+    expr: ExprId,
+    index: usize,
+}
+
 // FIXME: Document this
 #[derive(Debug)]
 pub struct Callable {
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 4e6887295c8..0ba0e446578 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -47,9 +47,9 @@ use crate::{
     source_analyzer::{resolve_hir_path, SourceAnalyzer},
     Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
     ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile,
-    ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, OverloadedDeref, Path,
-    ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias,
-    TypeParam, Union, Variant, VariantDef,
+    InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name,
+    OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField,
+    Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
 };
 
 const CONTINUE_NO_BREAKS: ControlFlow<Infallible, ()> = ControlFlow::Continue(());
@@ -546,11 +546,11 @@ impl<'db> SemanticsImpl<'db> {
         )
     }
 
-    /// Retrieves all the formatting parts of the format_args! template string.
+    /// Retrieves all the formatting parts of the format_args! (or `asm!`) template string.
     pub fn as_format_args_parts(
         &self,
         string: &ast::String,
-    ) -> Option<Vec<(TextRange, Option<PathResolution>)>> {
+    ) -> Option<Vec<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)>> {
         let quote = string.open_quote_text_range()?;
 
         let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?;
@@ -560,14 +560,31 @@ impl<'db> SemanticsImpl<'db> {
                 let string = ast::String::cast(token)?;
                 let literal =
                     string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?;
-                let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?;
-                let source_analyzer = self.analyze_no_infer(format_args.syntax())?;
-                let format_args = self.wrap_node_infile(format_args);
-                let res = source_analyzer
-                    .as_format_args_parts(self.db, format_args.as_ref())?
-                    .map(|(range, res)| (range + quote.end(), res))
-                    .collect();
-                Some(res)
+                let parent = literal.parent()?;
+                if let Some(format_args) = ast::FormatArgsExpr::cast(parent.clone()) {
+                    let source_analyzer = self.analyze_no_infer(format_args.syntax())?;
+                    let format_args = self.wrap_node_infile(format_args);
+                    let res = source_analyzer
+                        .as_format_args_parts(self.db, format_args.as_ref())?
+                        .map(|(range, res)| (range + quote.end(), res.map(Either::Left)))
+                        .collect();
+                    Some(res)
+                } else {
+                    let asm = ast::AsmExpr::cast(parent)?;
+                    let source_analyzer = self.analyze_no_infer(asm.syntax())?;
+                    let asm = self.wrap_node_infile(asm);
+                    let (owner, (expr, asm_parts)) = source_analyzer.as_asm_parts(asm.as_ref())?;
+                    let res = asm_parts
+                        .iter()
+                        .map(|&(range, index)| {
+                            (
+                                range + quote.end(),
+                                Some(Either::Right(InlineAsmOperand { owner, expr, index })),
+                            )
+                        })
+                        .collect();
+                    Some(res)
+                }
             })()
             .map_or(ControlFlow::Continue(()), ControlFlow::Break)
         })
@@ -578,7 +595,7 @@ impl<'db> SemanticsImpl<'db> {
         &self,
         original_token: SyntaxToken,
         offset: TextSize,
-    ) -> Option<(TextRange, Option<PathResolution>)> {
+    ) -> Option<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)> {
         let original_string = ast::String::cast(original_token.clone())?;
         let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?;
         let quote = original_string.open_quote_text_range()?;
@@ -599,13 +616,26 @@ impl<'db> SemanticsImpl<'db> {
         &self,
         string: ast::String,
         offset: TextSize,
-    ) -> Option<(TextRange, Option<PathResolution>)> {
+    ) -> Option<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)> {
         debug_assert!(offset <= string.syntax().text_range().len());
         let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?;
-        let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?;
-        let source_analyzer = &self.analyze_no_infer(format_args.syntax())?;
-        let format_args = self.wrap_node_infile(format_args);
-        source_analyzer.resolve_offset_in_format_args(self.db, format_args.as_ref(), offset)
+        let parent = literal.parent()?;
+        if let Some(format_args) = ast::FormatArgsExpr::cast(parent.clone()) {
+            let source_analyzer = &self.analyze_no_infer(format_args.syntax())?;
+            let format_args = self.wrap_node_infile(format_args);
+            source_analyzer
+                .resolve_offset_in_format_args(self.db, format_args.as_ref(), offset)
+                .map(|(range, res)| (range, res.map(Either::Left)))
+        } else {
+            let asm = ast::AsmExpr::cast(parent)?;
+            let source_analyzer = &self.analyze_no_infer(asm.syntax())?;
+            let asm = self.wrap_node_infile(asm);
+            source_analyzer.resolve_offset_in_asm_template(asm.as_ref(), offset).map(
+                |(owner, (expr, range, index))| {
+                    (range, Some(Either::Right(InlineAsmOperand { owner, expr, index })))
+                },
+            )
+        }
     }
 
     /// Maps a node down by mapping its first and last token down.
@@ -1781,6 +1811,7 @@ to_def_impls![
     (crate::Label, ast::Label, label_to_def),
     (crate::Adt, ast::Adt, adt_to_def),
     (crate::ExternCrateDecl, ast::ExternCrate, extern_crate_to_def),
+    (crate::InlineAsmOperand, ast::AsmOperandNamed, asm_operand_to_def),
     (MacroCallId, ast::MacroCall, macro_call_to_macro_call),
 ];
 
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
index edeb19030ac..fc629b9c6d0 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
@@ -110,7 +110,7 @@ use syntax::{
     AstNode, AstPtr, SyntaxNode,
 };
 
-use crate::{db::HirDatabase, InFile};
+use crate::{db::HirDatabase, InFile, InlineAsmOperand};
 
 #[derive(Default)]
 pub(super) struct SourceToDefCache {
@@ -273,6 +273,25 @@ impl SourceToDefCtx<'_, '_> {
             ast::Adt::Union(it) => self.union_to_def(InFile::new(file_id, it)).map(AdtId::UnionId),
         }
     }
+
+    pub(super) fn asm_operand_to_def(
+        &mut self,
+        src: InFile<&ast::AsmOperandNamed>,
+    ) -> Option<InlineAsmOperand> {
+        let asm = src.value.syntax().parent().and_then(ast::AsmExpr::cast)?;
+        let index = asm
+            .asm_pieces()
+            .filter_map(|it| match it {
+                ast::AsmPiece::AsmOperandNamed(it) => Some(it),
+                _ => None,
+            })
+            .position(|it| it == *src.value)?;
+        let container = self.find_pat_or_label_container(src.syntax_ref())?;
+        let (_, source_map) = self.db.body_with_source_map(container);
+        let expr = source_map.node_expr(src.with_value(&ast::Expr::AsmExpr(asm)))?;
+        Some(InlineAsmOperand { owner: container, expr, index })
+    }
+
     pub(super) fn bind_pat_to_def(
         &mut self,
         src: InFile<&ast::IdentPat>,
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index be0116862b9..f6f1da1b7d6 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -904,6 +904,20 @@ impl SourceAnalyzer {
         })
     }
 
+    pub(crate) fn resolve_offset_in_asm_template(
+        &self,
+        asm: InFile<&ast::AsmExpr>,
+        offset: TextSize,
+    ) -> Option<(DefWithBodyId, (ExprId, TextRange, usize))> {
+        let (def, _, body_source_map) = self.def.as_ref()?;
+        let (expr, args) = body_source_map.asm_template_args(asm)?;
+        Some(*def).zip(
+            args.iter()
+                .find(|(range, _)| range.contains_inclusive(offset))
+                .map(|(range, idx)| (expr, *range, *idx)),
+        )
+    }
+
     pub(crate) fn as_format_args_parts<'a>(
         &'a self,
         db: &'a dyn HirDatabase,
@@ -927,6 +941,14 @@ impl SourceAnalyzer {
         ))
     }
 
+    pub(crate) fn as_asm_parts(
+        &self,
+        asm: InFile<&ast::AsmExpr>,
+    ) -> Option<(DefWithBodyId, (ExprId, &[(TextRange, usize)]))> {
+        let (def, _, body_source_map) = self.def.as_ref()?;
+        Some(*def).zip(body_source_map.asm_template_args(asm))
+    }
+
     fn resolve_impl_method_or_trait_def(
         &self,
         db: &dyn HirDatabase,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
index 469f83644fc..c134c9f6725 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -10,8 +10,8 @@ use either::Either;
 use hir::{
     Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
     Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
-    Function, GenericParam, HasVisibility, HirDisplay, Impl, Label, Local, Macro, Module,
-    ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait,
+    Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro,
+    Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait,
     TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
 };
 use span::Edition;
@@ -51,7 +51,7 @@ pub enum Definition {
     ToolModule(ToolModule),
     ExternCrateDecl(ExternCrateDecl),
     InlineAsmRegOrRegClass(()),
-    InlineAsmRegOperand(()),
+    InlineAsmOperand(InlineAsmOperand),
 }
 
 impl Definition {
@@ -91,7 +91,7 @@ impl Definition {
             | Definition::TupleField(_)
             | Definition::ToolModule(_)
             | Definition::InlineAsmRegOrRegClass(_)
-            | Definition::InlineAsmRegOperand(_) => return None,
+            | Definition::InlineAsmOperand(_) => return None,
         };
         Some(module)
     }
@@ -127,7 +127,7 @@ impl Definition {
             | Definition::Label(_)
             | Definition::DeriveHelper(_)
             | Definition::InlineAsmRegOrRegClass(_)
-            | Definition::InlineAsmRegOperand(_) => return None,
+            | Definition::InlineAsmOperand(_) => return None,
         };
         Some(vis)
     }
@@ -156,9 +156,7 @@ impl Definition {
             Definition::ToolModule(_) => return None,  // FIXME
             Definition::DeriveHelper(it) => it.name(db),
             Definition::ExternCrateDecl(it) => return it.alias_or_name(db),
-            Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmRegOperand(_) => {
-                return None
-            } //  FIXME
+            Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmOperand(_) => return None, //  FIXME
         };
         Some(name)
     }
@@ -221,7 +219,7 @@ impl Definition {
             Definition::ToolModule(_) => None,
             Definition::DeriveHelper(_) => None,
             Definition::TupleField(_) => None,
-            Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmRegOperand(_) => None,
+            Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmOperand(_) => None,
         };
 
         docs.or_else(|| {
@@ -280,7 +278,7 @@ impl Definition {
             }
             // FIXME
             Definition::InlineAsmRegOrRegClass(_) => "inline_asm_reg_or_reg_class".to_owned(),
-            Definition::InlineAsmRegOperand(_) => "inline_asm_reg_operand".to_owned(),
+            Definition::InlineAsmOperand(_) => "inline_asm_reg_operand".to_owned(),
         }
     }
 }
@@ -442,7 +440,6 @@ impl NameClass {
         let _p = tracing::info_span!("NameClass::classify").entered();
 
         let parent = name.syntax().parent()?;
-
         let definition = match_ast! {
             match parent {
                 ast::Item(it) => classify_item(sema, it)?,
@@ -453,6 +450,7 @@ impl NameClass {
                 ast::Variant(it) => Definition::Variant(sema.to_def(&it)?),
                 ast::TypeParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()),
                 ast::ConstParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()),
+                ast::AsmOperandNamed(it) => Definition::InlineAsmOperand(sema.to_def(&it)?),
                 _ => return None,
             }
         };
@@ -769,6 +767,18 @@ impl From<Impl> for Definition {
     }
 }
 
+impl From<InlineAsmOperand> for Definition {
+    fn from(value: InlineAsmOperand) -> Self {
+        Definition::InlineAsmOperand(value)
+    }
+}
+
+impl From<Either<PathResolution, InlineAsmOperand>> for Definition {
+    fn from(value: Either<PathResolution, InlineAsmOperand>) -> Self {
+        value.either(Definition::from, Definition::from)
+    }
+}
+
 impl AsAssocItem for Definition {
     fn as_assoc_item(self, db: &dyn hir::db::HirDatabase) -> Option<AssocItem> {
         match self {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
index e8299a4a463..0435b2f0c6f 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
@@ -208,7 +208,7 @@ impl Definition {
             | Definition::TupleField(_)
             | Definition::InlineAsmRegOrRegClass(_) => return None,
             // FIXME:
-            Definition::InlineAsmRegOperand(_) => return None,
+            Definition::InlineAsmOperand(_) => return None,
             // FIXME: This should be doable in theory
             Definition::DeriveHelper(_) => return None,
         };
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 12ce5a403fe..4d0668ffea8 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -8,10 +8,11 @@ use std::mem;
 use std::{cell::LazyCell, cmp::Reverse};
 
 use base_db::{salsa::Database, SourceDatabase, SourceRootDatabase};
+use either::Either;
 use hir::{
     sym, Adt, AsAssocItem, DefWithBody, FileRange, FileRangeWrapper, HasAttrs, HasContainer,
-    HasSource, HirFileIdExt, InFile, InFileWrapper, InRealFile, ItemContainer, ModuleSource,
-    PathResolution, Semantics, Visibility,
+    HasSource, HirFileIdExt, InFile, InFileWrapper, InRealFile, InlineAsmOperand, ItemContainer,
+    ModuleSource, PathResolution, Semantics, Visibility,
 };
 use memchr::memmem::Finder;
 use parser::SyntaxKind;
@@ -917,7 +918,7 @@ impl<'a> FindUsages<'a> {
             for offset in Self::match_indices(&text, finder, search_range) {
                 tree.token_at_offset(offset).for_each(|token| {
                     let Some(str_token) = ast::String::cast(token.clone()) else { return };
-                    if let Some((range, nameres)) =
+                    if let Some((range, Some(nameres))) =
                         sema.check_for_format_args_template(token, offset)
                     {
                         if self.found_format_args_ref(file_id, range, str_token, nameres, sink) {}
@@ -1087,19 +1088,19 @@ impl<'a> FindUsages<'a> {
         file_id: EditionedFileId,
         range: TextRange,
         token: ast::String,
-        res: Option<PathResolution>,
+        res: Either<PathResolution, InlineAsmOperand>,
         sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
     ) -> bool {
-        match res.map(Definition::from) {
-            Some(def) if def == self.def => {
-                let reference = FileReference {
-                    range,
-                    name: FileReferenceNode::FormatStringEntry(token, range),
-                    category: ReferenceCategory::READ,
-                };
-                sink(file_id, reference)
-            }
-            _ => false,
+        let def = res.either(Definition::from, Definition::from);
+        if def == self.def {
+            let reference = FileReference {
+                range,
+                name: FileReferenceNode::FormatStringEntry(token, range),
+                category: ReferenceCategory::READ,
+            };
+            sink(file_id, reference)
+        } else {
+            false
         }
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index 184486ec0a1..ea16a11d56d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -221,7 +221,7 @@ pub(crate) fn resolve_doc_path_for_def(
         | Definition::Label(_)
         | Definition::DeriveHelper(_)
         | Definition::InlineAsmRegOrRegClass(_)
-        | Definition::InlineAsmRegOperand(_) => None,
+        | Definition::InlineAsmOperand(_) => None,
     }
     .map(Definition::from)
 }
@@ -676,7 +676,7 @@ fn filename_and_frag_for_def(
         | Definition::ToolModule(_)
         | Definition::DeriveHelper(_)
         | Definition::InlineAsmRegOrRegClass(_)
-        | Definition::InlineAsmRegOperand(_) => return None,
+        | Definition::InlineAsmOperand(_) => return None,
     };
 
     Some((def, res, None))
diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
index 1f996f716d0..14781b21296 100644
--- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
@@ -223,7 +223,7 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati
                 Variable
             }
         }
-        Definition::Label(..) | Definition::InlineAsmRegOperand(_) => Variable, // For lack of a better variant
+        Definition::Label(..) | Definition::InlineAsmOperand(_) => Variable, // For lack of a better variant
         Definition::DeriveHelper(..) => Attribute,
         Definition::BuiltinAttr(..) => Attribute,
         Definition::ToolModule(..) => Module,
@@ -323,7 +323,7 @@ pub(crate) fn def_to_moniker(
         | Definition::BuiltinAttr(_)
         | Definition::ToolModule(_)
         | Definition::InlineAsmRegOrRegClass(_)
-        | Definition::InlineAsmRegOperand(_) => return None,
+        | Definition::InlineAsmOperand(_) => return None,
 
         Definition::Local(local) => {
             if !local.is_param(db) {
diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
index eb28b78e60c..68039ef309d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
@@ -245,7 +245,7 @@ impl TryToNav for Definition {
             | Definition::InlineAsmRegOrRegClass(_)
             | Definition::BuiltinAttr(_) => None,
             // FIXME
-            Definition::InlineAsmRegOperand(_) => None,
+            Definition::InlineAsmOperand(_) => None,
             // FIXME: The focus range should be set to the helper declaration
             Definition::DeriveHelper(it) => it.derive().try_to_nav(db),
         }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs
index 518e7145479..7234108701a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs
@@ -19,21 +19,21 @@ pub(super) fn highlight_format_string(
     expanded_string: &ast::String,
     range: TextRange,
 ) {
-    if !is_format_string(expanded_string) {
+    if is_format_string(expanded_string) {
+        // 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 {
+                    range: piece_range + range.start(),
+                    highlight: highlight.into(),
+                    binding_hash: None,
+                });
+            }
+        });
+
         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 {
-                range: piece_range + range.start(),
-                highlight: highlight.into(),
-                binding_hash: None,
-            });
-        }
-    });
-
     if let Some(parts) = sema.as_format_args_parts(string) {
         parts.into_iter().for_each(|(range, res)| {
             if let Some(res) = res {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index fe1d0b452ae..96375937a12 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -537,7 +537,7 @@ pub(super) fn highlight_def(
         Definition::InlineAsmRegOrRegClass(_) => {
             Highlight::new(HlTag::Symbol(SymbolKind::InlineAsmRegOrRegClass))
         }
-        Definition::InlineAsmRegOperand(_) => Highlight::new(HlTag::Symbol(SymbolKind::Local)),
+        Definition::InlineAsmOperand(_) => Highlight::new(HlTag::Symbol(SymbolKind::Local)),
     };
 
     let def_crate = def.krate(db);
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
index bff52943afd..5583f1bc8df 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
@@ -316,7 +316,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag {
         Definition::ToolModule(_) => SymbolKind::ToolModule,
         Definition::DeriveHelper(_) => SymbolKind::DeriveHelper,
         Definition::InlineAsmRegOrRegClass(_) => SymbolKind::InlineAsmRegOrRegClass,
-        Definition::InlineAsmRegOperand(_) => SymbolKind::Local,
+        Definition::InlineAsmOperand(_) => SymbolKind::Local,
     };
     HlTag::Symbol(symbol)
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html
index 9cedf012f7f..d322a374b34 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html
@@ -50,17 +50,17 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <span class="keyword">let</span> <span class="variable declaration">foo</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="semicolon">;</span>
         <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">o</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
         <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
-            <span class="string_literal macro">"%input = OpLoad _ {0}"</span><span class="comma macro">,</span>
+            <span class="string_literal macro">"%input = </span><span class="variable library">O</span><span class="string_literal macro">pLoad _ {</span><span class="variable library">0</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
             <span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"%result = "</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="string_literal macro">" _ %input"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span>
-            <span class="string_literal macro">"OpStore {1} %result"</span><span class="comma macro">,</span>
+            <span class="string_literal macro">"OpStore {</span><span class="variable library">1</span><span class="string_literal macro">} %result</span><span class="variable library">"</span><span class="comma macro">,</span>
             <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="operator macro">&</span><span class="variable macro">foo</span><span class="comma macro">,</span>
             <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="operator macro">&</span><span class="keyword macro">mut</span> <span class="variable macro mutable">o</span><span class="comma macro">,</span>
         <span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
         <span class="keyword">let</span> <span class="variable declaration">thread_id</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
         <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"
-            mov {0}, gs:[0x30]
-            mov {0}, [{0}+0x48]
+            mov {</span><span class="variable library">0</span><span class="string_literal macro">}, gs:[0x30]
+            mov {</span><span class="variable library">0</span><span class="string_literal macro">}, [{</span><span class="variable library">0</span><span class="string_literal macro">}+0x48]
         "</span><span class="comma macro">,</span> <span class="keyword macro">out</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">thread_id</span><span class="comma macro">,</span> <span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">pure</span><span class="comma macro">,</span> <span class="keyword macro">readonly</span><span class="comma macro">,</span> <span class="keyword macro">nostack</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
         <span class="keyword">static</span> <span class="static declaration">UNMAP_BASE</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
@@ -69,28 +69,28 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <span class="keyword const">const</span> <span class="constant const declaration">OffPtr</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
         <span class="keyword const">const</span> <span class="constant const declaration">OffFn</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
         <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"
-            push {free_type}
-            push {free_size}
-            push {base}
+            push {</span><span class="variable library">free_type</span><span class="string_literal macro">}
+            push {</span><span class="variable library">free_size</span><span class="string_literal macro">}
+            push {</span><span class="variable library">base</span><span class="string_literal macro">}
 
             mov eax, fs:[30h]
             mov eax, [eax+8h]
-            add eax, {off_fn}
-            mov [eax-{off_fn}+{off_ptr}], eax
+            add eax, {</span><span class="variable library">off_fn</span><span class="string_literal macro">}
+            mov [eax-{</span><span class="variable library">off_fn</span><span class="string_literal macro">}+{</span><span class="variable library">off_ptr</span><span class="string_literal macro">}], eax
 
             push eax
 
-            jmp {virtual_free}
+            jmp {</span><span class="variable library">virtual_free</span><span class="string_literal macro">}
             "</span><span class="comma macro">,</span>
-            <span class="unresolved_reference declaration macro">off_ptr</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">OffPtr</span><span class="comma macro">,</span>
-            <span class="unresolved_reference declaration macro">off_fn</span>  <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">OffFn</span><span class="comma macro">,</span>
+            <span class="variable declaration library macro">off_ptr</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">OffPtr</span><span class="comma macro">,</span>
+            <span class="variable declaration library macro">off_fn</span>  <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">OffFn</span><span class="comma macro">,</span>
 
-            <span class="unresolved_reference declaration macro">free_size</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="numeric_literal macro">0</span><span class="comma macro">,</span>
-            <span class="unresolved_reference declaration macro">free_type</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">MEM_RELEASE</span><span class="comma macro">,</span>
+            <span class="variable declaration library macro">free_size</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="numeric_literal macro">0</span><span class="comma macro">,</span>
+            <span class="variable declaration library macro">free_type</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">MEM_RELEASE</span><span class="comma macro">,</span>
 
-            <span class="unresolved_reference declaration macro">virtual_free</span> <span class="operator macro">=</span> <span class="keyword macro">sym</span> <span class="static macro">VirtualFree</span><span class="comma macro">,</span>
+            <span class="variable declaration library macro">virtual_free</span> <span class="operator macro">=</span> <span class="keyword macro">sym</span> <span class="static macro">VirtualFree</span><span class="comma macro">,</span>
 
-            <span class="unresolved_reference declaration macro">base</span> <span class="operator macro">=</span> <span class="keyword macro">sym</span> <span class="static macro">UNMAP_BASE</span><span class="comma macro">,</span>
+            <span class="variable declaration library macro">base</span> <span class="operator macro">=</span> <span class="keyword macro">sym</span> <span class="static macro">UNMAP_BASE</span><span class="comma macro">,</span>
             <span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">noreturn</span><span class="parenthesis macro">)</span><span class="comma macro">,</span>
         <span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="brace">}</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index b387808b297..37bbc1e1ee2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -166,8 +166,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <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="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis 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="string_literal macro">"mov {</span><span class="variable library">0</span><span class="string_literal macro">}, {</span><span class="variable library">1</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
+        <span class="string_literal macro">"add {</span><span class="variable library">0</span><span class="string_literal macro">}, 5</span><span class="variable library">"</span><span class="comma macro">,</span>
         <span class="keyword macro">out</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">o</span><span class="comma macro">,</span>
         <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">i</span><span class="comma macro">,</span>
     <span class="parenthesis macro">)</span><span class="semicolon">;</span>
@@ -175,6 +175,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword const">const</span> <span class="constant const declaration">CONSTANT</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="colon">:</span>
     <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">m</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</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 default_library library 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="format_specifier">{</span><span class="variable reference">backslash</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="constant const">CONSTANT</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable mutable">m</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="macro">reuse_twice</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 reference">backslash</span><span class="format_specifier">}</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="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">CONSTANT</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">m</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="macro">reuse_twice</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">backslash</span><span class="format_specifier">}</span><span class="string_literal macro">"</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/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
index d6ccf38e743..39ca26fc508 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
@@ -329,11 +329,11 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
             break;
         }
 
-        let op = p.start();
+        let op_n = p.start();
         // Parse clobber_abi
         if p.eat_contextual_kw(T![clobber_abi]) {
             parse_clobber_abi(p);
-            op.complete(p, ASM_CLOBBER_ABI);
+            op_n.complete(p, ASM_CLOBBER_ABI);
             allow_templates = false;
             continue;
         }
@@ -341,7 +341,7 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
         // Parse options
         if p.eat_contextual_kw(T![options]) {
             parse_options(p);
-            op.complete(p, ASM_OPTIONS);
+            op_n.complete(p, ASM_OPTIONS);
             allow_templates = false;
             continue;
         }
@@ -356,12 +356,14 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
             false
         };
 
+        let op = p.start();
         let dir_spec = p.start();
         if p.eat(T![in]) || p.eat_contextual_kw(T![out]) || p.eat_contextual_kw(T![lateout]) {
             dir_spec.complete(p, ASM_DIR_SPEC);
             parse_reg(p);
             expr(p);
             op.complete(p, ASM_REG_OPERAND);
+            op_n.complete(p, ASM_OPERAND_NAMED);
         } else if p.eat_contextual_kw(T![inout]) || p.eat_contextual_kw(T![inlateout]) {
             dir_spec.complete(p, ASM_DIR_SPEC);
             parse_reg(p);
@@ -370,21 +372,26 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
                 expr(p);
             }
             op.complete(p, ASM_REG_OPERAND);
+            op_n.complete(p, ASM_OPERAND_NAMED);
         } else if p.eat_contextual_kw(T![label]) {
             dir_spec.abandon(p);
             block_expr(p);
-            op.complete(p, ASM_LABEL);
+            op.complete(p, ASM_OPERAND_NAMED);
+            op_n.complete(p, ASM_LABEL);
         } else if p.eat(T![const]) {
             dir_spec.abandon(p);
             expr(p);
             op.complete(p, ASM_CONST);
+            op_n.complete(p, ASM_OPERAND_NAMED);
         } else if p.eat_contextual_kw(T![sym]) {
             dir_spec.abandon(p);
             paths::type_path(p);
             op.complete(p, ASM_SYM);
+            op_n.complete(p, ASM_OPERAND_NAMED);
         } else if allow_templates {
             dir_spec.abandon(p);
             op.abandon(p);
+            op_n.abandon(p);
             if expr(p).is_none() {
                 p.err_and_bump("expected asm template");
             }
@@ -392,6 +399,7 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
         } else {
             dir_spec.abandon(p);
             op.abandon(p);
+            op_n.abandon(p);
             p.err_and_bump("expected asm operand");
             if p.at(T!['}']) {
                 break;
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index ee3adac158f..288a07ef44d 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -169,8 +169,10 @@ pub enum SyntaxKind {
     ASM_LABEL,
     ASM_OPERAND,
     ASM_OPERAND_EXPR,
+    ASM_OPERAND_NAMED,
     ASM_OPTION,
     ASM_OPTIONS,
+    ASM_PIECE,
     ASM_REG_OPERAND,
     ASM_REG_SPEC,
     ASM_SYM,
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast
index 2360135db7c..4afa9daf590 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast
@@ -35,43 +35,45 @@ SOURCE_FILE
               STRING "\"add {x}, {tmp}\""
             COMMA ","
             WHITESPACE "\n        "
-            ASM_REG_OPERAND
+            ASM_OPERAND_NAMED
               NAME
                 IDENT "x"
               WHITESPACE " "
               EQ "="
               WHITESPACE " "
-              ASM_DIR_SPEC
-                INOUT_KW "inout"
-              L_PAREN "("
-              ASM_REG_SPEC
-                NAME_REF
-                  IDENT "reg"
-              R_PAREN ")"
-              WHITESPACE " "
-              PATH_EXPR
-                PATH
-                  PATH_SEGMENT
-                    NAME_REF
-                      IDENT "x"
+              ASM_REG_OPERAND
+                ASM_DIR_SPEC
+                  INOUT_KW "inout"
+                L_PAREN "("
+                ASM_REG_SPEC
+                  NAME_REF
+                    IDENT "reg"
+                R_PAREN ")"
+                WHITESPACE " "
+                PATH_EXPR
+                  PATH
+                    PATH_SEGMENT
+                      NAME_REF
+                        IDENT "x"
             COMMA ","
             WHITESPACE "\n        "
-            ASM_REG_OPERAND
+            ASM_OPERAND_NAMED
               NAME
                 IDENT "tmp"
               WHITESPACE " "
               EQ "="
               WHITESPACE " "
-              ASM_DIR_SPEC
-                OUT_KW "out"
-              L_PAREN "("
-              ASM_REG_SPEC
-                NAME_REF
-                  IDENT "reg"
-              R_PAREN ")"
-              WHITESPACE " "
-              UNDERSCORE_EXPR
-                UNDERSCORE "_"
+              ASM_REG_OPERAND
+                ASM_DIR_SPEC
+                  OUT_KW "out"
+                L_PAREN "("
+                ASM_REG_SPEC
+                  NAME_REF
+                    IDENT "reg"
+                R_PAREN ")"
+                WHITESPACE " "
+                UNDERSCORE_EXPR
+                  UNDERSCORE "_"
             COMMA ","
             WHITESPACE "\n    "
             R_PAREN ")"
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 5dace66c32d..52ad439e4de 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -395,7 +395,7 @@ OffsetOfExpr =
 // global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")"
 // format_string := STRING_LITERAL / RAW_STRING_LITERAL
 AsmExpr =
-  Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmOperand (',' AsmOperand)*)? ','? ')'
+  Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')'
 
 // operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
 AsmOperandExpr = in_expr:Expr ('=>' out_expr:Expr)?
@@ -404,7 +404,7 @@ AsmDirSpec = 'in' | 'out' | 'lateout' | 'inout' | 'inlateout'
 // reg_spec := <register class> / "\"" <explicit register> "\""
 AsmRegSpec = '@string' | NameRef
 // reg_operand := [ident "="] dir_spec "(" reg_spec ")" operand_expr
-AsmRegOperand = (Name '=')? AsmDirSpec '(' AsmRegSpec ')' AsmOperandExpr
+AsmRegOperand = AsmDirSpec '(' AsmRegSpec ')' AsmOperandExpr
 // clobber_abi := "clobber_abi(" <abi> *("," <abi>) [","] ")"
 AsmClobberAbi = 'clobber_abi' '(' ('@string' (',' '@string')* ','?) ')'
 // option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw"
@@ -415,7 +415,9 @@ AsmLabel = 'label' BlockExpr
 AsmSym = 'sym' Path
 AsmConst = 'const' Expr
 // operand := reg_operand / clobber_abi / options
-AsmOperand = AsmRegOperand | AsmClobberAbi | AsmOptions | AsmLabel | AsmSym | AsmConst
+AsmOperand = AsmRegOperand | AsmLabel | AsmSym | AsmConst
+AsmOperandNamed = (Name '=')? AsmOperand
+AsmPiece = AsmOperandNamed | AsmClobberAbi | AsmOptions
 
 FormatArgsExpr =
   Attr* 'builtin' '#' 'format_args' '('
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 01d47c34bbd..c81a19f3bda 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -118,7 +118,7 @@ pub struct AsmExpr {
 impl ast::HasAttrs for AsmExpr {}
 impl AsmExpr {
     #[inline]
-    pub fn asm_operands(&self) -> AstChildren<AsmOperand> { support::children(&self.syntax) }
+    pub fn asm_pieces(&self) -> AstChildren<AsmPiece> { support::children(&self.syntax) }
     #[inline]
     pub fn template(&self) -> AstChildren<Expr> { support::children(&self.syntax) }
     #[inline]
@@ -160,6 +160,18 @@ impl AsmOperandExpr {
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct AsmOperandNamed {
+    pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasName for AsmOperandNamed {}
+impl AsmOperandNamed {
+    #[inline]
+    pub fn asm_operand(&self) -> Option<AsmOperand> { support::child(&self.syntax) }
+    #[inline]
+    pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct AsmOption {
     pub(crate) syntax: SyntaxNode,
 }
@@ -217,7 +229,6 @@ impl AsmOptions {
 pub struct AsmRegOperand {
     pub(crate) syntax: SyntaxNode,
 }
-impl ast::HasName for AsmRegOperand {}
 impl AsmRegOperand {
     #[inline]
     pub fn asm_dir_spec(&self) -> Option<AsmDirSpec> { support::child(&self.syntax) }
@@ -229,8 +240,6 @@ impl AsmRegOperand {
     pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
     #[inline]
     pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
-    #[inline]
-    pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -2224,15 +2233,20 @@ impl ast::HasVisibility for Adt {}
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum AsmOperand {
-    AsmClobberAbi(AsmClobberAbi),
     AsmConst(AsmConst),
     AsmLabel(AsmLabel),
-    AsmOptions(AsmOptions),
     AsmRegOperand(AsmRegOperand),
     AsmSym(AsmSym),
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum AsmPiece {
+    AsmClobberAbi(AsmClobberAbi),
+    AsmOperandNamed(AsmOperandNamed),
+    AsmOptions(AsmOptions),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum AssocItem {
     Const(Const),
     Fn(Fn),
@@ -2581,6 +2595,20 @@ impl AstNode for AsmOperandExpr {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
+impl AstNode for AsmOperandNamed {
+    #[inline]
+    fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_NAMED }
+    #[inline]
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        if Self::can_cast(syntax.kind()) {
+            Some(Self { syntax })
+        } else {
+            None
+        }
+    }
+    #[inline]
+    fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
 impl AstNode for AsmOption {
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTION }
@@ -4589,10 +4617,6 @@ impl AstNode for Adt {
         }
     }
 }
-impl From<AsmClobberAbi> for AsmOperand {
-    #[inline]
-    fn from(node: AsmClobberAbi) -> AsmOperand { AsmOperand::AsmClobberAbi(node) }
-}
 impl From<AsmConst> for AsmOperand {
     #[inline]
     fn from(node: AsmConst) -> AsmOperand { AsmOperand::AsmConst(node) }
@@ -4601,10 +4625,6 @@ impl From<AsmLabel> for AsmOperand {
     #[inline]
     fn from(node: AsmLabel) -> AsmOperand { AsmOperand::AsmLabel(node) }
 }
-impl From<AsmOptions> for AsmOperand {
-    #[inline]
-    fn from(node: AsmOptions) -> AsmOperand { AsmOperand::AsmOptions(node) }
-}
 impl From<AsmRegOperand> for AsmOperand {
     #[inline]
     fn from(node: AsmRegOperand) -> AsmOperand { AsmOperand::AsmRegOperand(node) }
@@ -4616,18 +4636,13 @@ impl From<AsmSym> for AsmOperand {
 impl AstNode for AsmOperand {
     #[inline]
     fn can_cast(kind: SyntaxKind) -> bool {
-        matches!(
-            kind,
-            ASM_CLOBBER_ABI | ASM_CONST | ASM_LABEL | ASM_OPTIONS | ASM_REG_OPERAND | ASM_SYM
-        )
+        matches!(kind, ASM_CONST | ASM_LABEL | ASM_REG_OPERAND | ASM_SYM)
     }
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option<Self> {
         let res = match syntax.kind() {
-            ASM_CLOBBER_ABI => AsmOperand::AsmClobberAbi(AsmClobberAbi { syntax }),
             ASM_CONST => AsmOperand::AsmConst(AsmConst { syntax }),
             ASM_LABEL => AsmOperand::AsmLabel(AsmLabel { syntax }),
-            ASM_OPTIONS => AsmOperand::AsmOptions(AsmOptions { syntax }),
             ASM_REG_OPERAND => AsmOperand::AsmRegOperand(AsmRegOperand { syntax }),
             ASM_SYM => AsmOperand::AsmSym(AsmSym { syntax }),
             _ => return None,
@@ -4637,15 +4652,49 @@ impl AstNode for AsmOperand {
     #[inline]
     fn syntax(&self) -> &SyntaxNode {
         match self {
-            AsmOperand::AsmClobberAbi(it) => &it.syntax,
             AsmOperand::AsmConst(it) => &it.syntax,
             AsmOperand::AsmLabel(it) => &it.syntax,
-            AsmOperand::AsmOptions(it) => &it.syntax,
             AsmOperand::AsmRegOperand(it) => &it.syntax,
             AsmOperand::AsmSym(it) => &it.syntax,
         }
     }
 }
+impl From<AsmClobberAbi> for AsmPiece {
+    #[inline]
+    fn from(node: AsmClobberAbi) -> AsmPiece { AsmPiece::AsmClobberAbi(node) }
+}
+impl From<AsmOperandNamed> for AsmPiece {
+    #[inline]
+    fn from(node: AsmOperandNamed) -> AsmPiece { AsmPiece::AsmOperandNamed(node) }
+}
+impl From<AsmOptions> for AsmPiece {
+    #[inline]
+    fn from(node: AsmOptions) -> AsmPiece { AsmPiece::AsmOptions(node) }
+}
+impl AstNode for AsmPiece {
+    #[inline]
+    fn can_cast(kind: SyntaxKind) -> bool {
+        matches!(kind, ASM_CLOBBER_ABI | ASM_OPERAND_NAMED | ASM_OPTIONS)
+    }
+    #[inline]
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        let res = match syntax.kind() {
+            ASM_CLOBBER_ABI => AsmPiece::AsmClobberAbi(AsmClobberAbi { syntax }),
+            ASM_OPERAND_NAMED => AsmPiece::AsmOperandNamed(AsmOperandNamed { syntax }),
+            ASM_OPTIONS => AsmPiece::AsmOptions(AsmOptions { syntax }),
+            _ => return None,
+        };
+        Some(res)
+    }
+    #[inline]
+    fn syntax(&self) -> &SyntaxNode {
+        match self {
+            AsmPiece::AsmClobberAbi(it) => &it.syntax,
+            AsmPiece::AsmOperandNamed(it) => &it.syntax,
+            AsmPiece::AsmOptions(it) => &it.syntax,
+        }
+    }
+}
 impl From<Const> for AssocItem {
     #[inline]
     fn from(node: Const) -> AssocItem { AssocItem::Const(node) }
@@ -6181,7 +6230,7 @@ impl AstNode for AnyHasName {
     fn can_cast(kind: SyntaxKind) -> bool {
         matches!(
             kind,
-            ASM_REG_OPERAND
+            ASM_OPERAND_NAMED
                 | CONST
                 | CONST_PARAM
                 | ENUM
@@ -6211,9 +6260,9 @@ impl AstNode for AnyHasName {
     #[inline]
     fn syntax(&self) -> &SyntaxNode { &self.syntax }
 }
-impl From<AsmRegOperand> for AnyHasName {
+impl From<AsmOperandNamed> for AnyHasName {
     #[inline]
-    fn from(node: AsmRegOperand) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+    fn from(node: AsmOperandNamed) -> AnyHasName { AnyHasName { syntax: node.syntax } }
 }
 impl From<Const> for AnyHasName {
     #[inline]
@@ -6460,6 +6509,11 @@ impl std::fmt::Display for AsmOperand {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for AsmPiece {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(self.syntax(), f)
+    }
+}
 impl std::fmt::Display for AssocItem {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)
@@ -6560,6 +6614,11 @@ impl std::fmt::Display for AsmOperandExpr {
         std::fmt::Display::fmt(self.syntax(), f)
     }
 }
+impl std::fmt::Display for AsmOperandNamed {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        std::fmt::Display::fmt(self.syntax(), f)
+    }
+}
 impl std::fmt::Display for AsmOption {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         std::fmt::Display::fmt(self.syntax(), f)