wdl_engine/
diagnostics.rs1use std::fmt;
4
5use wdl_analysis::diagnostics::Io;
6use wdl_analysis::types::Type;
7use wdl_ast::AstToken;
8use wdl_ast::Diagnostic;
9use wdl_ast::Ident;
10use wdl_ast::Span;
11use wdl_ast::TreeToken;
12
13pub fn integer_not_in_range(span: Span) -> Diagnostic {
15 Diagnostic::error(format!(
16 "literal integer exceeds the range for a 64-bit signed integer ({min}..={max})",
17 min = i64::MIN,
18 max = i64::MAX,
19 ))
20 .with_label("this literal integer is not in range", span)
21}
22
23pub fn integer_negation_not_in_range(value: i64, span: Span) -> Diagnostic {
25 Diagnostic::error(format!(
26 "negation of integer value {value} exceeds the range for a 64-bit signed integer \
27 ({min}..={max})",
28 min = i64::MIN,
29 max = i64::MAX,
30 ))
31 .with_highlight(span)
32}
33
34pub fn float_not_in_range(span: Span) -> Diagnostic {
36 Diagnostic::error(format!(
37 "literal float exceeds the range for a 64-bit float ({min:+e}..={max:+e})",
38 min = f64::MIN,
39 max = f64::MAX,
40 ))
41 .with_label("this literal float is not in range", span)
42}
43
44pub fn numeric_overflow(span: Span) -> Diagnostic {
46 Diagnostic::error("evaluation of arithmetic expression resulted in overflow")
47 .with_highlight(span)
48}
49
50pub fn division_by_zero(span: Span, divisor_span: Span) -> Diagnostic {
52 Diagnostic::error("attempt to divide by zero")
53 .with_highlight(span)
54 .with_label("this expression evaluated to zero", divisor_span)
55}
56
57pub fn exponent_not_in_range(span: Span) -> Diagnostic {
59 Diagnostic::error(format!(
60 "exponent exceeds acceptable range ({min}..={max})",
61 min = u32::MIN,
62 max = u32::MAX,
63 ))
64 .with_label("this value exceeds the range for an exponent", span)
65}
66
67pub fn cannot_call<T: TreeToken>(target: &Ident<T>) -> Diagnostic {
69 Diagnostic::error(format!(
70 "function `{target}` can only be called from task outputs",
71 target = target.text()
72 ))
73 .with_highlight(target.span())
74}
75
76pub fn runtime_type_mismatch(
78 e: anyhow::Error,
79 expected: &Type,
80 expected_span: Span,
81 actual: &Type,
82 actual_span: Span,
83) -> Diagnostic {
84 let e = e.context(format!(
85 "type mismatch: expected type `{expected}`, but found type `{actual}`"
86 ));
87
88 Diagnostic::error(format!("{e:#}"))
89 .with_label(format!("this is type `{actual}`"), actual_span)
90 .with_label(format!("this expects type `{expected}`"), expected_span)
91}
92
93pub fn if_conditional_mismatch(e: anyhow::Error, actual: &Type, actual_span: Span) -> Diagnostic {
95 let e = e.context(format!(
96 "type mismatch: expected `if` conditional expression to be type `Boolean`, but found type \
97 `{actual}`"
98 ));
99
100 Diagnostic::error(format!("{e:#}")).with_label(format!("this is type `{actual}`"), actual_span)
101}
102
103pub fn array_index_out_of_range(
105 index: i64,
106 count: usize,
107 span: Span,
108 target_span: Span,
109) -> Diagnostic {
110 Diagnostic::error(format!("array index {index} is out of range"))
111 .with_highlight(span)
112 .with_label(
113 if count == 0 {
114 "this array is empty".to_string()
115 } else {
116 format!(
117 "this array has only {count} element{s}",
118 s = if count == 1 { "" } else { "s" }
119 )
120 },
121 target_span,
122 )
123}
124
125pub fn map_key_not_found(span: Span) -> Diagnostic {
127 Diagnostic::error("the map does not contain an entry for the specified key")
128 .with_highlight(span)
129}
130
131pub fn not_an_object_member<T: TreeToken>(member: &Ident<T>) -> Diagnostic {
133 Diagnostic::error(format!(
134 "object does not have a member named `{member}`",
135 member = member.text()
136 ))
137 .with_highlight(member.span())
138}
139
140pub fn exponentiation_requirement(span: Span) -> Diagnostic {
142 Diagnostic::error("use of the exponentiation operator requires WDL version 1.2")
143 .with_highlight(span)
144}
145
146pub fn multiline_string_requirement(span: Span) -> Diagnostic {
148 Diagnostic::error("use of multi-line strings requires WDL version 1.2").with_highlight(span)
149}
150
151pub fn function_call_failed(name: &str, error: impl fmt::Display, span: Span) -> Diagnostic {
153 Diagnostic::error(format!("call to function `{name}` failed: {error}")).with_highlight(span)
154}
155
156pub fn decl_evaluation_failed(
158 e: anyhow::Error,
159 name: &str,
160 task: bool,
161 decl_name: &str,
162 io: Option<Io>,
163 span: Span,
164) -> Diagnostic {
165 let e = e.context(format!(
166 "failed to evaluate {decl_kind} `{decl_name}` for {kind} `{name}`",
167 kind = if task { "task" } else { "workflow" },
168 decl_kind = match io {
169 Some(Io::Input) => "input",
170 Some(Io::Output) => "output",
171 None => "declaration",
172 },
173 ));
174
175 Diagnostic::error(format!("{e:#}")).with_highlight(span)
176}
177
178pub fn task_localization_failed(e: anyhow::Error, name: &str, span: Span) -> Diagnostic {
180 Diagnostic::error(format!(
181 "{e:#}",
182 e = e.context(format!("failed to localize inputs for task `{name}`"))
183 ))
184 .with_highlight(span)
185}
186
187pub fn task_execution_failed(e: anyhow::Error, name: &str, id: &str, span: Span) -> Diagnostic {
189 Diagnostic::error(if name != id {
190 format!("task execution failed for task `{name}` (id `{id}`): {e:#}")
191 } else {
192 format!("task execution failed for task `{name}`: {e:#}")
193 })
194 .with_label("this task failed to execute", span)
195}