Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #18470 from Veykril/push-rxmtkvpulotn
Allow interpreting consts and statics with interpret function command
Lukas Wirth 2024-11-03
parent 20ab970 · parent 78f3112 · commit e44691a
-rw-r--r--crates/hir-def/src/data.rs1
-rw-r--r--crates/hir-ty/src/consteval.rs23
-rw-r--r--crates/hir-ty/src/mir/eval.rs10
-rw-r--r--crates/hir-ty/src/mir/eval/tests.rs2
-rw-r--r--crates/hir/src/lib.rs31
-rw-r--r--crates/ide-assists/src/handlers/inline_const_as_literal.rs5
-rw-r--r--crates/ide/src/interpret.rs64
-rw-r--r--crates/ide/src/interpret_function.rs47
-rw-r--r--crates/ide/src/lib.rs4
-rw-r--r--crates/intern/src/symbol/symbols.rs2
-rw-r--r--crates/rust-analyzer/src/cli/run_tests.rs9
-rw-r--r--editors/code/package.json2
12 files changed, 114 insertions, 86 deletions
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index f49018eaf3..6d07dc8f9b 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -37,6 +37,7 @@ 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 a56056b077..142766c039 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -56,6 +56,21 @@ 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 {
@@ -253,7 +268,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)
}
@@ -266,7 +281,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)
}
@@ -298,7 +313,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 {
@@ -339,7 +354,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(result) = interpret_mir(db, Arc::new(mir_body), true, None).0 {
+ if let Ok((Ok(result), _)) = interpret_mir(db, Arc::new(mir_body), true, None) {
return result;
}
}
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 47026995c0..d7029651fc 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -585,13 +585,9 @@ 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<Const>, MirOutput) {
+) -> Result<(Result<Const>, MirOutput)> {
let ty = body.locals[return_slot()].ty.clone();
- 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 mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?;
let it: Result<Const> = (|| {
if evaluator.ptr_size() != std::mem::size_of::<usize>() {
not_supported!("targets with different pointer size from host");
@@ -613,7 +609,7 @@ pub fn interpret_mir(
};
Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty))
})();
- (it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr })
+ Ok((it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr }))
}
#[cfg(test)]
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs
index 595a78da10..30d1137373 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/hir/src/lib.rs b/crates/hir/src/lib.rs
index a4045dc5ad..ebd84fd2be 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2306,22 +2306,15 @@ impl Function {
self,
db: &dyn HirDatabase,
span_formatter: impl Fn(FileId, TextRange) -> String,
- ) -> String {
+ ) -> Result<String, ConstEvalError> {
let krate = HasModule::krate(&self.id, db.upcast());
let edition = db.crate_graph()[krate].edition;
- let body = match db.monomorphized_mir_body(
+ let body = db.monomorphized_mir_body(
self.id.into(),
Substitution::empty(Interner),
db.trait_environment(self.id.into()),
- ) {
- 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 (result, output) = interpret_mir(db, body, false, None)?;
let mut text = match result {
Ok(_) => "pass".to_owned(),
Err(e) => {
@@ -2340,7 +2333,7 @@ impl Function {
text += "\n--------- stderr ---------\n";
text += &stderr;
}
- text
+ Ok(text)
}
}
@@ -2563,9 +2556,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, edition: Edition) -> Result<String, ConstEvalError> {
+ 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, edition)))
+ Ok(format!("{}", c.display(db, self.krate(db).edition(db))))
}
/// Evaluate the constant and return the result as a string, with more detailed information.
@@ -2640,7 +2633,15 @@ impl Static {
Type::from_value_def(db, self.id)
}
- /// Evaluate the constant and return the result as a string, with more detailed information.
+ /// 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.
///
/// 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 6b504a918b..2bd4c4da1e 100644
--- a/crates/ide-assists/src/handlers/inline_const_as_literal.rs
+++ b/crates/ide-assists/src/handlers/inline_const_as_literal.rs
@@ -51,10 +51,7 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>
| ast::Expr::MatchExpr(_)
| ast::Expr::MacroExpr(_)
| ast::Expr::BinExpr(_)
- | ast::Expr::CallExpr(_) => {
- let edition = ctx.sema.scope(variable.syntax())?.krate().edition(ctx.db());
- konst.eval(ctx.sema.db, edition).ok()?
- }
+ | ast::Expr::CallExpr(_) => konst.eval(ctx.sema.db).ok()?,
_ => return None,
};
diff --git a/crates/ide/src/interpret.rs b/crates/ide/src/interpret.rs
new file mode 100644
index 0000000000..5abe572a77
--- /dev/null
+++ b/crates/ide/src/interpret.rs
@@ -0,0 +1,64 @@
+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
deleted file mode 100644
index ff1317d135..0000000000
--- a/crates/ide/src/interpret_function.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-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 d053c4b3c9..d4ef9570e1 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_function;
+mod interpret;
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_function::interpret_function(db, position))
+ self.with_db(|db| interpret::interpret(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 a62cbc7fb2..aecafb444c 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -398,6 +398,8 @@ 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/cli/run_tests.rs b/crates/rust-analyzer/src/cli/run_tests.rs
index 11534bbeba..7398b9a9ef 100644
--- a/crates/rust-analyzer/src/cli/run_tests.rs
+++ b/crates/rust-analyzer/src/cli/run_tests.rs
@@ -61,12 +61,11 @@ impl flags::RunTests {
}
let mut sw_one = StopWatch::start();
let result = test.eval(db, span_formatter);
- if result.trim() == "pass" {
- pass_count += 1;
- } else {
- fail_count += 1;
+ match &result {
+ Ok(result) if result.trim() == "pass" => pass_count += 1,
+ _ => 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");
diff --git a/editors/code/package.json b/editors/code/package.json
index 6eebdf9f01..ccc8c0e384 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -125,7 +125,7 @@
},
{
"command": "rust-analyzer.interpretFunction",
- "title": "Interpret Function",
+ "title": "Interpret",
"category": "rust-analyzer (debug command)"
},
{