1use crate::LisetteDiagnostic;
2use syntax::ast::{Annotation, BinaryOperator, Span};
3use syntax::types::Type;
4
5pub fn blank_import_non_go(blank_span: Span) -> LisetteDiagnostic {
6 LisetteDiagnostic::error("Invalid import")
7 .with_resolve_code("blank_import_non_go")
8 .with_span_label(&blank_span, "only allowed for Go modules")
9 .with_help(
10 "Remove the underscore. Blank imports are allowed only for Go imports, \
11 because Lisette modules have no `init()` side effects.",
12 )
13}
14
15pub fn import_conflict(
16 alias: &str,
17 path1: &str,
18 path2: &str,
19 name_span: Span,
20) -> LisetteDiagnostic {
21 LisetteDiagnostic::error("Import conflict")
22 .with_resolve_code("import_conflict")
23 .with_span_label(
24 &name_span,
25 format!("conflicts with prior import `{}`", alias),
26 )
27 .with_help(format!(
28 "`{}` and `{}` resolve to the same name. Add an alias to at least one of them: \
29 `import my_{} \"{}\"`",
30 path1, path2, alias, path2
31 ))
32}
33
34pub fn reserved_import_alias(alias: &str, alias_span: Span) -> LisetteDiagnostic {
35 LisetteDiagnostic::error("Reserved import alias")
36 .with_resolve_code("reserved_import_alias")
37 .with_span_label(&alias_span, "reserved name")
38 .with_help(format!(
39 "`{}` is a reserved name and cannot be used as an import alias. \
40 Choose a different alias, e.g. `import my_{} \"...\"`",
41 alias, alias
42 ))
43}
44
45pub fn duplicate_import_path(path: &str, name_span: Span) -> LisetteDiagnostic {
46 LisetteDiagnostic::error("Duplicate import")
47 .with_resolve_code("duplicate_import")
48 .with_span_label(&name_span, "already imported above")
49 .with_help(format!(
50 "Module `{}` is already imported. Remove the duplicate import.",
51 path
52 ))
53}
54
55pub fn definition_shadows_import(
56 name: &str,
57 import_path: &str,
58 name_span: Span,
59) -> LisetteDiagnostic {
60 LisetteDiagnostic::error("Definition shadows import")
61 .with_resolve_code("definition_shadows_import")
62 .with_span_label(
63 &name_span,
64 format!("conflicts with imported module `{}`", import_path),
65 )
66 .with_help(format!(
67 "`{}` is already used as a module alias for `{}`. \
68 Rename this definition or use a different import alias.",
69 name, import_path
70 ))
71}
72
73pub fn statement_as_tail(span: Span) -> LisetteDiagnostic {
74 LisetteDiagnostic::error("Statement used as value")
75 .with_infer_code("statement_as_tail")
76 .with_span_label(&span, "this is a statement, not an expression")
77 .with_help(
78 "The last item in this block must be an expression that produces a value. \
79 Statements like `let`, `=`, `task`, and `defer` do not produce values.",
80 )
81}
82
83pub fn invalid_map_initialization(key: &Type, value: &Type, span: Span) -> LisetteDiagnostic {
84 LisetteDiagnostic::error("Invalid `Map` initialization")
85 .with_infer_code("invalid_map_initialization")
86 .with_span_label(&span, "invalid syntax")
87 .with_help(format!(
88 "To initialize a `Map`, use `Map.new<{}, {}>()`",
89 key, value
90 ))
91}
92
93pub fn self_type_not_supported(span: Span) -> LisetteDiagnostic {
94 let name_span = Span::new(span.file_id, span.byte_offset, 4); LisetteDiagnostic::error("Use of `Self` type")
96 .with_resolve_code("self_type_not_supported")
97 .with_span_label(&name_span, "invalid type")
98 .with_help(
99 "Use a type parameter instead, e.g. `interface Comparable<T> { fn compare(self, other: T) -> int }`",
100 )
101}
102
103pub fn type_not_found(type_name: &str, annotation_span: Span) -> LisetteDiagnostic {
104 let simple_name = type_name.rsplit('.').next().unwrap_or(type_name);
105 let name_span = Span::new(
106 annotation_span.file_id,
107 annotation_span.byte_offset,
108 simple_name.len() as u32,
109 );
110
111 let looks_like_type_param = simple_name.len() == 1
112 && simple_name.chars().next().is_some_and(|c| c.is_uppercase())
113 || ["Key", "Value", "Item", "Error", "Elem", "In", "Out"].contains(&simple_name);
114
115 if looks_like_type_param {
116 return LisetteDiagnostic::error("Undeclared type parameter")
117 .with_resolve_code("type_not_found")
118 .with_span_label(&name_span, "undeclared")
119 .with_help(format!(
120 "Declare the type parameter, e.g. `impl<{t}>` or `fn foo<{t}>`",
121 t = simple_name
122 ));
123 }
124
125 LisetteDiagnostic::error("Type not found")
126 .with_resolve_code("type_not_found")
127 .with_span_label(&name_span, "type not found in scope")
128 .with_help("Define or import this type")
129}
130
131pub fn undeclared_impl_type_param(
132 type_name: &str,
133 annotation_span: Span,
134 receiver_name: &str,
135) -> LisetteDiagnostic {
136 let name_span = Span::new(
137 annotation_span.file_id,
138 annotation_span.byte_offset,
139 type_name.len() as u32,
140 );
141
142 LisetteDiagnostic::error("Undeclared type parameter")
143 .with_resolve_code("type_not_found")
144 .with_span_label(&name_span, "undeclared")
145 .with_help(format!(
146 "Declare the type parameter: `impl<{t}> {r}<{t}>`",
147 t = type_name,
148 r = receiver_name
149 ))
150}
151
152pub fn type_param_with_args(type_arg_count: usize, span: Span) -> LisetteDiagnostic {
153 let noun = if type_arg_count == 1 {
154 "type argument"
155 } else {
156 "type arguments"
157 };
158
159 LisetteDiagnostic::error("Invalid type argument")
160 .with_infer_code("type_param_with_args")
161 .with_span_label(&span, "type is not parameterized")
162 .with_help(format!("Remove {}", noun))
163}
164
165pub fn type_args_on_non_generic(type_arg_count: usize, span: Span) -> LisetteDiagnostic {
166 let noun = if type_arg_count == 1 {
167 "type argument"
168 } else {
169 "type arguments"
170 };
171
172 LisetteDiagnostic::error("Unexpected type arguments")
173 .with_infer_code("type_arg_on_non_generic")
174 .with_span_label(&span, "accepts no type arguments")
175 .with_help(format!("Remove the {} from this call", noun))
176}
177
178pub fn circular_type_alias(type_name: &str, span: Span) -> LisetteDiagnostic {
179 LisetteDiagnostic::error("Circular type alias")
180 .with_resolve_code("circular_type_alias")
181 .with_span_label(&span, format!("`{}` references itself", type_name))
182 .with_help("Type aliases cannot be recursive")
183}
184
185pub fn name_not_found(
186 variable_name: &str,
187 span: Span,
188 available_names: &[String],
189) -> LisetteDiagnostic {
190 if matches!(variable_name, "nil" | "null" | "Nil" | "undefined") {
191 return LisetteDiagnostic::error(format!("`{}` is not supported", variable_name))
192 .with_resolve_code("nil_not_supported")
193 .with_span_label(&span, "does not exist")
194 .with_help("Absence is encoded with `Option<T>` in Lisette. Use `None` to represent absent values.");
195 }
196
197 if let Some(hint) = go_builtin_hint(variable_name) {
198 return LisetteDiagnostic::error("Name not found")
199 .with_resolve_code("name_not_found")
200 .with_span_label(&span, "name not found in scope")
201 .with_help(hint);
202 }
203
204 let mut diagnostic = LisetteDiagnostic::error("Name not found")
205 .with_resolve_code("name_not_found")
206 .with_span_label(&span, "name not found in scope");
207
208 let suggestion = available_names
209 .iter()
210 .filter_map(|c| {
211 let d = levenshtein_distance(variable_name, c);
212 (d <= 2).then_some((c, d))
213 })
214 .min_by_key(|(_, d)| *d)
215 .map(|(c, _)| c.clone());
216
217 if let Some(suggestion) = suggestion {
218 diagnostic = diagnostic.with_help(format!("Did you mean `{}`?", suggestion));
219 } else {
220 diagnostic = diagnostic.with_help(format!("Define or import `{}`.", variable_name));
221 }
222
223 diagnostic
224}
225
226pub fn self_in_static_method(span: Span) -> LisetteDiagnostic {
227 LisetteDiagnostic::error("Invalid `self`")
228 .with_resolve_code("self_in_static_method")
229 .with_span_label(&span, "`self` is not available here")
230 .with_help("Add a `self` parameter to the method if you need an instance method")
231}
232
233pub fn static_method_called_on_instance(
234 method_name: &str,
235 type_name: &str,
236 span: Span,
237) -> LisetteDiagnostic {
238 LisetteDiagnostic::error("Static method called on instance")
239 .with_infer_code("static_method_on_instance")
240 .with_span_label(&span, format!("`{}` is a static method", method_name))
241 .with_help(format!(
242 "Call it as `{}.{}(...)` on the type, not on an instance",
243 type_name, method_name
244 ))
245}
246
247pub fn function_or_value_not_found_in_module(name: &str, span: Span) -> LisetteDiagnostic {
248 LisetteDiagnostic::error("Name not found")
249 .with_resolve_code("not_found_in_module")
250 .with_span_label(&span, format!("`{}` not found in module", name))
251 .with_help("Ensure the name is exported and spelled correctly")
252}
253
254pub fn receiver_type_mismatch(
255 impl_type: &str,
256 receiver_type: &str,
257 span: Span,
258) -> LisetteDiagnostic {
259 LisetteDiagnostic::error("Type mismatch")
260 .with_infer_code("receiver_type_mismatch")
261 .with_span_label(
262 &span,
263 format!(
264 "expected `{}` or `Ref<{}>`, found `{}`",
265 impl_type, impl_type, receiver_type
266 ),
267 )
268 .with_help(format!(
269 "Change the receiver type to `{}` or `Ref<{}>`",
270 impl_type, impl_type
271 ))
272}
273
274pub fn receiver_must_be_named_self(actual_name: &str, span: Span) -> LisetteDiagnostic {
275 LisetteDiagnostic::error("Invalid receiver name")
276 .with_infer_code("receiver_not_self")
277 .with_span_label(&span, "expected `self`")
278 .with_help(format!(
279 "Rename `{}` to `self`. In an instance method definition, Lisette expects the first parameter to be named `self`",
280 actual_name
281 ))
282}
283
284pub fn disallowed_mutation(
285 variable_name: &str,
286 span: Span,
287 self_type_name: Option<&str>,
288) -> LisetteDiagnostic {
289 if variable_name == "self" {
290 if let Some(type_name) = self_type_name {
291 LisetteDiagnostic::error("Immutable receiver")
292 .with_infer_code("value_receiver_immutable")
293 .with_span_label(&span, "receiver is immutable")
294 .with_help(format!(
295 "Use `self: Ref<{type_name}>` to make the receiver mutable"
296 ))
297 } else {
298 LisetteDiagnostic::error("Immutable receiver")
299 .with_infer_code("value_receiver_immutable")
300 .with_span_label(&span, "receiver is immutable")
301 .with_help("Use `self: Ref<Self>` to make the receiver mutable")
302 }
303 } else {
304 LisetteDiagnostic::error("Immutable variable")
305 .with_infer_code("immutable")
306 .with_span_label(&span, "cannot mutate an immutable variable")
307 .with_help(format!(
308 "Declare using `let mut {variable_name}` to make the variable mutable"
309 ))
310 }
311}
312
313pub fn self_reference_in_assignment(span: Span) -> LisetteDiagnostic {
314 LisetteDiagnostic::error("Cannot reassign variable while taking its reference")
315 .with_infer_code("self_reference_in_assignment")
316 .with_span_label(&span, "disallowed")
317 .with_help("Separate the reassignment from reference taking, or use a different variable")
318}
319
320pub fn uppercase_binding(span: Span, name: &str) -> LisetteDiagnostic {
321 LisetteDiagnostic::error("Invalid binding name")
322 .with_infer_code("uppercase_binding")
323 .with_span_label(&span, "binding names must start with a lowercase letter")
324 .with_help(format!("Use a lowercase name instead of `{}`", name))
325}
326
327pub fn enum_variant_constructor_not_found(
328 span: Span,
329 enum_info: Option<(&str, &[String])>,
330 variant_name: &str,
331) -> LisetteDiagnostic {
332 let help = if let Some((enum_name, variants)) = enum_info {
333 if variants.iter().any(|v| v == variant_name) {
334 format!("Use `{}.{}` to match this variant", enum_name, variant_name)
335 } else if let Some(closest) = variants
336 .iter()
337 .filter_map(|v| {
338 let d = levenshtein_distance(variant_name, v);
339 (d <= 2).then_some((v, d))
340 })
341 .min_by_key(|(_, d)| *d)
342 .map(|(v, _)| v)
343 {
344 format!("Did you mean `{}.{}`?", enum_name, closest)
345 } else {
346 let variants_fmt = format_list(variants, |v| format!("`{}.{}`", enum_name, v));
347 format!(
348 "Available variants for `{}` are {}",
349 enum_name, variants_fmt
350 )
351 }
352 } else {
353 "Check that the variant is defined in the enum and spelled correctly".to_string()
354 };
355
356 LisetteDiagnostic::error("Variant not found")
357 .with_resolve_code("variant_not_found")
358 .with_span_label(&span, "not found")
359 .with_help(help)
360}
361
362pub fn value_enum_in_source_file(enum_name: &str, span: Span) -> LisetteDiagnostic {
363 LisetteDiagnostic::error("Invalid value enum")
364 .with_infer_code("value_enum_outside_typedef")
365 .with_span_label(&span, "not allowed in .lis files")
366 .with_help(format!(
367 "Use a regular enum instead: `enum {} {{ A, B, C }}`. Value enums exist only to represent Go's enums in typedefs.",
368 enum_name
369 ))
370}
371
372pub fn arity_mismatch(
373 expected: &[Type],
374 actual: &[Type],
375 generic_params: &[String],
376 is_constructor: bool,
377 span: Span,
378) -> LisetteDiagnostic {
379 let expected_str = if !generic_params.is_empty() {
380 generic_params.join(", ")
381 } else {
382 expected
383 .iter()
384 .map(|t| t.to_string())
385 .collect::<Vec<_>>()
386 .join(", ")
387 };
388
389 let actual_str = actual
390 .iter()
391 .map(|t| t.to_string())
392 .collect::<Vec<_>>()
393 .join(", ");
394
395 let expected_count = expected.len();
396 let actual_count = actual.len();
397 let expected_word = if expected_count == 1 {
398 "argument"
399 } else {
400 "arguments"
401 };
402
403 LisetteDiagnostic::error("Wrong argument count")
404 .with_infer_code("arg_count_mismatch")
405 .with_span_label(
406 &span,
407 format!("expected `({})`, found `({})`", expected_str, actual_str),
408 )
409 .with_help(format!(
410 "This {} expects {} {} but received {} arguments",
411 if is_constructor {
412 "constructor"
413 } else {
414 "function"
415 },
416 expected_count,
417 expected_word,
418 actual_count
419 ))
420}
421
422pub fn generics_arity_mismatch(
423 expected_generic_params: &[String],
424 actual_type_args: &[Annotation],
425 actual_types: &[Type],
426 span: Span,
427) -> LisetteDiagnostic {
428 let expected: Vec<Type> = expected_generic_params
429 .iter()
430 .map(|param| Type::nominal(param))
431 .collect();
432
433 let expected_str = expected
434 .iter()
435 .map(|t| t.to_string())
436 .collect::<Vec<_>>()
437 .join(", ");
438
439 let actual_str = actual_types
440 .iter()
441 .map(|t| t.to_string())
442 .collect::<Vec<_>>()
443 .join(", ");
444
445 let expected_count = expected.len();
446 let actual_count = actual_types.len();
447 let expected_word = if expected_count == 1 {
448 "type parameter"
449 } else {
450 "type parameters"
451 };
452
453 let generics_span =
454 if let (Some(first), Some(last)) = (actual_type_args.first(), actual_type_args.last()) {
455 let first_span = first.get_span();
456 let last_span = last.get_span();
457 Span::new(
458 first_span.file_id,
459 first_span.byte_offset.saturating_sub(1),
460 (last_span.byte_offset + last_span.byte_length + 1)
461 .saturating_sub(first_span.byte_offset.saturating_sub(1)),
462 )
463 } else {
464 span
465 };
466
467 LisetteDiagnostic::error("Wrong type argument count")
468 .with_infer_code("type_arg_count_mismatch")
469 .with_span_label(
470 &generics_span,
471 format!("expected `<{}>`, found `<{}>`", expected_str, actual_str),
472 )
473 .with_help(format!(
474 "This type expects {} {} but received {} type parameters",
475 expected_count, expected_word, actual_count
476 ))
477}
478
479pub fn tuple_arity_mismatch(
480 pattern_arity: usize,
481 expected_arity: usize,
482 span: Span,
483) -> LisetteDiagnostic {
484 LisetteDiagnostic::error("Tuple arity mismatch")
485 .with_infer_code("tuple_element_count_mismatch")
486 .with_span_label(
487 &span,
488 format!(
489 "expected {} elements, found {} elements",
490 expected_arity, pattern_arity
491 ),
492 )
493 .with_help("Adjust the pattern to match the number of elements in the tuple.")
494}
495
496pub fn struct_not_found(identifier: &str, span: Span) -> LisetteDiagnostic {
497 let simple_name = identifier.rsplit('.').next().unwrap_or(identifier);
498 let name_span = Span::new(span.file_id, span.byte_offset, simple_name.len() as u32);
499
500 LisetteDiagnostic::error("Struct not found")
501 .with_resolve_code("struct_not_found")
502 .with_span_label(&name_span, "struct not found in scope")
503 .with_help("Define or import this struct")
504}
505
506pub fn struct_missing_fields(
507 struct_name: &str,
508 missing: &[String],
509 span: Span,
510) -> LisetteDiagnostic {
511 let fields_list = missing.join(", ");
512
513 let simple_name = struct_name.rsplit('.').next().unwrap_or(struct_name);
514 let name_span = Span::new(span.file_id, span.byte_offset, simple_name.len() as u32);
515
516 LisetteDiagnostic::error(format!("Struct `{}` is missing fields", simple_name))
517 .with_infer_code("missing_struct_fields")
518 .with_span_label(&name_span, format!("missing fields: {}", fields_list))
519 .with_help("Initialize all fields in this struct literal")
520}
521
522pub fn pattern_missing_fields(missing: &[String], span: Span) -> LisetteDiagnostic {
523 let (noun, fields_fmt) = if missing.len() == 1 {
524 ("field", format!("`{}`", missing[0]))
525 } else {
526 let formatted: Vec<String> = missing.iter().map(|f| format!("`{}`", f)).collect();
527 ("fields", formatted.join(", "))
528 };
529
530 let pronoun = if missing.len() == 1 { "it" } else { "them" };
531
532 LisetteDiagnostic::error("Missing pattern fields")
533 .with_infer_code("pattern_missing_fields")
534 .with_span_label(&span, format!("missing {}", fields_fmt))
535 .with_help(format!(
536 "Include the missing {}, or use `..` to ignore {}",
537 noun, pronoun
538 ))
539}
540
541pub fn private_field_access(field_name: &str, struct_name: &str, span: Span) -> LisetteDiagnostic {
542 LisetteDiagnostic::error("Private field")
543 .with_resolve_code("private_field_spread")
544 .with_span_label(&span, "private")
545 .with_help(format!(
546 "Cannot access private field `{}` of struct `{}`. Mark the field as `pub`.",
547 field_name, struct_name
548 ))
549}
550
551pub fn private_method_access(method_name: &str, type_name: &str, span: Span) -> LisetteDiagnostic {
552 LisetteDiagnostic::error("Private method")
553 .with_span_label(&span, "private")
554 .with_help(format!(
555 "Cannot access private method `{}` of type `{}`. Mark the method as `pub`.",
556 method_name, type_name
557 ))
558}
559
560pub fn private_field_in_spread(
561 field_name: &str,
562 struct_name: &str,
563 span: Span,
564) -> LisetteDiagnostic {
565 LisetteDiagnostic::error("Private field")
566 .with_resolve_code("private_field_spread")
567 .with_span_label(&span, "private")
568 .with_help(format!(
569 "Cannot spread `{}` because field `{}` is private. Mark the field as `pub`.",
570 struct_name, field_name
571 ))
572}
573
574pub fn member_not_found(
575 ty: &Type,
576 field: &str,
577 span: Span,
578 available_fields: Option<&[String]>,
579) -> LisetteDiagnostic {
580 let mut diagnostic = LisetteDiagnostic::error("Member not found")
581 .with_infer_code("member_not_found")
582 .with_span_label(&span, format!("no member `{}` on type `{}`", field, ty));
583
584 let suggestion = available_fields.and_then(|fields| find_similar_name(field, fields));
585
586 if let Some(suggestion) = suggestion {
587 diagnostic = diagnostic.with_help(format!("Did you mean `{}`?", suggestion));
588 } else {
589 diagnostic = diagnostic.with_help("Ensure the field or method is defined on this type");
590 }
591
592 diagnostic
593}
594
595pub fn not_numeric(ty: &Type, span: Span) -> LisetteDiagnostic {
596 LisetteDiagnostic::error("Type mismatch")
597 .with_infer_code("type_mismatch")
598 .with_span_label(&span, format!("expected `int` or `float`, found `{}`", ty))
599 .with_help("The negation operator `-` can only be used with `int` or `float`")
600}
601
602pub fn not_numeric_for_binary(
603 operator: &BinaryOperator,
604 ty: &Type,
605 span: Span,
606) -> LisetteDiagnostic {
607 LisetteDiagnostic::error("Type mismatch")
608 .with_infer_code("type_mismatch")
609 .with_span_label(&span, format!("expected `int` or `float`, found `{}`", ty))
610 .with_help(format!(
611 "The `{}` operator can only be used with `int` or `float`",
612 operator
613 ))
614}
615
616pub fn binary_operator_type_mismatch(
617 operator: &BinaryOperator,
618 left_ty: &Type,
619 right_ty: &Type,
620 span: Span,
621) -> LisetteDiagnostic {
622 let label_msg = format!(
623 "cannot {} `{}` and `{}`",
624 operator_verb(operator),
625 left_ty.resolve(),
626 right_ty.resolve()
627 );
628
629 LisetteDiagnostic::error("Type mismatch")
630 .with_infer_code("type_mismatch")
631 .with_span_label(&span, label_msg)
632 .with_help(format!(
633 "The `{}` operator {}",
634 operator,
635 operator_help(operator)
636 ))
637}
638
639pub fn not_orderable(ty: &Type, span: Span) -> LisetteDiagnostic {
640 LisetteDiagnostic::error("Type mismatch")
641 .with_infer_code("type_mismatch")
642 .with_span_label(&span, format!("expected comparable, found `{}`", ty))
643 .with_help("Use comparison operators only with numeric, string, or boolean types")
644}
645
646pub fn not_comparable(ty: &Type, reason: &str, span: Span) -> LisetteDiagnostic {
647 LisetteDiagnostic::error("Type mismatch")
648 .with_infer_code("type_mismatch")
649 .with_span_label(&span, format!("`{}` cannot be compared with `==`", ty))
650 .with_help(format!(
651 "The `==` and `!=` operators cannot be used on {} because they are not comparable in Go",
652 reason
653 ))
654}
655
656pub fn division_by_zero(span: Span) -> LisetteDiagnostic {
657 LisetteDiagnostic::error("Division by zero")
658 .with_infer_code("division_by_zero")
659 .with_span_label(&span, "cannot divide by zero")
660 .with_help("This operation will panic at runtime")
661}
662
663pub fn incompatible_named_numeric_types(underlying_ty: &Type, span: Span) -> LisetteDiagnostic {
664 LisetteDiagnostic::error("Type mismatch")
665 .with_infer_code("incompatible_named_numeric_types")
666 .with_span_label(&span, "cannot compute")
667 .with_help(format!(
668 "Cast one to the other's type, or convert both to `{}`",
669 underlying_ty
670 ))
671}
672
673pub fn invalid_division_order(
674 operator: &BinaryOperator,
675 left_ty: &Type,
676 right_ty: &Type,
677 span: Span,
678) -> LisetteDiagnostic {
679 let (op_symbol, help_msg) = match operator {
680 BinaryOperator::Division => (
681 "/",
682 format!(
683 "To divide by `{}`, the dividend (left operand) must also be `{}`",
684 right_ty, right_ty
685 ),
686 ),
687 BinaryOperator::Remainder => (
688 "%",
689 format!(
690 "To take the remainder by `{}`, the dividend (left operand) must also be `{}`",
691 right_ty, right_ty
692 ),
693 ),
694 _ => unreachable!(),
695 };
696
697 LisetteDiagnostic::error("Invalid operation")
698 .with_infer_code("invalid_division_order")
699 .with_span_label(
700 &span,
701 format!("cannot compute `{}` {} `{}`", left_ty, op_symbol, right_ty),
702 )
703 .with_help(help_msg)
704}
705
706pub fn branch_type_mismatch(
707 consequence_ty: &Type,
708 consequence_span: Span,
709 alternative_ty: &Type,
710 alternative_span: Span,
711) -> LisetteDiagnostic {
712 LisetteDiagnostic::error("Type mismatch")
713 .with_infer_code("type_mismatch")
714 .with_span_label(
715 &consequence_span,
716 format!("this branch returns `{}`", consequence_ty),
717 )
718 .with_span_label(
719 &alternative_span,
720 format!("this branch returns `{}`", alternative_ty),
721 )
722 .with_help("All branches must return the same type")
723}
724
725pub fn missing_else_branch(span: Span) -> LisetteDiagnostic {
726 LisetteDiagnostic::error("Missing `else` branch")
727 .with_infer_code("missing_else_branch")
728 .with_span_label(&span, "`else` branch required")
729 .with_help("Add an `else` branch")
730}
731
732pub fn let_else_must_diverge(span: Span) -> LisetteDiagnostic {
733 LisetteDiagnostic::error("Invalid `else` block")
734 .with_infer_code("let_else_must_diverge")
735 .with_span_primary_label(&span, "this branch does not diverge")
736 .with_help("Add `return`, `break`, `continue`, or a diverging call in the `else` block")
737}
738
739pub fn return_outside_function(span: Span) -> LisetteDiagnostic {
740 LisetteDiagnostic::error("`return` outside function")
741 .with_infer_code("return_outside_function")
742 .with_span_label(&span, "`return` outside function")
743 .with_help("Use `return` only inside a function body")
744}
745
746pub fn disallowed_mut_use(span: Span) -> LisetteDiagnostic {
747 LisetteDiagnostic::error("Invalid `mut`")
748 .with_infer_code("mut_not_allowed")
749 .with_span_label(&span, "not allowed here")
750 .with_help("`mut` is not allowed with destructuring patterns")
751}
752
753pub fn cannot_match_on_functions(span: Span) -> LisetteDiagnostic {
754 LisetteDiagnostic::error("Invalid pattern")
755 .with_infer_code("invalid_pattern")
756 .with_span_label(&span, "cannot pattern match on functions")
757 .with_help("Functions cannot be compared for equality")
758}
759
760pub fn cannot_match_on_unknown(span: Span) -> LisetteDiagnostic {
761 LisetteDiagnostic::error("Cannot match on Unknown")
762 .with_infer_code("cannot_match_on_unknown")
763 .with_span_label(&span, "is type `Unknown`")
764 .with_help("Use `assert_type` to narrow this value into a concrete type before matching. Example: `let value = assert_type<MyType>(x)?`")
765}
766
767pub fn cannot_match_on_unconstrained_type(span: Span) -> LisetteDiagnostic {
768 LisetteDiagnostic::error("Uninferred type")
769 .with_infer_code("cannot_match_on_unconstrained_type")
770 .with_span_label(&span, "type cannot be inferred at this point")
771 .with_help("Add a type annotation on the value before matching on it")
772}
773
774pub fn duplicate_binding_in_pattern(
775 name: &str,
776 first_span: Span,
777 second_span: Span,
778) -> LisetteDiagnostic {
779 LisetteDiagnostic::error("Duplicate binding")
780 .with_infer_code("duplicate_binding_in_pattern")
781 .with_span_label(&first_span, format!("first use of `{}`", name))
782 .with_span_label(&second_span, "used again")
783 .with_help("Remove the duplicate binding")
784}
785
786pub fn literal_pattern_in_binding(span: Span) -> LisetteDiagnostic {
787 LisetteDiagnostic::error("Pattern might not match")
788 .with_infer_code("literal_in_binding")
789 .with_span_label(&span, "value might not equal this literal")
790 .with_help("Use `match` or `if` to compare values")
791}
792
793pub fn or_pattern_in_irrefutable_context(span: Span) -> LisetteDiagnostic {
794 LisetteDiagnostic::error("Invalid or-pattern")
795 .with_infer_code("or_pattern_in_irrefutable")
796 .with_span_label(&span, "or-patterns are not allowed here")
797 .with_help("Use a `match` expression instead.")
798 .with_note("Or-patterns can only be used in `match`, `if let`, and `while let`.")
799}
800
801pub fn or_pattern_binding_mismatch(
802 span: Span,
803 missing_in_later: &[&str],
804 missing_in_first: &[&str],
805) -> LisetteDiagnostic {
806 let missing = if !missing_in_later.is_empty() {
807 missing_in_later.join(", ")
808 } else {
809 missing_in_first.join(", ")
810 };
811
812 LisetteDiagnostic::error("Invalid or-pattern")
813 .with_infer_code("or_pattern_binding_mismatch")
814 .with_span_label(&span, "only bound here")
815 .with_help(format!(
816 "Variable {} is not bound in all alternatives. Use a wildcard `_` instead of a binding, or ensure all alternatives bind the same variable",
817 missing
818 ))
819}
820
821pub fn or_pattern_type_mismatch(span: Span, first_ty: &str, alt_ty: &str) -> LisetteDiagnostic {
822 LisetteDiagnostic::error("Invalid or-pattern")
823 .with_infer_code("or_pattern_type_mismatch")
824 .with_span_label(
825 &span,
826 format!("expected `{}`, found `{}`", first_ty, alt_ty),
827 )
828 .with_help(
829 "Use a wildcard `_` instead of a binding, or use separate match arms for each variant",
830 )
831}
832
833pub fn unknown_iterable_type(span: Span) -> LisetteDiagnostic {
834 LisetteDiagnostic::error("Uninferrable type")
835 .with_infer_code("type_not_inferred")
836 .with_span_label(&span, "cannot be inferred")
837 .with_help("Add a type annotation to the iterable expression")
838}
839
840pub fn not_iterable(ty: &Type, span: Span) -> LisetteDiagnostic {
841 LisetteDiagnostic::error("Not iterable")
842 .with_infer_code("not_iterable")
843 .with_span_label(&span, format!("`{}` is not iterable", ty))
844 .with_help("Use `Slice`, `Map`, `Range`, or `string`")
845}
846
847pub fn tuple_literal_required_in_loop(span: Span) -> LisetteDiagnostic {
848 LisetteDiagnostic::error("Invalid loop pattern")
849 .with_infer_code("invalid_pattern")
850 .with_span_label(&span, "tuple literal required here")
851 .with_help("Use `(key, value)` destructuring pattern for map or enumerated iteration")
852}
853
854pub fn propagate_on_partial(span: Span) -> LisetteDiagnostic {
855 LisetteDiagnostic::error("Cannot use `?` on `Partial`")
856 .with_infer_code("propagate_on_partial")
857 .with_span_label(&span, "`Partial` requires explicit `match`")
858 .with_help(
859 "The `?` operator is incompatible with `Partial` because it has \
860 three variants. Use `match` to handle `Ok`, `Err`, and `Both` \
861 explicitly.",
862 )
863}
864
865pub fn try_requires_result_or_option(span: Span) -> LisetteDiagnostic {
866 LisetteDiagnostic::error("Type mismatch")
867 .with_infer_code("try_requires_result_or_option")
868 .with_span_label(&span, "expects `Result` or `Option`")
869 .with_help("Use the `?` operator only on `Result` or `Option`")
870}
871
872pub fn try_outside_function(span: Span) -> LisetteDiagnostic {
873 LisetteDiagnostic::error("`?` outside function")
874 .with_infer_code("try_outside_function")
875 .with_span_label(&span, "`?` outside function")
876 .with_help("Use `?` only inside a function that returns `Result` or `Option`")
877}
878
879pub fn try_return_type_mismatch(expected: &str, actual_ty: &Type, span: Span) -> LisetteDiagnostic {
880 LisetteDiagnostic::error("Type mismatch")
881 .with_infer_code("try_return_type_mismatch")
882 .with_span_label(
883 &span,
884 format!(
885 "expects `{}`, but function returns `{}`",
886 expected, actual_ty
887 ),
888 )
889 .with_help(format!(
890 "Change the function return type to `{}` or remove the `?` operator",
891 expected
892 ))
893}
894
895pub fn try_block_empty(span: Span) -> LisetteDiagnostic {
896 LisetteDiagnostic::error("Empty `try` block")
897 .with_infer_code("try_block_empty")
898 .with_span_label(&span, "empty")
899 .with_help("Ensure the `try` block contains at least one expression")
900}
901
902pub fn try_block_no_question_mark(try_keyword_span: Span) -> LisetteDiagnostic {
903 LisetteDiagnostic::error("Useless `try` block")
904 .with_infer_code("try_block_no_question_mark")
905 .with_span_label(&try_keyword_span, "no `?` operator found")
906 .with_help("A `try` block must contain at least one `?` for propagation")
907}
908
909pub fn mixed_carriers_in_try_block(span: Span) -> LisetteDiagnostic {
910 LisetteDiagnostic::error("Mixed `try` block")
911 .with_infer_code("try_block_mixed_carriers")
912 .with_span_label(&span, "mixing `Option` and `Result`")
913 .with_help(
914 "A `try` block must use either all `Option` operations or all `Result` operations",
915 )
916}
917
918pub fn break_outside_loop(span: Span) -> LisetteDiagnostic {
919 LisetteDiagnostic::error("`break` outside loop")
920 .with_infer_code("break_outside_loop")
921 .with_span_label(&span, "not inside a loop")
922 .with_help("`break` can only be used inside `loop`, `for`, or `while`")
923}
924
925pub fn continue_outside_loop(span: Span) -> LisetteDiagnostic {
926 LisetteDiagnostic::error("`continue` outside loop")
927 .with_infer_code("continue_outside_loop")
928 .with_span_label(&span, "not inside a loop")
929 .with_help("`continue` can only be used inside `loop`, `for`, or `while`")
930}
931
932pub fn nested_function(span: Span) -> LisetteDiagnostic {
933 LisetteDiagnostic::error("Nested function declaration")
934 .with_infer_code("nested_function")
935 .with_span_label(&span, "functions can only be declared at top level")
936 .with_help("Use a lambda instead: `|x| x + 1` or `|x| { ... }`")
937}
938
939pub fn return_in_try_block(span: Span) -> LisetteDiagnostic {
940 LisetteDiagnostic::error("`return` in `try` block")
941 .with_infer_code("try_block_return")
942 .with_span_label(&span, "not inside a function")
943 .with_help(
944 "Use `return` inside a function, or use `Err(...)? ` to exit the `try` block early",
945 )
946}
947
948pub fn break_in_try_block(span: Span) -> LisetteDiagnostic {
949 LisetteDiagnostic::error("`break` in `try` block")
950 .with_infer_code("try_block_break")
951 .with_span_label(&span, "not inside a loop")
952 .with_help("Use `break` inside a loop, or use `Err(...)? ` to exit the `try` block early")
953}
954
955pub fn continue_in_try_block(span: Span) -> LisetteDiagnostic {
956 LisetteDiagnostic::error("`continue` in `try` block")
957 .with_infer_code("try_block_continue")
958 .with_span_label(&span, "not inside a loop")
959 .with_help(
960 "Use `continue` inside a loop, or use `Err(...)? ` to exit the `try` block early",
961 )
962}
963
964pub fn recover_block_empty(span: Span) -> LisetteDiagnostic {
965 LisetteDiagnostic::warn("Empty `recover` block")
966 .with_infer_code("recover_block_empty")
967 .with_span_label(&span, "empty")
968 .with_help("Ensure the `recover` block contains at least one expression that may panic")
969}
970
971pub fn recover_cannot_use_question_mark(span: Span) -> LisetteDiagnostic {
972 LisetteDiagnostic::error("`?` in `recover` block")
973 .with_infer_code("recover_cannot_use_question_mark")
974 .with_span_label(&span, "cannot propagate to `recover` block")
975 .with_help(
976 "Use a `try` block inside the `recover` block, or handle the `Result` explicitly",
977 )
978}
979
980pub fn return_in_recover_block(span: Span) -> LisetteDiagnostic {
981 LisetteDiagnostic::error("`return` in `recover` block")
982 .with_infer_code("recover_block_return")
983 .with_span_label(&span, "not allowed inside `recover` block")
984 .with_help("Remove the `return`, or move it inside a nested function")
985}
986
987pub fn break_in_recover_block(span: Span) -> LisetteDiagnostic {
988 LisetteDiagnostic::error("`break` in `recover` block")
989 .with_infer_code("recover_block_break")
990 .with_span_label(&span, "not allowed inside `recover` block")
991 .with_help("Remove the `break`, or move it inside a loop within the `recover` block")
992}
993
994pub fn continue_in_recover_block(span: Span) -> LisetteDiagnostic {
995 LisetteDiagnostic::error("`continue` in `recover` block")
996 .with_infer_code("recover_block_continue")
997 .with_span_label(&span, "not allowed inside `recover` block")
998 .with_help("Remove the `continue`, or move it inside a loop within the `recover` block")
999}
1000
1001pub fn expected_channel_receive(ty: &Type, span: Span) -> LisetteDiagnostic {
1002 LisetteDiagnostic::error("Expected channel receive")
1003 .with_infer_code("expected_channel_receive")
1004 .with_span_label(&span, format!("`{}` is not a channel receive", ty))
1005 .with_help("Use `ch.receive()` to receive from a channel in select")
1006}
1007
1008pub fn empty_select(span: Span) -> LisetteDiagnostic {
1009 LisetteDiagnostic::error("Empty select")
1010 .with_infer_code("empty_select")
1011 .with_span_label(&span, "select has no arms")
1012 .with_help(
1013 "Add at least one channel operation arm, e.g. `select { ch.receive() => v { ... } }`",
1014 )
1015}
1016
1017pub fn expected_channel_send(span: Span) -> LisetteDiagnostic {
1018 LisetteDiagnostic::error("Expected channel operation")
1019 .with_infer_code("expected_channel_send")
1020 .with_span_label(&span, "not a channel operation")
1021 .with_help("Use `ch.send(value)` or `ch.receive()` in select arms")
1022}
1023
1024pub fn bare_identifier_in_select_receive(span: &Span) -> LisetteDiagnostic {
1025 LisetteDiagnostic::error("Invalid select case")
1026 .with_infer_code("bare_identifier_in_select_receive")
1027 .with_span_label(span, "expected destructuring")
1028 .with_help("`ch.receive()` returns an `Option`, so use `let Some(v) = ch.receive()` to bind the value")
1029}
1030
1031pub fn none_pattern_in_select_receive(span: &Span) -> LisetteDiagnostic {
1032 LisetteDiagnostic::error("Invalid select case")
1033 .with_infer_code("none_pattern_in_select_receive")
1034 .with_span_label(span, "expected match")
1035 .with_help(
1036 "To detect channel close, use `match ch.receive() { Some(v) => ..., None => ... }`",
1037 )
1038}
1039
1040pub fn select_match_missing_some_arm(span: Span) -> LisetteDiagnostic {
1041 LisetteDiagnostic::error("Invalid select match")
1042 .with_infer_code("select_match_missing_some_arm")
1043 .with_span_label(&span, "missing `Some` arm")
1044 .with_help("`None` only handles channel close. Add a `Some(v) => ...` arm to handle received values")
1045}
1046
1047pub fn select_match_missing_none_arm(span: Span) -> LisetteDiagnostic {
1048 LisetteDiagnostic::error("Invalid select match")
1049 .with_infer_code("select_match_missing_none_arm")
1050 .with_span_label(&span, "missing `None` arm")
1051 .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() => ...`")
1052}
1053
1054pub fn select_match_duplicate_some_arm(span: Span) -> LisetteDiagnostic {
1055 LisetteDiagnostic::error("Invalid select match")
1056 .with_infer_code("select_match_duplicate_some_arm")
1057 .with_span_label(&span, "duplicate")
1058 .with_help(
1059 "Remove the duplicate `Some` arm. If you need to, use a `match` inside the arm body",
1060 )
1061}
1062
1063pub fn select_match_duplicate_none_arm(span: Span) -> LisetteDiagnostic {
1064 LisetteDiagnostic::error("Invalid select match")
1065 .with_infer_code("select_match_duplicate_none_arm")
1066 .with_span_label(&span, "duplicate")
1067 .with_help(
1068 "Remove the duplicate `None` arm. If you need to, use a `match` inside the arm body",
1069 )
1070}
1071
1072pub fn select_match_guard_not_allowed(span: Span) -> LisetteDiagnostic {
1073 LisetteDiagnostic::error("Invalid select match")
1074 .with_infer_code("select_match_guard_not_allowed")
1075 .with_span_label(&span, "not supported")
1076 .with_help("Match arms inside `select` do not support guards. Move the condition inside the arm body: `Some(v) => { if condition { ... } }`")
1077}
1078
1079pub fn select_match_invalid_pattern(span: Span) -> LisetteDiagnostic {
1080 LisetteDiagnostic::error("Invalid select match")
1081 .with_infer_code("select_match_invalid_pattern")
1082 .with_span_label(&span, "unsupported pattern")
1083 .with_help("Select match arms support only `Some(...)` and `None` patterns")
1084}
1085
1086pub fn select_receive_refutable_pattern(span: Span) -> LisetteDiagnostic {
1087 LisetteDiagnostic::error("Refutable pattern in select receive")
1088 .with_infer_code("select_receive_refutable_pattern")
1089 .with_span_label(&span, "may not match all received values")
1090 .with_help(
1091 "Select receive requires an irrefutable binding like `Some(v)` or `Some(_)`. \
1092 Use a regular `match` inside the arm body to filter values",
1093 )
1094}
1095
1096pub fn multiple_select_receives(first_span: Span, second_span: Span) -> LisetteDiagnostic {
1097 LisetteDiagnostic::error("Invalid select")
1098 .with_infer_code("multiple_select_receives")
1099 .with_span_label(&first_span, "first receive arm")
1100 .with_span_label(&second_span, "second receive arm")
1101 .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")
1102}
1103
1104pub fn duplicate_select_default(first_span: Span, second_span: Span) -> LisetteDiagnostic {
1105 LisetteDiagnostic::error("Invalid select")
1106 .with_infer_code("duplicate_select_default")
1107 .with_span_label(&first_span, "first default arm")
1108 .with_span_label(&second_span, "duplicate default arm")
1109 .with_help(
1110 "A select block can have at most one default arm (`_ => ...`). Remove the duplicate.",
1111 )
1112}
1113
1114pub fn non_exhaustive_select_expression(span: Span) -> LisetteDiagnostic {
1115 LisetteDiagnostic::error("Non-exhaustive select expression")
1116 .with_infer_code("non_exhaustive_select_expression")
1117 .with_span_label(&span, "may not produce a value")
1118 .with_help("Add a default arm `_ => ...` to handle closed channels")
1119}
1120
1121pub fn type_must_be_known(span: Span) -> LisetteDiagnostic {
1122 LisetteDiagnostic::error("Uninferrable type")
1123 .with_infer_code("type_not_inferred")
1124 .with_span_label(&span, "cannot be inferred")
1125 .with_help("Add a type annotation to help the compiler infer the type")
1126}
1127
1128pub fn uninferred_binding(name: &str, span: Span) -> LisetteDiagnostic {
1129 LisetteDiagnostic::error("Uninferrable type")
1130 .with_infer_code("type_not_inferred")
1131 .with_span_label(&span, "cannot be inferred")
1132 .with_help(format!(
1133 "Add a type annotation. For example: `let {}: Slice<int> = ...`",
1134 name
1135 ))
1136}
1137
1138pub fn unconstrained_type_param(param_name: &str, span: Span) -> LisetteDiagnostic {
1139 LisetteDiagnostic::error("Unconstrained type parameter")
1140 .with_infer_code("unconstrained_type_param")
1141 .with_span_label(
1142 &span,
1143 format!(
1144 "`{}` is not constrained by parameters or return type",
1145 param_name
1146 ),
1147 )
1148 .with_help(format!(
1149 "Use `{}` in a parameter or return type, or provide an explicit type argument: `func<SomeType>(...)`",
1150 param_name
1151 ))
1152}
1153
1154pub fn slice_index_type_mismatch(index_ty: &Type, span: Span) -> LisetteDiagnostic {
1155 LisetteDiagnostic::error("Type mismatch")
1156 .with_infer_code("slice_index_type_mismatch")
1157 .with_span_label(&span, format!("expected `int`, found `{}`", index_ty))
1158 .with_help(
1159 "Use an integer to index into a `Slice`. For key-value lookup, use a `Map<K, V>`",
1160 )
1161}
1162
1163pub fn only_slices_and_maps_indexable(ty: &Type, span: Span) -> LisetteDiagnostic {
1164 LisetteDiagnostic::error("Not indexable")
1165 .with_infer_code("not_indexable")
1166 .with_span_label(&span, format!("expected `Slice` or `Map`, found `{}`", ty))
1167 .with_help("Only `Slice` and `Map` can be indexed into")
1168}
1169
1170pub fn string_not_indexable(span: Span, receiver: &str) -> LisetteDiagnostic {
1171 LisetteDiagnostic::error("Cannot index into `string`")
1172 .with_infer_code("string_not_indexable")
1173 .with_span_label(&span, "not indexable")
1174 .with_help(format!(
1175 "Use `{receiver}.rune_at(i)` to get a `rune`, or `{receiver}.byte_at(i)` to get a `byte`"
1176 ))
1177}
1178
1179pub fn not_callable(ty: &Type, span: Span) -> LisetteDiagnostic {
1180 let help = match (ty.get_underlying(), ty.get_name()) {
1181 (Some(_), Some(name)) => format!(
1182 "Only functions can be called with `()`. To convert a value, use `value as {}`",
1183 name
1184 ),
1185 _ => "Only functions can be called with `()`".to_string(),
1186 };
1187
1188 LisetteDiagnostic::error("Not callable")
1189 .with_infer_code("not_callable")
1190 .with_span_label(&span, format!("expected function, found `{}`", ty))
1191 .with_help(help)
1192}
1193
1194pub fn type_conversion_arity(
1195 type_name: &str,
1196 actual_count: usize,
1197 span: Span,
1198) -> LisetteDiagnostic {
1199 LisetteDiagnostic::error("Wrong argument count")
1200 .with_infer_code("type_conversion_arity")
1201 .with_span_label(
1202 &span,
1203 format!("expected 1 argument, found {}", actual_count),
1204 )
1205 .with_help(format!(
1206 "Type conversion `{}(value)` takes exactly one argument — the value to convert",
1207 type_name
1208 ))
1209}
1210
1211#[derive(Debug, Clone)]
1212pub struct InterfaceViolation {
1213 pub interface_name: String,
1214 pub parent_of: Option<String>,
1215 pub missing: Vec<(String, Type)>,
1216 pub incompatible: Vec<(String, Type, Type)>,
1217}
1218
1219pub fn interface_not_implemented(
1220 interface_name: &str,
1221 type_name: &str,
1222 violations: &[InterfaceViolation],
1223 span: Span,
1224) -> LisetteDiagnostic {
1225 let mut help_lines = Vec::new();
1226
1227 let mut missing_sections: Vec<(String, Vec<String>)> = Vec::new();
1228 let mut incompatible_sections: Vec<(String, Vec<String>)> = Vec::new();
1229
1230 for violation in violations {
1231 let header = if let Some(ref parent) = violation.parent_of {
1232 format!(
1233 "From `{}` (required by `{}`)",
1234 violation.interface_name, parent
1235 )
1236 } else {
1237 format!("From `{}`", violation.interface_name)
1238 };
1239
1240 if !violation.missing.is_empty() {
1241 let methods: Vec<String> = violation
1242 .missing
1243 .iter()
1244 .map(|(name, sig)| format!(" - {}: {}", name, sig))
1245 .collect();
1246 missing_sections.push((header.clone(), methods));
1247 }
1248
1249 if !violation.incompatible.is_empty() {
1250 let methods: Vec<String> = violation
1251 .incompatible
1252 .iter()
1253 .map(|(name, expected, actual)| {
1254 format!(" - {}: found `{}`, expected `{}`", name, actual, expected)
1255 })
1256 .collect();
1257 incompatible_sections.push((header, methods));
1258 }
1259 }
1260
1261 if !missing_sections.is_empty() {
1262 help_lines.push("Missing methods:".to_string());
1263 for (header, methods) in &missing_sections {
1264 help_lines.push(format!(" {}", header));
1265 for method in methods {
1266 help_lines.push(format!(" {}", method));
1267 }
1268 }
1269 }
1270
1271 if !incompatible_sections.is_empty() {
1272 help_lines.push("Incompatible methods:".to_string());
1273 for (header, methods) in &incompatible_sections {
1274 help_lines.push(format!(" {}", header));
1275 for method in methods {
1276 help_lines.push(format!(" {}", method));
1277 }
1278 }
1279 }
1280
1281 LisetteDiagnostic::error("Interface not implemented")
1282 .with_infer_code("interface_not_implemented")
1283 .with_span_label(
1284 &span,
1285 format!("`{}` does not implement `{}`", type_name, interface_name),
1286 )
1287 .with_help(help_lines.join("\n"))
1288}
1289
1290pub fn pointer_receiver_interface_mismatch(
1291 interface_name: &str,
1292 type_name: &str,
1293 methods: &[String],
1294 span: Span,
1295) -> LisetteDiagnostic {
1296 let methods_str = methods
1297 .iter()
1298 .map(|m| format!("`{}.{}`", type_name, m))
1299 .collect::<Vec<_>>()
1300 .join(", ");
1301 let takes = if methods.len() == 1 {
1302 format!("{} takes `self: Ref<{}>`", methods_str, type_name)
1303 } else {
1304 format!("{} take `self: Ref<{}>`", methods_str, type_name)
1305 };
1306 LisetteDiagnostic::error("Interface not implemented")
1307 .with_infer_code("interface_not_implemented")
1308 .with_span_label(
1309 &span,
1310 format!("`{}` does not implement `{}`", type_name, interface_name),
1311 )
1312 .with_help(format!("{}, so pass a `Ref<{}>`.", takes, type_name))
1313}
1314
1315pub fn unknown_outside_typedef(span: Span) -> LisetteDiagnostic {
1316 LisetteDiagnostic::error("Invalid `Unknown` type")
1317 .with_infer_code("unknown_outside_typedef")
1318 .with_span_label(&span, "not allowed in `.lis` files")
1319 .with_help("Remove the `Unknown` annotation.")
1320 .with_note("`Unknown` can only be used in `.d.lis` files. This type exists only to represent Go's `any` in typedefs.")
1321}
1322
1323pub fn opaque_type_outside_typedef(span: Span) -> LisetteDiagnostic {
1324 LisetteDiagnostic::error("Undefined type")
1325 .with_infer_code("undefined_type_outside_typedef")
1326 .with_span_label(&span, "needs a definition")
1327 .with_help("Use `type Point = ...` to define the type.")
1328 .with_note("Opaque declarations are only allowed in `.d.lis` files.")
1329}
1330
1331pub fn bodyless_function_outside_typedef(span: Span) -> LisetteDiagnostic {
1332 LisetteDiagnostic::error("Missing function body")
1333 .with_infer_code("bodyless_function_outside_typedef")
1334 .with_span_label(&span, "needs a body")
1335 .with_help("Add a body: `fn greet() { ... }`.")
1336 .with_note("Bodyless declarations are only allowed in `.d.lis` files.")
1337}
1338
1339pub fn valueless_const_outside_typedef(span: Span) -> LisetteDiagnostic {
1340 LisetteDiagnostic::error("Missing const value")
1341 .with_infer_code("valueless_const_outside_typedef")
1342 .with_span_label(&span, "needs a value")
1343 .with_help("Ensure the constant has a value: `const MAX_SIZE: int = 100`.")
1344 .with_note("Valueless const declarations are only allowed in `.d.lis` files.")
1345}
1346
1347pub fn valueless_const_missing_annotation(span: Span) -> LisetteDiagnostic {
1348 LisetteDiagnostic::error("Missing const annotation")
1349 .with_infer_code("valueless_const_missing_annotation")
1350 .with_span_label(&span, "needs a type annotation")
1351 .with_help("Value-less const declarations require a type annotation: `const MAX_SIZE: int`")
1352}
1353
1354pub fn variable_declaration_outside_typedef(span: Span) -> LisetteDiagnostic {
1355 LisetteDiagnostic::error("Invalid variable declaration")
1356 .with_infer_code("variable_declaration_outside_typedef")
1357 .with_span_label(&span, "`var` is not allowed here")
1358 .with_help("Use `let` to declare a variable: `let x: int = 0`.")
1359 .with_note("`var` declarations are only allowed in `.d.lis` files.")
1360}
1361
1362pub fn range_full_not_valid_expression(span: Span) -> LisetteDiagnostic {
1363 LisetteDiagnostic::error("Invalid expression")
1364 .with_infer_code("range_full_not_expression")
1365 .with_span_label(&span, "`..` can only be used in slice indexing")
1366 .with_help("Use `arr[..]` to get a full slice, or provide bounds like `0..10`")
1367}
1368
1369pub fn range_not_iterable(range_type: &str, span: Span) -> LisetteDiagnostic {
1370 LisetteDiagnostic::error("Not iterable")
1371 .with_infer_code("range_not_iterable")
1372 .with_span_label(&span, format!("`{}` has no start bound", range_type))
1373 .with_help("Use a range with a start bound, e.g. `0..10` instead of `..10`")
1374}
1375
1376pub fn taking_value_of_ufcs_method(span: Span) -> LisetteDiagnostic {
1377 LisetteDiagnostic::error("Invalid method value")
1378 .with_infer_code("taking_value_of_ufcs_method")
1379 .with_span_label(&span, "taking value not allowed")
1380 .with_help(
1381 "This method cannot be taken as a value. Call the method directly: `obj.method(...)`",
1382 )
1383}
1384
1385pub fn duplicate_definition(kind: &str, name: &str, span: Span) -> LisetteDiagnostic {
1386 LisetteDiagnostic::error(format!("Duplicate {}", kind))
1387 .with_infer_code("duplicate_definition")
1388 .with_span_label(&span, "already defined")
1389 .with_help(format!(
1390 "`{}` is already defined in this module. Rename or remove this definition.",
1391 name
1392 ))
1393}
1394
1395pub fn duplicate_impl_item(item_name: &str, type_name: &str, span: Span) -> LisetteDiagnostic {
1396 LisetteDiagnostic::error("Duplicate name in impl")
1397 .with_infer_code("duplicate_impl_item")
1398 .with_span_label(&span, "method name already taken")
1399 .with_help(format!(
1400 "Method `{}` is already defined for type `{}`. Rename one of the methods.",
1401 item_name, type_name
1402 ))
1403}
1404
1405pub fn duplicate_method_across_specialized_impls(
1406 method_name: &str,
1407 type_name: &str,
1408 generics: &[String],
1409 span: Span,
1410) -> LisetteDiagnostic {
1411 let params = generics.join(", ");
1412 LisetteDiagnostic::error("Duplicate method across specialized `impl` blocks")
1413 .with_infer_code("duplicate_method_across_specialized_impls")
1414 .with_span_label(&span, "already defined in another specialization")
1415 .with_help(format!(
1416 "Specialized `impl` blocks for `{type_name}` share a method namespace. \
1417 Use different method names, or move `{method_name}` to a generic `impl<{params}> {type_name}<{params}> {{}}` block."
1418 ))
1419}
1420
1421pub fn method_shadows_field(type_name: &str, field_name: &str, span: Span) -> LisetteDiagnostic {
1422 LisetteDiagnostic::error("Method shadows struct field")
1423 .with_infer_code("method_shadows_field")
1424 .with_span_label(&span, "same as field")
1425 .with_help(format!(
1426 "`{}` has a field `{}` and a method `{}`. Rename either the field or the method",
1427 type_name, field_name, field_name
1428 ))
1429}
1430
1431pub fn non_int_range_not_iterable(element_ty: &Type, span: Span) -> LisetteDiagnostic {
1432 LisetteDiagnostic::error("Not iterable")
1433 .with_infer_code("non_int_range_not_iterable")
1434 .with_span_label(
1435 &span,
1436 format!("cannot iterate over `Range<{}>`", element_ty),
1437 )
1438 .with_help("Range iteration requires integer bounds")
1439}
1440
1441pub fn only_slices_indexable_by_range(ty: &Type, span: &Span) -> LisetteDiagnostic {
1442 LisetteDiagnostic::error("Type mismatch")
1443 .with_infer_code("range_index_not_slice")
1444 .with_span_label(
1445 span,
1446 format!("expected `Slice` or `string`, found `{}`", ty),
1447 )
1448 .with_help("Range indexing only works on `Slice` and `string`")
1449}
1450
1451pub fn empty_body_return_mismatch(expected_ty: &Type, span: Span) -> LisetteDiagnostic {
1452 LisetteDiagnostic::error("Type mismatch")
1453 .with_infer_code("type_mismatch")
1454 .with_span_label(
1455 &span,
1456 format!("promises `{}`, but returns `()`", expected_ty),
1457 )
1458 .with_help("Return a value or change the return type annotation to `()`.")
1459 .with_note("An empty function body implicitly returns `()`.")
1460}
1461
1462fn operator_verb(operator: &BinaryOperator) -> &'static str {
1463 match operator {
1464 BinaryOperator::Addition => "add",
1465 BinaryOperator::Subtraction => "subtract",
1466 BinaryOperator::Multiplication => "multiply",
1467 BinaryOperator::Division => "divide",
1468 BinaryOperator::Remainder => "get remainder of",
1469 BinaryOperator::Equal | BinaryOperator::NotEqual => "compare",
1470 BinaryOperator::LessThan
1471 | BinaryOperator::LessThanOrEqual
1472 | BinaryOperator::GreaterThan
1473 | BinaryOperator::GreaterThanOrEqual => "compare",
1474 BinaryOperator::And | BinaryOperator::Or => "apply logical operator to",
1475 BinaryOperator::Pipeline => "pipe",
1476 }
1477}
1478
1479fn operator_help(op: &BinaryOperator) -> &'static str {
1480 match op {
1481 BinaryOperator::Addition => "requires both operands to have the same type",
1482 BinaryOperator::Subtraction
1483 | BinaryOperator::Multiplication
1484 | BinaryOperator::Division
1485 | BinaryOperator::Remainder => "requires both operands to have the same numeric type",
1486 BinaryOperator::Equal | BinaryOperator::NotEqual => {
1487 "requires both operands to have the same type"
1488 }
1489 BinaryOperator::LessThan
1490 | BinaryOperator::LessThanOrEqual
1491 | BinaryOperator::GreaterThan
1492 | BinaryOperator::GreaterThanOrEqual => "requires both operands to have the same type",
1493 BinaryOperator::And | BinaryOperator::Or => "requires both operands to be bool",
1494 BinaryOperator::Pipeline => "should have been desugared",
1495 }
1496}
1497
1498pub fn task_in_expression_position(span: Span) -> LisetteDiagnostic {
1499 LisetteDiagnostic::error("Invalid `task`")
1500 .with_infer_code("task_in_expression_position")
1501 .with_span_label(&span, "produces no value")
1502 .with_help("Move `task` to its own statement")
1503}
1504
1505pub fn defer_in_expression_position(span: Span) -> LisetteDiagnostic {
1506 LisetteDiagnostic::error("Invalid `defer`")
1507 .with_infer_code("defer_in_expression_position")
1508 .with_span_label(&span, "produces no value")
1509 .with_help("Move `defer` to its own statement")
1510}
1511
1512pub fn non_addressable_expression(expression_kind: &str, span: Span) -> LisetteDiagnostic {
1513 LisetteDiagnostic::error("Non-addressable expression")
1514 .with_infer_code("non_addressable_expression")
1515 .with_span_label(&span, format!("cannot take address of {}", expression_kind))
1516 .with_help("Assign the value to a variable first, then take its address")
1517}
1518
1519pub fn non_addressable_assignment(expression_kind: &str, span: Span) -> LisetteDiagnostic {
1520 LisetteDiagnostic::error("Cannot assign to non-addressable expression")
1521 .with_infer_code("non_addressable_assignment")
1522 .with_span_label(&span, format!("cannot assign to {}", expression_kind))
1523 .with_help("Assign the value to a variable first, then modify it")
1524}
1525
1526pub fn newtype_field_assignment(type_name: &str, span: Span) -> LisetteDiagnostic {
1527 LisetteDiagnostic::error("Cannot assign to newtype field")
1528 .with_infer_code("newtype_field_assignment")
1529 .with_span_label(&span, "newtype fields are read-only")
1530 .with_help(format!(
1531 "Reconstruct the newtype: `variable = {type_name}(new_value)`"
1532 ))
1533}
1534
1535pub fn complex_select_expression(span: Span) -> LisetteDiagnostic {
1536 LisetteDiagnostic::error("Complex expression in `select` arm")
1537 .with_infer_code("complex_select_expression")
1538 .with_span_label(&span, "expected simple expression")
1539 .with_help("Hoist to a `let` binding before the `select`")
1540}
1541
1542pub fn ref_slice_append(span: Span) -> LisetteDiagnostic {
1543 LisetteDiagnostic::error("Cannot call append/extend on `Ref<Slice>`")
1544 .with_infer_code("ref_slice_append")
1545 .with_span_label(&span, "dereference the ref first")
1546 .with_help("Use `r.*.append(x)` to deref, then append")
1547}
1548
1549pub fn map_field_chain_assignment(span: Span) -> LisetteDiagnostic {
1550 LisetteDiagnostic::error("Cannot assign to field of map entry")
1551 .with_infer_code("map_field_chain_assignment")
1552 .with_span_label(&span, "assignment not allowed here")
1553 .with_help(
1554 "Extract, modify, and reinsert: `let mut entry = m[key]; entry.field = value; m[key] = entry`",
1555 )
1556}
1557
1558pub fn enum_field_type_conflict(
1559 loc_a: &str,
1560 type_a: &str,
1561 loc_b: &str,
1562 type_b: &str,
1563 span: Span,
1564) -> LisetteDiagnostic {
1565 LisetteDiagnostic::error("Conflicting field types across enum variants")
1566 .with_infer_code("enum_field_type_conflict")
1567 .with_span_label(&span, "field type mismatch")
1568 .with_help(format!(
1569 "`{loc_a}` is `{type_a}` but `{loc_b}` is `{type_b}`. Rename one of the fields or align their types",
1570 ))
1571}
1572
1573pub fn cannot_auto_address_receiver(
1574 receiver_kind: &str,
1575 method_name: &str,
1576 expected_ty: &Type,
1577 actual_ty: &Type,
1578 span: Span,
1579) -> LisetteDiagnostic {
1580 let readable_kind = match receiver_kind {
1581 "map index expression" => "map lookup",
1582 "function call" => "function result",
1583 "literal" => "literal",
1584 "binary expression" => "expression result",
1585 "conditional expression" => "conditional result",
1586 "match expression" => "match result",
1587 "block expression" => "block result",
1588 "lambda" => "lambda",
1589 "tuple" => "tuple",
1590 "range expression" => "range expression",
1591 _ => "expression",
1592 };
1593
1594 LisetteDiagnostic::error("Expression not modifiable")
1595 .with_infer_code("cannot_auto_address_receiver")
1596 .with_span_label(&span, "modifies its receiver")
1597 .with_help(format!(
1598 "Assign the {} to a variable first, then call the method. The receiver of `{}` is `{}`, not `{}`",
1599 readable_kind, method_name, expected_ty, actual_ty
1600 ))
1601}
1602
1603pub fn break_value_in_non_loop(span: Span) -> LisetteDiagnostic {
1604 LisetteDiagnostic::error("`break` with value in non-`loop` loop")
1605 .with_infer_code("break_value_in_non_loop")
1606 .with_span_label(&span, "`break` with value only allowed in `loop`")
1607 .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.")
1608}
1609
1610pub fn defer_in_loop(span: Span) -> LisetteDiagnostic {
1611 LisetteDiagnostic::error("`defer` inside loop")
1612 .with_infer_code("defer_in_loop")
1613 .with_span_label(&span, "not allowed inside loop")
1614 .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); }`")
1615}
1616
1617pub fn propagate_in_condition(span: Span) -> LisetteDiagnostic {
1618 LisetteDiagnostic::error("`?` cannot be used inside a condition")
1619 .with_infer_code("propagate_in_condition")
1620 .with_span_label(&span, "`?` inside condition")
1621 .with_help("Bind the result first: `let val = expression?; if val { ... }`")
1622}
1623
1624pub fn propagate_in_defer(span: Span) -> LisetteDiagnostic {
1625 LisetteDiagnostic::error("Invalid `?` in `defer`")
1626 .with_infer_code("propagate_in_defer")
1627 .with_span_label(&span, "`?` not allowed here")
1628 .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); } }`")
1629}
1630
1631pub fn return_in_defer_block(span: Span) -> LisetteDiagnostic {
1632 LisetteDiagnostic::error("`return` in `defer` block")
1633 .with_infer_code("return_in_defer_block")
1634 .with_span_label(&span, "not allowed inside `defer` block")
1635 .with_help("Remove the `return` as it only exits the `defer` block")
1636}
1637
1638pub fn break_in_defer_block(span: Span) -> LisetteDiagnostic {
1639 LisetteDiagnostic::error("`break` in `defer` block")
1640 .with_infer_code("break_in_defer_block")
1641 .with_span_label(&span, "not allowed inside `defer` block")
1642 .with_help("Remove the `break`, or move it inside a loop within the `defer` block")
1643}
1644
1645pub fn continue_in_defer_block(span: Span) -> LisetteDiagnostic {
1646 LisetteDiagnostic::error("`continue` in `defer` block")
1647 .with_infer_code("continue_in_defer_block")
1648 .with_span_label(&span, "not allowed inside `defer` block")
1649 .with_help("Remove the `continue`, or move it inside a loop within the `defer` block")
1650}
1651
1652pub fn invalid_cast(source_ty: &Type, target_ty: &Type, span: Span) -> LisetteDiagnostic {
1653 let same_constructor_with_unresolved = source_ty
1654 .get_qualified_id()
1655 .zip(target_ty.get_qualified_id())
1656 .is_some_and(|(s, t)| s == t)
1657 && source_ty.has_unbound_variables();
1658
1659 let help = if same_constructor_with_unresolved {
1660 format!(
1661 "Use a type annotation instead: `let x: {} = ...`",
1662 target_ty,
1663 )
1664 } else if source_ty.is_string() {
1665 "Strings cannot be cast to numbers and require explicit conversion. Use `strconv.Atoi()` to parse.".into()
1666 } else if source_ty.is_complex() || target_ty.is_complex() {
1667 "Complex numbers cannot be cast directly. Use `real(c)` or `imaginary(c)` to extract components.".into()
1668 } else {
1669 "Casts are supported between numeric types, strings and byte/rune slices, concrete types and interfaces, and values and their type aliases.".into()
1670 };
1671
1672 LisetteDiagnostic::error("Invalid cast")
1673 .with_infer_code("invalid_cast")
1674 .with_span_label(
1675 &span,
1676 format!("cannot cast `{}` to `{}`", source_ty, target_ty),
1677 )
1678 .with_help(help)
1679}
1680
1681pub fn chained_cast(span: Span) -> LisetteDiagnostic {
1682 LisetteDiagnostic::error("Invalid cast")
1683 .with_infer_code("chained_cast")
1684 .with_span_label(&span, "chained cast not allowed")
1685 .with_help("Use an intermediate variable if you need to cast through multiple types")
1686}
1687
1688pub fn redundant_cast(ty: &Type, span: Span) -> LisetteDiagnostic {
1689 LisetteDiagnostic::warn("Redundant cast")
1690 .with_infer_code("redundant_cast")
1691 .with_span_label(&span, format!("casting `{}` to itself has no effect", ty))
1692 .with_help("Remove the unnecessary cast")
1693}
1694
1695pub fn integer_literal_overflow(
1696 target_ty: &str,
1697 min: i128,
1698 max: i128,
1699 span: Span,
1700) -> LisetteDiagnostic {
1701 LisetteDiagnostic::error("Integer literal overflow")
1702 .with_infer_code("integer_literal_overflow")
1703 .with_span_label(&span, format!("overflows `{}`", target_ty))
1704 .with_help(format!(
1705 "`{}` must be in range `{}` to `{}`",
1706 target_ty, min, max
1707 ))
1708}
1709
1710pub fn float_literal_overflow(target_ty: &str, span: Span) -> LisetteDiagnostic {
1711 LisetteDiagnostic::error("Float literal overflow")
1712 .with_infer_code("float_literal_overflow")
1713 .with_span_label(&span, format!("value overflows `{}`", target_ty))
1714 .with_help(format!(
1715 "Use `float64` for larger values, or ensure the value fits in `{}`",
1716 target_ty
1717 ))
1718}
1719
1720pub fn cannot_negate_unsigned(target_ty: &str, span: Span) -> LisetteDiagnostic {
1721 LisetteDiagnostic::error("Cannot negate unsigned type")
1722 .with_infer_code("cannot_negate_unsigned")
1723 .with_span_label(&span, format!("cannot negate `{}`", target_ty))
1724 .with_help("Unsigned types cannot represent negative values")
1725}
1726
1727fn go_builtin_hint(name: &str) -> Option<&'static str> {
1728 match name {
1729 "len" => Some(
1730 "Lisette has no `len` builtin. Use the `.length()` method instead, e.g. `items.length()`.",
1731 ),
1732 "cap" => Some(
1733 "Lisette has no `cap` builtin. Use the `.capacity()` method instead, e.g. `items.capacity()`.",
1734 ),
1735 "make" => Some(
1736 "Lisette has no `make` builtin. Use constructor methods instead, e.g. `Channel.new<int>()`, `Slice.new<int>()`, `Map.new<K, V>()`.",
1737 ),
1738 "append" => Some(
1739 "Lisette has no `append` builtin. Use the `.append()` method instead, e.g. `items.append(1)`.",
1740 ),
1741 "close" => Some(
1742 "Lisette has no `close` builtin. Use the `.close()` method instead, e.g. `ch.close()`.",
1743 ),
1744 "copy" => Some(
1745 "Lisette has no `copy` builtin. Use the `.copy_from()` method instead, e.g. `dst.copy_from(src)`.",
1746 ),
1747 "delete" => Some(
1748 "Lisette has no `delete` builtin. Use the `.delete()` method instead, e.g. `map.delete(key)`.",
1749 ),
1750 "new" => Some(
1751 "Lisette has no `new` builtin. Use constructor methods instead, e.g. `MyStruct { field: value }` or `MyType.new()`.",
1752 ),
1753 "print" | "println" | "printf" => Some(
1754 "Lisette has no `print` builtin. Use `fmt.Println`, `fmt.Printf`, etc. after `import \"go:fmt\"`.",
1755 ),
1756 _ => None,
1757 }
1758}
1759
1760pub fn levenshtein_distance(a: &str, b: &str) -> usize {
1761 let b_len = b.len();
1762
1763 if a.is_empty() {
1764 return b_len;
1765 }
1766 if b_len == 0 {
1767 return a.len();
1768 }
1769
1770 let mut prev: Vec<usize> = (0..=b_len).collect();
1771 let mut curr = vec![0; b_len + 1];
1772
1773 for (i, a_char) in a.chars().enumerate() {
1774 curr[0] = i + 1;
1775 for (j, b_char) in b.chars().enumerate() {
1776 let cost = if a_char == b_char { 0 } else { 1 };
1777 curr[j + 1] = (prev[j + 1] + 1).min(curr[j] + 1).min(prev[j] + cost);
1778 }
1779 std::mem::swap(&mut prev, &mut curr);
1780 }
1781
1782 prev[b_len]
1783}
1784
1785pub fn find_similar_name(name: &str, candidates: &[String]) -> Option<String> {
1788 let best_distance = candidates
1789 .iter()
1790 .filter_map(|c| {
1791 let d = levenshtein_distance(name, c);
1792 (d <= 2).then_some((c, d))
1793 })
1794 .min_by_key(|(_, d)| *d);
1795
1796 let by_prefix = if name.len() >= 2 {
1797 candidates
1798 .iter()
1799 .filter(|c| c.starts_with(name) || name.starts_with(c.as_str()))
1800 .min_by_key(|c| c.len().abs_diff(name.len()))
1801 } else {
1802 None
1803 };
1804
1805 match (best_distance, by_prefix) {
1806 (Some((d, dist)), Some(p)) => {
1807 if dist <= 1 {
1810 Some(d.clone())
1811 } else {
1812 Some(p.clone())
1813 }
1814 }
1815 (Some((d, _)), None) => Some(d.clone()),
1816 (None, Some(p)) => Some(p.clone()),
1817 (None, None) => None,
1818 }
1819}
1820
1821pub fn cannot_infer_type_argument(span: Span) -> LisetteDiagnostic {
1822 LisetteDiagnostic::error("Missing type argument")
1823 .with_infer_code("missing_type_argument")
1824 .with_span_label(&span, "expected type argument")
1825 .with_help("Supply a type argument for the call, e.g. `Channel.new<int>()`")
1826}
1827
1828fn format_list<T, F>(items: &[T], fmt: F) -> String
1829where
1830 F: Fn(&T) -> String,
1831{
1832 match items.len() {
1833 0 => String::new(),
1834 1 => fmt(&items[0]),
1835 2 => format!("{} and {}", fmt(&items[0]), fmt(&items[1])),
1836 _ => {
1837 let mut result = String::new();
1838 for (i, item) in items.iter().enumerate() {
1839 if i > 0 {
1840 result.push_str(", ");
1841 }
1842 if i == items.len() - 1 {
1843 result.push_str("and ");
1844 }
1845 result.push_str(&fmt(item));
1846 }
1847 result
1848 }
1849 }
1850}
1851
1852pub fn recursive_generic_instantiation(type_name: &str, span: Span) -> LisetteDiagnostic {
1853 LisetteDiagnostic::error("Recursive generic instantiation")
1854 .with_infer_code("recursive_instantiation")
1855 .with_span_label(&span, format!("`{}` is nested within itself", type_name))
1856 .with_help(format!(
1857 "Go does not allow recursive type instantiation (e.g., `{0}<{0}<T>>`). \
1858 Use a wrapper type or a different design.",
1859 type_name
1860 ))
1861}
1862
1863pub fn non_comparable_map_key(key_ty: &Type, reason: &str, span: Span) -> LisetteDiagnostic {
1864 LisetteDiagnostic::error("Invalid map key type")
1865 .with_infer_code("non_comparable_map_key")
1866 .with_span_label(&span, format!("`{}` is not comparable", key_ty))
1867 .with_help(format!(
1868 "Map keys must be comparable in Go. {} cannot be used as map keys.",
1869 reason
1870 ))
1871}
1872
1873pub fn ref_of_interface_type(inner_ty: &Type, span: Span) -> LisetteDiagnostic {
1874 LisetteDiagnostic::error("Invalid use of `Ref` with interface")
1875 .with_infer_code("ref_of_interface")
1876 .with_span_label(&span, "not allowed")
1877 .with_help(format!(
1878 "Use `{}` instead of `Ref<{}>`. Interfaces are already reference types in Go.",
1879 inner_ty, inner_ty
1880 ))
1881}
1882
1883pub fn float_modulo_not_supported(span: Span) -> LisetteDiagnostic {
1884 LisetteDiagnostic::error("Invalid operation")
1885 .with_infer_code("float_modulo")
1886 .with_span_label(&span, "`%` is not supported on floating-point types")
1887 .with_help("Use `math.Mod(x, y)` for floating-point modulo")
1888}
1889
1890pub fn recursive_type(type_name: &str, span: Span) -> LisetteDiagnostic {
1891 LisetteDiagnostic::error("Recursive type has infinite size")
1892 .with_infer_code("recursive_type")
1893 .with_span_label(
1894 &span,
1895 format!("`{}` contains itself without indirection", type_name),
1896 )
1897 .with_help(format!(
1898 "Use `Ref<{}>` for indirection. For example: `next: Option<Ref<{}>>`",
1899 type_name, type_name
1900 ))
1901}
1902
1903pub fn interface_self_embedding(interface_name: &str, span: Span) -> LisetteDiagnostic {
1904 LisetteDiagnostic::error("Recursive interface embedding")
1905 .with_infer_code("interface_cycle")
1906 .with_span_label(&span, format!("`{}` embeds itself", interface_name))
1907 .with_help("An interface cannot embed itself. Remove the self-referencing `impl`.")
1908}
1909
1910pub fn interface_embedding_cycle(cycle: &[String], span: Span) -> LisetteDiagnostic {
1911 let cycle_str = cycle.join(" → ");
1912 LisetteDiagnostic::error("Recursive interface embedding")
1913 .with_infer_code("interface_cycle")
1914 .with_span_label(&span, "creates a cycle")
1915 .with_help(format!(
1916 "Interface embedding cycle detected: {}. Break the cycle by removing one of the embeddings.",
1917 cycle_str
1918 ))
1919}
1920
1921pub fn interface_method_conflict(
1922 interface_name: &str,
1923 method_name: &str,
1924 parent1: &str,
1925 parent2: &str,
1926 span: Span,
1927) -> LisetteDiagnostic {
1928 LisetteDiagnostic::error("Conflicting method signatures")
1929 .with_infer_code("interface_method_conflict")
1930 .with_span_label(&span, format!("duplicate method `{}`", method_name))
1931 .with_help(format!(
1932 "Interface `{}` inherits conflicting definitions of `{}` from `{}` and `{}`. \
1933 Rename one of the methods or remove one of the embeddings.",
1934 interface_name, method_name, parent1, parent2
1935 ))
1936}
1937
1938pub fn impl_on_foreign_type(type_name: &str, module_name: &str, span: Span) -> LisetteDiagnostic {
1939 LisetteDiagnostic::error("Cannot implement methods on foreign type")
1940 .with_infer_code("impl_on_foreign_type")
1941 .with_span_label(
1942 &span,
1943 format!("`{}` is defined in module `{}`", type_name, module_name),
1944 )
1945 .with_help(format!(
1946 "Methods can only be defined on types in the same module. \
1947 Use a standalone function instead: `fn my_method(w: {}) {{ ... }}`",
1948 type_name
1949 ))
1950}
1951
1952pub fn prelude_type_shadowed(name: &str, span: Span) -> LisetteDiagnostic {
1953 LisetteDiagnostic::error("Cannot shadow prelude type")
1954 .with_infer_code("prelude_type_shadowed")
1955 .with_span_label(&span, format!("`{}` is a prelude type", name))
1956 .with_help(format!(
1957 "Choose a different name — `{}` is defined in the prelude and cannot be redefined",
1958 name
1959 ))
1960}
1961
1962pub fn non_pub_interface_with_pub_impl(
1963 interface_name: &str,
1964 struct_name: &str,
1965 span: Span,
1966) -> LisetteDiagnostic {
1967 LisetteDiagnostic::error("Visibility mismatch in interface implementation")
1968 .with_infer_code("non_pub_interface_pub_impl")
1969 .with_span_label(
1970 &span,
1971 "has public methods, but interface is private",
1972 )
1973 .with_help(format!(
1974 "`{}` implements public methods for the private interface `{}`. Either make the interface `pub`, or remove `pub` from the struct methods",
1975 struct_name, interface_name
1976 ))
1977}
1978
1979pub fn missing_constraint_on_generic_return_type(
1980 fn_name: &str,
1981 param_name: &str,
1982 constraint: &Type,
1983 span: Span,
1984) -> LisetteDiagnostic {
1985 LisetteDiagnostic::error("Missing constraint on generic return type")
1986 .with_infer_code("missing_constraint_on_return_type")
1987 .with_span_label(
1988 &span,
1989 format!("expected `{}` to be constrained", param_name),
1990 )
1991 .with_help(
1992 format!(
1993 "Constrain the generic: `{}<{}: {}>()`",
1994 fn_name, param_name, constraint
1995 ) + ". The function returns a type whose methods depend on the constraint",
1996 )
1997}
1998
1999pub fn panic_in_expression_position(span: Span) -> LisetteDiagnostic {
2000 LisetteDiagnostic::error("`panic()` used as a value")
2001 .with_infer_code("panic_in_expression_position")
2002 .with_span_label(&span, "disallowed")
2003 .with_help("`panic()` can only be used in statement position, not assigned to a variable or passed as an argument")
2004}
2005
2006pub fn specialized_impl_cannot_satisfy_interface(
2007 struct_name: &str,
2008 interface_name: &str,
2009 method_name: &str,
2010 generics: &[String],
2011 span: Span,
2012) -> LisetteDiagnostic {
2013 let params = generics.join(", ");
2014 LisetteDiagnostic::error("Specialized impl cannot satisfy interface")
2015 .with_infer_code("specialized_impl_cannot_satisfy_interface")
2016 .with_span_label(
2017 &span,
2018 format!(
2019 "`{}` on `{}` cannot satisfy `{}`",
2020 method_name, struct_name, interface_name
2021 ),
2022 )
2023 .with_help(format!(
2024 "Methods in specialized `impl` blocks cannot satisfy interfaces. \
2025 Move `{}` to a generic `impl` block: `impl<{params}> {}<{params}> {{}}`",
2026 method_name, struct_name
2027 ))
2028}
2029
2030pub fn native_method_value(method: &str, span: Span) -> LisetteDiagnostic {
2031 LisetteDiagnostic::error("Cannot use native method as a value")
2032 .with_infer_code("native_method_value")
2033 .with_span_label(&span, "native methods must be called directly")
2034 .with_help(format!(
2035 "Use a closure instead: `|args| receiver.{}(args)`",
2036 method
2037 ))
2038}
2039
2040pub fn native_constructor_value(name: &str, span: Span) -> LisetteDiagnostic {
2041 LisetteDiagnostic::error("Cannot use native constructor as a value")
2042 .with_infer_code("native_constructor_value")
2043 .with_span_label(&span, "native constructors must be called directly")
2044 .with_help(format!("Use a closure instead: `|args| {name}(args)`"))
2045}
2046
2047pub fn private_method_expression(span: Span) -> LisetteDiagnostic {
2048 LisetteDiagnostic::error("Cannot use private method as a value")
2049 .with_infer_code("private_method_expression")
2050 .with_span_label(&span, "private methods must be called directly")
2051 .with_help("Use a closure instead: `|self_, args| self_.method(args)`")
2052}
2053
2054pub fn float_literal_int_cast(span: Span) -> LisetteDiagnostic {
2055 LisetteDiagnostic::error("Cannot cast float literal to integer directly")
2056 .with_infer_code("float_literal_int_cast")
2057 .with_span_label(&span, "unsupported cast")
2058 .with_help("Bind to a variable first: `let f = 1.0; f as int`")
2059}
2060
2061pub fn const_requires_simple_expression(span: Span) -> LisetteDiagnostic {
2062 LisetteDiagnostic::error("`const` requires a simple expression")
2063 .with_infer_code("const_requires_simple_expression")
2064 .with_span_label(&span, "expected literal or simple expression")
2065 .with_help("Use `let` for computed values")
2066}
2067
2068pub fn complex_sub_expression(span: Span) -> LisetteDiagnostic {
2069 LisetteDiagnostic::error("Complex expression used as sub-expression")
2070 .with_infer_code("complex_sub_expression")
2071 .with_span_label(&span, "expected simple expression")
2072 .with_help("Hoist to a `let` binding")
2073}
2074
2075pub fn reference_through_newtype(span: Span) -> LisetteDiagnostic {
2076 LisetteDiagnostic::error("Cannot take reference through newtype boundary")
2077 .with_infer_code("reference_through_newtype")
2078 .with_span_label(&span, "newtype `.0` inside `&`")
2079 .with_help("Bind the inner value first: `let inner = val.0; &inner`")
2080}
2081
2082pub fn immutable_argument_to_mut_param(
2083 var_name: &str,
2084 span: Span,
2085 is_external: bool,
2086) -> LisetteDiagnostic {
2087 let help = if is_external {
2088 format!(
2089 "Bindings in Lisette are immutable by default. Use `let mut {} = ...` to allow mutation",
2090 var_name
2091 )
2092 } else {
2093 format!(
2094 "Bindings in Lisette are immutable by default. Use `let mut {} = ...` to allow mutation, \
2095 or make the parameter immutable by removing `mut` from the function signature",
2096 var_name
2097 )
2098 };
2099 LisetteDiagnostic::error("Immutable argument passed to `mut` parameter")
2100 .with_infer_code("immutable_arg_to_mut_param")
2101 .with_span_label(&span, "expected mutable, found immutable")
2102 .with_help(help)
2103}
2104
2105pub fn failure_propagation_in_expression(span: Span) -> LisetteDiagnostic {
2106 LisetteDiagnostic::error("Failure propagation in expression position")
2107 .with_infer_code("failure_propagation_in_expression")
2108 .with_span_label(
2109 &span,
2110 "`Err(..)?` and `None?` always early-return and never produce a value",
2111 )
2112 .with_help("Use `return Err(..)` or `return None` instead")
2113}
2114
2115pub fn never_call_in_expression(span: Span) -> LisetteDiagnostic {
2116 LisetteDiagnostic::error("Never-returning call in expression position")
2117 .with_infer_code("never_call_in_expression")
2118 .with_span_label(&span, "`panic` never returns and cannot produce a value")
2119 .with_help("Use `panic(...)` as a statement instead")
2120}
2121
2122pub fn invalid_main_signature(span: Span) -> LisetteDiagnostic {
2123 LisetteDiagnostic::error("Invalid main signature")
2124 .with_infer_code("invalid_main_signature")
2125 .with_span_label(&span, "`main` must have no parameters and no return type")
2126 .with_help(
2127 "Use `fn main() { ... }`. To handle errors, use `match` or `if let` \
2128 inside main instead of returning `Result`.",
2129 )
2130}
2131
2132pub fn parenthesized_qualifier(path: &str, member: &str, span: Span) -> LisetteDiagnostic {
2133 LisetteDiagnostic::error("Unnecessary parentheses around qualifier")
2134 .with_infer_code("parenthesized_qualifier")
2135 .with_span_label(&span, "parenthesized qualifier")
2136 .with_help(format!("Remove the parentheses: `{}.{}`", path, member))
2137}
2138
2139pub fn type_alias_as_qualifier(
2140 alias: &str,
2141 underlying: &str,
2142 member: &str,
2143 span: Span,
2144) -> LisetteDiagnostic {
2145 LisetteDiagnostic::error("Cannot use generic type alias as qualifier")
2146 .with_infer_code("type_alias_as_qualifier")
2147 .with_span_label(
2148 &span,
2149 format!("`{}` aliases `{}`", alias, underlying),
2150 )
2151 .with_help(format!(
2152 "Aliases for types with generic parameters are not supported as qualifiers. Use the original type directly: `{}.{}`",
2153 underlying, member
2154 ))
2155}
2156
2157pub fn control_flow_in_expression(keyword: &str, span: Span) -> LisetteDiagnostic {
2158 LisetteDiagnostic::error(format!(
2159 "`{}` cannot be used in expression position",
2160 keyword
2161 ))
2162 .with_infer_code("control_flow_in_expression")
2163 .with_span_label(
2164 &span,
2165 format!("`{}` is a statement and cannot produce a value", keyword),
2166 )
2167 .with_help(format!(
2168 "Use `{}` as a standalone statement instead",
2169 keyword
2170 ))
2171}
2172
2173pub fn reference_aliases_sibling(ref_span: Span, var_name: &str) -> LisetteDiagnostic {
2174 LisetteDiagnostic::error("Reference aliases sibling expression")
2175 .with_infer_code("reference_aliases_sibling")
2176 .with_span_label(
2177 &ref_span,
2178 format!(
2179 "`&{}` could mutate `{}` used by a sibling",
2180 var_name, var_name
2181 ),
2182 )
2183 .with_help(format!(
2184 "Bind `{}` to a `let` before this expression to make evaluation order explicit",
2185 var_name
2186 ))
2187}