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