diff --git a/src/tools/rust-analyzer/.typos.toml b/src/tools/rust-analyzer/.typos.toml index e7e764ce03517..febfb233bd979 100644 --- a/src/tools/rust-analyzer/.typos.toml +++ b/src/tools/rust-analyzer/.typos.toml @@ -15,6 +15,7 @@ extend-ignore-re = [ '"flate2"', "raison d'ĂȘtre", "inout", + "INOUT", "optin" ] diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 6eaded6da14ab..85ef3a6ba92ca 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2624,6 +2624,7 @@ version = "0.1.0" dependencies = [ "anyhow", "directories", + "either", "flate2", "itertools", "proc-macro2", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index e55628c8dcf4c..9e972e12118da 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index f5fe8f8770130..34db43939a7ca 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -100,7 +100,14 @@ pub struct BodySourceMap { field_map_back: FxHashMap, pat_field_map_back: FxHashMap, - format_args_template_map: FxHashMap>, + template_map: Option< + Box<( + // format_args! + FxHashMap>, + // asm! + FxHashMap>, + )>, + >, expansions: FxHashMap>, MacroFileId>, @@ -426,7 +433,16 @@ impl BodySourceMap { node: InFile<&ast::FormatArgsExpr>, ) -> Option<&[(syntax::TextRange, Name)]> { let src = node.map(AstPtr::new).map(AstPtr::upcast::); - 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::); + 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, Vec<(tt::TextRange, Name)>>, + FxHashMap, Vec<(tt::TextRange, usize)>>, + )> { + self.template_map.as_deref() + } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 145afc1b610c2..9c547574ecb1f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -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 { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs new file mode 100644 index 0000000000000..bf6ba41482c47 --- /dev/null +++ b/src/tools/rust-analyzer/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, + ) -> ExprId { + let mut clobber_abis = FxIndexSet::default(); + let mut operands = vec![]; + let mut options = AsmOptions::empty(); + + let mut named_pos: FxHashMap = Default::default(); + let mut named_args: FxHashMap = Default::default(); + let mut reg_args: FxHashSet = Default::default(); + for piece in asm.asm_pieces() { + let slot = operands.len(); + let mut lower_reg = |reg: Option| { + 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/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 86fd092603ffd..d9358a28822e7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/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, 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, + late: bool, + }, + InOut { + reg: InlineAsmRegOrRegClass, + expr: ExprId, + late: bool, + }, + SplitInOut { + reg: InlineAsmRegOrRegClass, + in_expr: ExprId, + out_expr: Option, + 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/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index b6dbba12cd6d6..37ae1ab39b494 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 795d9b14df961..15fed45caf02f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/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 { - // 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 { - // Expand to nothing (at item-level) - ExpandResult::ok(quote! {span =>}) -} - fn cfg_expand( db: &dyn ExpandDatabase, id: MacroCallId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 36327d1d49c0a..5cad08b93956e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index b1c793a1e38f6..a82d091cbb028 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index 7fed5f0203bad..8e52725e53600 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 5a6161f94289f..10ab6d3ff8939 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs index 7d52a28b91e91..82c90ac30101b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/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> { + 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/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 81d6466c2f31d..3b8b901fa14e3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -43,7 +43,7 @@ use hir_def::{ body::{BodyDiagnostic, SyntheticSyntax}, data::adt::VariantData, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, - hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, + hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat}, item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode}, lang_item::LangItemTarget, layout::{self, ReprOptions, TargetDataLayout}, @@ -5246,6 +5246,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 { + 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/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index c78b59826c955..0ba0e446578e7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -47,9 +47,9 @@ use crate::{ source_analyzer::{resolve_hir_path, SourceAnalyzer}, Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile, - ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, OverloadedDeref, Path, - ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, - TypeParam, Union, Variant, VariantDef, + InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, + OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, + Type, TypeAlias, TypeParam, Union, Variant, VariantDef, }; const CONTINUE_NO_BREAKS: ControlFlow = 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)>> { + ) -> Option>)>> { 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)> { + ) -> Option<(TextRange, Option>)> { 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)> { + ) -> Option<(TextRange, Option>)> { 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/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index edeb19030ac00..fc629b9c6d077 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -110,7 +110,7 @@ use syntax::{ AstNode, AstPtr, SyntaxNode, }; -use crate::{db::HirDatabase, InFile}; +use crate::{db::HirDatabase, InFile, InlineAsmOperand}; #[derive(Default)] pub(super) struct SourceToDefCache { @@ -273,6 +273,25 @@ impl SourceToDefCtx<'_, '_> { ast::Adt::Union(it) => self.union_to_def(InFile::new(file_id, it)).map(AdtId::UnionId), } } + + pub(super) fn asm_operand_to_def( + &mut self, + src: InFile<&ast::AsmOperandNamed>, + ) -> Option { + let asm = src.value.syntax().parent().and_then(ast::AsmExpr::cast)?; + let index = asm + .asm_pieces() + .filter_map(|it| match it { + ast::AsmPiece::AsmOperandNamed(it) => Some(it), + _ => None, + }) + .position(|it| it == *src.value)?; + let container = self.find_pat_or_label_container(src.syntax_ref())?; + let (_, source_map) = self.db.body_with_source_map(container); + let expr = source_map.node_expr(src.with_value(&ast::Expr::AsmExpr(asm)))?; + Some(InlineAsmOperand { owner: container, expr, index }) + } + pub(super) fn bind_pat_to_def( &mut self, src: InFile<&ast::IdentPat>, diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index be0116862b9cf..f6f1da1b7d6bd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -904,6 +904,20 @@ impl SourceAnalyzer { }) } + pub(crate) fn resolve_offset_in_asm_template( + &self, + asm: InFile<&ast::AsmExpr>, + offset: TextSize, + ) -> Option<(DefWithBodyId, (ExprId, TextRange, usize))> { + let (def, _, body_source_map) = self.def.as_ref()?; + let (expr, args) = body_source_map.asm_template_args(asm)?; + Some(*def).zip( + args.iter() + .find(|(range, _)| range.contains_inclusive(offset)) + .map(|(range, idx)| (expr, *range, *idx)), + ) + } + pub(crate) fn as_format_args_parts<'a>( &'a self, db: &'a dyn HirDatabase, @@ -927,6 +941,14 @@ impl SourceAnalyzer { )) } + pub(crate) fn as_asm_parts( + &self, + asm: InFile<&ast::AsmExpr>, + ) -> Option<(DefWithBodyId, (ExprId, &[(TextRange, usize)]))> { + let (def, _, body_source_map) = self.def.as_ref()?; + Some(*def).zip(body_source_map.asm_template_args(asm)) + } + fn resolve_impl_method_or_trait_def( &self, db: &dyn HirDatabase, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index f8c8b12bd25af..8c97ebd550032 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 5d4b1999088a0..099f26eba78a9 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -10,8 +10,8 @@ use either::Either; use hir::{ Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType, Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field, - Function, GenericParam, HasVisibility, HirDisplay, Impl, Label, Local, Macro, Module, - ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait, + Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro, + Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility, }; use span::Edition; @@ -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 for Definition { } } +impl From for Definition { + fn from(value: InlineAsmOperand) -> Self { + Definition::InlineAsmOperand(value) + } +} + +impl From> for Definition { + fn from(value: Either) -> Self { + value.either(Definition::from, Definition::from) + } +} + impl AsAssocItem for Definition { fn as_assoc_item(self, db: &dyn hir::db::HirDatabase) -> Option { match self { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index ab161f0ce5712..3435757ad3182 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -224,6 +224,7 @@ pub enum SymbolKind { Function, Method, Impl, + InlineAsmRegOrRegClass, Label, LifetimeParam, Local, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 262eefeec00e4..f1404ed9f22cd 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 12ce5a403fe87..ad30c624c417e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -8,10 +8,11 @@ use std::mem; use std::{cell::LazyCell, cmp::Reverse}; use base_db::{salsa::Database, SourceDatabase, SourceRootDatabase}; +use either::Either; use hir::{ sym, Adt, AsAssocItem, DefWithBody, FileRange, FileRangeWrapper, HasAttrs, HasContainer, - HasSource, HirFileIdExt, InFile, InFileWrapper, InRealFile, ItemContainer, ModuleSource, - PathResolution, Semantics, Visibility, + HasSource, HirFileIdExt, InFile, InFileWrapper, InRealFile, InlineAsmOperand, ItemContainer, + ModuleSource, PathResolution, Semantics, Visibility, }; use memchr::memmem::Finder; use parser::SyntaxKind; @@ -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, + res: Either, sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { - match res.map(Definition::from) { - Some(def) if def == self.def => { - let reference = FileReference { - range, - name: FileReferenceNode::FormatStringEntry(token, range), - category: ReferenceCategory::READ, - }; - sink(file_id, reference) - } - _ => false, + let def = res.either(Definition::from, Definition::from); + if def == self.def { + let reference = FileReference { + range, + name: FileReferenceNode::FormatStringEntry(token, range), + category: ReferenceCategory::READ, + }; + sink(file_id, reference) + } else { + false } } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index 925ae620231d2..ea16a11d56d02 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 4be1b570981f6..14781b2129693 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 9ace9fda62b97..9bc7bf411f05e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/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> { + 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 { /// The macro call site. diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 42b7472c645f3..e46cb5a781f4a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -2975,6 +2975,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( diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs index 518e71454798f..7234108701a34 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs @@ -19,21 +19,21 @@ pub(super) fn highlight_format_string( expanded_string: &ast::String, range: TextRange, ) { - if !is_format_string(expanded_string) { + if is_format_string(expanded_string) { + // FIXME: Replace this with the HIR info we have now. + lex_format_specifiers(string, &mut |piece_range, kind| { + if let Some(highlight) = highlight_format_specifier(kind) { + stack.add(HlRange { + range: piece_range + range.start(), + highlight: highlight.into(), + binding_hash: None, + }); + } + }); + return; } - // FIXME: Replace this with the HIR info we have now. - lex_format_specifiers(string, &mut |piece_range, kind| { - if let Some(highlight) = highlight_format_specifier(kind) { - stack.add(HlRange { - range: piece_range + range.start(), - highlight: highlight.into(), - binding_hash: None, - }); - } - }); - if let Some(parts) = sema.as_format_args_parts(string) { parts.into_iter().for_each(|(range, res)| { if let Some(res) = res { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index eeba9cf35c997..96375937a12e8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -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/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index bc1ec530076ce..5583f1bc8df92 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -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/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index e329023606a1b..3b5d1af0ac72a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html new file mode 100644 index 0000000000000..d830a3887217b --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html @@ -0,0 +1,97 @@ + + +
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),
+        );
+    }
+}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index cb47fc68bc1b6..d5b9fc0e2c421 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -165,16 +165,16 @@ toho!("{}fmt", 0); let i: u64 = 3; let o: u64; - asm!( - "mov {0}, {1}", - "add {0}, 5", - out(reg) o, - in(reg) i, + core::arch::asm!( + "mov {0}, {1}", + "add {0}, 5", + out(reg) o, + in(reg) i, ); const CONSTANT: () = (): let mut m = (); format_args!(concat!("{}"), "{}"); - format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); - reuse_twice!("{backslash}"); + format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); + reuse_twice!("{backslash}"); } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index c06ea155fb86b..f47b2115bfc68 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index a678c1f3a70e6..39ca26fc508c4 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -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 { 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 { + 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/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs index 7d3eb5de25f9a..f6b3783d1cace 100644 --- a/src/tools/rust-analyzer/crates/parser/src/parser.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 00f212487ae68..288a07ef44dc6 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -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 { 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/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index 9ce5a2ae748fd..164d0f36f1be2 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast new file mode 100644 index 0000000000000..4afa9daf5904b --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rs new file mode 100644 index 0000000000000..0906cd3e7197d --- /dev/null +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast index 361900b6d3e4b..19a84ac540961 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs index 14431b0210ea8..920d0f794f242 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index b29268f133f3a..4902c9f88c143 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 43375ce6ae051..52ad439e4decc 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/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 := / "\"" "\"" +AsmRegSpec = '@string' | NameRef +// reg_operand := [ident "="] dir_spec "(" reg_spec ")" operand_expr +AsmRegOperand = AsmDirSpec '(' AsmRegSpec ')' AsmOperandExpr +// clobber_abi := "clobber_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/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index c9b39e9922c92..c81a19f3bda41 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -64,6 +64,53 @@ impl ArrayType { pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmClobberAbi { + pub(crate) syntax: SyntaxNode, +} +impl AsmClobberAbi { + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + #[inline] + pub fn clobber_abi_token(&self) -> Option { + support::token(&self.syntax, T![clobber_abi]) + } + #[inline] + pub fn string_token(&self) -> Option { 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 { support::child(&self.syntax) } + #[inline] + pub fn const_token(&self) -> Option { 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 { support::token(&self.syntax, T![in]) } + #[inline] + pub fn inlateout_token(&self) -> Option { + support::token(&self.syntax, T![inlateout]) + } + #[inline] + pub fn inout_token(&self) -> Option { support::token(&self.syntax, T![inout]) } + #[inline] + pub fn lateout_token(&self) -> Option { support::token(&self.syntax, T![lateout]) } + #[inline] + pub fn out_token(&self) -> Option { support::token(&self.syntax, T![out]) } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AsmExpr { pub(crate) syntax: SyntaxNode, @@ -71,7 +118,9 @@ pub struct AsmExpr { impl ast::HasAttrs for AsmExpr {} impl AsmExpr { #[inline] - pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn asm_pieces(&self) -> AstChildren { support::children(&self.syntax) } + #[inline] + pub fn template(&self) -> AstChildren { support::children(&self.syntax) } #[inline] pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } #[inline] @@ -79,11 +128,142 @@ impl AsmExpr { #[inline] pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } #[inline] + pub fn comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } + #[inline] pub fn asm_token(&self) -> Option { support::token(&self.syntax, T![asm]) } #[inline] pub fn builtin_token(&self) -> Option { 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 { support::child(&self.syntax) } + #[inline] + pub fn label_token(&self) -> Option { 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 { support::child(&self.syntax) } + #[inline] + pub fn out_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn fat_arrow_token(&self) -> Option { 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 { support::child(&self.syntax) } + #[inline] + pub fn eq_token(&self) -> Option { 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 { + support::token(&self.syntax, T![att_syntax]) + } + #[inline] + pub fn may_unwind_token(&self) -> Option { + support::token(&self.syntax, T![may_unwind]) + } + #[inline] + pub fn nomem_token(&self) -> Option { support::token(&self.syntax, T![nomem]) } + #[inline] + pub fn noreturn_token(&self) -> Option { + support::token(&self.syntax, T![noreturn]) + } + #[inline] + pub fn nostack_token(&self) -> Option { support::token(&self.syntax, T![nostack]) } + #[inline] + pub fn preserves_flags_token(&self) -> Option { + support::token(&self.syntax, T![preserves_flags]) + } + #[inline] + pub fn pure_token(&self) -> Option { support::token(&self.syntax, T![pure]) } + #[inline] + pub fn raw_token(&self) -> Option { support::token(&self.syntax, T![raw]) } + #[inline] + pub fn readonly_token(&self) -> Option { + 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 { support::child(&self.syntax) } + #[inline] + pub fn asm_options(&self) -> AstChildren { support::children(&self.syntax) } + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + #[inline] + pub fn comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } + #[inline] + pub fn options_token(&self) -> Option { 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 { support::child(&self.syntax) } + #[inline] + pub fn asm_operand_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn asm_reg_spec(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { 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 { support::child(&self.syntax) } + #[inline] + pub fn string_token(&self) -> Option { 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 { support::child(&self.syntax) } + #[inline] + pub fn sym_token(&self) -> Option { support::token(&self.syntax, T![sym]) } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AssocItemList { pub(crate) syntax: SyntaxNode, @@ -2051,6 +2231,21 @@ impl ast::HasGenericParams for Adt {} 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), @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 for AsmOperand { + #[inline] + fn from(node: AsmConst) -> AsmOperand { AsmOperand::AsmConst(node) } +} +impl From for AsmOperand { + #[inline] + fn from(node: AsmLabel) -> AsmOperand { AsmOperand::AsmLabel(node) } +} +impl From for AsmOperand { + #[inline] + fn from(node: AsmRegOperand) -> AsmOperand { AsmOperand::AsmRegOperand(node) } +} +impl From 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 { + 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 for AsmPiece { + #[inline] + fn from(node: AsmClobberAbi) -> AsmPiece { AsmPiece::AsmClobberAbi(node) } +} +impl From for AsmPiece { + #[inline] + fn from(node: AsmOperandNamed) -> AsmPiece { AsmPiece::AsmOperandNamed(node) } +} +impl From 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 { + 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 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 for AnyHasName { + #[inline] + fn from(node: AsmOperandNamed) -> AnyHasName { AnyHasName { syntax: node.syntax } } +} impl From 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/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 4b7e23388c7e9..fec270a556045 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/xtask/Cargo.toml b/src/tools/rust-analyzer/xtask/Cargo.toml index 192de8694721d..4bc1821ee5eab 100644 --- a/src/tools/rust-analyzer/xtask/Cargo.toml +++ b/src/tools/rust-analyzer/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/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs index 39e06f9642db1..e7534582f2b42 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs +++ b/src/tools/rust-analyzer/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::>(); + // FIXME: This generates enum kinds? let nodes = grammar.nodes.iter().map(|name| format_ident!("{}", name)).collect::>(); 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/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs index 34151bd958765..f1a96e0c6a2d4 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs +++ b/src/tools/rust-analyzer/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),