diff options
Diffstat (limited to 'crates/typst-eval')
| -rw-r--r-- | crates/typst-eval/src/call.rs | 4 | ||||
| -rw-r--r-- | crates/typst-eval/src/code.rs | 37 | ||||
| -rw-r--r-- | crates/typst-eval/src/flow.rs | 33 |
3 files changed, 59 insertions, 15 deletions
diff --git a/crates/typst-eval/src/call.rs b/crates/typst-eval/src/call.rs index 9dfb7693..f48734cb 100644 --- a/crates/typst-eval/src/call.rs +++ b/crates/typst-eval/src/call.rs @@ -253,8 +253,8 @@ pub fn eval_closure( // Handle control flow. let output = body.eval(&mut vm)?; match vm.flow { - Some(FlowEvent::Return(_, Some(explicit))) => return Ok(explicit), - Some(FlowEvent::Return(_, None)) => {} + Some(FlowEvent::Return(_, Some(explicit), _)) => return Ok(explicit), + Some(FlowEvent::Return(_, None, _)) => {} Some(flow) => bail!(flow.forbidden()), None => {} } diff --git a/crates/typst-eval/src/code.rs b/crates/typst-eval/src/code.rs index 918d9d2a..ba5256c1 100644 --- a/crates/typst-eval/src/code.rs +++ b/crates/typst-eval/src/code.rs @@ -1,12 +1,15 @@ use ecow::{eco_vec, EcoVec}; -use typst_library::diag::{bail, error, At, SourceResult}; +use typst_library::diag::{bail, error, warning, At, SourceResult}; +use typst_library::engine::Engine; use typst_library::foundations::{ - ops, Array, Capturer, Closure, Content, ContextElem, Dict, Func, NativeElement, Str, - Value, + ops, Array, Capturer, Closure, Content, ContextElem, Dict, Func, NativeElement, + Selector, Str, Value, }; +use typst_library::introspection::{Counter, State}; use typst_syntax::ast::{self, AstNode}; +use typst_utils::singleton; -use crate::{CapturesVisitor, Eval, Vm}; +use crate::{CapturesVisitor, Eval, FlowEvent, Vm}; impl Eval for ast::Code<'_> { type Output = Value; @@ -54,7 +57,8 @@ fn eval_code<'a>( output = ops::join(output, value).at(span)?; - if vm.flow.is_some() { + if let Some(event) = &vm.flow { + warn_for_discarded_content(&mut vm.engine, event, &output); break; } } @@ -358,3 +362,26 @@ impl Eval for ast::Contextual<'_> { Ok(ContextElem::new(func).pack()) } } + +/// Emits a warning when we discard content while returning unconditionally. +fn warn_for_discarded_content(engine: &mut Engine, event: &FlowEvent, joined: &Value) { + let FlowEvent::Return(span, Some(_), false) = event else { return }; + let Value::Content(tree) = &joined else { return }; + + let selector = singleton!( + Selector, + Selector::Or(eco_vec![State::select_any(), Counter::select_any()]) + ); + + let mut warning = warning!( + *span, + "this return unconditionally discards the content before it"; + hint: "try omitting the `return` to automatically join all values" + ); + + if tree.query_first(selector).is_some() { + warning.hint("state/counter updates are content that must end up in the document to have an effect"); + } + + engine.sink.warn(warning); +} diff --git a/crates/typst-eval/src/flow.rs b/crates/typst-eval/src/flow.rs index 231e6899..b5ba487f 100644 --- a/crates/typst-eval/src/flow.rs +++ b/crates/typst-eval/src/flow.rs @@ -17,8 +17,8 @@ pub enum FlowEvent { /// Skip the remainder of the current iteration in a loop. Continue(Span), /// Stop execution of a function early, optionally returning an explicit - /// value. - Return(Span, Option<Value>), + /// value. The final boolean indicates whether the return was conditional. + Return(Span, Option<Value>, bool), } impl FlowEvent { @@ -31,7 +31,7 @@ impl FlowEvent { Self::Continue(span) => { error!(span, "cannot continue outside of loop") } - Self::Return(span, _) => { + Self::Return(span, _, _) => { error!(span, "cannot return outside of function") } } @@ -43,13 +43,20 @@ impl Eval for ast::Conditional<'_> { fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> { let condition = self.condition(); - if condition.eval(vm)?.cast::<bool>().at(condition.span())? { - self.if_body().eval(vm) + let output = if condition.eval(vm)?.cast::<bool>().at(condition.span())? { + self.if_body().eval(vm)? } else if let Some(else_body) = self.else_body() { - else_body.eval(vm) + else_body.eval(vm)? } else { - Ok(Value::None) + Value::None + }; + + // Mark the return as conditional. + if let Some(FlowEvent::Return(_, _, conditional)) = &mut vm.flow { + *conditional = true; } + + Ok(output) } } @@ -95,6 +102,11 @@ impl Eval for ast::WhileLoop<'_> { vm.flow = flow; } + // Mark the return as conditional. + if let Some(FlowEvent::Return(_, _, conditional)) = &mut vm.flow { + *conditional = true; + } + Ok(output) } } @@ -168,6 +180,11 @@ impl Eval for ast::ForLoop<'_> { vm.flow = flow; } + // Mark the return as conditional. + if let Some(FlowEvent::Return(_, _, conditional)) = &mut vm.flow { + *conditional = true; + } + Ok(output) } } @@ -200,7 +217,7 @@ impl Eval for ast::FuncReturn<'_> { fn eval(self, vm: &mut Vm) -> SourceResult<Self::Output> { let value = self.body().map(|body| body.eval(vm)).transpose()?; if vm.flow.is_none() { - vm.flow = Some(FlowEvent::Return(self.span(), value)); + vm.flow = Some(FlowEvent::Return(self.span(), value, false)); } Ok(Value::None) } |
