Repository: rust-analyzer/rust-analyzer
Pull request #10397 (2021-09-30 14:32:07)

fix: fix format string highlighting for `panic!` and `assert!`
part of https://github.com/rust-analyzer/rust-analyzer/issues/10394

bors r+


Patch:
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 297497ac1b..e148118348 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -180,22 +180,29 @@ fn assert_expand(
     _id: MacroCallId,
     tt: &tt::Subtree,
 ) -> ExpandResult<tt::Subtree> {
-    // A hacky implementation for goto def and hover
-    // We expand `assert!(cond, arg1, arg2)` to
-    // ```
-    // {(cond, &(arg1), &(arg2));}
-    // ```,
-    // which is wrong but useful.
-
+    let krate = tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() };
     let args = parse_exprs_with_sep(tt, ',');
-
-    let arg_tts = args.into_iter().flat_map(|arg| {
-        quote! { &(#arg), }
-    }.token_trees);
-
-    let expanded = quote! {
-        { { (##arg_tts); } }
+    let expanded = match &*args {
+        [cond, panic_args @ ..] => {
+            let comma = tt::Subtree {
+                delimiter: None,
+                token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
+                    char: ',',
+                    spacing: tt::Spacing::Alone,
+                    id: tt::TokenId::unspecified(),
+                }))],
+            };
+            let cond = cond.clone();
+            let panic_args = itertools::Itertools::intersperse(panic_args.iter().cloned(), comma);
+            quote! {{
+                if !#cond {
+                    #krate::panic!(##panic_args);
+                }
+            }}
+        }
+        [] => quote! {{}},
     };
+
     ExpandResult::ok(expanded)
 }
 
@@ -731,7 +738,7 @@ mod tests {
             }
             assert!(true, "{} {:?}", arg1(a, b, c), arg2);
             "#,
-            expect![["{{(&(true), &(\"{} {:?}\"), &(arg1(a,b,c)), &(arg2),);}}"]],
+            expect![[r#"{if!true{$crate::panic!("{} {:?}",arg1(a,b,c),arg2);}}"#]],
         );
     }
 
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs
index 5bbadb0f45..ce24311115 100644
--- a/crates/ide/src/syntax_highlighting/format.rs
+++ b/crates/ide/src/syntax_highlighting/format.rs
@@ -31,14 +31,16 @@ fn is_format_string(string: &ast::String) -> Option<()> {
     let parent = string.syntax().parent()?;
 
     let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?;
-    if !matches!(name.text().as_str(), "format_args" | "format_args_nl") {
+    if !matches!(
+        name.text().as_str(),
+        "format_args" | "format_args_nl" | "const_format_args" | "panic_2015" | "panic_2021"
+    ) {
         return None;
     }
 
     let first_literal = parent
         .children_with_tokens()
-        .filter_map(|it| it.as_token().cloned().and_then(ast::String::cast))
-        .next()?;
+        .find_map(|it| it.as_token().cloned().and_then(ast::String::cast))?;
     if &first_literal != string {
         return None;
     }


Test patch:
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 9c20c8ae4b..2992755ded 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -51,6 +51,34 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="colon">:</span>expr<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>args<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">{</span> <span class="comment">/* compiler built-in */</span> <span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span>
 <span class="brace">}</span>
 
+<span class="keyword">mod</span> <span class="module declaration">panic</span> <span class="brace">{</span>
+    <span class="keyword">pub</span> <span class="keyword">macro</span> <span class="macro declaration">panic_2015</span> <span class="brace">{</span>
+        <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span>
+            <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic<span class="parenthesis">(</span><span class="string_literal">"explicit panic"</span><span class="parenthesis">)</span>
+        <span class="parenthesis">)</span><span class="comma">,</span>
+        <span class="parenthesis">(</span><span class="punctuation">$</span>msg<span class="colon">:</span>literal <span class="punctuation">$</span><span class="parenthesis">(</span><span class="comma">,</span><span class="parenthesis">)</span><span class="operator control">?</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span>
+            <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic<span class="parenthesis">(</span><span class="punctuation">$</span>msg<span class="parenthesis">)</span>
+        <span class="parenthesis">)</span><span class="comma">,</span>
+        <span class="comment">// Use `panic_str` instead of `panic_display::&lt;&str&gt;` for non_fmt_panic lint.</span>
+        <span class="parenthesis">(</span><span class="punctuation">$</span>msg<span class="colon">:</span>expr <span class="punctuation">$</span><span class="parenthesis">(</span><span class="comma">,</span><span class="parenthesis">)</span><span class="operator control">?</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span>
+            <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_str<span class="parenthesis">(</span><span class="punctuation">$</span>msg<span class="parenthesis">)</span>
+        <span class="parenthesis">)</span><span class="comma">,</span>
+        <span class="comment">// Special-case the single-argument case for const_panic.</span>
+        <span class="parenthesis">(</span><span class="string_literal">"{}"</span><span class="comma">,</span> <span class="punctuation">$</span>arg<span class="colon">:</span>expr <span class="punctuation">$</span><span class="parenthesis">(</span><span class="comma">,</span><span class="parenthesis">)</span><span class="operator control">?</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span>
+            <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_display<span class="parenthesis">(</span><span class="operator">&</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span>
+        <span class="parenthesis">)</span><span class="comma">,</span>
+        <span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="colon">:</span>expr<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span>
+            <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_fmt<span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>const_format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
+        <span class="parenthesis">)</span><span class="comma">,</span>
+    <span class="brace">}</span>
+<span class="brace">}</span>
+
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">rustc_builtin_macro</span><span class="parenthesis attribute">(</span><span class="none attribute">std_panic</span><span class="parenthesis attribute">)</span><span class="attribute attribute">]</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">macro_export</span><span class="attribute attribute">]</span>
+<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">panic</span> <span class="brace">{</span><span class="brace">}</span>
+<span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="builtin_attr attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span>
+<span class="keyword">macro_rules</span><span class="punctuation">!</span> <span class="macro declaration">assert</span> <span class="brace">{</span><span class="brace">}</span>
+
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
     <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
     <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span>                 <span class="comment">// =&gt; "Hello"</span>
