1use crate::LisetteDiagnostic;
2use syntax::ast::{Annotation, BinaryOperator, Span};
3use syntax::types::{SimpleKind, Type};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum MismatchedTailKind {
7 Result,
8 Option,
9 Partial,
10 Value,
11}
12
13impl MismatchedTailKind {
14 pub fn allow_alias(&self) -> &'static str {
15 match self {
16 Self::Result => "unused_result",
17 Self::Option => "unused_option",
18 Self::Partial => "unused_partial",
19 Self::Value => "unused_value",
20 }
21 }
22}
23
24pub fn mismatched_tail_value(
25 actual_span: &Span,
26 actual_ty: &str,
27 expected_span: &Span,
28 expected_ty: &str,
29) -> LisetteDiagnostic {
30 LisetteDiagnostic::error("Mismatch between return type and return value")
31 .with_infer_code("mismatched_return_value")
32 .with_span_primary_label(actual_span, format!("returns `{}`", actual_ty))
33 .with_span_label(
34 expected_span,
35 format!("has `{}` as implicit return type", expected_ty),
36 )
37 .with_help(format!(
38 "If the `{}` return type is intended, discard the return value with `let _ = ...`. If the `{}` return value is intended, add `-> {}` to the function signature.",
39 expected_ty, actual_ty, actual_ty
40 ))
41}
42
43pub fn blank_import_non_go(blank_span: Span) -> LisetteDiagnostic {
44 LisetteDiagnostic::error("Invalid import")
45 .with_resolve_code("blank_import_non_go")
46 .with_span_label(&blank_span, "only allowed for Go modules")
47 .with_help(
48 "Remove the underscore. Blank imports are allowed only for Go imports, \
49 because Lisette modules have no `init()` side effects.",
50 )
51}
52
53pub fn import_conflict(
54 alias: &str,
55 path1: &str,
56 path2: &str,
57 name_span: Span,
58) -> LisetteDiagnostic {
59 LisetteDiagnostic::error("Import conflict")
60 .with_resolve_code("import_conflict")
61 .with_span_label(
62 &name_span,
63 format!("conflicts with prior import `{}`", alias),
64 )
65 .with_help(format!(
66 "`{}` and `{}` resolve to the same name. Add an alias to at least one of them: \
67 `import my_{} \"{}\"`",
68 path1, path2, alias, path2
69 ))
70}
71
72pub fn reserved_import_alias(alias: &str, alias_span: Span) -> LisetteDiagnostic {
73 LisetteDiagnostic::error("Reserved import alias")
74 .with_resolve_code("reserved_import_alias")
75 .with_span_label(&alias_span, "reserved name")
76 .with_help(format!(
77 "`{}` is a reserved name and cannot be used as an import alias. \
78 Choose a different alias, e.g. `import my_{} \"...\"`",
79 alias, alias
80 ))
81}
82
83pub fn duplicate_import_path(path: &str, name_span: Span) -> LisetteDiagnostic {
84 LisetteDiagnostic::error("Duplicate import")
85 .with_resolve_code("duplicate_import")
86 .with_span_label(&name_span, "already imported above")
87 .with_help(format!(
88 "Module `{}` is already imported. Remove the duplicate import.",
89 path
90 ))
91}
92
93pub fn definition_shadows_import(
94 name: &str,
95 import_path: &str,
96 name_span: Span,
97) -> LisetteDiagnostic {
98 LisetteDiagnostic::error("Definition shadows import")
99 .with_resolve_code("definition_shadows_import")
100 .with_span_label(
101 &name_span,
102 format!("conflicts with imported module `{}`", import_path),
103 )
104 .with_help(format!(
105 "`{}` is already used as a module alias for `{}`. \
106 Rename this definition or use a different import alias.",
107 name, import_path
108 ))
109}
110
111pub fn statement_as_tail(span: Span) -> LisetteDiagnostic {
112 LisetteDiagnostic::error("Statement used as value")
113 .with_infer_code("statement_as_tail")
114 .with_span_label(&span, "this is a statement, not an expression")
115 .with_help(
116 "The last item in this block must be an expression that produces a value. \
117 Statements like `let`, `=`, `task`, and `defer` do not produce values.",
118 )
119}
120
121pub fn invalid_map_initialization(key: &Type, value: &Type, span: Span) -> LisetteDiagnostic {
122 LisetteDiagnostic::error("Invalid `Map` initialization")
123 .with_infer_code("invalid_map_initialization")
124 .with_span_label(&span, "invalid syntax")
125 .with_help(format!(
126 "To initialize a `Map`, use `Map.new<{}, {}>()`",
127 key, value
128 ))
129}
130
131pub fn self_type_not_supported(span: Span, impl_receiver: Option<&str>) -> LisetteDiagnostic {
132 let name_span = Span::new(span.file_id, span.byte_offset, 4); let help = match impl_receiver {
134 Some(name) => format!("Replace `Self` with `{}`.", name),
135 None => "Use a type parameter instead, e.g. `interface Comparable<T> { fn compare(self, other: T) -> int }`".to_string(),
136 };
137 LisetteDiagnostic::error("Use of `Self` type")
138 .with_resolve_code("self_type_not_supported")
139 .with_span_label(&name_span, "invalid type")
140 .with_help(help)
141}
142
143pub fn type_not_found(type_name: &str, annotation_span: Span) -> LisetteDiagnostic {
144 let simple_name = type_name.rsplit('.').next().unwrap_or(type_name);
145 let qualifier_offset = (type_name.len() - simple_name.len()) as u32;
146 let name_span = Span::new(
147 annotation_span.file_id,
148 annotation_span.byte_offset + qualifier_offset,
149 simple_name.len() as u32,
150 );
151
152 let looks_like_type_param = simple_name.len() == 1
153 && simple_name.chars().next().is_some_and(|c| c.is_uppercase())
154 || ["Key", "Value", "Item", "Error", "Elem", "In", "Out"].contains(&simple_name);
155
156 if looks_like_type_param {
157 return LisetteDiagnostic::error("Undeclared type parameter")
158 .with_resolve_code("type_not_found")
159 .with_span_label(&name_span, "undeclared")
160 .with_help(format!(
161 "Declare the type parameter, e.g. `impl<{t}>` or `fn foo<{t}>`",
162 t = simple_name
163 ));
164 }
165
166 LisetteDiagnostic::error("Type not found")
167 .with_resolve_code("type_not_found")
168 .with_span_label(&name_span, "type not found in scope")
169 .with_help("Define or import this type")
170}
171
172pub fn value_in_type_position(
173 name: &str,
174 kind: &str,
175 annotation_span: Span,
176 help: Option<String>,
177) -> LisetteDiagnostic {
178 let name_span = Span::new(
179 annotation_span.file_id,
180 annotation_span.byte_offset,
181 name.len() as u32,
182 );
183
184 let mut diag = LisetteDiagnostic::error("Value in type position")
185 .with_resolve_code("value_in_type_position")
186 .with_span_label(&name_span, format!("expected type, found {}", kind));
187
188 if let Some(help) = help {
189 diag = diag.with_help(help);
190 }
191
192 diag
193}
194
195pub fn undeclared_impl_type_param(
196 type_name: &str,
197 annotation_span: Span,
198 receiver_name: &str,
199) -> LisetteDiagnostic {
200 let name_span = Span::new(
201 annotation_span.file_id,
202 annotation_span.byte_offset,
203 type_name.len() as u32,
204 );
205
206 LisetteDiagnostic::error("Undeclared type parameter")
207 .with_resolve_code("type_not_found")
208 .with_span_label(&name_span, "undeclared")
209 .with_help(format!(
210 "Declare the type parameter: `impl<{t}> {r}<{t}>`",
211 t = type_name,
212 r = receiver_name
213 ))
214}
215
216pub fn type_param_with_args(type_arg_count: usize, span: Span) -> LisetteDiagnostic {
217 let noun = if type_arg_count == 1 {
218 "type argument"
219 } else {
220 "type arguments"
221 };
222
223 LisetteDiagnostic::error("Invalid type argument")
224 .with_infer_code("type_param_with_args")
225 .with_span_label(&span, "type is not parameterized")
226 .with_help(format!("Remove {}", noun))
227}
228
229pub fn type_args_on_non_generic(type_arg_count: usize, span: Span) -> LisetteDiagnostic {
230 let noun = if type_arg_count == 1 {
231 "type argument"
232 } else {
233 "type arguments"
234 };
235
236 LisetteDiagnostic::error("Unexpected type arguments")
237 .with_infer_code("type_arg_on_non_generic")
238 .with_span_label(&span, "accepts no type arguments")
239 .with_help(format!("Remove the {} from this call", noun))
240}
241
242pub fn circular_type_alias(type_name: &str, span: Span) -> LisetteDiagnostic {
243 LisetteDiagnostic::error("Circular type alias")
244 .with_resolve_code("circular_type_alias")
245 .with_span_label(&span, format!("`{}` references itself", type_name))
246 .with_help("Type aliases cannot be recursive")
247}
248
249pub fn const_disallows_composite(span: Span) -> LisetteDiagnostic {
250 LisetteDiagnostic::error("Composite value in `const`")
251 .with_infer_code("const_disallows_composite")
252 .with_span_label(&span, "not allowed")
253 .with_help("`const` only accepts primitive values: `bool`, `int`, `float`, and `string`")
254}
255
256pub fn const_cycle(cycle: &[String], span: Span) -> LisetteDiagnostic {
257 let mut diagnostic = LisetteDiagnostic::error("`const` init cycle")
258 .with_infer_code("const_cycle")
259 .with_help(
260 "`const` initializers cannot refer to themselves, either directly or transitively",
261 );
262 diagnostic = if cycle.len() == 1 {
263 diagnostic.with_span_label(&span, "self-reference")
264 } else {
265 let chain = cycle
266 .iter()
267 .map(|name| format!("`{}`", name))
268 .collect::<Vec<_>>()
269 .join(" → ");
270 diagnostic.with_span_label(&span, format!("cycle: {} → `{}`", chain, cycle[0]))
271 };
272 diagnostic
273}
274
275pub fn name_not_found(
276 variable_name: &str,
277 span: Span,
278 available_names: &[String],
279 expected_ty: Option<&Type>,
280) -> LisetteDiagnostic {
281 if matches!(variable_name, "nil" | "null" | "Nil" | "undefined") {
282 let help = nil_help_for(expected_ty);
283 return LisetteDiagnostic::error(format!("`{}` is not supported", variable_name))
284 .with_resolve_code("nil_not_supported")
285 .with_span_label(&span, "does not exist")
286 .with_help(help);
287 }
288
289 if let Some(hint) = go_builtin_hint(variable_name) {
290 return LisetteDiagnostic::error("Name not found")
291 .with_resolve_code("name_not_found")
292 .with_span_label(&span, "name not found in scope")
293 .with_help(hint);
294 }
295
296 let mut diagnostic = LisetteDiagnostic::error("Name not found")
297 .with_resolve_code("name_not_found")
298 .with_span_label(&span, "name not found in scope");
299
300 let suggestion = available_names
301 .iter()
302 .filter_map(|c| {
303 let d = levenshtein_distance(variable_name, c);
304 (d <= 2).then_some((c, d))
305 })
306 .min_by_key(|(_, d)| *d)
307 .map(|(c, _)| c.clone());
308
309 if let Some(suggestion) = suggestion {
310 diagnostic = diagnostic.with_help(format!("Did you mean `{}`?", suggestion));
311 } else {
312 diagnostic = diagnostic.with_help(format!("Define or import `{}`.", variable_name));
313 }
314
315 diagnostic
316}
317
318fn nil_help_for(expected_ty: Option<&Type>) -> String {
320 match expected_ty {
321 Some(ty) if ty.is_slice() => format!("For an empty `{}`, use `[]`.", ty),
322 Some(ty) if ty.is_map() => format!("For an empty `{}`, use `Map.new()`.", ty),
323 _ => {
324 "Absence is encoded with `Option<T>` in Lisette. Use `None` to represent absent values."
325 .to_string()
326 }
327 }
328}
329
330pub fn self_in_static_method(span: Span) -> LisetteDiagnostic {
331 LisetteDiagnostic::error("Invalid `self`")
332 .with_resolve_code("self_in_static_method")
333 .with_span_label(&span, "`self` is not available here")
334 .with_help("Add a `self` parameter to the method if you need an instance method")
335}
336
337pub fn static_method_called_on_instance(
338 method_name: &str,
339 type_name: &str,
340 span: Span,
341) -> LisetteDiagnostic {
342 LisetteDiagnostic::error("Static method called on instance")
343 .with_infer_code("static_method_on_instance")
344 .with_span_label(&span, format!("`{}` is a static method", method_name))
345 .with_help(format!(
346 "Call it as `{}.{}(...)` on the type, not on an instance",
347 type_name, method_name
348 ))
349}
350
351pub fn function_or_value_not_found_in_module(name: &str, span: Span) -> LisetteDiagnostic {
352 LisetteDiagnostic::error("Name not found")
353 .with_resolve_code("not_found_in_module")
354 .with_span_label(&span, format!("`{}` not found in module", name))
355 .with_help("Ensure the name is exported and spelled correctly")
356}
357
358pub fn receiver_type_mismatch(
359 impl_type: &str,
360 receiver_type: &str,
361 span: Span,
362) -> LisetteDiagnostic {
363 LisetteDiagnostic::error("Type mismatch")
364 .with_infer_code("receiver_type_mismatch")
365 .with_span_label(
366 &span,
367 format!(
368 "expected `{}` or `Ref<{}>`, found `{}`",
369 impl_type, impl_type, receiver_type
370 ),
371 )
372 .with_help(format!(
373 "Change the receiver type to `{}` or `Ref<{}>`",
374 impl_type, impl_type
375 ))
376}
377
378pub fn receiver_must_be_named_self(actual_name: &str, span: Span) -> LisetteDiagnostic {
379 LisetteDiagnostic::error("Invalid receiver name")
380 .with_infer_code("receiver_not_self")
381 .with_span_label(&span, "expected `self`")
382 .with_help(format!(
383 "Rename `{}` to `self`. In an instance method definition, Lisette expects the first parameter to be named `self`",
384 actual_name
385 ))
386}
387
388pub fn stringer_signature_mismatch(method_name: &str, span: Span) -> LisetteDiagnostic {
389 LisetteDiagnostic::error("Reserved method signature")
390 .with_infer_code("stringer_signature_mismatch")
391 .with_span_label(
392 &span,
393 format!("`{}` must have signature `(self) -> string`", method_name),
394 )
395 .with_help(format!(
396 "`{}` is reserved for the Go `fmt.Stringer` (or `fmt.GoStringer`) interface and is auto-emitted by Lisette. Either change the signature to `(self) -> string`, or rename the method",
397 method_name
398 ))
399}
400
401pub fn json_method_override(method_name: &str, span: Span) -> LisetteDiagnostic {
402 LisetteDiagnostic::error("Reserved JSON method")
403 .with_infer_code("json_method_override")
404 .with_span_label(
405 &span,
406 format!("`{}` collides with the method generated by `#[json]`", method_name),
407 )
408 .with_help(
409 "`#[json]` already generates `MarshalJSON` and `UnmarshalJSON`. Remove this method, or drop `#[json]` and write both yourself",
410 )
411}
412
413pub fn json_non_serializable_field(span: &Span, kind: &str, skippable: bool) -> LisetteDiagnostic {
414 let fix = if skippable {
415 " Drop the field, or exclude it with `#[json(skip)]`."
416 } else {
417 " Remove it from the variant."
418 };
419 LisetteDiagnostic::error("Non-serializable field in a `#[json]` type")
420 .with_infer_code("json_non_serializable_field")
421 .with_span_label(span, format!("a {kind} cannot be JSON-encoded"))
422 .with_help(format!("Go's `encoding/json` cannot marshal {kind}s.{fix}"))
423}
424
425pub fn disallowed_mutation(
426 variable_name: &str,
427 span: Span,
428 self_type_name: Option<&str>,
429 is_pattern_binding: bool,
430 is_const_binding: bool,
431) -> LisetteDiagnostic {
432 if variable_name == "self" {
433 if let Some(type_name) = self_type_name {
434 LisetteDiagnostic::error("Immutable receiver")
435 .with_infer_code("value_receiver_immutable")
436 .with_span_label(&span, "receiver is immutable")
437 .with_help(format!(
438 "Use `self: Ref<{type_name}>` to make the receiver mutable"
439 ))
440 } else {
441 LisetteDiagnostic::error("Immutable receiver")
442 .with_infer_code("value_receiver_immutable")
443 .with_span_label(&span, "receiver is immutable")
444 .with_help("Use `self: Ref<Self>` to make the receiver mutable")
445 }
446 } else if is_const_binding {
447 LisetteDiagnostic::error("Cannot mutate `const`")
448 .with_infer_code("immutable")
449 .with_span_label(&span, "cannot mutate a `const`")
450 .with_help(format!(
451 "`const` bindings are immutable. Rebind with `let mut {variable_name} = {variable_name}` to mutate a local copy"
452 ))
453 } else if is_pattern_binding {
454 LisetteDiagnostic::error("Immutable variable")
455 .with_infer_code("immutable")
456 .with_span_label(&span, "cannot mutate an immutable variable")
457 .with_help(format!(
458 "Pattern bindings are immutable; rebind with `let mut {variable_name} = {variable_name}` to mutate"
459 ))
460 } else {
461 LisetteDiagnostic::error("Immutable variable")
462 .with_infer_code("immutable")
463 .with_span_label(&span, "cannot mutate an immutable variable")
464 .with_help(format!(
465 "Declare using `let mut {variable_name}` to make the variable mutable"
466 ))
467 }
468}
469
470pub fn self_reference_in_assignment(span: Span) -> LisetteDiagnostic {
471 LisetteDiagnostic::error("Cannot reassign variable while taking its reference")
472 .with_infer_code("self_reference_in_assignment")
473 .with_span_label(&span, "disallowed")
474 .with_help("Separate the reassignment from reference taking, or use a different variable")
475}
476
477pub fn uppercase_binding(span: Span, name: &str) -> LisetteDiagnostic {
478 LisetteDiagnostic::error("Invalid binding name")
479 .with_infer_code("uppercase_binding")
480 .with_span_label(&span, "binding names must start with a lowercase letter")
481 .with_help(format!("Use a lowercase name instead of `{}`", name))
482}
483
484pub fn enum_variant_constructor_not_found(
485 span: Span,
486 enum_info: Option<(&str, &[String])>,
487 variant_name: &str,
488 bare_allowed: bool,
489) -> LisetteDiagnostic {
490 let help = if let Some((enum_name, variants)) = enum_info {
491 if variants.iter().any(|v| v == variant_name) {
492 if bare_allowed {
493 format!("Use `{}` to match this variant", variant_name)
494 } else {
495 format!("Use `{}.{}` to match this variant", enum_name, variant_name)
496 }
497 } else if let Some(closest) = variants
498 .iter()
499 .filter_map(|v| {
500 let d = levenshtein_distance(variant_name, v);
501 (d <= 2).then_some((v, d))
502 })
503 .min_by_key(|(_, d)| *d)
504 .map(|(v, _)| v)
505 {
506 if bare_allowed {
507 format!("Did you mean `{}`?", closest)
508 } else {
509 format!("Did you mean `{}.{}`?", enum_name, closest)
510 }
511 } else {
512 let variants_fmt = if bare_allowed {
513 format_list(variants, |v| format!("`{}`", v))
514 } else {
515 format_list(variants, |v| format!("`{}.{}`", enum_name, v))
516 };
517 format!(
518 "Available variants for `{}` are {}",
519 enum_name, variants_fmt
520 )
521 }
522 } else {
523 "Check that the variant is defined in the enum and spelled correctly".to_string()
524 };
525
526 LisetteDiagnostic::error("Variant not found")
527 .with_resolve_code("variant_not_found")
528 .with_span_label(&span, "not found")
529 .with_help(help)
530}
531
532pub fn const_pattern_not_eligible(name: &str, span: Span) -> LisetteDiagnostic {
533 LisetteDiagnostic::error("Pattern target is not a matchable value")
534 .with_infer_code("const_pattern_not_eligible")
535 .with_span_label(&span, "a function cannot be a match pattern")
536 .with_help(format!(
537 "`{}` is a function or method value. Const patterns match named constants or package-level values that the compiler can emit as a Go `case`, not callables.",
538 name
539 ))
540}
541
542pub fn const_pattern_outside_match_arm(name: &str, span: Span) -> LisetteDiagnostic {
543 LisetteDiagnostic::error("Const pattern outside a match arm")
544 .with_infer_code("const_pattern_outside_match_arm")
545 .with_span_label(&span, "const patterns are only allowed in match arms")
546 .with_help(format!(
547 "`{}` is a constant, so this pattern is refutable. Match on it inside a `match` expression, or compare with `==` in a `let` or function parameter.",
548 name
549 ))
550}
551
552pub fn arity_mismatch(
553 expected: &[Type],
554 actual: &[Type],
555 generic_params: &[String],
556 is_constructor: bool,
557 span: Span,
558) -> LisetteDiagnostic {
559 let expected_str = if !generic_params.is_empty() {
560 generic_params.join(", ")
561 } else {
562 expected
563 .iter()
564 .map(|t| t.to_string())
565 .collect::<Vec<_>>()
566 .join(", ")
567 };
568
569 let actual_str = actual
570 .iter()
571 .map(|t| t.to_string())
572 .collect::<Vec<_>>()
573 .join(", ");
574
575 let expected_count = expected.len();
576 let actual_count = actual.len();
577 let expected_word = if expected_count == 1 {
578 "argument"
579 } else {
580 "arguments"
581 };
582 let actual_word = if actual_count == 1 {
583 "argument"
584 } else {
585 "arguments"
586 };
587
588 LisetteDiagnostic::error("Wrong argument count")
589 .with_infer_code("arg_count_mismatch")
590 .with_span_label(
591 &span,
592 format!("expected `({})`, found `({})`", expected_str, actual_str),
593 )
594 .with_help(format!(
595 "This {} expects {} {} but received {} {}",
596 if is_constructor {
597 "constructor"
598 } else {
599 "function"
600 },
601 expected_count,
602 expected_word,
603 actual_count,
604 actual_word
605 ))
606}
607
608pub fn generics_arity_mismatch(
609 expected_generic_params: &[String],
610 actual_type_args: &[Annotation],
611 actual_types: &[Type],
612 span: Span,
613) -> LisetteDiagnostic {
614 let expected: Vec<Type> = expected_generic_params
615 .iter()
616 .map(|param| Type::Parameter(param.as_str().into()))
617 .collect();
618
619 let expected_str = expected
620 .iter()
621 .map(|t| t.to_string())
622 .collect::<Vec<_>>()
623 .join(", ");
624
625 let actual_str = actual_types
626 .iter()
627 .map(|t| t.to_string())
628 .collect::<Vec<_>>()
629 .join(", ");
630
631 let expected_count = expected.len();
632 let actual_count = actual_types.len();
633 let expected_word = if expected_count == 1 {
634 "type parameter"
635 } else {
636 "type parameters"
637 };
638 let actual_word = if actual_count == 1 {
639 "type parameter"
640 } else {
641 "type parameters"
642 };
643
644 let generics_span =
645 if let (Some(first), Some(last)) = (actual_type_args.first(), actual_type_args.last()) {
646 let first_span = first.get_span();
647 let last_span = last.get_span();
648 Span::new(
649 first_span.file_id,
650 first_span.byte_offset.saturating_sub(1),
651 (last_span.byte_offset + last_span.byte_length + 1)
652 .saturating_sub(first_span.byte_offset.saturating_sub(1)),
653 )
654 } else {
655 span
656 };
657
658 LisetteDiagnostic::error("Wrong type argument count")
659 .with_infer_code("type_arg_count_mismatch")
660 .with_span_label(
661 &generics_span,
662 format!("expected `<{}>`, found `<{}>`", expected_str, actual_str),
663 )
664 .with_help(format!(
665 "This type expects {} {} but received {} {}",
666 expected_count, expected_word, actual_count, actual_word
667 ))
668}
669
670pub fn tuple_arity_mismatch(
671 pattern_arity: usize,
672 expected_arity: usize,
673 span: Span,
674) -> LisetteDiagnostic {
675 let expected_word = if expected_arity == 1 {
676 "element"
677 } else {
678 "elements"
679 };
680 let actual_word = if pattern_arity == 1 {
681 "element"
682 } else {
683 "elements"
684 };
685 LisetteDiagnostic::error("Tuple arity mismatch")
686 .with_infer_code("tuple_element_count_mismatch")
687 .with_span_label(
688 &span,
689 format!(
690 "expected {} {}, found {} {}",
691 expected_arity, expected_word, pattern_arity, actual_word
692 ),
693 )
694 .with_help("Adjust the pattern to match the number of elements in the tuple.")
695}
696
697pub fn struct_not_found(identifier: &str, span: Span) -> LisetteDiagnostic {
698 let simple_name = identifier.rsplit('.').next().unwrap_or(identifier);
699 let qualifier_offset = (identifier.len() - simple_name.len()) as u32;
700 let name_span = Span::new(
701 span.file_id,
702 span.byte_offset + qualifier_offset,
703 simple_name.len() as u32,
704 );
705
706 LisetteDiagnostic::error("Struct not found")
707 .with_resolve_code("struct_not_found")
708 .with_span_label(&name_span, "struct not found in scope")
709 .with_help("Define or import this struct")
710}
711
712pub fn struct_missing_fields(
713 struct_name: &str,
714 missing: &[String],
715 span: Span,
716) -> LisetteDiagnostic {
717 let fields_list = missing.join(", ");
718
719 let simple_name = struct_name.rsplit('.').next().unwrap_or(struct_name);
720 let qualifier_offset = (struct_name.len() - simple_name.len()) as u32;
721 let name_span = Span::new(
722 span.file_id,
723 span.byte_offset + qualifier_offset,
724 simple_name.len() as u32,
725 );
726
727 LisetteDiagnostic::error(format!("Struct `{}` is missing fields", simple_name))
728 .with_infer_code("missing_struct_fields")
729 .with_span_label(&name_span, format!("missing fields: {}", fields_list))
730 .with_help("Initialize all fields, or add `..` to zero-fill the rest")
731}
732
733pub fn pattern_missing_fields(missing: &[String], span: Span) -> LisetteDiagnostic {
734 let (noun, fields_fmt) = if missing.len() == 1 {
735 ("field", format!("`{}`", missing[0]))
736 } else {
737 let formatted: Vec<String> = missing.iter().map(|f| format!("`{}`", f)).collect();
738 ("fields", formatted.join(", "))
739 };
740
741 let pronoun = if missing.len() == 1 { "it" } else { "them" };
742
743 LisetteDiagnostic::error("Missing pattern fields")
744 .with_infer_code("pattern_missing_fields")
745 .with_span_label(&span, format!("missing {}", fields_fmt))
746 .with_help(format!(
747 "Include the missing {}, or use `..` to ignore {}",
748 noun, pronoun
749 ))
750}
751
752pub fn private_field_access(field_name: &str, struct_name: &str, span: Span) -> LisetteDiagnostic {
753 LisetteDiagnostic::error("Private field")
754 .with_resolve_code("private_field_access")
755 .with_span_label(&span, "private")
756 .with_help(format!(
757 "Cannot access private field `{}` of struct `{}`. Mark the field as `pub`.",
758 field_name, struct_name
759 ))
760}
761
762pub fn private_method_access(method_name: &str, type_name: &str, span: Span) -> LisetteDiagnostic {
763 LisetteDiagnostic::error("Private method")
764 .with_resolve_code("private_method_access")
765 .with_span_label(&span, "private")
766 .with_help(format!(
767 "Cannot access private method `{}` of type `{}`. Mark the method as `pub`.",
768 method_name, type_name
769 ))
770}
771
772pub fn private_field_in_spread(
773 field_name: &str,
774 struct_name: &str,
775 span: Span,
776) -> LisetteDiagnostic {
777 LisetteDiagnostic::error("Private field")
778 .with_resolve_code("private_field_spread")
779 .with_span_label(&span, "private")
780 .with_help(format!(
781 "Cannot spread `{}` because field `{}` is private. Mark the field as `pub`.",
782 struct_name, field_name
783 ))
784}
785
786pub fn private_field_in_zero_fill(
787 field_name: &str,
788 struct_name: &str,
789 owning_module: &str,
790 span: Span,
791) -> LisetteDiagnostic {
792 LisetteDiagnostic::error("Private field")
793 .with_resolve_code("private_field_zero_fill")
794 .with_span_label(&span, "private")
795 .with_help(format!(
796 "`{}` of `{}` cannot be zero-filled because `{}` is private to module `{}`. \
797 Provide an explicit value, or have `{}` expose `{}` as `pub` or offer a \
798 constructor.",
799 field_name, struct_name, field_name, owning_module, owning_module, field_name
800 ))
801}
802
803pub fn field_no_zero(
804 struct_name: &str,
805 field_name: &str,
806 field_ty: &Type,
807 chain: &[&str],
808 private: Option<(&str, &str, &str)>,
809 span: Span,
810) -> LisetteDiagnostic {
811 let main = match private {
812 Some((priv_struct, priv_field, priv_module)) => format!(
813 "`{}` of `{}` cannot be zero-filled because `{}.{}` is private to module `{}`. \
814 Provide an explicit value for `{}`, or have `{}` expose `{}` as `pub`.",
815 field_name,
816 struct_name,
817 priv_struct,
818 priv_field,
819 priv_module,
820 field_name,
821 priv_module,
822 priv_field
823 ),
824 None if chain.is_empty() => format!(
825 "Field `{}` of type `{}` has no zero value. Provide an explicit value, \
826 or wrap the field type in `Option<T>`.",
827 field_name, field_ty
828 ),
829 None => format!(
830 "Field `{}.{}` of type `{}` has no zero value. Provide an explicit value for \
831 `{}`, or wrap the field type in `Option<T>`.",
832 field_name,
833 chain.join("."),
834 field_ty,
835 field_name
836 ),
837 };
838 LisetteDiagnostic::error("Field has no zero value")
839 .with_infer_code("field_no_zero")
840 .with_span_label(&span, "no zero available")
841 .with_help(main)
842}
843
844pub fn unresolved_receiver_type(member: &str, span: Span) -> LisetteDiagnostic {
845 LisetteDiagnostic::error("Cannot infer receiver type")
846 .with_infer_code("unresolved_receiver_type")
847 .with_span_label(
848 &span,
849 format!(
850 "cannot resolve `.{}` because the receiver type is unknown",
851 member
852 ),
853 )
854 .with_help(
855 "Annotate the receiver's binding, e.g. `let x: SomeType = ...` or \
856 `|param: SomeType| ...`.",
857 )
858}
859
860pub fn member_not_found(
861 ty: &Type,
862 field: &str,
863 span: Span,
864 available_fields: Option<&[String]>,
865 unwrap_hint: Option<UnwrapHint>,
866 is_call_target: bool,
867) -> LisetteDiagnostic {
868 let mut diagnostic = LisetteDiagnostic::error("Member not found")
869 .with_infer_code("member_not_found")
870 .with_span_label(&span, format!("no member `{}` on type `{}`", field, ty));
871
872 if matches!(field, "unwrap" | "expect") && (ty.is_option() || ty.is_result() || ty.is_partial())
873 {
874 let help = if ty.is_option() {
875 format!(
876 "Lisette does not provide `{}()`. Use `?` to propagate, `match` to handle both \
877 cases (e.g. `match <expr> {{ Some(x) => x, None => ... }}`), `let else` for \
878 early exit, or `unwrap_or(default)` for a fallback.",
879 field
880 )
881 } else if ty.is_result() {
882 format!(
883 "Lisette does not provide `{}()`. Use `?` to propagate, `match` to handle both \
884 cases (e.g. `match <expr> {{ Ok(x) => x, Err(e) => ... }}`), `let else` for \
885 early exit, or `unwrap_or(default)` for a fallback.",
886 field
887 )
888 } else {
889 format!(
890 "Lisette does not provide `{}()`. The `?` operator is not supported on \
891 `Partial`; use `match` to handle all three cases (e.g. `match <expr> \
892 {{ Ok(x) => ..., Err(e) => ..., Both(x, e) => ... }}`) or `unwrap_or(default)` \
893 for a fallback.",
894 field
895 )
896 };
897 diagnostic = diagnostic.with_help(help);
898 return diagnostic;
899 }
900
901 if let Some(hint) = unwrap_hint {
902 let (wrapper_name, pattern) = match hint.wrapper {
903 UnwrapWrapper::Option => (
904 "Option",
905 format!(
906 "match <expr> {{ Some(x) => x.{}(...), None => ... }}",
907 field
908 ),
909 ),
910 UnwrapWrapper::Result => (
911 "Result",
912 format!(
913 "match <expr> {{ Ok(x) => x.{}(...), Err(e) => ... }}",
914 field
915 ),
916 ),
917 };
918 diagnostic = diagnostic.with_help(format!(
919 "Unwrap the `{}` to extract the `{}` value, then call `{}` on it, e.g. `{}`",
920 wrapper_name, hint.inner_ty, field, pattern
921 ));
922 return diagnostic;
923 }
924
925 let suggestion = available_fields.and_then(|fields| find_similar_name(field, fields));
926
927 if let Some(suggestion) = suggestion {
928 let rendered = if is_call_target {
929 format!("{}()", suggestion)
930 } else {
931 suggestion
932 };
933 diagnostic = diagnostic.with_help(format!("Did you mean `{}`?", rendered));
934 } else {
935 diagnostic = diagnostic.with_help("Ensure the field or method is defined on this type");
936 }
937
938 diagnostic
939}
940
941#[derive(Debug, Clone, Copy)]
942pub enum UnwrapWrapper {
943 Option,
944 Result,
945}
946
947#[derive(Debug, Clone)]
948pub struct UnwrapHint {
949 pub wrapper: UnwrapWrapper,
950 pub inner_ty: Type,
951}
952
953pub fn not_numeric(ty: &Type, span: Span) -> LisetteDiagnostic {
954 LisetteDiagnostic::error("Type mismatch")
955 .with_infer_code("type_mismatch")
956 .with_span_label(&span, format!("expected `int` or `float`, found `{}`", ty))
957 .with_help("The negation operator `-` can only be used with `int` or `float`")
958}
959
960pub fn not_integer(ty: &Type, span: Span) -> LisetteDiagnostic {
961 LisetteDiagnostic::error("Type mismatch")
962 .with_infer_code("type_mismatch")
963 .with_span_label(&span, format!("expected integer type, found `{}`", ty))
964 .with_help("The bitwise complement operator `^` can only be used with integer types")
965}
966
967pub fn not_numeric_for_binary(
968 operator: &BinaryOperator,
969 ty: &Type,
970 span: Span,
971) -> LisetteDiagnostic {
972 LisetteDiagnostic::error("Type mismatch")
973 .with_infer_code("type_mismatch")
974 .with_span_label(&span, format!("expected `int` or `float`, found `{}`", ty))
975 .with_help(format!(
976 "The `{}` operator can only be used with `int` or `float`",
977 operator
978 ))
979}
980
981pub fn not_integer_for_binary(
982 operator: &BinaryOperator,
983 ty: &Type,
984 span: Span,
985) -> LisetteDiagnostic {
986 LisetteDiagnostic::error("Type mismatch")
987 .with_infer_code("type_mismatch")
988 .with_span_label(&span, format!("expected integer type, found `{}`", ty))
989 .with_help(format!(
990 "The `{}` operator can only be used with integer types",
991 operator
992 ))
993}
994
995pub fn binary_operator_type_mismatch(
996 operator: &BinaryOperator,
997 left_ty: &Type,
998 right_ty: &Type,
999 span: Span,
1000) -> LisetteDiagnostic {
1001 let label_msg = format!(
1002 "cannot {} `{}` and `{}`",
1003 operator_verb(operator),
1004 left_ty,
1005 right_ty
1006 );
1007
1008 LisetteDiagnostic::error("Type mismatch")
1009 .with_infer_code("type_mismatch")
1010 .with_span_label(&span, label_msg)
1011 .with_help(format!(
1012 "The `{}` operator {}",
1013 operator,
1014 operator_help(operator)
1015 ))
1016}
1017
1018pub fn not_orderable(ty: &Type, span: Span) -> LisetteDiagnostic {
1019 LisetteDiagnostic::error("Type mismatch")
1020 .with_infer_code("type_mismatch")
1021 .with_span_label(&span, format!("expected orderable, found `{}`", ty))
1022 .with_help("Use comparison operators only with numeric, string, or boolean types")
1023}
1024
1025pub fn param_needs_ordered_bound(param: &str, span: Span) -> LisetteDiagnostic {
1026 LisetteDiagnostic::error("Type mismatch")
1027 .with_infer_code("type_mismatch")
1028 .with_span_label(&span, format!("expected orderable, found `{}`", param))
1029 .with_help(format!(
1030 "`{param}` is an unconstrained type parameter. Add a `cmp.Ordered` bound to compare it, \
1031 e.g. `<{param}: cmp.Ordered>` and add `import \"go:cmp\"` at the top"
1032 ))
1033}
1034
1035pub fn not_comparable(ty: &Type, reason: &str, span: Span) -> LisetteDiagnostic {
1036 LisetteDiagnostic::error("Type mismatch")
1037 .with_infer_code("type_mismatch")
1038 .with_span_label(&span, format!("`{}` cannot be compared with `==`", ty))
1039 .with_help(format!(
1040 "The `==` and `!=` operators cannot be used on {} because they are not comparable in Go",
1041 reason
1042 ))
1043}
1044
1045pub fn not_orderable_bound(span: Span) -> LisetteDiagnostic {
1046 LisetteDiagnostic::error("Bound not satisfied")
1047 .with_infer_code("not_orderable_bound")
1048 .with_span_label(&span, "does not satisfy `cmp.Ordered`")
1049 .with_help(
1050 "The type parameter must be `cmp.Ordered` but the argument is not orderable. \
1051 Relax the bound or pass an argument that satisfies it",
1052 )
1053}
1054
1055pub fn not_comparable_bound(span: Span) -> LisetteDiagnostic {
1056 LisetteDiagnostic::error("Bound not satisfied")
1057 .with_infer_code("not_comparable_bound")
1058 .with_span_label(&span, "does not satisfy `Comparable`")
1059 .with_help(
1060 "The parameter must be `Comparable` but the argument is not comparable. \
1061 Relax the bound or pass an argument that satisfies it",
1062 )
1063}
1064
1065pub fn bound_only_in_value_position(name: &str, span: Span) -> LisetteDiagnostic {
1066 LisetteDiagnostic::error(format!("`{}` is a bound, not a value type", name))
1067 .with_infer_code("bound_only_in_value_position")
1068 .with_span_label(&span, "not allowed here")
1069 .with_help(format!(
1070 "Use `{}` only as a bound to constrain a generic parameter, e.g. `fn f<T: {}>(x: T)`",
1071 name, name
1072 ))
1073}
1074
1075pub fn missing_bound_on_param(
1076 param_name: &str,
1077 required_bound: &str,
1078 span: Span,
1079) -> LisetteDiagnostic {
1080 let short = required_bound.rsplit('.').next().unwrap_or(required_bound);
1081 LisetteDiagnostic::error("Missing bound on type parameter")
1082 .with_infer_code("missing_bound_on_param")
1083 .with_span_label(&span, format!("does not satisfy `{}`", short))
1084 .with_help(format!(
1085 "The parameter must be `{}` but the argument is unbounded. \
1086 Add this bound to the enclosing function: `<{}: {}>`",
1087 required_bound, param_name, required_bound
1088 ))
1089}
1090
1091pub fn division_by_zero(span: Span) -> LisetteDiagnostic {
1092 LisetteDiagnostic::error("Division by zero")
1093 .with_infer_code("division_by_zero")
1094 .with_span_label(&span, "cannot divide by zero")
1095 .with_help("This operation will panic at runtime")
1096}
1097
1098pub fn incompatible_named_types(underlying_ty: &Type, span: Span) -> LisetteDiagnostic {
1099 LisetteDiagnostic::error("Type mismatch")
1100 .with_infer_code("incompatible_named_types")
1101 .with_span_label(&span, "cannot compute")
1102 .with_help(format!(
1103 "Cast one to the other's type, or convert both to `{}`",
1104 underlying_ty
1105 ))
1106}
1107
1108pub fn named_primitive_needs_cast(
1109 primitive_ty: &Type,
1110 named_ty: &Type,
1111 span: Span,
1112) -> LisetteDiagnostic {
1113 LisetteDiagnostic::error("Type mismatch")
1114 .with_infer_code("type_mismatch")
1115 .with_span_label(
1116 &span,
1117 format!("cannot compute `{}` with `{}`", primitive_ty, named_ty),
1118 )
1119 .with_help(format!("Cast with `as`, e.g. `value as {}`", named_ty))
1120}
1121
1122pub fn invalid_division_order(
1123 operator: &BinaryOperator,
1124 left_ty: &Type,
1125 right_ty: &Type,
1126 span: Span,
1127) -> LisetteDiagnostic {
1128 let (op_symbol, help_msg) = match operator {
1129 BinaryOperator::Division => (
1130 "/",
1131 format!(
1132 "To divide by `{}`, the dividend (left operand) must also be `{}`",
1133 right_ty, right_ty
1134 ),
1135 ),
1136 BinaryOperator::Remainder => (
1137 "%",
1138 format!(
1139 "To take the remainder by `{}`, the dividend (left operand) must also be `{}`",
1140 right_ty, right_ty
1141 ),
1142 ),
1143 _ => unreachable!(),
1144 };
1145
1146 LisetteDiagnostic::error("Invalid operation")
1147 .with_infer_code("invalid_division_order")
1148 .with_span_label(
1149 &span,
1150 format!("cannot compute `{}` {} `{}`", left_ty, op_symbol, right_ty),
1151 )
1152 .with_help(help_msg)
1153}
1154
1155pub fn branch_type_mismatch(
1156 consequence_ty: &Type,
1157 consequence_span: Span,
1158 alternative_ty: &Type,
1159 alternative_span: Span,
1160) -> LisetteDiagnostic {
1161 LisetteDiagnostic::error("Type mismatch")
1162 .with_infer_code("type_mismatch")
1163 .with_span_label(
1164 &consequence_span,
1165 format!("this branch returns `{}`", consequence_ty),
1166 )
1167 .with_span_label(
1168 &alternative_span,
1169 format!("this branch returns `{}`", alternative_ty),
1170 )
1171 .with_help("All branches must return the same type")
1172}
1173
1174pub fn let_else_must_diverge(span: Span) -> LisetteDiagnostic {
1175 LisetteDiagnostic::error("Invalid `else` block")
1176 .with_infer_code("let_else_must_diverge")
1177 .with_span_primary_label(&span, "this branch does not diverge")
1178 .with_help("Add `return`, `break`, `continue`, or a diverging call in the `else` block")
1179}
1180
1181pub fn return_outside_function(span: Span) -> LisetteDiagnostic {
1182 LisetteDiagnostic::error("`return` outside function")
1183 .with_infer_code("return_outside_function")
1184 .with_span_label(&span, "`return` outside function")
1185 .with_help("Use `return` only inside a function body")
1186}
1187
1188pub fn disallowed_mut_use(span: Span) -> LisetteDiagnostic {
1189 LisetteDiagnostic::error("Invalid `mut`")
1190 .with_infer_code("mut_not_allowed")
1191 .with_span_label(&span, "not allowed here")
1192 .with_help("`mut` is not allowed with destructuring patterns")
1193}
1194
1195pub fn cannot_match_on_functions(span: Span) -> LisetteDiagnostic {
1196 LisetteDiagnostic::error("Invalid pattern")
1197 .with_infer_code("invalid_pattern")
1198 .with_span_label(&span, "cannot pattern match on functions")
1199 .with_help("Functions cannot be compared for equality")
1200}
1201
1202pub fn cannot_match_on_unknown(span: Span) -> LisetteDiagnostic {
1203 LisetteDiagnostic::error("Cannot match on Unknown")
1204 .with_infer_code("cannot_match_on_unknown")
1205 .with_span_label(&span, "is type `Unknown`")
1206 .with_help("Use `assert_type` to narrow this value into a concrete type before matching. Example: `let value = assert_type<MyType>(x)?`")
1207}
1208
1209pub fn cannot_match_on_unconstrained_type(span: Span) -> LisetteDiagnostic {
1210 LisetteDiagnostic::error("Uninferred type")
1211 .with_infer_code("cannot_match_on_unconstrained_type")
1212 .with_span_label(&span, "type cannot be inferred at this point")
1213 .with_help("Add a type annotation on the value before matching on it")
1214}
1215
1216pub fn duplicate_binding_in_pattern(
1217 name: &str,
1218 first_span: Span,
1219 second_span: Span,
1220) -> LisetteDiagnostic {
1221 LisetteDiagnostic::error("Duplicate binding")
1222 .with_infer_code("duplicate_binding_in_pattern")
1223 .with_span_label(&first_span, format!("first use of `{}`", name))
1224 .with_span_label(&second_span, "used again")
1225 .with_help("Remove the duplicate binding")
1226}
1227
1228pub fn literal_pattern_in_binding(span: Span) -> LisetteDiagnostic {
1229 LisetteDiagnostic::error("Pattern might not match")
1230 .with_infer_code("literal_in_binding")
1231 .with_span_label(&span, "value might not equal this literal")
1232 .with_help("Use `match` or `if` to compare values")
1233}
1234
1235pub fn as_binding_in_irrefutable_context(span: Span) -> LisetteDiagnostic {
1236 LisetteDiagnostic::error("Invalid `as` binding")
1237 .with_infer_code("as_binding_in_irrefutable_context")
1238 .with_span_label(&span, "`as` is disallowed here")
1239 .with_help("Use `as` only in `match`, `if let`, and `while let`")
1240}
1241
1242pub fn select_some_as_binding_not_supported(span: Span) -> LisetteDiagnostic {
1243 LisetteDiagnostic::error("Cannot alias `Some(...)` in select")
1244 .with_infer_code("select_some_as_not_supported")
1245 .with_span_label(&span, "`as` cannot be placed around `Some(...)`")
1246 .with_help(
1247 "Place `as` inside `Some(...)` to bind the received value: `Some(value as alias)`",
1248 )
1249}
1250
1251pub fn redundant_as_identifier(inner: &str, alias: &str, span: Span) -> LisetteDiagnostic {
1252 LisetteDiagnostic::error("Redundant `as` binding")
1253 .with_infer_code("redundant_as_binding")
1254 .with_span_label(&span, format!("`{}` already binds this value", inner))
1255 .with_help(format!(
1256 "Use `{}` directly, or rename `{}` to `{}`",
1257 alias, inner, alias
1258 ))
1259}
1260
1261pub fn redundant_as_wildcard(alias: &str, span: Span) -> LisetteDiagnostic {
1262 LisetteDiagnostic::error("Redundant `as` binding")
1263 .with_infer_code("redundant_as_binding")
1264 .with_span_label(&span, "`_` binds nothing")
1265 .with_help(format!("Replace `_ as {}` with just `{}`", alias, alias))
1266}
1267
1268pub fn redundant_as_literal(literal: &str, alias: &str, span: Span) -> LisetteDiagnostic {
1269 LisetteDiagnostic::error("Redundant `as` binding")
1270 .with_infer_code("redundant_as_binding")
1271 .with_span_label(&span, format!("`{}` is always `{}`", alias, literal))
1272 .with_help(format!(
1273 "Replace `{} as {}` with just `{}`",
1274 literal, alias, literal
1275 ))
1276}
1277
1278pub fn or_pattern_in_irrefutable_context(span: Span) -> LisetteDiagnostic {
1279 LisetteDiagnostic::error("Invalid or-pattern")
1280 .with_infer_code("or_pattern_in_irrefutable")
1281 .with_span_label(&span, "or-patterns are not allowed here")
1282 .with_help("Use a `match` expression instead.")
1283 .with_note("Or-patterns can only be used in `match`, `if let`, and `while let`.")
1284}
1285
1286pub fn or_pattern_binding_mismatch(
1287 span: Span,
1288 missing_in_later: &[&str],
1289 missing_in_first: &[&str],
1290) -> LisetteDiagnostic {
1291 let missing = if !missing_in_later.is_empty() {
1292 missing_in_later.join(", ")
1293 } else {
1294 missing_in_first.join(", ")
1295 };
1296
1297 LisetteDiagnostic::error("Invalid or-pattern")
1298 .with_infer_code("or_pattern_binding_mismatch")
1299 .with_span_label(&span, "only bound here")
1300 .with_help(format!(
1301 "Variable {} is not bound in all alternatives. Use a wildcard `_` instead of a binding, or ensure all alternatives bind the same variable",
1302 missing
1303 ))
1304}
1305
1306pub fn or_pattern_type_mismatch(span: Span, first_ty: &str, alt_ty: &str) -> LisetteDiagnostic {
1307 LisetteDiagnostic::error("Invalid or-pattern")
1308 .with_infer_code("or_pattern_type_mismatch")
1309 .with_span_label(
1310 &span,
1311 format!("expected `{}`, found `{}`", first_ty, alt_ty),
1312 )
1313 .with_help(
1314 "Use a wildcard `_` instead of a binding, or use separate match arms for each variant",
1315 )
1316}
1317
1318pub fn unknown_iterable_type(span: Span) -> LisetteDiagnostic {
1319 LisetteDiagnostic::error("Uninferrable type")
1320 .with_infer_code("type_not_inferred")
1321 .with_span_label(&span, "cannot be inferred")
1322 .with_help("Add a type annotation to the iterable expression")
1323}
1324
1325pub fn not_iterable(ty: &Type, span: Span) -> LisetteDiagnostic {
1326 LisetteDiagnostic::error("Not iterable")
1327 .with_infer_code("not_iterable")
1328 .with_span_label(&span, format!("`{}` is not iterable", ty))
1329 .with_help("Use `Slice`, `Map`, `Range`, `Channel`, or `string`")
1330}
1331
1332pub fn tuple_literal_required_in_loop(span: Span) -> LisetteDiagnostic {
1333 LisetteDiagnostic::error("Invalid loop pattern")
1334 .with_infer_code("invalid_pattern")
1335 .with_span_label(&span, "tuple literal required here")
1336 .with_help("Use `(key, value)` destructuring pattern for map or enumerated iteration")
1337}
1338
1339pub fn propagate_on_partial(span: Span) -> LisetteDiagnostic {
1340 LisetteDiagnostic::error("Cannot use `?` on `Partial`")
1341 .with_infer_code("propagate_on_partial")
1342 .with_span_label(&span, "`Partial` requires explicit `match`")
1343 .with_help(
1344 "The `?` operator is incompatible with `Partial` because it has \
1345 three variants. Use `match` to handle `Ok`, `Err`, and `Both` \
1346 explicitly.",
1347 )
1348}
1349
1350pub fn try_requires_result_or_option(span: Span) -> LisetteDiagnostic {
1351 LisetteDiagnostic::error("Type mismatch")
1352 .with_infer_code("try_requires_result_or_option")
1353 .with_span_label(&span, "expects `Result` or `Option`")
1354 .with_help("Use the `?` operator only on `Result` or `Option`")
1355}
1356
1357pub fn try_outside_function(span: Span) -> LisetteDiagnostic {
1358 LisetteDiagnostic::error("`?` outside function")
1359 .with_infer_code("try_outside_function")
1360 .with_span_label(&span, "`?` outside function")
1361 .with_help("Use `?` only inside a function that returns `Result` or `Option`")
1362}
1363
1364pub fn try_return_type_mismatch(expected: &str, actual_ty: &Type, span: Span) -> LisetteDiagnostic {
1365 LisetteDiagnostic::error("Type mismatch")
1366 .with_infer_code("try_return_type_mismatch")
1367 .with_span_label(
1368 &span,
1369 format!(
1370 "expects `{}`, but function returns `{}`",
1371 expected, actual_ty
1372 ),
1373 )
1374 .with_help(format!(
1375 "Change the function return type to `{}` or remove the `?` operator",
1376 expected
1377 ))
1378}
1379
1380pub fn try_block_empty(span: Span) -> LisetteDiagnostic {
1381 LisetteDiagnostic::error("Empty `try` block")
1382 .with_infer_code("try_block_empty")
1383 .with_span_label(&span, "empty")
1384 .with_help("Ensure the `try` block contains at least one expression")
1385}
1386
1387pub fn try_block_no_question_mark(try_keyword_span: Span) -> LisetteDiagnostic {
1388 LisetteDiagnostic::error("Useless `try` block")
1389 .with_infer_code("try_block_no_question_mark")
1390 .with_span_label(&try_keyword_span, "no `?` operator found")
1391 .with_help("A `try` block must contain at least one `?` for propagation")
1392}
1393
1394pub fn mixed_carriers_in_try_block(span: Span) -> LisetteDiagnostic {
1395 LisetteDiagnostic::error("Mixed `try` block")
1396 .with_infer_code("try_block_mixed_carriers")
1397 .with_span_label(&span, "mixing `Option` and `Result`")
1398 .with_help(
1399 "A `try` block must use either all `Option` operations or all `Result` operations",
1400 )
1401}
1402
1403pub fn break_outside_loop(span: Span) -> LisetteDiagnostic {
1404 LisetteDiagnostic::error("`break` outside loop")
1405 .with_infer_code("break_outside_loop")
1406 .with_span_label(&span, "not inside a loop")
1407 .with_help("`break` can only be used inside `loop`, `for`, or `while`")
1408}
1409
1410pub fn continue_outside_loop(span: Span) -> LisetteDiagnostic {
1411 LisetteDiagnostic::error("`continue` outside loop")
1412 .with_infer_code("continue_outside_loop")
1413 .with_span_label(&span, "not inside a loop")
1414 .with_help("`continue` can only be used inside `loop`, `for`, or `while`")
1415}
1416
1417pub fn nested_function(span: Span) -> LisetteDiagnostic {
1418 LisetteDiagnostic::error("Nested function declaration")
1419 .with_infer_code("nested_function")
1420 .with_span_label(&span, "functions can only be declared at top level")
1421 .with_help("Use a lambda instead: `|x| x + 1` or `|x| { ... }`")
1422}
1423
1424pub fn return_in_try_block(span: Span) -> LisetteDiagnostic {
1425 LisetteDiagnostic::error("`return` in `try` block")
1426 .with_infer_code("try_block_return")
1427 .with_span_label(&span, "not inside a function")
1428 .with_help(
1429 "Use `return` inside a function, or use `Err(...)?` to exit the `try` block early",
1430 )
1431}
1432
1433pub fn break_in_try_block(span: Span) -> LisetteDiagnostic {
1434 LisetteDiagnostic::error("`break` in `try` block")
1435 .with_infer_code("try_block_break")
1436 .with_span_label(&span, "not inside a loop")
1437 .with_help("Use `break` inside a loop, or use `Err(...)?` to exit the `try` block early")
1438}
1439
1440pub fn continue_in_try_block(span: Span) -> LisetteDiagnostic {
1441 LisetteDiagnostic::error("`continue` in `try` block")
1442 .with_infer_code("try_block_continue")
1443 .with_span_label(&span, "not inside a loop")
1444 .with_help("Use `continue` inside a loop, or use `Err(...)?` to exit the `try` block early")
1445}
1446
1447pub fn recover_block_empty(span: Span) -> LisetteDiagnostic {
1448 LisetteDiagnostic::warn("Empty `recover` block")
1449 .with_infer_code("recover_block_empty")
1450 .with_span_label(&span, "empty")
1451 .with_help("Ensure the `recover` block contains at least one expression that may panic")
1452}
1453
1454pub fn recover_cannot_use_question_mark(span: Span) -> LisetteDiagnostic {
1455 LisetteDiagnostic::error("`?` in `recover` block")
1456 .with_infer_code("recover_cannot_use_question_mark")
1457 .with_span_label(&span, "cannot propagate to `recover` block")
1458 .with_help(
1459 "Use a `try` block inside the `recover` block, or handle the `Result` explicitly",
1460 )
1461}
1462
1463pub fn return_in_recover_block(span: Span) -> LisetteDiagnostic {
1464 LisetteDiagnostic::error("`return` in `recover` block")
1465 .with_infer_code("recover_block_return")
1466 .with_span_label(&span, "not allowed inside `recover` block")
1467 .with_help("Remove the `return`, or move it inside a nested function")
1468}
1469
1470pub fn break_in_recover_block(span: Span) -> LisetteDiagnostic {
1471 LisetteDiagnostic::error("`break` in `recover` block")
1472 .with_infer_code("recover_block_break")
1473 .with_span_label(&span, "not allowed inside `recover` block")
1474 .with_help("Remove the `break`, or move it inside a loop within the `recover` block")
1475}
1476
1477pub fn continue_in_recover_block(span: Span) -> LisetteDiagnostic {
1478 LisetteDiagnostic::error("`continue` in `recover` block")
1479 .with_infer_code("recover_block_continue")
1480 .with_span_label(&span, "not allowed inside `recover` block")
1481 .with_help("Remove the `continue`, or move it inside a loop within the `recover` block")
1482}
1483
1484pub fn expected_channel_receive(ty: &Type, span: Span) -> LisetteDiagnostic {
1485 LisetteDiagnostic::error("Expected channel receive")
1486 .with_infer_code("expected_channel_receive")
1487 .with_span_label(&span, format!("`{}` is not a channel receive", ty))
1488 .with_help("Use `ch.receive()` to receive from a channel in select")
1489}
1490
1491pub fn empty_select(span: Span) -> LisetteDiagnostic {
1492 LisetteDiagnostic::error("Empty select")
1493 .with_infer_code("empty_select")
1494 .with_span_label(&span, "select has no arms")
1495 .with_help(
1496 "Add at least one channel operation arm, e.g. `select { ch.receive() => v { ... } }`",
1497 )
1498}
1499
1500pub fn expected_channel_send(span: Span) -> LisetteDiagnostic {
1501 LisetteDiagnostic::error("Expected channel operation")
1502 .with_infer_code("expected_channel_send")
1503 .with_span_label(&span, "not a channel operation")
1504 .with_help("Use `ch.send(value)` or `ch.receive()` in select arms")
1505}
1506
1507pub fn bare_identifier_in_select_receive(span: Span) -> LisetteDiagnostic {
1508 LisetteDiagnostic::error("Invalid select case")
1509 .with_infer_code("bare_identifier_in_select_receive")
1510 .with_span_label(&span, "expected destructuring")
1511 .with_help("`ch.receive()` returns an `Option`, so use `let Some(v) = ch.receive()` to bind the value")
1512}
1513
1514pub fn none_pattern_in_select_receive(span: Span) -> LisetteDiagnostic {
1515 LisetteDiagnostic::error("Invalid select case")
1516 .with_infer_code("none_pattern_in_select_receive")
1517 .with_span_label(&span, "expected match")
1518 .with_help(
1519 "To detect channel close, use `match ch.receive() { Some(v) => ..., None => ... }`",
1520 )
1521}
1522
1523pub fn select_match_missing_some_arm(span: Span) -> LisetteDiagnostic {
1524 LisetteDiagnostic::error("Invalid select match")
1525 .with_infer_code("select_match_missing_some_arm")
1526 .with_span_label(&span, "missing `Some` arm")
1527 .with_help("`None` only handles channel close. Add a `Some(v) => ...` arm to handle received values")
1528}
1529
1530pub fn select_match_missing_none_arm(span: Span) -> LisetteDiagnostic {
1531 LisetteDiagnostic::error("Invalid select match")
1532 .with_infer_code("select_match_missing_none_arm")
1533 .with_span_label(&span, "missing `None` arm")
1534 .with_help("Matching on `ch.receive()` requires handling channel close. Add a `None => ...` arm to handle channel close, or simplify to `let Some(v) = ch.receive() => ...`")
1535}
1536
1537pub fn select_match_duplicate_some_arm(span: Span) -> LisetteDiagnostic {
1538 LisetteDiagnostic::error("Invalid select match")
1539 .with_infer_code("select_match_duplicate_some_arm")
1540 .with_span_label(&span, "duplicate")
1541 .with_help(
1542 "Remove the duplicate `Some` arm. If you need to, use a `match` inside the arm body",
1543 )
1544}
1545
1546pub fn select_match_duplicate_none_arm(span: Span) -> LisetteDiagnostic {
1547 LisetteDiagnostic::error("Invalid select match")
1548 .with_infer_code("select_match_duplicate_none_arm")
1549 .with_span_label(&span, "duplicate")
1550 .with_help(
1551 "Remove the duplicate `None` arm. If you need to, use a `match` inside the arm body",
1552 )
1553}
1554
1555pub fn select_match_guard_not_allowed(span: Span) -> LisetteDiagnostic {
1556 LisetteDiagnostic::error("Invalid select match")
1557 .with_infer_code("select_match_guard_not_allowed")
1558 .with_span_label(&span, "not supported")
1559 .with_help("Match arms inside `select` do not support guards. Move the condition inside the arm body: `Some(v) => { if condition { ... } }`")
1560}
1561
1562pub fn select_match_invalid_pattern(span: Span) -> LisetteDiagnostic {
1563 LisetteDiagnostic::error("Invalid select match")
1564 .with_infer_code("select_match_invalid_pattern")
1565 .with_span_label(&span, "unsupported pattern")
1566 .with_help("Select match arms support only `Some(...)` and `None` patterns")
1567}
1568
1569pub fn select_receive_refutable_pattern(span: Span) -> LisetteDiagnostic {
1570 LisetteDiagnostic::error("Refutable pattern in select receive")
1571 .with_infer_code("select_receive_refutable_pattern")
1572 .with_span_label(&span, "may not match all received values")
1573 .with_help(
1574 "Select receive requires an irrefutable binding like `Some(v)` or `Some(_)`. \
1575 Use a regular `match` inside the arm body to filter values",
1576 )
1577}
1578
1579pub fn multiple_select_receives(first_span: Span, second_span: Span) -> LisetteDiagnostic {
1580 LisetteDiagnostic::error("Invalid select")
1581 .with_infer_code("multiple_select_receives")
1582 .with_span_label(&first_span, "first receive arm")
1583 .with_span_label(&second_span, "second receive arm")
1584 .with_help("Multiple shorthand receive arms can lead to unexpected behavior when a channel closes. Use `match ch.receive() { Some(v) => ..., None => ... }` to handle closes explicitly")
1585}
1586
1587pub fn duplicate_select_default(first_span: Span, second_span: Span) -> LisetteDiagnostic {
1588 LisetteDiagnostic::error("Invalid select")
1589 .with_infer_code("duplicate_select_default")
1590 .with_span_label(&first_span, "first default arm")
1591 .with_span_label(&second_span, "duplicate default arm")
1592 .with_help(
1593 "A select block can have at most one default arm (`_ => ...`). Remove the duplicate.",
1594 )
1595}
1596
1597pub fn non_exhaustive_select_expression(span: Span) -> LisetteDiagnostic {
1598 LisetteDiagnostic::error("Non-exhaustive select expression")
1599 .with_infer_code("non_exhaustive_select_expression")
1600 .with_span_label(&span, "may not produce a value")
1601 .with_help("Add a default arm `_ => ...` to handle closed channels")
1602}
1603
1604pub fn type_must_be_known(span: Span) -> LisetteDiagnostic {
1605 LisetteDiagnostic::error("Uninferrable type")
1606 .with_infer_code("type_not_inferred")
1607 .with_span_label(&span, "cannot be inferred")
1608 .with_help("Add a type annotation to help the compiler infer the type")
1609}
1610
1611pub fn uninferred_binding(name: &str, span: Span) -> LisetteDiagnostic {
1612 LisetteDiagnostic::error("Uninferrable type")
1613 .with_infer_code("type_not_inferred")
1614 .with_span_label(&span, "cannot be inferred")
1615 .with_help(format!(
1616 "Add a type annotation. For example: `let {}: Slice<int> = ...`",
1617 name
1618 ))
1619}
1620
1621pub fn unconstrained_type_param(param_name: &str, span: Span) -> LisetteDiagnostic {
1622 LisetteDiagnostic::error("Unconstrained type parameter")
1623 .with_infer_code("unconstrained_type_param")
1624 .with_span_label(
1625 &span,
1626 format!(
1627 "`{}` is not constrained by parameters or return type",
1628 param_name
1629 ),
1630 )
1631 .with_help(format!(
1632 "Use `{}` in a parameter or return type, or provide an explicit type argument: `func<SomeType>(...)`",
1633 param_name
1634 ))
1635}
1636
1637pub fn slice_index_type_mismatch(index_ty: &Type, span: Span) -> LisetteDiagnostic {
1638 LisetteDiagnostic::error("Type mismatch")
1639 .with_infer_code("slice_index_type_mismatch")
1640 .with_span_label(&span, format!("expected `int`, found `{}`", index_ty))
1641 .with_help(
1642 "Use an integer to index into a `Slice`. For key-value lookup, use a `Map<K, V>`",
1643 )
1644}
1645
1646pub fn only_slices_and_maps_indexable(ty: &Type, span: Span) -> LisetteDiagnostic {
1647 LisetteDiagnostic::error("Not indexable")
1648 .with_infer_code("not_indexable")
1649 .with_span_label(&span, format!("expected `Slice` or `Map`, found `{}`", ty))
1650 .with_help("Only `Slice` and `Map` can be indexed into")
1651}
1652
1653pub fn string_not_indexable(span: Span, receiver: &str) -> LisetteDiagnostic {
1654 LisetteDiagnostic::error("Cannot index into `string`")
1655 .with_infer_code("string_not_indexable")
1656 .with_span_label(&span, "not indexable")
1657 .with_help(format!(
1658 "Use `{receiver}.rune_at(i)` to get a `rune`, or `{receiver}.byte_at(i)` to get a `byte`"
1659 ))
1660}
1661
1662pub fn string_not_sliceable(span: Span, receiver: &str) -> LisetteDiagnostic {
1663 LisetteDiagnostic::error("Cannot slice into `string`")
1664 .with_infer_code("string_not_sliceable")
1665 .with_span_label(&span, "not sliceable")
1666 .with_help(format!(
1667 "Use `{receiver}.substring(a..b)` for a rune-indexed substring, or `{receiver}.bytes()[a..b]` for a range of bytes"
1668 ))
1669}
1670
1671pub fn string_not_iterable(span: Span, receiver: &str) -> LisetteDiagnostic {
1672 LisetteDiagnostic::error("Cannot iterate over `string`")
1673 .with_infer_code("string_not_iterable")
1674 .with_span_label(&span, "not iterable")
1675 .with_help(format!(
1676 "Use `for r in {receiver}.runes()` for code points, or `for b in {receiver}.bytes()` for bytes"
1677 ))
1678}
1679
1680pub fn colon_in_subscript(
1681 span: Span,
1682 receiver: &str,
1683 type_name: Option<&str>,
1684) -> LisetteDiagnostic {
1685 let (message, label, help) = match type_name {
1686 Some("string") => (
1687 "Invalid syntax for string slicing",
1688 "expected a method call",
1689 format!(
1690 "Use `{receiver}.substring(a..b)` for a string, or `{receiver}.bytes()[a..b]` for a range of bytes"
1691 ),
1692 ),
1693 _ => (
1694 "Invalid syntax for subslicing",
1695 "expected `..`",
1696 format!(
1697 "Use `{receiver}[a..b]` or `{receiver}[a..=b]` for an exclusive or inclusive slice, respectively"
1698 ),
1699 ),
1700 };
1701 LisetteDiagnostic::error(message)
1702 .with_parse_code("colon_in_subscript")
1703 .with_span_label(&span, label)
1704 .with_help(help)
1705}
1706
1707pub fn not_callable(
1708 ty: &Type,
1709 callee_name: Option<&str>,
1710 arg_name: Option<&str>,
1711 span: Span,
1712) -> LisetteDiagnostic {
1713 let type_name = ty.get_name();
1714 let is_type_call = matches!((callee_name, type_name), (Some(c), Some(t)) if c == t);
1715 let is_cast_target = ty.get_underlying().is_some()
1716 || type_name.is_some_and(|n| SimpleKind::from_name(n).is_some());
1717
1718 let help = if is_type_call && is_cast_target {
1719 let subject = arg_name.unwrap_or("value");
1720 format!(
1721 "Use `{} as {}` to cast between types",
1722 subject,
1723 type_name.unwrap()
1724 )
1725 } else {
1726 "Only functions can be called with `()`".to_string()
1727 };
1728
1729 LisetteDiagnostic::error("Not callable")
1730 .with_infer_code("not_callable")
1731 .with_span_label(&span, format!("expected function, found `{}`", ty))
1732 .with_help(help)
1733}
1734
1735pub fn type_conversion_arity(
1736 type_name: &str,
1737 actual_count: usize,
1738 span: Span,
1739) -> LisetteDiagnostic {
1740 LisetteDiagnostic::error("Wrong argument count")
1741 .with_infer_code("type_conversion_arity")
1742 .with_span_label(
1743 &span,
1744 format!("expected 1 argument, found {}", actual_count),
1745 )
1746 .with_help(format!(
1747 "Type conversion `{}(value)` takes exactly one argument — the value to convert",
1748 type_name
1749 ))
1750}
1751
1752#[derive(Debug, Clone)]
1753pub struct InterfaceViolation {
1754 pub interface_name: String,
1755 pub parent_of: Option<String>,
1756 pub missing: Vec<(String, Type)>,
1757 pub incompatible: Vec<(String, Type, Type)>,
1758}
1759
1760pub fn interface_not_implemented(
1761 interface_name: &str,
1762 type_name: &str,
1763 violations: &[InterfaceViolation],
1764 span: Span,
1765) -> LisetteDiagnostic {
1766 let mut help_lines = Vec::new();
1767
1768 let mut missing_sections: Vec<(String, Vec<String>)> = Vec::new();
1769 let mut incompatible_sections: Vec<(String, Vec<String>)> = Vec::new();
1770
1771 for violation in violations {
1772 let header = if let Some(ref parent) = violation.parent_of {
1773 format!(
1774 "From `{}` (required by `{}`)",
1775 violation.interface_name, parent
1776 )
1777 } else {
1778 format!("From `{}`", violation.interface_name)
1779 };
1780
1781 if !violation.missing.is_empty() {
1782 let methods: Vec<String> = violation
1783 .missing
1784 .iter()
1785 .map(|(name, sig)| format!(" - {}: {}", name, sig))
1786 .collect();
1787 missing_sections.push((header.clone(), methods));
1788 }
1789
1790 if !violation.incompatible.is_empty() {
1791 let methods: Vec<String> = violation
1792 .incompatible
1793 .iter()
1794 .map(|(name, expected, actual)| {
1795 format!(" - {}: expected `{}`, found `{}`", name, expected, actual)
1796 })
1797 .collect();
1798 incompatible_sections.push((header, methods));
1799 }
1800 }
1801
1802 if !missing_sections.is_empty() {
1803 help_lines.push("Missing methods:".to_string());
1804 for (header, methods) in &missing_sections {
1805 help_lines.push(format!(" {}", header));
1806 for method in methods {
1807 help_lines.push(format!(" {}", method));
1808 }
1809 }
1810 }
1811
1812 if !incompatible_sections.is_empty() {
1813 help_lines.push("Incompatible methods:".to_string());
1814 for (header, methods) in &incompatible_sections {
1815 help_lines.push(format!(" {}", header));
1816 for method in methods {
1817 help_lines.push(format!(" {}", method));
1818 }
1819 }
1820 }
1821
1822 LisetteDiagnostic::error("Interface not implemented")
1823 .with_infer_code("interface_not_implemented")
1824 .with_span_label(
1825 &span,
1826 format!("`{}` does not implement `{}`", type_name, interface_name),
1827 )
1828 .with_help(help_lines.join("\n"))
1829}
1830
1831#[derive(Debug, Clone, Copy)]
1832pub enum WrapperKind {
1833 Result,
1834 Option,
1835 Partial,
1836}
1837
1838pub fn wrapper_does_not_implement_interface(
1839 interface_name: &str,
1840 wrapper: WrapperKind,
1841 wrapper_ty: &Type,
1842 span: Span,
1843) -> LisetteDiagnostic {
1844 let help = match wrapper {
1845 WrapperKind::Result => "Unwrap the `Result` with `match` or `if let` first.",
1846 WrapperKind::Option => "Unwrap the `Option` with `match` or `if let` first.",
1847 WrapperKind::Partial => "Unwrap the `Partial` with `match` first.",
1848 };
1849 LisetteDiagnostic::error("Interface not implemented")
1850 .with_infer_code("interface_not_implemented")
1851 .with_span_label(
1852 &span,
1853 format!("`{}` does not implement `{}`", wrapper_ty, interface_name),
1854 )
1855 .with_help(help)
1856}
1857
1858pub fn pointer_receiver_interface_mismatch(
1859 interface_name: &str,
1860 type_name: &str,
1861 methods: &[String],
1862 span: Span,
1863) -> LisetteDiagnostic {
1864 let methods_str = methods
1865 .iter()
1866 .map(|m| format!("`{}.{}`", type_name, m))
1867 .collect::<Vec<_>>()
1868 .join(", ");
1869 let takes = if methods.len() == 1 {
1870 format!("{} takes `self: Ref<{}>`", methods_str, type_name)
1871 } else {
1872 format!("{} take `self: Ref<{}>`", methods_str, type_name)
1873 };
1874 LisetteDiagnostic::error("Interface not implemented")
1875 .with_infer_code("interface_not_implemented")
1876 .with_span_label(
1877 &span,
1878 format!("`{}` does not implement `{}`", type_name, interface_name),
1879 )
1880 .with_help(format!("{}, so pass a `Ref<{}>`.", takes, type_name))
1881}
1882
1883pub fn unknown_in_bound_position(span: Span) -> LisetteDiagnostic {
1884 LisetteDiagnostic::error("Invalid `Unknown` bound")
1885 .with_infer_code("unknown_in_bound_position")
1886 .with_span_label(&span, "invalid bound")
1887 .with_help("`Unknown` cannot constrain a generic")
1888}
1889
1890pub fn unknown_in_const_annotation(span: Span) -> LisetteDiagnostic {
1891 LisetteDiagnostic::error("Invalid `Unknown` in `const` annotation")
1892 .with_infer_code("unknown_in_const_annotation")
1893 .with_span_label(&span, "invalid annotation")
1894 .with_help("`Unknown` cannot be used to annotate a constant")
1895}
1896
1897pub fn unknown_as_map_key(span: Span) -> LisetteDiagnostic {
1898 LisetteDiagnostic::error("`Unknown` cannot be used as a map key")
1899 .with_infer_code("unknown_as_map_key")
1900 .with_span_label(&span, "key resolves to `any`")
1901 .with_help("Use a concrete comparable key type.")
1902 .with_note("Go's `map[any]V` admits non-comparable runtime values that panic on insertion.")
1903}
1904
1905pub fn opaque_type_outside_typedef(span: Span) -> LisetteDiagnostic {
1906 LisetteDiagnostic::error("Undefined type")
1907 .with_infer_code("undefined_type_outside_typedef")
1908 .with_span_label(&span, "needs a definition")
1909 .with_help("Use `type Point = ...` to define the type.")
1910 .with_note("Opaque declarations are only allowed in `.d.lis` files.")
1911}
1912
1913pub fn bodyless_function_outside_typedef(span: Span) -> LisetteDiagnostic {
1914 LisetteDiagnostic::error("Missing function body")
1915 .with_infer_code("bodyless_function_outside_typedef")
1916 .with_span_label(&span, "needs a body")
1917 .with_help("Add a body: `fn greet() { ... }`.")
1918 .with_note("Bodyless declarations are only allowed in `.d.lis` files.")
1919}
1920
1921pub fn valueless_const_outside_typedef(span: Span) -> LisetteDiagnostic {
1922 LisetteDiagnostic::error("Missing const value")
1923 .with_infer_code("valueless_const_outside_typedef")
1924 .with_span_label(&span, "needs a value")
1925 .with_help("Ensure the constant has a value: `const MAX_SIZE: int = 100`.")
1926 .with_note("Valueless const declarations are only allowed in `.d.lis` files.")
1927}
1928
1929pub fn valueless_const_missing_annotation(span: Span) -> LisetteDiagnostic {
1930 LisetteDiagnostic::error("Missing const annotation")
1931 .with_infer_code("valueless_const_missing_annotation")
1932 .with_span_label(&span, "needs a type annotation")
1933 .with_help("Valueless const declarations require a type annotation: `const MAX_SIZE: int`")
1934}
1935
1936pub fn variable_declaration_outside_typedef(span: Span) -> LisetteDiagnostic {
1937 LisetteDiagnostic::error("Invalid variable declaration")
1938 .with_infer_code("variable_declaration_outside_typedef")
1939 .with_span_label(&span, "`var` is not allowed here")
1940 .with_help("Use `let` to declare a variable: `let x: int = 0`.")
1941 .with_note("`var` declarations are only allowed in `.d.lis` files.")
1942}
1943
1944pub fn range_full_not_valid_expression(span: Span) -> LisetteDiagnostic {
1945 LisetteDiagnostic::error("Invalid expression")
1946 .with_infer_code("range_full_not_expression")
1947 .with_span_label(&span, "`..` can only be used in slice indexing")
1948 .with_help("Use `arr[..]` to get a full slice, or provide bounds like `0..10`")
1949}
1950
1951pub fn range_not_iterable(range_type: &str, span: Span) -> LisetteDiagnostic {
1952 LisetteDiagnostic::error("Not iterable")
1953 .with_infer_code("range_not_iterable")
1954 .with_span_label(&span, format!("`{}` has no start bound", range_type))
1955 .with_help("Use a range with a start bound, e.g. `0..10` instead of `..10`")
1956}
1957
1958pub fn taking_value_of_ufcs_method(span: Span) -> LisetteDiagnostic {
1959 LisetteDiagnostic::error("Invalid method value")
1960 .with_infer_code("taking_value_of_ufcs_method")
1961 .with_span_label(&span, "taking value not allowed")
1962 .with_help(
1963 "This method cannot be taken as a value. Call the method directly: `obj.method(...)`",
1964 )
1965}
1966
1967pub fn duplicate_definition(kind: &str, name: &str, span: Span) -> LisetteDiagnostic {
1968 LisetteDiagnostic::error(format!("Duplicate {}", kind))
1969 .with_infer_code("duplicate_definition")
1970 .with_span_label(&span, "already defined")
1971 .with_help(format!(
1972 "`{}` is already defined in this module. Rename or remove this definition.",
1973 name
1974 ))
1975}
1976
1977pub fn duplicate_impl_item(item_name: &str, type_name: &str, span: Span) -> LisetteDiagnostic {
1978 LisetteDiagnostic::error("Duplicate name in impl")
1979 .with_infer_code("duplicate_impl_item")
1980 .with_span_label(&span, "method name already taken")
1981 .with_help(format!(
1982 "Method `{}` is already defined for type `{}`. Rename one of the methods.",
1983 item_name, type_name
1984 ))
1985}
1986
1987pub fn duplicate_method_across_specialized_impls(
1988 method_name: &str,
1989 type_name: &str,
1990 generics: &[String],
1991 span: Span,
1992) -> LisetteDiagnostic {
1993 let params = generics.join(", ");
1994 LisetteDiagnostic::error("Duplicate method across specialized `impl` blocks")
1995 .with_infer_code("duplicate_method_across_specialized_impls")
1996 .with_span_label(&span, "already defined in another specialization")
1997 .with_help(format!(
1998 "Specialized `impl` blocks for `{type_name}` share a method namespace. \
1999 Use different method names, or move `{method_name}` to a generic `impl<{params}> {type_name}<{params}> {{}}` block."
2000 ))
2001}
2002
2003pub fn method_shadows_field(type_name: &str, field_name: &str, span: Span) -> LisetteDiagnostic {
2004 LisetteDiagnostic::error("Method shadows struct field")
2005 .with_infer_code("method_shadows_field")
2006 .with_span_label(&span, "same as field")
2007 .with_help(format!(
2008 "`{}` has a field `{}` and a method `{}`. Rename either the field or the method",
2009 type_name, field_name, field_name
2010 ))
2011}
2012
2013pub fn non_int_range_not_iterable(element_ty: &Type, span: Span) -> LisetteDiagnostic {
2014 LisetteDiagnostic::error("Not iterable")
2015 .with_infer_code("non_int_range_not_iterable")
2016 .with_span_label(
2017 &span,
2018 format!("cannot iterate over `Range<{}>`", element_ty),
2019 )
2020 .with_help("Range iteration requires integer bounds")
2021}
2022
2023pub fn only_slices_indexable_by_range(ty: &Type, span: Span) -> LisetteDiagnostic {
2024 LisetteDiagnostic::error("Type mismatch")
2025 .with_infer_code("range_index_not_slice")
2026 .with_span_label(
2027 &span,
2028 format!("expected `Slice` or `string`, found `{}`", ty),
2029 )
2030 .with_help("Range indexing only works on `Slice` and `string`")
2031}
2032
2033pub fn empty_body_return_mismatch(expected_ty: &Type, span: Span) -> LisetteDiagnostic {
2034 LisetteDiagnostic::error("Type mismatch")
2035 .with_infer_code("type_mismatch")
2036 .with_span_label(
2037 &span,
2038 format!("promises `{}`, but returns `()`", expected_ty),
2039 )
2040 .with_help("Return a value or change the return type annotation to `()`.")
2041 .with_note("An empty function body implicitly returns `()`.")
2042}
2043
2044fn operator_verb(operator: &BinaryOperator) -> &'static str {
2045 match operator {
2046 BinaryOperator::Addition => "add",
2047 BinaryOperator::Subtraction => "subtract",
2048 BinaryOperator::Multiplication => "multiply",
2049 BinaryOperator::Division => "divide",
2050 BinaryOperator::Remainder => "get remainder of",
2051 BinaryOperator::BitwiseAnd
2052 | BinaryOperator::BitwiseOr
2053 | BinaryOperator::BitwiseXor
2054 | BinaryOperator::BitwiseAndNot => "apply bitwise operator to",
2055 BinaryOperator::ShiftLeft | BinaryOperator::ShiftRight => "shift",
2056 BinaryOperator::Equal | BinaryOperator::NotEqual => "compare",
2057 BinaryOperator::LessThan
2058 | BinaryOperator::LessThanOrEqual
2059 | BinaryOperator::GreaterThan
2060 | BinaryOperator::GreaterThanOrEqual => "compare",
2061 BinaryOperator::And | BinaryOperator::Or => "apply logical operator to",
2062 BinaryOperator::Pipeline => "pipe",
2063 }
2064}
2065
2066fn operator_help(op: &BinaryOperator) -> &'static str {
2067 match op {
2068 BinaryOperator::Addition => "requires both operands to have the same type",
2069 BinaryOperator::Subtraction
2070 | BinaryOperator::Multiplication
2071 | BinaryOperator::Division
2072 | BinaryOperator::Remainder
2073 | BinaryOperator::BitwiseAnd
2074 | BinaryOperator::BitwiseOr
2075 | BinaryOperator::BitwiseXor
2076 | BinaryOperator::BitwiseAndNot => "requires both operands to have the same integer type",
2077 BinaryOperator::ShiftLeft | BinaryOperator::ShiftRight => {
2078 "requires integer operands (the result type comes from the left operand)"
2079 }
2080 BinaryOperator::Equal | BinaryOperator::NotEqual => {
2081 "requires both operands to have the same type"
2082 }
2083 BinaryOperator::LessThan
2084 | BinaryOperator::LessThanOrEqual
2085 | BinaryOperator::GreaterThan
2086 | BinaryOperator::GreaterThanOrEqual => "requires both operands to have the same type",
2087 BinaryOperator::And | BinaryOperator::Or => "requires both operands to be bool",
2088 BinaryOperator::Pipeline => "should have been desugared",
2089 }
2090}
2091
2092pub fn task_in_expression_position(span: Span) -> LisetteDiagnostic {
2093 LisetteDiagnostic::error("Invalid `task`")
2094 .with_infer_code("task_in_expression_position")
2095 .with_span_label(&span, "produces no value")
2096 .with_help("Move `task` to its own statement")
2097}
2098
2099pub fn defer_in_expression_position(span: Span) -> LisetteDiagnostic {
2100 LisetteDiagnostic::error("Invalid `defer`")
2101 .with_infer_code("defer_in_expression_position")
2102 .with_span_label(&span, "produces no value")
2103 .with_help("Move `defer` to its own statement")
2104}
2105
2106pub fn non_addressable_expression(expression_kind: &str, span: Span) -> LisetteDiagnostic {
2107 LisetteDiagnostic::error("Non-addressable expression")
2108 .with_infer_code("non_addressable_expression")
2109 .with_span_label(&span, format!("cannot take address of {}", expression_kind))
2110 .with_help("Assign the value to a variable first, then take its address")
2111}
2112
2113pub fn non_addressable_const(span: Span) -> LisetteDiagnostic {
2114 LisetteDiagnostic::error("Cannot take address of `const`")
2115 .with_infer_code("non_addressable_const")
2116 .with_span_label(&span, "not addressable")
2117 .with_help(
2118 "`const` bindings are not addressable. Copy the value into a local `let` first if you need a reference",
2119 )
2120}
2121
2122pub fn non_addressable_assignment(expression_kind: &str, span: Span) -> LisetteDiagnostic {
2123 LisetteDiagnostic::error("Cannot assign to non-addressable expression")
2124 .with_infer_code("non_addressable_assignment")
2125 .with_span_label(&span, format!("cannot assign to {}", expression_kind))
2126 .with_help("Assign the value to a variable first, then modify it")
2127}
2128
2129pub fn newtype_field_assignment(type_name: &str, span: Span) -> LisetteDiagnostic {
2130 LisetteDiagnostic::error("Cannot assign to newtype field")
2131 .with_infer_code("newtype_field_assignment")
2132 .with_span_label(&span, "newtype fields are read-only")
2133 .with_help(format!(
2134 "Reconstruct the newtype: `variable = {type_name}(new_value)`"
2135 ))
2136}
2137
2138pub fn interpolation_without_stringer(
2139 type_name: &str,
2140 span: Span,
2141 pointer_newtype: bool,
2142) -> LisetteDiagnostic {
2143 let help = if pointer_newtype {
2144 "Interpolate the inner value directly, or change the representation".to_string()
2145 } else {
2146 format!("Mark `{type_name}` with `#[display]`, or interpolate its fields directly.")
2147 };
2148 LisetteDiagnostic::error(format!("`{type_name}` cannot be interpolated"))
2149 .with_infer_code("interpolation_without_stringer")
2150 .with_span_label(&span, "has no display form")
2151 .with_help(help)
2152}
2153
2154pub fn complex_select_expression(span: Span) -> LisetteDiagnostic {
2155 LisetteDiagnostic::error("Complex expression in `select` arm")
2156 .with_infer_code("complex_select_expression")
2157 .with_span_label(&span, "expected simple expression")
2158 .with_help("Hoist to a `let` binding before the `select`")
2159}
2160
2161pub fn ref_slice_append(span: Span) -> LisetteDiagnostic {
2162 LisetteDiagnostic::error("Cannot call append/extend on `Ref<Slice>`")
2163 .with_infer_code("ref_slice_append")
2164 .with_span_label(&span, "dereference the ref first")
2165 .with_help("Use `r.*.append(x)` to deref, then append")
2166}
2167
2168pub fn map_field_chain_assignment(span: Span) -> LisetteDiagnostic {
2169 LisetteDiagnostic::error("Cannot assign to field of map entry")
2170 .with_infer_code("map_field_chain_assignment")
2171 .with_span_label(&span, "assignment not allowed here")
2172 .with_help(
2173 "Extract, modify, and reinsert: `let mut entry = m[key]; entry.field = value; m[key] = entry`",
2174 )
2175}
2176
2177pub fn enum_field_type_conflict(
2178 loc_a: &str,
2179 type_a: &str,
2180 loc_b: &str,
2181 type_b: &str,
2182 span: Span,
2183) -> LisetteDiagnostic {
2184 LisetteDiagnostic::error("Conflicting field types across enum variants")
2185 .with_infer_code("enum_field_type_conflict")
2186 .with_span_label(&span, "field type mismatch")
2187 .with_help(format!(
2188 "`{loc_a}` is `{type_a}` but `{loc_b}` is `{type_b}`. Rename one of the fields or align their types",
2189 ))
2190}
2191
2192pub fn cannot_auto_address_receiver(
2193 receiver_kind: &str,
2194 method_name: &str,
2195 expected_ty: &Type,
2196 actual_ty: &Type,
2197 span: Span,
2198) -> LisetteDiagnostic {
2199 let readable_kind = match receiver_kind {
2200 "map index expression" => "map lookup",
2201 "function call" => "function result",
2202 "literal" => "literal",
2203 "binary expression" => "expression result",
2204 "conditional expression" => "conditional result",
2205 "match expression" => "match result",
2206 "block expression" => "block result",
2207 "lambda" => "lambda",
2208 "tuple" => "tuple",
2209 "range expression" => "range expression",
2210 _ => "expression",
2211 };
2212
2213 LisetteDiagnostic::error("Expression not modifiable")
2214 .with_infer_code("cannot_auto_address_receiver")
2215 .with_span_label(&span, "modifies its receiver")
2216 .with_help(format!(
2217 "Assign the {} to a variable first, then call the method. The receiver of `{}` is `{}`, not `{}`",
2218 readable_kind, method_name, expected_ty, actual_ty
2219 ))
2220}
2221
2222pub fn break_value_in_non_loop(span: Span) -> LisetteDiagnostic {
2223 LisetteDiagnostic::error("`break` with value in non-`loop` loop")
2224 .with_infer_code("break_value_in_non_loop")
2225 .with_span_label(&span, "`break` with value only allowed in `loop`")
2226 .with_help("`break` with a value is only meaningful in `loop` expressions, which can return the value. In `for` and `while` loops, use `break` without a value.")
2227}
2228
2229pub fn defer_in_loop(span: Span) -> LisetteDiagnostic {
2230 LisetteDiagnostic::error("`defer` inside loop")
2231 .with_infer_code("defer_in_loop")
2232 .with_span_label(&span, "not allowed inside loop")
2233 .with_help("Wrap the loop body in a helper function, e.g. `fn process(file: File) { defer file.close(); ... }` and call it in the loop: `for f in files { process(f); }`")
2234}
2235
2236pub fn empty_range(span: &Span) -> LisetteDiagnostic {
2237 LisetteDiagnostic::error("Empty range")
2238 .with_infer_code("empty_range")
2239 .with_span_label(span, "start is greater than end")
2240 .with_help("Swap the bounds.")
2241}
2242
2243pub fn decimal_file_mode(span: &Span, value: u64) -> LisetteDiagnostic {
2244 LisetteDiagnostic::error("File permission written in decimal")
2245 .with_infer_code("decimal_file_mode")
2246 .with_span_label(span, format!("decimal {value} is octal 0o{value:o}"))
2247 .with_help(
2248 "File permissions are conventionally written in octal. Use a `0o` prefix (for example `0o644`) so the bits line up with the rwx triples.",
2249 )
2250}
2251
2252pub fn empty_infinite_loop(span: &Span) -> LisetteDiagnostic {
2253 LisetteDiagnostic::error("Empty infinite loop")
2254 .with_infer_code("empty_infinite_loop")
2255 .with_span_label(span, "busy-spins")
2256 .with_help(
2257 "An empty `loop` spins forever at 100% CPU. Block on a channel, call `time.Sleep`, or add a `break`.",
2258 )
2259}
2260
2261pub fn empty_select_default(span: &Span) -> LisetteDiagnostic {
2262 LisetteDiagnostic::error("Empty `select` default arm in a loop")
2263 .with_infer_code("empty_select_default")
2264 .with_span_label(span, "busy-spins")
2265 .with_help("Drop the `_ =>` arm to block, or do work in it.")
2266}
2267
2268pub fn repeated_if_condition(span: &Span) -> LisetteDiagnostic {
2269 LisetteDiagnostic::error("`if` condition repeated in `else if`")
2270 .with_infer_code("repeated_if_condition")
2271 .with_span_label(span, "same as prior condition")
2272 .with_help(
2273 "This branch is unreachable, because its condition duplicates the preceding condition. Did you mean a different condition?",
2274 )
2275}
2276
2277pub fn unchanging_loop_condition(span: &Span) -> LisetteDiagnostic {
2278 LisetteDiagnostic::error("Loop condition never changes")
2279 .with_infer_code("unchanging_loop_condition")
2280 .with_span_label(span, "never changes")
2281 .with_help(
2282 "Nothing in the loop body changes this condition, so the loop either never runs or never terminates. Did you forget to update a variable, or mean to `break` out of the loop?",
2283 )
2284}
2285
2286pub fn index_out_of_bounds(span: &Span, index_text: &str) -> LisetteDiagnostic {
2287 LisetteDiagnostic::error("Index out of bounds")
2288 .with_infer_code("index_out_of_bounds")
2289 .with_span_label(span, "out of bounds")
2290 .with_help(format!("Indexing at `{index_text}` will panic at runtime"))
2291}
2292
2293pub fn oversized_shift(
2294 span: &Span,
2295 type_name: &str,
2296 bit_width: u32,
2297 shift_amount: u64,
2298) -> LisetteDiagnostic {
2299 LisetteDiagnostic::error("Shift amount exceeds integer width")
2300 .with_infer_code("oversized_shift")
2301 .with_span_label(span, "shift exceeds width")
2302 .with_help(format!(
2303 "`{type_name}` is {bit_width} bits wide, so shifting by {shift_amount} discards every bit of the value."
2304 ))
2305}
2306
2307pub fn nan_comparison(span: &Span, always_true: bool) -> LisetteDiagnostic {
2308 let result = if always_true { "true" } else { "false" };
2309
2310 LisetteDiagnostic::error("Comparison with NaN")
2311 .with_infer_code("nan_comparison")
2312 .with_span_label(span, format!("always {result}"))
2313 .with_help(
2314 "NaN is unequal to every value including itself. Use `math.IsNaN(x)` to test for NaN.",
2315 )
2316}
2317
2318pub fn deferred_lock(span: Span, locked: &str, unlock: &str) -> LisetteDiagnostic {
2319 LisetteDiagnostic::error(format!("Deferred `{locked}` instead of `{unlock}`"))
2320 .with_infer_code("deferred_lock")
2321 .with_span_label(&span, "re-locks at function exit")
2322 .with_help(format!(
2323 "Deferring `{locked}` re-acquires the lock when the function returns, deadlocking the next caller. Did you mean `{unlock}`?"
2324 ))
2325}
2326
2327pub fn propagate_in_condition(span: Span) -> LisetteDiagnostic {
2328 LisetteDiagnostic::error("`?` cannot be used inside a condition")
2329 .with_infer_code("propagate_in_condition")
2330 .with_span_label(&span, "`?` inside condition")
2331 .with_help("Bind the result first: `let val = expression?; if val { ... }`")
2332}
2333
2334pub fn propagate_in_defer(span: Span) -> LisetteDiagnostic {
2335 LisetteDiagnostic::error("Invalid `?` in `defer`")
2336 .with_infer_code("propagate_in_defer")
2337 .with_span_label(&span, "`?` not allowed here")
2338 .with_help("`defer` in combination with `?` is not allowed due to confusing semantics. Handle the error inside a `defer` block: `defer { if let Err(e) = file.close() { log(e); } }`")
2339}
2340
2341pub fn return_in_defer_block(span: Span) -> LisetteDiagnostic {
2342 LisetteDiagnostic::error("`return` in `defer` block")
2343 .with_infer_code("return_in_defer_block")
2344 .with_span_label(&span, "not allowed inside `defer` block")
2345 .with_help("Remove the `return` as it only exits the `defer` block")
2346}
2347
2348pub fn break_in_defer_block(span: Span) -> LisetteDiagnostic {
2349 LisetteDiagnostic::error("`break` in `defer` block")
2350 .with_infer_code("break_in_defer_block")
2351 .with_span_label(&span, "not allowed inside `defer` block")
2352 .with_help("Remove the `break`, or move it inside a loop within the `defer` block")
2353}
2354
2355pub fn continue_in_defer_block(span: Span) -> LisetteDiagnostic {
2356 LisetteDiagnostic::error("`continue` in `defer` block")
2357 .with_infer_code("continue_in_defer_block")
2358 .with_span_label(&span, "not allowed inside `defer` block")
2359 .with_help("Remove the `continue`, or move it inside a loop within the `defer` block")
2360}
2361
2362pub fn invalid_cast(source_ty: &Type, target_ty: &Type, span: Span) -> LisetteDiagnostic {
2363 let same_constructor_with_unresolved = source_ty
2364 .get_qualified_id()
2365 .zip(target_ty.get_qualified_id())
2366 .is_some_and(|(s, t)| s == t)
2367 && source_ty.has_unbound_variables();
2368
2369 let help = if same_constructor_with_unresolved {
2370 format!(
2371 "Use a type annotation instead: `let x: {} = ...`",
2372 target_ty,
2373 )
2374 } else if source_ty.is_string() {
2375 "Strings cannot be cast to numbers and require explicit conversion. Use `strconv.Atoi()` to parse.".into()
2376 } else if source_ty.is_complex() || target_ty.is_complex() {
2377 "Complex numbers cannot be cast directly. Use `real(c)` or `imaginary(c)` to extract components.".into()
2378 } else if source_ty.has_underlying_rune() && target_ty.has_underlying_byte() {
2379 "rune (int32) is wider than byte (uint8) and may not fit. Use an intermediate variable to cast via int first: `let n = r as int; n as byte`".into()
2380 } else if source_ty.has_underlying_byte() && target_ty.is_string() {
2381 "A byte has two readings as a string. Use `[b] as string` to preserve the byte (may be invalid UTF-8), or cast through a rune to encode as a codepoint: `let r = b as rune; r as string`".into()
2382 } else {
2383 "Casts are supported between numeric types, between string and byte/rune slices, from rune to string, and from concrete types to interfaces.".into()
2384 };
2385
2386 LisetteDiagnostic::error("Invalid cast")
2387 .with_infer_code("invalid_cast")
2388 .with_span_label(
2389 &span,
2390 format!("cannot cast `{}` to `{}`", source_ty, target_ty),
2391 )
2392 .with_help(help)
2393}
2394
2395pub fn chained_cast(span: Span) -> LisetteDiagnostic {
2396 LisetteDiagnostic::error("Invalid cast")
2397 .with_infer_code("chained_cast")
2398 .with_span_label(&span, "chained cast not allowed")
2399 .with_help("Use an intermediate variable if you need to cast through multiple types")
2400}
2401
2402pub fn redundant_cast(ty: &Type, span: Span) -> LisetteDiagnostic {
2403 LisetteDiagnostic::info("Redundant cast")
2404 .with_infer_code("redundant_cast")
2405 .with_span_label(&span, format!("casting `{}` to itself has no effect", ty))
2406 .with_help("Remove the unnecessary cast")
2407}
2408
2409pub fn redundant_assert_type(ty: &Type, span: Span) -> LisetteDiagnostic {
2410 LisetteDiagnostic::info("Redundant type assertion")
2411 .with_infer_code("redundant_assert_type")
2412 .with_span_label(&span, format!("already of type `{}`", ty))
2413 .with_help(format!(
2414 "`assert_type` narrows an `Unknown` value, but this value is already `{}`, so the assertion always succeeds",
2415 ty
2416 ))
2417}
2418
2419pub fn integer_literal_overflow(
2420 target_ty: &str,
2421 min: i128,
2422 max: i128,
2423 span: Span,
2424) -> LisetteDiagnostic {
2425 LisetteDiagnostic::error("Integer literal overflow")
2426 .with_infer_code("integer_literal_overflow")
2427 .with_span_label(&span, format!("overflows `{}`", target_ty))
2428 .with_help(format!(
2429 "`{}` must be in range `{}` to `{}`",
2430 target_ty, min, max
2431 ))
2432}
2433
2434pub fn float_literal_overflow(target_ty: &str, span: Span) -> LisetteDiagnostic {
2435 LisetteDiagnostic::error("Float literal overflow")
2436 .with_infer_code("float_literal_overflow")
2437 .with_span_label(&span, format!("value overflows `{}`", target_ty))
2438 .with_help(format!(
2439 "Use `float64` for larger values, or ensure the value fits in `{}`",
2440 target_ty
2441 ))
2442}
2443
2444pub fn cannot_negate_unsigned(target_ty: &str, span: Span) -> LisetteDiagnostic {
2445 LisetteDiagnostic::error("Cannot negate unsigned type")
2446 .with_infer_code("cannot_negate_unsigned")
2447 .with_span_label(&span, format!("cannot negate `{}`", target_ty))
2448 .with_help("Unsigned types cannot represent negative values")
2449}
2450
2451fn go_builtin_hint(name: &str) -> Option<&'static str> {
2452 match name {
2453 "len" => Some(
2454 "Lisette has no `len` builtin. Use the `.length()` method instead, e.g. `items.length()`.",
2455 ),
2456 "cap" => Some(
2457 "Lisette has no `cap` builtin. Use the `.capacity()` method instead, e.g. `items.capacity()`.",
2458 ),
2459 "make" => Some(
2460 "Lisette has no `make` builtin. Use constructor methods instead, e.g. `Channel.new<int>()`, `Slice.new<int>()`, `Map.new<K, V>()`.",
2461 ),
2462 "append" => Some(
2463 "Lisette has no `append` builtin. Use the `.append()` method instead, e.g. `items.append(1)`.",
2464 ),
2465 "close" => Some(
2466 "Lisette has no `close` builtin. Use the `.close()` method instead, e.g. `ch.close()`.",
2467 ),
2468 "copy" => Some(
2469 "Lisette has no `copy` builtin. Use the `.copy_from()` method instead, e.g. `dst.copy_from(src)`.",
2470 ),
2471 "delete" => Some(
2472 "Lisette has no `delete` builtin. Use the `.delete()` method instead, e.g. `map.delete(key)`.",
2473 ),
2474 "new" => Some(
2475 "Lisette has no `new` builtin. Use constructor methods instead, e.g. `MyStruct { field: value }` or `MyType.new()`.",
2476 ),
2477 "print" | "println" | "printf" => Some(
2478 "Lisette has no `print` builtin. Use `fmt.Println`, `fmt.Printf`, etc. after `import \"go:fmt\"`.",
2479 ),
2480 _ => None,
2481 }
2482}
2483
2484pub fn levenshtein_distance(a: &str, b: &str) -> usize {
2485 let b_len = b.len();
2486
2487 if a.is_empty() {
2488 return b_len;
2489 }
2490 if b_len == 0 {
2491 return a.len();
2492 }
2493
2494 let mut prev: Vec<usize> = (0..=b_len).collect();
2495 let mut curr = vec![0; b_len + 1];
2496
2497 for (i, a_char) in a.chars().enumerate() {
2498 curr[0] = i + 1;
2499 for (j, b_char) in b.chars().enumerate() {
2500 let cost = if a_char == b_char { 0 } else { 1 };
2501 curr[j + 1] = (prev[j + 1] + 1).min(curr[j] + 1).min(prev[j] + cost);
2502 }
2503 std::mem::swap(&mut prev, &mut curr);
2504 }
2505
2506 prev[b_len]
2507}
2508
2509pub fn find_similar_name(name: &str, candidates: &[String]) -> Option<String> {
2512 let best_distance = candidates
2513 .iter()
2514 .filter_map(|c| {
2515 let d = levenshtein_distance(name, c);
2516 (d <= 2).then_some((c, d))
2517 })
2518 .min_by_key(|(_, d)| *d);
2519
2520 let by_prefix = if name.len() >= 2 {
2521 candidates
2522 .iter()
2523 .filter(|c| c.starts_with(name) || name.starts_with(c.as_str()))
2524 .min_by_key(|c| c.len().abs_diff(name.len()))
2525 } else {
2526 None
2527 };
2528
2529 match (best_distance, by_prefix) {
2530 (Some((d, dist)), Some(p)) => {
2531 if dist <= 1 {
2534 Some(d.clone())
2535 } else {
2536 Some(p.clone())
2537 }
2538 }
2539 (Some((d, _)), None) => Some(d.clone()),
2540 (None, Some(p)) => Some(p.clone()),
2541 (None, None) => None,
2542 }
2543}
2544
2545pub fn cannot_infer_type_argument(span: Span) -> LisetteDiagnostic {
2546 LisetteDiagnostic::error("Missing type argument")
2547 .with_infer_code("missing_type_argument")
2548 .with_span_label(&span, "expected type argument")
2549 .with_help("Supply a type argument for the call, e.g. `Channel.new<int>()`")
2550}
2551
2552fn format_list<T, F>(items: &[T], fmt: F) -> String
2553where
2554 F: Fn(&T) -> String,
2555{
2556 match items.len() {
2557 0 => String::new(),
2558 1 => fmt(&items[0]),
2559 2 => format!("{} and {}", fmt(&items[0]), fmt(&items[1])),
2560 _ => {
2561 let mut result = String::new();
2562 for (i, item) in items.iter().enumerate() {
2563 if i > 0 {
2564 result.push_str(", ");
2565 }
2566 if i == items.len() - 1 {
2567 result.push_str("and ");
2568 }
2569 result.push_str(&fmt(item));
2570 }
2571 result
2572 }
2573 }
2574}
2575
2576pub fn recursive_generic_instantiation(type_name: &str, span: Span) -> LisetteDiagnostic {
2577 LisetteDiagnostic::error("Recursive generic instantiation")
2578 .with_infer_code("recursive_instantiation")
2579 .with_span_label(&span, format!("`{}` is nested within itself", type_name))
2580 .with_help(format!(
2581 "Go does not allow recursive type instantiation (e.g., `{0}<{0}<T>>`). \
2582 Use a wrapper type or a different design.",
2583 type_name
2584 ))
2585}
2586
2587pub fn non_comparable_map_key(key_ty: &Type, reason: &str, span: Span) -> LisetteDiagnostic {
2588 LisetteDiagnostic::error("Invalid map key type")
2589 .with_infer_code("non_comparable_map_key")
2590 .with_span_label(&span, format!("`{}` is not comparable", key_ty))
2591 .with_help(format!(
2592 "Map keys must be comparable in Go. {} cannot be used as map keys.",
2593 reason
2594 ))
2595}
2596
2597pub fn ref_of_interface_type(inner_ty: &Type, span: Span) -> LisetteDiagnostic {
2598 LisetteDiagnostic::error("Invalid use of `Ref` with interface")
2599 .with_infer_code("ref_of_interface")
2600 .with_span_label(&span, "not allowed")
2601 .with_help(format!(
2602 "Use `{}` instead of `Ref<{}>`. Interfaces are already reference types in Go.",
2603 inner_ty, inner_ty
2604 ))
2605}
2606
2607pub fn float_modulo_not_supported(span: Span) -> LisetteDiagnostic {
2608 LisetteDiagnostic::error("Invalid operation")
2609 .with_infer_code("float_modulo")
2610 .with_span_label(&span, "`%` is not supported on floating-point types")
2611 .with_help("Use `math.Mod(x, y)` for floating-point modulo")
2612}
2613
2614pub fn recursive_type(type_name: &str, span: Span) -> LisetteDiagnostic {
2615 LisetteDiagnostic::error("Recursive type has infinite size")
2616 .with_infer_code("recursive_type")
2617 .with_span_label(
2618 &span,
2619 format!("`{}` contains itself without indirection", type_name),
2620 )
2621 .with_help(format!(
2622 "Use `Ref<{}>` for indirection. For example: `next: Option<Ref<{}>>`",
2623 type_name, type_name
2624 ))
2625}
2626
2627pub fn interface_self_embedding(interface_name: &str, span: Span) -> LisetteDiagnostic {
2628 LisetteDiagnostic::error("Recursive interface embedding")
2629 .with_infer_code("interface_cycle")
2630 .with_span_label(&span, format!("`{}` embeds itself", interface_name))
2631 .with_help("An interface cannot embed itself. Remove the self-referencing `impl`.")
2632}
2633
2634pub fn interface_embedding_cycle(cycle: &[String], span: Span) -> LisetteDiagnostic {
2635 let cycle_str = cycle.join(" → ");
2636 LisetteDiagnostic::error("Recursive interface embedding")
2637 .with_infer_code("interface_cycle")
2638 .with_span_label(&span, "creates a cycle")
2639 .with_help(format!(
2640 "Interface embedding cycle detected: {}. Break the cycle by removing one of the embeddings.",
2641 cycle_str
2642 ))
2643}
2644
2645pub fn interface_method_conflict(
2646 interface_name: &str,
2647 method_name: &str,
2648 parent1: &str,
2649 parent2: &str,
2650 span: Span,
2651) -> LisetteDiagnostic {
2652 LisetteDiagnostic::error("Conflicting method signatures")
2653 .with_infer_code("interface_method_conflict")
2654 .with_span_label(&span, format!("duplicate method `{}`", method_name))
2655 .with_help(format!(
2656 "Interface `{}` inherits conflicting definitions of `{}` from `{}` and `{}`. \
2657 Rename one of the methods or remove one of the embeddings.",
2658 interface_name, method_name, parent1, parent2
2659 ))
2660}
2661
2662pub fn impl_on_foreign_type(type_name: &str, module_name: &str, span: Span) -> LisetteDiagnostic {
2663 LisetteDiagnostic::error("Cannot implement methods on foreign type")
2664 .with_infer_code("impl_on_foreign_type")
2665 .with_span_label(
2666 &span,
2667 format!("`{}` is defined in module `{}`", type_name, module_name),
2668 )
2669 .with_help(format!(
2670 "Methods can only be defined on types in the same module. \
2671 Use a standalone function instead: `fn my_method(w: {}) {{ ... }}`",
2672 type_name
2673 ))
2674}
2675
2676pub fn impl_on_type_alias(_type_name: &str, span: Span) -> LisetteDiagnostic {
2677 LisetteDiagnostic::error("Cannot implement methods on type alias")
2678 .with_infer_code("impl_on_type_alias")
2679 .with_span_label(&span, "type alias")
2680 .with_help(
2681 "A type alias cannot carry its own methods. Either add methods to the underlying \
2682 type directly or define a tuple struct instead",
2683 )
2684}
2685
2686pub fn prelude_type_shadowed(name: &str, span: Span) -> LisetteDiagnostic {
2687 LisetteDiagnostic::error("Cannot shadow prelude type")
2688 .with_infer_code("prelude_type_shadowed")
2689 .with_span_label(&span, format!("`{}` is a prelude type", name))
2690 .with_help(format!(
2691 "Choose a different name — `{}` is defined in the prelude and cannot be redefined",
2692 name
2693 ))
2694}
2695
2696pub fn prelude_function_shadowed(name: &str, span: Span) -> LisetteDiagnostic {
2697 LisetteDiagnostic::error("Cannot shadow prelude function")
2698 .with_infer_code("prelude_function_shadowed")
2699 .with_span_label(&span, format!("`{}` is a prelude function", name))
2700 .with_help(format!(
2701 "Choose a different name — `{}` is defined in the prelude and cannot be redefined",
2702 name
2703 ))
2704}
2705
2706pub fn pub_type_not_exportable(name: &str, suggested: &str, span: Span) -> LisetteDiagnostic {
2707 LisetteDiagnostic::error("Public type is not exportable")
2708 .with_infer_code("pub_type_not_exportable")
2709 .with_span_label(&span, format!("`{}` cannot be exported from Go", name))
2710 .with_help(format!(
2711 "Public types become exported Go identifiers, which must start with an uppercase letter. Rename to `{}`",
2712 suggested
2713 ))
2714}
2715
2716pub fn predeclared_type_shadowed(name: &str, span: Span) -> LisetteDiagnostic {
2717 LisetteDiagnostic::error("Cannot shadow Go predeclared identifier")
2718 .with_infer_code("predeclared_type_shadowed")
2719 .with_span_label(&span, format!("`{}` is a Go predeclared identifier", name))
2720 .with_help(format!(
2721 "Lisette emits `{}` directly in the generated Go, so a user declaration with this name shadows the builtin and the output fails to compile. Choose a different name",
2722 name
2723 ))
2724}
2725
2726pub fn non_pub_interface_with_pub_impl(
2727 interface_name: &str,
2728 struct_name: &str,
2729 span: Span,
2730) -> LisetteDiagnostic {
2731 LisetteDiagnostic::error("Visibility mismatch in interface implementation")
2732 .with_infer_code("non_pub_interface_pub_impl")
2733 .with_span_label(
2734 &span,
2735 "has public methods, but interface is private",
2736 )
2737 .with_help(format!(
2738 "`{}` implements public methods for the private interface `{}`. Either make the interface `pub`, or remove `pub` from the struct methods",
2739 struct_name, interface_name
2740 ))
2741}
2742
2743pub fn missing_constraint_on_generic_return_type(
2744 fn_name: &str,
2745 param_name: &str,
2746 constraint: &Type,
2747 span: Span,
2748) -> LisetteDiagnostic {
2749 LisetteDiagnostic::error("Missing constraint on generic return type")
2750 .with_infer_code("missing_constraint_on_return_type")
2751 .with_span_label(
2752 &span,
2753 format!("expected `{}` to be constrained", param_name),
2754 )
2755 .with_help(
2756 format!(
2757 "Constrain the generic: `{}<{}: {}>()`",
2758 fn_name, param_name, constraint
2759 ) + ". The function returns a type whose methods depend on the constraint",
2760 )
2761}
2762
2763pub fn panic_in_expression_position(span: Span) -> LisetteDiagnostic {
2764 LisetteDiagnostic::error("`panic()` used as a value")
2765 .with_infer_code("panic_in_expression_position")
2766 .with_span_label(&span, "disallowed")
2767 .with_help("`panic()` can only be used in statement position, not assigned to a variable or passed as an argument")
2768}
2769
2770pub fn specialized_impl_cannot_satisfy_interface(
2771 struct_name: &str,
2772 interface_name: &str,
2773 method_name: &str,
2774 generics: &[String],
2775 span: Span,
2776) -> LisetteDiagnostic {
2777 let params = generics.join(", ");
2778 LisetteDiagnostic::error("Specialized impl cannot satisfy interface")
2779 .with_infer_code("specialized_impl_cannot_satisfy_interface")
2780 .with_span_label(
2781 &span,
2782 format!(
2783 "`{}` on `{}` cannot satisfy `{}`",
2784 method_name, struct_name, interface_name
2785 ),
2786 )
2787 .with_help(format!(
2788 "Methods in specialized `impl` blocks cannot satisfy interfaces. \
2789 Move `{}` to a generic `impl` block: `impl<{params}> {}<{params}> {{}}`",
2790 method_name, struct_name
2791 ))
2792}
2793
2794pub enum NativeMethodForm {
2795 Instance,
2796 Static,
2797}
2798
2799pub fn native_method_value(method: &str, form: NativeMethodForm, span: Span) -> LisetteDiagnostic {
2800 let help = match form {
2801 NativeMethodForm::Instance => format!(
2802 "Call it directly: `receiver.{method}()`. To use it as a value, wrap in a closure: `|args| receiver.{method}(args)`"
2803 ),
2804 NativeMethodForm::Static => {
2805 format!("Use a closure instead: `|args| receiver.{method}(args)`")
2806 }
2807 };
2808 LisetteDiagnostic::error("Cannot use native method as a value")
2809 .with_infer_code("native_method_value")
2810 .with_span_label(&span, "native methods must be called directly")
2811 .with_help(help)
2812}
2813
2814pub fn native_constructor_value(name: &str, span: Span) -> LisetteDiagnostic {
2815 LisetteDiagnostic::error("Cannot use native constructor as a value")
2816 .with_infer_code("native_constructor_value")
2817 .with_span_label(&span, "native constructors must be called directly")
2818 .with_help(format!("Use a closure instead: `|args| {name}(args)`"))
2819}
2820
2821pub fn enum_variant_constructor_value(name: &str, span: Span) -> LisetteDiagnostic {
2822 LisetteDiagnostic::error("Cannot use enum variant as value")
2823 .with_infer_code("enum_variant_constructor_value")
2824 .with_span_label(&span, "used as value")
2825 .with_help(format!(
2826 "Instantiate the variant: `{name} {{ field: value, ... }}`"
2827 ))
2828}
2829
2830pub fn record_struct_value(name: &str, span: Span) -> LisetteDiagnostic {
2831 LisetteDiagnostic::error("Cannot use struct type as a value")
2832 .with_infer_code("record_struct_value")
2833 .with_span_label(&span, "struct types cannot be used as expressions")
2834 .with_help(format!(
2835 "Use a struct literal instead: `{name} {{ field: value, ... }}`"
2836 ))
2837}
2838
2839pub fn namespace_alias_used_as_value(span: Span) -> LisetteDiagnostic {
2840 LisetteDiagnostic::error("Cannot use a module or enum-type alias as a value")
2841 .with_infer_code("namespace_alias_used_as_value")
2842 .with_span_label(
2843 &span,
2844 "this alias refers to a type or module, not a runtime value",
2845 )
2846 .with_help("Access a member instead, e.g. `alias.VariantName`")
2847}
2848
2849pub fn let_binding_module_namespace(module_name: &str, span: Span) -> LisetteDiagnostic {
2850 LisetteDiagnostic::error("Cannot bind a module namespace to a variable")
2851 .with_infer_code("let_binding_module_namespace")
2852 .with_span_label(&span, "module namespaces are not runtime values")
2853 .with_help(format!(
2854 "Use an import alias instead: `import alias \"{module_name}\"`"
2855 ))
2856}
2857
2858pub fn let_binding_enum_type(type_name: &str, span: Span) -> LisetteDiagnostic {
2859 LisetteDiagnostic::error("Cannot bind an enum type to a variable")
2860 .with_infer_code("let_binding_enum_type")
2861 .with_span_label(&span, "enum types are not runtime values")
2862 .with_help(format!(
2863 "Use a type alias instead: `type Alias = {type_name}`"
2864 ))
2865}
2866
2867pub fn private_method_expression(span: Span) -> LisetteDiagnostic {
2868 LisetteDiagnostic::error("Cannot use private method as a value")
2869 .with_infer_code("private_method_expression")
2870 .with_span_label(&span, "private methods must be called directly")
2871 .with_help("Use a closure instead: `|self_, args| self_.method(args)`")
2872}
2873
2874pub fn float_literal_int_cast(span: Span) -> LisetteDiagnostic {
2875 LisetteDiagnostic::error("Cannot cast float literal to integer directly")
2876 .with_infer_code("float_literal_int_cast")
2877 .with_span_label(&span, "unsupported cast")
2878 .with_help("Bind to a variable first: `let f = 1.0; f as int`")
2879}
2880
2881pub fn const_requires_simple_expression(span: Span) -> LisetteDiagnostic {
2882 LisetteDiagnostic::error("`const` requires a simple expression")
2883 .with_infer_code("const_requires_simple_expression")
2884 .with_span_label(&span, "expected literal or simple expression")
2885 .with_help("Use `let` for computed values")
2886}
2887
2888pub fn complex_sub_expression(span: Span) -> LisetteDiagnostic {
2889 LisetteDiagnostic::error("Complex expression used as sub-expression")
2890 .with_infer_code("complex_sub_expression")
2891 .with_span_label(&span, "expected simple expression")
2892 .with_help("Hoist to a `let` binding")
2893}
2894
2895pub fn reference_through_newtype(span: Span) -> LisetteDiagnostic {
2896 LisetteDiagnostic::error("Cannot take reference through newtype boundary")
2897 .with_infer_code("reference_through_newtype")
2898 .with_span_label(&span, "newtype `.0` inside `&`")
2899 .with_help("Bind the inner value first: `let inner = val.0; &inner`")
2900}
2901
2902pub fn immutable_argument_to_mut_param(
2903 var_name: &str,
2904 callee_label: &str,
2905 span: Span,
2906) -> LisetteDiagnostic {
2907 let help = format!(
2908 "{callee_label} may mutate `{var_name}`, so declare it mutable using `let mut {var_name} = ...`."
2909 );
2910 LisetteDiagnostic::error("Immutable argument passed to `mut` parameter")
2911 .with_infer_code("immutable_arg_to_mut_param")
2912 .with_span_label(&span, "expected mutable, found immutable")
2913 .with_help(help)
2914}
2915
2916pub fn failure_propagation_in_expression(span: Span) -> LisetteDiagnostic {
2917 LisetteDiagnostic::error("Failure propagation in expression position")
2918 .with_infer_code("failure_propagation_in_expression")
2919 .with_span_label(
2920 &span,
2921 "`Err(..)?` and `None?` always early-return and never produce a value",
2922 )
2923 .with_help("Use `return Err(..)` or `return None` instead")
2924}
2925
2926pub fn never_call_in_expression(span: Span) -> LisetteDiagnostic {
2927 LisetteDiagnostic::error("Never-returning call in expression position")
2928 .with_infer_code("never_call_in_expression")
2929 .with_span_label(&span, "`panic` never returns and cannot produce a value")
2930 .with_help("Use `panic(...)` as a statement instead")
2931}
2932
2933pub fn invalid_main_signature(span: Span) -> LisetteDiagnostic {
2934 LisetteDiagnostic::error("Invalid main signature")
2935 .with_infer_code("invalid_main_signature")
2936 .with_span_label(&span, "`main` must have no parameters and no return type")
2937 .with_help(
2938 "Use `fn main() { ... }`. To handle errors, use `match` or `if let` \
2939 inside main instead of returning `Result`.",
2940 )
2941}
2942
2943pub fn parenthesized_qualifier(path: &str, member: &str, span: Span) -> LisetteDiagnostic {
2944 LisetteDiagnostic::error("Unnecessary parentheses around qualifier")
2945 .with_infer_code("parenthesized_qualifier")
2946 .with_span_label(&span, "parenthesized qualifier")
2947 .with_help(format!("Remove the parentheses: `{}.{}`", path, member))
2948}
2949
2950pub fn type_alias_as_qualifier(
2951 alias: &str,
2952 underlying: &str,
2953 member: &str,
2954 span: Span,
2955) -> LisetteDiagnostic {
2956 LisetteDiagnostic::error("Cannot use generic type alias as qualifier")
2957 .with_infer_code("type_alias_as_qualifier")
2958 .with_span_label(
2959 &span,
2960 format!("`{}` aliases `{}`", alias, underlying),
2961 )
2962 .with_help(format!(
2963 "Aliases for types with generic parameters are not supported as qualifiers. Use the original type directly: `{}.{}`",
2964 underlying, member
2965 ))
2966}
2967
2968pub fn ref_qualifier(member: &str, span: Span) -> LisetteDiagnostic {
2969 LisetteDiagnostic::error("Invalid `Ref` construction")
2970 .with_infer_code("ref_qualifier")
2971 .with_span_label(&span, format!("`Ref` has no `{}`", member))
2972 .with_help("To take a reference, use `&value`")
2973}
2974
2975pub fn control_flow_in_expression(keyword: &str, span: Span) -> LisetteDiagnostic {
2976 LisetteDiagnostic::error(format!(
2977 "`{}` cannot be used in expression position",
2978 keyword
2979 ))
2980 .with_infer_code("control_flow_in_expression")
2981 .with_span_label(
2982 &span,
2983 format!("`{}` is a statement and cannot produce a value", keyword),
2984 )
2985 .with_help(format!(
2986 "Use `{}` as a standalone statement instead",
2987 keyword
2988 ))
2989}
2990
2991pub fn spread_on_non_variadic(span: Span) -> LisetteDiagnostic {
2992 LisetteDiagnostic::error("Invalid spread argument")
2993 .with_infer_code("spread_on_non_variadic")
2994 .with_span_label(&span, "this function does not accept variadic arguments")
2995 .with_help("Only functions with a `VarArgs<T>` parameter accept a `xs...` spread")
2996}
2997
2998pub fn range_to_for_variadic(span: Span, var_name: Option<&str>) -> LisetteDiagnostic {
2999 let suggestion = match var_name {
3000 Some(name) => format!("Use postfix: `{}...`", name),
3001 None => "Use postfix `...` for variadic spread".to_string(),
3002 };
3003 LisetteDiagnostic::error("Invalid range argument")
3004 .with_infer_code("range_to_for_variadic")
3005 .with_span_label(&span, "this is a range, not a spread")
3006 .with_help(suggestion)
3007}
3008
3009pub fn reference_aliases_sibling(ref_span: Span, var_name: &str) -> LisetteDiagnostic {
3010 LisetteDiagnostic::error("Reference aliases sibling expression")
3011 .with_infer_code("reference_aliases_sibling")
3012 .with_span_label(
3013 &ref_span,
3014 format!(
3015 "`&{}` could mutate `{}` used by a sibling",
3016 var_name, var_name
3017 ),
3018 )
3019 .with_help(format!(
3020 "Bind `{}` to a `let` before this expression to make evaluation order explicit",
3021 var_name
3022 ))
3023}