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