Skip to content

Commit 6a66db5

Browse files
authored
Merge pull request #64: LSP server fix & animations
Fixes concurrency issues in the LSP server and adds animations for VSCode
2 parents 759aecb + 480e34c commit 6a66db5

32 files changed

+328
-77
lines changed

src/main.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,10 @@ fn verify_files_main(
856856
let (name, mut verify_unit) = verify_unit.enter_with_name();
857857

858858
limits_ref.check_limits()?;
859+
860+
// Set the current unit as ongoing
861+
server.set_ongoing_unit(verify_unit.span)?;
862+
859863
// 4. Desugaring: transforming spec calls to procs
860864
verify_unit.desugar_spec_calls(&mut tcx, name.to_string())?;
861865

@@ -933,10 +937,6 @@ fn verify_files_main(
933937
&slice_vars,
934938
)?;
935939

936-
server
937-
.handle_vc_check_result(name, verify_unit.span, &mut result, &mut translate)
938-
.map_err(VerifyError::ServerError)?;
939-
940940
if options.debug_options.z3_trace {
941941
info!("Z3 tracing output will be written to `z3.log`.");
942942
}
@@ -956,6 +956,12 @@ fn verify_files_main(
956956
ProveResult::Proof => num_proven += 1,
957957
ProveResult::Counterexample | ProveResult::Unknown(_) => num_failures += 1,
958958
}
959+
960+
limits_ref.check_limits()?;
961+
962+
server
963+
.handle_vc_check_result(name, verify_unit.span, &mut result, &mut translate)
964+
.map_err(VerifyError::ServerError)?;
959965
}
960966