@@ -100,4 +128,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> ничоси <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
     <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> "</span><span class="comma">,</span> thingy<span class="comma">,</span> n2<span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">panic!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">panic!</span><span class="parenthesis">(</span><span class="string_literal">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">assert!</span><span class="parenthesis">(</span><span class="bool_literal">true</span><span class="comma">,</span> <span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="macro">assert!</span><span class="parenthesis">(</span><span class="bool_literal">true</span><span class="comma">,</span> <span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> asdasd"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 9fc730e007..d5018f2327 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -446,6 +446,34 @@ macro_rules! format_args_nl {
     ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
 }
 
+mod panic {
+    pub macro panic_2015 {
+        () => (
+            $crate::panicking::panic("explicit panic")
+        ),
+        ($msg:literal $(,)?) => (
+            $crate::panicking::panic($msg)
+        ),
+        // Use `panic_str` instead of `panic_display::<&str>` for non_fmt_panic lint.
+        ($msg:expr $(,)?) => (
+            $crate::panicking::panic_str($msg)
+        ),
+        // Special-case the single-argument case for const_panic.
+        ("{}", $arg:expr $(,)?) => (
+            $crate::panicking::panic_display(&$arg)
+        ),
+        ($fmt:expr, $($arg:tt)+) => (
+            $crate::panicking::panic_fmt($crate::const_format_args!($fmt, $($arg)+))
+        ),
+    }
+}
+
+#[rustc_builtin_macro(std_panic)]
+#[macro_export]
+macro_rules! panic {}
+#[rustc_builtin_macro]
+macro_rules! assert {}
+
 fn main() {
     // from https://doc.rust-lang.org/std/fmt/index.html
     println!("Hello");                 // => "Hello"
@@ -495,6 +523,10 @@ fn main() {
     println!("{ничоси}", ничоси = 92);
 
     println!("{:x?} {} ", thingy, n2);
+    panic!("{}", 0);
+    panic!("more {}", 1);
+    assert!(true, "{}", 1);
+    assert!(true, "{} asdasd", 1);
 }"#
         .trim(),
         expect_file!["./test_data/highlight_strings.html"],