Repository: rust-lang/rust-analyzer
Pull request #18473 (2024-11-04 10:04:44)

Render extern blocks in `file_structure`


Patch:
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index 6d07dc8f9b..f49018eaf3 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -37,7 +37,6 @@ pub struct FunctionData {
     pub name: Name,
     pub params: Box<[TypeRefId]>,
     pub ret_type: TypeRefId,
-    // FIXME: why are these stored here? They should be accessed via the query
     pub attrs: Attrs,
     pub visibility: RawVisibility,
     pub abi: Option<Symbol>,
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 142766c039..a56056b077 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -56,21 +56,6 @@ pub enum ConstEvalError {
     MirEvalError(MirEvalError),
 }
 
-impl ConstEvalError {
-    pub fn pretty_print(
-        &self,
-        f: &mut String,
-        db: &dyn HirDatabase,
-        span_formatter: impl Fn(span::FileId, span::TextRange) -> String,
-        edition: span::Edition,
-    ) -> std::result::Result<(), std::fmt::Error> {
-        match self {
-            ConstEvalError::MirLowerError(e) => e.pretty_print(f, db, span_formatter, edition),
-            ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter, edition),
-        }
-    }
-}
-
 impl From<MirLowerError> for ConstEvalError {
     fn from(value: MirLowerError) -> Self {
         match value {
@@ -268,7 +253,7 @@ pub(crate) fn const_eval_query(
         }
         GeneralConstId::InTypeConstId(c) => db.mir_body(c.into())?,
     };
-    let c = interpret_mir(db, body, false, trait_env)?.0?;
+    let c = interpret_mir(db, body, false, trait_env).0?;
     Ok(c)
 }
 
@@ -281,7 +266,7 @@ pub(crate) fn const_eval_static_query(
         Substitution::empty(Interner),
         db.trait_environment_for_body(def.into()),
     )?;
-    let c = interpret_mir(db, body, false, None)?.0?;
+    let c = interpret_mir(db, body, false, None).0?;
     Ok(c)
 }
 
@@ -313,7 +298,7 @@ pub(crate) fn const_eval_discriminant_variant(
         Substitution::empty(Interner),
         db.trait_environment_for_body(def),
     )?;
-    let c = interpret_mir(db, mir_body, false, None)?.0?;
+    let c = interpret_mir(db, mir_body, false, None).0?;
     let c = if is_signed {
         try_const_isize(db, &c).unwrap()
     } else {
@@ -354,7 +339,7 @@ pub(crate) fn eval_to_const(
         }
     }
     if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr) {
-        if let Ok((Ok(result), _)) = interpret_mir(db, Arc::new(mir_body), true, None) {
+        if let Ok(result) = interpret_mir(db, Arc::new(mir_body), true, None).0 {
             return result;
         }
     }
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index d7029651fc..47026995c0 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -585,9 +585,13 @@ pub fn interpret_mir(
     // (and probably should) do better here, for example by excluding bindings outside of the target expression.
     assert_placeholder_ty_is_unused: bool,
     trait_env: Option<Arc<TraitEnvironment>>,
-) -> Result<(Result<Const>, MirOutput)> {
+) -> (Result<Const>, MirOutput) {
     let ty = body.locals[return_slot()].ty.clone();
-    let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?;
+    let mut evaluator =
+        match Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env) {
+            Ok(it) => it,
+            Err(e) => return (Err(e), MirOutput { stdout: vec![], stderr: vec![] }),
+        };
     let it: Result<Const> = (|| {
         if evaluator.ptr_size() != std::mem::size_of::<usize>() {
             not_supported!("targets with different pointer size from host");
@@ -609,7 +613,7 @@ pub fn interpret_mir(
         };
         Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty))
     })();
