Repository: rust-lang/rust-analyzer
Pull request #18053 (2024-09-05 13:08:32)

fix: Couple asm! parsing and lowering fixes


Patch:
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index 34db43939a..f18083d387 100644
--- a/crates/hir-def/src/body.rs
+++ b/crates/hir-def/src/body.rs
@@ -105,7 +105,7 @@ pub struct BodySourceMap {
             // format_args!
             FxHashMap<ExprId, Vec<(syntax::TextRange, Name)>>,
             // asm!
-            FxHashMap<ExprId, Vec<(syntax::TextRange, usize)>>,
+            FxHashMap<ExprId, Vec<Vec<(syntax::TextRange, usize)>>>,
         )>,
     >,
 
@@ -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::<ast::Expr>);
         let expr = self.expr_map.get(&src)?;
         Some(*expr).zip(self.template_map.as_ref()?.1.get(expr).map(std::ops::Deref::deref))
@@ -487,7 +487,7 @@ impl BodySourceMap {
         &self,
     ) -> Option<&(
         FxHashMap<Idx<Expr>, Vec<(tt::TextRange, Name)>>,
-        FxHashMap<Idx<Expr>, Vec<(tt::TextRange, usize)>>,
+        FxHashMap<Idx<Expr>, Vec<Vec<(tt::TextRange, usize)>>>,
     )> {
         self.template_map.as_deref()
     }
diff --git a/crates/hir-def/src/body/lower/asm.rs b/crates/hir-def/src/body/lower/asm.rs
index bf6ba41482..448bc2f033 100644
--- a/crates/hir-def/src/body/lower/asm.rs
+++ b/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/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 0ba0e44657..2e67a5e454 100644
--- a/crates/hir/src/semantics.rs
+++ b/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/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index f6f1da1b7d..3da67ae23f 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/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/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index 5348e855be..4c8e3fc304 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/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/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 39ca26fc50..2333e6c862 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -361,16 +361,20 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
         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);


Test patch:
diff --git a/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs
index 10ab6d3ff8..53b69c12f0 100644
--- a/crates/hir-ty/src/tests/macros.rs
+++ b/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/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html
index d830a38872..15a6386aa3 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html
@@ -50,9 +50,9 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         <span class="keyword">let</span> <span class="variable declaration">foo</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="semicolon">;</span>
         <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">o</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
         <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
-            <span class="string_literal macro">"%input = </span><span class="variable">O</span><span class="string_literal macro">pLoad _ {</span><span class="variable">0</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
+            <span class="string_literal macro">"%input = OpLoad _ {</span><span class="variable">0</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
             <span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"%result = "</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="string_literal macro">" _ %input"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span>
-            <span class="string_literal macro">"OpStore {</span><span class="variable">1</span><span class="string_literal macro">} %result</span><span class="variable">"</span><span class="comma macro">,</span>
+            <span class="string_literal macro">"OpStore {</span><span class="variable">1</span><span class="string_literal macro">} %result"</span><span class="comma macro">,</span>
             <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="operator macro">&</span><span class="variable macro">foo</span><span class="comma macro">,</span>
             <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="operator macro">&</span><span class="keyword macro">mut</span> <span class="variable macro mutable">o</span><span class="comma macro">,</span>
         <span class="parenthesis macro">)</span><span class="semicolon">;</span>
@@ -94,4 +94,26 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
             <span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">noreturn</span><span class="parenthesis macro">)</span><span class="comma macro">,</span>
         <span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="brace">}</span>
+<span class="brace">}</span>
+<span class="comment">// taken from https://github.com/rust-embedded/cortex-m/blob/47921b51f8b960344fcfa1255a50a0d19efcde6d/cortex-m/src/asm.rs#L254-L274</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">inline</span><span class="attribute_bracket attribute">]</span>
+<span class="keyword">pub</span> <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration public unsafe">bootstrap</span><span class="parenthesis">(</span><span class="value_param declaration">msp</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">u32</span><span class="comma">,</span> <span class="value_param declaration">rv</span><span class="colon">:</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">u32</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">!</span> <span class="brace">{</span>
+    <span class="comment">// Ensure thumb mode is set.</span>
+    <span class="keyword">let</span> <span class="variable declaration">rv</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="value_param">rv</span> <span class="keyword">as</span> <span class="builtin_type">u32</span><span class="parenthesis">)</span> <span class="bitwise">|</span> <span class="numeric_literal">1</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">msp</span> <span class="operator">=</span> <span class="value_param">msp</span> <span class="keyword">as</span> <span class="builtin_type">u32</span><span class="semicolon">;</span>
+    <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
+        <span class="string_literal macro">"mrs {</span><span class="variable">tmp</span><span class="string_literal macro">}, CONTROL"</span><span class="comma macro">,</span>
+        <span class="string_literal macro">"bics {</span><span class="variable">tmp</span><span class="string_literal macro">}, {</span><span class="variable">spsel</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
+        <span class="string_literal macro">"msr CONTROL, {</span><span class="variable">tmp</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
+        <span class="string_literal macro">"isb"</span><span class="comma macro">,</span>
+        <span class="string_literal macro">"msr MSP, {</span><span class="variable">msp</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
+        <span class="string_literal macro">"bx {</span><span class="variable">rv</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
+        <span class="comment macro">// `out(reg) _` is not permitted in a `noreturn` asm! call,</span>
+        <span class="comment macro">// so instead use `in(reg) 0` and don't restore it afterwards.</span>
+        <span class="variable declaration macro">tmp</span> <span class="operator macro">=</span> <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="numeric_literal macro">0</span><span class="comma macro">,</span>
+        <span class="variable declaration macro">spsel</span> <span class="operator macro">=</span> <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="numeric_literal macro">2</span><span class="comma macro">,</span>
+        <span class="variable declaration macro">msp</span> <span class="operator macro">=</span> <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">msp</span><span class="comma macro">,</span>
+        <span class="variable declaration macro">rv</span> <span class="operator macro">=</span> <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">rv</span><span class="comma macro">,</span>
+        <span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">noreturn</span><span class="comma macro">,</span> <span class="keyword macro">nomem</span><span class="comma macro">,</span> <span class="keyword macro">nostack</span><span class="parenthesis macro">)</span><span class="comma macro">,</span>
+    <span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index d5b9fc0e2c..5594a36e73 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -167,7 +167,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span>
     <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
         <span class="string_literal macro">"mov {</span><span class="variable">0</span><span class="string_literal macro">}, {</span><span class="variable">1</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
-        <span class="string_literal macro">"add {</span><span class="variable">0</span><span class="string_literal macro">}, 5</span><span class="variable">"</span><span class="comma macro">,</span>
+        <span class="string_literal macro">"add {</span><span class="variable">0</span><span class="string_literal macro">}, 5"</span><span class="comma macro">,</span>
         <span class="keyword macro">out</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">o</span><span class="comma macro">,</span>
         <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">i</span><span class="comma macro">,</span>
     <span class="parenthesis macro">)</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index f47b2115bf..82833d716b 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/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/crates/parser/test_data/parser/inline/ok/asm_expr.rast b/crates/parser/test_data/parser/inline/ok/asm_expr.rast
index 4afa9daf59..f0213d0b5e 100644
--- a/crates/parser/test_data/parser/inline/ok/asm_expr.rast
+++ b/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 ")"