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 runtime_type_mismatch(
69 e: anyhow::Error,
70 expected: &Type,
71 expected_span: Span,
72 actual: &Type,
73 actual_span: Span,
74) -> Diagnostic {
75 let e = e.context(format!(
76 "type mismatch: expected type `{expected}`, but found type `{actual}`"
77 ));
78
79 Diagnostic::error(format!("{e:#}"))
80 .with_label(format!("this is type `{actual}`"), actual_span)
81 .with_label(format!("this expects type `{expected}`"), expected_span)
82}
83
84pub fn if_conditional_mismatch(e: anyhow::Error, actual: &Type, actual_span: Span) -> Diagnostic {
86 let e = e.context(format!(
87 "type mismatch: expected `if` conditional expression to be type `Boolean`, but found type \
88 `{actual}`"
89 ));
90
91 Diagnostic::error(format!("{e:#}")).with_label(format!("this is type `{actual}`"), actual_span)
92}
93
94pub fn array_index_out_of_range(
96 index: i64,
97 count: usize,
98 span: Span,
99 target_span: Span,
100) -> Diagnostic {
101 Diagnostic::error(format!("array index {index} is out of range"))
102 .with_highlight(span)
103 .with_label(
104 if count == 0 {
105 "this array is empty".to_string()
106 } else {
107 format!(
108 "this array has only {count} element{s}",
109 s = if count == 1 { "" } else { "s" }
110 )
111 },
112 target_span,
113 )
114}
115
116pub fn map_key_not_found(span: Span) -> Diagnostic {
118 Diagnostic::error("the map does not contain an entry for the specified key")
119 .with_highlight(span)
120}
121
122pub fn not_an_object_member<T: TreeToken>(member: &Ident<T>) -> Diagnostic {
124 Diagnostic::error(format!(
125 "object does not have a member named `{member}`",
126 member = member.text()
127 ))
128 .with_highlight(member.span())
129}
130
131pub fn exponentiation_requirement(span: Span) -> Diagnostic {
133 Diagnostic::error("use of the exponentiation operator requires WDL version 1.2")
134 .with_highlight(span)
135}
136
137pub fn multiline_string_requirement(span: Span) -> Diagnostic {
139 Diagnostic::error("use of multi-line strings requires WDL version 1.2").with_highlight(span)
140}
141
142pub fn function_call_failed(name: &str, error: impl fmt::Display, span: Span) -> Diagnostic {
144 Diagnostic::error(format!("call to function `{name}` failed: {error}")).with_highlight(span)
145}
146
147pub fn decl_evaluation_failed(
149 e: anyhow::Error,
150 name: &str,
151 task: bool,
152 decl_name: &str,
153 io: Option<Io>,
154 span: Span,
155) -> Diagnostic {
156 let e = e.context(format!(
157 "failed to evaluate {decl_kind} `{decl_name}` for {kind} `{name}`",
158 kind = if task { "task" } else { "workflow" },
159 decl_kind = match io {
160 Some(Io::Input) => "input",
161 Some(Io::Output) => "output",
162 None => "declaration",
163 },
164 ));
165
166 Diagnostic::error(format!("{e:#}")).with_highlight(span)
167}
168
169pub fn task_localization_failed(e: anyhow::Error, name: &str, span: Span) -> Diagnostic {
171 Diagnostic::error(format!(
172 "{e:#}",
173 e = e.context(format!("failed to localize inputs for task `{name}`"))
174 ))
175 .with_highlight(span)
176}
177
178pub fn task_execution_failed(e: anyhow::Error, name: &str, id: &str, span: Span) -> Diagnostic {
180 Diagnostic::error(if name != id {
181 format!("task execution failed for task `{name}` (id `{id}`): {e:#}")
182 } else {
183 format!("task execution failed for task `{name}`: {e:#}")
184 })
185 .with_label("this task failed to execute", span)
186}