-    Ok((it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr }))
+    (it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr })
 }
 
 #[cfg(test)]
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index ebd84fd2be..a4045dc5ad 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2306,15 +2306,22 @@ impl Function {
         self,
         db: &dyn HirDatabase,
         span_formatter: impl Fn(FileId, TextRange) -> String,
-    ) -> Result<String, ConstEvalError> {
+    ) -> String {
         let krate = HasModule::krate(&self.id, db.upcast());
         let edition = db.crate_graph()[krate].edition;
-        let body = db.monomorphized_mir_body(
+        let body = match db.monomorphized_mir_body(
             self.id.into(),
             Substitution::empty(Interner),
             db.trait_environment(self.id.into()),
-        )?;
-        let (result, output) = interpret_mir(db, body, false, None)?;
+        ) {
+            Ok(body) => body,
+            Err(e) => {
+                let mut r = String::new();
+                _ = e.pretty_print(&mut r, db, &span_formatter, edition);
+                return r;
+            }
+        };
+        let (result, output) = interpret_mir(db, body, false, None);
         let mut text = match result {
             Ok(_) => "pass".to_owned(),
             Err(e) => {
@@ -2333,7 +2340,7 @@ impl Function {
             text += "\n--------- stderr ---------\n";
             text += &stderr;
         }
-        Ok(text)
+        text
     }
 }
 
@@ -2556,9 +2563,9 @@ impl Const {
     /// Evaluate the constant and return the result as a string.
     ///
     /// This function is intended for IDE assistance, different from [`Const::render_eval`].
-    pub fn eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
+    pub fn eval(self, db: &dyn HirDatabase, edition: Edition) -> Result<String, ConstEvalError> {
         let c = db.const_eval(self.id.into(), Substitution::empty(Interner), None)?;
-        Ok(format!("{}", c.display(db, self.krate(db).edition(db))))
+        Ok(format!("{}", c.display(db, edition)))
     }
 
     /// Evaluate the constant and return the result as a string, with more detailed information.
@@ -2633,15 +2640,7 @@ impl Static {
         Type::from_value_def(db, self.id)
     }
 
-    /// Evaluate the static and return the result as a string.
-    ///
-    /// This function is intended for IDE assistance, different from [`Static::render_eval`].
-    pub fn eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
-        let c = db.const_eval(self.id.into(), Substitution::empty(Interner), None)?;
-        Ok(format!("{}", c.display(db, self.krate(db).edition(db))))
-    }
-
-    /// Evaluate the static and return the result as a string, with more detailed information.
+    /// Evaluate the constant and return the result as a string, with more detailed information.
     ///
     /// This function is intended for user-facing display.
     pub fn render_eval(
diff --git a/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/crates/ide-assists/src/handlers/inline_const_as_literal.rs
index 2bd4c4da1e..6b504a918b 100644
--- a/crates/ide-assists/src/handlers/inline_const_as_literal.rs
+++ b/crates/ide-assists/src/handlers/inline_const_as_literal.rs
@@ -51,7 +51,10 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>
             | ast::Expr::MatchExpr(_)
             | ast::Expr::MacroExpr(_)
             | ast::Expr::BinExpr(_)
-            | ast::Expr::CallExpr(_) => konst.eval(ctx.sema.db).ok()?,
+            | ast::Expr::CallExpr(_) => {
+                let edition = ctx.sema.scope(variable.syntax())?.krate().edition(ctx.db());
+                konst.eval(ctx.sema.db, edition).ok()?
+            }
             _ => return None,
         };
 
diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs
index 055080ad17..5ef65c209c 100644
--- a/crates/ide/src/file_structure.rs
+++ b/crates/ide/src/file_structure.rs
@@ -19,6 +19,7 @@ pub struct StructureNode {
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub enum StructureNodeKind {
     SymbolKind(SymbolKind),
+    ExternBlock,
     Region,
 }
 
@@ -158,6 +159,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
             ast::Trait(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Trait)),
             ast::TraitAlias(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::TraitAlias)),
             ast::Module(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Module)),
