summaryrefslogtreecommitdiff
path: root/crates/typst-eval
diff options
context:
space:
mode:
Diffstat (limited to 'crates/typst-eval')
-rw-r--r--crates/typst-eval/src/call.rs4
-rw-r--r--crates/typst-eval/src/code.rs37
-rw-r--r--crates/typst-eval/src/flow.rs33
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)
}