1use crate::types::WebIdlResult;
6use oak_idl::ast::{IdlItem, IdlRoot};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct TypeCompatibilityResult {
14 pub is_compatible: bool,
16 pub reason: Option<String>,
18 pub suggestions: Vec<String>,
20 pub details: CompatibilityDetails,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct CompatibilityDetails {
29 pub source_type: String,
31 pub target_type: String,
33 pub level: CompatibilityLevel,
35 pub matched_mappings: Vec<(String, String)>,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub enum CompatibilityLevel {
44 Full,
46 Partial,
48 Incompatible,
50 NeedsConversion,
52}
53
54impl Default for TypeCompatibilityResult {
55 fn default() -> Self {
56 Self {
57 is_compatible: true,
58 reason: None,
59 suggestions: Vec::new(),
60 details: CompatibilityDetails {
61 source_type: String::new(),
62 target_type: String::new(),
63 level: CompatibilityLevel::Full,
64 matched_mappings: Vec::new(),
65 },
66 }
67 }
68}
69
70impl TypeCompatibilityResult {
71 pub fn compatible(source_type: impl Into<String>, target_type: impl Into<String>) -> Self {
82 Self {
83 is_compatible: true,
84 reason: None,
85 suggestions: Vec::new(),
86 details: CompatibilityDetails {
87 source_type: source_type.into(),
88 target_type: target_type.into(),
89 level: CompatibilityLevel::Full,
90 matched_mappings: Vec::new(),
91 },
92 }
93 }
94
95 pub fn incompatible(source_type: impl Into<String>, target_type: impl Into<String>, reason: impl Into<String>) -> Self {
107 Self {
108 is_compatible: false,
109 reason: Some(reason.into()),
110 suggestions: Vec::new(),
111 details: CompatibilityDetails {
112 source_type: source_type.into(),
113 target_type: target_type.into(),
114 level: CompatibilityLevel::Incompatible,
115 matched_mappings: Vec::new(),
116 },
117 }
118 }
119
120 pub fn with_suggestion(mut self, suggestion: impl Into<String>) -> Self {
130 self.suggestions.push(suggestion.into());
131 self
132 }
133
134 pub fn with_level(mut self, level: CompatibilityLevel) -> Self {
144 self.details.level = level;
145 self
146 }
147
148 pub fn with_mapping(mut self, source: impl Into<String>, target: impl Into<String>) -> Self {
159 self.details.matched_mappings.push((source.into(), target.into()));
160 self
161 }
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
168pub struct TypeError {
169 pub message: String,
171 pub code: String,
173 pub line: Option<usize>,
175 pub column: Option<usize>,
177 pub file_path: Option<String>,
179 pub suggestions: Vec<String>,
181 pub related_types: Vec<String>,
183 pub severity: TypeErrorSeverity,
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq)]
191pub enum TypeErrorSeverity {
192 Error,
194 Warning,
196 Info,
198 Hint,
200}
201
202impl TypeError {
203 pub fn new(message: impl Into<String>, code: impl Into<String>) -> Self {
214 Self {
215 message: message.into(),
216 code: code.into(),
217 line: None,
218 column: None,
219 file_path: None,
220 suggestions: Vec::new(),
221 related_types: Vec::new(),
222 severity: TypeErrorSeverity::Error,
223 }
224 }
225
226 pub fn with_location(mut self, line: usize, column: usize) -> Self {
237 self.line = Some(line);
238 self.column = Some(column);
239 self
240 }
241
242 pub fn with_file(mut self, path: impl Into<String>) -> Self {
252 self.file_path = Some(path.into());
253 self
254 }
255
256 pub fn with_suggestion(mut self, suggestion: impl Into<String>) -> Self {
266 self.suggestions.push(suggestion.into());
267 self
268 }
269
270 pub fn with_related_type(mut self, type_name: impl Into<String>) -> Self {
280 self.related_types.push(type_name.into());
281 self
282 }
283
284 pub fn with_severity(mut self, severity: TypeErrorSeverity) -> Self {
294 self.severity = severity;
295 self
296 }
297
298 pub fn format(&self) -> String {
304 let mut result = String::new();
305
306 let severity_str = match self.severity {
307 TypeErrorSeverity::Error => "error",
308 TypeErrorSeverity::Warning => "warning",
309 TypeErrorSeverity::Info => "info",
310 TypeErrorSeverity::Hint => "hint",
311 };
312
313 result.push_str(&format!("{}[{}]: {}", severity_str, self.code, self.message));
314
315 if let Some(ref path) = self.file_path {
316 result.push_str(&format!("\n --> {}", path));
317 if let (Some(line), Some(col)) = (self.line, self.column) {
318 result.push_str(&format!(":{}:{}", line, col));
319 }
320 }
321
322 if !self.related_types.is_empty() {
323 result.push_str("\n Related types:");
324 for type_name in &self.related_types {
325 result.push_str(&format!("\n - {}", type_name));
326 }
327 }
328
329 if !self.suggestions.is_empty() {
330 result.push_str("\n Suggestions:");
331 for suggestion in &self.suggestions {
332 result.push_str(&format!("\n - {}", suggestion));
333 }
334 }
335
336 result
337 }
338}
339
340#[derive(Debug, Clone, Default)]
344pub struct TypeErrorCollector {
345 errors: Vec<TypeError>,
347 error_count: usize,
349 warning_count: usize,
351}
352
353impl TypeErrorCollector {
354 pub fn new() -> Self {
360 Self::default()
361 }
362
363 pub fn report_error(&mut self, error: TypeError) {
369 match error.severity {
370 TypeErrorSeverity::Error => self.error_count += 1,
371 TypeErrorSeverity::Warning => self.warning_count += 1,
372 _ => {}
373 }
374 self.errors.push(error);
375 }
376
377 pub fn add_error(&mut self, message: impl Into<String>, code: impl Into<String>) {
384 self.report_error(TypeError::new(message, code));
385 }
386
387 pub fn add_error_with_location(&mut self, message: impl Into<String>, code: impl Into<String>, line: usize, column: usize) {
396 self.report_error(TypeError::new(message, code).with_location(line, column));
397 }
398
399 pub fn get_errors(&self) -> &[TypeError] {
405 &self.errors
406 }
407
408 pub fn get_errors_by_severity(&self, severity: TypeErrorSeverity) -> Vec<&TypeError> {
418 self.errors.iter().filter(|e| e.severity == severity).collect()
419 }
420
421 pub fn error_count(&self) -> usize {
427 self.error_count
428 }
429
430 pub fn warning_count(&self) -> usize {
436 self.warning_count
437 }
438
439 pub fn has_errors(&self) -> bool {
445 self.error_count > 0
446 }
447
448 pub fn has_warnings(&self) -> bool {
454 self.warning_count > 0
455 }
456
457 pub fn clear(&mut self) {
459 self.errors.clear();
460 self.error_count = 0;
461 self.warning_count = 0;
462 }
463
464 pub fn format_errors(&self) -> String {
470 if self.errors.is_empty() {
471 return String::from("No errors found.");
472 }
473
474 let mut result = String::new();
475 result.push_str(&format!("Found {} error(s) and {} warning(s):\n\n", self.error_count, self.warning_count));
476
477 for (i, error) in self.errors.iter().enumerate() {
478 if i > 0 {
479 result.push_str("\n\n");
480 }
481 result.push_str(&error.format());
482 }
483
484 result
485 }
486
487 pub fn merge(&mut self, other: TypeErrorCollector) {
493 self.error_count += other.error_count;
494 self.warning_count += other.warning_count;
495 self.errors.extend(other.errors);
496 }
497}
498
499#[derive(Debug, Clone, PartialEq, Eq)]
503pub struct InferredType {
504 pub type_name: String,
506 pub confidence: u8,
508 pub source: TypeInferenceSource,
510 pub related_info: Vec<String>,
512}
513
514#[derive(Debug, Clone, Copy, PartialEq, Eq)]
518pub enum TypeInferenceSource {
519 Literal,
521 Expression,
523 Context,
525 DefaultValue,
527 Usage,
529 Annotation,
531}
532
533impl InferredType {
534 pub fn new(type_name: impl Into<String>) -> Self {
544 Self { type_name: type_name.into(), confidence: 100, source: TypeInferenceSource::Literal, related_info: Vec::new() }
545 }
546
547 pub fn with_confidence(mut self, confidence: u8) -> Self {
557 self.confidence = confidence.min(100);
558 self
559 }
560
561 pub fn with_source(mut self, source: TypeInferenceSource) -> Self {
571 self.source = source;
572 self
573 }
574
575 pub fn with_related_info(mut self, info: impl Into<String>) -> Self {
585 self.related_info.push(info.into());
586 self
587 }
588}
589
590#[derive(Debug, Clone)]
594pub struct TypeInference {
595 type_aliases: HashMap<String, String>,
597 interfaces: HashMap<String, Vec<InterfaceProperty>>,
599}
600
601#[derive(Debug, Clone, PartialEq, Eq)]
605pub struct InterfaceProperty {
606 pub name: String,
608 pub type_name: String,
610 pub optional: bool,
612 pub readonly: bool,
614}
615
616impl TypeInference {
617 pub fn new() -> Self {
623 Self { type_aliases: HashMap::new(), interfaces: HashMap::new() }
624 }
625
626 pub fn from_webidl_result(result: &WebIdlResult) -> Self {
636 let mut inference = Self::new();
637 inference.load_webidl_types(result);
638 inference
639 }
640
641 pub fn load_webidl_types(&mut self, result: &WebIdlResult) {
647 for item in &result.root.items {
648 match item {
649 IdlItem::Interface(interface) => {
650 let properties: Vec<InterfaceProperty> = interface
651 .members
652 .iter()
653 .filter_map(|member| match member {
654 oak_idl::ast::IdlMember::Attribute(attr) => Some(InterfaceProperty {
655 name: attr.name.clone(),
656 type_name: attr.type_name.clone(),
657 optional: false,
658 readonly: attr.readonly,
659 }),
660 _ => None,
661 })
662 .collect();
663 self.interfaces.insert(interface.name.clone(), properties);
664 }
665 IdlItem::Struct(struct_) => {
666 let properties: Vec<InterfaceProperty> = struct_
667 .fields
668 .iter()
669 .map(|field| InterfaceProperty {
670 name: field.name.clone(),
671 type_name: field.type_name.clone(),
672 optional: field.type_name.ends_with('?'),
673 readonly: false,
674 })
675 .collect();
676 self.interfaces.insert(struct_.name.clone(), properties);
677 }
678 IdlItem::Typedef(typedef) => {
679 self.type_aliases.insert(typedef.name.clone(), typedef.type_name.clone());
680 }
681 _ => {}
682 }
683 }
684 }
685
686 pub fn infer_type(&self, value: &str) -> InferredType {
696 let trimmed = value.trim();
697
698 if trimmed.is_empty() {
699 return InferredType::new("undefined").with_source(TypeInferenceSource::Literal).with_confidence(100);
700 }
701
702 if trimmed == "null" {
703 return InferredType::new("null").with_source(TypeInferenceSource::Literal).with_confidence(100);
704 }
705
706 if trimmed == "undefined" {
707 return InferredType::new("undefined").with_source(TypeInferenceSource::Literal).with_confidence(100);
708 }
709
710 if trimmed == "true" || trimmed == "false" {
711 return InferredType::new("boolean").with_source(TypeInferenceSource::Literal).with_confidence(100);
712 }
713
714 if Self::is_number_literal(trimmed) {
715 return InferredType::new("number").with_source(TypeInferenceSource::Literal).with_confidence(100);
716 }
717
718 if Self::is_string_literal(trimmed) {
719 return InferredType::new("string").with_source(TypeInferenceSource::Literal).with_confidence(100);
720 }
721
722 if trimmed.starts_with('[') && trimmed.ends_with(']') {
723 return self.infer_array_type(trimmed);
724 }
725
726 if trimmed.starts_with('{') && trimmed.ends_with('}') {
727 return self.infer_object_type(trimmed);
728 }
729
730 if trimmed.starts_with("new ") {
731 return self.infer_constructor_type(trimmed);
732 }
733
734 if trimmed.contains('(') && trimmed.contains(')') {
735 return self.infer_function_expression_type(trimmed);
736 }
737
738 InferredType::new("any").with_source(TypeInferenceSource::Context).with_confidence(50)
739 }
740
741 fn is_number_literal(s: &str) -> bool {
751 if s.is_empty() {
752 return false;
753 }
754
755 let s = s.trim();
756
757 if s.starts_with("0x") || s.starts_with("0X") {
758 return s[2..].chars().all(|c| c.is_ascii_hexdigit());
759 }
760
761 if s.starts_with("0b") || s.starts_with("0B") {
762 return s[2..].chars().all(|c| c == '0' || c == '1');
763 }
764
765 if s.starts_with("0o") || s.starts_with("0O") {
766 return s[2..].chars().all(|c| c >= '0' && c <= '7');
767 }
768
769 let mut has_dot = false;
770 let mut has_exp = false;
771 let chars: Vec<char> = s.chars().collect();
772
773 for (i, &c) in chars.iter().enumerate() {
774 if i == 0 && (c == '+' || c == '-') {
775 continue;
776 }
777
778 if c == '.' && !has_dot && !has_exp {
779 has_dot = true;
780 continue;
781 }
782
783 if (c == 'e' || c == 'E') && !has_exp {
784 has_exp = true;
785 continue;
786 }
787
788 if (c == '+' || c == '-') && i > 0 && (chars[i - 1] == 'e' || chars[i - 1] == 'E') {
789 continue;
790 }
791
792 if !c.is_ascii_digit() {
793 return false;
794 }
795 }
796
797 !s.is_empty() && s != "." && s != "+" && s != "-"
798 }
799
800 fn is_string_literal(s: &str) -> bool {
810 if s.len() < 2 {
811 return false;
812 }
813
814 let first = s.chars().next().unwrap();
815 let last = s.chars().last().unwrap();
816
817 (first == '"' && last == '"') || (first == '\'' && last == '\'') || (first == '`' && last == '`')
818 }
819
820 fn infer_array_type(&self, value: &str) -> InferredType {
830 let inner = &value[1..value.len() - 1].trim();
831
832 if inner.is_empty() {
833 return InferredType::new("any[]")
834 .with_source(TypeInferenceSource::Literal)
835 .with_confidence(100)
836 .with_related_info("Empty array");
837 }
838
839 let elements: Vec<&str> = self.split_array_elements(inner);
840 let mut element_types: Vec<String> = Vec::new();
841
842 for element in elements {
843 let inferred = self.infer_type(element);
844 element_types.push(inferred.type_name);
845 }
846
847 if element_types.is_empty() {
848 return InferredType::new("any[]").with_source(TypeInferenceSource::Literal).with_confidence(100);
849 }
850
851 let unique_types: Vec<&String> = element_types.iter().collect::<std::collections::HashSet<_>>().into_iter().collect();
852
853 if unique_types.len() == 1 {
854 InferredType::new(format!("{}[]", unique_types[0])).with_source(TypeInferenceSource::Literal).with_confidence(95)
855 }
856 else {
857 let union_type = element_types.join(" | ");
858 InferredType::new(format!("({})[]", union_type)).with_source(TypeInferenceSource::Literal).with_confidence(90)
859 }
860 }
861
862 fn split_array_elements<'a>(&self, s: &'a str) -> Vec<&'a str> {
872 let mut elements = Vec::new();
873 let mut depth = 0;
874 let mut start = 0;
875 let mut in_string = false;
876 let mut string_char = '\0';
877
878 for (i, c) in s.char_indices() {
879 if in_string {
880 if c == string_char && s.as_bytes().get(i.wrapping_sub(1)).copied() != Some(b'\\') {
881 in_string = false;
882 }
883 }
884 else {
885 match c {
886 '"' | '\'' | '`' => {
887 in_string = true;
888 string_char = c;
889 }
890 '[' | '{' | '(' => depth += 1,
891 ']' | '}' | ')' => depth -= 1,
892 ',' if depth == 0 => {
893 elements.push(s[start..i].trim());
894 start = i + 1;
895 }
896 _ => {}
897 }
898 }
899 }
900
901 if start < s.len() {
902 elements.push(s[start..].trim());
903 }
904
905 elements
906 }
907
908 fn infer_object_type(&self, value: &str) -> InferredType {
918 let inner = &value[1..value.len() - 1].trim();
919
920 if inner.is_empty() {
921 return InferredType::new("object")
922 .with_source(TypeInferenceSource::Literal)
923 .with_confidence(100)
924 .with_related_info("Empty object");
925 }
926
927 let properties = self.parse_object_properties(inner);
928 let mut type_parts: Vec<String> = Vec::new();
929
930 for (key, prop_value) in &properties {
931 let inferred = self.infer_type(prop_value);
932 type_parts.push(format!("{}: {}", key, inferred.type_name));
933 }
934
935 if type_parts.is_empty() {
936 InferredType::new("object").with_source(TypeInferenceSource::Literal).with_confidence(100)
937 }
938 else {
939 InferredType::new(format!("{{ {} }}", type_parts.join("; ")))
940 .with_source(TypeInferenceSource::Literal)
941 .with_confidence(90)
942 }
943 }
944
945 fn parse_object_properties(&self, s: &str) -> Vec<(String, String)> {
955 let mut properties = Vec::new();
956 let mut depth = 0;
957 let mut start = 0;
958 let mut in_string = false;
959 let mut string_char = '\0';
960
961 for (i, c) in s.char_indices() {
962 if in_string {
963 if c == string_char && s.as_bytes().get(i.wrapping_sub(1)).copied() != Some(b'\\') {
964 in_string = false;
965 }
966 }
967 else {
968 match c {
969 '"' | '\'' | '`' => {
970 in_string = true;
971 string_char = c;
972 }
973 '[' | '{' | '(' => depth += 1,
974 ']' | '}' | ')' => depth -= 1,
975 ',' if depth == 0 => {
976 if let Some(prop) = self.parse_single_property(s[start..i].trim()) {
977 properties.push(prop);
978 }
979 start = i + 1;
980 }
981 _ => {}
982 }
983 }
984 }
985
986 if start < s.len() {
987 if let Some(prop) = self.parse_single_property(s[start..].trim()) {
988 properties.push(prop);
989 }
990 }
991
992 properties
993 }
994
995 fn parse_single_property(&self, s: &str) -> Option<(String, String)> {
1005 let colon_pos = s.find(':')?;
1006 let key = s[..colon_pos].trim().trim_matches('"').trim_matches('\'').to_string();
1007 let value = s[colon_pos + 1..].trim().to_string();
1008 Some((key, value))
1009 }
1010
1011 fn infer_constructor_type(&self, value: &str) -> InferredType {
1021 let without_new = value[4..].trim();
1022
1023 let type_name = if without_new.contains('(') {
1024 &without_new[..without_new.find('(').unwrap_or(without_new.len())]
1025 }
1026 else {
1027 without_new
1028 };
1029
1030 InferredType::new(type_name.trim()).with_source(TypeInferenceSource::Expression).with_confidence(95)
1031 }
1032
1033 fn infer_function_expression_type(&self, value: &str) -> InferredType {
1043 if value.starts_with("function") || value.starts_with("async function") {
1044 return InferredType::new("Function").with_source(TypeInferenceSource::Expression).with_confidence(90);
1045 }
1046
1047 if value.starts_with('(') || value.starts_with("async (") {
1048 return InferredType::new("(...args: any[]) => any")
1049 .with_source(TypeInferenceSource::Expression)
1050 .with_confidence(85);
1051 }
1052
1053 InferredType::new("any").with_source(TypeInferenceSource::Expression).with_confidence(50)
1054 }
1055
1056 pub fn infer_expression_type(&self, expression: &str) -> InferredType {
1066 let trimmed = expression.trim();
1067
1068 if trimmed.is_empty() {
1069 return InferredType::new("undefined").with_source(TypeInferenceSource::Expression).with_confidence(100);
1070 }
1071
1072 if let Some(idx) = trimmed.find(" + ") {
1073 let left = &trimmed[..idx];
1074 let right = &trimmed[idx + 3..];
1075 return self.infer_binary_expression_type(left, right, "+");
1076 }
1077
1078 if let Some(idx) = trimmed.find(" - ") {
1079 let left = &trimmed[..idx];
1080 let right = &trimmed[idx + 3..];
1081 return self.infer_binary_expression_type(left, right, "-");
1082 }
1083
1084 if let Some(idx) = trimmed.find(" * ") {
1085 let left = &trimmed[..idx];
1086 let right = &trimmed[idx + 3..];
1087 return self.infer_binary_expression_type(left, right, "*");
1088 }
1089
1090 if let Some(idx) = trimmed.find(" / ") {
1091 let left = &trimmed[..idx];
1092 let right = &trimmed[idx + 3..];
1093 return self.infer_binary_expression_type(left, right, "/");
1094 }
1095
1096 if let Some(idx) = trimmed.find(" && ") {
1097 let left = &trimmed[..idx];
1098 let right = &trimmed[idx + 4..];
1099 return self.infer_logical_expression_type(left, right, "&&");
1100 }
1101
1102 if let Some(idx) = trimmed.find(" || ") {
1103 let left = &trimmed[..idx];
1104 let right = &trimmed[idx + 4..];
1105 return self.infer_logical_expression_type(left, right, "||");
1106 }
1107
1108 if trimmed.starts_with('!') {
1109 return InferredType::new("boolean").with_source(TypeInferenceSource::Expression).with_confidence(100);
1110 }
1111
1112 if trimmed.starts_with("typeof ") {
1113 return InferredType::new("string").with_source(TypeInferenceSource::Expression).with_confidence(100);
1114 }
1115
1116 if trimmed.starts_with("instanceof ") {
1117 return InferredType::new("boolean").with_source(TypeInferenceSource::Expression).with_confidence(100);
1118 }
1119
1120 if trimmed.contains('?') && trimmed.contains(':') {
1121 return self.infer_ternary_expression_type(trimmed);
1122 }
1123
1124 if trimmed.contains('.') && !Self::is_number_literal(trimmed) {
1125 return self.infer_property_access_type(trimmed);
1126 }
1127
1128 if trimmed.ends_with(']') && trimmed.contains('[') {
1129 return self.infer_index_access_type(trimmed);
1130 }
1131
1132 self.infer_type(trimmed)
1133 }
1134
1135 fn infer_binary_expression_type(&self, left: &str, right: &str, operator: &str) -> InferredType {
1147 let left_type = self.infer_type(left.trim());
1148 let right_type = self.infer_type(right.trim());
1149
1150 match operator {
1151 "+" => {
1152 if left_type.type_name == "string" || right_type.type_name == "string" {
1153 InferredType::new("string").with_source(TypeInferenceSource::Expression).with_confidence(95)
1154 }
1155 else {
1156 InferredType::new("number").with_source(TypeInferenceSource::Expression).with_confidence(90)
1157 }
1158 }
1159 "-" | "*" | "/" | "%" => {
1160 InferredType::new("number").with_source(TypeInferenceSource::Expression).with_confidence(95)
1161 }
1162 _ => self.infer_type("any"),
1163 }
1164 }
1165
1166 fn infer_logical_expression_type(&self, left: &str, right: &str, operator: &str) -> InferredType {
1178 let left_type = self.infer_type(left.trim());
1179 let right_type = self.infer_type(right.trim());
1180
1181 match operator {
1182 "&&" => InferredType::new(right_type.type_name).with_source(TypeInferenceSource::Expression).with_confidence(80),
1183 "||" => InferredType::new(format!("{} | {}", left_type.type_name, right_type.type_name))
1184 .with_source(TypeInferenceSource::Expression)
1185 .with_confidence(80),
1186 _ => self.infer_type("any"),
1187 }
1188 }
1189
1190 fn infer_ternary_expression_type(&self, expression: &str) -> InferredType {
1200 let depth = Self::find_ternary_colon(expression);
1201 if let Some(colon_pos) = depth {
1202 let question_pos = expression.find('?').unwrap();
1203 let true_branch = expression[question_pos + 1..colon_pos].trim();
1204 let false_branch = expression[colon_pos + 1..].trim();
1205
1206 let true_type = self.infer_type(true_branch);
1207 let false_type = self.infer_type(false_branch);
1208
1209 if true_type.type_name == false_type.type_name {
1210 InferredType::new(true_type.type_name).with_source(TypeInferenceSource::Expression).with_confidence(95)
1211 }
1212 else {
1213 InferredType::new(format!("{} | {}", true_type.type_name, false_type.type_name))
1214 .with_source(TypeInferenceSource::Expression)
1215 .with_confidence(90)
1216 }
1217 }
1218 else {
1219 InferredType::new("any").with_source(TypeInferenceSource::Expression).with_confidence(50)
1220 }
1221 }
1222
1223 fn find_ternary_colon(expression: &str) -> Option<usize> {
1233 let mut depth = 0;
1234 let mut after_question = false;
1235
1236 for (i, c) in expression.char_indices() {
1237 match c {
1238 '?' => {
1239 depth += 1;
1240 after_question = true;
1241 }
1242 ':' if after_question && depth == 1 => {
1243 return Some(i);
1244 }
1245 ':' if depth > 1 => {
1246 depth -= 1;
1247 }
1248 _ => {}
1249 }
1250 }
1251
1252 None
1253 }
1254
1255 fn infer_property_access_type(&self, expression: &str) -> InferredType {
1265 let parts: Vec<&str> = expression.split('.').collect();
1266 if parts.len() < 2 {
1267 return InferredType::new("any").with_source(TypeInferenceSource::Expression).with_confidence(50);
1268 }
1269
1270 let object_name = parts[0].trim();
1271 let property_name = parts[1].trim();
1272
1273 if let Some(properties) = self.interfaces.get(object_name) {
1274 for prop in properties {
1275 if prop.name == property_name {
1276 return InferredType::new(&prop.type_name)
1277 .with_source(TypeInferenceSource::Expression)
1278 .with_confidence(95)
1279 .with_related_info(format!("Property of {}", object_name));
1280 }
1281 }
1282 }
1283
1284 InferredType::new("any").with_source(TypeInferenceSource::Expression).with_confidence(60)
1285 }
1286
1287 fn infer_index_access_type(&self, expression: &str) -> InferredType {
1297 let Some(open_bracket) = expression.find('[') else { return InferredType::new("any") };
1298 let Some(close_bracket) = expression.rfind(']') else { return InferredType::new("any") };
1299
1300 let object_name = &expression[..open_bracket];
1301 let index_str = &expression[open_bracket + 1..close_bracket];
1302
1303 let object_type = self.infer_type(object_name.trim());
1304
1305 if object_type.type_name.ends_with("[]") {
1306 let element_type = &object_type.type_name[..object_type.type_name.len() - 2];
1307 return InferredType::new(element_type).with_source(TypeInferenceSource::Expression).with_confidence(90);
1308 }
1309
1310 if object_type.type_name.starts_with("Array<") && object_type.type_name.ends_with('>') {
1311 let element_type = &object_type.type_name[6..object_type.type_name.len() - 1];
1312 return InferredType::new(element_type).with_source(TypeInferenceSource::Expression).with_confidence(90);
1313 }
1314
1315 if Self::is_number_literal(index_str.trim()) {
1316 return InferredType::new("any")
1317 .with_source(TypeInferenceSource::Expression)
1318 .with_confidence(70)
1319 .with_related_info("Array element access");
1320 }
1321
1322 InferredType::new("any").with_source(TypeInferenceSource::Expression).with_confidence(60)
1323 }
1324
1325 pub fn add_type_alias(&mut self, name: impl Into<String>, target: impl Into<String>) {
1332 self.type_aliases.insert(name.into(), target.into());
1333 }
1334
1335 pub fn add_interface(&mut self, name: impl Into<String>, properties: Vec<InterfaceProperty>) {
1342 self.interfaces.insert(name.into(), properties);
1343 }
1344}
1345
1346impl Default for TypeInference {
1347 fn default() -> Self {
1348 Self::new()
1349 }
1350}
1351
1352pub struct WebIdlTypeChecker {
1356 pub webidl_result: WebIdlResult,
1358 pub error_collector: TypeErrorCollector,
1360 pub type_inference: TypeInference,
1362}
1363
1364impl WebIdlTypeChecker {
1365 pub fn new(webidl_result: WebIdlResult) -> Self {
1375 let type_inference = TypeInference::from_webidl_result(&webidl_result);
1376 Self { webidl_result, error_collector: TypeErrorCollector::new(), type_inference }
1377 }
1378
1379 pub fn check_type_compatibility_detailed(&self, ts_type: &str, webidl_type: &str) -> TypeCompatibilityResult {
1390 let basic_map = self.get_basic_type_mappings();
1391
1392 for (ts, webidl) in &basic_map {
1393 if ts_type == *ts && webidl_type == *webidl {
1394 return TypeCompatibilityResult::compatible(ts_type, webidl_type).with_mapping(ts_type, webidl_type);
1395 }
1396 }
1397
1398 if let result @ TypeCompatibilityResult { is_compatible: true, .. } =
1399 self.check_function_type_compatibility(ts_type, webidl_type)
1400 {
1401 return result;
1402 }
1403
1404 if let result @ TypeCompatibilityResult { is_compatible: true, .. } =
1405 self.check_object_type_compatibility(ts_type, webidl_type)
1406 {
1407 return result;
1408 }
1409
1410 if let result @ TypeCompatibilityResult { is_compatible: true, .. } =
1411 self.check_array_type_compatibility(ts_type, webidl_type)
1412 {
1413 return result;
1414 }
1415
1416 if let result @ TypeCompatibilityResult { is_compatible: true, .. } =
1417 self.check_complex_type_compatibility_detailed(ts_type, webidl_type)
1418 {
1419 return result;
1420 }
1421
1422 if let result @ TypeCompatibilityResult { is_compatible: true, .. } =
1423 self.check_user_defined_types_detailed(ts_type, webidl_type)
1424 {
1425 return result;
1426 }
1427
1428 TypeCompatibilityResult::incompatible(
1429 ts_type,
1430 webidl_type,
1431 format!("Type '{}' is not compatible with WebIDL type '{}'", ts_type, webidl_type),
1432 )
1433 .with_suggestion(format!("Consider using a compatible type for '{}'", webidl_type))
1434 }
1435
1436 fn get_basic_type_mappings(&self) -> Vec<(&'static str, &'static str)> {
1442 vec![
1443 ("string", "DOMString"),
1444 ("string", "ByteString"),
1445 ("string", "USVString"),
1446 ("string", "string"),
1447 ("number", "byte"),
1448 ("number", "octet"),
1449 ("number", "short"),
1450 ("number", "unsigned short"),
1451 ("number", "long"),
1452 ("number", "unsigned long"),
1453 ("number", "long long"),
1454 ("number", "unsigned long long"),
1455 ("number", "float"),
1456 ("number", "unrestricted float"),
1457 ("number", "double"),
1458 ("number", "unrestricted double"),
1459 ("number", "integer"),
1460 ("number", "unsigned integer"),
1461 ("boolean", "boolean"),
1462 ("any", "any"),
1463 ("undefined", "undefined"),
1464 ("null", "null"),
1465 ("void", "void"),
1466 ]
1467 }
1468
1469 pub fn check_function_type_compatibility(&self, ts_type: &str, webidl_type: &str) -> TypeCompatibilityResult {
1480 let ts_params = self.parse_function_params(ts_type);
1481 let webidl_params = self.parse_function_params(webidl_type);
1482
1483 if ts_params.is_none() && webidl_params.is_none() {
1484 return TypeCompatibilityResult::incompatible(ts_type, webidl_type, "Not function types");
1485 }
1486
1487 let ts_params = ts_params.unwrap_or_default();
1488 let webidl_params = webidl_params.unwrap_or_default();
1489
1490 let ts_return = self.extract_return_type(ts_type);
1491 let webidl_return = self.extract_return_type(webidl_type);
1492
1493 if ts_params.len() != webidl_params.len() {
1494 return TypeCompatibilityResult::incompatible(
1495 ts_type,
1496 webidl_type,
1497 format!("Parameter count mismatch: {} vs {}", ts_params.len(), webidl_params.len()),
1498 )
1499 .with_suggestion("Ensure function signatures have the same number of parameters");
1500 }
1501
1502 let mut all_compatible = true;
1503 let mut details = TypeCompatibilityResult::compatible(ts_type, webidl_type);
1504
1505 for (i, (ts_param, webidl_param)) in ts_params.iter().zip(webidl_params.iter()).enumerate() {
1506 let param_result = self.check_type_compatibility(ts_param, webidl_param);
1507 if !param_result {
1508 all_compatible = false;
1509 details = details.with_suggestion(format!(
1510 "Parameter {} type '{}' should be compatible with '{}'",
1511 i + 1,
1512 ts_param,
1513 webidl_param
1514 ));
1515 }
1516 }
1517
1518 if let (Some(ts_ret), Some(webidl_ret)) = (&ts_return, &webidl_return) {
1519 if !self.check_type_compatibility(ts_ret, webidl_ret) {
1520 all_compatible = false;
1521 details =
1522 details.with_suggestion(format!("Return type '{}' should be compatible with '{}'", ts_ret, webidl_ret));
1523 }
1524 }
1525
1526 if all_compatible {
1527 details
1528 }
1529 else {
1530 TypeCompatibilityResult::incompatible(ts_type, webidl_type, "Function signature mismatch")
1531 .with_level(CompatibilityLevel::Partial)
1532 }
1533 }
1534
1535 fn parse_function_params(&self, func_type: &str) -> Option<Vec<String>> {
1545 let start = func_type.find('(')?;
1546 let end = func_type.rfind(')')?;
1547
1548 if start >= end {
1549 return Some(Vec::new());
1550 }
1551
1552 let params_str = &func_type[start + 1..end];
1553 if params_str.trim().is_empty() {
1554 return Some(Vec::new());
1555 }
1556
1557 let params: Vec<String> = params_str
1558 .split(',')
1559 .filter_map(|p| {
1560 let p = p.trim();
1561 if p.is_empty() {
1562 return None;
1563 }
1564
1565 if p.contains(':') {
1566 let colon_pos = p.find(':')?;
1567 Some(p[colon_pos + 1..].trim().to_string())
1568 }
1569 else {
1570 Some(p.to_string())
1571 }
1572 })
1573 .collect();
1574
1575 Some(params)
1576 }
1577
1578 fn extract_return_type(&self, func_type: &str) -> Option<String> {
1588 let arrow_pos = func_type.find("=>")?;
1589 let return_type = func_type[arrow_pos + 2..].trim();
1590
1591 if return_type.is_empty() { Some("void".to_string()) } else { Some(return_type.to_string()) }
1592 }
1593
1594 pub fn check_object_type_compatibility(&self, ts_type: &str, webidl_type: &str) -> TypeCompatibilityResult {
1605 let ts_props = self.parse_object_properties_for_compatibility(ts_type);
1606 let webidl_props = self.get_webidl_dictionary_properties(webidl_type);
1607
1608 if ts_props.is_empty() && webidl_props.is_empty() {
1609 return TypeCompatibilityResult::incompatible(ts_type, webidl_type, "Not object types");
1610 }
1611
1612 let mut missing_props: Vec<String> = Vec::new();
1613 let mut incompatible_props: Vec<(String, String, String)> = Vec::new();
1614 let mut matched_props: Vec<(String, String)> = Vec::new();
1615
1616 for (prop_name, prop_type) in &webidl_props {
1617 if let Some(ts_prop_type) = ts_props.get(prop_name) {
1618 if self.check_type_compatibility(ts_prop_type, prop_type) {
1619 matched_props.push((prop_name.clone(), ts_prop_type.clone()));
1620 }
1621 else {
1622 incompatible_props.push((prop_name.clone(), ts_prop_type.clone(), prop_type.clone()));
1623 }
1624 }
1625 else {
1626 missing_props.push(prop_name.clone());
1627 }
1628 }
1629
1630 if missing_props.is_empty() && incompatible_props.is_empty() {
1631 let mut result = TypeCompatibilityResult::compatible(ts_type, webidl_type);
1632 for (name, ts_type) in matched_props {
1633 result = result.with_mapping(name, ts_type);
1634 }
1635 result
1636 }
1637 else {
1638 let mut reason = String::new();
1639 if !missing_props.is_empty() {
1640 reason.push_str(&format!("Missing properties: {} ", missing_props.join(", ")));
1641 }
1642 if !incompatible_props.is_empty() {
1643 reason.push_str("Incompatible property types: ");
1644 for (name, ts, webidl) in &incompatible_props {
1645 reason.push_str(&format!("{} ({} vs {}) ", name, ts, webidl));
1646 }
1647 }
1648
1649 TypeCompatibilityResult::incompatible(ts_type, webidl_type, reason.trim()).with_level(CompatibilityLevel::Partial)
1650 }
1651 }
1652
1653 fn parse_object_properties_for_compatibility(&self, obj_type: &str) -> HashMap<String, String> {
1663 let mut props = HashMap::new();
1664
1665 if !obj_type.starts_with('{') || !obj_type.ends_with('}') {
1666 return props;
1667 }
1668
1669 let inner = &obj_type[1..obj_type.len() - 1];
1670 let parts: Vec<&str> = inner.split(';').chain(inner.split(',')).collect();
1671
1672 for part in parts {
1673 let part = part.trim();
1674 if part.is_empty() {
1675 continue;
1676 }
1677
1678 if let Some(colon_pos) = part.find(':') {
1679 let name = part[..colon_pos].trim().to_string();
1680 let type_name = part[colon_pos + 1..].trim().to_string();
1681
1682 let name = name.trim_end_matches('?').trim().to_string();
1683 props.insert(name, type_name);
1684 }
1685 }
1686
1687 props
1688 }
1689
1690 fn get_webidl_dictionary_properties(&self, dict_name: &str) -> HashMap<String, String> {
1700 let mut props = HashMap::new();
1701
1702 for item in &self.webidl_result.root.items {
1703 if let IdlItem::Struct(struct_) = item {
1704 if struct_.name == dict_name {
1705 for field in &struct_.fields {
1706 props.insert(field.name.clone(), field.type_name.clone());
1707 }
1708 break;
1709 }
1710 }
1711
1712 if let IdlItem::Interface(interface) = item {
1713 if interface.name == dict_name {
1714 for member in &interface.members {
1715 if let oak_idl::ast::IdlMember::Attribute(attr) = member {
1716 props.insert(attr.name.clone(), attr.type_name.clone());
1717 }
1718 }
1719 break;
1720 }
1721 }
1722 }
1723
1724 props
1725 }
1726
1727 pub fn check_array_type_compatibility(&self, ts_type: &str, webidl_type: &str) -> TypeCompatibilityResult {
1738 let ts_element = self.extract_array_element_type(ts_type);
1739 let webidl_element = self.extract_sequence_element_type(webidl_type);
1740
1741 match (ts_element, webidl_element) {
1742 (Some(ts_elem), Some(webidl_elem)) => {
1743 if self.check_type_compatibility(&ts_elem, &webidl_elem) {
1744 TypeCompatibilityResult::compatible(ts_type, webidl_type).with_mapping(&ts_elem, &webidl_elem)
1745 }
1746 else {
1747 TypeCompatibilityResult::incompatible(
1748 ts_type,
1749 webidl_type,
1750 format!("Array element type '{}' is not compatible with '{}'", ts_elem, webidl_elem),
1751 )
1752 .with_suggestion(format!("Use array with element type compatible with '{}'", webidl_elem))
1753 }
1754 }
1755 _ => TypeCompatibilityResult::incompatible(ts_type, webidl_type, "Not array types"),
1756 }
1757 }
1758
1759 fn extract_array_element_type(&self, array_type: &str) -> Option<String> {
1769 if array_type.ends_with("[]") {
1770 return Some(array_type[..array_type.len() - 2].to_string());
1771 }
1772
1773 if array_type.starts_with("Array<") && array_type.ends_with('>') {
1774 return Some(array_type[6..array_type.len() - 1].to_string());
1775 }
1776
1777 if array_type.starts_with("ReadonlyArray<") && array_type.ends_with('>') {
1778 return Some(array_type[14..array_type.len() - 1].to_string());
1779 }
1780
1781 None
1782 }
1783
1784 fn extract_sequence_element_type(&self, sequence_type: &str) -> Option<String> {
1794 if sequence_type.starts_with("sequence<") && sequence_type.ends_with('>') {
1795 return Some(sequence_type[9..sequence_type.len() - 1].to_string());
1796 }
1797
1798 if sequence_type.starts_with("FrozenArray<") && sequence_type.ends_with('>') {
1799 return Some(sequence_type[12..sequence_type.len() - 1].to_string());
1800 }
1801
1802 if sequence_type.ends_with("[]") {
1803 return Some(sequence_type[..sequence_type.len() - 2].to_string());
1804 }
1805
1806 None
1807 }
1808
1809 fn check_complex_type_compatibility_detailed(&self, ts_type: &str, webidl_type: &str) -> TypeCompatibilityResult {
1820 if ts_type.starts_with("Array<") && webidl_type.starts_with("sequence<") {
1821 let ts_inner = &ts_type[6..ts_type.len() - 1];
1822 let webidl_inner = &webidl_type[9..webidl_type.len() - 1];
1823
1824 if self.check_type_compatibility(ts_inner, webidl_inner) {
1825 return TypeCompatibilityResult::compatible(ts_type, webidl_type).with_mapping(ts_inner, webidl_inner);
1826 }
1827 }
1828
1829 if ts_type.starts_with("Promise<") && webidl_type.starts_with("Promise<") {
1830 let ts_inner = &ts_type[8..ts_type.len() - 1];
1831 let webidl_inner = &webidl_type[8..webidl_type.len() - 1];
1832
1833 if self.check_type_compatibility(ts_inner, webidl_inner) {
1834 return TypeCompatibilityResult::compatible(ts_type, webidl_type).with_mapping(ts_inner, webidl_inner);
1835 }
1836 }
1837
1838 if ts_type.contains('|') && webidl_type.contains(" or ") {
1839 return self.check_union_type_compatibility(ts_type, webidl_type);
1840 }
1841
1842 if ts_type.starts_with('{') && ts_type.contains("[key:") && webidl_type.starts_with("record<") {
1843 return TypeCompatibilityResult::compatible(ts_type, webidl_type).with_level(CompatibilityLevel::NeedsConversion);
1844 }
1845
1846 TypeCompatibilityResult::incompatible(ts_type, webidl_type, "Complex types not compatible")
1847 }
1848
1849 fn check_union_type_compatibility(&self, ts_type: &str, webidl_type: &str) -> TypeCompatibilityResult {
1860 let ts_types: Vec<&str> = ts_type.split('|').map(|t| t.trim()).collect();
1861 let webidl_types: Vec<&str> = webidl_type.split(" or ").map(|t| t.trim()).collect();
1862
1863 if ts_types.len() != webidl_types.len() {
1864 return TypeCompatibilityResult::incompatible(
1865 ts_type,
1866 webidl_type,
1867 format!("Union type member count mismatch: {} vs {}", ts_types.len(), webidl_types.len()),
1868 )
1869 .with_suggestion("Ensure union types have the same number of members");
1870 }
1871
1872 let mut all_compatible = true;
1873 let mut result = TypeCompatibilityResult::compatible(ts_type, webidl_type);
1874
1875 for (ts, webidl) in ts_types.iter().zip(webidl_types.iter()) {
1876 if self.check_type_compatibility(ts, webidl) {
1877 result = result.with_mapping(*ts, *webidl);
1878 }
1879 else {
1880 all_compatible = false;
1881 }
1882 }
1883
1884 if all_compatible {
1885 result
1886 }
1887 else {
1888 TypeCompatibilityResult::incompatible(ts_type, webidl_type, "Union type members not compatible")
1889 }
1890 }
1891
1892 fn check_user_defined_types_detailed(&self, ts_type: &str, webidl_type: &str) -> TypeCompatibilityResult {
1903 if ts_type == webidl_type {
1904 return TypeCompatibilityResult::compatible(ts_type, webidl_type).with_mapping(ts_type, webidl_type);
1905 }
1906
1907 self.check_type_alias_compatibility_detailed(ts_type, webidl_type)
1908 }
1909
1910 fn check_type_alias_compatibility_detailed(&self, ts_type: &str, webidl_type: &str) -> TypeCompatibilityResult {
1921 for item in &self.webidl_result.root.items {
1922 if let IdlItem::Typedef(typedef) = item {
1923 if typedef.name == webidl_type {
1924 if self.check_type_compatibility(ts_type, &typedef.type_name) {
1925 return TypeCompatibilityResult::compatible(ts_type, webidl_type)
1926 .with_mapping(ts_type, &typedef.type_name)
1927 .with_level(CompatibilityLevel::NeedsConversion);
1928 }
1929 }
1930 }
1931 }
1932
1933 TypeCompatibilityResult::incompatible(ts_type, webidl_type, "No matching type alias found")
1934 }
1935
1936 pub fn check_type_compatibility(&self, ts_type: &str, webidl_type: &str) -> bool {
1947 let type_map = self.get_basic_type_mappings();
1948
1949 for (ts, webidl) in type_map {
1950 if ts == ts_type && webidl == webidl_type {
1951 return true;
1952 }
1953 }
1954
1955 if self.check_complex_type_compatibility(ts_type, webidl_type) {
1956 return true;
1957 }
1958
1959 self.check_user_defined_types(ts_type, webidl_type)
1960 }
1961
1962 fn check_complex_type_compatibility(&self, ts_type: &str, webidl_type: &str) -> bool {
1973 if ts_type.starts_with("Array<") && webidl_type.starts_with("sequence<") {
1974 let ts_inner = &ts_type[6..ts_type.len() - 1];
1975 let webidl_inner = &webidl_type[9..webidl_type.len() - 1];
1976 return self.check_type_compatibility(ts_inner, webidl_inner);
1977 }
1978
1979 if ts_type.starts_with("Promise<") && webidl_type.starts_with("Promise<") {
1980 let ts_inner = &ts_type[8..ts_type.len() - 1];
1981 let webidl_inner = &webidl_type[8..webidl_type.len() - 1];
1982 return self.check_type_compatibility(ts_inner, webidl_inner);
1983 }
1984
1985 if ts_type.contains('|') && webidl_type.contains(" or ") {
1986 let ts_types: Vec<&str> = ts_type.split('|').map(|t| t.trim()).collect();
1987 let webidl_types: Vec<&str> = webidl_type.split(" or ").map(|t| t.trim()).collect();
1988
1989 if ts_types.len() != webidl_types.len() {
1990 return false;
1991 }
1992
1993 for (ts, webidl) in ts_types.iter().zip(webidl_types.iter()) {
1994 if !self.check_type_compatibility(ts, webidl) {
1995 return false;
1996 }
1997 }
1998
1999 return true;
2000 }
2001
2002 if ts_type.starts_with('{') && ts_type.contains("[key:") && webidl_type.starts_with("record<") {
2003 return true;
2004 }
2005
2006 false
2007 }
2008
2009 fn check_user_defined_types(&self, ts_type: &str, webidl_type: &str) -> bool {
2020 if ts_type == webidl_type {
2021 return true;
2022 }
2023
2024 self.check_type_alias_compatibility(ts_type, webidl_type)
2025 }
2026
2027 fn check_type_alias_compatibility(&self, ts_type: &str, webidl_type: &str) -> bool {
2038 for item in &self.webidl_result.root.items {
2039 if let IdlItem::Typedef(typedef) = item {
2040 if typedef.name == webidl_type {
2041 return self.check_type_compatibility(ts_type, &typedef.type_name);
2042 }
2043 }
2044 }
2045
2046 false
2047 }
2048
2049 pub fn validate_webidl_types(&self) -> bool {
2055 if self.webidl_result.root.items.is_empty() {
2056 return false;
2057 }
2058
2059 self.validate_items(&self.webidl_result.root)
2060 }
2061
2062 fn validate_items(&self, root: &IdlRoot) -> bool {
2072 for item in &root.items {
2073 match item {
2074 IdlItem::Interface(interface) => {
2075 if interface.name.is_empty() {
2076 return false;
2077 }
2078 }
2079 IdlItem::Struct(struct_) => {
2080 if struct_.name.is_empty() {
2081 return false;
2082 }
2083 }
2084 IdlItem::Enum(enum_) => {
2085 if enum_.name.is_empty() {
2086 return false;
2087 }
2088 if enum_.variants.is_empty() {
2089 return false;
2090 }
2091 }
2092 IdlItem::Typedef(typedef) => {
2093 if typedef.name.is_empty() {
2094 return false;
2095 }
2096 }
2097 IdlItem::Const(const_) => {
2098 if const_.name.is_empty() {
2099 return false;
2100 }
2101 }
2102 IdlItem::Module(module) => {
2103 if module.name.is_empty() {
2104 return false;
2105 }
2106 if !self.validate_items(&IdlRoot { items: module.items.clone() }) {
2107 return false;
2108 }
2109 }
2110 }
2111 }
2112
2113 true
2114 }
2115
2116 pub fn validate_and_collect_errors(&mut self) -> &TypeErrorCollector {
2122 self.validate_items_with_errors(&self.webidl_result.root.clone(), None);
2123 &self.error_collector
2124 }
2125
2126 fn validate_items_with_errors(&mut self, root: &IdlRoot, file_path: Option<&str>) {
2133 for item in &root.items {
2134 match item {
2135 IdlItem::Interface(interface) => {
2136 if interface.name.is_empty() {
2137 self.error_collector.report_error(
2138 TypeError::new("Interface name cannot be empty", "E001")
2139 .with_file(file_path.unwrap_or("unknown"))
2140 .with_severity(TypeErrorSeverity::Error),
2141 );
2142 }
2143
2144 for member in &interface.members {
2145 if let oak_idl::ast::IdlMember::Attribute(attr) = member {
2146 if attr.name.is_empty() {
2147 self.error_collector.report_error(
2148 TypeError::new("Attribute name cannot be empty", "E002")
2149 .with_file(file_path.unwrap_or("unknown"))
2150 .with_severity(TypeErrorSeverity::Error),
2151 );
2152 }
2153 if attr.type_name.is_empty() {
2154 self.error_collector.report_error(
2155 TypeError::new(format!("Attribute '{}' has no type specified", attr.name), "E003")
2156 .with_file(file_path.unwrap_or("unknown"))
2157 .with_severity(TypeErrorSeverity::Warning),
2158 );
2159 }
2160 }
2161 }
2162 }
2163 IdlItem::Struct(struct_) => {
2164 if struct_.name.is_empty() {
2165 self.error_collector.report_error(
2166 TypeError::new("Struct name cannot be empty", "E004")
2167 .with_file(file_path.unwrap_or("unknown"))
2168 .with_severity(TypeErrorSeverity::Error),
2169 );
2170 }
2171
2172 for field in &struct_.fields {
2173 if field.name.is_empty() {
2174 self.error_collector.report_error(
2175 TypeError::new("Field name cannot be empty", "E005")
2176 .with_file(file_path.unwrap_or("unknown"))
2177 .with_severity(TypeErrorSeverity::Error),
2178 );
2179 }
2180 }
2181 }
2182 IdlItem::Enum(enum_) => {
2183 if enum_.name.is_empty() {
2184 self.error_collector.report_error(
2185 TypeError::new("Enum name cannot be empty", "E006")
2186 .with_file(file_path.unwrap_or("unknown"))
2187 .with_severity(TypeErrorSeverity::Error),
2188 );
2189 }
2190 if enum_.variants.is_empty() {
2191 self.error_collector.report_error(
2192 TypeError::new(format!("Enum '{}' has no variants", enum_.name), "E007")
2193 .with_file(file_path.unwrap_or("unknown"))
2194 .with_severity(TypeErrorSeverity::Warning),
2195 );
2196 }
2197 }
2198 IdlItem::Typedef(typedef) => {
2199 if typedef.name.is_empty() {
2200 self.error_collector.report_error(
2201 TypeError::new("Typedef name cannot be empty", "E008")
2202 .with_file(file_path.unwrap_or("unknown"))
2203 .with_severity(TypeErrorSeverity::Error),
2204 );
2205 }
2206 if typedef.type_name.is_empty() {
2207 self.error_collector.report_error(
2208 TypeError::new(format!("Typedef '{}' has no target type", typedef.name), "E009")
2209 .with_file(file_path.unwrap_or("unknown"))
2210 .with_severity(TypeErrorSeverity::Error),
2211 );
2212 }
2213 }
2214 IdlItem::Const(const_) => {
2215 if const_.name.is_empty() {
2216 self.error_collector.report_error(
2217 TypeError::new("Const name cannot be empty", "E010")
2218 .with_file(file_path.unwrap_or("unknown"))
2219 .with_severity(TypeErrorSeverity::Error),
2220 );
2221 }
2222 if const_.value.is_empty() {
2223 self.error_collector.report_error(
2224 TypeError::new(format!("Const '{}' has no value", const_.name), "E011")
2225 .with_file(file_path.unwrap_or("unknown"))
2226 .with_severity(TypeErrorSeverity::Warning),
2227 );
2228 }
2229 }
2230 IdlItem::Module(module) => {
2231 if module.name.is_empty() {
2232 self.error_collector.report_error(
2233 TypeError::new("Module name cannot be empty", "E012")
2234 .with_file(file_path.unwrap_or("unknown"))
2235 .with_severity(TypeErrorSeverity::Error),
2236 );
2237 }
2238 self.validate_items_with_errors(&IdlRoot { items: module.items.clone() }, file_path);
2239 }
2240 }
2241 }
2242 }
2243}