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 34db43939a7ca..f18083d387356 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -105,7 +105,7 @@ pub struct BodySourceMap { // format_args! FxHashMap>, // asm! - FxHashMap>, + FxHashMap>>, )>, >, @@ -439,7 +439,7 @@ impl BodySourceMap { pub fn asm_template_args( &self, node: InFile<&ast::AsmExpr>, - ) -> Option<(ExprId, &[(syntax::TextRange, usize)])> { + ) -> Option<(ExprId, &[Vec<(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)) @@ -487,7 +487,7 @@ impl BodySourceMap { &self, ) -> Option<&( FxHashMap, Vec<(tt::TextRange, Name)>>, - FxHashMap, Vec<(tt::TextRange, usize)>>, + FxHashMap, Vec>>, )> { self.template_map.as_deref() } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs index bf6ba41482c47..448bc2f033f66 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs @@ -158,11 +158,14 @@ impl ExprCollector<'_> { 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))| { + .enumerate() + .filter_map(|(idx, it)| Some((idx, it.clone(), self.expand_macros_to_string(it)?))) + .for_each(|(idx, expr, (s, is_direct_literal))| { + mappings.resize_with(idx + 1, Vec::default); let Ok(text) = s.value() else { return; }; + let mappings = &mut mappings[idx]; let template_snippet = match expr { ast::Expr::Literal(literal) => match literal.kind() { ast::LiteralKind::String(s) => Some(s.text().to_owned()), 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 10ab6d3ff8939..53b69c12f05d5 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 @@ -1498,6 +1498,11 @@ fn main() { 43..44 '1': i32 58..63 'mut o': i32 66..67 '0': i32 + !95..104 'thread_id': usize + !103..107 '&foo': &'? i32 + !104..107 'foo': i32 + !115..120 '&muto': &'? mut i32 + !119..120 'o': i32 293..294 'o': i32 308..317 'thread_id': usize "#]], diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 0ba0e446578e7..2e67a5e454bfa 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -572,9 +572,11 @@ impl<'db> SemanticsImpl<'db> { } else { let asm = ast::AsmExpr::cast(parent)?; let source_analyzer = self.analyze_no_infer(asm.syntax())?; + let line = asm.template().position(|it| *it.syntax() == literal)?; let asm = self.wrap_node_infile(asm); let (owner, (expr, asm_parts)) = source_analyzer.as_asm_parts(asm.as_ref())?; let res = asm_parts + .get(line)? .iter() .map(|&(range, index)| { ( @@ -629,8 +631,9 @@ impl<'db> SemanticsImpl<'db> { } else { let asm = ast::AsmExpr::cast(parent)?; let source_analyzer = &self.analyze_no_infer(asm.syntax())?; + let line = asm.template().position(|it| *it.syntax() == literal)?; let asm = self.wrap_node_infile(asm); - source_analyzer.resolve_offset_in_asm_template(asm.as_ref(), offset).map( + source_analyzer.resolve_offset_in_asm_template(asm.as_ref(), line, offset).map( |(owner, (expr, range, index))| { (range, Some(Either::Right(InlineAsmOperand { owner, expr, index }))) }, 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 f6f1da1b7d6bd..3da67ae23f83b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -907,12 +907,14 @@ impl SourceAnalyzer { pub(crate) fn resolve_offset_in_asm_template( &self, asm: InFile<&ast::AsmExpr>, + line: usize, 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() + args.get(line)? + .iter() .find(|(range, _)| range.contains_inclusive(offset)) .map(|(range, idx)| (expr, *range, *idx)), ) @@ -944,7 +946,7 @@ impl SourceAnalyzer { pub(crate) fn as_asm_parts( &self, asm: InFile<&ast::AsmExpr>, - ) -> Option<(DefWithBodyId, (ExprId, &[(TextRange, usize)]))> { + ) -> Option<(DefWithBodyId, (ExprId, &[Vec<(TextRange, usize)>]))> { let (def, _, body_source_map) = self.def.as_ref()?; Some(*def).zip(body_source_map.asm_template_args(asm)) } diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 5348e855be4b0..4c8e3fc3040c2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -2004,6 +2004,36 @@ fn main() { { return; } +"#, + ) + } + + #[test] + fn asm() { + check( + r#" +//- minicore: asm +#[inline] +pub unsafe fn bootstrap() -> ! { + builtin#asm( + "blabla", + "mrs {tmp}, CONTROL", + // ^^^ read + "blabla", + "bics {tmp}, {spsel}", + // ^^^ read + "blabla", + "msr CONTROL, {tmp}", + // ^^^ read + "blabla", + tmp$0 = inout(reg) 0, + // ^^^ + aaa = in(reg) 2, + aaa = in(reg) msp, + aaa = in(reg) rv, + options(noreturn, nomem, nostack), + ); +} "#, ) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html index d830a3887217b..15a6386aa3c88 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html @@ -50,9 +50,9 @@ let foo = 1; let mut o = 0; core::arch::asm!( - "%input = OpLoad _ {0}", + "%input = OpLoad _ {0}", concat!("%result = ", "bar", " _ %input"), - "OpStore {1} %result", + "OpStore {1} %result", in(reg) &foo, in(reg) &mut o, ); @@ -94,4 +94,26 @@ options(noreturn), ); } +} +// taken from https://github.com/rust-embedded/cortex-m/blob/47921b51f8b960344fcfa1255a50a0d19efcde6d/cortex-m/src/asm.rs#L254-L274 +#[inline] +pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! { + // Ensure thumb mode is set. + let rv = (rv as u32) | 1; + let msp = msp as u32; + core::arch::asm!( + "mrs {tmp}, CONTROL", + "bics {tmp}, {spsel}", + "msr CONTROL, {tmp}", + "isb", + "msr MSP, {msp}", + "bx {rv}", + // `out(reg) _` is not permitted in a `noreturn` asm! call, + // so instead use `in(reg) 0` and don't restore it afterwards. + tmp = in(reg) 0, + spsel = in(reg) 2, + msp = in(reg) msp, + rv = in(reg) rv, + options(noreturn, nomem, nostack), + ); } \ 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 d5b9fc0e2c421..5594a36e7318f 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 @@ -167,7 +167,7 @@ let o: u64; core::arch::asm!( "mov {0}, {1}", - "add {0}, 5", + "add {0}, 5", out(reg) o, in(reg) i, ); 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 f47b2115bfc68..82833d716b533 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 @@ -1331,6 +1331,28 @@ fn main() { ); } } +// taken from https://github.com/rust-embedded/cortex-m/blob/47921b51f8b960344fcfa1255a50a0d19efcde6d/cortex-m/src/asm.rs#L254-L274 +#[inline] +pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! { + // Ensure thumb mode is set. + let rv = (rv as u32) | 1; + let msp = msp as u32; + core::arch::asm!( + "mrs {tmp}, CONTROL", + "bics {tmp}, {spsel}", + "msr CONTROL, {tmp}", + "isb", + "msr MSP, {msp}", + "bx {rv}", + // `out(reg) _` is not permitted in a `noreturn` asm! call, + // so instead use `in(reg) 0` and don't restore it afterwards. + tmp = in(reg) 0, + spsel = in(reg) 2, + msp = in(reg) msp, + rv = in(reg) rv, + options(noreturn, nomem, nostack), + ); +} "#, 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 39ca26fc508c4..2333e6c862be5 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 @@ -361,16 +361,20 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { 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); + let op_expr = p.start(); expr(p); + op_expr.complete(p, ASM_OPERAND_EXPR); 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); + let op_expr = p.start(); expr(p); if p.eat(T![=>]) { expr(p); } + op_expr.complete(p, ASM_OPERAND_EXPR); op.complete(p, ASM_REG_OPERAND); op_n.complete(p, ASM_OPERAND_NAMED); } else if p.eat_contextual_kw(T![label]) { @@ -430,6 +434,7 @@ fn parse_options(p: &mut Parser<'_>) { let m = p.start(); if !OPTIONS.iter().any(|&syntax| p.eat_contextual_kw(syntax)) { p.err_and_bump("expected asm option"); + m.abandon(p); continue; } m.complete(p, ASM_OPTION); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast index 4afa9daf5904b..f0213d0b5e36a 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast @@ -50,11 +50,12 @@ SOURCE_FILE IDENT "reg" R_PAREN ")" WHITESPACE " " - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "x" + ASM_OPERAND_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" COMMA "," WHITESPACE "\n " ASM_OPERAND_NAMED @@ -72,8 +73,9 @@ SOURCE_FILE IDENT "reg" R_PAREN ")" WHITESPACE " " - UNDERSCORE_EXPR - UNDERSCORE "_" + ASM_OPERAND_EXPR + UNDERSCORE_EXPR + UNDERSCORE "_" COMMA "," WHITESPACE "\n " R_PAREN ")"