961967
if !options.lsp_options.language_server {

src/resource_limits.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,19 @@ impl LimitsRef {
163163

164164
/// Check whether the monitoring thread has indicated a timeout or an OOM.
165165
pub fn check_limits(&self) -> Result<(), LimitError> {
166+
// Timeout flag is set to 1 by `await_with_resource_limits`, only if the hard timeout is reached.
166167
match self.0.done.load(Ordering::Relaxed) {
167-
0 => Ok(()),
168+
0 => {
169+
// Normal timeout might be reached even though the hard timeout is not reached yet.
170+
// Check for timeout by checking the remaining time
171+
if let Some(timeout) = self.0.timeout {
172+
if Instant::now() >= timeout {
173+
self.set_error(LimitError::Timeout);
174+
return Err(LimitError::Timeout);
175+
}
176+
}
177+
Ok(())
178+
}
168179
1 => Err(LimitError::Timeout),
169180
2 => Err(LimitError::Oom),
170181
_ => unreachable!(),

src/servers/cli.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ impl Server for CliServer {
8989
Ok(())
9090
}
9191

92+
fn set_ongoing_unit(&mut self, _span: Span) -> Result<(), VerifyError> {
93+
// Not relevant for CLI
94+
Ok(())
95+
}
96+
9297
fn handle_vc_check_result<'smt, 'ctx>(
9398
&mut self,
9499
name: &SourceUnitName,
@@ -100,11 +105,6 @@ impl Server for CliServer {
100105
Ok(())
101106
}
102107

103-
fn handle_not_checked(&mut self, _span: Span) -> Result<(), ServerError> {
104-
// Not relevant for CLI
105-
Ok(())
106-
}
107-
108108
fn exit_code(&self) -> ExitCode {
109109
if self.has_emitted_errors {
110110
ExitCode::FAILURE

src/servers/lsp.rs

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::{
55
sync::{Arc, Mutex},
66
};
77

8+
use ariadne::ReportKind;
89
use crossbeam_channel::Sender;
910

1011
use lsp_server::{Connection, IoThreads, Message, Request, Response};
@@ -249,6 +250,40 @@ impl LspServer {
249250
Ok(())
250251
}
251252

253+
/// Convert `VerifyResult::Todo` and `VerifyResult::Verifying` statuses to `VerifyResult::Timeout`, send the results to the client and emit diagnostics for them.
254+
fn handle_timeout_for_results(&mut self) -> Result<(), ServerError> {
255+
let mut diagnostics: Vec<Diagnostic> = Vec::new();
256+
257+
// Transform results and collect diagnostics for timed out procedures
258+
for (span, status) in &mut self.statuses {
259+
match status {
260+
VerifyResult::Ongoing => {
261+
diagnostics.push(
262+
Diagnostic::new(ReportKind::Error, *span)
263+
.with_message("Timed out while verifying this procedure"),
264+
);
265+
*status = VerifyResult::Timeout;
266+
}
267+
VerifyResult::Todo => {
268+
diagnostics.push(
269+
Diagnostic::new(ReportKind::Warning, *span)
270+
.with_message("Skipped this procedure due to timeout"),
271+
);
272+
*status = VerifyResult::Timeout;
273+
}
274+
_ => {}
275+
}
276+
}
277+
278+
// Add the collected diagnostics to the server
279+
for diagnostic in diagnostics {
280+
self.add_diagnostic(diagnostic)?;
281+
}
282+
283+
self.publish_verify_statuses()?;
284+
Ok(())
285+
}
286+
252287
fn clear_file_information(&mut self, file_id: &FileId) -> Result<(), ServerError> {
253288
if let Some(diag) = self.diagnostics.get_mut(file_id) {
254289
diag.clear();
@@ -317,6 +352,15 @@ impl Server for LspServer {
317352

318353
fn register_source_unit(&mut self, span: Span) -> Result<(), VerifyError> {
319354
self.statuses.insert(span, VerifyResult::Todo);
355+
self.publish_verify_statuses()
356+
.map_err(VerifyError::ServerError)?;
357+
Ok(())
358+
}
359+
360+
fn set_ongoing_unit(&mut self, span: Span) -> Result<(), VerifyError> {
361+
self.statuses.insert(span, VerifyResult::Ongoing);
362+
self.publish_verify_statuses()
363+
.map_err(VerifyError::ServerError)?;
320364
Ok(())
321365
}
322366

@@ -335,13 +379,6 @@ impl Server for LspServer {
335379
self.publish_verify_statuses()?;
336380
Ok(())
337381
}
338-
339-
fn handle_not_checked(&mut self, span: Span) -> Result<(), ServerError> {
340-
let prev = self.statuses.insert(span, VerifyResult::Unknown);
341-
assert!(prev.is_some());
342-
self.publish_verify_statuses()?;
343-
Ok(())
344-
}
345382
}
346383

347384
/// A type alias representing an asynchronous closure that returns a `Result<(), VerifyError>`.
@@ -440,29 +477,29 @@ async fn handle_verify_request(
440477
};
441478

442479
let result = verify(&[file_id]).await;
443-
let res = match &result {
444-
Ok(_) => Response::new_ok(id, Value::Null),
445-
Err(err) => Response::new_err(id, 0, format!("{}", err)),
480+
481+
let response = match result {
482+
Ok(()) => Response::new_ok(id.clone(), Value::Null),
483+
Err(err) => match err {
484+
VerifyError::Diagnostic(diagnostic) => {
485+
server.lock().unwrap().add_diagnostic(diagnostic)?;
486+
Response::new_ok(id.clone(), Value::Null)
487+
}
488+
VerifyError::Interrupted | VerifyError::LimitError(_) => {
489+
server
490+
.lock()
491+
.unwrap()
492+
.handle_timeout_for_results()
493+
.map_err(VerifyError::ServerError)?;
494+
Response::new_ok(id.clone(), Value::Null)
495+
}
496+
_ => Response::new_err(id, 0, format!("{}", err)),
497+
},
446498
};
499+
447500
sender
448-
.send(Message::Response(res))
501+
.send(Message::Response(response))
449502
.map_err(|e| VerifyError::ServerError(e.into()))?;
450-
match result {
451-
Ok(()) => {}
452-
Err(VerifyError::Diagnostic(diagnostic)) => {
453-
server.lock().unwrap().add_diagnostic(diagnostic)?;
454-
}
455-
Err(VerifyError::Interrupted) | Err(VerifyError::LimitError(_)) => {
456-
// If the verification is interrupted or a limit is reached before the verification starts, no verification statuses are published yet.
457-
// In this case, the client needs to be notified about the registered source units that are not checked yet (marked with VerifyResult::Todo).
458-
// This acts as a fallback mechanism for this case.
459-
server
460-
.lock()
461-
.unwrap()
462-
.publish_verify_statuses()
463-
.map_err(VerifyError::ServerError)?;
464-
}
465-
Err(err) => Err(err)?,
466-
}
503+
467504
Ok(())
468505
}

src/servers/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@ pub type ServerError = Box<dyn Error + Send + Sync>;
3333
pub enum VerifyResult {
3434
// If the verification is not done yet the result is Todo
3535
Todo,
36+
Ongoing,
3637
Verified,
3738
Failed,
3839
Unknown,
40+
Timeout,
3941
}
4042

4143
impl VerifyResult {
@@ -71,6 +73,9 @@ pub trait Server: Send {
7173
/// Register a source unit span with the server.
7274
fn register_source_unit(&mut self, span: Span) -> Result<(), VerifyError>;
7375

76+
/// Register a verify unit span as the current verifying with the server.
77+
fn set_ongoing_unit(&mut self, span: Span) -> Result<(), VerifyError>;
78+
7479
/// Send a verification status message to the client (a custom notification).
7580
fn handle_vc_check_result<'smt, 'ctx>(
7681
&mut self,
@@ -80,9 +85,6 @@ pub trait Server: Send {
8085
translate: &mut TranslateExprs<'smt, 'ctx>,
8186
) -> Result<(), ServerError>;
8287

83-
/// Sends an unknown verification result for the not checked proc with the given span.
84-
fn handle_not_checked(&mut self, span: Span) -> Result<(), ServerError>;
85-
8688
/// Return an exit code for the process.
8789
///
8890
/// Default implementation returns `ExitCode::SUCCESS`.

src/servers/test.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ impl Server for TestServer {
6464
Ok(())
6565
}
6666

67+
fn set_ongoing_unit(&mut self, _span: Span) -> Result<(), VerifyError> {
68+
Ok(())
69+
}
70+
6771
fn handle_vc_check_result<'smt, 'ctx>(
6872
&mut self,
6973
_name: &SourceUnitName,
@@ -75,9 +79,4 @@ impl Server for TestServer {
7579
.insert(span, VerifyResult::from_prove_result(&result.prove_result));
7680
Ok(())
7781
}
78-
79-
fn handle_not_checked(&mut self, span: Span) -> Result<(), ServerError> {
80-
self.statuses.insert(span, VerifyResult::Unknown);
81-
Ok(())
82-
}
8382
}

src/slicing/transform_test.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
//! *Prove* using the SMT solver that our transformations are correct.
22
3-
use std::time::{Duration, Instant};
4-
53
use itertools::Itertools;
64
use z3rro::{
75
model::SmtEval,
@@ -109,8 +107,7 @@ fn prove_equiv(
109107
let tcx = &transform_tcx.tcx;
110108
let builder = ExprBuilder::new(Span::dummy_span());
111109

112-
let deadline = Instant::now() + Duration::from_millis(1);
113-
let mut vcgen = Vcgen::new(tcx, &LimitsRef::new(Some(deadline), None), None);
110+
let mut vcgen = Vcgen::new(tcx, &LimitsRef::new(None, None), None);
114111
let stmt1_vc = vcgen
115112
.vcgen_stmts(stmt1, transform_tcx.post.clone())
116113
.unwrap();

src/smt/pretty_model.rs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
//! Pretty-printing an SMT model.
22
3-
use std::{
4-
collections::BTreeMap,
5-
fmt::Display,
6-
rc::Rc,
7-
time::{Duration, Instant},
8-
};
3+
use std::{collections::BTreeMap, fmt::Display, rc::Rc};
94

105
use itertools::Itertools;
116
use z3rro::model::{InstrumentedModel, ModelConsistency, SmtEvalError};
@@ -72,15 +67,9 @@ pub fn pretty_vc_value<'smt, 'ctx>(
7267
);
7368
let mut res = subst_expr;
7469

75-
// This deadline is not actually used. It is used to create a dummy [`LimitsRef`] object below
76-
let deadline = Instant::now() + Duration::from_millis(1);
77-
7870
// The limit error is not handled here, therefore discard the result
79-
let _ = apply_subst(
80-
translate.ctx.tcx(),
81-
&mut res,
82-
&LimitsRef::new(Some(deadline), None),
83-
);
71+
apply_subst(translate.ctx.tcx(), &mut res, &LimitsRef::new(None, None)).unwrap();
72+
8473
res
8574
};
8675

171 Bytes
Loading
183 Bytes
Loading

0 commit comments

Comments
 (0)