1pub mod ice;
21
22use gruel_span::Span;
23use std::borrow::Cow;
24use std::collections::HashSet;
25use std::fmt;
26use thiserror::Error;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
57pub struct ErrorCode(pub u16);
58
59impl ErrorCode {
60 pub const UNEXPECTED_CHARACTER: Self = Self(1);
64 pub const INVALID_INTEGER: Self = Self(2);
65 pub const INVALID_STRING_ESCAPE: Self = Self(3);
66 pub const UNTERMINATED_STRING: Self = Self(4);
67 pub const INVALID_FLOAT: Self = Self(5);
68
69 pub const UNEXPECTED_TOKEN: Self = Self(100);
73 pub const UNEXPECTED_EOF: Self = Self(101);
74 pub const PARSE_ERROR: Self = Self(102);
75
76 pub const NO_MAIN_FUNCTION: Self = Self(200);
80 pub const UNDEFINED_VARIABLE: Self = Self(201);
81 pub const UNDEFINED_FUNCTION: Self = Self(202);
82 pub const ASSIGN_TO_IMMUTABLE: Self = Self(203);
83 pub const UNKNOWN_TYPE: Self = Self(204);
84 pub const USE_AFTER_MOVE: Self = Self(205);
85 pub const TYPE_MISMATCH: Self = Self(206);
86 pub const WRONG_ARGUMENT_COUNT: Self = Self(207);
87
88 pub const MISSING_FIELDS: Self = Self(400);
92 pub const UNKNOWN_FIELD: Self = Self(401);
93 pub const DUPLICATE_FIELD: Self = Self(402);
94 pub const COPY_STRUCT_NON_COPY_FIELD: Self = Self(403);
95 pub const RESERVED_TYPE_NAME: Self = Self(404);
96 pub const DUPLICATE_TYPE_DEFINITION: Self = Self(405);
97 pub const LINEAR_VALUE_NOT_CONSUMED: Self = Self(406);
98 pub const LINEAR_STRUCT_COPY: Self = Self(407);
99 pub const HANDLE_STRUCT_MISSING_METHOD: Self = Self(408);
100 pub const HANDLE_METHOD_WRONG_SIGNATURE: Self = Self(409);
101 pub const DUPLICATE_METHOD: Self = Self(410);
102 pub const UNDEFINED_METHOD: Self = Self(411);
103 pub const UNDEFINED_ASSOC_FN: Self = Self(412);
104 pub const METHOD_CALL_ON_NON_STRUCT: Self = Self(413);
105 pub const METHOD_CALLED_AS_ASSOC_FN: Self = Self(414);
106 pub const ASSOC_FN_CALLED_AS_METHOD: Self = Self(415);
107 pub const DUPLICATE_DESTRUCTOR: Self = Self(416);
108 pub const DESTRUCTOR_UNKNOWN_TYPE: Self = Self(417);
109 pub const DUPLICATE_CONSTANT: Self = Self(418);
110 pub const CONST_EXPR_NOT_SUPPORTED: Self = Self(434);
111 pub const DUPLICATE_VARIANT: Self = Self(419);
112 pub const UNKNOWN_VARIANT: Self = Self(420);
113 pub const UNKNOWN_ENUM_TYPE: Self = Self(421);
114 pub const FIELD_WRONG_ORDER: Self = Self(422);
115 pub const FIELD_ACCESS_ON_NON_STRUCT: Self = Self(423);
116 pub const INVALID_ASSIGNMENT_TARGET: Self = Self(424);
117 pub const INOUT_NON_LVALUE: Self = Self(425);
118 pub const INOUT_EXCLUSIVE_ACCESS: Self = Self(426);
119 pub const BORROW_NON_LVALUE: Self = Self(427);
120 pub const MUTATE_BORROWED_VALUE: Self = Self(428);
121 pub const MOVE_OUT_OF_BORROW: Self = Self(429);
122 pub const BORROW_INOUT_CONFLICT: Self = Self(430);
123 pub const INOUT_KEYWORD_MISSING: Self = Self(431);
124 pub const BORROW_KEYWORD_MISSING: Self = Self(432);
125 pub const EMPTY_STRUCT: Self = Self(433);
126
127 pub const BREAK_OUTSIDE_LOOP: Self = Self(500);
131 pub const CONTINUE_OUTSIDE_LOOP: Self = Self(501);
132 pub const INTRINSIC_REQUIRES_CHECKED: Self = Self(502);
133 pub const UNCHECKED_CALL_REQUIRES_CHECKED: Self = Self(503);
134
135 pub const NON_EXHAUSTIVE_MATCH: Self = Self(600);
139 pub const EMPTY_MATCH: Self = Self(601);
140 pub const INVALID_MATCH_TYPE: Self = Self(602);
141
142 pub const UNKNOWN_INTRINSIC: Self = Self(700);
146 pub const INTRINSIC_WRONG_ARG_COUNT: Self = Self(701);
147 pub const INTRINSIC_TYPE_MISMATCH: Self = Self(702);
148 pub const IMPORT_REQUIRES_STRING_LITERAL: Self = Self(703);
149 pub const MODULE_NOT_FOUND: Self = Self(704);
150 pub const STD_LIB_NOT_FOUND: Self = Self(705);
151 pub const PRIVATE_MEMBER_ACCESS: Self = Self(706);
152 pub const UNKNOWN_MODULE_MEMBER: Self = Self(707);
153
154 pub const LITERAL_OUT_OF_RANGE: Self = Self(800);
158 pub const CANNOT_NEGATE_UNSIGNED: Self = Self(801);
159 pub const CHAINED_COMPARISON: Self = Self(802);
160
161 pub const INDEX_ON_NON_ARRAY: Self = Self(900);
165 pub const ARRAY_LENGTH_MISMATCH: Self = Self(901);
166 pub const INDEX_OUT_OF_BOUNDS: Self = Self(902);
167 pub const TYPE_ANNOTATION_REQUIRED: Self = Self(903);
168 pub const MOVE_OUT_OF_INDEX: Self = Self(904);
169
170 pub const LINK_ERROR: Self = Self(1000);
174 pub const UNSUPPORTED_TARGET: Self = Self(1001);
175
176 pub const PREVIEW_FEATURE_REQUIRED: Self = Self(1100);
180
181 pub const COMPTIME_EVALUATION_FAILED: Self = Self(1200);
185 pub const COMPTIME_ARG_NOT_CONST: Self = Self(1201);
186 pub const COMPTIME_USER_ERROR: Self = Self(1202);
187
188 pub const INTERNAL_ERROR: Self = Self(9000);
192 pub const INTERNAL_CODEGEN_ERROR: Self = Self(9001);
193}
194
195impl fmt::Display for ErrorCode {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 write!(f, "E{:04}", self.0)
198 }
199}
200
201#[derive(Debug, Clone, PartialEq, Eq)]
254pub struct MissingFieldsError {
255 pub struct_name: String,
256 pub missing_fields: Vec<String>,
257}
258
259#[derive(Debug, Clone, PartialEq, Eq)]
261pub struct CopyStructNonCopyFieldError {
262 pub struct_name: String,
263 pub field_name: String,
264 pub field_type: String,
265}
266
267#[derive(Debug, Clone, PartialEq, Eq)]
269pub struct IntrinsicTypeMismatchError {
270 pub name: String,
271 pub expected: String,
272 pub found: String,
273}
274
275#[derive(Debug, Clone, PartialEq, Eq)]
277pub struct FieldWrongOrderError {
278 pub struct_name: String,
279 pub expected_field: String,
280 pub found_field: String,
281}
282
283#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
299#[non_exhaustive]
300pub enum PreviewFeature {
301 TestInfra,
304}
305
306#[derive(Debug, Clone, PartialEq, Eq)]
308pub struct ParsePreviewFeatureError(String);
309
310impl fmt::Display for ParsePreviewFeatureError {
311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312 write!(f, "unknown preview feature '{}'", self.0)
313 }
314}
315
316impl std::error::Error for ParsePreviewFeatureError {}
317
318impl PreviewFeature {
319 pub fn name(&self) -> &'static str {
321 match *self {
322 PreviewFeature::TestInfra => "test_infra",
323 }
324 }
325
326 pub fn adr(&self) -> &'static str {
328 match *self {
329 PreviewFeature::TestInfra => "ADR-0005",
330 }
331 }
332
333 pub fn all() -> &'static [PreviewFeature] {
335 &[PreviewFeature::TestInfra]
336 }
337
338 pub fn all_names() -> String {
340 if Self::all().is_empty() {
341 "(none)".to_string()
342 } else {
343 Self::all()
344 .iter()
345 .map(|f| f.name())
346 .collect::<Vec<_>>()
347 .join(", ")
348 }
349 }
350}
351
352impl std::str::FromStr for PreviewFeature {
353 type Err = ParsePreviewFeatureError;
354
355 fn from_str(s: &str) -> Result<Self, Self::Err> {
356 match s {
357 "test_infra" => Ok(PreviewFeature::TestInfra),
358 _ => Err(ParsePreviewFeatureError(s.to_string())),
359 }
360 }
361}
362
363impl fmt::Display for PreviewFeature {
364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365 write!(f, "{}", self.name())
366 }
367}
368
369pub type PreviewFeatures = HashSet<PreviewFeature>;
371
372#[derive(Debug, Clone)]
381pub struct Label {
382 pub message: String,
384 pub span: Span,
386}
387
388impl Label {
389 pub fn new(message: impl Into<String>, span: Span) -> Self {
391 Self {
392 message: message.into(),
393 span,
394 }
395 }
396}
397
398#[derive(Debug, Clone)]
403pub struct Note(pub String);
404
405impl Note {
406 pub fn new(message: impl Into<String>) -> Self {
408 Self(message.into())
409 }
410}
411
412impl std::fmt::Display for Note {
413 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
414 write!(f, "{}", self.0)
415 }
416}
417
418#[derive(Debug, Clone)]
423pub struct Help(pub String);
424
425impl Help {
426 pub fn new(message: impl Into<String>) -> Self {
428 Self(message.into())
429 }
430}
431
432impl std::fmt::Display for Help {
433 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
434 write!(f, "{}", self.0)
435 }
436}
437
438#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
443pub enum Applicability {
444 MachineApplicable,
448
449 MaybeIncorrect,
454
455 HasPlaceholders,
460
461 #[default]
466 Unspecified,
467}
468
469impl std::fmt::Display for Applicability {
470 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
471 match self {
472 Applicability::MachineApplicable => write!(f, "MachineApplicable"),
473 Applicability::MaybeIncorrect => write!(f, "MaybeIncorrect"),
474 Applicability::HasPlaceholders => write!(f, "HasPlaceholders"),
475 Applicability::Unspecified => write!(f, "Unspecified"),
476 }
477 }
478}
479
480#[derive(Debug, Clone)]
485pub struct Suggestion {
486 pub message: String,
488 pub span: Span,
490 pub replacement: String,
492 pub applicability: Applicability,
494}
495
496impl Suggestion {
497 pub fn new(message: impl Into<String>, span: Span, replacement: impl Into<String>) -> Self {
499 Self {
500 message: message.into(),
501 span,
502 replacement: replacement.into(),
503 applicability: Applicability::Unspecified,
504 }
505 }
506
507 pub fn machine_applicable(
509 message: impl Into<String>,
510 span: Span,
511 replacement: impl Into<String>,
512 ) -> Self {
513 Self {
514 message: message.into(),
515 span,
516 replacement: replacement.into(),
517 applicability: Applicability::MachineApplicable,
518 }
519 }
520
521 pub fn maybe_incorrect(
523 message: impl Into<String>,
524 span: Span,
525 replacement: impl Into<String>,
526 ) -> Self {
527 Self {
528 message: message.into(),
529 span,
530 replacement: replacement.into(),
531 applicability: Applicability::MaybeIncorrect,
532 }
533 }
534
535 pub fn with_placeholders(
537 message: impl Into<String>,
538 span: Span,
539 replacement: impl Into<String>,
540 ) -> Self {
541 Self {
542 message: message.into(),
543 span,
544 replacement: replacement.into(),
545 applicability: Applicability::HasPlaceholders,
546 }
547 }
548
549 pub fn with_applicability(mut self, applicability: Applicability) -> Self {
551 self.applicability = applicability;
552 self
553 }
554}
555
556#[derive(Debug, Clone, Default)]
561pub struct Diagnostic {
562 pub labels: Vec<Label>,
564 pub notes: Vec<Note>,
566 pub helps: Vec<Help>,
568 pub suggestions: Vec<Suggestion>,
570}
571
572impl Diagnostic {
573 pub fn new() -> Self {
575 Self::default()
576 }
577
578 pub fn is_empty(&self) -> bool {
580 self.labels.is_empty()
581 && self.notes.is_empty()
582 && self.helps.is_empty()
583 && self.suggestions.is_empty()
584 }
585}
586
587#[derive(Debug, Clone)]
607#[must_use = "compiler diagnostics should not be ignored"]
608pub struct DiagnosticWrapper<K> {
609 pub kind: K,
611 span: Option<Span>,
612 diagnostic: Box<Diagnostic>,
613}
614
615impl<K> DiagnosticWrapper<K> {
616 #[inline]
618 pub fn new(kind: K, span: Span) -> Self {
619 Self {
620 kind,
621 span: Some(span),
622 diagnostic: Box::new(Diagnostic::new()),
623 }
624 }
625
626 #[inline]
631 pub fn without_span(kind: K) -> Self {
632 Self {
633 kind,
634 span: None,
635 diagnostic: Box::new(Diagnostic::new()),
636 }
637 }
638
639 #[inline]
641 pub fn has_span(&self) -> bool {
642 self.span.is_some()
643 }
644
645 #[inline]
647 pub fn span(&self) -> Option<Span> {
648 self.span
649 }
650
651 #[inline]
653 pub fn diagnostic(&self) -> &Diagnostic {
654 &self.diagnostic
655 }
656
657 #[inline]
661 pub fn with_label(mut self, message: impl Into<String>, span: Span) -> Self {
662 self.diagnostic.labels.push(Label::new(message, span));
663 self
664 }
665
666 #[inline]
670 pub fn with_note(mut self, message: impl Into<String>) -> Self {
671 self.diagnostic.notes.push(Note::new(message));
672 self
673 }
674
675 #[inline]
679 pub fn with_help(mut self, message: impl Into<String>) -> Self {
680 self.diagnostic.helps.push(Help::new(message));
681 self
682 }
683
684 #[inline]
688 pub fn with_suggestion(mut self, suggestion: Suggestion) -> Self {
689 self.diagnostic.suggestions.push(suggestion);
690 self
691 }
692}
693
694impl<K: fmt::Display> fmt::Display for DiagnosticWrapper<K> {
695 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
696 write!(f, "{}", self.kind)
697 }
698}
699
700impl<K: fmt::Display + fmt::Debug> std::error::Error for DiagnosticWrapper<K> {}
701
702pub type CompileError = DiagnosticWrapper<ErrorKind>;
719
720fn format_argument_count(expected: usize, found: usize) -> String {
723 if expected == 1 {
724 format!("expected {} argument, found {}", expected, found)
725 } else {
726 format!("expected {} arguments, found {}", expected, found)
727 }
728}
729
730fn format_missing_fields(err: &MissingFieldsError) -> String {
731 if err.missing_fields.len() == 1 {
732 format!(
733 "missing field '{}' in struct '{}'",
734 err.missing_fields[0], err.struct_name
735 )
736 } else {
737 let fields = err
738 .missing_fields
739 .iter()
740 .map(|f| format!("'{}'", f))
741 .collect::<Vec<_>>()
742 .join(", ");
743 format!("missing fields {} in struct '{}'", fields, err.struct_name)
744 }
745}
746
747fn format_intrinsic_arg_count(name: &str, expected: usize, found: usize) -> String {
748 if expected == 1 {
749 format!(
750 "intrinsic '@{}' expects {} argument, found {}",
751 name, expected, found
752 )
753 } else {
754 format!(
755 "intrinsic '@{}' expects {} arguments, found {}",
756 name, expected, found
757 )
758 }
759}
760
761fn format_array_length_mismatch(expected: u64, found: u64) -> String {
762 if expected == 1 {
763 format!(
764 "expected array of {} element, found {} elements",
765 expected, found
766 )
767 } else {
768 format!(
769 "expected array of {} elements, found {} elements",
770 expected, found
771 )
772 }
773}
774
775#[derive(Debug, Clone, PartialEq, Eq, Error)]
777pub enum ErrorKind {
778 #[error("unexpected character: {0}")]
780 UnexpectedCharacter(char),
781 #[error("invalid integer literal")]
782 InvalidInteger,
783 #[error("invalid floating-point literal")]
784 InvalidFloat,
785 #[error("invalid escape sequence: \\{0}")]
786 InvalidStringEscape(char),
787 #[error("unterminated string literal")]
788 UnterminatedString,
789
790 #[error("expected {expected}, found {found}")]
792 UnexpectedToken {
793 expected: Cow<'static, str>,
794 found: Cow<'static, str>,
795 },
796 #[error("unexpected end of file, expected {expected}")]
797 UnexpectedEof { expected: Cow<'static, str> },
798 #[error("{0}")]
802 ParseError(String),
803
804 #[error("no main function found")]
806 NoMainFunction,
807 #[error("undefined variable '{0}'")]
808 UndefinedVariable(String),
809 #[error("undefined function '{0}'")]
810 UndefinedFunction(String),
811 #[error("cannot assign to immutable variable '{0}'")]
812 AssignToImmutable(String),
813 #[error("unknown type '{0}'")]
814 UnknownType(String),
815 #[error("use of moved value '{0}'")]
817 UseAfterMove(String),
818 #[error("cannot move field `{field}` out of `{type_name}`")]
820 CannotMoveField { type_name: String, field: String },
821 #[error("type mismatch: expected {expected}, found {found}")]
822 TypeMismatch { expected: String, found: String },
823 #[error("{}", format_argument_count(*.expected, *.found))]
824 WrongArgumentCount { expected: usize, found: usize },
825
826 #[error("{}", format_missing_fields(.0))]
828 MissingFields(Box<MissingFieldsError>),
829 #[error("unknown field '{field_name}' in struct '{struct_name}'")]
830 UnknownField {
831 struct_name: String,
832 field_name: String,
833 },
834 #[error("duplicate field '{field_name}' in struct '{struct_name}'")]
835 DuplicateField {
836 struct_name: String,
837 field_name: String,
838 },
839 #[error("missing field `{field}` in destructuring of `{struct_name}`")]
841 MissingFieldInDestructure { struct_name: String, field: String },
842 #[error("empty struct is not allowed")]
844 EmptyStruct,
845 #[error("anonymous enum must have at least one variant")]
847 EmptyAnonEnum,
848 #[error("@copy struct '{struct_name}' has field '{field_name}' with non-Copy type '{field_type}'", struct_name = .0.struct_name, field_name = .0.field_name, field_type = .0.field_type)]
850 CopyStructNonCopyField(Box<CopyStructNonCopyFieldError>),
851 #[error("cannot define type `{type_name}`: name is reserved for built-in type")]
853 ReservedTypeName { type_name: String },
854 #[error("duplicate type definition: `{type_name}` is already defined")]
856 DuplicateTypeDefinition { type_name: String },
857 #[error("linear value '{0}' must be consumed but was dropped")]
859 LinearValueNotConsumed(String),
860 #[error("linear struct '{0}' cannot be marked @copy")]
862 LinearStructCopy(String),
863 #[error("struct '{struct_name}' is marked @handle but has no `handle` method")]
865 HandleStructMissingMethod { struct_name: String },
866 #[error(
868 "struct '{struct_name}' has `handle` method with wrong signature: expected `fn handle(self: {struct_name}) -> {struct_name}`, found `{found_signature}`"
869 )]
870 HandleMethodWrongSignature {
871 struct_name: String,
872 found_signature: String,
873 },
874 #[error("duplicate method '{method_name}' for type '{type_name}'")]
876 DuplicateMethod {
877 type_name: String,
878 method_name: String,
879 },
880 #[error("no method named '{method_name}' found for type '{type_name}'")]
882 UndefinedMethod {
883 type_name: String,
884 method_name: String,
885 },
886 #[error("no associated function named '{function_name}' found for type '{type_name}'")]
888 UndefinedAssocFn {
889 type_name: String,
890 function_name: String,
891 },
892 #[error("no method named '{method_name}' on type '{found}'")]
894 MethodCallOnNonStruct { found: String, method_name: String },
895 #[error(
897 "'{type_name}::{method_name}' is a method, not an associated function; use receiver.{method_name}() syntax"
898 )]
899 MethodCalledAsAssocFn {
900 type_name: String,
901 method_name: String,
902 },
903 #[error(
905 "'{function_name}' is an associated function, not a method; use {type_name}::{function_name}() syntax"
906 )]
907 AssocFnCalledAsMethod {
908 type_name: String,
909 function_name: String,
910 },
911
912 #[error("duplicate destructor for type '{type_name}'")]
915 DuplicateDestructor { type_name: String },
916 #[error("unknown type '{type_name}' in destructor")]
918 DestructorUnknownType { type_name: String },
919
920 #[error("duplicate {kind} '{name}'")]
923 DuplicateConstant { name: String, kind: String },
924 #[error("{expr_kind} is not supported in const context")]
926 ConstExprNotSupported { expr_kind: String },
927
928 #[error("duplicate variant '{variant_name}' in enum '{enum_name}'")]
930 DuplicateVariant {
931 enum_name: String,
932 variant_name: String,
933 },
934 #[error("unknown variant '{variant_name}' in enum '{enum_name}'")]
935 UnknownVariant {
936 enum_name: String,
937 variant_name: String,
938 },
939 #[error("unknown enum type '{0}'")]
940 UnknownEnumType(String),
941 #[error("struct '{struct_name}' fields must be initialized in declaration order: expected '{expected_field}', found '{found_field}'", struct_name = .0.struct_name, expected_field = .0.expected_field, found_field = .0.found_field)]
942 FieldWrongOrder(Box<FieldWrongOrderError>),
943 #[error("field access on non-struct type '{found}'")]
944 FieldAccessOnNonStruct { found: String },
945 #[error("invalid assignment target")]
946 InvalidAssignmentTarget,
947 #[error("inout argument must be an lvalue (variable, field, or array element)")]
949 InoutNonLvalue,
950 #[error("cannot pass same variable '{variable}' to multiple inout parameters")]
952 InoutExclusiveAccess { variable: String },
953 #[error("borrow argument must be a variable, field, or array element")]
955 BorrowNonLvalue,
956 #[error("cannot mutate borrowed value '{variable}'")]
958 MutateBorrowedValue { variable: String },
959 #[error("cannot move out of borrowed value '{variable}'")]
961 MoveOutOfBorrow { variable: String },
962 #[error("cannot borrow '{variable}' while it is mutably borrowed (inout)")]
964 BorrowInoutConflict { variable: String },
965 #[error("argument to inout parameter must use 'inout' keyword")]
967 InoutKeywordMissing,
968 #[error("argument to borrow parameter must use 'borrow' keyword")]
970 BorrowKeywordMissing,
971
972 #[error("'break' outside of loop")]
974 BreakOutsideLoop,
975 #[error(
976 "'break' in for-in loop over array with non-Copy element type '{element_type}' would leak un-iterated elements"
977 )]
978 BreakInConsumingForLoop { element_type: String },
979 #[error("'continue' outside of loop")]
980 ContinueOutsideLoop,
981
982 #[error("intrinsic '@{0}' can only be used inside a `checked` block")]
984 IntrinsicRequiresChecked(String),
985 #[error("call to unchecked function '{0}' can only be used inside a `checked` block")]
986 UncheckedCallRequiresChecked(String),
987
988 #[error("match is not exhaustive")]
990 NonExhaustiveMatch,
991 #[error("match expression has no arms")]
992 EmptyMatch,
993 #[error("cannot match on type '{0}', expected integer, bool, or enum")]
994 InvalidMatchType(String),
995
996 #[error("unknown intrinsic '@{0}'")]
998 UnknownIntrinsic(String),
999 #[error("{}", format_intrinsic_arg_count(name, *.expected, *.found))]
1000 IntrinsicWrongArgCount {
1001 name: String,
1002 expected: usize,
1003 found: usize,
1004 },
1005 #[error("intrinsic '@{name}' expects {expected}, found {found}", name = .0.name, expected = .0.expected, found = .0.found)]
1006 IntrinsicTypeMismatch(Box<IntrinsicTypeMismatchError>),
1007
1008 #[error("@import requires a string literal argument")]
1010 ImportRequiresStringLiteral,
1011 #[error("cannot find module '{path}'")]
1012 ModuleNotFound {
1013 path: String,
1014 candidates: Vec<String>,
1016 },
1017 #[error("standard library not found")]
1018 StdLibNotFound,
1019 #[error("{item_kind} `{name}` is private")]
1020 PrivateMemberAccess { item_kind: String, name: String },
1021 #[error("module `{module_name}` has no member `{member_name}`")]
1022 UnknownModuleMember {
1023 module_name: String,
1024 member_name: String,
1025 },
1026
1027 #[error("literal value {value} is out of range for type '{ty}'")]
1029 LiteralOutOfRange { value: u64, ty: String },
1030
1031 #[error("cannot apply unary operator `-` to type '{0}'")]
1033 CannotNegateUnsigned(String),
1034 #[error("comparison operators cannot be chained")]
1035 ChainedComparison,
1036
1037 #[error("cannot index into non-array type '{found}'")]
1039 IndexOnNonArray { found: String },
1040 #[error("{}", format_array_length_mismatch(*.expected, *.found))]
1041 ArrayLengthMismatch { expected: u64, found: u64 },
1042 #[error("index out of bounds: the length is {length} but the index is {index}")]
1043 IndexOutOfBounds { index: i64, length: u64 },
1044 #[error("type annotation required for empty array")]
1045 TypeAnnotationRequired,
1046 #[error("cannot move out of indexed position: element type '{element_type}' is not Copy")]
1048 MoveOutOfIndex { element_type: String },
1049
1050 #[error("link error: {0}")]
1052 LinkError(String),
1053
1054 #[error("unsupported target: {0}")]
1056 UnsupportedTarget(String),
1057
1058 #[error("{what} requires preview feature `{}`", .feature.name())]
1060 PreviewFeatureRequired {
1061 feature: PreviewFeature,
1062 what: String,
1063 },
1064
1065 #[error("comptime evaluation failed: {reason}")]
1067 ComptimeEvaluationFailed { reason: String },
1068
1069 #[error("comptime parameter requires a compile-time known value")]
1070 ComptimeArgNotConst { param_name: String },
1071
1072 #[error("{0}")]
1073 ComptimeUserError(String),
1074
1075 #[error("internal compiler error: {0}")]
1077 InternalError(String),
1078
1079 #[error("internal codegen error: {0}")]
1081 InternalCodegenError(String),
1082}
1083
1084impl ErrorKind {
1085 pub fn code(&self) -> ErrorCode {
1090 match self {
1091 ErrorKind::UnexpectedCharacter(_) => ErrorCode::UNEXPECTED_CHARACTER,
1093 ErrorKind::InvalidInteger => ErrorCode::INVALID_INTEGER,
1094 ErrorKind::InvalidFloat => ErrorCode::INVALID_FLOAT,
1095 ErrorKind::InvalidStringEscape(_) => ErrorCode::INVALID_STRING_ESCAPE,
1096 ErrorKind::UnterminatedString => ErrorCode::UNTERMINATED_STRING,
1097
1098 ErrorKind::UnexpectedToken { .. } => ErrorCode::UNEXPECTED_TOKEN,
1100 ErrorKind::UnexpectedEof { .. } => ErrorCode::UNEXPECTED_EOF,
1101 ErrorKind::ParseError(_) => ErrorCode::PARSE_ERROR,
1102
1103 ErrorKind::NoMainFunction => ErrorCode::NO_MAIN_FUNCTION,
1105 ErrorKind::UndefinedVariable(_) => ErrorCode::UNDEFINED_VARIABLE,
1106 ErrorKind::UndefinedFunction(_) => ErrorCode::UNDEFINED_FUNCTION,
1107 ErrorKind::AssignToImmutable(_) => ErrorCode::ASSIGN_TO_IMMUTABLE,
1108 ErrorKind::UnknownType(_) => ErrorCode::UNKNOWN_TYPE,
1109 ErrorKind::UseAfterMove(_) => ErrorCode::USE_AFTER_MOVE,
1110 ErrorKind::CannotMoveField { .. } => ErrorCode::USE_AFTER_MOVE,
1111 ErrorKind::TypeMismatch { .. } => ErrorCode::TYPE_MISMATCH,
1112 ErrorKind::WrongArgumentCount { .. } => ErrorCode::WRONG_ARGUMENT_COUNT,
1113
1114 ErrorKind::MissingFields(_) => ErrorCode::MISSING_FIELDS,
1116 ErrorKind::MissingFieldInDestructure { .. } => ErrorCode::MISSING_FIELDS,
1117 ErrorKind::UnknownField { .. } => ErrorCode::UNKNOWN_FIELD,
1118 ErrorKind::DuplicateField { .. } => ErrorCode::DUPLICATE_FIELD,
1119 ErrorKind::EmptyStruct => ErrorCode::EMPTY_STRUCT,
1120 ErrorKind::EmptyAnonEnum => ErrorCode::EMPTY_STRUCT, ErrorKind::CopyStructNonCopyField(_) => ErrorCode::COPY_STRUCT_NON_COPY_FIELD,
1122 ErrorKind::ReservedTypeName { .. } => ErrorCode::RESERVED_TYPE_NAME,
1123 ErrorKind::DuplicateTypeDefinition { .. } => ErrorCode::DUPLICATE_TYPE_DEFINITION,
1124 ErrorKind::LinearValueNotConsumed(_) => ErrorCode::LINEAR_VALUE_NOT_CONSUMED,
1125 ErrorKind::LinearStructCopy(_) => ErrorCode::LINEAR_STRUCT_COPY,
1126 ErrorKind::HandleStructMissingMethod { .. } => ErrorCode::HANDLE_STRUCT_MISSING_METHOD,
1127 ErrorKind::HandleMethodWrongSignature { .. } => {
1128 ErrorCode::HANDLE_METHOD_WRONG_SIGNATURE
1129 }
1130 ErrorKind::DuplicateMethod { .. } => ErrorCode::DUPLICATE_METHOD,
1131 ErrorKind::UndefinedMethod { .. } => ErrorCode::UNDEFINED_METHOD,
1132 ErrorKind::UndefinedAssocFn { .. } => ErrorCode::UNDEFINED_ASSOC_FN,
1133 ErrorKind::MethodCallOnNonStruct { .. } => ErrorCode::METHOD_CALL_ON_NON_STRUCT,
1134 ErrorKind::MethodCalledAsAssocFn { .. } => ErrorCode::METHOD_CALLED_AS_ASSOC_FN,
1135 ErrorKind::AssocFnCalledAsMethod { .. } => ErrorCode::ASSOC_FN_CALLED_AS_METHOD,
1136 ErrorKind::DuplicateDestructor { .. } => ErrorCode::DUPLICATE_DESTRUCTOR,
1137 ErrorKind::DestructorUnknownType { .. } => ErrorCode::DESTRUCTOR_UNKNOWN_TYPE,
1138 ErrorKind::DuplicateConstant { .. } => ErrorCode::DUPLICATE_CONSTANT,
1139 ErrorKind::ConstExprNotSupported { .. } => ErrorCode::CONST_EXPR_NOT_SUPPORTED,
1140 ErrorKind::DuplicateVariant { .. } => ErrorCode::DUPLICATE_VARIANT,
1141 ErrorKind::UnknownVariant { .. } => ErrorCode::UNKNOWN_VARIANT,
1142 ErrorKind::UnknownEnumType(_) => ErrorCode::UNKNOWN_ENUM_TYPE,
1143 ErrorKind::FieldWrongOrder(_) => ErrorCode::FIELD_WRONG_ORDER,
1144 ErrorKind::FieldAccessOnNonStruct { .. } => ErrorCode::FIELD_ACCESS_ON_NON_STRUCT,
1145 ErrorKind::InvalidAssignmentTarget => ErrorCode::INVALID_ASSIGNMENT_TARGET,
1146 ErrorKind::InoutNonLvalue => ErrorCode::INOUT_NON_LVALUE,
1147 ErrorKind::InoutExclusiveAccess { .. } => ErrorCode::INOUT_EXCLUSIVE_ACCESS,
1148 ErrorKind::BorrowNonLvalue => ErrorCode::BORROW_NON_LVALUE,
1149 ErrorKind::MutateBorrowedValue { .. } => ErrorCode::MUTATE_BORROWED_VALUE,
1150 ErrorKind::MoveOutOfBorrow { .. } => ErrorCode::MOVE_OUT_OF_BORROW,
1151 ErrorKind::BorrowInoutConflict { .. } => ErrorCode::BORROW_INOUT_CONFLICT,
1152 ErrorKind::InoutKeywordMissing => ErrorCode::INOUT_KEYWORD_MISSING,
1153 ErrorKind::BorrowKeywordMissing => ErrorCode::BORROW_KEYWORD_MISSING,
1154
1155 ErrorKind::BreakOutsideLoop => ErrorCode::BREAK_OUTSIDE_LOOP,
1157 ErrorKind::BreakInConsumingForLoop { .. } => ErrorCode::BREAK_OUTSIDE_LOOP,
1158 ErrorKind::ContinueOutsideLoop => ErrorCode::CONTINUE_OUTSIDE_LOOP,
1159 ErrorKind::IntrinsicRequiresChecked(_) => ErrorCode::INTRINSIC_REQUIRES_CHECKED,
1160 ErrorKind::UncheckedCallRequiresChecked(_) => {
1161 ErrorCode::UNCHECKED_CALL_REQUIRES_CHECKED
1162 }
1163
1164 ErrorKind::NonExhaustiveMatch => ErrorCode::NON_EXHAUSTIVE_MATCH,
1166 ErrorKind::EmptyMatch => ErrorCode::EMPTY_MATCH,
1167 ErrorKind::InvalidMatchType(_) => ErrorCode::INVALID_MATCH_TYPE,
1168
1169 ErrorKind::UnknownIntrinsic(_) => ErrorCode::UNKNOWN_INTRINSIC,
1171 ErrorKind::IntrinsicWrongArgCount { .. } => ErrorCode::INTRINSIC_WRONG_ARG_COUNT,
1172 ErrorKind::IntrinsicTypeMismatch(_) => ErrorCode::INTRINSIC_TYPE_MISMATCH,
1173 ErrorKind::ImportRequiresStringLiteral => ErrorCode::IMPORT_REQUIRES_STRING_LITERAL,
1174 ErrorKind::ModuleNotFound { .. } => ErrorCode::MODULE_NOT_FOUND,
1175 ErrorKind::StdLibNotFound => ErrorCode::STD_LIB_NOT_FOUND,
1176 ErrorKind::PrivateMemberAccess { .. } => ErrorCode::PRIVATE_MEMBER_ACCESS,
1177 ErrorKind::UnknownModuleMember { .. } => ErrorCode::UNKNOWN_MODULE_MEMBER,
1178
1179 ErrorKind::LiteralOutOfRange { .. } => ErrorCode::LITERAL_OUT_OF_RANGE,
1181 ErrorKind::CannotNegateUnsigned(_) => ErrorCode::CANNOT_NEGATE_UNSIGNED,
1182 ErrorKind::ChainedComparison => ErrorCode::CHAINED_COMPARISON,
1183
1184 ErrorKind::IndexOnNonArray { .. } => ErrorCode::INDEX_ON_NON_ARRAY,
1186 ErrorKind::ArrayLengthMismatch { .. } => ErrorCode::ARRAY_LENGTH_MISMATCH,
1187 ErrorKind::IndexOutOfBounds { .. } => ErrorCode::INDEX_OUT_OF_BOUNDS,
1188 ErrorKind::TypeAnnotationRequired => ErrorCode::TYPE_ANNOTATION_REQUIRED,
1189 ErrorKind::MoveOutOfIndex { .. } => ErrorCode::MOVE_OUT_OF_INDEX,
1190
1191 ErrorKind::LinkError(_) => ErrorCode::LINK_ERROR,
1193 ErrorKind::UnsupportedTarget(_) => ErrorCode::UNSUPPORTED_TARGET,
1194
1195 ErrorKind::PreviewFeatureRequired { .. } => ErrorCode::PREVIEW_FEATURE_REQUIRED,
1197
1198 ErrorKind::ComptimeEvaluationFailed { .. } => ErrorCode::COMPTIME_EVALUATION_FAILED,
1200 ErrorKind::ComptimeArgNotConst { .. } => ErrorCode::COMPTIME_ARG_NOT_CONST,
1201 ErrorKind::ComptimeUserError(_) => ErrorCode::COMPTIME_USER_ERROR,
1202
1203 ErrorKind::InternalError(_) => ErrorCode::INTERNAL_ERROR,
1205 ErrorKind::InternalCodegenError(_) => ErrorCode::INTERNAL_CODEGEN_ERROR,
1206 }
1207 }
1208}
1209
1210impl CompileError {
1211 #[inline]
1213 pub fn at(kind: ErrorKind, pos: u32) -> Self {
1214 Self {
1215 kind,
1216 span: Some(Span::point(pos)),
1217 diagnostic: Box::new(Diagnostic::new()),
1218 }
1219 }
1220}
1221
1222pub type CompileResult<T> = Result<T, CompileError>;
1224
1225#[derive(Debug, Clone)]
1257pub struct CompileErrors {
1258 errors: Vec<CompileError>,
1259}
1260
1261impl CompileErrors {
1262 pub fn new() -> Self {
1264 Self { errors: Vec::new() }
1265 }
1266
1267 pub fn from_error(error: CompileError) -> Self {
1269 Self {
1270 errors: vec![error],
1271 }
1272 }
1273
1274 pub fn push(&mut self, error: CompileError) {
1276 self.errors.push(error);
1277 }
1278
1279 pub fn extend(&mut self, other: CompileErrors) {
1281 self.errors.extend(other.errors);
1282 }
1283
1284 pub fn is_empty(&self) -> bool {
1286 self.errors.is_empty()
1287 }
1288
1289 pub fn len(&self) -> usize {
1291 self.errors.len()
1292 }
1293
1294 pub fn first(&self) -> Option<&CompileError> {
1296 self.errors.first()
1297 }
1298
1299 pub fn iter(&self) -> impl Iterator<Item = &CompileError> {
1301 self.errors.iter()
1302 }
1303
1304 pub fn as_slice(&self) -> &[CompileError] {
1306 &self.errors
1307 }
1308
1309 pub fn into_result(self) -> Result<(), CompileErrors> {
1313 if self.is_empty() { Ok(()) } else { Err(self) }
1314 }
1315
1316 pub fn into_result_with<T>(self, value: T) -> Result<T, CompileErrors> {
1324 if self.is_empty() {
1325 Ok(value)
1326 } else {
1327 Err(self)
1328 }
1329 }
1330}
1331
1332impl Default for CompileErrors {
1333 fn default() -> Self {
1334 Self::new()
1335 }
1336}
1337
1338impl IntoIterator for CompileErrors {
1339 type Item = CompileError;
1340 type IntoIter = std::vec::IntoIter<CompileError>;
1341
1342 fn into_iter(self) -> Self::IntoIter {
1343 self.errors.into_iter()
1344 }
1345}
1346
1347impl From<CompileError> for CompileErrors {
1348 fn from(error: CompileError) -> Self {
1349 Self::from_error(error)
1350 }
1351}
1352
1353impl From<Vec<CompileError>> for CompileErrors {
1354 fn from(errors: Vec<CompileError>) -> Self {
1355 Self { errors }
1356 }
1357}
1358
1359impl From<CompileErrors> for CompileError {
1360 fn from(errors: CompileErrors) -> Self {
1365 debug_assert!(
1366 !errors.is_empty(),
1367 "converting empty CompileErrors to CompileError"
1368 );
1369 errors.errors.into_iter().next().unwrap_or_else(|| {
1370 CompileError::without_span(ErrorKind::InternalError(
1371 "empty error collection converted to single error".into(),
1372 ))
1373 })
1374 }
1375}
1376
1377impl fmt::Display for CompileErrors {
1378 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1379 match self.errors.len() {
1380 0 => write!(f, "no errors"),
1381 1 => write!(f, "{}", self.errors[0]),
1382 n => write!(
1383 f,
1384 "{} (and {} more error{})",
1385 self.errors[0],
1386 n - 1,
1387 if n == 2 { "" } else { "s" }
1388 ),
1389 }
1390 }
1391}
1392
1393impl std::error::Error for CompileErrors {}
1394
1395pub type MultiErrorResult<T> = Result<T, CompileErrors>;
1397
1398pub trait OptionExt<T> {
1415 fn ok_or_compile_error(self, kind: ErrorKind, span: Span) -> CompileResult<T>;
1417}
1418
1419impl<T> OptionExt<T> for Option<T> {
1420 #[inline]
1421 fn ok_or_compile_error(self, kind: ErrorKind, span: Span) -> CompileResult<T> {
1422 self.ok_or_else(|| CompileError::new(kind, span))
1423 }
1424}
1425
1426#[derive(Debug, Clone, PartialEq, Eq, Error)]
1428pub enum WarningKind {
1429 #[error("unused variable '{0}'")]
1431 UnusedVariable(String),
1432 #[error("unused function '{0}'")]
1434 UnusedFunction(String),
1435 #[error("unreachable code")]
1437 UnreachableCode,
1438 #[error("unreachable pattern '{0}'")]
1440 UnreachablePattern(String),
1441 #[error("comptime debug statement present — remove before release")]
1443 ComptimeDbgPresent(String),
1444}
1445
1446pub type CompileWarning = DiagnosticWrapper<WarningKind>;
1456
1457impl WarningKind {
1458 pub fn unused_variable_name(&self) -> Option<&str> {
1460 match self {
1461 WarningKind::UnusedVariable(name) => Some(name),
1462 _ => None,
1463 }
1464 }
1465
1466 pub fn format_with_line(&self, line_number: Option<usize>) -> String {
1472 match (self, line_number) {
1473 (WarningKind::UnusedVariable(name), Some(line)) => {
1474 format!("unused variable '{}' (line {})", name, line)
1475 }
1476 (WarningKind::UnusedFunction(name), Some(line)) => {
1477 format!("unused function '{}' (line {})", name, line)
1478 }
1479 _ => self.to_string(),
1480 }
1481 }
1482}
1483
1484#[cfg(test)]
1485mod tests {
1486 use super::*;
1487
1488 #[test]
1489 fn test_error_with_span() {
1490 let span = Span::new(10, 20);
1491 let error = CompileError::new(ErrorKind::InvalidInteger, span);
1492
1493 assert!(error.has_span());
1494 assert_eq!(error.span(), Some(span));
1495 assert_eq!(error.to_string(), "invalid integer literal");
1496 }
1497
1498 #[test]
1499 fn test_error_without_span() {
1500 let error = CompileError::without_span(ErrorKind::NoMainFunction);
1501
1502 assert!(!error.has_span());
1503 assert_eq!(error.span(), None);
1504 assert_eq!(error.to_string(), "no main function found");
1505 }
1506
1507 #[test]
1508 fn test_error_at_position() {
1509 let error = CompileError::at(ErrorKind::InvalidInteger, 42);
1510
1511 assert!(error.has_span());
1512 assert_eq!(error.span(), Some(Span::point(42)));
1513 }
1514
1515 #[test]
1516 fn test_error_messages() {
1517 let cases: Vec<(ErrorKind, &str)> = vec![
1518 (
1519 ErrorKind::UnexpectedCharacter('@'),
1520 "unexpected character: @",
1521 ),
1522 (
1523 ErrorKind::UnexpectedToken {
1524 expected: Cow::Borrowed("identifier"),
1525 found: Cow::Borrowed("'+'"),
1526 },
1527 "expected identifier, found '+'",
1528 ),
1529 (
1530 ErrorKind::UnexpectedEof {
1531 expected: Cow::Borrowed("'}'"),
1532 },
1533 "unexpected end of file, expected '}'",
1534 ),
1535 (
1536 ErrorKind::ParseError("custom parse error".into()),
1537 "custom parse error",
1538 ),
1539 (
1540 ErrorKind::UndefinedVariable("foo".into()),
1541 "undefined variable 'foo'",
1542 ),
1543 (
1544 ErrorKind::UndefinedFunction("bar".into()),
1545 "undefined function 'bar'",
1546 ),
1547 (
1548 ErrorKind::AssignToImmutable("x".into()),
1549 "cannot assign to immutable variable 'x'",
1550 ),
1551 (ErrorKind::UnknownType("Foo".into()), "unknown type 'Foo'"),
1552 (
1553 ErrorKind::TypeMismatch {
1554 expected: "i32".into(),
1555 found: "bool".into(),
1556 },
1557 "type mismatch: expected i32, found bool",
1558 ),
1559 (
1560 ErrorKind::WrongArgumentCount {
1561 expected: 1,
1562 found: 3,
1563 },
1564 "expected 1 argument, found 3",
1565 ),
1566 (
1567 ErrorKind::WrongArgumentCount {
1568 expected: 2,
1569 found: 0,
1570 },
1571 "expected 2 arguments, found 0",
1572 ),
1573 (
1574 ErrorKind::LinkError("undefined symbol".into()),
1575 "link error: undefined symbol",
1576 ),
1577 ];
1578 for (kind, expected) in cases {
1579 let error = CompileError::without_span(kind);
1580 assert_eq!(error.to_string(), expected);
1581 }
1582 }
1583
1584 #[test]
1585 fn test_error_kind_equality() {
1586 assert_eq!(ErrorKind::InvalidInteger, ErrorKind::InvalidInteger);
1587 assert_eq!(ErrorKind::NoMainFunction, ErrorKind::NoMainFunction);
1588 assert_ne!(ErrorKind::InvalidInteger, ErrorKind::NoMainFunction);
1589 }
1590
1591 #[test]
1592 fn test_error_implements_std_error() {
1593 fn assert_error<T: std::error::Error>() {}
1594 assert_error::<CompileError>();
1595 }
1596
1597 #[test]
1602 fn test_diagnostic_empty_by_default() {
1603 let diag = Diagnostic::new();
1604 assert!(diag.is_empty());
1605 assert!(diag.labels.is_empty());
1606 assert!(diag.notes.is_empty());
1607 assert!(diag.helps.is_empty());
1608 assert!(diag.suggestions.is_empty());
1609 }
1610
1611 #[test]
1612 fn test_diagnostic_not_empty() {
1613 let mut diag = Diagnostic::new();
1615 diag.labels.push(Label::new("test", Span::new(0, 10)));
1616 assert!(!diag.is_empty());
1617
1618 let mut diag = Diagnostic::new();
1620 diag.notes.push(Note::new("test note"));
1621 assert!(!diag.is_empty());
1622
1623 let mut diag = Diagnostic::new();
1625 diag.helps.push(Help::new("test help"));
1626 assert!(!diag.is_empty());
1627
1628 let mut diag = Diagnostic::new();
1630 diag.suggestions
1631 .push(Suggestion::new("try this", Span::new(0, 10), "replacement"));
1632 assert!(!diag.is_empty());
1633 }
1634
1635 #[test]
1636 fn test_label_creation() {
1637 let span = Span::new(10, 20);
1638 let label = Label::new("expected type here", span);
1639 assert_eq!(label.message, "expected type here");
1640 assert_eq!(label.span, span);
1641 }
1642
1643 #[test]
1644 fn test_note_display() {
1645 let note = Note::new("types must match exactly");
1646 assert_eq!(note.to_string(), "types must match exactly");
1647 }
1648
1649 #[test]
1650 fn test_help_display() {
1651 let help = Help::new("consider adding a type annotation");
1652 assert_eq!(help.to_string(), "consider adding a type annotation");
1653 }
1654
1655 #[test]
1656 fn test_suggestion_creation() {
1657 let span = Span::new(10, 20);
1658 let suggestion = Suggestion::new("try this fix", span, "new_code");
1659 assert_eq!(suggestion.message, "try this fix");
1660 assert_eq!(suggestion.span, span);
1661 assert_eq!(suggestion.replacement, "new_code");
1662 assert_eq!(suggestion.applicability, Applicability::Unspecified);
1663 }
1664
1665 #[test]
1666 fn test_suggestion_machine_applicable() {
1667 let span = Span::new(0, 5);
1668 let suggestion = Suggestion::machine_applicable("rename variable", span, "new_name");
1669 assert_eq!(suggestion.applicability, Applicability::MachineApplicable);
1670 }
1671
1672 #[test]
1673 fn test_suggestion_maybe_incorrect() {
1674 let span = Span::new(0, 5);
1675 let suggestion = Suggestion::maybe_incorrect("try adding mut", span, "mut x");
1676 assert_eq!(suggestion.applicability, Applicability::MaybeIncorrect);
1677 }
1678
1679 #[test]
1680 fn test_suggestion_with_placeholders() {
1681 let span = Span::new(0, 5);
1682 let suggestion = Suggestion::with_placeholders("add type annotation", span, ": <type>");
1683 assert_eq!(suggestion.applicability, Applicability::HasPlaceholders);
1684 }
1685
1686 #[test]
1687 fn test_suggestion_with_applicability() {
1688 let span = Span::new(0, 5);
1689 let suggestion = Suggestion::new("fix", span, "new_code")
1690 .with_applicability(Applicability::MachineApplicable);
1691 assert_eq!(suggestion.applicability, Applicability::MachineApplicable);
1692 }
1693
1694 #[test]
1695 fn test_applicability_display() {
1696 assert_eq!(
1697 Applicability::MachineApplicable.to_string(),
1698 "MachineApplicable"
1699 );
1700 assert_eq!(Applicability::MaybeIncorrect.to_string(), "MaybeIncorrect");
1701 assert_eq!(
1702 Applicability::HasPlaceholders.to_string(),
1703 "HasPlaceholders"
1704 );
1705 assert_eq!(Applicability::Unspecified.to_string(), "Unspecified");
1706 }
1707
1708 #[test]
1709 fn test_applicability_default() {
1710 assert_eq!(Applicability::default(), Applicability::Unspecified);
1711 }
1712
1713 #[test]
1714 fn test_error_with_suggestion() {
1715 let span = Span::new(10, 20);
1716 let error =
1717 CompileError::new(ErrorKind::AssignToImmutable("x".to_string()), span).with_suggestion(
1718 Suggestion::machine_applicable("add mut", Span::new(4, 5), "mut x"),
1719 );
1720
1721 let diag = error.diagnostic();
1722 assert_eq!(diag.suggestions.len(), 1);
1723 assert_eq!(diag.suggestions[0].message, "add mut");
1724 assert_eq!(diag.suggestions[0].replacement, "mut x");
1725 assert_eq!(
1726 diag.suggestions[0].applicability,
1727 Applicability::MachineApplicable
1728 );
1729 }
1730
1731 #[test]
1732 fn test_error_with_label() {
1733 let span = Span::new(10, 20);
1734 let label_span = Span::new(0, 5);
1735 let error = CompileError::new(
1736 ErrorKind::TypeMismatch {
1737 expected: "i32".to_string(),
1738 found: "bool".to_string(),
1739 },
1740 span,
1741 )
1742 .with_label("expected because of this", label_span);
1743
1744 let diag = error.diagnostic();
1745 assert_eq!(diag.labels.len(), 1);
1746 assert_eq!(diag.labels[0].message, "expected because of this");
1747 assert_eq!(diag.labels[0].span, label_span);
1748 }
1749
1750 #[test]
1751 fn test_error_with_note() {
1752 let span = Span::new(10, 20);
1753 let error = CompileError::new(
1754 ErrorKind::TypeMismatch {
1755 expected: "i32".to_string(),
1756 found: "bool".to_string(),
1757 },
1758 span,
1759 )
1760 .with_note("if and else branches must have compatible types");
1761
1762 let diag = error.diagnostic();
1763 assert_eq!(diag.notes.len(), 1);
1764 assert_eq!(
1765 diag.notes[0].to_string(),
1766 "if and else branches must have compatible types"
1767 );
1768 }
1769
1770 #[test]
1771 fn test_error_with_help() {
1772 let span = Span::new(10, 20);
1773 let error = CompileError::new(ErrorKind::AssignToImmutable("x".to_string()), span)
1774 .with_help("consider making `x` mutable: `let mut x`");
1775
1776 let diag = error.diagnostic();
1777 assert_eq!(diag.helps.len(), 1);
1778 assert_eq!(
1779 diag.helps[0].to_string(),
1780 "consider making `x` mutable: `let mut x`"
1781 );
1782 }
1783
1784 #[test]
1785 fn test_error_with_multiple_diagnostics() {
1786 let span = Span::new(10, 20);
1787 let label_span = Span::new(0, 5);
1788 let error = CompileError::new(
1789 ErrorKind::TypeMismatch {
1790 expected: "i32".to_string(),
1791 found: "bool".to_string(),
1792 },
1793 span,
1794 )
1795 .with_label("then branch is here", label_span)
1796 .with_note("if and else branches must have compatible types")
1797 .with_help("consider using a type conversion");
1798
1799 let diag = error.diagnostic();
1800 assert_eq!(diag.labels.len(), 1);
1801 assert_eq!(diag.notes.len(), 1);
1802 assert_eq!(diag.helps.len(), 1);
1803 }
1804
1805 #[test]
1806 fn test_error_diagnostic_empty_by_default() {
1807 let span = Span::new(10, 20);
1808 let error = CompileError::new(ErrorKind::InvalidInteger, span);
1809 assert!(error.diagnostic().is_empty());
1810 }
1811
1812 #[test]
1813 fn test_warning_with_help() {
1814 let span = Span::new(10, 20);
1815 let warning = CompileWarning::new(WarningKind::UnusedVariable("foo".to_string()), span)
1816 .with_help("if this is intentional, prefix it with an underscore: `_foo`");
1817
1818 let diag = warning.diagnostic();
1819 assert_eq!(diag.helps.len(), 1);
1820 assert_eq!(
1821 diag.helps[0].to_string(),
1822 "if this is intentional, prefix it with an underscore: `_foo`"
1823 );
1824 }
1825
1826 #[test]
1827 fn test_warning_with_label_and_note() {
1828 let span = Span::new(20, 25);
1829 let diverging_span = Span::new(10, 18);
1830 let warning = CompileWarning::new(WarningKind::UnreachableCode, span)
1831 .with_label(
1832 "any code following this expression is unreachable",
1833 diverging_span,
1834 )
1835 .with_note("this warning occurs because the preceding expression diverges");
1836
1837 let diag = warning.diagnostic();
1838 assert_eq!(diag.labels.len(), 1);
1839 assert_eq!(diag.labels[0].span, diverging_span);
1840 assert_eq!(diag.notes.len(), 1);
1841 }
1842
1843 #[test]
1844 fn test_warning_diagnostic_empty_by_default() {
1845 let span = Span::new(10, 20);
1846 let warning = CompileWarning::new(WarningKind::UnreachableCode, span);
1847 assert!(warning.diagnostic().is_empty());
1848 }
1849
1850 #[test]
1855 fn test_preview_feature_test_infra() {
1856 let feature: PreviewFeature = "test_infra".parse().unwrap();
1857 assert_eq!(feature, PreviewFeature::TestInfra);
1858 assert_eq!(feature.name(), "test_infra");
1859 assert_eq!(feature.adr(), "ADR-0005");
1860 }
1861
1862 #[test]
1863 fn test_preview_feature_from_str_unknown() {
1864 assert!("unknown".parse::<PreviewFeature>().is_err());
1865 assert!("".parse::<PreviewFeature>().is_err());
1866 }
1867
1868 #[test]
1869 fn test_parse_preview_feature_error_display() {
1870 let err = "bad_feature".parse::<PreviewFeature>().unwrap_err();
1871 assert_eq!(err.to_string(), "unknown preview feature 'bad_feature'");
1872 }
1873
1874 #[test]
1875 fn test_preview_feature_all_contains_test_infra() {
1876 let all = PreviewFeature::all();
1877 assert!(all.contains(&PreviewFeature::TestInfra));
1878 }
1879
1880 #[test]
1881 fn test_preview_feature_all_names() {
1882 let names = PreviewFeature::all_names();
1883 assert_eq!(names, "test_infra");
1884 }
1885
1886 #[test]
1891 fn test_option_ext_some() {
1892 let span = Span::new(10, 20);
1893 let result: CompileResult<i32> =
1894 Some(42).ok_or_compile_error(ErrorKind::InvalidInteger, span);
1895 assert!(result.is_ok());
1896 assert_eq!(result.unwrap(), 42);
1897 }
1898
1899 #[test]
1900 fn test_option_ext_none() {
1901 let span = Span::new(10, 20);
1902 let result: CompileResult<i32> = None.ok_or_compile_error(ErrorKind::InvalidInteger, span);
1903 assert!(result.is_err());
1904 let error = result.unwrap_err();
1905 assert_eq!(error.span(), Some(span));
1906 assert!(matches!(error.kind, ErrorKind::InvalidInteger));
1907 }
1908
1909 #[test]
1910 fn test_option_ext_with_complex_error() {
1911 let span = Span::new(5, 15);
1912 let result: CompileResult<String> =
1913 None.ok_or_compile_error(ErrorKind::UndefinedVariable("foo".to_string()), span);
1914 assert!(result.is_err());
1915 let error = result.unwrap_err();
1916 assert_eq!(error.to_string(), "undefined variable 'foo'");
1917 }
1918
1919 #[test]
1924 fn test_compile_errors_new_is_empty() {
1925 let errors = CompileErrors::new();
1926 assert!(errors.is_empty());
1927 assert_eq!(errors.len(), 0);
1928 }
1929
1930 #[test]
1931 fn test_compile_errors_from_error() {
1932 let error = CompileError::without_span(ErrorKind::InvalidInteger);
1933 let errors = CompileErrors::from_error(error);
1934 assert!(!errors.is_empty());
1935 assert_eq!(errors.len(), 1);
1936 }
1937
1938 #[test]
1939 fn test_compile_errors_push() {
1940 let mut errors = CompileErrors::new();
1941 errors.push(CompileError::without_span(ErrorKind::InvalidInteger));
1942 errors.push(CompileError::without_span(ErrorKind::NoMainFunction));
1943 assert_eq!(errors.len(), 2);
1944 }
1945
1946 #[test]
1947 fn test_compile_errors_extend() {
1948 let mut errors1 = CompileErrors::new();
1949 errors1.push(CompileError::without_span(ErrorKind::InvalidInteger));
1950
1951 let mut errors2 = CompileErrors::new();
1952 errors2.push(CompileError::without_span(ErrorKind::NoMainFunction));
1953 errors2.push(CompileError::without_span(ErrorKind::BreakOutsideLoop));
1954
1955 errors1.extend(errors2);
1956 assert_eq!(errors1.len(), 3);
1957 }
1958
1959 #[test]
1960 fn test_compile_errors_first() {
1961 let mut errors = CompileErrors::new();
1962 assert!(errors.first().is_none());
1963
1964 errors.push(CompileError::without_span(ErrorKind::InvalidInteger));
1965 errors.push(CompileError::without_span(ErrorKind::NoMainFunction));
1966
1967 let first = errors.first().unwrap();
1968 assert!(matches!(first.kind, ErrorKind::InvalidInteger));
1969 }
1970
1971 #[test]
1972 fn test_compile_errors_iter() {
1973 let mut errors = CompileErrors::new();
1974 errors.push(CompileError::without_span(ErrorKind::InvalidInteger));
1975 errors.push(CompileError::without_span(ErrorKind::NoMainFunction));
1976
1977 let kinds: Vec<_> = errors.iter().map(|e| &e.kind).collect();
1978 assert_eq!(kinds.len(), 2);
1979 }
1980
1981 #[test]
1982 fn test_compile_errors_into_result_empty() {
1983 let errors = CompileErrors::new();
1984 assert!(errors.into_result().is_ok());
1985 }
1986
1987 #[test]
1988 fn test_compile_errors_into_result_non_empty() {
1989 let mut errors = CompileErrors::new();
1990 errors.push(CompileError::without_span(ErrorKind::InvalidInteger));
1991 assert!(errors.into_result().is_err());
1992 }
1993
1994 #[test]
1995 fn test_compile_errors_into_result_with() {
1996 let errors = CompileErrors::new();
1997 let result = errors.into_result_with(42);
1998 assert_eq!(result.unwrap(), 42);
1999
2000 let mut errors = CompileErrors::new();
2001 errors.push(CompileError::without_span(ErrorKind::InvalidInteger));
2002 let result = errors.into_result_with(42);
2003 assert!(result.is_err());
2004 }
2005
2006 #[test]
2007 fn test_compile_errors_from_single_error() {
2008 let error = CompileError::without_span(ErrorKind::InvalidInteger);
2009 let errors: CompileErrors = error.into();
2010 assert_eq!(errors.len(), 1);
2011 }
2012
2013 #[test]
2014 fn test_compile_errors_to_single_error() {
2015 let mut errors = CompileErrors::new();
2016 errors.push(CompileError::without_span(ErrorKind::InvalidInteger));
2017 errors.push(CompileError::without_span(ErrorKind::NoMainFunction));
2018
2019 let error: CompileError = errors.into();
2020 assert!(matches!(error.kind, ErrorKind::InvalidInteger));
2022 }
2023
2024 #[test]
2028 #[cfg_attr(debug_assertions, ignore)]
2029 fn test_empty_compile_errors_to_single_error() {
2030 let empty = CompileErrors::new();
2033 let error: CompileError = empty.into();
2034
2035 match &error.kind {
2037 ErrorKind::InternalError(msg) => {
2038 assert!(msg.contains("empty error collection"));
2039 }
2040 other => panic!("expected InternalError, got {:?}", other),
2041 }
2042 }
2043
2044 #[test]
2045 fn test_compile_errors_display_empty() {
2046 let errors = CompileErrors::new();
2047 assert_eq!(errors.to_string(), "no errors");
2048 }
2049
2050 #[test]
2051 fn test_compile_errors_display_single() {
2052 let errors =
2053 CompileErrors::from_error(CompileError::without_span(ErrorKind::InvalidInteger));
2054 assert_eq!(errors.to_string(), "invalid integer literal");
2055 }
2056
2057 #[test]
2058 fn test_compile_errors_display_multiple() {
2059 let mut errors = CompileErrors::new();
2060 errors.push(CompileError::without_span(ErrorKind::InvalidInteger));
2061 errors.push(CompileError::without_span(ErrorKind::NoMainFunction));
2062 assert_eq!(
2063 errors.to_string(),
2064 "invalid integer literal (and 1 more error)"
2065 );
2066
2067 errors.push(CompileError::without_span(ErrorKind::BreakOutsideLoop));
2068 assert_eq!(
2069 errors.to_string(),
2070 "invalid integer literal (and 2 more errors)"
2071 );
2072 }
2073
2074 #[test]
2079 fn test_error_code_display() {
2080 assert_eq!(ErrorCode::TYPE_MISMATCH.to_string(), "E0206");
2081 assert_eq!(ErrorCode::UNDEFINED_VARIABLE.to_string(), "E0201");
2082 assert_eq!(ErrorCode::INTERNAL_ERROR.to_string(), "E9000");
2083 assert_eq!(ErrorCode(1).to_string(), "E0001");
2084 assert_eq!(ErrorCode(42).to_string(), "E0042");
2085 assert_eq!(ErrorCode(1234).to_string(), "E1234");
2086 }
2087
2088 #[test]
2089 fn test_error_kind_codes() {
2090 let cases: Vec<(ErrorKind, ErrorCode)> = vec![
2091 (
2093 ErrorKind::UnexpectedCharacter('@'),
2094 ErrorCode::UNEXPECTED_CHARACTER,
2095 ),
2096 (ErrorKind::InvalidInteger, ErrorCode::INVALID_INTEGER),
2097 (ErrorKind::InvalidFloat, ErrorCode::INVALID_FLOAT),
2098 (
2099 ErrorKind::InvalidStringEscape('n'),
2100 ErrorCode::INVALID_STRING_ESCAPE,
2101 ),
2102 (
2103 ErrorKind::UnterminatedString,
2104 ErrorCode::UNTERMINATED_STRING,
2105 ),
2106 (
2108 ErrorKind::UnexpectedToken {
2109 expected: "identifier".into(),
2110 found: "+".into(),
2111 },
2112 ErrorCode::UNEXPECTED_TOKEN,
2113 ),
2114 (
2115 ErrorKind::UnexpectedEof {
2116 expected: "}".into(),
2117 },
2118 ErrorCode::UNEXPECTED_EOF,
2119 ),
2120 (
2121 ErrorKind::ParseError("custom error".into()),
2122 ErrorCode::PARSE_ERROR,
2123 ),
2124 (ErrorKind::NoMainFunction, ErrorCode::NO_MAIN_FUNCTION),
2126 (
2127 ErrorKind::UndefinedVariable("x".into()),
2128 ErrorCode::UNDEFINED_VARIABLE,
2129 ),
2130 (
2131 ErrorKind::UndefinedFunction("foo".into()),
2132 ErrorCode::UNDEFINED_FUNCTION,
2133 ),
2134 (
2135 ErrorKind::TypeMismatch {
2136 expected: "i32".into(),
2137 found: "bool".into(),
2138 },
2139 ErrorCode::TYPE_MISMATCH,
2140 ),
2141 (ErrorKind::BreakOutsideLoop, ErrorCode::BREAK_OUTSIDE_LOOP),
2143 (
2144 ErrorKind::ContinueOutsideLoop,
2145 ErrorCode::CONTINUE_OUTSIDE_LOOP,
2146 ),
2147 (
2149 ErrorKind::InternalError("bug".into()),
2150 ErrorCode::INTERNAL_ERROR,
2151 ),
2152 (
2153 ErrorKind::InternalCodegenError("codegen bug".into()),
2154 ErrorCode::INTERNAL_CODEGEN_ERROR,
2155 ),
2156 ];
2157 for (kind, expected_code) in cases {
2158 assert_eq!(kind.code(), expected_code, "wrong code for: {kind}");
2159 }
2160 }
2161
2162 #[test]
2163 fn test_error_code_equality() {
2164 assert_eq!(ErrorCode::TYPE_MISMATCH, ErrorCode(206));
2165 assert_ne!(ErrorCode::TYPE_MISMATCH, ErrorCode::UNDEFINED_VARIABLE);
2166 }
2167
2168 #[test]
2169 fn test_error_code_hash() {
2170 use std::collections::HashSet;
2171 let mut set = HashSet::new();
2172 set.insert(ErrorCode::TYPE_MISMATCH);
2173 set.insert(ErrorCode::UNDEFINED_VARIABLE);
2174 assert_eq!(set.len(), 2);
2175 assert!(set.contains(&ErrorCode::TYPE_MISMATCH));
2176 }
2177
2178 #[test]
2183 fn test_error_kind_size() {
2184 let size = std::mem::size_of::<ErrorKind>();
2186
2187 assert!(
2196 size <= 64,
2197 "ErrorKind is {} bytes, exceeds 64-byte limit. \
2198 Consider boxing large variants (≥ 72 bytes / 3+ Strings). \
2199 See the boxing policy documentation above ErrorKind.",
2200 size
2201 );
2202 }
2203
2204 #[test]
2205 fn test_error_kind_variant_sizes() {
2206 use std::mem::size_of;
2207
2208 println!("String: {} bytes", size_of::<String>());
2210 println!("Vec<String>: {} bytes", size_of::<Vec<String>>());
2211 println!(
2212 "Cow<'static, str>: {} bytes",
2213 size_of::<Cow<'static, str>>()
2214 );
2215
2216 println!("TypeMismatch data: {} bytes", size_of::<(String, String)>());
2218 println!("UnknownField data: {} bytes", size_of::<(String, String)>());
2219 println!(
2220 "DuplicateField data: {} bytes",
2221 size_of::<(String, String)>()
2222 );
2223 println!(
2224 "ModuleNotFound data: {} bytes",
2225 size_of::<(String, Vec<String>)>()
2226 );
2227
2228 println!(
2230 "MissingFieldsError: {} bytes",
2231 size_of::<MissingFieldsError>()
2232 );
2233 println!(
2234 "CopyStructNonCopyFieldError: {} bytes",
2235 size_of::<CopyStructNonCopyFieldError>()
2236 );
2237 println!(
2238 "IntrinsicTypeMismatchError: {} bytes",
2239 size_of::<IntrinsicTypeMismatchError>()
2240 );
2241 println!(
2242 "FieldWrongOrderError: {} bytes",
2243 size_of::<FieldWrongOrderError>()
2244 );
2245 }
2246}