Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #18022 - Veykril:asm-parse, r=Veykril
feat: IDE support for `asm!` expressions Fixes https://github.com/rust-lang/rust-analyzer/issues/10461, Fixes https://github.com/rust-lang/rust-analyzer/issues/6031 Progresses https://github.com/rust-lang/rust-analyzer/issues/11621 Notably this only works for asm expressions not items yet. Most IDE features work, mainly completions need extra logic still.
bors 2024-09-05
parent 50882fb · parent c075a99 · commit d927daf
-rw-r--r--.typos.toml1
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--crates/hir-def/src/body.rs36
-rw-r--r--crates/hir-def/src/body/lower.rs14
-rw-r--r--crates/hir-def/src/body/lower/asm.rs254
-rw-r--r--crates/hir-def/src/hir.rs131
-rw-r--r--crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs6
-rw-r--r--crates/hir-expand/src/builtin/fn_macro.rs34
-rw-r--r--crates/hir-ty/src/infer/closure.rs21
-rw-r--r--crates/hir-ty/src/infer/expr.rs67
-rw-r--r--crates/hir-ty/src/infer/mutability.rs26
-rw-r--r--crates/hir-ty/src/tests/macros.rs99
-rw-r--r--crates/hir/src/has_source.rs27
-rw-r--r--crates/hir/src/lib.rs22
-rw-r--r--crates/hir/src/semantics.rs70
-rw-r--r--crates/hir/src/semantics/source_to_def.rs21
-rw-r--r--crates/hir/src/source_analyzer.rs22
-rw-r--r--crates/ide-completion/src/item.rs1
-rw-r--r--crates/ide-db/src/defs.rs37
-rw-r--r--crates/ide-db/src/lib.rs1
-rw-r--r--crates/ide-db/src/rename.rs4
-rw-r--r--crates/ide-db/src/search.rs47
-rw-r--r--crates/ide/src/doc_links.rs8
-rw-r--r--crates/ide/src/moniker.rs7
-rw-r--r--crates/ide/src/navigation_target.rs29
-rw-r--r--crates/ide/src/rename.rs56
-rw-r--r--crates/ide/src/syntax_highlighting/format.rs24
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs4
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs2
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_asm.html97
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html14
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs63
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs179
-rw-r--r--crates/parser/src/parser.rs8
-rw-r--r--crates/parser/src/syntax_kind/generated.rs79
-rw-r--r--crates/parser/test_data/generated/runner.rs2
-rw-r--r--crates/parser/test_data/parser/inline/ok/asm_expr.rast83
-rw-r--r--crates/parser/test_data/parser/inline/ok/asm_expr.rs10
-rw-r--r--crates/parser/test_data/parser/inline/ok/builtin_expr.rast2
-rw-r--r--crates/parser/test_data/parser/inline/ok/builtin_expr.rs2
-rw-r--r--crates/rust-analyzer/src/lsp/to_proto.rs3
-rw-r--r--crates/syntax/rust.ungram27
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs501
-rw-r--r--crates/test-utils/src/minicore.rs23
-rw-r--r--xtask/Cargo.toml1
-rw-r--r--xtask/src/codegen/grammar.rs40
-rw-r--r--xtask/src/codegen/grammar/ast_src.rs26
49 files changed, 2070 insertions, 164 deletions
diff --git a/.typos.toml b/.typos.toml
index e7e764ce03..febfb233bd 100644
--- a/.typos.toml
+++ b/.typos.toml
@@ -15,6 +15,7 @@ extend-ignore-re = [
'"flate2"',
"raison d'ĂȘtre",
"inout",
+ "INOUT",
"optin"
]
diff --git a/Cargo.lock b/Cargo.lock
index 6eaded6da1..85ef3a6ba9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2624,6 +2624,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"directories",
+ "either",
"flate2",
"itertools",
"proc-macro2",
diff --git a/Cargo.toml b/Cargo.toml
index e55628c8dc..9e972e1211 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -185,6 +185,7 @@ style = { level = "warn", priority = -1 }
suspicious = { level = "warn", priority = -1 }
## allow following lints
+too_long_first_doc_paragraph = "allow"
# subjective
single_match = "allow"
# () makes a fine error in most cases
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index f5fe8f8770..34db43939a 100644
--- a/crates/hir-def/src/body.rs
+++ b/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();
@@ -463,4 +482,13 @@ impl BodySourceMap {
diagnostics.shrink_to_fit();
binding_definitions.shrink_to_fit();
}
+
+ pub fn template_map(
+ &self,
+ ) -> Option<&(
+ FxHashMap<Idx<Expr>, Vec<(tt::TextRange, Name)>>,
+ FxHashMap<Idx<Expr>, Vec<(tt::TextRange, usize)>>,
+ )> {
+ self.template_map.as_deref()
+ }
}
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 145afc1b61..9c547574ec 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -1,6 +1,8 @@
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
//! representation.
+mod asm;
+
use std::mem;
use base_db::CrateId;
@@ -35,8 +37,8 @@ use crate::{
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,
+ Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, OffsetOf, Pat,
+ PatId, RecordFieldPat, RecordLitField, Statement,
},
item_scope::BuiltinShadowMode,
lang_item::LangItem,
@@ -693,10 +695,7 @@ impl ExprCollector<'_> {
}
}
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
- ast::Expr::AsmExpr(e) => {
- let e = self.collect_expr_opt(e.expr());
- self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr)
- }
+ ast::Expr::AsmExpr(e) => self.lower_inline_asm(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();
@@ -1848,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
}
@@ -2061,6 +2060,7 @@ impl ExprCollector<'_> {
is_assignee_expr: false,
})
}
+
// endregion: format
fn lang_path(&self, lang: LangItem) -> Option<Path> {
diff --git a/crates/hir-def/src/body/lower/asm.rs b/crates/hir-def/src/body/lower/asm.rs
new file mode 100644
index 0000000000..bf6ba41482
--- /dev/null
+++ b/crates/hir-def/src/body/lower/asm.rs
@@ -0,0 +1,254 @@
+//! Lowering of inline assembly.
+use hir_expand::name::Name;
+use intern::Symbol;
+use rustc_hash::{FxHashMap, FxHashSet};
+use syntax::{
+ ast::{self, HasName, IsString},
+ AstNode, AstPtr, AstToken, T,
+};
+use tt::{TextRange, TextSize};
+
+use crate::{
+ body::lower::{ExprCollector, FxIndexSet},
+ hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass},
+};
+
+impl ExprCollector<'_> {
+ pub(super) fn lower_inline_asm(
+ &mut self,
+ asm: ast::AsmExpr,
+ syntax_ptr: AstPtr<ast::Expr>,
+ ) -> ExprId {
+ let mut clobber_abis = FxIndexSet::default();
+ let mut operands = vec![];
+ let mut options = AsmOptions::empty();
+
+ 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 piece in asm.asm_pieces() {
+ let slot = operands.len();
+ let mut lower_reg = |reg: Option<ast::AsmRegSpec>| {
+ let reg = reg?;
+ if let Some(string) = reg.string_token() {
+ reg_args.insert(slot);
+ Some(InlineAsmRegOrRegClass::Reg(Symbol::intern(string.text())))
+ } else {
+ reg.name_ref().map(|name_ref| {
+ InlineAsmRegOrRegClass::RegClass(Symbol::intern(&name_ref.text()))
+ })
+ }
+ };
+
+ 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::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,
+ T![may_unwind] => AsmOptions::MAY_UNWIND,
+ T![nomem] => AsmOptions::NOMEM,
+ T![noreturn] => AsmOptions::NORETURN,
+ T![nostack] => AsmOptions::NOSTACK,
+ T![preserves_flags] => AsmOptions::PRESERVES_FLAGS,
+ T![pure] => AsmOptions::PURE,
+ T![raw] => AsmOptions::RAW,
+ T![readonly] => AsmOptions::READONLY,
+ _ => return,
+ }
+ });
+ continue;
+ }
+ ast::AsmPiece::AsmOperandNamed(op) => {
+ let name = op.name().map(|name| Symbol::intern(&name.text()));
+ if let Some(name) = &name {
+ named_args.insert(name.clone(), slot);
+ named_pos.insert(slot, name.clone());
+ }
+ let Some(op) = op.asm_operand() else { continue };
+ (
+ name.map(Name::new_symbol_root),
+ 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;
+ }
+ }
+ 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);
+ }
+
+ let mut mappings = vec![];
+ let mut curarg = 0;
+ if !options.contains(AsmOptions::RAW) {
+ // Don't treat raw asm as a format string.
+ asm.template()
+ .filter_map(|it| Some((it.clone(), self.expand_macros_to_string(it)?)))
+ .for_each(|(expr, (s, is_direct_literal))| {
+ let Ok(text) = s.value() else {
+ return;
+ };
+ let template_snippet = match expr {
+ ast::Expr::Literal(literal) => match literal.kind() {
+ ast::LiteralKind::String(s) => Some(s.text().to_owned()),
+ _ => None,
+ },
+ _ => None,
+ };
+ let str_style = match s.quote_offsets() {
+ Some(offsets) => {
+ let raw = usize::from(offsets.quotes.0.len()) - 1;
+ // subtract 1 for the `r` prefix
+ (raw != 0).then(|| raw - 1)
+ }
+ None => None,
+ };
+
+ let mut parser = rustc_parse_format::Parser::new(
+ &text,
+ str_style,
+ template_snippet,
+ false,
+ rustc_parse_format::ParseMode::InlineAsm,
+ );
+ parser.curarg = curarg;
+
+ let mut unverified_pieces = Vec::new();
+ while let Some(piece) = parser.next() {
+ if !parser.errors.is_empty() {
+ break;
+ } else {
+ unverified_pieces.push(piece);
+ }
+ }
+
+ curarg = parser.curarg;
+
+ let to_span = |inner_span: rustc_parse_format::InnerSpan| {
+ is_direct_literal.then(|| {
+ TextRange::new(
+ inner_span.start.try_into().unwrap(),
+ inner_span.end.try_into().unwrap(),
+ ) - TextSize::from(str_style.map(|it| it + 1).unwrap_or(0) as u32 + 1)
+ })
+ };
+ for piece in unverified_pieces {
+ match piece {
+ rustc_parse_format::Piece::String(_) => {}
+ rustc_parse_format::Piece::NextArgument(arg) => {
+ // let span = arg_spans.next();
+
+ let (operand_idx, _name) = match arg.position {
+ rustc_parse_format::ArgumentIs(idx)
+ | rustc_parse_format::ArgumentImplicitlyIs(idx) => {
+ if idx >= operands.len()
+ || named_pos.contains_key(&idx)
+ || reg_args.contains(&idx)
+ {
+ (None, None)
+ } else {
+ (Some(idx), None)
+ }
+ }
+ rustc_parse_format::ArgumentNamed(name) => {
+ let name = Symbol::intern(name);
+ (
+ named_args.get(&name).copied(),
+ Some(Name::new_symbol_root(name)),
+ )
+ }
+ };
+
+ if let Some(operand_idx) = operand_idx {
+ if let Some(position_span) = to_span(arg.position_span) {
+ mappings.push((position_span, operand_idx));
+ }
+ }
+ }
+ }
+ }
+ })
+ };
+ let idx = self.alloc_expr(
+ Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }),
+ syntax_ptr,
+ );
+ self.source_map.template_map.get_or_insert_with(Default::default).1.insert(idx, mappings);
+ idx
+ }
+}
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index 86fd092603..d9358a2882 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -307,7 +307,120 @@ pub struct OffsetOf {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InlineAsm {
- pub e: ExprId,
+ pub operands: Box<[(Option<Name>, AsmOperand)]>,
+ pub options: AsmOptions,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct AsmOptions(u16);
+bitflags::bitflags! {
+ impl AsmOptions: u16 {
+ const PURE = 1 << 0;
+ const NOMEM = 1 << 1;
+ const READONLY = 1 << 2;
+ const PRESERVES_FLAGS = 1 << 3;
+ const NORETURN = 1 << 4;
+ const NOSTACK = 1 << 5;
+ const ATT_SYNTAX = 1 << 6;
+ const RAW = 1 << 7;
+ const MAY_UNWIND = 1 << 8;
+ }
+}
+
+impl AsmOptions {
+ pub const COUNT: usize = Self::all().bits().count_ones() as usize;
+
+ pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);
+ pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN);
+
+ pub fn human_readable_names(&self) -> Vec<&'static str> {
+ let mut options = vec![];
+
+ if self.contains(AsmOptions::PURE) {
+ options.push("pure");
+ }
+ if self.contains(AsmOptions::NOMEM) {
+ options.push("nomem");
+ }
+ if self.contains(AsmOptions::READONLY) {
+ options.push("readonly");
+ }
+ if self.contains(AsmOptions::PRESERVES_FLAGS) {
+ options.push("preserves_flags");
+ }
+ if self.contains(AsmOptions::NORETURN) {
+ options.push("noreturn");
+ }
+ if self.contains(AsmOptions::NOSTACK) {
+ options.push("nostack");
+ }
+ if self.contains(AsmOptions::ATT_SYNTAX) {
+ options.push("att_syntax");
+ }
+ if self.contains(AsmOptions::RAW) {
+ options.push("raw");
+ }
+ if self.contains(AsmOptions::MAY_UNWIND) {
+ options.push("may_unwind");
+ }
+
+ options
+ }
+}
+
+impl std::fmt::Debug for AsmOptions {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ bitflags::parser::to_writer(self, f)
+ }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum AsmOperand {
+ In {
+ reg: InlineAsmRegOrRegClass,
+ expr: ExprId,
+ },
+ Out {
+ reg: InlineAsmRegOrRegClass,
+ expr: Option<ExprId>,
+ late: bool,
+ },
+ InOut {
+ reg: InlineAsmRegOrRegClass,
+ expr: ExprId,
+ late: bool,
+ },
+ SplitInOut {
+ reg: InlineAsmRegOrRegClass,
+ in_expr: ExprId,
+ out_expr: Option<ExprId>,
+ late: bool,
+ },
+ Label(ExprId),
+ Const(ExprId),
+ Sym(Path),
+}
+
+impl AsmOperand {
+ pub fn reg(&self) -> Option<&InlineAsmRegOrRegClass> {
+ match self {
+ Self::In { reg, .. }
+ | Self::Out { reg, .. }
+ | Self::InOut { reg, .. }
+ | Self::SplitInOut { reg, .. } => Some(reg),
+ Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None,
+ }
+ }
+
+ pub fn is_clobber(&self) -> bool {
+ matches!(self, AsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None })
+ }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq, Hash)]
+pub enum InlineAsmRegOrRegClass {
+ Reg(Symbol),
+ RegClass(Symbol),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -372,7 +485,21 @@ impl Expr {
match self {
Expr::Missing => {}
Expr::Path(_) | Expr::OffsetOf(_) => {}
- Expr::InlineAsm(it) => f(it.e),
+ Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
+ AsmOperand::In { expr, .. }
+ | AsmOperand::Out { expr: Some(expr), .. }
+ | AsmOperand::InOut { expr, .. } => f(*expr),
+ AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+ f(*in_expr);
+ if let Some(out_expr) = out_expr {
+ f(*out_expr);
+ }
+ }
+ AsmOperand::Out { expr: None, .. }
+ | AsmOperand::Const(_)
+ | AsmOperand::Label(_)
+ | AsmOperand::Sym(_) => (),
+ }),
Expr::If { condition, then_branch, else_branch } => {
f(*condition);
f(*then_branch);
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 b6dbba12cd..37ae1ab39b 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
@@ -50,11 +50,7 @@ fn main() {
let i: u64 = 3;
let o: u64;
unsafe {
- builtin #asm ( {
- $crate::format_args!("mov {0}, {1}");
- $crate::format_args!("add {0}, 5");
- }
- );
+ builtin #asm ("mov {0}, {1}", "add {0}, 5", out (reg)o, in (reg)i, );
}
}
"##]],
diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs
index 795d9b14df..15fed45caf 100644
--- a/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/crates/hir-expand/src/builtin/fn_macro.rs
@@ -119,9 +119,8 @@ register_builtin! {
(module_path, ModulePath) => module_path_expand,
(assert, Assert) => assert_expand,
(stringify, Stringify) => stringify_expand,
- (llvm_asm, LlvmAsm) => asm_expand,
(asm, Asm) => asm_expand,
- (global_asm, GlobalAsm) => global_asm_expand,
+ (global_asm, GlobalAsm) => asm_expand,
(cfg, Cfg) => cfg_expand,
(core_panic, CorePanic) => panic_expand,
(std_panic, StdPanic) => panic_expand,
@@ -324,40 +323,15 @@ fn asm_expand(
tt: &tt::Subtree,
span: Span,
) -> ExpandResult<tt::Subtree> {
- // We expand all assembly snippets to `format_args!` invocations to get format syntax
- // highlighting for them.
- let mut literals = Vec::new();
- for tt in tt.token_trees.chunks(2) {
- match tt {
- [tt::TokenTree::Leaf(tt::Leaf::Literal(lit))]
- | [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', span: _, spacing: _ }))] =>
- {
- let dollar_krate = dollar_crate(span);
- literals.push(quote!(span=>#dollar_krate::format_args!(#lit);));
- }
- _ => break,
- }
- }
-
+ let mut tt = tt.clone();
+ tt.delimiter.kind = tt::DelimiterKind::Parenthesis;
let pound = mk_pound(span);
let expanded = quote! {span =>
- builtin #pound asm (
- {##literals}
- )
+ builtin #pound asm #tt
};
ExpandResult::ok(expanded)
}
-fn global_asm_expand(
- _db: &dyn ExpandDatabase,
- _id: MacroCallId,
- _tt: &tt::Subtree,
- span: Span,
-) -> ExpandResult<tt::Subtree> {
- // Expand to nothing (at item-level)
- ExpandResult::ok(quote! {span =>})
-}
-
fn cfg_expand(
db: &dyn ExpandDatabase,
id: MacroCallId,
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 36327d1d49..5cad08b939 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -10,7 +10,10 @@ use chalk_ir::{
use either::Either;
use hir_def::{
data::adt::VariantData,
- hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp},
+ hir::{
+ Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement,
+ UnaryOp,
+ },
lang_item::LangItem,
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
@@ -666,7 +669,21 @@ impl InferenceContext<'_> {
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
match &self.body[tgt_expr] {
Expr::OffsetOf(_) => (),
- Expr::InlineAsm(e) => self.walk_expr_without_adjust(e.e),
+ Expr::InlineAsm(e) => e.operands.iter().for_each(|(_, op)| match op {
+ AsmOperand::In { expr, .. }
+ | AsmOperand::Out { expr: Some(expr), .. }
+ | AsmOperand::InOut { expr, .. } => self.walk_expr_without_adjust(*expr),
+ AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+ self.walk_expr_without_adjust(*in_expr);
+ if let Some(out_expr) = out_expr {
+ self.walk_expr_without_adjust(*out_expr);
+ }
+ }
+ AsmOperand::Out { expr: None, .. }
+ | AsmOperand::Const(_)
+ | AsmOperand::Label(_)
+ | AsmOperand::Sym(_) => (),
+ }),
Expr::If { condition, then_branch, else_branch } => {
self.consume_expr(*condition);
self.consume_expr(*then_branch);
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index b1c793a1e3..a82d091cbb 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -9,7 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin
use either::Either;
use hir_def::{
hir::{
- ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
+ ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId,
+ Literal, Statement, UnaryOp,
},
lang_item::{LangItem, LangItemTarget},
path::{GenericArg, GenericArgs, Path},
@@ -41,9 +42,9 @@ use crate::{
primitive::{self, UintTy},
static_lifetime, to_chalk_trait_id,
traits::FnTrait,
- Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnAbi, FnPointer, FnSig,
- FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder,
- TyExt, TyKind,
+ Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, FnAbi, FnPointer,
+ FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty,
+ TyBuilder, TyExt, TyKind,
};
use super::{
@@ -924,9 +925,61 @@ impl InferenceContext<'_> {
expected
}
Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
- Expr::InlineAsm(it) => {
- self.infer_expr_no_expect(it.e);
- self.result.standard_types.unit.clone()
+ Expr::InlineAsm(asm) => {
+ let mut check_expr_asm_operand = |expr, is_input: bool| {
+ let ty = self.infer_expr_no_expect(expr);
+
+ // If this is an input value, we require its type to be fully resolved
+ // at this point. This allows us to provide helpful coercions which help
+ // pass the type candidate list in a later pass.
+ //
+ // We don't require output types to be resolved at this point, which
+ // allows them to be inferred based on how they are used later in the
+ // function.
+ if is_input {
+ let ty = self.resolve_ty_shallow(&ty);
+ match ty.kind(Interner) {
+ TyKind::FnDef(def, parameters) => {
+ let fnptr_ty = TyKind::Function(
+ CallableSig::from_def(self.db, *def, parameters).to_fn_ptr(),
+ )
+ .intern(Interner);
+ _ = self.coerce(Some(expr), &ty, &fnptr_ty);
+ }
+ TyKind::Ref(mutbl, _, base_ty) => {
+ let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner);
+ _ = self.coerce(Some(expr), &ty, &ptr_ty);
+ }
+ _ => {}
+ }
+ }
+ };
+
+ let diverge = asm.options.contains(AsmOptions::NORETURN);
+ asm.operands.iter().for_each(|(_, operand)| match *operand {
+ AsmOperand::In { expr, .. } => check_expr_asm_operand(expr, true),
+ AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => {
+ check_expr_asm_operand(expr, false)
+ }
+ AsmOperand::Out { expr: None, .. } => (),
+ AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+ check_expr_asm_operand(in_expr, true);
+ if let Some(out_expr) = out_expr {
+ check_expr_asm_operand(out_expr, false);
+ }
+ }
+ // FIXME
+ AsmOperand::Label(_) => (),
+ // FIXME
+ AsmOperand::Const(_) => (),
+ // FIXME
+ AsmOperand::Sym(_) => (),
+ });
+ if diverge {
+ self.result.standard_types.never.clone()
+ } else {
+ self.result.standard_types.unit.clone()
+ }
}
};
// use a new type variable if we got unknown here
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
index 7fed5f0203..8e52725e53 100644
--- a/crates/hir-ty/src/infer/mutability.rs
+++ b/crates/hir-ty/src/infer/mutability.rs
@@ -3,7 +3,9 @@
use chalk_ir::{cast::Cast, Mutability};
use hir_def::{
- hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
+ hir::{
+ Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp,
+ },
lang_item::LangItem,
};
use hir_expand::name::Name;
@@ -39,7 +41,25 @@ impl InferenceContext<'_> {
fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
match &self.body[tgt_expr] {
Expr::Missing => (),
- Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not),
+ Expr::InlineAsm(e) => {
+ e.operands.iter().for_each(|(_, op)| match op {
+ AsmOperand::In { expr, .. }
+ | AsmOperand::Out { expr: Some(expr), .. }
+ | AsmOperand::InOut { expr, .. } => {
+ self.infer_mut_expr_without_adjust(*expr, Mutability::Not)
+ }
+ AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+ self.infer_mut_expr_without_adjust(*in_expr, Mutability::Not);
+ if let Some(out_expr) = out_expr {
+ self.infer_mut_expr_without_adjust(*out_expr, Mutability::Not);
+ }
+ }
+ AsmOperand::Out { expr: None, .. }
+ | AsmOperand::Label(_)
+ | AsmOperand::Sym(_)
+ | AsmOperand::Const(_) => (),
+ });
+ }
Expr::OffsetOf(_) => (),
&Expr::If { condition, then_branch, else_branch } => {
self.infer_mut_expr(condition, Mutability::Not);
@@ -129,7 +149,7 @@ impl InferenceContext<'_> {
target,
}) = base_adjustments
{
- // For assignee exprs `IndexMut` obiligations are already applied
+ // For assignee exprs `IndexMut` obligations are already applied
if !is_assignee_expr {
if let TyKind::Ref(_, _, ty) = target.kind(Interner) {
base_ty = Some(ty.clone());
diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs
index 5a6161f942..10ab6d3ff8 100644
--- a/crates/hir-ty/src/tests/macros.rs
+++ b/crates/hir-ty/src/tests/macros.rs
@@ -1,7 +1,7 @@
use expect_test::expect;
use test_utils::{bench, bench_fixture, skip_slow_tests};
-use crate::tests::check_infer_with_mismatches;
+use crate::tests::{check_infer_with_mismatches, check_no_mismatches};
use super::{check_infer, check_types};
@@ -1406,3 +1406,100 @@ fn foo(t: Tensor) {
"#,
);
}
+
+#[test]
+fn asm_unit() {
+ check_no_mismatches(
+ r#"
+//- minicore: asm
+fn unit() {
+ core::arch::asm!("")
+}
+"#,
+ );
+}
+
+#[test]
+fn asm_no_return() {
+ check_no_mismatches(
+ r#"
+//- minicore: asm
+fn unit() -> ! {
+ core::arch::asm!("", options(noreturn))
+}
+"#,
+ );
+}
+
+#[test]
+fn asm_things() {
+ check_infer(
+ r#"
+//- minicore: asm, concat
+fn main() {
+ unsafe {
+ let foo = 1;
+ let mut o = 0;
+ core::arch::asm!(
+ "%input = OpLoad _ {0}",
+ concat!("%result = ", bar, " _ %input"),
+ "OpStore {1} %result",
+ in(reg) &foo,
+ in(reg) &mut o,
+ );
+ o
+
+ let thread_id: usize;
+ core::arch::asm!("
+ mov {0}, gs:[0x30]
+ mov {0}, [{0}+0x48]
+ ", out(reg) thread_id, options(pure, readonly, nostack));
+
+ static UNMAP_BASE: usize;
+ const MEM_RELEASE: usize;
+ static VirtualFree: usize;
+ const OffPtr: usize;
+ const OffFn: usize;
+ core::arch::asm!("
+ push {free_type}
+ push {free_size}
+ push {base}
+
+ mov eax, fs:[30h]
+ mov eax, [eax+8h]
+ add eax, {off_fn}
+ mov [eax-{off_fn}+{off_ptr}], eax
+
+ push eax
+
+ jmp {virtual_free}
+ ",
+ off_ptr = const OffPtr,
+ off_fn = const OffFn,
+
+ free_size = const 0,
+ free_type = const MEM_RELEASE,
+
+ virtual_free = sym VirtualFree,
+
+ base = sym UNMAP_BASE,
+ options(noreturn),
+ );
+ }
+}
+"#,
+ expect![[r#"
+ !0..122 'builti...muto,)': ()
+ !0..136 'builti...tack))': ()
+ !0..449 'builti...urn),)': !
+ 10..1236 '{ ... } }': ()
+ 16..1234 'unsafe... }': ()
+ 37..40 'foo': i32
+ 43..44 '1': i32
+ 58..63 'mut o': i32
+ 66..67 '0': i32
+ 293..294 'o': i32
+ 308..317 'thread_id': usize
+ "#]],
+ )
+}
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index 7d52a28b91..82c90ac301 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -14,8 +14,8 @@ use tt::TextRange;
use crate::{
db::HirDatabase, Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
- Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static, Struct, Trait,
- TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant,
+ InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static,
+ Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant,
};
pub trait HasSource {
@@ -292,3 +292,26 @@ impl HasSource for ExternCrateDecl {
Some(self.id.lookup(db.upcast()).source(db.upcast()))
}
}
+
+impl HasSource for InlineAsmOperand {
+ type Ast = ast::AsmOperandNamed;
+ fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
+ let (_body, source_map) = db.body_with_source_map(self.owner);
+ if let Ok(src) = source_map.expr_syntax(self.expr) {
+ let root = src.file_syntax(db.upcast());
+ return src
+ .map(|ast| match ast.to_node(&root) {
+ ast::Expr::AsmExpr(asm) => asm
+ .asm_pieces()
+ .filter_map(|it| match it {
+ ast::AsmPiece::AsmOperandNamed(it) => Some(it),
+ _ => None,
+ })
+ .nth(self.index),
+ _ => None,
+ })
+ .transpose();
+ }
+ None
+ }
+}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 81d6466c2f..3b8b901fa1 100644
--- a/crates/hir/src/lib.rs
+++ b/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,26 @@ impl Type {
}
}
+#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
+pub struct InlineAsmOperand {
+ owner: DefWithBodyId,
+ expr: ExprId,
+ index: usize,
+}
+
+impl InlineAsmOperand {
+ pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
+ self.owner.into()
+ }
+
+ pub fn name(&self, db: &dyn HirDatabase) -> Option<Name> {
+ match &db.body(self.owner)[self.expr] {
+ hir_def::hir::Expr::InlineAsm(e) => e.operands.get(self.index)?.0.clone(),
+ _ => None,
+ }
+ }
+}
+
// FIXME: Document this
#[derive(Debug)]
pub struct Callable {
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index c78b59826c..0ba0e44657 100644
--- a/crates/hir/src/semantics.rs
+++ b/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(());
@@ -368,7 +368,6 @@ impl<'db> SemanticsImpl<'db> {
| BuiltinFnLikeExpander::File
| BuiltinFnLikeExpander::ModulePath
| BuiltinFnLikeExpander::Asm
- | BuiltinFnLikeExpander::LlvmAsm
| BuiltinFnLikeExpander::GlobalAsm
| BuiltinFnLikeExpander::LogSyntax
| BuiltinFnLikeExpander::TraceMacros
@@ -547,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()?;
@@ -561,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)
})
@@ -579,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()?;
@@ -600,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.
@@ -1782,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/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index edeb19030a..fc629b9c6d 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/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/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index be0116862b..f6f1da1b7d 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/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/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index f8c8b12bd2..8c97ebd550 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -360,6 +360,7 @@ impl CompletionItemKind {
SymbolKind::Field => "fd",
SymbolKind::Function => "fn",
SymbolKind::Impl => "im",
+ SymbolKind::InlineAsmRegOrRegClass => "ar",
SymbolKind::Label => "lb",
SymbolKind::LifetimeParam => "lt",
SymbolKind::Local => "lc",
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index 5d4b199908..099f26eba7 100644
--- a/crates/ide-db/src/defs.rs
+++ b/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;
@@ -50,6 +50,8 @@ pub enum Definition {
BuiltinAttr(BuiltinAttr),
ToolModule(ToolModule),
ExternCrateDecl(ExternCrateDecl),
+ InlineAsmRegOrRegClass(()),
+ InlineAsmOperand(InlineAsmOperand),
}
impl Definition {
@@ -83,11 +85,13 @@ impl Definition {
Definition::Label(it) => it.module(db),
Definition::ExternCrateDecl(it) => it.module(db),
Definition::DeriveHelper(it) => it.derive().module(db),
+ Definition::InlineAsmOperand(it) => it.parent(db).module(db),
Definition::BuiltinAttr(_)
| Definition::BuiltinType(_)
| Definition::BuiltinLifetime(_)
| Definition::TupleField(_)
- | Definition::ToolModule(_) => return None,
+ | Definition::ToolModule(_)
+ | Definition::InlineAsmRegOrRegClass(_) => return None,
};
Some(module)
}
@@ -121,7 +125,9 @@ impl Definition {
| Definition::Local(_)
| Definition::GenericParam(_)
| Definition::Label(_)
- | Definition::DeriveHelper(_) => return None,
+ | Definition::DeriveHelper(_)
+ | Definition::InlineAsmRegOrRegClass(_)
+ | Definition::InlineAsmOperand(_) => return None,
};
Some(vis)
}
@@ -150,6 +156,8 @@ impl Definition {
Definition::ToolModule(_) => return None, // FIXME
Definition::DeriveHelper(it) => it.name(db),
Definition::ExternCrateDecl(it) => return it.alias_or_name(db),
+ Definition::InlineAsmRegOrRegClass(_) => return None,
+ Definition::InlineAsmOperand(op) => return op.name(db),
};
Some(name)
}
@@ -212,6 +220,7 @@ impl Definition {
Definition::ToolModule(_) => None,
Definition::DeriveHelper(_) => None,
Definition::TupleField(_) => None,
+ Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmOperand(_) => None,
};
docs.or_else(|| {
@@ -268,6 +277,9 @@ impl Definition {
Definition::DeriveHelper(it) => {
format!("derive_helper {}", it.name(db).display(db, edition))
}
+ // FIXME
+ Definition::InlineAsmRegOrRegClass(_) => "inline_asm_reg_or_reg_class".to_owned(),
+ Definition::InlineAsmOperand(_) => "inline_asm_reg_operand".to_owned(),
}
}
}
@@ -429,7 +441,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)?,
@@ -440,6 +451,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,
}
};
@@ -699,6 +711,9 @@ impl NameRefClass {
NameRefClass::ExternCrateShorthand { krate, decl: extern_crate }
})
},
+ ast::AsmRegSpec(_) => {
+ Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(())))
+ },
_ => None
}
}
@@ -753,6 +768,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/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index ab161f0ce5..3435757ad3 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -224,6 +224,7 @@ pub enum SymbolKind {
Function,
Method,
Impl,
+ InlineAsmRegOrRegClass,
Label,
LifetimeParam,
Local,
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index 262eefeec0..f1404ed9f2 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -200,12 +200,14 @@ impl Definition {
.and_then(syn_ctx_is_root)
}
}
+ Definition::InlineAsmOperand(it) => name_range(it, sema).and_then(syn_ctx_is_root),
Definition::BuiltinType(_)
| Definition::BuiltinLifetime(_)
| Definition::BuiltinAttr(_)
| Definition::SelfType(_)
| Definition::ToolModule(_)
- | Definition::TupleField(_) => return None,
+ | Definition::TupleField(_)
+ | Definition::InlineAsmRegOrRegClass(_) => return None,
// FIXME: This should be doable in theory
Definition::DeriveHelper(_) => return None,
};
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index 12ce5a403f..ad30c624c4 100644
--- a/crates/ide-db/src/search.rs
+++ b/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;
@@ -318,6 +319,23 @@ impl Definition {
};
}
+ if let Definition::InlineAsmOperand(op) = self {
+ let def = match op.parent(db) {
+ DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()),
+ DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()),
+ DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
+ DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
+ // FIXME: implement
+ DefWithBody::InTypeConst(_) => return SearchScope::empty(),
+ };
+ return match def {
+ Some(def) => SearchScope::file_range(
+ def.as_ref().original_file_range_with_macro_call_body(db),
+ ),
+ None => SearchScope::single_file(file_id),
+ };
+ }
+
if let Definition::SelfType(impl_) = self {
return match impl_.source(db).map(|src| src.syntax().cloned()) {
Some(def) => SearchScope::file_range(
@@ -908,7 +926,6 @@ impl<'a> FindUsages<'a> {
let finder = &Finder::new(name);
let include_self_kw_refs =
self.include_self_kw_refs.as_ref().map(|ty| (ty, Finder::new("Self")));
-
for (text, file_id, search_range) in Self::scope_files(sema.db, &search_scope) {
self.sema.db.unwind_if_cancelled();
let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone());
@@ -917,7 +934,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 +1104,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/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 925ae62023..ea16a11d56 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -219,7 +219,9 @@ pub(crate) fn resolve_doc_path_for_def(
| Definition::Local(_)
| Definition::GenericParam(_)
| Definition::Label(_)
- | Definition::DeriveHelper(_) => None,
+ | Definition::DeriveHelper(_)
+ | Definition::InlineAsmRegOrRegClass(_)
+ | Definition::InlineAsmOperand(_) => None,
}
.map(Definition::from)
}
@@ -672,7 +674,9 @@ fn filename_and_frag_for_def(
| Definition::BuiltinAttr(_)
| Definition::BuiltinLifetime(_)
| Definition::ToolModule(_)
- | Definition::DeriveHelper(_) => return None,
+ | Definition::DeriveHelper(_)
+ | Definition::InlineAsmRegOrRegClass(_)
+ | Definition::InlineAsmOperand(_) => return None,
};
Some((def, res, None))
diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs
index 4be1b57098..14781b2129 100644
--- a/crates/ide/src/moniker.rs
+++ b/crates/ide/src/moniker.rs
@@ -223,11 +223,12 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati
Variable
}
}
- Definition::Label(..) => 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,
Definition::ExternCrateDecl(..) => Module,
+ Definition::InlineAsmRegOrRegClass(..) => Module,
}
}
@@ -320,7 +321,9 @@ pub(crate) fn def_to_moniker(
| Definition::DeriveHelper(_)
| Definition::BuiltinLifetime(_)
| Definition::BuiltinAttr(_)
- | Definition::ToolModule(_) => return None,
+ | Definition::ToolModule(_)
+ | Definition::InlineAsmRegOrRegClass(_)
+ | Definition::InlineAsmOperand(_) => return None,
Definition::Local(local) => {
if !local.is_param(db) {
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index 9ace9fda62..9bc7bf411f 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -237,11 +237,13 @@ impl TryToNav for Definition {
Definition::Trait(it) => it.try_to_nav(db),
Definition::TraitAlias(it) => it.try_to_nav(db),
Definition::TypeAlias(it) => it.try_to_nav(db),
- Definition::ExternCrateDecl(it) => Some(it.try_to_nav(db)?),
+ Definition::ExternCrateDecl(it) => it.try_to_nav(db),
+ Definition::InlineAsmOperand(it) => it.try_to_nav(db),
Definition::BuiltinLifetime(_)
| Definition::BuiltinType(_)
| Definition::TupleField(_)
| Definition::ToolModule(_)
+ | Definition::InlineAsmRegOrRegClass(_)
| Definition::BuiltinAttr(_) => None,
// FIXME: The focus range should be set to the helper declaration
Definition::DeriveHelper(it) => it.derive().try_to_nav(db),
@@ -693,6 +695,31 @@ impl TryToNav for hir::ConstParam {
}
}
+impl TryToNav for hir::InlineAsmOperand {
+ fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
+ let InFile { file_id, value } = &self.source(db)?;
+ let file_id = *file_id;
+ Some(orig_range_with_focus(db, file_id, value.syntax(), value.name()).map(
+ |(FileRange { file_id, range: full_range }, focus_range)| {
+ let edition = self.parent(db).module(db).krate().edition(db);
+ NavigationTarget {
+ file_id,
+ name: self
+ .name(db)
+ .map_or_else(|| "_".into(), |it| it.display(db, edition).to_smolstr()),
+ alias: None,
+ kind: Some(SymbolKind::Local),
+ full_range,
+ focus_range,
+ container_name: None,
+ description: None,
+ docs: None,
+ }
+ },
+ ))
+ }
+}
+
#[derive(Debug)]
pub struct UpmappingResult<T> {
/// The macro call site.
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index 42b7472c64..e46cb5a781 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -2976,6 +2976,62 @@ fn test() {
}
#[test]
+ fn asm_operand() {
+ check(
+ "bose",
+ r#"
+//- minicore: asm
+fn test() {
+ core::arch::asm!(
+ "push {base}",
+ base$0 = const 0
+ );
+}
+"#,
+ r#"
+fn test() {
+ core::arch::asm!(
+ "push {bose}",
+ bose = const 0
+ );
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn asm_operand2() {
+ check(
+ "bose",
+ r#"
+//- minicore: asm
+fn test() {
+ core::arch::asm!(
+ "push {base$0}",
+ "push {base}",
+ boo = const 0,
+ virtual_free = sym VIRTUAL_FREE,
+ base = const 0,
+ boo = const 0,
+ );
+}
+"#,
+ r#"
+fn test() {
+ core::arch::asm!(
+ "push {bose}",
+ "push {bose}",
+ boo = const 0,
+ virtual_free = sym VIRTUAL_FREE,
+ bose = const 0,
+ boo = const 0,
+ );
+}
+"#,
+ );
+ }
+
+ #[test]
fn rename_path_inside_use_tree() {
check(
"Baz",
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs
index 518e714547..7234108701 100644
--- a/crates/ide/src/syntax_highlighting/format.rs
+++ b/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/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index eeba9cf35c..96375937a1 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -534,6 +534,10 @@ pub(super) fn highlight_def(
Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)),
Definition::ToolModule(_) => Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)),
Definition::DeriveHelper(_) => Highlight::new(HlTag::Symbol(SymbolKind::DeriveHelper)),
+ Definition::InlineAsmRegOrRegClass(_) => {
+ Highlight::new(HlTag::Symbol(SymbolKind::InlineAsmRegOrRegClass))
+ }
+ Definition::InlineAsmOperand(_) => Highlight::new(HlTag::Symbol(SymbolKind::Local)),
};
let def_crate = def.krate(db);
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index bc1ec53007..5583f1bc8d 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -315,6 +315,8 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag {
Definition::BuiltinAttr(_) => SymbolKind::BuiltinAttr,
Definition::ToolModule(_) => SymbolKind::ToolModule,
Definition::DeriveHelper(_) => SymbolKind::DeriveHelper,
+ Definition::InlineAsmRegOrRegClass(_) => SymbolKind::InlineAsmRegOrRegClass,
+ Definition::InlineAsmOperand(_) => SymbolKind::Local,
};
HlTag::Symbol(symbol)
}
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index e329023606..3b5d1af0ac 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -146,6 +146,7 @@ impl HlTag {
SymbolKind::Field => "field",
SymbolKind::Function => "function",
SymbolKind::Impl => "self_type",
+ SymbolKind::InlineAsmRegOrRegClass => "reg",
SymbolKind::Label => "label",
SymbolKind::LifetimeParam => "lifetime",
SymbolKind::Local => "variable",
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html
new file mode 100644
index 0000000000..d830a38872
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html
@@ -0,0 +1,97 @@
+
+<style>
+body { margin: 0; }
+pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
+
+.lifetime { color: #DFAF8F; font-style: italic; }
+.label { color: #DFAF8F; font-style: italic; }
+.comment { color: #7F9F7F; }
+.documentation { color: #629755; }
+.intra_doc_link { font-style: italic; }
+.injected { opacity: 0.65 ; }
+.struct, .enum { color: #7CB8BB; }
+.enum_variant { color: #BDE0F3; }
+.string_literal { color: #CC9393; }
+.field { color: #94BFF3; }
+.function { color: #93E0E3; }
+.function.unsafe { color: #BC8383; }
+.trait.unsafe { color: #BC8383; }
+.operator.unsafe { color: #BC8383; }
+.mutable.unsafe { color: #BC8383; text-decoration: underline; }
+.keyword.unsafe { color: #BC8383; font-weight: bold; }
+.macro.unsafe { color: #BC8383; }
+.parameter { color: #94BFF3; }
+.text { color: #DCDCCC; }
+.type { color: #7CB8BB; }
+.builtin_type { color: #8CD0D3; }
+.type_param { color: #DFAF8F; }
+.attribute { color: #94BFF3; }
+.numeric_literal { color: #BFEBBF; }
+.bool_literal { color: #BFE6EB; }
+.macro { color: #94BFF3; }
+.proc_macro { color: #94BFF3; text-decoration: underline; }
+.derive { color: #94BFF3; font-style: italic; }
+.module { color: #AFD8AF; }
+.value_param { color: #DCDCCC; }
+.variable { color: #DCDCCC; }
+.format_specifier { color: #CC696B; }
+.mutable { text-decoration: underline; }
+.escape_sequence { color: #94BFF3; }
+.keyword { color: #F0DFAF; font-weight: bold; }
+.control { font-style: italic; }
+.reference { font-style: italic; font-weight: bold; }
+.const { font-weight: bolder; }
+
+.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
+.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
+</style>
+<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
+ <span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
+ <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 = </span><span class="variable">O</span><span class="string_literal macro">pLoad _ {</span><span class="variable">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 {</span><span class="variable">1</span><span class="string_literal macro">} %result</span><span class="variable">"</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 {</span><span class="variable">0</span><span class="string_literal macro">}, gs:[0x30]
+ mov {</span><span class="variable">0</span><span class="string_literal macro">}, [{</span><span class="variable">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>
+ <span class="keyword const">const</span> <span class="constant const declaration">MEM_RELEASE</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
+ <span class="keyword">static</span> <span class="static declaration">VirtualFree</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">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 {</span><span class="variable">free_type</span><span class="string_literal macro">}
+ push {</span><span class="variable">free_size</span><span class="string_literal macro">}
+ push {</span><span class="variable">base</span><span class="string_literal macro">}
+
+ mov eax, fs:[30h]
+ mov eax, [eax+8h]
+ add eax, {</span><span class="variable">off_fn</span><span class="string_literal macro">}
+ mov [eax-{</span><span class="variable">off_fn</span><span class="string_literal macro">}+{</span><span class="variable">off_ptr</span><span class="string_literal macro">}], eax
+
+ push eax
+
+ jmp {</span><span class="variable">virtual_free</span><span class="string_literal macro">}
+ "</span><span class="comma macro">,</span>
+ <span class="variable 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="variable 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 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 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 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 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>
+<span class="brace">}</span></code></pre> \ No newline at end of file
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 cb47fc68bc..d5b9fc0e2c 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -165,16 +165,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<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 default_library library">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="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="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 {</span><span class="variable">0</span><span class="string_literal macro">}, {</span><span class="variable">1</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
+ <span class="string_literal macro">"add {</span><span class="variable">0</span><span class="string_literal macro">}, 5</span><span class="variable">"</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>
<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/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index c06ea155fb..f47b2115bf 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -549,7 +549,7 @@ fn main() {
toho!("{}fmt", 0);
let i: u64 = 3;
let o: u64;
- asm!(
+ core::arch::asm!(
"mov {0}, {1}",
"add {0}, 5",
out(reg) o,
@@ -1275,3 +1275,64 @@ fn f<'de, T: Deserialize<'de>>() {
);
let _ = analysis.highlight(HL_CONFIG, file_id).unwrap();
}
+
+#[test]
+fn test_asm_highlighting() {
+ check_highlighting(
+ r#"
+//- minicore: asm, concat
+fn main() {
+ unsafe {
+ let foo = 1;
+ let mut o = 0;
+ core::arch::asm!(
+ "%input = OpLoad _ {0}",
+ concat!("%result = ", "bar", " _ %input"),
+ "OpStore {1} %result",
+ in(reg) &foo,
+ in(reg) &mut o,
+ );
+
+ let thread_id: usize;
+ core::arch::asm!("
+ mov {0}, gs:[0x30]
+ mov {0}, [{0}+0x48]
+ ", out(reg) thread_id, options(pure, readonly, nostack));
+
+ static UNMAP_BASE: usize;
+ const MEM_RELEASE: usize;
+ static VirtualFree: usize;
+ const OffPtr: usize;
+ const OffFn: usize;
+ core::arch::asm!("
+ push {free_type}
+ push {free_size}
+ push {base}
+
+ mov eax, fs:[30h]
+ mov eax, [eax+8h]
+ add eax, {off_fn}
+ mov [eax-{off_fn}+{off_ptr}], eax
+
+ push eax
+
+ jmp {virtual_free}
+ ",
+ off_ptr = const OffPtr,
+ off_fn = const OffFn,
+
+ free_size = const 0,
+ free_type = const MEM_RELEASE,
+
+ virtual_free = sym VirtualFree,
+
+ base = sym UNMAP_BASE,
+ options(noreturn),
+ );
+ }
+}
+"#,
+ expect_file!["./test_data/highlight_asm.html"],
+ false,
+ );
+}
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index a678c1f3a7..39ca26fc50 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -245,7 +245,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
// test builtin_expr
// fn foo() {
-// builtin#asm(0);
+// builtin#asm("");
// builtin#format_args("", 0, 1, a = 2 + 3, a + b);
// builtin#offset_of(Foo, bar.baz.0);
// }
@@ -297,18 +297,183 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
p.expect(T![')']);
Some(m.complete(p, FORMAT_ARGS_EXPR))
} else if p.at_contextual_kw(T![asm]) {
- p.bump_remap(T![asm]);
- p.expect(T!['(']);
- // FIXME: We just put expression here so highlighting kind of keeps working
- expr(p);
- p.expect(T![')']);
- Some(m.complete(p, ASM_EXPR))
+ parse_asm_expr(p, m)
} else {
m.abandon(p);
None
}
}
+// test asm_expr
+// fn foo() {
+// builtin#asm(
+// "mov {tmp}, {x}",
+// "shl {tmp}, 1",
+// "shl {x}, 2",
+// "add {x}, {tmp}",
+// x = inout(reg) x,
+// tmp = out(reg) _,
+// );
+// }
+fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
+ p.bump_remap(T![asm]);
+ p.expect(T!['(']);
+ if expr(p).is_none() {
+ p.err_and_bump("expected asm template");
+ }
+ let mut allow_templates = true;
+ while !p.at(EOF) && !p.at(T![')']) {
+ p.expect(T![,]);
+ // accept trailing commas
+ if p.at(T![')']) {
+ break;
+ }
+
+ let op_n = p.start();
+ // Parse clobber_abi
+ if p.eat_contextual_kw(T![clobber_abi]) {
+ parse_clobber_abi(p);
+ op_n.complete(p, ASM_CLOBBER_ABI);
+ allow_templates = false;
+ continue;
+ }
+
+ // Parse options
+ if p.eat_contextual_kw(T![options]) {
+ parse_options(p);
+ op_n.complete(p, ASM_OPTIONS);
+ allow_templates = false;
+ continue;
+ }
+
+ // Parse operand names
+ if p.at(T![ident]) && p.nth_at(1, T![=]) {
+ name(p);
+ p.bump(T![=]);
+ allow_templates = false;
+ true
+ } else {
+ 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);
+ expr(p);
+ if p.eat(T![=>]) {
+ 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_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");
+ }
+ continue;
+ } else {
+ dir_spec.abandon(p);
+ op.abandon(p);
+ op_n.abandon(p);
+ p.err_and_bump("expected asm operand");
+ if p.at(T!['}']) {
+ break;
+ }
+ continue;
+ };
+ allow_templates = false;
+ }
+ p.expect(T![')']);
+ Some(m.complete(p, ASM_EXPR))
+}
+
+fn parse_options(p: &mut Parser<'_>) {
+ p.expect(T!['(']);
+
+ while !p.eat(T![')']) && !p.at(EOF) {
+ const OPTIONS: &[SyntaxKind] = &[
+ T![pure],
+ T![nomem],
+ T![readonly],
+ T![preserves_flags],
+ T![noreturn],
+ T![nostack],
+ T![may_unwind],
+ T![att_syntax],
+ T![raw],
+ ];
+ let m = p.start();
+ if !OPTIONS.iter().any(|&syntax| p.eat_contextual_kw(syntax)) {
+ p.err_and_bump("expected asm option");
+ continue;
+ }
+ m.complete(p, ASM_OPTION);
+
+ // Allow trailing commas
+ if p.eat(T![')']) {
+ break;
+ }
+ p.expect(T![,]);
+ }
+}
+
+fn parse_clobber_abi(p: &mut Parser<'_>) {
+ p.expect(T!['(']);
+
+ while !p.eat(T![')']) && !p.at(EOF) {
+ if !p.expect(T![string]) {
+ break;
+ }
+
+ // Allow trailing commas
+ if p.eat(T![')']) {
+ break;
+ }
+ p.expect(T![,]);
+ }
+}
+
+fn parse_reg(p: &mut Parser<'_>) {
+ p.expect(T!['(']);
+ if p.at(T![ident]) {
+ let m = p.start();
+ name_ref(p);
+ m.complete(p, ASM_REG_SPEC);
+ } else if p.at(T![string]) {
+ let m = p.start();
+ p.bump_any();
+ m.complete(p, ASM_REG_SPEC);
+ } else {
+ p.err_and_bump("expected register name");
+ }
+ p.expect(T![')']);
+}
+
// test array_expr
// fn foo() {
// [];
diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs
index 7d3eb5de25..f6b3783d1c 100644
--- a/crates/parser/src/parser.rs
+++ b/crates/parser/src/parser.rs
@@ -131,6 +131,14 @@ impl<'t> Parser<'t> {
true
}
+ pub(crate) fn eat_contextual_kw(&mut self, kind: SyntaxKind) -> bool {
+ if !self.at_contextual_kw(kind) {
+ return false;
+ }
+ self.bump_remap(kind);
+ true
+ }
+
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 00f212487a..288a07ef44 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -111,16 +111,32 @@ pub enum SyntaxKind {
YIELD_KW,
ASM_KW,
ASYNC_KW,
+ ATT_SYNTAX_KW,
AUTO_KW,
AWAIT_KW,
BUILTIN_KW,
+ CLOBBER_ABI_KW,
DEFAULT_KW,
DYN_KW,
FORMAT_ARGS_KW,
GEN_KW,
+ INLATEOUT_KW,
+ INOUT_KW,
+ LABEL_KW,
+ LATEOUT_KW,
MACRO_RULES_KW,
+ MAY_UNWIND_KW,
+ NOMEM_KW,
+ NORETURN_KW,
+ NOSTACK_KW,
OFFSET_OF_KW,
+ OPTIONS_KW,
+ OUT_KW,
+ PRESERVES_FLAGS_KW,
+ PURE_KW,
RAW_KW,
+ READONLY_KW,
+ SYM_KW,
TRY_KW,
UNION_KW,
YEET_KW,
@@ -146,7 +162,20 @@ pub enum SyntaxKind {
ARG_LIST,
ARRAY_EXPR,
ARRAY_TYPE,
+ ASM_CLOBBER_ABI,
+ ASM_CONST,
+ ASM_DIR_SPEC,
ASM_EXPR,
+ ASM_LABEL,
+ ASM_OPERAND,
+ ASM_OPERAND_EXPR,
+ ASM_OPERAND_NAMED,
+ ASM_OPTION,
+ ASM_OPTIONS,
+ ASM_PIECE,
+ ASM_REG_OPERAND,
+ ASM_REG_SPEC,
+ ASM_SYM,
ASSOC_ITEM,
ASSOC_ITEM_LIST,
ASSOC_TYPE_ARG,
@@ -364,14 +393,30 @@ impl SyntaxKind {
pub fn is_contextual_keyword(self, edition: Edition) -> bool {
match self {
ASM_KW => true,
+ ATT_SYNTAX_KW => true,
AUTO_KW => true,
BUILTIN_KW => true,
+ CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
FORMAT_ARGS_KW => true,
+ INLATEOUT_KW => true,
+ INOUT_KW => true,
+ LABEL_KW => true,
+ LATEOUT_KW => true,
MACRO_RULES_KW => true,
+ MAY_UNWIND_KW => true,
+ NOMEM_KW => true,
+ NORETURN_KW => true,
+ NOSTACK_KW => true,
OFFSET_OF_KW => true,
+ OPTIONS_KW => true,
+ OUT_KW => true,
+ PRESERVES_FLAGS_KW => true,
+ PURE_KW => true,
RAW_KW => true,
+ READONLY_KW => true,
+ SYM_KW => true,
UNION_KW => true,
YEET_KW => true,
_ => false,
@@ -435,14 +480,30 @@ impl SyntaxKind {
GEN_KW if Edition::Edition2024 <= edition => true,
TRY_KW if Edition::Edition2018 <= edition => true,
ASM_KW => true,
+ ATT_SYNTAX_KW => true,
AUTO_KW => true,
BUILTIN_KW => true,
+ CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
FORMAT_ARGS_KW => true,
+ INLATEOUT_KW => true,
+ INOUT_KW => true,
+ LABEL_KW => true,
+ LATEOUT_KW => true,
MACRO_RULES_KW => true,
+ MAY_UNWIND_KW => true,
+ NOMEM_KW => true,
+ NORETURN_KW => true,
+ NOSTACK_KW => true,
OFFSET_OF_KW => true,
+ OPTIONS_KW => true,
+ OUT_KW => true,
+ PRESERVES_FLAGS_KW => true,
+ PURE_KW => true,
RAW_KW => true,
+ READONLY_KW => true,
+ SYM_KW => true,
UNION_KW => true,
YEET_KW => true,
_ => false,
@@ -580,14 +641,30 @@ impl SyntaxKind {
pub fn from_contextual_keyword(ident: &str, edition: Edition) -> Option<SyntaxKind> {
let kw = match ident {
"asm" => ASM_KW,
+ "att_syntax" => ATT_SYNTAX_KW,
"auto" => AUTO_KW,
"builtin" => BUILTIN_KW,
+ "clobber_abi" => CLOBBER_ABI_KW,
"default" => DEFAULT_KW,
"dyn" if edition < Edition::Edition2018 => DYN_KW,
"format_args" => FORMAT_ARGS_KW,
+ "inlateout" => INLATEOUT_KW,
+ "inout" => INOUT_KW,
+ "label" => LABEL_KW,
+ "lateout" => LATEOUT_KW,
"macro_rules" => MACRO_RULES_KW,
+ "may_unwind" => MAY_UNWIND_KW,
+ "nomem" => NOMEM_KW,
+ "noreturn" => NORETURN_KW,
+ "nostack" => NOSTACK_KW,
"offset_of" => OFFSET_OF_KW,
+ "options" => OPTIONS_KW,
+ "out" => OUT_KW,
+ "preserves_flags" => PRESERVES_FLAGS_KW,
+ "pure" => PURE_KW,
"raw" => RAW_KW,
+ "readonly" => READONLY_KW,
+ "sym" => SYM_KW,
"union" => UNION_KW,
"yeet" => YEET_KW,
_ => return None,
@@ -630,4 +707,4 @@ impl SyntaxKind {
}
}
#[macro_export]
-macro_rules ! T { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
+macro_rules ! T { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [label] => { $ crate :: SyntaxKind :: LABEL_KW } ; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [may_unwind] => { $ crate :: SyntaxKind :: MAY_UNWIND_KW } ; [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW } ; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW } ; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW } ; [out] => { $ crate :: SyntaxKind :: OUT_KW } ; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW } ; [pure] => { $ crate :: SyntaxKind :: PURE_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [sym] => { $ crate :: SyntaxKind :: SYM_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs
index 9ce5a2ae74..164d0f36f1 100644
--- a/crates/parser/test_data/generated/runner.rs
+++ b/crates/parser/test_data/generated/runner.rs
@@ -19,6 +19,8 @@ mod ok {
#[test]
fn as_precedence() { run_and_expect_no_errors("test_data/parser/inline/ok/as_precedence.rs"); }
#[test]
+ fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); }
+ #[test]
fn assoc_const_eq() {
run_and_expect_no_errors("test_data/parser/inline/ok/assoc_const_eq.rs");
}
diff --git a/crates/parser/test_data/parser/inline/ok/asm_expr.rast b/crates/parser/test_data/parser/inline/ok/asm_expr.rast
new file mode 100644
index 0000000000..4afa9daf59
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/asm_expr.rast
@@ -0,0 +1,83 @@
+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"
+ POUND "#"
+ ASM_KW "asm"
+ L_PAREN "("
+ WHITESPACE "\n "
+ LITERAL
+ STRING "\"mov {tmp}, {x}\""
+ COMMA ","
+ WHITESPACE "\n "
+ LITERAL
+ STRING "\"shl {tmp}, 1\""
+ COMMA ","
+ WHITESPACE "\n "
+ LITERAL
+ STRING "\"shl {x}, 2\""
+ COMMA ","
+ WHITESPACE "\n "
+ LITERAL
+ STRING "\"add {x}, {tmp}\""
+ COMMA ","
+ WHITESPACE "\n "
+ ASM_OPERAND_NAMED
+ NAME
+ IDENT "x"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ 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_OPERAND_NAMED
+ NAME
+ IDENT "tmp"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ 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 ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/asm_expr.rs b/crates/parser/test_data/parser/inline/ok/asm_expr.rs
new file mode 100644
index 0000000000..0906cd3e71
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/asm_expr.rs
@@ -0,0 +1,10 @@
+fn foo() {
+ builtin#asm(
+ "mov {tmp}, {x}",
+ "shl {tmp}, 1",
+ "shl {x}, 2",
+ "add {x}, {tmp}",
+ x = inout(reg) x,
+ tmp = out(reg) _,
+ );
+}
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 361900b6d3..19a84ac540 100644
--- a/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
+++ b/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
@@ -19,7 +19,7 @@ SOURCE_FILE
ASM_KW "asm"
L_PAREN "("
LITERAL
- INT_NUMBER "0"
+ STRING "\"\""
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n "
diff --git a/crates/parser/test_data/parser/inline/ok/builtin_expr.rs b/crates/parser/test_data/parser/inline/ok/builtin_expr.rs
index 14431b0210..920d0f794f 100644
--- a/crates/parser/test_data/parser/inline/ok/builtin_expr.rs
+++ b/crates/parser/test_data/parser/inline/ok/builtin_expr.rs
@@ -1,5 +1,5 @@
fn foo() {
- builtin#asm(0);
+ builtin#asm("");
builtin#format_args("", 0, 1, a = 2 + 3, a + b);
builtin#offset_of(Foo, bar.baz.0);
}
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index b29268f133..4902c9f88c 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -80,6 +80,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
| SymbolKind::ValueParam
| SymbolKind::Label => lsp_types::SymbolKind::VARIABLE,
SymbolKind::Union => lsp_types::SymbolKind::STRUCT,
+ SymbolKind::InlineAsmRegOrRegClass => lsp_types::SymbolKind::VARIABLE,
}
}
@@ -159,6 +160,7 @@ pub(crate) fn completion_item_kind(
SymbolKind::Variant => lsp_types::CompletionItemKind::ENUM_MEMBER,
SymbolKind::BuiltinAttr => lsp_types::CompletionItemKind::FUNCTION,
SymbolKind::ToolModule => lsp_types::CompletionItemKind::MODULE,
+ SymbolKind::InlineAsmRegOrRegClass => lsp_types::CompletionItemKind::KEYWORD,
},
}
}
@@ -702,6 +704,7 @@ fn semantic_token_type_and_modifiers(
SymbolKind::ProcMacro => types::PROC_MACRO,
SymbolKind::BuiltinAttr => types::BUILTIN_ATTRIBUTE,
SymbolKind::ToolModule => types::TOOL_MODULE,
+ SymbolKind::InlineAsmRegOrRegClass => types::KEYWORD,
},
HlTag::AttributeBracket => types::ATTRIBUTE_BRACKET,
HlTag::BoolLiteral => types::BOOLEAN,
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 43375ce6ae..52ad439e4d 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -391,8 +391,33 @@ Expr =
OffsetOfExpr =
Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')'
+// asm := "asm!(" format_string *("," format_string) *("," operand) [","] ")"
+// global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")"
+// format_string := STRING_LITERAL / RAW_STRING_LITERAL
AsmExpr =
- Attr* 'builtin' '#' 'asm' '(' Expr ')'
+ Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')'
+
+// operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
+AsmOperandExpr = in_expr:Expr ('=>' out_expr:Expr)?
+// dir_spec := "in" / "out" / "lateout" / "inout" / "inlateout"
+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 = 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"
+AsmOption = 'pure' | 'nomem' | 'readonly' | 'preserves_flags' | 'noreturn' | 'nostack' | 'att_syntax' | 'raw' | 'may_unwind'
+// options := "options(" option *("," option) [","] ")"
+AsmOptions = 'options' '(' AsmOption *(',' AsmOption) ','? ')'
+AsmLabel = 'label' BlockExpr
+AsmSym = 'sym' Path
+AsmConst = 'const' Expr
+// operand := reg_operand / clobber_abi / options
+AsmOperand = AsmRegOperand | AsmLabel | AsmSym | AsmConst
+AsmOperandNamed = (Name '=')? AsmOperand
+AsmPiece = AsmOperandNamed | AsmClobberAbi | AsmOptions
FormatArgsExpr =
Attr* 'builtin' '#' 'format_args' '('
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index c9b39e9922..c81a19f3bd 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -65,13 +65,62 @@ impl ArrayType {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct AsmClobberAbi {
+ pub(crate) syntax: SyntaxNode,
+}
+impl AsmClobberAbi {
+ #[inline]
+ 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 clobber_abi_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![clobber_abi])
+ }
+ #[inline]
+ pub fn string_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![string]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct AsmConst {
+ pub(crate) syntax: SyntaxNode,
+}
+impl AsmConst {
+ #[inline]
+ pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+ #[inline]
+ pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct AsmDirSpec {
+ pub(crate) syntax: SyntaxNode,
+}
+impl AsmDirSpec {
+ #[inline]
+ pub fn in_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![in]) }
+ #[inline]
+ pub fn inlateout_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![inlateout])
+ }
+ #[inline]
+ pub fn inout_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![inout]) }
+ #[inline]
+ pub fn lateout_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![lateout]) }
+ #[inline]
+ pub fn out_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![out]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AsmExpr {
pub(crate) syntax: SyntaxNode,
}
impl ast::HasAttrs for AsmExpr {}
impl AsmExpr {
#[inline]
- pub fn expr(&self) -> Option<Expr> { support::child(&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]
pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) }
#[inline]
@@ -79,12 +128,143 @@ impl AsmExpr {
#[inline]
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
#[inline]
+ pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+ #[inline]
pub fn asm_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![asm]) }
#[inline]
pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct AsmLabel {
+ pub(crate) syntax: SyntaxNode,
+}
+impl AsmLabel {
+ #[inline]
+ pub fn block_expr(&self) -> Option<BlockExpr> { support::child(&self.syntax) }
+ #[inline]
+ pub fn label_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![label]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct AsmOperandExpr {
+ pub(crate) syntax: SyntaxNode,
+}
+impl AsmOperandExpr {
+ #[inline]
+ pub fn in_expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+ #[inline]
+ pub fn out_expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+ #[inline]
+ pub fn fat_arrow_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=>]) }
+}
+
+#[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,
+}
+impl AsmOption {
+ #[inline]
+ pub fn att_syntax_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![att_syntax])
+ }
+ #[inline]
+ pub fn may_unwind_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![may_unwind])
+ }
+ #[inline]
+ pub fn nomem_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![nomem]) }
+ #[inline]
+ pub fn noreturn_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![noreturn])
+ }
+ #[inline]
+ pub fn nostack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![nostack]) }
+ #[inline]
+ pub fn preserves_flags_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![preserves_flags])
+ }
+ #[inline]
+ pub fn pure_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![pure]) }
+ #[inline]
+ pub fn raw_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![raw]) }
+ #[inline]
+ pub fn readonly_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![readonly])
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct AsmOptions {
+ pub(crate) syntax: SyntaxNode,
+}
+impl AsmOptions {
+ #[inline]
+ pub fn asm_option(&self) -> Option<AsmOption> { support::child(&self.syntax) }
+ #[inline]
+ pub fn asm_options(&self) -> AstChildren<AsmOption> { support::children(&self.syntax) }
+ #[inline]
+ 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 comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+ #[inline]
+ pub fn options_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![options]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct AsmRegOperand {
+ pub(crate) syntax: SyntaxNode,
+}
+impl AsmRegOperand {
+ #[inline]
+ pub fn asm_dir_spec(&self) -> Option<AsmDirSpec> { support::child(&self.syntax) }
+ #[inline]
+ pub fn asm_operand_expr(&self) -> Option<AsmOperandExpr> { support::child(&self.syntax) }
+ #[inline]
+ pub fn asm_reg_spec(&self) -> Option<AsmRegSpec> { support::child(&self.syntax) }
+ #[inline]
+ 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![')']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct AsmRegSpec {
+ pub(crate) syntax: SyntaxNode,
+}
+impl AsmRegSpec {
+ #[inline]
+ pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
+ #[inline]
+ pub fn string_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![string]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct AsmSym {
+ pub(crate) syntax: SyntaxNode,
+}
+impl AsmSym {
+ #[inline]
+ pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
+ #[inline]
+ pub fn sym_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![sym]) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AssocItemList {
pub(crate) syntax: SyntaxNode,
}
@@ -2052,6 +2232,21 @@ impl ast::HasName for Adt {}
impl ast::HasVisibility for Adt {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum AsmOperand {
+ AsmConst(AsmConst),
+ AsmLabel(AsmLabel),
+ 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),
@@ -2316,6 +2511,48 @@ impl AstNode for ArrayType {
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for AsmClobberAbi {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CLOBBER_ABI }
+ #[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 AsmConst {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CONST }
+ #[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 AsmDirSpec {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_DIR_SPEC }
+ #[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 AsmExpr {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_EXPR }
@@ -2330,6 +2567,118 @@ impl AstNode for AsmExpr {
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for AsmLabel {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_LABEL }
+ #[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 AsmOperandExpr {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_EXPR }
+ #[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 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 }
+ #[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 AsmOptions {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTIONS }
+ #[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 AsmRegOperand {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_OPERAND }
+ #[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 AsmRegSpec {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_SPEC }
+ #[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 AsmSym {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_SYM }
+ #[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 AssocItemList {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_ITEM_LIST }
@@ -4268,6 +4617,84 @@ impl AstNode for Adt {
}
}
}
+impl From<AsmConst> for AsmOperand {
+ #[inline]
+ fn from(node: AsmConst) -> AsmOperand { AsmOperand::AsmConst(node) }
+}
+impl From<AsmLabel> for AsmOperand {
+ #[inline]
+ fn from(node: AsmLabel) -> AsmOperand { AsmOperand::AsmLabel(node) }
+}
+impl From<AsmRegOperand> for AsmOperand {
+ #[inline]
+ fn from(node: AsmRegOperand) -> AsmOperand { AsmOperand::AsmRegOperand(node) }
+}
+impl From<AsmSym> for AsmOperand {
+ #[inline]
+ fn from(node: AsmSym) -> AsmOperand { AsmOperand::AsmSym(node) }
+}
+impl AstNode for AsmOperand {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool {
+ matches!(kind, ASM_CONST | ASM_LABEL | ASM_REG_OPERAND | ASM_SYM)
+ }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ ASM_CONST => AsmOperand::AsmConst(AsmConst { syntax }),
+ ASM_LABEL => AsmOperand::AsmLabel(AsmLabel { syntax }),
+ ASM_REG_OPERAND => AsmOperand::AsmRegOperand(AsmRegOperand { syntax }),
+ ASM_SYM => AsmOperand::AsmSym(AsmSym { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ AsmOperand::AsmConst(it) => &it.syntax,
+ AsmOperand::AsmLabel(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) }
@@ -5803,7 +6230,8 @@ impl AstNode for AnyHasName {
fn can_cast(kind: SyntaxKind) -> bool {
matches!(
kind,
- CONST
+ ASM_OPERAND_NAMED
+ | CONST
| CONST_PARAM
| ENUM
| FN
@@ -5832,6 +6260,10 @@ impl AstNode for AnyHasName {
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl From<AsmOperandNamed> for AnyHasName {
+ #[inline]
+ fn from(node: AsmOperandNamed) -> AnyHasName { AnyHasName { syntax: node.syntax } }
+}
impl From<Const> for AnyHasName {
#[inline]
fn from(node: Const) -> AnyHasName { AnyHasName { syntax: node.syntax } }
@@ -6072,6 +6504,16 @@ impl std::fmt::Display for Adt {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for AsmOperand {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ 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)
@@ -6142,11 +6584,66 @@ impl std::fmt::Display for ArrayType {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for AsmClobberAbi {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for AsmConst {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for AsmDirSpec {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for AsmExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for AsmLabel {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for AsmOperandExpr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ 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)
+ }
+}
+impl std::fmt::Display for AsmOptions {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for AsmRegOperand {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for AsmRegSpec {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for AsmSym {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for AssocItemList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 4b7e23388c..fec270a556 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -1475,6 +1475,19 @@ mod panicking {
}
// endregion:panic
+// region:asm
+mod arch {
+ #[rustc_builtin_macro]
+ pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) {
+ /* compiler built-in */
+ }
+ #[rustc_builtin_macro]
+ pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) {
+ /* compiler built-in */
+ }
+}
+// endregion:asm
+
#[macro_use]
mod macros {
// region:panic
@@ -1487,16 +1500,6 @@ mod macros {
}
// endregion:panic
- // region:asm
- #[macro_export]
- #[rustc_builtin_macro]
- macro_rules! asm {
- ($($arg:tt)*) => {
- /* compiler built-in */
- };
- }
- // endregion:asm
-
// region:assert
#[macro_export]
#[rustc_builtin_macro]
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index 192de86947..4bc1821ee5 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -19,6 +19,7 @@ stdx.workspace = true
proc-macro2 = "1.0.47"
quote = "1.0.20"
ungrammar = "1.16.1"
+either.workspace = true
itertools.workspace = true
# Avoid adding more dependencies to this crate
diff --git a/xtask/src/codegen/grammar.rs b/xtask/src/codegen/grammar.rs
index 39e06f9642..e7534582f2 100644
--- a/xtask/src/codegen/grammar.rs
+++ b/xtask/src/codegen/grammar.rs
@@ -11,9 +11,11 @@ use std::{
fs,
};
+use either::Either;
use itertools::Itertools;
use proc_macro2::{Punct, Spacing};
use quote::{format_ident, quote};
+use stdx::panic_context;
use ungrammar::{Grammar, Rule};
use crate::{
@@ -462,6 +464,7 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
let tokens = grammar.tokens.iter().map(|name| format_ident!("{}", name)).collect::<Vec<_>>();
+ // FIXME: This generates enum kinds?
let nodes = grammar.nodes.iter().map(|name| format_ident!("{}", name)).collect::<Vec<_>>();
let ast = quote! {
@@ -711,6 +714,7 @@ fn lower(grammar: &Grammar) -> AstSrc {
for &node in &nodes {
let name = grammar[node].name.clone();
let rule = &grammar[node].rule;
+ let _g = panic_context::enter(name.clone());
match lower_enum(grammar, rule) {
Some(variants) => {
let enum_src = AstEnumSrc { doc: Vec::new(), name, traits: Vec::new(), variants };
@@ -838,11 +842,16 @@ fn lower_separated_list(
Rule::Seq(it) => it,
_ => return false,
};
- let (node, repeat, trailing_sep) = match rule.as_slice() {
+
+ let (nt, repeat, trailing_sep) = match rule.as_slice() {
[Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => {
- (node, repeat, Some(trailing_sep))
+ (Either::Left(node), repeat, Some(trailing_sep))
+ }
+ [Rule::Node(node), Rule::Rep(repeat)] => (Either::Left(node), repeat, None),
+ [Rule::Token(token), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => {
+ (Either::Right(token), repeat, Some(trailing_sep))
}
- [Rule::Node(node), Rule::Rep(repeat)] => (node, repeat, None),
+ [Rule::Token(token), Rule::Rep(repeat)] => (Either::Right(token), repeat, None),
_ => return false,
};
let repeat = match &**repeat {
@@ -851,15 +860,28 @@ fn lower_separated_list(
};
if !matches!(
repeat.as_slice(),
- [comma, Rule::Node(n)]
- if trailing_sep.map_or(true, |it| comma == &**it) && n == node
+ [comma, nt_]
+ if trailing_sep.map_or(true, |it| comma == &**it) && match (nt, nt_) {
+ (Either::Left(node), Rule::Node(nt_)) => node == nt_,
+ (Either::Right(token), Rule::Token(nt_)) => token == nt_,
+ _ => false,
+ }
) {
return false;
}
- let ty = grammar[*node].name.clone();
- let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));
- let field = Field::Node { name, ty, cardinality: Cardinality::Many };
- acc.push(field);
+ match nt {
+ Either::Right(token) => {
+ let name = clean_token_name(&grammar[*token].name);
+ let field = Field::Token(name);
+ acc.push(field);
+ }
+ Either::Left(node) => {
+ let ty = grammar[*node].name.clone();
+ let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));
+ let field = Field::Node { name, ty, cardinality: Cardinality::Many };
+ acc.push(field);
+ }
+ }
true
}
diff --git a/xtask/src/codegen/grammar/ast_src.rs b/xtask/src/codegen/grammar/ast_src.rs
index 34151bd958..f1a96e0c6a 100644
--- a/xtask/src/codegen/grammar/ast_src.rs
+++ b/xtask/src/codegen/grammar/ast_src.rs
@@ -113,7 +113,31 @@ const RESERVED: &[&str] = &[
const CONTEXTUAL_KEYWORDS: &[&str] =
&["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet"];
// keywords we use for special macro expansions
-const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &["builtin", "offset_of", "format_args", "asm"];
+const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[
+ "asm",
+ "att_syntax",
+ "builtin",
+ "clobber_abi",
+ "format_args",
+ // "in",
+ "inlateout",
+ "inout",
+ "label",
+ "lateout",
+ "may_unwind",
+ "nomem",
+ "noreturn",
+ "nostack",
+ "offset_of",
+ "options",
+ "out",
+ "preserves_flags",
+ "pure",
+ // "raw",
+ "readonly",
+ "sym",
+];
+
// keywords that are keywords depending on the edition
const EDITION_DEPENDENT_KEYWORDS: &[(&str, Edition)] = &[
("try", Edition::Edition2018),