+            ast::Macro(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)),
             ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::TypeAlias)),
             ast::RecordField(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Field)),
             ast::Const(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::Const)),
@@ -205,7 +207,23 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
 
                 Some(node)
             },
-            ast::Macro(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)),
+            ast::ExternBlock(it) => {
+                let mut label = "extern".to_owned();
+                let abi = it.abi()?;
+                if let Some(abi) = abi.string_token() {
+                    label.push(' ');
+                    label.push_str(abi.text());
+                }
+                Some(StructureNode {
+                    parent: None,
+                    label,
+                    navigation_range: abi.syntax().text_range(),
+                    node_range: it.syntax().text_range(),
+                    kind: StructureNodeKind::ExternBlock,
+                    detail: None,
+                    deprecated: false,
+                })
+            },
             _ => None,
         }
     }
@@ -327,6 +345,8 @@ fn f() {}
 fn g() {}
 }
 
+extern "C" {}
+
 fn let_statements() {
     let x = 42;
     let mut y = x;
@@ -662,11 +682,20 @@ fn let_statements() {
                         ),
                         deprecated: false,
                     },
+                    StructureNode {
+                        parent: None,
+                        label: "extern \"C\"",
+                        navigation_range: 638..648,
+                        node_range: 638..651,
+                        kind: ExternBlock,
+                        detail: None,
+                        deprecated: false,
+                    },
                     StructureNode {
                         parent: None,
                         label: "let_statements",
-                        navigation_range: 641..655,
-                        node_range: 638..798,
+                        navigation_range: 656..670,
+                        node_range: 653..813,
                         kind: SymbolKind(
                             Function,
                         ),
@@ -677,11 +706,11 @@ fn let_statements() {
                     },
                     StructureNode {
                         parent: Some(
-                            26,
+                            27,
                         ),
                         label: "x",
-                        navigation_range: 668..669,
-                        node_range: 664..675,
+                        navigation_range: 683..684,
+                        node_range: 679..690,
                         kind: SymbolKind(
                             Local,
                         ),
@@ -690,11 +719,11 @@ fn let_statements() {
                     },
                     StructureNode {
                         parent: Some(
-                            26,
+                            27,
                         ),
                         label: "mut y",
-                        navigation_range: 684..689,
-                        node_range: 680..694,
+                        navigation_range: 699..704,
+                        node_range: 695..709,
                         kind: SymbolKind(
                             Local,
                         ),
@@ -703,11 +732,11 @@ fn let_statements() {
                     },
                     StructureNode {
                         parent: Some(
-                            26,
+                            27,
                         ),
                         label: "Foo { .. }",
-                        navigation_range: 703..725,
-                        node_range: 699..738,
+                        navigation_range: 718..740,
+                        node_range: 714..753,
                         kind: SymbolKind(
                             Local,
                         ),
@@ -716,11 +745,11 @@ fn let_statements() {
                     },
                     StructureNode {
                         parent: Some(
-                            26,
+                            27,
                         ),
                         label: "_",
-                        navigation_range: 788..789,
-                        node_range: 784..796,
+                        navigation_range: 803..804,
+                        node_range: 799..811,
                         kind: SymbolKind(
                             Local,
                         ),
diff --git a/crates/ide/src/interpret.rs b/crates/ide/src/interpret.rs
deleted file mode 100644
index 5fa6f4e484..0000000000
--- a/crates/ide/src/interpret.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-use hir::{DefWithBody, Semantics};
-use ide_db::{base_db::SourceRootDatabase, FilePosition, LineIndexDatabase, RootDatabase};
-use std::time::{Duration, Instant};
-use stdx::format_to;
-use syntax::{algo::ancestors_at_offset, ast, AstNode, TextRange};
-
-// Feature: Interpret A Function, Static Or Const.
-//
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **rust-analyzer: Interpret**
-// |===
-pub(crate) fn interpret(db: &RootDatabase, position: FilePosition) -> String {
-    match find_and_interpret(db, position) {
-        Some((duration, mut result)) => {
-            result.push('\n');
-            format_to!(result, "----------------------\n");
-            format_to!(result, "  Finished in {}s\n", duration.as_secs_f32());
-            result
-        }
-        _ => "Not inside a function, const or static".to_owned(),
-    }
-}
-
-fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<(Duration, String)> {
-    let sema = Semantics::new(db);
-    let source_file = sema.parse_guess_edition(position.file_id);
-
-    let item = ancestors_at_offset(source_file.syntax(), position.offset)
-        .filter(|it| !ast::MacroCall::can_cast(it.kind()))
-        .find_map(ast::Item::cast)?;
-    let def: DefWithBody = match item {
-        ast::Item::Fn(it) => sema.to_def(&it)?.into(),
-        ast::Item::Const(it) => sema.to_def(&it)?.into(),
-        ast::Item::Static(it) => sema.to_def(&it)?.into(),
-        _ => return None,
-    };
-    let span_formatter = |file_id, text_range: TextRange| {
-        let path = &db
-            .source_root(db.file_source_root(file_id))
-            .path_for_file(&file_id)
-            .map(|x| x.to_string());
-        let path = path.as_deref().unwrap_or("<unknown file>");
-        match db.line_index(file_id).try_line_col(text_range.start()) {
-            Some(line_col) => format!("file://{path}:{}:{}", line_col.line + 1, line_col.col),
-            None => format!("file://{path} range {text_range:?}"),
-        }
-    };
-    let start_time = Instant::now();
-    let res = match def {
-        DefWithBody::Function(it) => it.eval(db, span_formatter),
-        DefWithBody::Static(it) => it.eval(db),
-        DefWithBody::Const(it) => it.eval(db),
-        _ => unreachable!(),
-    };
-    let res = res.unwrap_or_else(|e| {
-        let mut r = String::new();
-        _ = e.pretty_print(&mut r, db, span_formatter, def.module(db).krate().edition(db));
-        r
-    });
-    let duration = Instant::now() - start_time;
-    Some((duration, res))
-}
diff --git a/crates/ide/src/interpret_function.rs b/crates/ide/src/interpret_function.rs
new file mode 100644
index 0000000000..ff1317d135
--- /dev/null
+++ b/crates/ide/src/interpret_function.rs
@@ -0,0 +1,47 @@
+use hir::Semantics;
+use ide_db::{base_db::SourceRootDatabase, FilePosition, LineIndexDatabase, RootDatabase};
+use std::{fmt::Write, time::Instant};
+use syntax::{algo::ancestors_at_offset, ast, AstNode, TextRange};
+
+// Feature: Interpret Function
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **rust-analyzer: Interpret Function**
+// |===
+pub(crate) fn interpret_function(db: &RootDatabase, position: FilePosition) -> String {
+    let start_time = Instant::now();
+    let mut result =
+        find_and_interpret(db, position).unwrap_or_else(|| "Not inside a function body".to_owned());
+    let duration = Instant::now() - start_time;
+    writeln!(result).unwrap();
+    writeln!(result, "----------------------").unwrap();
+    writeln!(result, "  Finished in {}s", duration.as_secs_f32()).unwrap();
+    result
+}
+
+fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<String> {
+    let sema = Semantics::new(db);
+    let source_file = sema.parse_guess_edition(position.file_id);
+
+    let item = ancestors_at_offset(source_file.syntax(), position.offset)
+        .filter(|it| !ast::MacroCall::can_cast(it.kind()))
+        .find_map(ast::Item::cast)?;
+    let def = match item {
+        ast::Item::Fn(it) => sema.to_def(&it)?,
+        _ => return None,
+    };
+    let span_formatter = |file_id, text_range: TextRange| {
+        let path = &db
+            .source_root(db.file_source_root(file_id))
+            .path_for_file(&file_id)
+            .map(|x| x.to_string());
+        let path = path.as_deref().unwrap_or("<unknown file>");
+        match db.line_index(file_id).try_line_col(text_range.start()) {
+            Some(line_col) => format!("file://{path}#{}:{}", line_col.line + 1, line_col.col),
+            None => format!("file://{path} range {text_range:?}"),
+        }
+    };
+    Some(def.eval(db, span_formatter))
+}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index d4ef9570e1..d053c4b3c9 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -33,7 +33,7 @@ mod goto_type_definition;
 mod highlight_related;
 mod hover;
 mod inlay_hints;
-mod interpret;
+mod interpret_function;
 mod join_lines;
 mod markdown_remove;
 mod matching_brace;
@@ -350,7 +350,7 @@ impl Analysis {
     }
 
     pub fn interpret_function(&self, position: FilePosition) -> Cancellable<String> {
-        self.with_db(|db| interpret::interpret(db, position))
+        self.with_db(|db| interpret_function::interpret_function(db, position))
     }
 
     pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index aecafb444c..a62cbc7fb2 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -398,8 +398,6 @@ define_symbols! {
     rustc_const_panic_str,
     rustc_deprecated_safe_2024,
     rustc_has_incoherent_inherent_impls,
-    rustc_intrinsic,
-    rustc_intrinsic_must_be_overridden,
     rustc_layout_scalar_valid_range_end,
     rustc_layout_scalar_valid_range_start,
     rustc_legacy_const_generics,
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index d654dc3e7f..c59e4699f2 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -88,6 +88,7 @@ pub(crate) fn structure_node_kind(kind: StructureNodeKind) -> lsp_types::SymbolK
     match kind {
         StructureNodeKind::SymbolKind(symbol) => symbol_kind(symbol),
         StructureNodeKind::Region => lsp_types::SymbolKind::NAMESPACE,
+        StructureNodeKind::ExternBlock => lsp_types::SymbolKind::NAMESPACE,
     }
 }
 
diff --git a/editors/code/package.json b/editors/code/package.json
index ccc8c0e384..6eebdf9f01 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -125,7 +125,7 @@
             },
             {
                 "command": "rust-analyzer.interpretFunction",
-                "title": "Interpret",
+                "title": "Interpret Function",
                 "category": "rust-analyzer (debug command)"
             },
             {


Test patch:
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs
index 30d1137373..595a78da10 100644
--- a/crates/hir-ty/src/mir/eval/tests.rs
+++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -32,7 +32,7 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String),
         )
         .map_err(|e| MirEvalError::MirLowerError(func_id, e))?;
 
-    let (result, output) = interpret_mir(db, body, false, None)?;
+    let (result, output) = interpret_mir(db, body, false, None);
     result?;
     Ok((output.stdout().into_owned(), output.stderr().into_owned()))
 }
diff --git a/crates/rust-analyzer/src/cli/run_tests.rs b/crates/rust-analyzer/src/cli/run_tests.rs
index 7398b9a9ef..11534bbeba 100644
--- a/crates/rust-analyzer/src/cli/run_tests.rs
+++ b/crates/rust-analyzer/src/cli/run_tests.rs
@@ -61,11 +61,12 @@ impl flags::RunTests {
             }
             let mut sw_one = StopWatch::start();
             let result = test.eval(db, span_formatter);
-            match &result {
-                Ok(result) if result.trim() == "pass" => pass_count += 1,
-                _ => fail_count += 1,
+            if result.trim() == "pass" {
+                pass_count += 1;
+            } else {
+                fail_count += 1;
             }
-            println!("{result:?}");
+            println!("{result}");
             eprintln!("{:<20} {}", format!("test {}", full_name), sw_one.elapsed());
         }
         println!("{pass_count} passed, {fail_count} failed, {ignore_count} ignored");